Skip to content

Commit

Permalink
Merge branch 'main' into fix-useBodyScrollLock
Browse files Browse the repository at this point in the history
  • Loading branch information
sofiushko authored Nov 14, 2024
2 parents 90e3572 + 60a8b92 commit c62302c
Show file tree
Hide file tree
Showing 23 changed files with 1,045 additions and 8 deletions.
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
# Changelog

## [6.35.1](https://github.com/gravity-ui/uikit/compare/v6.35.0...v6.35.1) (2024-11-13)


### Bug Fixes

* fix incorrect scss "use" ([#1943](https://github.com/gravity-ui/uikit/issues/1943)) ([c9506dd](https://github.com/gravity-ui/uikit/commit/c9506dd5cfb78f155ea4e38f09e060233c4f691b))

## [6.35.0](https://github.com/gravity-ui/uikit/compare/v6.34.0...v6.35.0) (2024-11-11)


### Features

* **FilePreview:** add component ([#1880](https://github.com/gravity-ui/uikit/issues/1880)) ([b5de671](https://github.com/gravity-ui/uikit/commit/b5de67126a76467853957df6d8e5436cc460886e))
* **NumberInput:** add new component ([#1826](https://github.com/gravity-ui/uikit/issues/1826)) ([75be05e](https://github.com/gravity-ui/uikit/commit/75be05e42c95226592cdd2139894e9ff791280af))
* **PlaceholderContainer:** add qa prop ([#1925](https://github.com/gravity-ui/uikit/issues/1925)) ([77ad28d](https://github.com/gravity-ui/uikit/commit/77ad28d8aa0af910999e0ed54fce806767241946))


### Bug Fixes

* **PlaceholderContainer:** add component to index file ([#1924](https://github.com/gravity-ui/uikit/issues/1924)) ([cdce171](https://github.com/gravity-ui/uikit/commit/cdce1715a6fe5ceebff7b43c9146c3c1bef9f093))
* **Popover:** use visible title for popup a11y label ([#1941](https://github.com/gravity-ui/uikit/issues/1941)) ([f9d75e8](https://github.com/gravity-ui/uikit/commit/f9d75e8b1d902fd526b4a089d7bf6675299f2584))
* **Toaster:** reveal animation lag ([#1927](https://github.com/gravity-ui/uikit/issues/1927)) ([e16fa1c](https://github.com/gravity-ui/uikit/commit/e16fa1ccc7e1f053f4ff696d24ee6d4bbfd35a6a))

## [6.34.0](https://github.com/gravity-ui/uikit/compare/v6.33.0...v6.34.0) (2024-10-25)


Expand Down
14 changes: 12 additions & 2 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,23 @@ task('copy-i18n', () => {

task('styles-global', () => {
return src(['styles/styles.scss', 'styles/fonts.scss'])
.pipe(sass().on('error', sass.logError))
.pipe(
sass().on('error', function (error) {
sass.logError.call(this, error);
process.exit(1);
}),
)
.pipe(dest('styles'));
});

task('styles-components', () => {
return src(['src/components/**/*.scss', '!src/components/**/__stories__/**/*'])
.pipe(sass().on('error', sass.logError))
.pipe(
sass().on('error', function (error) {
sass.logError.call(this, error);
process.exit(1);
}),
)
.pipe(dest(path.resolve(BUILD_DIR, 'esm', 'components')))
.pipe(dest(path.resolve(BUILD_DIR, 'cjs', 'components')));
});
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@gravity-ui/uikit",
"version": "6.34.0",
"version": "6.35.1",
"description": "Gravity UI base styling and components",
"keywords": [
"component",
Expand Down
148 changes: 148 additions & 0 deletions src/components/FilePreview/FilePreview.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
@use 'sass:math';
@use '../variables';

$block: '.#{variables.$ns}file-preview';

$smallRoundedButtonSize: 24px;

#{$block} {
--_-box-shadow: none;
--_-border-radius: 4px;
--_-color-base-background: transparent;

position: relative;

width: 120px;

&:hover,
&:focus-within {
#{$block}__actions:not(#{$block}__actions_hide) {
opacity: 1;
}

--_-color-base-background: var(--g-color-base-simple-hover, rgba(0, 0, 0, 0.05));
}

&__actions {
position: absolute;
inset-block-start: -1 * math.div($smallRoundedButtonSize, 2);
inset-inline-end: -1 * math.div($smallRoundedButtonSize, 2);
z-index: 1;

display: flex;
gap: 4px;

opacity: 0;
}

&:hover {
--_-color-base-background: var(--g-color-base-simple-hover);
}

&__card {
display: flex;
flex-direction: column;
align-items: center;

position: relative;
outline: none;

box-shadow: var(--gc-card-box-shadow);
border-radius: var(--_-border-radius);
padding: 4px 10px;

&_clickable {
cursor: pointer;
}

&_hoverable {
background-color: var(--_-color-base-background);
}

&::after {
position: absolute;
inset: 0;
border-radius: var(--_-border-radius);
pointer-events: none;
}

&:hover {
--_-box-shadow: 0px 3px 10px var(--g-color-sfx-shadow);
}

&:focus::after {
content: '';
box-shadow: 0 0 0 2px var(--g-color-line-misc);
}

&:focus:not(:focus-visible)::after {
box-shadow: none;
}
}

&__icon {
display: flex;
justify-content: center;
align-items: center;

border-radius: 4px;
background-color: var(--g-color-base-generic-medium);

height: 40px;
width: 40px;

&-svg {
color: var(--g-color-base-background);
}

&_type {
&_image,
&_video,
&_code,
&_archive,
&_music {
background-color: var(--g-color-base-misc-heavy);
}
&_text {
background-color: var(--g-color-base-info-heavy);
}
&_pdf {
background-color: var(--g-color-base-danger-medium);
}
&_table {
background-color: var(--g-color-base-positive-medium);
}
}
}

&__name {
margin-block-start: 4px;
}

&__name,
&__description {
text-align: center;
width: 100%;
}

&__image-container {
position: relative;

border-radius: 4px;
overflow: hidden;

height: 64px;
width: 96px;
}

&-image {
position: absolute;
inset-block-start: 0;
inset-inline-start: 0;

object-fit: cover;

height: 100%;
width: 100%;
}
}
166 changes: 166 additions & 0 deletions src/components/FilePreview/FilePreview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import React from 'react';

import {
FileZipper as ArchiveIcon,
Code as CodeIcon,
FileQuestion as DefaultIcon,
Picture as ImageIcon,
MusicNote as MusicIcon,
LogoAcrobat as PdfIcon,
LayoutHeaderCellsLarge as TableIcon,
TextAlignLeft as TextIcon,
Filmstrip as VideoIcon,
} from '@gravity-ui/icons';

import {useActionHandlers, useUniqId} from '../../hooks';
import {useBoolean} from '../../hooks/private';
import {Icon} from '../Icon';
import type {IconData} from '../Icon';
import {Text} from '../Text';
import {useMobile} from '../mobile';
import type {QAProps} from '../types';
import {block} from '../utils/cn';

import {FilePreviewAction} from './FilePreviewAction';
import type {FilePreviewActionProps} from './FilePreviewAction';
import {MobileImagePreview} from './MobileImagePreview/MobileImagePreview';
import type {FileType} from './types';
import {getFileType} from './utils';

import './FilePreview.scss';

const cn = block('file-preview');

const FILE_ICON: Record<FileType, IconData> = {
default: DefaultIcon,
image: ImageIcon,
video: VideoIcon,
code: CodeIcon,
archive: ArchiveIcon,
music: MusicIcon,
text: TextIcon,
pdf: PdfIcon,
table: TableIcon,
};

export interface FilePreviewProps extends QAProps {
className?: string;

file: File;
imageSrc?: string;
description?: string;

onClick?: React.MouseEventHandler<HTMLDivElement>;
actions?: FilePreviewActionProps[];
}

export function FilePreview({
className,
qa,
file,
imageSrc,
description,
onClick,
actions,
}: FilePreviewProps) {
const id = useUniqId();

const [previewSrc, setPreviewSrc] = React.useState<string | undefined>(imageSrc);
const [isPreviewSheetVisible, showPreviewSheet, closePreviewSheet] = useBoolean(false);
const mobile = useMobile();
const type = getFileType(file);

const {onKeyDown} = useActionHandlers(onClick);

React.useEffect(() => {
if (imageSrc) return undefined;

try {
const createdUrl = URL.createObjectURL(file);

setPreviewSrc(createdUrl);

return () => {
URL.revokeObjectURL(createdUrl);
};
} catch (error: unknown) {
return undefined;
}
}, [file, imageSrc]);

const clickable = Boolean(onClick);
const withActions = Boolean(actions?.length);

const isPreviewString = typeof previewSrc === 'string';
const hideActions = isPreviewString && mobile;

const handleClick: React.MouseEventHandler<HTMLDivElement> = React.useCallback(
(e) => {
if (onClick) {
onClick(e);
return;
}

if (mobile && isPreviewString) {
showPreviewSheet();
}
},
[isPreviewString, mobile, onClick, showPreviewSheet],
);

return (
<div className={cn(null, className)} data-qa={qa}>
<div
className={cn('card', {clickable, hoverable: clickable || withActions})}
role={clickable ? 'button' : undefined}
onKeyDown={clickable ? onKeyDown : undefined}
tabIndex={clickable ? 0 : undefined}
onClick={clickable ? handleClick : undefined}
>
{isPreviewString ? (
<div className={cn('image-container')}>
<img className={cn('image')} src={previewSrc} alt={file.name} />
</div>
) : (
<div className={cn('icon', {type})}>
<Icon className={cn('icon-svg')} data={FILE_ICON[type]} size={20} />
</div>
)}
<Text className={cn('name')} color="secondary" ellipsis title={file.name}>
{file.name}
</Text>
{Boolean(description) && (
<Text
className={cn('description')}
color="secondary"
ellipsis
title={description}
>
{description}
</Text>
)}
</div>
{actions?.length ? (
<div className={cn('actions', {hide: hideActions})}>
{actions.map((action, index) => (
<FilePreviewAction
key={`${id}-${index}`}
id={`${id}-${index}`}
{...action}
/>
))}
</div>
) : null}

<MobileImagePreview
visible={isPreviewSheetVisible}
onClose={closePreviewSheet}
actions={actions}
previewSrc={previewSrc}
fileName={file.name}
/>
</div>
);
}

FilePreview.displayName = 'FilePreview';
Loading

0 comments on commit c62302c

Please sign in to comment.