From 63a9537070ebf836662a778b09a5e7e3972d207b Mon Sep 17 00:00:00 2001 From: iljs Date: Wed, 3 Dec 2025 10:29:33 +0300 Subject: [PATCH] feat(plasma-new-hope): add File component --- .../src/components/File/File.styles.ts | 215 ++++++++++++++++ .../src/components/File/File.tokens.ts | 81 ++++++ .../src/components/File/File.tsx | 157 ++++++++++++ .../src/components/File/File.types.ts | 88 +++++++ .../src/components/File/index.ts | 3 + .../File/variations/_disabled/base.ts | 16 ++ .../components/File/variations/_size/base.ts | 10 + .../components/File/variations/_view/base.ts | 16 ++ .../components/_Icon/Icon.assets/Download.tsx | 16 ++ .../components/_Icon/Icons/IconDownload.tsx | 17 ++ .../src/components/_Icon/index.tsx | 1 + .../examples/components/File/File.config.ts | 236 ++++++++++++++++++ .../examples/components/File/File.stories.tsx | 126 ++++++++++ .../src/examples/components/File/File.ts | 8 + 14 files changed, 990 insertions(+) create mode 100644 packages/plasma-new-hope/src/components/File/File.styles.ts create mode 100644 packages/plasma-new-hope/src/components/File/File.tokens.ts create mode 100644 packages/plasma-new-hope/src/components/File/File.tsx create mode 100644 packages/plasma-new-hope/src/components/File/File.types.ts create mode 100644 packages/plasma-new-hope/src/components/File/index.ts create mode 100644 packages/plasma-new-hope/src/components/File/variations/_disabled/base.ts create mode 100644 packages/plasma-new-hope/src/components/File/variations/_size/base.ts create mode 100644 packages/plasma-new-hope/src/components/File/variations/_view/base.ts create mode 100644 packages/plasma-new-hope/src/components/_Icon/Icon.assets/Download.tsx create mode 100644 packages/plasma-new-hope/src/components/_Icon/Icons/IconDownload.tsx create mode 100644 packages/plasma-new-hope/src/examples/components/File/File.config.ts create mode 100644 packages/plasma-new-hope/src/examples/components/File/File.stories.tsx create mode 100644 packages/plasma-new-hope/src/examples/components/File/File.ts diff --git a/packages/plasma-new-hope/src/components/File/File.styles.ts b/packages/plasma-new-hope/src/components/File/File.styles.ts new file mode 100644 index 0000000000..a7f5ae01fa --- /dev/null +++ b/packages/plasma-new-hope/src/components/File/File.styles.ts @@ -0,0 +1,215 @@ +import { css } from '@linaria/core'; +import { styled } from '@linaria/react'; + +import { applyEllipsis } from '../../mixins'; +import { component, mergeConfig } from '../../engines'; +import { progressBarCircularConfig, progressBarCircularTokens } from '../ProgressBarCircular'; +import { progressConfig, progressTokens } from '../Progress'; +import { IconClose } from '../_Icon'; + +import { classes, tokens } from './File.tokens'; + +const mergedProgressBarCircularConfig = mergeConfig(progressBarCircularConfig); +const ProgressBarCircular = component(mergedProgressBarCircularConfig); + +const mergedProgressConfig = mergeConfig(progressConfig); +const Progress = component(mergedProgressConfig); + +export const FileLabel = styled.span` + color: var(${tokens.fileLabelColor}); + font-family: var(${tokens.fileLabelFontFamily}); + font-size: var(${tokens.fileLabelFontSize}); + font-style: var(${tokens.fileLabelFontStyle}); + font-weight: var(${tokens.fileLabelFontWeight}); + letter-spacing: var(${tokens.fileLabelLetterSpacing}); + line-height: var(${tokens.fileLabelLineHeight}); + + ${applyEllipsis()} +`; + +export const FileDescription = styled.span` + color: var(${tokens.fileDescriptionColor}); + font-family: var(${tokens.fileDescriptionFontFamily}); + font-size: var(${tokens.fileDescriptionFontSize}); + font-style: var(${tokens.fileDescriptionFontStyle}); + font-weight: var(${tokens.fileDescriptionFontWeight}); + letter-spacing: var(${tokens.fileDescriptionLetterSpacing}); + line-height: var(${tokens.fileDescriptionLineHeight}); + + ${applyEllipsis()} +`; + +export const FileTextContent = styled.div` + display: flex; + flex-direction: column; + flex-grow: 1; + min-width: 0; +`; + +export const CancelButton = styled.button` + display: flex; + align-items: center; + justify-content: center; + width: var(${tokens.iconCloseSize}); + height: var(${tokens.iconCloseSize}); + padding: 0; + margin: 0; + border: none; + background: transparent; + cursor: pointer; + color: var(${tokens.iconCloseColor}); + + :hover { + opacity: 0.8; + } + + :active { + opacity: 0.6; + } +`; + +export const StyledIconClose = styled(IconClose)` + width: 100%; + height: 100%; + color: currentColor; +`; + +export const FileThumb = styled.div` + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + + width: var(${tokens.fileThumbSize}); + height: var(${tokens.fileThumbSize}); + margin: var(${tokens.fileThumbMargin}); + border-radius: var(${tokens.fileThumbRadius}); + overflow: hidden; + color: var(${tokens.fileColor}); + + img { + width: 100%; + height: 100%; + object-fit: cover; + } +`; + +export const FileAction = styled.div` + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + + width: var(${tokens.fileActionSize}); + height: var(${tokens.fileActionSize}); + margin: var(${tokens.fileActionMargin}); + color: var(${tokens.fileColor}); +`; + +export const FileLoader = styled.div` + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + margin: var(${tokens.fileActionMargin}); +`; + +export const StyledProgressBarCircular = styled(ProgressBarCircular)` + ${progressBarCircularTokens.size}: var(${tokens.progressBarCircularSize}); + ${progressBarCircularTokens.width}: var(${tokens.progressBarCircularWidth}); + ${progressBarCircularTokens.height}: var(${tokens.progressBarCircularHeight}); + + ${progressBarCircularTokens.strokeWidth}: var(${tokens.progressBarCircularStrokeWidth}); + ${progressBarCircularTokens.strokeSize}: var(${tokens.progressBarCircularStrokeSize}); + + ${progressBarCircularTokens.backgroundStroke}: var(${tokens.progressBarCircularBackgroundStroke}); + ${progressBarCircularTokens.progressStroke}: var(${tokens.progressBarCircularStroke}); + + ${progressBarCircularTokens.contentColor}: var(${tokens.progressBarCircularContentColor}); + ${progressBarCircularTokens.contentFontFamily}: var(${tokens.progressBarCircularContentFontFamily}); + ${progressBarCircularTokens.contentFontSize}: var(${tokens.progressBarCircularContentFontSize}); + ${progressBarCircularTokens.contentFontStyle}: var(${tokens.progressBarCircularContentFontStyle}); + ${progressBarCircularTokens.contentFontWeight}: var(${tokens.progressBarCircularContentFontWeight}); + ${progressBarCircularTokens.contentLetterSpacing}: var(${tokens.progressBarCircularContentLetterSpacing}); + ${progressBarCircularTokens.contentLineHeight}: var(${tokens.progressBarCircularContentLineHeight}); +`; + +export const StyledProgress = styled(Progress)` + ${progressTokens.trackBackgroundColor}: var(${tokens.progressTrackBackgroundColor}); + ${progressTokens.trackHeight}: var(${tokens.progressTrackHeight}); + ${progressTokens.trackBorderRadius}: var(${tokens.progressTrackBorderRadius}); + + ${progressTokens.progressFilledBackgroundColor}: var(${tokens.progressFilledBackgroundColor}); + ${progressTokens.progressFilledHeight}: var(${tokens.progressFilledHeight}); + ${progressTokens.progressFilledBorderRadius}: var(${tokens.progressFilledBorderRadius}); + width: 100%; +`; + +export const FileLinearLoader = styled.div` + position: absolute; + bottom: -2px; + left: 0; + right: 0; + height: var(${tokens.fileLinearLoaderHeight}); + display: flex; + align-items: center; +`; + +export const FileContent = styled.div` + display: flex; + align-items: center; + width: 100%; + height: var(${tokens.fileHeight}); +`; + +export const base = css` + position: relative; + display: flex; + align-items: space-between; + box-sizing: border-box; + overflow: hidden; + width: var(${tokens.fileWidth}, 100%); + + background: var(${tokens.fileBackgroundColor}); + border-radius: var(${tokens.fileRadius}); + padding: var(${tokens.filePadding}); + padding-left: var(${tokens.filePaddingLeft}, var(${tokens.filePadding})); + padding-right: var(${tokens.filePaddingRight}, var(${tokens.filePadding})); + + cursor: pointer; + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + + :hover { + background: var(${tokens.fileBackgroundColorHover}, var(${tokens.fileBackgroundColor})); + } + + :active { + background: var(${tokens.fileBackgroundColorActive}, var(${tokens.fileBackgroundColor})); + } + + &.${classes.fileDisabled} { + opacity: var(${tokens.fileDisabledOpacity}); + cursor: not-allowed; + + :hover, + :active { + background: var(${tokens.fileBackgroundColor}); + } + } + + &.${classes.fileLoaderLinear} { + padding-bottom: var(${tokens.fileLinearLoaderHeight}); + } + + &.${classes.fileActionLeft} { + ${FileAction} { + order: -1; + } + } + + &.${classes.fileActionRight} { + ${FileAction} { + order: 1; + } + } +`; diff --git a/packages/plasma-new-hope/src/components/File/File.tokens.ts b/packages/plasma-new-hope/src/components/File/File.tokens.ts new file mode 100644 index 0000000000..4b2e4d105d --- /dev/null +++ b/packages/plasma-new-hope/src/components/File/File.tokens.ts @@ -0,0 +1,81 @@ +export const classes = { + fileItem: 'file-item', + fileDisabled: 'file-disabled', + fileLoading: 'file-loading', + fileActionLeft: 'file-action-left', + fileActionRight: 'file-action-right', + fileLoaderLinear: 'file-loader-linear', +}; + +export const tokens = { + fileColor: '--plasma-file-color', + fileBackgroundColor: '--plasma-file-background-color', + fileBackgroundColorHover: '--plasma-file-background-color-hover', + fileBackgroundColorActive: '--plasma-file-background-color-active', + + fileLabelColor: '--plasma-file-label-color', + fileLabelFontFamily: '--plasma-file-label-font-family', + fileLabelFontSize: '--plasma-file-label-font-size', + fileLabelFontStyle: '--plasma-file-label-font-style', + fileLabelFontWeight: '--plasma-file-label-font-weight', + fileLabelLetterSpacing: '--plasma-file-label-letter-spacing', + fileLabelLineHeight: '--plasma-file-label-line-height', + + fileDescriptionColor: '--plasma-file-description-color', + fileDescriptionFontFamily: '--plasma-file-description-font-family', + fileDescriptionFontSize: '--plasma-file-description-font-size', + fileDescriptionFontStyle: '--plasma-file-description-font-style', + fileDescriptionFontWeight: '--plasma-file-description-font-weight', + fileDescriptionLetterSpacing: '--plasma-file-description-letter-spacing', + fileDescriptionLineHeight: '--plasma-file-description-line-height', + + fileHeight: '--plasma-file-height', + fileWidth: '--plasma-file-width', + filePadding: '--plasma-file-padding', + filePaddingLeft: '--plasma-file-padding-left', + filePaddingRight: '--plasma-file-padding-right', + fileRadius: '--plasma-file-radius', + + fileThumbSize: '--plasma-file-thumb-size', + fileThumbMargin: '--plasma-file-thumb-margin', + fileThumbRadius: '--plasma-file-thumb-radius', + + fileActionMargin: '--plasma-file-action-margin', + fileActionSize: '--plasma-file-action-size', + + fileLoaderSize: '--plasma-file-loader-size', + fileLoaderColor: '--plasma-file-loader-color', + + fileDisabledOpacity: '--plasma-file-disabled-opacity', + + // IconClose tokens + iconCloseSize: '--plasma-file-icon-close-size', + iconCloseColor: '--plasma-file-icon-close-color', + + // ProgressBarCircular tokens + progressBarCircularSize: '--plasma-file-progressbar-circular-size', + progressBarCircularWidth: '--plasma-file-progressbar-circular-width', + progressBarCircularHeight: '--plasma-file-progressbar-circular-height', + progressBarCircularStrokeWidth: '--plasma-file-progressbar-circular-stroke-width', + progressBarCircularStrokeSize: '--plasma-file-progressbar-circular-stroke-size', + progressBarCircularBackgroundStroke: '--plasma-file-progressbar-circular-background-stroke', + progressBarCircularStroke: '--plasma-file-progressbar-circular-stroke', + progressBarCircularContentColor: '--plasma-file-progressbar-circular-content-color', + progressBarCircularContentFontFamily: '--plasma-file-progressbar-circular-content-font-family', + progressBarCircularContentFontSize: '--plasma-file-progressbar-circular-content-font-size', + progressBarCircularContentFontStyle: '--plasma-file-progressbar-circular-content-font-style', + progressBarCircularContentFontWeight: '--plasma-file-progressbar-circular-content-font-weight', + progressBarCircularContentLetterSpacing: '--plasma-file-progressbar-circular-content-letter-spacing', + progressBarCircularContentLineHeight: '--plasma-file-progressbar-circular-content-line-height', + + // Progress (linear) tokens + progressTrackBackgroundColor: '--plasma-file-progress-track-background-color', + progressTrackHeight: '--plasma-file-progress-track-height', + progressTrackBorderRadius: '--plasma-file-progress-track-border-radius', + progressFilledBackgroundColor: '--plasma-file-progress-filled-background-color', + progressFilledHeight: '--plasma-file-progress-filled-height', + progressFilledBorderRadius: '--plasma-file-progress-filled-border-radius', + + // Linear loader container height + fileLinearLoaderHeight: '--plasma-file-linear-loader-height', +}; diff --git a/packages/plasma-new-hope/src/components/File/File.tsx b/packages/plasma-new-hope/src/components/File/File.tsx new file mode 100644 index 0000000000..839b6a6fdf --- /dev/null +++ b/packages/plasma-new-hope/src/components/File/File.tsx @@ -0,0 +1,157 @@ +import React, { forwardRef } from 'react'; + +import type { RootProps } from '../../engines'; +import { cx } from '../../utils'; +import { getFileIcon } from '../Attach/utils/getFileicon'; + +import { base as viewCSS } from './variations/_view/base'; +import { base as sizeCSS } from './variations/_size/base'; +import { base as disabledCSS } from './variations/_disabled/base'; +import type { FileProps } from './File.types'; +import { + base, + FileLabel, + FileDescription, + FileTextContent, + FileThumb, + FileAction, + FileLoader, + FileLinearLoader, + FileContent, + StyledProgressBarCircular, + StyledProgress, + CancelButton, + StyledIconClose, +} from './File.styles'; +import { classes } from './File.tokens'; + +export const fileRoot = (Root: RootProps) => + forwardRef((props, ref) => { + const { + label, + description, + actionPlacement = 'right', + fileUrl, + thumbIcon, + thumbUrl, + actionContent, + isLoading = false, + loaderType = 'circular', + loader, + loaderValue = 0, + loaderMaxValue = 100, + view, + size, + disabled = false, + onCancel, + className, + ...rest + } = props; + + const actionPlacementClass = actionPlacement === 'left' ? classes.fileActionLeft : classes.fileActionRight; + const disabledClass = disabled ? classes.fileDisabled : undefined; + const loadingClass = isLoading ? classes.fileLoading : undefined; + const loaderLinearClass = loaderType === 'linear' ? classes.fileLoaderLinear : undefined; + const extension = fileUrl ? fileUrl.split('.').pop() : undefined; + + const formatBytes = (bytes: number): string => { + if (bytes === 0) return '0 Bytes'; + const k = 1024; + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return `${parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`; + }; + + const formattedDescription = typeof description === 'number' ? formatBytes(description) : description; + + const renderLoader = () => { + if (!isLoading) return null; + + if (loaderType === 'linear') { + return ( + + {loader || } + + ); + } + + return ( + + {loader || ( + + + + + + )} + + ); + }; + + const renderAction = () => { + if (isLoading && loaderType === 'circular') { + return renderLoader(); + } + + if (actionContent) { + return {actionContent}; + } + + return null; + }; + + return ( + + + {(thumbUrl || thumbIcon || fileUrl) && ( + + {thumbUrl ? : thumbIcon || getFileIcon(extension, size)} + + )} + + {label} + {formattedDescription && {formattedDescription}} + + {renderAction()} + + {loaderType === 'linear' && renderLoader()} + + ); + }); + +export const fileConfig = { + name: 'File', + tag: 'div', + layout: fileRoot, + base, + variations: { + view: { + css: viewCSS, + }, + size: { + css: sizeCSS, + }, + disabled: { + css: disabledCSS, + attrs: true, + }, + }, + defaults: { + view: 'default', + size: 'm', + }, +}; diff --git a/packages/plasma-new-hope/src/components/File/File.types.ts b/packages/plasma-new-hope/src/components/File/File.types.ts new file mode 100644 index 0000000000..1d55cc2ca1 --- /dev/null +++ b/packages/plasma-new-hope/src/components/File/File.types.ts @@ -0,0 +1,88 @@ +import type { HTMLAttributes, ReactNode, MouseEvent } from 'react'; + +export type LoaderType = 'circular' | 'linear'; +export type ActionPlacement = 'left' | 'right'; + +export type BaseFileProps = { + /** + * Название файла + */ + label?: ReactNode; + /** + * Размер файла в байтах или любая другая строка + */ + description?: number | string | ReactNode; + /** + * Размещение контента ( кнопка действия и loader ) относительно текста + * @default 'right' + */ + actionPlacement?: ActionPlacement; + /** + * Контент слева (иконка файла) + * thumbIcon, thumbUrl и fileUrl не могут быть использованы одновременно + */ + thumbIcon?: ReactNode; + /** + * Контент слева (URL изображения) + * thumbIcon, thumbUrl и fileUrl не могут быть использованы одновременно + */ + thumbUrl?: string; + /** + * Контент слева, что иконка файла определяется автоматически в зависимости от расширения файла + * thumbIcon, thumbUrl и fileUrl не могут быть использованы одновременно + */ + fileUrl?: string; + /** + * Контент справа + * Виден только если isLoading = false + */ + actionContent?: ReactNode; + /** + * Состояние загрузки + * @default false + */ + isLoading?: boolean; + /** + * Тип загрузчика + * @default 'circular' + */ + loaderType?: LoaderType; + /** + * Слот для компонента загрузки. В зависимости от loaderType слот находиться в разных местах: + * - 'circular' - справа вместо contentRight + * - 'linear' - в низу компонента + * @default ProgressBarCircular + */ + loader?: ReactNode; + /** + * Значение загрузчика + * @default 0 + */ + loaderValue?: number; + /** + * Максимальное значение загрузчика + * @default 100 + */ + loaderMaxValue?: number; + /** + * Вид файла + * @default 'default' + */ + view?: string; + /** + * Размер файла + * @default 'm' + */ + size?: string; + /** + * Файл неактивен + * @default false + */ + disabled?: boolean; + /** + * Функция вызываемая при отмене загруки + */ + onCancel?: (event: MouseEvent) => void; +}; + +export type FileProps = Omit, 'label'> & BaseFileProps; diff --git a/packages/plasma-new-hope/src/components/File/index.ts b/packages/plasma-new-hope/src/components/File/index.ts new file mode 100644 index 0000000000..a5ebef61c0 --- /dev/null +++ b/packages/plasma-new-hope/src/components/File/index.ts @@ -0,0 +1,3 @@ +export { fileRoot, fileConfig } from './File'; +export type { FileProps, BaseFileProps, LoaderType, ActionPlacement } from './File.types'; +export { tokens as fileTokens, classes as fileClasses } from './File.tokens'; diff --git a/packages/plasma-new-hope/src/components/File/variations/_disabled/base.ts b/packages/plasma-new-hope/src/components/File/variations/_disabled/base.ts new file mode 100644 index 0000000000..82d303d72b --- /dev/null +++ b/packages/plasma-new-hope/src/components/File/variations/_disabled/base.ts @@ -0,0 +1,16 @@ +import { css } from '@linaria/core'; + +import { tokens, classes } from '../../File.tokens'; + +export const base = css` + &[disabled], + &.${classes.fileDisabled} { + opacity: var(${tokens.fileDisabledOpacity}); + cursor: not-allowed; + + :hover, + :active { + background: var(${tokens.fileBackgroundColor}); + } + } +`; diff --git a/packages/plasma-new-hope/src/components/File/variations/_size/base.ts b/packages/plasma-new-hope/src/components/File/variations/_size/base.ts new file mode 100644 index 0000000000..689ece0adb --- /dev/null +++ b/packages/plasma-new-hope/src/components/File/variations/_size/base.ts @@ -0,0 +1,10 @@ +import { css } from '@linaria/core'; + +import { tokens } from '../../File.tokens'; + +export const base = css` + padding: var(${tokens.filePadding}); + padding-left: var(${tokens.filePaddingLeft}, var(${tokens.filePadding})); + padding-right: var(${tokens.filePaddingRight}, var(${tokens.filePadding})); + border-radius: var(${tokens.fileRadius}); +`; diff --git a/packages/plasma-new-hope/src/components/File/variations/_view/base.ts b/packages/plasma-new-hope/src/components/File/variations/_view/base.ts new file mode 100644 index 0000000000..fc309659cd --- /dev/null +++ b/packages/plasma-new-hope/src/components/File/variations/_view/base.ts @@ -0,0 +1,16 @@ +import { css } from '@linaria/core'; + +import { tokens } from '../../File.tokens'; + +export const base = css` + color: var(${tokens.fileColor}); + background: var(${tokens.fileBackgroundColor}); + + :hover { + background: var(${tokens.fileBackgroundColorHover}, var(${tokens.fileBackgroundColor})); + } + + :active { + background: var(${tokens.fileBackgroundColorActive}, var(${tokens.fileBackgroundColor})); + } +`; diff --git a/packages/plasma-new-hope/src/components/_Icon/Icon.assets/Download.tsx b/packages/plasma-new-hope/src/components/_Icon/Icon.assets/Download.tsx new file mode 100644 index 0000000000..6a8a7d1ce0 --- /dev/null +++ b/packages/plasma-new-hope/src/components/_Icon/Icon.assets/Download.tsx @@ -0,0 +1,16 @@ +import React from 'react'; + +import { IconProps } from '../IconRoot'; + +export const Download: React.FC = (props) => ( + + + + +); diff --git a/packages/plasma-new-hope/src/components/_Icon/Icons/IconDownload.tsx b/packages/plasma-new-hope/src/components/_Icon/Icons/IconDownload.tsx new file mode 100644 index 0000000000..bba93f1020 --- /dev/null +++ b/packages/plasma-new-hope/src/components/_Icon/Icons/IconDownload.tsx @@ -0,0 +1,17 @@ +import React from 'react'; + +import { Download } from '../Icon.assets/Download'; +import { IconRoot, IconProps } from '../IconRoot'; + +export const IconDownload: React.FC = ({ size = 's', color, className, sizeCustomProperty, ...rest }) => { + return ( + + ); +}; diff --git a/packages/plasma-new-hope/src/components/_Icon/index.tsx b/packages/plasma-new-hope/src/components/_Icon/index.tsx index 98c0b8ec0c..a7dffc568f 100644 --- a/packages/plasma-new-hope/src/components/_Icon/index.tsx +++ b/packages/plasma-new-hope/src/components/_Icon/index.tsx @@ -51,3 +51,4 @@ export { IconDoneCircleOutline } from './Icons/IconDoneCircleOutline'; export { IconTrash } from './Icons/IconTrash'; export { IconResizeDiagonal } from './Icons/IconResizeDiagonal'; export { IconDotsHorizontalOutline } from './Icons/IconDotsHorizontalOutline'; +export { IconDownload } from './Icons/IconDownload'; diff --git a/packages/plasma-new-hope/src/examples/components/File/File.config.ts b/packages/plasma-new-hope/src/examples/components/File/File.config.ts new file mode 100644 index 0000000000..7c63d33184 --- /dev/null +++ b/packages/plasma-new-hope/src/examples/components/File/File.config.ts @@ -0,0 +1,236 @@ +import { css } from '@linaria/core'; + +import { fileTokens } from '../../../components/File'; + +export const config = { + defaults: { + view: 'default', + size: 'm', + }, + variations: { + view: { + default: css` + ${fileTokens.fileColor}: var(--text-primary); + + ${fileTokens.fileLabelColor}: var(--text-primary); + ${fileTokens.fileDescriptionColor}: var(--text-secondary); + + ${fileTokens.fileDisabledOpacity}: 0.4; + + ${fileTokens.progressBarCircularBackgroundStroke}: var(--surface-transparent-tertiary); + ${fileTokens.progressBarCircularStroke}: var(--surface-solid-default); + ${fileTokens.progressBarCircularContentColor}: var(--text-primary); + + ${fileTokens.progressTrackBackgroundColor}: var(--surface-transparent-secondary); + ${fileTokens.progressFilledBackgroundColor}: var(--surface-solid-default); + `, + negative: css` + ${fileTokens.fileColor}: var(--text-primary); + + ${fileTokens.fileLabelColor}: var(--text-primary); + ${fileTokens.fileDescriptionColor}: var(--text-secondary); + + ${fileTokens.progressBarCircularBackgroundStroke}: var(--surface-transparent-tertiary); + ${fileTokens.progressBarCircularStroke}: var(--surface-negative); + ${fileTokens.progressBarCircularContentColor}: var(--text-primary); + + ${fileTokens.progressTrackBackgroundColor}: var(--surface-transparent-secondary); + ${fileTokens.progressFilledBackgroundColor}: var(--surface-negative); + `, + }, + size: { + l: css` + ${fileTokens.fileHeight}: 2.5rem; + ${fileTokens.filePadding}: 0rem; + ${fileTokens.fileRadius}: 0rem; + + ${fileTokens.fileLabelFontFamily}: var(--plasma-typo-body-l-font-family); + ${fileTokens.fileLabelFontSize}: var(--plasma-typo-body-l-font-size); + ${fileTokens.fileLabelFontStyle}: var(--plasma-typo-body-l-font-style); + ${fileTokens.fileLabelFontWeight}: var(--plasma-typo-body-l-font-weight); + ${fileTokens.fileLabelLetterSpacing}: var(--plasma-typo-body-l-letter-spacing); + ${fileTokens.fileLabelLineHeight}: var(--plasma-typo-body-l-line-height); + + ${fileTokens.fileDescriptionFontFamily}: var(--plasma-typo-body-s-font-family); + ${fileTokens.fileDescriptionFontSize}: var(--plasma-typo-body-s-font-size); + ${fileTokens.fileDescriptionFontStyle}: var(--plasma-typo-body-s-font-style); + ${fileTokens.fileDescriptionFontWeight}: var(--plasma-typo-body-s-font-weight); + ${fileTokens.fileDescriptionLetterSpacing}: var(--plasma-typo-body-s-letter-spacing); + ${fileTokens.fileDescriptionLineHeight}: var(--plasma-typo-body-s-line-height); + + ${fileTokens.fileThumbSize}: 3rem; + ${fileTokens.fileThumbMargin}: 0 0.75rem 0 0; + ${fileTokens.fileThumbRadius}: 0.5rem; + + ${fileTokens.fileActionMargin}: 0 0 0 0.75rem; + ${fileTokens.fileActionSize}: 2rem; + + ${fileTokens.iconCloseSize}: 1.5rem; + ${fileTokens.iconCloseColor}: var(--text-primary); + + ${fileTokens.progressBarCircularSize}: 48; + ${fileTokens.progressBarCircularHeight}: 3rem; + ${fileTokens.progressBarCircularWidth}: 3rem; + ${fileTokens.progressBarCircularStrokeWidth}: 0.125rem; + ${fileTokens.progressBarCircularStrokeSize}: 2; + ${fileTokens.progressBarCircularContentFontFamily}: var(--plasma-typo-body-m-font-family); + ${fileTokens.progressBarCircularContentFontSize}: var(--plasma-typo-body-m-font-size); + ${fileTokens.progressBarCircularContentFontStyle}: var(--plasma-typo-body-m-font-style); + ${fileTokens.progressBarCircularContentFontWeight}: var(--plasma-typo-body-m-font-weight); + ${fileTokens.progressBarCircularContentLetterSpacing}: var(--plasma-typo-body-m-letter-spacing); + ${fileTokens.progressBarCircularContentLineHeight}: var(--plasma-typo-body-m-line-height); + + ${fileTokens.progressTrackHeight}: 0.25rem; + ${fileTokens.progressTrackBorderRadius}: 0.125rem; + ${fileTokens.progressFilledHeight}: 0.375rem; + ${fileTokens.progressFilledBorderRadius}: 0.375rem; + + ${fileTokens.fileLinearLoaderHeight}: 0.75rem; + `, + m: css` + ${fileTokens.fileHeight}: 2.5rem; + ${fileTokens.filePadding}: 0; + ${fileTokens.fileRadius}: 0; + + ${fileTokens.fileLabelFontFamily}: var(--plasma-typo-body-m-font-family); + ${fileTokens.fileLabelFontSize}: var(--plasma-typo-body-m-font-size); + ${fileTokens.fileLabelFontStyle}: var(--plasma-typo-body-m-font-style); + ${fileTokens.fileLabelFontWeight}: var(--plasma-typo-body-m-font-weight); + ${fileTokens.fileLabelLetterSpacing}: var(--plasma-typo-body-m-letter-spacing); + ${fileTokens.fileLabelLineHeight}: var(--plasma-typo-body-m-line-height); + + ${fileTokens.fileDescriptionFontFamily}: var(--plasma-typo-body-xs-font-family); + ${fileTokens.fileDescriptionFontSize}: var(--plasma-typo-body-xs-font-size); + ${fileTokens.fileDescriptionFontStyle}: var(--plasma-typo-body-xs-font-style); + ${fileTokens.fileDescriptionFontWeight}: var(--plasma-typo-body-xs-font-weight); + ${fileTokens.fileDescriptionLetterSpacing}: var(--plasma-typo-body-xs-letter-spacing); + ${fileTokens.fileDescriptionLineHeight}: var(--plasma-typo-body-xs-line-height); + + ${fileTokens.fileThumbSize}: 2.5rem; + ${fileTokens.fileThumbMargin}: 0 0.625rem 0 0; + ${fileTokens.fileThumbRadius}: 0.375rem; + + ${fileTokens.fileActionMargin}: 0 0 0 0.625rem; + ${fileTokens.fileActionSize}: 1.5rem; + + ${fileTokens.iconCloseSize}: 1rem; + ${fileTokens.iconCloseColor}: var(--text-primary); + + ${fileTokens.progressBarCircularSize}: 40; + ${fileTokens.progressBarCircularHeight}: 2.5rem; + ${fileTokens.progressBarCircularWidth}: 2.5rem; + ${fileTokens.progressBarCircularStrokeWidth}: 0.125rem; + ${fileTokens.progressBarCircularStrokeSize}: 2; + ${fileTokens.progressBarCircularContentFontFamily}: var(--plasma-typo-body-s-font-family); + ${fileTokens.progressBarCircularContentFontSize}: var(--plasma-typo-body-s-font-size); + ${fileTokens.progressBarCircularContentFontStyle}: var(--plasma-typo-body-s-font-style); + ${fileTokens.progressBarCircularContentFontWeight}: var(--plasma-typo-body-s-font-weight); + ${fileTokens.progressBarCircularContentLetterSpacing}: var(--plasma-typo-body-s-letter-spacing); + ${fileTokens.progressBarCircularContentLineHeight}: var(--plasma-typo-body-s-line-height); + + ${fileTokens.progressTrackHeight}: 0.25rem; + ${fileTokens.progressTrackBorderRadius}: 0.125rem; + ${fileTokens.progressFilledHeight}: 0.375rem; + ${fileTokens.progressFilledBorderRadius}: 0.375rem; + + ${fileTokens.fileLinearLoaderHeight}: 0.75rem; + `, + s: css` + ${fileTokens.fileHeight}: 2.5rem; + ${fileTokens.filePadding}: 0rem; + ${fileTokens.fileRadius}: 0rem; + + ${fileTokens.fileLabelFontFamily}: var(--plasma-typo-body-s-font-family); + ${fileTokens.fileLabelFontSize}: var(--plasma-typo-body-s-font-size); + ${fileTokens.fileLabelFontStyle}: var(--plasma-typo-body-s-font-style); + ${fileTokens.fileLabelFontWeight}: var(--plasma-typo-body-s-font-weight); + ${fileTokens.fileLabelLetterSpacing}: var(--plasma-typo-body-s-letter-spacing); + ${fileTokens.fileLabelLineHeight}: var(--plasma-typo-body-s-line-height); + + ${fileTokens.fileDescriptionFontFamily}: var(--plasma-typo-body-xs-font-family); + ${fileTokens.fileDescriptionFontSize}: var(--plasma-typo-body-xs-font-size); + ${fileTokens.fileDescriptionFontStyle}: var(--plasma-typo-body-xs-font-style); + ${fileTokens.fileDescriptionFontWeight}: var(--plasma-typo-body-xs-font-weight); + ${fileTokens.fileDescriptionLetterSpacing}: var(--plasma-typo-body-xs-letter-spacing); + ${fileTokens.fileDescriptionLineHeight}: var(--plasma-typo-body-xs-line-height); + + ${fileTokens.fileThumbSize}: 2rem; + ${fileTokens.fileThumbMargin}: 0 0.5rem 0 0; + ${fileTokens.fileThumbRadius}: 0.25rem; + + ${fileTokens.fileActionMargin}: 0 0 0 0.5rem; + ${fileTokens.fileActionSize}: 1.5rem; + + ${fileTokens.iconCloseSize}: 1rem; + ${fileTokens.iconCloseColor}: var(--text-primary); + + ${fileTokens.progressBarCircularSize}: 32; + ${fileTokens.progressBarCircularHeight}: 2rem; + ${fileTokens.progressBarCircularWidth}: 2rem; + ${fileTokens.progressBarCircularStrokeWidth}: 0.125rem; + ${fileTokens.progressBarCircularStrokeSize}: 2; + ${fileTokens.progressBarCircularContentFontFamily}: var(--plasma-typo-body-xs-font-family); + ${fileTokens.progressBarCircularContentFontSize}: var(--plasma-typo-body-xs-font-size); + ${fileTokens.progressBarCircularContentFontStyle}: var(--plasma-typo-body-xs-font-style); + ${fileTokens.progressBarCircularContentFontWeight}: var(--plasma-typo-body-xs-font-weight); + ${fileTokens.progressBarCircularContentLetterSpacing}: var(--plasma-typo-body-xs-letter-spacing); + ${fileTokens.progressBarCircularContentLineHeight}: var(--plasma-typo-body-xs-line-height); + + ${fileTokens.progressTrackHeight}: 0.25rem; + ${fileTokens.progressTrackBorderRadius}: 0.125rem; + ${fileTokens.progressFilledHeight}: 0.375rem; + ${fileTokens.progressFilledBorderRadius}: 0.375rem; + + ${fileTokens.fileLinearLoaderHeight}: 0.75rem; + `, + xs: css` + ${fileTokens.fileHeight}: 2rem; + ${fileTokens.filePadding}: 0rem; + ${fileTokens.fileRadius}: 0rem; + + ${fileTokens.fileLabelFontFamily}: var(--plasma-typo-body-xs-font-family); + ${fileTokens.fileLabelFontSize}: var(--plasma-typo-body-xs-font-size); + ${fileTokens.fileLabelFontStyle}: var(--plasma-typo-body-xs-font-style); + ${fileTokens.fileLabelFontWeight}: var(--plasma-typo-body-xs-font-weight); + ${fileTokens.fileLabelLetterSpacing}: var(--plasma-typo-body-xs-letter-spacing); + ${fileTokens.fileLabelLineHeight}: var(--plasma-typo-body-xs-line-height); + + ${fileTokens.fileDescriptionFontFamily}: var(--plasma-typo-body-xxs-font-family); + ${fileTokens.fileDescriptionFontSize}: var(--plasma-typo-body-xxs-font-size); + ${fileTokens.fileDescriptionFontStyle}: var(--plasma-typo-body-xxs-font-style); + ${fileTokens.fileDescriptionFontWeight}: var(--plasma-typo-body-xxs-font-weight); + ${fileTokens.fileDescriptionLetterSpacing}: var(--plasma-typo-body-xxs-letter-spacing); + ${fileTokens.fileDescriptionLineHeight}: var(--plasma-typo-body-xxs-line-height); + + ${fileTokens.fileThumbSize}: 1.75rem; + ${fileTokens.fileThumbMargin}: 0 0.375rem 0 0; + ${fileTokens.fileThumbRadius}: 0.25rem; + + ${fileTokens.fileActionMargin}: 0 0 0 0.375rem; + ${fileTokens.fileActionSize}: 1.25rem; + + ${fileTokens.iconCloseSize}: 0.75rem; + ${fileTokens.iconCloseColor}: var(--text-primary); + + ${fileTokens.progressBarCircularSize}: 24; + ${fileTokens.progressBarCircularHeight}: 1.5rem; + ${fileTokens.progressBarCircularWidth}: 1.5rem; + ${fileTokens.progressBarCircularStrokeWidth}: 0.125rem; + ${fileTokens.progressBarCircularStrokeSize}: 2; + ${fileTokens.progressBarCircularContentFontFamily}: var(--plasma-typo-body-xxs-font-family); + ${fileTokens.progressBarCircularContentFontSize}: var(--plasma-typo-body-xxs-font-size); + ${fileTokens.progressBarCircularContentFontStyle}: var(--plasma-typo-body-xxs-font-style); + ${fileTokens.progressBarCircularContentFontWeight}: var(--plasma-typo-body-xxs-font-weight); + ${fileTokens.progressBarCircularContentLetterSpacing}: var(--plasma-typo-body-xxs-letter-spacing); + ${fileTokens.progressBarCircularContentLineHeight}: var(--plasma-typo-body-xxs-line-height); + + ${fileTokens.progressTrackHeight}: 0.25rem; + ${fileTokens.progressTrackBorderRadius}: 0.125rem; + ${fileTokens.progressFilledHeight}: 0.375rem; + ${fileTokens.progressFilledBorderRadius}: 0.375rem; + + ${fileTokens.fileLinearLoaderHeight}: 0.75rem; + `, + }, + }, +}; diff --git a/packages/plasma-new-hope/src/examples/components/File/File.stories.tsx b/packages/plasma-new-hope/src/examples/components/File/File.stories.tsx new file mode 100644 index 0000000000..a05e15c733 --- /dev/null +++ b/packages/plasma-new-hope/src/examples/components/File/File.stories.tsx @@ -0,0 +1,126 @@ +import React, { ComponentProps } from 'react'; +import type { StoryObj, Meta } from '@storybook/react-vite'; +import { InSpacingDecorator, getConfigVariations } from '@salutejs/plasma-sb-utils'; + +import { WithTheme } from '../../_helpers'; +import { IconFolder, IconDownload, IconTrash } from '../../../components/_Icon'; + +import { File } from './File'; +import { config } from './File.config'; + +const { views, sizes } = getConfigVariations(config); + +type StoryPropsDefault = ComponentProps; + +const meta: Meta = { + title: 'Data Display/File', + component: File, + decorators: [WithTheme, InSpacingDecorator], + argTypes: { + view: { + options: views, + control: { + type: 'select', + }, + }, + size: { + options: sizes, + control: { + type: 'select', + }, + }, + label: { + control: { + type: 'text', + }, + }, + description: { + control: { + type: 'text', + }, + }, + fileUrl: { + control: { + type: 'text', + }, + }, + thumbUrl: { + control: { + type: 'text', + }, + }, + thumbIcon: { + control: { + type: 'boolean', + }, + mapping: { + true: , + false: undefined, + }, + }, + actionPlacement: { + options: ['left', 'right'], + control: { + type: 'radio', + }, + }, + loaderType: { + options: ['circular', 'linear'], + control: { + type: 'radio', + }, + }, + isLoading: { + control: { + type: 'boolean', + }, + }, + loaderValue: { + control: { + type: 'range', + min: 0, + max: 100, + step: 1, + }, + if: { + arg: 'isLoading', + eq: true, + }, + }, + disabled: { + control: { + type: 'boolean', + }, + }, + actionContent: { + options: ['none', 'download', 'trash'], + control: { + type: 'select', + }, + mapping: { + none: undefined, + download: , + trash: , + }, + }, + }, +}; + +export default meta; + +const TXTIcon = () => {}; + +export const Default: StoryObj = { + args: { + view: 'default', + size: 'm', + label: 'Document.pdf', + description: 2048576, + fileUrl: 'Document.pdf', + actionPlacement: 'right', + actionContent: 'download', + isLoading: false, + loaderValue: 0, + disabled: false, + }, +}; diff --git a/packages/plasma-new-hope/src/examples/components/File/File.ts b/packages/plasma-new-hope/src/examples/components/File/File.ts new file mode 100644 index 0000000000..f82c1cc53c --- /dev/null +++ b/packages/plasma-new-hope/src/examples/components/File/File.ts @@ -0,0 +1,8 @@ +import { fileConfig } from '../../../components/File'; +import { component, mergeConfig } from '../../../engines'; + +import { config } from './File.config'; + +const mergedConfig = mergeConfig(fileConfig, config); + +export const File = component(mergedConfig);