@synerise/ds-file-uploader 2.0.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [2.1.0](https://github.com/Synerise/synerise-design/compare/@synerise/ds-file-uploader@2.0.1...@synerise/ds-file-uploader@2.1.0) (2026-09-05)
7
+
8
+ ### Bug Fixes
9
+
10
+ - address review on the stored-file thumbnail ([2856afa](https://github.com/Synerise/synerise-design/commit/2856afa081bf2edf7887c1102494adf497da3253))
11
+ - render previewUrl in the avatar uploader's file row ([414f004](https://github.com/Synerise/synerise-design/commit/414f0043a067b4beb34f464296d9f8eacb418a8e))
12
+
13
+ ### Features
14
+
15
+ - render a stored file's url as the uploader thumbnail ([bd1a6eb](https://github.com/Synerise/synerise-design/commit/bd1a6eb4094f0ca380585dc98168f76dfae4b61e))
16
+
6
17
  ## [2.0.1](https://github.com/Synerise/synerise-design/compare/@synerise/ds-file-uploader@2.0.0...@synerise/ds-file-uploader@2.0.1) (2026-09-02)
7
18
 
8
19
  **Note:** Version bump only for package @synerise/ds-file-uploader
package/CLAUDE.md CHANGED
@@ -46,30 +46,32 @@ src/
46
46
 
47
47
  Full-width drop-zone uploader. `forwardRef<FileUploaderRef, FileUploaderProps>`.
48
48
 
49
- | Prop | Type | Default | Description |
50
- |------|------|---------|-------------|
51
- | `mode` | `'single' \| 'multi-medium' \| 'multi-large'` | `'single'` | Layout/behaviour mode. `multi-large` shows a large 160px drop area |
52
- | `files` | `ExtendedFile[]` | `[]` | Controlled list of uploaded files to display |
53
- | `onUpload` | `(files: FileWithContent[]) => void` | `undefined` | Called after drop/select with files enriched with `.content` |
54
- | `onRemove` | `(file: FileWithContent, index: number) => void` | `undefined` | Called when user removes a file row |
55
- | `accept` | `string[]` | `undefined` | Accepted MIME types (e.g. `['image/png', 'application/pdf']`) |
56
- | `filesAmount` | `number` | `undefined` | Maximum number of files. Throws if set to `< 1`. Drop zone hides once limit is reached |
57
- | `removable` | `boolean` | `true` | Show remove button on file rows |
58
- | `disabled` | `boolean` | `undefined` | Disables the drop zone and file rows |
59
- | `error` | `string` | `undefined` | Displays a global error message below the drop zone |
60
- | `retry` | `boolean` | `undefined` | When `true` and a file has an error, shows a Retry button that re-opens the file dialog |
61
- | `hideSize` | `boolean` | `undefined` | Hides the file size row on file items (per-file errors still render). Useful for backend placeholders with `size: 0` |
62
- | `label` | `string` | `undefined` | Label above the drop zone |
63
- | `tooltip` | `string` | `undefined` | Tooltip (ℹ icon) shown next to the label (requires `label`) |
64
- | `description` | `string` | `undefined` | Hint text below the error area |
65
- | `texts` | `FileUploaderTexts` | `undefined` | i18n overrides (see below). Falls back to `react-intl` message IDs |
66
- | `className` | `string` | `undefined` | Extra CSS class; merged with `'ds-file-uploader'` |
67
- | + HTML div attrs | — | — | Spread onto the outer `<div>` container |
49
+ | Prop | Type | Default | Description |
50
+ | ---------------- | ------------------------------------------------ | ----------- | -------------------------------------------------------------------------------------------------------------------- |
51
+ | `mode` | `'single' \| 'multi-medium' \| 'multi-large'` | `'single'` | Layout/behaviour mode. `multi-large` shows a large 160px drop area |
52
+ | `files` | `ExtendedFile[]` | `[]` | Controlled list of uploaded files to display |
53
+ | `onUpload` | `(files: FileWithContent[]) => void` | `undefined` | Called after drop/select with files enriched with `.content` |
54
+ | `onRemove` | `(file: FileWithContent, index: number) => void` | `undefined` | Called when user removes a file row |
55
+ | `accept` | `string[]` | `undefined` | Accepted MIME types (e.g. `['image/png', 'application/pdf']`) |
56
+ | `filesAmount` | `number` | `undefined` | Maximum number of files. Throws if set to `< 1`. Drop zone hides once limit is reached |
57
+ | `removable` | `boolean` | `true` | Show remove button on file rows |
58
+ | `disabled` | `boolean` | `undefined` | Disables the drop zone and file rows |
59
+ | `error` | `string` | `undefined` | Displays a global error message below the drop zone |
60
+ | `retry` | `boolean` | `undefined` | When `true` and a file has an error, shows a Retry button that re-opens the file dialog |
61
+ | `hideSize` | `boolean` | `undefined` | Hides the file size row on file items (per-file errors still render). Useful for backend placeholders with `size: 0` |
62
+ | `label` | `string` | `undefined` | Label above the drop zone |
63
+ | `tooltip` | `string` | `undefined` | Tooltip (ℹ icon) shown next to the label (requires `label`) |
64
+ | `description` | `string` | `undefined` | Hint text below the error area |
65
+ | `texts` | `FileUploaderTexts` | `undefined` | i18n overrides (see below). Falls back to `react-intl` message IDs |
66
+ | `className` | `string` | `undefined` | Extra CSS class; merged with `'ds-file-uploader'` |
67
+ | + HTML div attrs | — | — | Spread onto the outer `<div>` container |
68
68
 
69
69
  **Imperative ref** (`FileUploaderRef`):
70
+
70
71
  ```ts
71
72
  { open: () => void; inputRef: RefObject<HTMLInputElement>; rootRef: RefObject<HTMLElement> }
72
73
  ```
74
+
73
75
  Call `ref.current.open()` to programmatically open the file picker.
74
76
 
75
77
  ### `AvatarUploader`
@@ -79,6 +81,7 @@ Call `ref.current.open()` to programmatically open the file picker.
79
81
  ### `ItemUploader`
80
82
 
81
83
  `forwardRef<FileUploaderRef, ItemUploaderProps>`. Same as `FileUploader` except:
84
+
82
85
  - `mode` type: `'single' | 'multi'` (not `'multi-medium' | 'multi-large'`)
83
86
  - Shows a ghost-primary "Add file" button instead of a drop-zone area. Button label is **hardcoded** — not driven by `texts`.
84
87
  - Does not render a `description` below error messages (it's passed to `FileViewItem` but `FileViewItem` doesn't render it).
@@ -86,6 +89,7 @@ Call `ref.current.open()` to programmatically open the file picker.
86
89
  ### `FileUploaderStyles`
87
90
 
88
91
  Aggregated style object exported for consumers who need to extend or override styled-components:
92
+
89
93
  ```ts
90
94
  {
91
95
  FileUploader: FileUploaderStyles,
@@ -97,30 +101,30 @@ Aggregated style object exported for consumers who need to extend or override st
97
101
 
98
102
  ### Types exported
99
103
 
100
- | Type | Description |
101
- |------|-------------|
102
- | `FileUploaderProps` | Main props interface (extends `WithHTMLAttributes<HTMLDivElement, ..>`) |
103
- | `ItemUploaderProps` | Same as `FileUploaderProps` with narrower `mode` |
104
- | `ExtendedFile` | `{ file: FileWithContent; error?: string; disabled?: boolean; progress?: number; success?: boolean }` |
105
- | `FileWithContent` | `File & { content?: FileContent }` |
106
- | `FileContent` | `string \| ArrayBuffer \| null` |
107
- | `FileUploaderRef` | `{ open; inputRef; rootRef }` |
104
+ | Type | Description |
105
+ | ------------------- | -------------------------------------------------------------------------------------------------------------------------- |
106
+ | `FileUploaderProps` | Main props interface (extends `WithHTMLAttributes<HTMLDivElement, ..>`) |
107
+ | `ItemUploaderProps` | Same as `FileUploaderProps` with narrower `mode` |
108
+ | `ExtendedFile` | `{ file: FileWithContent; error?: string; disabled?: boolean; progress?: number; success?: boolean; previewUrl?: string }` |
109
+ | `FileWithContent` | `File & { content?: FileContent }` |
110
+ | `FileContent` | `string \| ArrayBuffer \| null` |
111
+ | `FileUploaderRef` | `{ open; inputRef; rootRef }` |
108
112
 
109
113
  ### `FileUploaderTexts` shape
110
114
 
111
- | Key | Default (react-intl) | Description |
112
- |-----|----------------------|-------------|
113
- | `buttonLabel` | `"Upload file"` | Drop-zone button label (compact mode) |
114
- | `buttonLabelLarge` | `"Upload file"` | Drop-zone label (multi-large, no files) |
115
- | `buttonDescription` | `"Description"` | Drop-zone sub-label (multi-large) |
116
- | `size` | `"Size"` | Prefix before file size in file row |
117
- | `removeTooltip` | `"Remove"` | Tooltip on remove icon |
118
- | `cancelText` | `"Cancel"` | Popconfirm cancel button |
119
- | `okText` | `"OK"` | Popconfirm confirm button |
120
- | `removeConfirmTitle` | `"Remove"` | Popconfirm title |
121
- | `fileWeight` | `"File weight"` | Label for file weight during upload |
122
- | `retryLabel` | `"Retry"` | Label on retry button |
123
- | `percent` | — | Current upload percentage (number) — passed to `ProgressBar` |
115
+ | Key | Default (react-intl) | Description |
116
+ | -------------------- | -------------------- | ------------------------------------------------------------ |
117
+ | `buttonLabel` | `"Upload file"` | Drop-zone button label (compact mode) |
118
+ | `buttonLabelLarge` | `"Upload file"` | Drop-zone label (multi-large, no files) |
119
+ | `buttonDescription` | `"Description"` | Drop-zone sub-label (multi-large) |
120
+ | `size` | `"Size"` | Prefix before file size in file row |
121
+ | `removeTooltip` | `"Remove"` | Tooltip on remove icon |
122
+ | `cancelText` | `"Cancel"` | Popconfirm cancel button |
123
+ | `okText` | `"OK"` | Popconfirm confirm button |
124
+ | `removeConfirmTitle` | `"Remove"` | Popconfirm title |
125
+ | `fileWeight` | `"File weight"` | Label for file weight during upload |
126
+ | `retryLabel` | `"Retry"` | Label on retry button |
127
+ | `percent` | — | Current upload percentage (number) — passed to `ProgressBar` |
124
128
 
125
129
  ## Usage patterns
126
130
 
@@ -190,4 +194,27 @@ Styles in `FileUploader.styles.ts` (and per-variant siblings). Uses `props.theme
190
194
  - **`retry` in `FileView`:** When `retry=true` and a file row has `error`, the retry button spreads `getRootProps()` (from `useDropzone`) to open the file picker — it first calls `onRemove` to clear the errored entry, then re-opens the dialog.
191
195
  - **`texts.percent`** is typed `number` (not `ReactNode`) because it's passed directly to `ProgressBar`'s `percent` prop.
192
196
  - **`FileViewAvatarTexts`** is not exported from `index.ts` — accessible only as a deep import.
197
+ - **`previewUrl` renders a file the browser cannot read.** A file already in remote storage is
198
+ usually reachable only as a url: storage buckets commonly answer without CORS headers, so
199
+ `fetch` fails on preflight and the url cannot be turned back into a `File`. Consumers therefore
200
+ pass a zero-byte placeholder `File` for the name and `previewUrl` for the picture — an `<img>`
201
+ needs no CORS. All three variants honour it, and `FileViewAvatar` honours it twice: once for the
202
+ avatar background and once for the file row beside it, each of which has its own thumbnail
203
+ branch. Pair it with `hideSize` in `FileUploader` only — `AvatarUploader` and `ItemUploader`
204
+ render no size. It also sidesteps the `URL.createObjectURL` leak noted above for that file, since
205
+ no object url is created when `previewUrl` is set.
206
+ - **Thumbnail size is per variant, deliberately.** `PreviewThumbnail` matches the footprint of the
207
+ glyph branch it replaces in that variant — 40px with `PreviewImage`'s negative margins in
208
+ `FileView`, 24px in `FileViewAvatar`, 20px in `FileViewItem`, whose row is a fixed 32px tall.
209
+ Using one size everywhere makes a list that mixes a stored file with a picked one render two
210
+ different shapes. Note the component is already uneven here: the image-mime branch is 40px in
211
+ `FileView` while `PlaceholderImage` is 32px in all three.
212
+ - **`toCssUrl` guards the avatar background.** `FileViewAvatar` interpolates the source into a CSS
213
+ `url('…')`. That was safe while it was always a locally created `blob:` url; `previewUrl` is
214
+ consumer input, and a value containing `')` would close the `url()` and inject declarations.
215
+ `encodeURI` alone does not help — it leaves `'`, `"`, `(` and `)` intact — so those four are
216
+ percent-encoded on top of it.
217
+ - **`data-testid` on both thumbnail and glyph.** `file-preview-thumbnail` and `file-mime-glyph`
218
+ exist so specs need not match generated styled-components class names, which only carry readable
219
+ names because `babel-plugin-styled-components` runs with `displayName: true`.
193
220
  - **Uses Vitest** for testing.
package/README.md CHANGED
@@ -39,33 +39,34 @@ import FileUploader from '@synerise/ds-file-uploader'
39
39
 
40
40
  ## FileUploader
41
41
 
42
- | Property | Description | Type | Default |
43
- | ----------- | -------------------------------------------------------------------------------- | ----------------------------------------- | -------- |
44
- | mode | uploader operation mode | `single` / `multi-medium` / `multi-large` | `single` |
45
- | filesAmount | uploader maximum files number | number | - |
46
- | description | description shown with uploader | string | - |
47
- | disabled | whether the uploader should be disabled | boolean | - |
48
- | removable | whether or not files should be removable | boolean | `true` |
49
- | label | label shown on top of the uploader | string | - |
50
- | tooltip | display a tooltip near the label (label value is required for tooltip to appear) | string | - |
51
- | error | display general error | string | - |
52
- | retry | when true and a file has an error, shows a retry button that re-opens the dialog | boolean | - |
53
- | hideSize | hides the file size row on file items (per-file errors still render) | boolean | - |
54
- | texts | texts to display | FileUploaderTexts | - |
55
- | files | uploaded files | ExtendedFile[] | `[]` |
56
- | accept | accepted mime types to upload | string[] | - |
57
- | onRemove | event fired when a file is removed | (file: FileWithContent, index: number) => void | - |
58
- | onUpload | event fired when files are selected or dropped | (files: FileWithContent[]) => void | - |
42
+ | Property | Description | Type | Default |
43
+ | ----------- | -------------------------------------------------------------------------------- | ---------------------------------------------- | -------- |
44
+ | mode | uploader operation mode | `single` / `multi-medium` / `multi-large` | `single` |
45
+ | filesAmount | uploader maximum files number | number | - |
46
+ | description | description shown with uploader | string | - |
47
+ | disabled | whether the uploader should be disabled | boolean | - |
48
+ | removable | whether or not files should be removable | boolean | `true` |
49
+ | label | label shown on top of the uploader | string | - |
50
+ | tooltip | display a tooltip near the label (label value is required for tooltip to appear) | string | - |
51
+ | error | display general error | string | - |
52
+ | retry | when true and a file has an error, shows a retry button that re-opens the dialog | boolean | - |
53
+ | hideSize | hides the file size row on file items (per-file errors still render) | boolean | - |
54
+ | texts | texts to display | FileUploaderTexts | - |
55
+ | files | uploaded files | ExtendedFile[] | `[]` |
56
+ | accept | accepted mime types to upload | string[] | - |
57
+ | onRemove | event fired when a file is removed | (file: FileWithContent, index: number) => void | - |
58
+ | onUpload | event fired when files are selected or dropped | (files: FileWithContent[]) => void | - |
59
59
 
60
60
  ## ExtendedFile
61
61
 
62
- | Property | Description | Type | Default |
63
- | -------- | ------------------------------------------ | --------------- | ------- |
64
- | file | actual file | FileWithContent | - |
65
- | disabled | whether or not the file should be disabled | boolean | - |
66
- | error | file error text | string | - |
67
- | progress | upload progress | number | - |
68
- | success | upload success | boolean | - |
62
+ | Property | Description | Type | Default |
63
+ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | ------- |
64
+ | file | actual file | FileWithContent | - |
65
+ | disabled | whether or not the file should be disabled | boolean | - |
66
+ | error | file error text | string | - |
67
+ | progress | upload progress | number | - |
68
+ | success | upload success | boolean | - |
69
+ | previewUrl | url rendered as the file thumbnail instead of the mime-type glyph — for a file already stored elsewhere, whose bytes the browser cannot fetch back (remote storage usually answers without CORS headers). In `FileUploader` pair it with `hideSize`, since the placeholder `file` carries no bytes; `AvatarUploader` and `ItemUploader` render no size. | string | - |
69
70
 
70
71
  ## FileWithContent (extends [File](https://developer.mozilla.org/pl/docs/Web/API/File))
71
72
 
@@ -75,16 +76,16 @@ import FileUploader from '@synerise/ds-file-uploader'
75
76
 
76
77
  ## FileUploaderTexts
77
78
 
78
- | Property | Description | Type | Default |
79
- | ----------------- | -------------------------------------------------------- | ------------------------ | ------- |
80
- | buttonLabel | upload button label (compact drop zone) | string / React.ReactNode | - |
81
- | buttonLabelLarge | upload label (multi-large mode, no files uploaded yet) | string / React.ReactNode | - |
82
- | buttonDescription | upload button description (multi-large mode) | string / React.ReactNode | - |
83
- | size | file size label prefix | string / React.ReactNode | - |
84
- | removeTooltip | tooltip text on the remove icon | string / React.ReactNode | - |
85
- | cancelText | popconfirm cancel button label | string / React.ReactNode | - |
86
- | okText | popconfirm confirm button label | string / React.ReactNode | - |
87
- | removeConfirmTitle| popconfirm title | string / React.ReactNode | - |
88
- | fileWeight | label for file weight during upload | string / React.ReactNode | - |
89
- | retryLabel | label on the retry button | string / React.ReactNode | - |
90
- | percent | current upload percentage (passed to ProgressBar) | number | - |
79
+ | Property | Description | Type | Default |
80
+ | ------------------ | ------------------------------------------------------ | ------------------------ | ------- |
81
+ | buttonLabel | upload button label (compact drop zone) | string / React.ReactNode | - |
82
+ | buttonLabelLarge | upload label (multi-large mode, no files uploaded yet) | string / React.ReactNode | - |
83
+ | buttonDescription | upload button description (multi-large mode) | string / React.ReactNode | - |
84
+ | size | file size label prefix | string / React.ReactNode | - |
85
+ | removeTooltip | tooltip text on the remove icon | string / React.ReactNode | - |
86
+ | cancelText | popconfirm cancel button label | string / React.ReactNode | - |
87
+ | okText | popconfirm confirm button label | string / React.ReactNode | - |
88
+ | removeConfirmTitle | popconfirm title | string / React.ReactNode | - |
89
+ | fileWeight | label for file weight during upload | string / React.ReactNode | - |
90
+ | retryLabel | label on the retry button | string / React.ReactNode | - |
91
+ | percent | current upload percentage (passed to ProgressBar) | number | - |
@@ -4,8 +4,8 @@ import { FormattedMessage } from "react-intl";
4
4
  import Icon, { Close3M, FileM, RepeatM } from "@synerise/ds-icon";
5
5
  import Tooltip from "@synerise/ds-tooltip";
6
6
  import { ICON_MAP } from "./FileViewAvatar.const.js";
7
- import { FileAvatarContainer, AvatarContainer, RemoveWrapper, FileViewContainer, FileView, PreviewImage, PlaceholderImage, Info, Name, LoaderIcon, SmallLoader, RepeatIcon, RemoveButtonWrapper, DescriptionUploader } from "./FileViewAvatar.styles.js";
8
- import { isPreviewableMimeType } from "./FileViewAvatar.util.js";
7
+ import { FileAvatarContainer, AvatarContainer, RemoveWrapper, FileViewContainer, FileView, PreviewThumbnail, PreviewImage, PlaceholderImage, Info, Name, LoaderIcon, SmallLoader, RepeatIcon, RemoveButtonWrapper, DescriptionUploader } from "./FileViewAvatar.styles.js";
8
+ import { toCssUrl, isPreviewableMimeType } from "./FileViewAvatar.util.js";
9
9
  const FileViewAvatar = ({
10
10
  data,
11
11
  texts,
@@ -17,9 +17,10 @@ const FileViewAvatar = ({
17
17
  disabled,
18
18
  error,
19
19
  file,
20
+ previewUrl,
20
21
  progress
21
22
  } = data;
22
- const fileSource = URL.createObjectURL(data.file);
23
+ const fileSource = toCssUrl(previewUrl ?? URL.createObjectURL(file));
23
24
  const finalTexts = {
24
25
  retryTooltip: /* @__PURE__ */ jsx(FormattedMessage, { id: "DS.FILE-UPLOADER.RETRY-TOOLTIP", defaultMessage: "Retry" }),
25
26
  removeTooltip: /* @__PURE__ */ jsx(FormattedMessage, { id: "DS.FILE-UPLOADER.REMOVE-TOOLTIP", defaultMessage: "Remove" }),
@@ -41,7 +42,8 @@ const FileViewAvatar = ({
41
42
  /* @__PURE__ */ jsx(AvatarContainer, { removable, disabled, source: fileSource, children: removable && !disabled && !error && !hasProgress && /* @__PURE__ */ jsx(RemoveWrapper, { onClick: handleRemoveAvatar, onMouseDown: () => setPressed(true), pressed, "data-testid": "file-view-avatar-remove", children: /* @__PURE__ */ jsx(Tooltip, { title: finalTexts.removeTooltip, children: /* @__PURE__ */ jsx(Icon, { component: /* @__PURE__ */ jsx(Close3M, {}), size: 24 }) }) }) }),
42
43
  /* @__PURE__ */ jsxs(FileViewContainer, { children: [
43
44
  /* @__PURE__ */ jsx(Tooltip, { title: file.name, children: /* @__PURE__ */ jsxs(FileView, { progress: hasProgress, disabled, error: hasError, removable, type: "button", children: [
44
- isPreviewableMimeType(file.type) ? /* @__PURE__ */ jsx(PreviewImage, { children: /* @__PURE__ */ jsx(Icon, { component: ICON_MAP[file.type], size: 24 }) }) : /* @__PURE__ */ jsx(PlaceholderImage, { children: /* @__PURE__ */ jsx(Icon, { component: /* @__PURE__ */ jsx(FileM, {}), size: 24 }) }),
45
+ previewUrl && /* @__PURE__ */ jsx(PreviewThumbnail, { src: previewUrl, alt: "", "data-testid": "file-preview-thumbnail" }),
46
+ !previewUrl && (isPreviewableMimeType(file.type) ? /* @__PURE__ */ jsx(PreviewImage, { "data-testid": "file-mime-glyph", children: /* @__PURE__ */ jsx(Icon, { component: ICON_MAP[file.type], size: 24 }) }) : /* @__PURE__ */ jsx(PlaceholderImage, { "data-testid": "file-mime-glyph", children: /* @__PURE__ */ jsx(Icon, { component: /* @__PURE__ */ jsx(FileM, {}), size: 24 }) })),
45
47
  /* @__PURE__ */ jsx(Info, { children: /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(Name, { children: file.name }) }) }),
46
48
  hasProgress && /* @__PURE__ */ jsx(LoaderIcon, { children: /* @__PURE__ */ jsx(SmallLoader, { color: "blue", size: "S" }) }),
47
49
  error && /* @__PURE__ */ jsx(Tooltip, { title: finalTexts.retryTooltip, children: /* @__PURE__ */ jsx(RepeatIcon, { children: /* @__PURE__ */ jsx(Icon, { component: /* @__PURE__ */ jsx(RepeatM, {}) }) }) }),
@@ -6,6 +6,7 @@ export declare const SmallLoader: import('styled-components').StyledComponent<"d
6
6
  size?: keyof typeof import('@synerise/ds-loader/dist/Loader.types').LoaderSize;
7
7
  color: string;
8
8
  }, never>;
9
+ export declare const PreviewThumbnail: import('styled-components').StyledComponent<"img", any, {}, never>;
9
10
  export declare const PlaceholderImage: import('styled-components').StyledComponent<"div", any, {}, never>;
10
11
  export declare const FileAvatarContainer: import('styled-components').StyledComponent<"div", any, {}, never>;
11
12
  export declare const FileViewContainer: import('styled-components').StyledComponent<"div", any, {}, never>;
@@ -19,48 +19,52 @@ const SmallLoader = /* @__PURE__ */ styled(Loader).withConfig({
19
19
  displayName: "FileViewAvatarstyles__SmallLoader",
20
20
  componentId: "sc-1jbyu2z-3"
21
21
  })(["border:1px solid ", ";border-top:2px solid transparent;border-radius:50%;animation:", " 2s linear infinite;"], (props) => props.theme.palette[`${props.color}-600`], spinnerAnimation);
22
+ const PreviewThumbnail = /* @__PURE__ */ styled.img.withConfig({
23
+ displayName: "FileViewAvatarstyles__PreviewThumbnail",
24
+ componentId: "sc-1jbyu2z-4"
25
+ })(["width:24px;height:24px;border-radius:3px;object-fit:contain;background-color:", ";"], (props) => props.theme.palette["grey-200"]);
22
26
  const PlaceholderImage = /* @__PURE__ */ styled.div.withConfig({
23
27
  displayName: "FileViewAvatarstyles__PlaceholderImage",
24
- componentId: "sc-1jbyu2z-4"
28
+ componentId: "sc-1jbyu2z-5"
25
29
  })(["background-color:", ";width:32px;height:32px;border-radius:3px;padding:4px;", "{color:", ";}"], (props) => props.theme.palette["grey-200"], IconContainer, (props) => props.theme.palette["grey-500"]);
26
30
  const FileAvatarContainer = /* @__PURE__ */ styled.div.withConfig({
27
31
  displayName: "FileViewAvatarstyles__FileAvatarContainer",
28
- componentId: "sc-1jbyu2z-5"
32
+ componentId: "sc-1jbyu2z-6"
29
33
  })(["display:flex;"]);
30
34
  const FileViewContainer = /* @__PURE__ */ styled.div.withConfig({
31
35
  displayName: "FileViewAvatarstyles__FileViewContainer",
32
- componentId: "sc-1jbyu2z-6"
36
+ componentId: "sc-1jbyu2z-7"
33
37
  })([""]);
34
38
  const Info = /* @__PURE__ */ styled.div.withConfig({
35
39
  displayName: "FileViewAvatarstyles__Info",
36
- componentId: "sc-1jbyu2z-7"
40
+ componentId: "sc-1jbyu2z-8"
37
41
  })(["overflow:hidden;margin:0 0 0 4px;width:100%;"]);
38
42
  const DescriptionUploader = /* @__PURE__ */ styled(Description).withConfig({
39
43
  displayName: "FileViewAvatarstyles__DescriptionUploader",
40
- componentId: "sc-1jbyu2z-8"
44
+ componentId: "sc-1jbyu2z-9"
41
45
  })(["margin:16px 0 8px 0;color:", ";"], (props) => props.theme.palette["grey-500"]);
42
46
  const Name = /* @__PURE__ */ styled(Label).withConfig({
43
47
  displayName: "FileViewAvatarstyles__Name",
44
- componentId: "sc-1jbyu2z-9"
48
+ componentId: "sc-1jbyu2z-10"
45
49
  })(["&&{color:", ";white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:260px;cursor:initial;}"], (props) => props.theme.palette["grey-600"]);
46
50
  const RemoveButtonWrapper = /* @__PURE__ */ styled.div.withConfig({
47
51
  displayName: "FileViewAvatarstyles__RemoveButtonWrapper",
48
- componentId: "sc-1jbyu2z-10"
52
+ componentId: "sc-1jbyu2z-11"
49
53
  })(["display:", ";background-color:transparent;z-index:10;border:0;padding:0;margin:0 4px 0 0;height:16px;width:16px;position:absolute;top:8px;right:5px;cursor:pointer;overflow:", ";", "{position:absolute;right:-2px;top:-2px;transition:fill 0.3s;color:", ";&:hover{color:", ";}}"], (props) => props.pressed ? "flex" : "none", (props) => props.pressed ? "visible" : "hidden", IconContainer, (props) => props.theme.palette["red-600"], (props) => props.theme.palette["red-500"]);
50
54
  const RemoveWrapper = /* @__PURE__ */ styled.div.withConfig({
51
55
  displayName: "FileViewAvatarstyles__RemoveWrapper",
52
- componentId: "sc-1jbyu2z-11"
56
+ componentId: "sc-1jbyu2z-12"
53
57
  })(["z-index:100;height:0;width:0;position:relative;bottom:10px;left:68px;cursor:pointer;overflow:hidden;", "{transition:fill 0.3s;color:", ";&:hover{color:", ";}}"], IconContainer, (props) => props.theme.palette["red-600"], (props) => props.theme.palette["red-600"]);
54
58
  const AvatarContainer = /* @__PURE__ */ styled.div.withConfig({
55
59
  displayName: "FileViewAvatarstyles__AvatarContainer",
56
- componentId: "sc-1jbyu2z-12"
60
+ componentId: "sc-1jbyu2z-13"
57
61
  })(["background:url('", "') 50% 50% no-repeat;background-size:cover;min-width:80px;min-height:80px;width:80px;height:80px;border-radius:3px;overflow:visible;margin-right:14px;", ";&:hover{", "{display:flex;overflow:visible;}}"], (props) => props.source, (props) => props.disabled && `
58
62
  background-color: ${props.theme.palette["grey-100"]};
59
63
  opacity: 0.4;
60
64
  `, RemoveWrapper);
61
65
  const FileView = /* @__PURE__ */ styled.button.withConfig({
62
66
  displayName: "FileViewAvatarstyles__FileView",
63
- componentId: "sc-1jbyu2z-13"
67
+ componentId: "sc-1jbyu2z-14"
64
68
  })(["background-color:", ";border-radius:3px;border:2px solid transparent;display:flex;align-items:center;padding:7px 12px 7px 6px;height:32px;text-align:left;line-height:initial;position:relative;margin:0 0 12px;&:last-of-type{margin:0;}&:hover{border-color:", ";padding-right:", ";", "{color:", ";}", "{", "{color:", ";}}", "}&:focus{border-color:", ";background-color:", ";}&:hover{background-color:", ";}&:active{border-color:", ";background-color:", ";", "{color:", ";}", "{", "{color:", ";}}}", ";", ";", ";"], (props) => props.theme.palette["grey-100"], (props) => props.theme.palette["grey-200"], (props) => props.removable ? "30px" : "12px", Name, (props) => props.theme.palette["blue-600"], PreviewImage, IconContainer, (props) => props.theme.palette["blue-600"], (props) => props.removable && !props.disabled && `
65
69
  ${RemoveButtonWrapper} {
66
70
  display: block;
@@ -93,6 +97,7 @@ export {
93
97
  Name,
94
98
  PlaceholderImage,
95
99
  PreviewImage,
100
+ PreviewThumbnail,
96
101
  RemoveButtonWrapper,
97
102
  RemoveWrapper,
98
103
  RepeatIcon,
@@ -1,2 +1,3 @@
1
1
  import { PreviewableMimeType } from './FileViewAvatar.types';
2
2
  export declare const isPreviewableMimeType: (mimeType: string) => mimeType is PreviewableMimeType;
3
+ export declare const toCssUrl: (source: string) => string;
@@ -2,6 +2,18 @@ import { previewableMimeTypes } from "./FileViewAvatar.const.js";
2
2
  const isPreviewableMimeType = (mimeType) => {
3
3
  return previewableMimeTypes.includes(mimeType);
4
4
  };
5
+ const CSS_URL_ESCAPES = {
6
+ "'": "%27",
7
+ '"': "%22",
8
+ "(": "%28",
9
+ ")": "%29",
10
+ "\\": "%5C",
11
+ "\n": "%0A",
12
+ "\r": "%0D",
13
+ " ": "%20"
14
+ };
15
+ const toCssUrl = (source) => source.replace(/['"()\\\n\r ]/g, (character) => CSS_URL_ESCAPES[character]);
5
16
  export {
6
- isPreviewableMimeType
17
+ isPreviewableMimeType,
18
+ toCssUrl
7
19
  };
@@ -18,6 +18,18 @@ export type ExtendedFile = {
18
18
  disabled?: boolean;
19
19
  progress?: number;
20
20
  success?: boolean;
21
+ /**
22
+ * Renders this url as the file's thumbnail instead of the mime-type glyph.
23
+ *
24
+ * For a file already stored elsewhere the consumer usually has its url but not its bytes —
25
+ * remote storage commonly answers without CORS headers, so the browser cannot fetch them back
26
+ * into a `File`. An `<img>` needs no CORS, so passing the url shows the real thumbnail while
27
+ * `file` stays a placeholder carrying just the name.
28
+ *
29
+ * In `FileUploader` pair it with `hideSize`, since a placeholder reports 0 B. `AvatarUploader`
30
+ * and `ItemUploader` render no size at all, so they need nothing extra.
31
+ */
32
+ previewUrl?: string;
21
33
  };
22
34
  export type FileWithContent = File & {
23
35
  content?: FileContent;
@@ -8,7 +8,7 @@ import Icon, { FileM, Close3M, RepeatM, Check3M, WarningFillM } from "@synerise/
8
8
  import ProgressBar from "@synerise/ds-progress-bar";
9
9
  import Tooltip from "@synerise/ds-tooltip";
10
10
  import { isPreviewableMimeType, ICON_MAP } from "./FileView.const.js";
11
- import { FileViewContainer, PreviewImage, PlaceholderImage, Info, Name, FileName, FileWeight, FlexRow, RemoveWrapper, SizeOrError, CheckButtonWrapper, PopconfirmOnRemove, RemoveButtonWrapper } from "./FileView.styles.js";
11
+ import { FileViewContainer, PreviewThumbnail, PreviewImage, PlaceholderImage, Info, Name, FileName, FileWeight, FlexRow, RemoveWrapper, SizeOrError, CheckButtonWrapper, PopconfirmOnRemove, RemoveButtonWrapper } from "./FileView.styles.js";
12
12
  const FileView = ({
13
13
  data,
14
14
  texts,
@@ -24,6 +24,7 @@ const FileView = ({
24
24
  disabled,
25
25
  error,
26
26
  file,
27
+ previewUrl,
27
28
  progress,
28
29
  success
29
30
  } = data;
@@ -44,7 +45,8 @@ const FileView = ({
44
45
  setPressed(false);
45
46
  };
46
47
  return /* @__PURE__ */ jsxs(FileViewContainer, { success, pressed, progress: hasProgress, disabled, error: hasError, removable, type: "button", children: [
47
- isPreviewableMimeType(file.type) ? /* @__PURE__ */ jsx(PreviewImage, { children: /* @__PURE__ */ jsx(Icon, { component: ICON_MAP[file.type], size: 40 }) }) : /* @__PURE__ */ jsx(PlaceholderImage, { children: /* @__PURE__ */ jsx(Icon, { component: /* @__PURE__ */ jsx(FileM, {}), size: 24 }) }),
48
+ previewUrl && /* @__PURE__ */ jsx(PreviewThumbnail, { src: previewUrl, alt: "", "data-testid": "file-preview-thumbnail" }),
49
+ !previewUrl && (isPreviewableMimeType(file.type) ? /* @__PURE__ */ jsx(PreviewImage, { "data-testid": "file-mime-glyph", children: /* @__PURE__ */ jsx(Icon, { component: ICON_MAP[file.type], size: 40 }) }) : /* @__PURE__ */ jsx(PlaceholderImage, { "data-testid": "file-mime-glyph", children: /* @__PURE__ */ jsx(Icon, { component: /* @__PURE__ */ jsx(FileM, {}), size: 24 }) })),
48
50
  /* @__PURE__ */ jsx(Info, { progress: hasProgress, children: hasProgress ? /* @__PURE__ */ jsxs(Fragment, { children: [
49
51
  /* @__PURE__ */ jsxs(Name, { children: [
50
52
  /* @__PURE__ */ jsx(FileName, { children: file.name }),
@@ -1,5 +1,6 @@
1
1
  export declare const PreviewImage: import('styled-components').StyledComponent<"div", any, {}, never>;
2
2
  export declare const PlaceholderImage: import('styled-components').StyledComponent<"div", any, {}, never>;
3
+ export declare const PreviewThumbnail: import('styled-components').StyledComponent<"img", any, {}, never>;
3
4
  export declare const Info: import('styled-components').StyledComponent<"div", any, {
4
5
  progress: boolean;
5
6
  }, never>;
@@ -10,49 +10,53 @@ const PlaceholderImage = /* @__PURE__ */ styled.div.withConfig({
10
10
  displayName: "FileViewstyles__PlaceholderImage",
11
11
  componentId: "sc-wpdc66-1"
12
12
  })(["background-color:", ";width:32px;height:32px;border-radius:3px;padding:4px;", "{color:", ";}"], (props) => props.theme.palette["grey-200"], IconContainer, (props) => props.theme.palette["grey-500"]);
13
+ const PreviewThumbnail = /* @__PURE__ */ styled.img.withConfig({
14
+ displayName: "FileViewstyles__PreviewThumbnail",
15
+ componentId: "sc-wpdc66-2"
16
+ })(["width:40px;height:40px;margin:-4px -8px -4px -4px;border-radius:3px;object-fit:contain;background-color:", ";"], (props) => props.theme.palette["grey-200"]);
13
17
  const Info = /* @__PURE__ */ styled.div.withConfig({
14
18
  displayName: "FileViewstyles__Info",
15
- componentId: "sc-wpdc66-2"
19
+ componentId: "sc-wpdc66-3"
16
20
  })(["overflow:hidden;margin:0 0 0 10px;width:", ";"], (props) => props.progress ? "100%" : "80%");
17
21
  const PopconfirmOnRemove = /* @__PURE__ */ styled(Popconfirm).withConfig({
18
22
  displayName: "FileViewstyles__PopconfirmOnRemove",
19
- componentId: "sc-wpdc66-3"
23
+ componentId: "sc-wpdc66-4"
20
24
  })([".ant-popover-buttons{.ant-btn-sm{&:first-of-type{padding-left:6px;}}}"]);
21
25
  const FileWeight = /* @__PURE__ */ styled.div.withConfig({
22
26
  displayName: "FileViewstyles__FileWeight",
23
- componentId: "sc-wpdc66-4"
27
+ componentId: "sc-wpdc66-5"
24
28
  })(["color:", ";padding-right:30px;font-weight:normal;font-size:13px;"], (props) => props.theme.palette["grey-500"]);
25
29
  const FileName = /* @__PURE__ */ styled.div.withConfig({
26
30
  displayName: "FileViewstyles__FileName",
27
- componentId: "sc-wpdc66-5"
31
+ componentId: "sc-wpdc66-6"
28
32
  })(["white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;"]);
29
33
  const Name = /* @__PURE__ */ styled(Label).withConfig({
30
34
  displayName: "FileViewstyles__Name",
31
- componentId: "sc-wpdc66-6"
35
+ componentId: "sc-wpdc66-7"
32
36
  })(["&&{color:", ";max-width:100%;cursor:initial;white-space:nowrap;display:flex;justify-content:space-between;}"], (props) => props.theme.palette["grey-600"]);
33
37
  const FlexRow = /* @__PURE__ */ styled.div.withConfig({
34
38
  displayName: "FileViewstyles__FlexRow",
35
- componentId: "sc-wpdc66-7"
39
+ componentId: "sc-wpdc66-8"
36
40
  })(["display:flex;"]);
37
41
  const SizeOrError = /* @__PURE__ */ styled(Text).withConfig({
38
42
  displayName: "FileViewstyles__SizeOrError",
39
- componentId: "sc-wpdc66-8"
43
+ componentId: "sc-wpdc66-9"
40
44
  })(["&&{color:", ";}"], (props) => props.theme.palette["grey-600"]);
41
45
  const RemoveWrapper = /* @__PURE__ */ styled.div.withConfig({
42
46
  displayName: "FileViewstyles__RemoveWrapper",
43
- componentId: "sc-wpdc66-9"
47
+ componentId: "sc-wpdc66-10"
44
48
  })(["display:flex;background-color:transparent;z-index:10;border:0;padding:0;margin:0;height:16px;width:16px;position:absolute;top:14px;right:10px;cursor:pointer;", "{position:absolute;right:-2px;top:-2px;transition:color 0.3s;color:", ";&:hover{color:", ";}}"], IconContainer, (props) => props.theme.palette["grey-300"], (props) => props.theme.palette["red-500"]);
45
49
  const CheckButtonWrapper = /* @__PURE__ */ styled.div.withConfig({
46
50
  displayName: "FileViewstyles__CheckButtonWrapper",
47
- componentId: "sc-wpdc66-10"
51
+ componentId: "sc-wpdc66-11"
48
52
  })(["display:flex;background-color:transparent;z-index:10;margin:0;height:16px;width:16px;position:absolute;top:14px;right:10px;cursor:pointer;", "{position:absolute;right:-2px;top:-2px;transition:color 0.3s;color:", ";&:hover{color:", ";}}"], IconContainer, (props) => props.theme.palette["green-600"], (props) => props.theme.palette["green-500"]);
49
53
  const RemoveButtonWrapper = /* @__PURE__ */ styled.div.withConfig({
50
54
  displayName: "FileViewstyles__RemoveButtonWrapper",
51
- componentId: "sc-wpdc66-11"
55
+ componentId: "sc-wpdc66-12"
52
56
  })(["display:", ";background-color:transparent;z-index:10;border:0;padding:0;margin:0;height:16px;width:16px;position:absolute;top:14px;right:10px;cursor:pointer;overflow:", ";", "{position:absolute;right:-2px;top:-2px;transition:color 0.3s;color:", ";&:hover{color:", ";}}"], (props) => props.pressed ? "flex" : "none", (props) => props.pressed ? "visible" : "hidden", IconContainer, (props) => props.theme.palette["red-600"], (props) => props.theme.palette["red-500"]);
53
57
  const FileViewContainer = /* @__PURE__ */ styled.button.withConfig({
54
58
  displayName: "FileViewstyles__FileViewContainer",
55
- componentId: "sc-wpdc66-12"
59
+ componentId: "sc-wpdc66-13"
56
60
  })(["background-color:", ";border-radius:3px;border:1px solid ", ";display:flex;align-items:center;padding:12px 6px;height:48px;width:100%;text-align:left;line-height:initial;position:relative;margin:0 0 12px;&:last-of-type{margin:0;}&:hover{border-color:", ";", "}&:hover{", "}&:focus{border-color:", ";background-color:", ";", "}&:hover{background-color:", ";}&:active{border-color:", ";background-color:", ";}", ";", ";.ant-progress-line{margin:8px 0 0 !important;width:93%;}"], (props) => props.theme.palette.white, (props) => props.theme.palette["grey-200"], (props) => props.theme.palette["grey-300"], (props) => props.removable && !props.disabled && `
57
61
  ${RemoveButtonWrapper} {
58
62
  display: block;
@@ -88,6 +92,7 @@ export {
88
92
  PlaceholderImage,
89
93
  PopconfirmOnRemove,
90
94
  PreviewImage,
95
+ PreviewThumbnail,
91
96
  RemoveButtonWrapper,
92
97
  RemoveWrapper,
93
98
  SizeOrError
@@ -4,7 +4,7 @@ import { FormattedMessage } from "react-intl";
4
4
  import Icon, { FileM, RepeatM, Close3M } from "@synerise/ds-icon";
5
5
  import Tooltip from "@synerise/ds-tooltip";
6
6
  import { isPreviewableMimeType, ICON_MAP } from "./FileViewItem.const.js";
7
- import { FileViewContainer, FileView, PreviewImage, PlaceholderImage, Info, Name, LoaderIcon, SmallLoader, RepeatIcon, RemoveButtonWrapper } from "./FileViewItem.styles.js";
7
+ import { FileViewContainer, FileView, PreviewThumbnail, PreviewImage, PlaceholderImage, Info, Name, LoaderIcon, SmallLoader, RepeatIcon, RemoveButtonWrapper } from "./FileViewItem.styles.js";
8
8
  const FileViewItem = ({
9
9
  data,
10
10
  texts,
@@ -15,6 +15,7 @@ const FileViewItem = ({
15
15
  disabled,
16
16
  error,
17
17
  file,
18
+ previewUrl,
18
19
  progress
19
20
  } = data;
20
21
  const finalTexts = {
@@ -30,7 +31,8 @@ const FileViewItem = ({
30
31
  removeButtonSetPressed(false);
31
32
  };
32
33
  return /* @__PURE__ */ jsx(FileViewContainer, { children: /* @__PURE__ */ jsx(Tooltip, { title: file.name, children: /* @__PURE__ */ jsxs(FileView, { progress: hasProgress, disabled, error: hasError, removable, type: "button", children: [
33
- isPreviewableMimeType(file.type) ? /* @__PURE__ */ jsx(PreviewImage, { children: /* @__PURE__ */ jsx(Icon, { component: ICON_MAP[file.type], size: 20 }) }) : /* @__PURE__ */ jsx(PlaceholderImage, { children: /* @__PURE__ */ jsx(Icon, { component: /* @__PURE__ */ jsx(FileM, {}), size: 24 }) }),
34
+ previewUrl && /* @__PURE__ */ jsx(PreviewThumbnail, { src: previewUrl, alt: "", "data-testid": "file-preview-thumbnail" }),
35
+ !previewUrl && (isPreviewableMimeType(file.type) ? /* @__PURE__ */ jsx(PreviewImage, { "data-testid": "file-mime-glyph", children: /* @__PURE__ */ jsx(Icon, { component: ICON_MAP[file.type], size: 20 }) }) : /* @__PURE__ */ jsx(PlaceholderImage, { "data-testid": "file-mime-glyph", children: /* @__PURE__ */ jsx(Icon, { component: /* @__PURE__ */ jsx(FileM, {}), size: 24 }) })),
34
36
  /* @__PURE__ */ jsx(Info, { children: /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(Name, { children: file.name }) }) }),
35
37
  hasProgress && /* @__PURE__ */ jsx(LoaderIcon, { children: /* @__PURE__ */ jsx(SmallLoader, { color: "blue", size: "S" }) }),
36
38
  error && /* @__PURE__ */ jsx(Tooltip, { title: finalTexts.retryTooltip, children: /* @__PURE__ */ jsx(RepeatIcon, { children: /* @__PURE__ */ jsx(Icon, { component: /* @__PURE__ */ jsx(RepeatM, {}) }) }) }),
@@ -6,6 +6,7 @@ export declare const SmallLoader: import('styled-components').StyledComponent<"d
6
6
  size?: keyof typeof import('@synerise/ds-loader/dist/Loader.types').LoaderSize;
7
7
  color: string;
8
8
  }, never>;
9
+ export declare const PreviewThumbnail: import('styled-components').StyledComponent<"img", any, {}, never>;
9
10
  export declare const PlaceholderImage: import('styled-components').StyledComponent<"div", any, {}, never>;
10
11
  export declare const FileViewContainer: import('styled-components').StyledComponent<"div", any, {}, never>;
11
12
  export declare const Info: import('styled-components').StyledComponent<"div", any, {}, never>;
@@ -19,29 +19,33 @@ const SmallLoader = /* @__PURE__ */ styled(Loader).withConfig({
19
19
  displayName: "FileViewItemstyles__SmallLoader",
20
20
  componentId: "sc-1rkoxpv-3"
21
21
  })(["border:1px solid ", ";border-top:2px solid transparent;border-radius:50%;animation:", " 2s linear infinite;"], (props) => props.theme.palette[`${props.color}-600`], spinnerAnimation);
22
+ const PreviewThumbnail = /* @__PURE__ */ styled.img.withConfig({
23
+ displayName: "FileViewItemstyles__PreviewThumbnail",
24
+ componentId: "sc-1rkoxpv-4"
25
+ })(["width:20px;height:20px;border-radius:3px;object-fit:contain;background-color:", ";"], (props) => props.theme.palette["grey-200"]);
22
26
  const PlaceholderImage = /* @__PURE__ */ styled.div.withConfig({
23
27
  displayName: "FileViewItemstyles__PlaceholderImage",
24
- componentId: "sc-1rkoxpv-4"
28
+ componentId: "sc-1rkoxpv-5"
25
29
  })(["background-color:", ";width:32px;height:32px;border-radius:3px;padding:4px;", "{color:", ";}"], (props) => props.theme.palette["grey-200"], IconContainer, (props) => props.theme.palette["grey-500"]);
26
30
  const FileViewContainer = /* @__PURE__ */ styled.div.withConfig({
27
31
  displayName: "FileViewItemstyles__FileViewContainer",
28
- componentId: "sc-1rkoxpv-5"
32
+ componentId: "sc-1rkoxpv-6"
29
33
  })(["padding-bottom:8px;"]);
30
34
  const Info = /* @__PURE__ */ styled.div.withConfig({
31
35
  displayName: "FileViewItemstyles__Info",
32
- componentId: "sc-1rkoxpv-6"
36
+ componentId: "sc-1rkoxpv-7"
33
37
  })(["overflow:hidden;margin:0 0 0 4px;width:100%;"]);
34
38
  const Name = /* @__PURE__ */ styled(Label).withConfig({
35
39
  displayName: "FileViewItemstyles__Name",
36
- componentId: "sc-1rkoxpv-7"
40
+ componentId: "sc-1rkoxpv-8"
37
41
  })(["&&{color:", ";white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:260px;cursor:initial;}"], (props) => props.theme.palette["grey-600"]);
38
42
  const RemoveButtonWrapper = /* @__PURE__ */ styled.div.withConfig({
39
43
  displayName: "FileViewItemstyles__RemoveButtonWrapper",
40
- componentId: "sc-1rkoxpv-8"
44
+ componentId: "sc-1rkoxpv-9"
41
45
  })(["display:", ";background-color:transparent;z-index:10;border:0;padding:0;margin:0 4px 0 0;height:16px;width:16px;position:absolute;top:8px;right:5px;cursor:pointer;overflow:", ";", "{position:absolute;right:-2px;top:-2px;transition:fill 0.3s;color:", ";&:hover{color:", ";}}"], (props) => props.pressed ? "flex" : "none", (props) => props.pressed ? "visible" : "hidden", IconContainer, (props) => props.theme.palette["red-600"], (props) => props.theme.palette["red-500"]);
42
46
  const FileView = /* @__PURE__ */ styled.button.withConfig({
43
47
  displayName: "FileViewItemstyles__FileView",
44
- componentId: "sc-1rkoxpv-9"
48
+ componentId: "sc-1rkoxpv-10"
45
49
  })(["background-color:", ";border-radius:3px;border:2px solid transparent;display:flex;align-items:center;padding:7px 12px 7px 6px;height:32px;text-align:left;line-height:initial;position:relative;margin:0 0 12px;&:last-of-type{margin:0;}&:hover{border-color:", ";padding-right:", ";", "{color:", ";}", "{", "{color:", ";}}", "}&:focus{border-color:", ";background-color:", ";}&:hover{background-color:", ";}&:active{border-color:", ";background-color:", ";", "{color:", ";}", "{", "{color:", ";}}}", ";", ";", ";"], (props) => props.theme.palette["grey-100"], (props) => props.theme.palette["grey-200"], (props) => props.removable ? "30px" : "12px", Name, (props) => props.theme.palette["blue-600"], PreviewImage, IconContainer, (props) => props.theme.palette["blue-600"], (props) => props.removable && !props.disabled && `
46
50
  ${RemoveButtonWrapper} {
47
51
  display: block;
@@ -71,6 +75,7 @@ export {
71
75
  Name,
72
76
  PlaceholderImage,
73
77
  PreviewImage,
78
+ PreviewThumbnail,
74
79
  RemoveButtonWrapper,
75
80
  RepeatIcon,
76
81
  SmallLoader,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synerise/ds-file-uploader",
3
- "version": "2.0.1",
3
+ "version": "2.1.0",
4
4
  "description": "FileUploader UI Component for the Synerise Design System",
5
5
  "license": "ISC",
6
6
  "repository": "Synerise/synerise-design",
@@ -60,5 +60,5 @@
60
60
  "styled-components": "^5.3.3",
61
61
  "vitest": "4"
62
62
  },
63
- "gitHead": "1dc97a2bae9f1d20c3f35e6672d059d502c90d1a"
63
+ "gitHead": "80bee93b1c15695d79ad13e242f3d42c4f3200f4"
64
64
  }