@fibery/ui-kit 1.40.3 → 1.41.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.
Files changed (174) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/eslint.config.mjs +16 -0
  3. package/package.json +11 -13
  4. package/src/a11y-color.test.ts +162 -0
  5. package/src/a11y-color.ts +13 -20
  6. package/src/actions-menu/actions-menu-item.tsx +9 -12
  7. package/src/actions-menu/context-actions-menu.tsx +8 -3
  8. package/src/ai/model.tsx +0 -1
  9. package/src/ai/temperature.tsx +0 -1
  10. package/src/animated-height-container.tsx +3 -3
  11. package/src/antd/ant-modal.tsx +10 -5
  12. package/src/antd/index.tsx +3 -0
  13. package/src/antd/input.tsx +0 -1
  14. package/src/antd/styles.ts +15 -15
  15. package/src/app-icon-with-fallback.tsx +6 -6
  16. package/src/app-icon-wrapper.tsx +2 -3
  17. package/src/app-icon.tsx +4 -4
  18. package/src/avatar.tsx +3 -3
  19. package/src/button/base-button.tsx +0 -1
  20. package/src/button/button.tsx +1 -0
  21. package/src/button/make-button-colors.ts +5 -13
  22. package/src/checkbox.tsx +2 -2
  23. package/src/collapsible-section.tsx +11 -1
  24. package/src/color-picker/ColorPickerOrLoader.js +2 -2
  25. package/src/color-utils.test.ts +307 -0
  26. package/src/color-utils.ts +215 -0
  27. package/src/command-menu/index.tsx +1 -0
  28. package/src/comment.tsx +3 -2
  29. package/src/context-menu/index.tsx +12 -7
  30. package/src/date-picker/contexts.ts +2 -2
  31. package/src/date-picker/date-range-picker.tsx +3 -4
  32. package/src/date-picker/single-date-picker.tsx +147 -119
  33. package/src/date-picker/types.ts +4 -3
  34. package/src/day-select/iso-week-day-select.tsx +2 -2
  35. package/src/day-select/week-day-select.tsx +2 -2
  36. package/src/delayed.tsx +0 -1
  37. package/src/design-system/alpha.test.ts +59 -0
  38. package/src/design-system/alpha.ts +41 -0
  39. package/src/design-system/animation.ts +10 -0
  40. package/src/design-system/colors-css.test.ts +39 -0
  41. package/src/design-system/colors-css.ts +25 -0
  42. package/src/design-system/colors-js.test.ts +232 -0
  43. package/src/design-system/colors-js.ts +107 -0
  44. package/src/design-system/colors.test.ts +74 -0
  45. package/src/design-system/colors.ts +759 -0
  46. package/src/design-system/date.ts +3 -0
  47. package/src/design-system/layout.ts +106 -0
  48. package/src/design-system/theme.test.ts +94 -0
  49. package/src/design-system/theme.ts +93 -0
  50. package/src/design-system/typography.ts +179 -0
  51. package/src/design-system/vars.test.ts +1679 -0
  52. package/src/design-system/vars.ts +52 -0
  53. package/src/design-system.test.ts +45 -22
  54. package/src/design-system.ts +25 -1339
  55. package/src/dropdown-menu/index.tsx +21 -16
  56. package/src/emoji-picker/app-icon-picker.tsx +5 -5
  57. package/src/emoji-picker/emoji-picker-content-with-color.tsx +4 -4
  58. package/src/emoji-picker/icon-emoji-picker.tsx +3 -3
  59. package/src/favorites-icon.tsx +1 -1
  60. package/src/fibermoji-placeholder.tsx +3 -3
  61. package/src/field-container.tsx +11 -2
  62. package/src/file-item/file-icon.tsx +169 -0
  63. package/src/file-item/file-menu-items.tsx +68 -0
  64. package/src/file-item/file-preview-actions.tsx +38 -0
  65. package/src/file-item/file-title.tsx +48 -0
  66. package/src/file-item/types.ts +27 -0
  67. package/src/file-item/use-register-in-image-gallery.tsx +70 -0
  68. package/src/file-item-2.tsx +32 -348
  69. package/src/file-item.tsx +6 -3
  70. package/src/hue-shift.test.ts +91 -0
  71. package/src/icons/ast/ChatBubble.ts +8 -0
  72. package/src/icons/ast/ChatFloat.ts +8 -0
  73. package/src/icons/ast/ChatSidebar.ts +8 -0
  74. package/src/icons/ast/FileCounter.ts +8 -0
  75. package/src/icons/ast/FileMultiple.ts +8 -0
  76. package/src/icons/ast/FileOther.ts +8 -0
  77. package/src/icons/ast/NoBorder.ts +8 -0
  78. package/src/icons/ast/NoFill.ts +8 -0
  79. package/src/icons/ast/ValueEdit.ts +8 -0
  80. package/src/icons/ast/index.tsx +9 -0
  81. package/src/icons/react/ChatBubble.tsx +13 -0
  82. package/src/icons/react/ChatFloat.tsx +13 -0
  83. package/src/icons/react/ChatSidebar.tsx +13 -0
  84. package/src/icons/react/FileCounter.tsx +13 -0
  85. package/src/icons/react/FileMultiple.tsx +13 -0
  86. package/src/icons/react/FileOther.tsx +13 -0
  87. package/src/icons/react/NoBorder.tsx +13 -0
  88. package/src/icons/react/NoFill.tsx +13 -0
  89. package/src/icons/react/ValueEdit.tsx +13 -0
  90. package/src/icons/react/index.tsx +9 -0
  91. package/src/icons/svg/chat-bubble.svg +4 -0
  92. package/src/icons/svg/chat-float.svg +4 -0
  93. package/src/icons/svg/chat-sidebar.svg +4 -0
  94. package/src/icons/svg/file-counter.svg +3 -0
  95. package/src/icons/svg/file-multiple.svg +3 -0
  96. package/src/icons/svg/file-other.svg +3 -0
  97. package/src/icons/svg/no-border.svg +17 -0
  98. package/src/icons/svg/no-fill.svg +4 -0
  99. package/src/icons/svg/value-edit.svg +3 -0
  100. package/src/images-gallery/images-gallery.tsx +8 -6
  101. package/src/images-gallery/slide-buttons.tsx +4 -11
  102. package/src/is-iOS.ts +0 -1
  103. package/src/is-in-popup.ts +2 -1
  104. package/src/lists/actions-menu-row-surface.tsx +7 -7
  105. package/src/loading-sausage.tsx +2 -2
  106. package/src/media-query-utils.ts +1 -2
  107. package/src/mobile-keyboard-aware-popup.tsx +6 -5
  108. package/src/modal-menu/modal-menu-content.tsx +2 -2
  109. package/src/number-input/decimal.js +9 -7
  110. package/src/number-input/{number-inline-input-with-autosize.tsx → number-input-inline-with-autosize.tsx} +20 -41
  111. package/src/number-input/number-input-inline.tsx +118 -0
  112. package/src/number-input/{index.test.js → number-input.test.tsx} +38 -43
  113. package/src/number-input/number-input.tsx +63 -121
  114. package/src/number-input/types.ts +19 -0
  115. package/src/number-input/utils.ts +61 -0
  116. package/src/online-users.tsx +2 -2
  117. package/src/palette-generator.test.ts +309 -0
  118. package/src/palette-generator.ts +160 -0
  119. package/src/palettes/_.ts +72 -0
  120. package/src/palettes/slate-arch.colors-snapshot.test.ts +1689 -0
  121. package/src/palettes/slate-arch.ts +188 -0
  122. package/src/palettes/slate-user.colors-snapshot.test.ts +1689 -0
  123. package/src/palettes/slate-user.ts +187 -0
  124. package/src/palettes/warm-arch.colors-snapshot.test.ts +1689 -0
  125. package/src/palettes/warm-arch.ts +222 -0
  126. package/src/palettes/warm-user.colors-snapshot.test.ts +1689 -0
  127. package/src/palettes/warm-user.ts +222 -0
  128. package/src/platform.ts +0 -3
  129. package/src/popover/get-element-ref.ts +28 -0
  130. package/src/popover/index.tsx +236 -339
  131. package/src/popover/{mobile-popup-context.tsx → mobile-popover-context.tsx} +7 -7
  132. package/src/popover/mobile-popover.tsx +169 -0
  133. package/src/popover/modifiers.tsx +2 -2
  134. package/src/popover/popup-stack-context.tsx +8 -9
  135. package/src/progress.tsx +2 -2
  136. package/src/reactions/reaction-button.tsx +12 -6
  137. package/src/root-theme-provider.test.tsx +411 -0
  138. package/src/scale-generator.ts +356 -0
  139. package/src/select/components/menu-list-virtualized.tsx +12 -25
  140. package/src/select/components/menu.tsx +12 -2
  141. package/src/select/index.tsx +6 -5
  142. package/src/select/select.tsx +38 -39
  143. package/src/select/styles.ts +0 -1
  144. package/src/select/util.ts +1 -1
  145. package/src/static-palettes.ts +356 -0
  146. package/src/thematic-color-picker.tsx +266 -0
  147. package/src/thematic-constants.tsx +27 -0
  148. package/src/thematic-controls.tsx +144 -0
  149. package/src/thematic-scales.tsx +122 -0
  150. package/src/thematic-state.ts +370 -0
  151. package/src/thematic.tsx +386 -0
  152. package/src/theme-provider.test.tsx +820 -0
  153. package/src/theme-provider.tsx +158 -92
  154. package/src/theme-settings.ts +67 -12
  155. package/src/theme-styles.ts +58 -6
  156. package/src/toast/toast-action.tsx +1 -1
  157. package/src/toast/toast.tsx +1 -2
  158. package/src/toggle-on-off.tsx +2 -2
  159. package/src/toggle.tsx +7 -8
  160. package/src/tooltip.tsx +14 -10
  161. package/src/type-badge.tsx +7 -14
  162. package/src/unit/styles.ts +2 -25
  163. package/src/unit/unit-with-tooltip.tsx +3 -2
  164. package/src/use-is-phone.tsx +7 -2
  165. package/src/use-long-press.tsx +2 -2
  166. package/src/use-on-screen-keyboard-data.tsx +2 -2
  167. package/src/with-data.tsx +4 -3
  168. package/src/workflow-progress-icon.tsx +2 -2
  169. package/.eslintignore +0 -3
  170. package/.eslintrc +0 -14
  171. package/src/__mocks__/createInlineTheme.js +0 -3
  172. package/src/create-inline-theme.ts +0 -66
  173. package/src/number-input/index.js +0 -191
  174. package/src/palette.ts +0 -237
@@ -1,28 +1,16 @@
1
- /* eslint-disable max-lines */
2
1
  import {css, cx} from "@linaria/core";
3
- import {CSSProperties, useCallback, useEffect, useState} from "react";
4
- import {ActionsMenu, ActionsMenuItem, ActionsMenuSeparator} from "./actions-menu";
2
+ import {CSSProperties, useState} from "react";
3
+ import {ActionsMenu} from "./actions-menu";
5
4
  import {ActionsButtonCompact} from "./button/actions-button-compact";
6
- import {border, colors, space, themeVars, fontSize, fontWeight, lineHeight} from "./design-system";
5
+ import {border, fontSize, fontWeight, lineHeight, space, themeVars} from "./design-system";
7
6
  import {makeButtonColors} from "./button/make-button-colors";
8
- import Delete from "./icons/react/Delete";
9
- import FileDownload from "./icons/react/FileDownload";
10
- import FileImage from "./icons/react/FileImage";
11
- import FileVideo from "./icons/react/FileVideo";
12
- import FileTable from "./icons/react/FileTable";
13
- import FileDocument from "./icons/react/FileDocument";
14
- import FileArchive from "./icons/react/FileArchive";
15
- import FilePresentation from "./icons/react/FilePresentation";
16
- import Link from "./icons/react/Link";
17
- import RicheditorOpenLink from "./icons/react/RicheditorOpenLink";
7
+ import {useTheme} from "./theme-provider";
18
8
  import Spinner from "./icons/react/Spinner";
19
- import AppWiki from "./icons/react/AppWiki";
20
- import {copyUrlToClipboard} from "./copy-to-clipboard";
21
- import {useToast} from "./toast/toast-provider";
22
9
  import {Icon} from "./icons/Icon";
23
- import {useImagesGalleryMethods, useImagesGalleryRegistry} from "./images-gallery/images-gallery";
24
- import {isPhoneApp} from "./use-is-phone";
25
- import Pencil from "./icons/react/Pencil";
10
+ import {FileIcon} from "./file-item/file-icon";
11
+ import {FileMenuItems} from "./file-item/file-menu-items";
12
+ import {UploadFileWithContent} from "./file-item/types";
13
+ import {FileTitle as FileTitleInner} from "./file-item/file-title";
26
14
 
27
15
  const fileItemStyle = css`
28
16
  display: block;
@@ -79,34 +67,21 @@ const actionsButtonStyle = css`
79
67
  opacity: 0;
80
68
  `;
81
69
 
82
- export type FileItemType = {
83
- uid: string;
84
- name: string;
85
- url: string;
86
- downloadUrl?: string | null;
87
- thumbnailUrl: string | null;
88
- contentType: string;
89
- previewData: null | {
90
- type: "image" | "video" | "iframe";
91
- previewSrc: string | null;
92
- originalSrc: string;
93
- };
94
- status?: string;
95
- };
96
-
97
70
  export type FileItemProps = {
98
- data: FileItemType;
71
+ data: UploadFileWithContent;
99
72
  onDownloadClick?: () => void;
100
- onRemoveClick?: (x: FileItemType) => void;
101
- onRenameClick?: (x: {id: string; name: string}) => void;
73
+ onRemoveClick?: () => void;
74
+ onRenameClick?: (file: {id: string; name: string}) => void;
75
+ onClick?: () => void;
102
76
  disabled?: boolean;
103
77
  };
104
78
 
105
79
  export const minFileTileWidth = 120;
106
80
 
107
81
  export function FileItem(props: FileItemProps) {
108
- const {onDownloadClick, onRemoveClick} = props;
109
- const {name, thumbnailUrl, status, previewData: preview, contentType} = props.data;
82
+ const theme = useTheme();
83
+ const {onDownloadClick, onRemoveClick, onClick} = props;
84
+ const {name, thumbnailUrl, status, contentType} = props.data;
110
85
  const isPending = status === "uploading" || status === "deleting";
111
86
  const [isThumbnailLoadFailed, setIsThumbnailLoadFailed] = useState(false);
112
87
  const isBackgroundReady = !isThumbnailLoadFailed && Boolean(thumbnailUrl && !isPending);
@@ -114,60 +89,6 @@ export function FileItem(props: FileItemProps) {
114
89
 
115
90
  const showFileIcon = !isPending && !isBackgroundReady;
116
91
 
117
- const [zoomed, setZoomed] = useState(false);
118
- const imagesRegistry = useImagesGalleryRegistry();
119
- const galleryMethods = useImagesGalleryMethods();
120
-
121
- useEffect(() => {
122
- if (!preview) {
123
- return;
124
- }
125
-
126
- imagesRegistry.set(preview.originalSrc, {
127
- type: preview.type,
128
- src: preview.originalSrc,
129
- label: name,
130
- zoomed: false,
131
- setZoomed,
132
- actions: <FilePreviewActions url={preview.originalSrc} name={name} onDownloadClick={onDownloadClick} />,
133
- });
134
- }, [imagesRegistry, name, preview, onDownloadClick]);
135
-
136
- useEffect(() => {
137
- if (!preview) {
138
- return undefined;
139
- }
140
-
141
- if (zoomed) {
142
- galleryMethods.setActivated(true);
143
- }
144
- const imageData = imagesRegistry.get(preview.originalSrc);
145
- if (imageData) {
146
- imageData.zoomed = zoomed;
147
- } else {
148
- imagesRegistry.set(preview.originalSrc, {
149
- type: preview.type,
150
- src: preview.originalSrc,
151
- label: name,
152
- zoomed,
153
- setZoomed,
154
- actions: <FilePreviewActions url={preview.originalSrc} name={name} onDownloadClick={onDownloadClick} />,
155
- });
156
- }
157
-
158
- return () => {
159
- imagesRegistry.delete(preview.originalSrc);
160
- };
161
- }, [galleryMethods, zoomed, preview, imagesRegistry, name, onDownloadClick]);
162
-
163
- const onContainerClick = useCallback(() => {
164
- if (preview) {
165
- setZoomed(true);
166
- } else {
167
- onDownloadClick?.();
168
- }
169
- }, [preview, onDownloadClick]);
170
-
171
92
  return (
172
93
  <>
173
94
  <div
@@ -219,7 +140,7 @@ export function FileItem(props: FileItemProps) {
219
140
  >
220
141
  <div
221
142
  style={isBackgroundReady ? {backgroundImage: `url(${thumbnailUrl})`} : {}}
222
- onClick={onContainerClick}
143
+ onClick={onClick}
223
144
  className={cx(
224
145
  css`
225
146
  flex-shrink: 0;
@@ -321,7 +242,7 @@ export function FileItem(props: FileItemProps) {
321
242
  data={props.data}
322
243
  disabled={props.disabled}
323
244
  actionsButtonStyle={
324
- isBackgroundReady ? makeButtonColors(colors.inversedTextColor, "ghost") : undefined
245
+ isBackgroundReady ? makeButtonColors(themeVars.inversedTextColor, "ghost", theme) : undefined
325
246
  }
326
247
  onDownloadClick={onDownloadClick}
327
248
  onRemoveClick={props.onRemoveClick}
@@ -366,16 +287,14 @@ function FileActionsMenu({
366
287
  onRenameClick,
367
288
  actionsButtonStyle,
368
289
  }: {
369
- data: FileItemType;
290
+ data: UploadFileWithContent;
370
291
  onDownloadClick?: () => void;
371
- onRemoveClick?: (data: FileItemType) => void;
292
+ onRemoveClick?: () => void;
372
293
  onRenameClick?: (data: {id: string; name: string}) => void;
373
294
  disabled?: boolean;
374
295
  setIsActionsOpened: (val: boolean) => void;
375
296
  actionsButtonStyle?: CSSProperties;
376
297
  }) {
377
- const toast = useToast();
378
-
379
298
  return (
380
299
  <ActionsMenu
381
300
  onOpenChange={setIsActionsOpened}
@@ -388,51 +307,20 @@ function FileActionsMenu({
388
307
  />
389
308
  }
390
309
  >
391
- <ActionsMenuItem
392
- Icon={Link}
393
- onSelect={() => {
394
- copyUrlToClipboard({url: `${window.origin}${data.url}`, label: data.name})
395
- .then(() => toast.success({title: "Link copied to clipboard"}))
396
- .catch((error) => toast.error({title: "Unable to copy link to clipboard", subTitle: error.message}));
397
- }}
398
- >
399
- Copy file URL
400
- </ActionsMenuItem>
401
- <a href={data.url} target="_blank" rel="noopener noreferrer">
402
- <ActionsMenuItem Icon={RicheditorOpenLink}>
403
- {isPhoneApp() ? "Open in browser" : "Open in new tab"}
404
- </ActionsMenuItem>
405
- </a>
406
- {isPhoneApp() ? (
407
- <ActionsMenuItem Icon={FileDownload} onSelect={onDownloadClick}>
408
- Download
409
- </ActionsMenuItem>
410
- ) : (
411
- <a download={data.name} href={`${data.url}?attachment`}>
412
- <ActionsMenuItem Icon={FileDownload}>Download</ActionsMenuItem>
413
- </a>
414
- )}
415
- {onRenameClick && data.status === "done" ? (
416
- <>
417
- <ActionsMenuSeparator />
418
- <ActionsMenuItem Icon={Pencil} onSelect={() => onRenameClick?.({id: data.uid, name: data.name})}>
419
- Rename
420
- </ActionsMenuItem>
421
- </>
422
- ) : null}
423
- <ActionsMenuSeparator />
424
- <ActionsMenuItem Icon={Delete} onSelect={() => onRemoveClick?.(data)} dangerous disabled={disabled}>
425
- Delete
426
- </ActionsMenuItem>
310
+ {
311
+ <FileMenuItems
312
+ file={data}
313
+ disabled={disabled}
314
+ onDownloadClick={onDownloadClick}
315
+ onRemoveClick={onRemoveClick}
316
+ onRenameClick={onRenameClick}
317
+ />
318
+ }
427
319
  </ActionsMenu>
428
320
  );
429
321
  }
430
322
 
431
323
  function FileTitle({title, className}: {title: string; className?: string}) {
432
- const parts = title.split(".");
433
- const ext = parts.length > 1 ? "." + parts[parts.length - 1] : "";
434
- const name = title.substring(0, title.length - ext.length);
435
-
436
324
  return (
437
325
  <div
438
326
  className={cx(
@@ -458,216 +346,12 @@ function FileTitle({title, className}: {title: string; className?: string}) {
458
346
  className
459
347
  )}
460
348
  >
461
- <div
462
- className={css`
463
- display: flex;
464
- min-width: 0;
465
- align-items: center;
349
+ <FileTitleInner
350
+ title={title}
351
+ containerCss={css`
466
352
  max-width: 180px;
467
353
  `}
468
- >
469
- <span
470
- className={css`
471
- white-space: nowrap;
472
- overflow: hidden;
473
- text-overflow: ellipsis;
474
- /* ellipsis shows ONLY if this span overflows */
475
- min-width: 0;
476
- `}
477
- >
478
- {name}
479
- </span>
480
- <span
481
- className={css`
482
- flex: 0 0 auto;
483
- `}
484
- >
485
- {ext}
486
- </span>
487
- </div>
354
+ />
488
355
  </div>
489
356
  );
490
357
  }
491
-
492
- function FilePreviewActions({url, name, onDownloadClick}: {url: string; name: string; onDownloadClick?: () => void}) {
493
- if (isPhoneApp()) {
494
- return (
495
- <>
496
- <a href={url} target="_blank" rel="noopener noreferrer">
497
- <ActionsMenuItem Icon={RicheditorOpenLink}>Open in browser</ActionsMenuItem>
498
- </a>
499
- <ActionsMenuItem Icon={FileDownload} onSelect={onDownloadClick}>
500
- Download
501
- </ActionsMenuItem>
502
- </>
503
- );
504
- }
505
-
506
- return (
507
- <>
508
- <a href={url} target="_blank" rel="noopener noreferrer">
509
- <ActionsMenuItem Icon={RicheditorOpenLink}>Open in new tab</ActionsMenuItem>
510
- </a>
511
- <a download={name} href={`${url}?attachment`}>
512
- <ActionsMenuItem Icon={FileDownload}>Download</ActionsMenuItem>
513
- </a>
514
- </>
515
- );
516
- }
517
-
518
- function FileIcon({contentType}: {contentType?: string}) {
519
- if (!contentType) {
520
- return <AppWiki />;
521
- }
522
- if (contentType.startsWith("image/")) {
523
- return <FileImage />;
524
- }
525
- if (contentType.startsWith("video/")) {
526
- return <FileVideo />;
527
- }
528
- if (SheetsContentTypes.includes(contentType)) {
529
- return <FileTable />;
530
- }
531
- if (ArchiveContentTypes.includes(contentType)) {
532
- return <FileArchive />;
533
- }
534
- if (PresentationContentTypes.includes(contentType)) {
535
- return <FilePresentation />;
536
- }
537
- if (DocumentContentTypes.includes(contentType) || contentType.startsWith("text/")) {
538
- return <FileDocument />;
539
- }
540
-
541
- return <AppWiki />;
542
- }
543
-
544
- const ArchiveContentTypes = [
545
- "application/zip",
546
- "application/zip-compressed",
547
- "application/x-zip",
548
- "application/x-zip-compressed",
549
- "multipart/x-zip",
550
-
551
- "application/x-rar-compressed",
552
- "application/vnd.rar",
553
-
554
- "application/x-7z-compressed",
555
-
556
- "application/gzip",
557
- "application/x-gzip",
558
-
559
- "application/x-tar",
560
-
561
- "application/x-bzip",
562
- "application/x-bzip2",
563
- "application/x-bzip3",
564
- "application/x-bzip4",
565
-
566
- "application/x-xz",
567
-
568
- "application/zstd",
569
- "application/x-zstd",
570
-
571
- "application/x-lzma",
572
- "application/x-lzip",
573
-
574
- "application/x-lzh",
575
- "application/x-lha",
576
-
577
- "application/x-iso9660-image",
578
- "application/x-cd-image",
579
-
580
- "application/x-apple-diskimage",
581
-
582
- "application/x-arj",
583
-
584
- "application/x-cpio",
585
-
586
- "application/x-rpm",
587
-
588
- "application/x-deb",
589
- "application/vnd.debian.binary-package",
590
-
591
- "application/x-compress",
592
- "application/x-archive",
593
- "application/vnd.android.package-archive",
594
- ];
595
-
596
- const PresentationContentTypes = [
597
- "application/x-iwork-keynote-sffkey",
598
- "application/vnd.ms-powerpoint",
599
- "application/vnd.openxmlformats-officedocument.presentationml.presentation",
600
- "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
601
- "application/vnd.openxmlformats-officedocument.presentationml.template",
602
- "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
603
- "application/vnd.ms-powerpoint.slideshow.macroEnabled.12",
604
- "application/vnd.ms-powerpoint.template.macroEnabled.12",
605
- "application/vnd.oasis.opendocument.presentation",
606
- "application/vnd.oasis.opendocument.presentation-template",
607
- "application/vnd.apple.keynote",
608
- "application/vnd.google-apps.presentation",
609
- "application/pdf",
610
- "application/vnd.sun.xml.impress",
611
- "application/vnd.stardivision.impress",
612
- ];
613
-
614
- const SheetsContentTypes = [
615
- "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
616
- "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
617
- "application/vnd.ms-excel",
618
- "application/vnd.ms-excel.sheet.macroEnabled.12",
619
- "application/vnd.ms-excel.template.macroEnabled.12",
620
- "application/vnd.ms-excel.addin.macroEnabled.12",
621
- "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
622
- "application/vnd.oasis.opendocument.spreadsheet",
623
- "application/vnd.oasis.opendocument.spreadsheet-template",
624
- "application/vnd.apple.numbers",
625
- "application/vnd.google-apps.spreadsheet",
626
- "text/csv",
627
- "text/x-comma-separated-values",
628
- "application/csv",
629
- "text/tab-separated-values",
630
- "application/vnd.lotus-1-2-3",
631
- "application/x-123",
632
- "application/vnd.stardivision.calc",
633
- "application/vnd.sun.xml.calc",
634
- "application/x-dbase",
635
- "application/x-iwork-numbers-sffnumbers",
636
- ];
637
-
638
- const DocumentContentTypes = [
639
- "application/msword",
640
- "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
641
- "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
642
- "application/vnd.ms-word.document.macroEnabled.12",
643
- "application/vnd.ms-word.template.macroEnabled.12",
644
-
645
- "application/vnd.oasis.opendocument.text",
646
- "application/vnd.oasis.opendocument.text-template",
647
- "application/vnd.oasis.opendocument.text-master",
648
- "application/vnd.oasis.opendocument.text-web",
649
-
650
- "application/vnd.apple.pages",
651
- "application/x-iwork-pages-sffpages",
652
-
653
- "application/rtf",
654
- "text/rtf",
655
-
656
- "text/plain",
657
- "text/markdown",
658
- "text/html",
659
-
660
- "application/vnd.google-apps.document",
661
-
662
- "application/vnd.sun.xml.writer",
663
- "application/vnd.sun.xml.writer.template",
664
- "application/vnd.sun.xml.writer.global",
665
-
666
- "application/vnd.stardivision.writer",
667
- "application/vnd.stardivision.writer-global",
668
-
669
- "application/x-mswrite",
670
- "application/x-abiword",
671
-
672
- "application/pdf",
673
- ];
package/src/file-item.tsx CHANGED
@@ -3,8 +3,9 @@ import {useCallback, useEffect, useState} from "react";
3
3
  import {ActionsMenu, ActionsMenuItem} from "./actions-menu";
4
4
  import {ActionsButtonCompact} from "./button/actions-button-compact";
5
5
  import {Item} from "./item";
6
- import {border, colors, fontWeight, space, textStyles, themeVars} from "./design-system";
6
+ import {border, fontWeight, space, textStyles, themeVars} from "./design-system";
7
7
  import {makeButtonColors} from "./button/make-button-colors";
8
+ import {useTheme} from "./theme-provider";
8
9
  import {useImagesGalleryMethods, useImagesGalleryRegistry} from "./images-gallery/images-gallery";
9
10
  import FileDownload from "./icons/react/FileDownload";
10
11
  import RicheditorOpenLink from "./icons/react/RicheditorOpenLink";
@@ -94,6 +95,7 @@ export type FileItemProps = {
94
95
  };
95
96
 
96
97
  export function FileItem(props: FileItemProps) {
98
+ const theme = useTheme();
97
99
  const {onDownloadClick, onRemoveClick} = props;
98
100
  const {name, url, thumbnailUrl, status, previewData: preview} = props.data;
99
101
  const isActive = status === "uploading" || status === "deleting";
@@ -171,7 +173,6 @@ export function FileItem(props: FileItemProps) {
171
173
  `
172
174
  )}
173
175
  >
174
- {/* eslint-disable-next-line jsx-a11y/anchor-has-content */}
175
176
  <div
176
177
  style={isBackgroundReady ? {backgroundImage: `url(${thumbnailUrl})`} : {}}
177
178
  onClick={onContainerClick}
@@ -201,7 +202,9 @@ export function FileItem(props: FileItemProps) {
201
202
  className={css`
202
203
  z-index: 1;
203
204
  `}
204
- style={isBackgroundReady ? makeButtonColors(colors.inversedTextColor, "ghost") : undefined}
205
+ style={
206
+ isBackgroundReady ? makeButtonColors(themeVars.inversedTextColor, "ghost", theme) : undefined
207
+ }
205
208
  />
206
209
  }
207
210
  >
@@ -0,0 +1,91 @@
1
+ import {makeThematicPalette, SCALE_PRESETS} from "./palette-generator";
2
+ import {OKLCH, parse, to} from "./color-utils";
3
+
4
+ function getHue(color: string): number {
5
+ const oklch = to(parse(color), OKLCH);
6
+ return oklch.coords[2] ?? 0;
7
+ }
8
+
9
+ describe("shiftHueTowards", () => {
10
+ it("no shift when baseChroma is 0", () => {
11
+ const palette = makeThematicPalette({
12
+ baseHue: 200,
13
+ baseChroma: 0,
14
+ accentHue: 264,
15
+ accentChroma: 0.16,
16
+ });
17
+
18
+ // Semantic colors should keep original hues (within 1° tolerance)
19
+ expect(Math.abs(getHue(palette.red[9]) - SCALE_PRESETS.red.hue)).toBeLessThan(1);
20
+ expect(Math.abs(getHue(palette.teal[9]) - SCALE_PRESETS.teal.hue)).toBeLessThan(1);
21
+ expect(Math.abs(getHue(palette.yellow[9]) - SCALE_PRESETS.yellow.hue)).toBeLessThan(1);
22
+ });
23
+
24
+ it("shifts hue towards baseHue when baseChroma is high", () => {
25
+ const palette = makeThematicPalette({
26
+ baseHue: 200, // blue-ish
27
+ baseChroma: 0.15, // high chroma → 30% shift
28
+ accentHue: 264,
29
+ accentChroma: 0.16,
30
+ });
31
+
32
+ const redHue = getHue(palette.red[9]);
33
+ const tealHue = getHue(palette.teal[9]);
34
+ const yellowHue = getHue(palette.yellow[9]);
35
+
36
+ // Hues should differ from original presets
37
+ expect(redHue).not.toBeCloseTo(SCALE_PRESETS.red.hue, 0);
38
+ expect(tealHue).not.toBeCloseTo(SCALE_PRESETS.teal.hue, 0);
39
+ expect(yellowHue).not.toBeCloseTo(SCALE_PRESETS.yellow.hue, 0);
40
+
41
+ // Teal (183) should shift towards 200 → increases
42
+ expect(tealHue).toBeGreaterThan(SCALE_PRESETS.teal.hue);
43
+ expect(tealHue).toBeLessThan(200);
44
+
45
+ // Yellow (100) should shift towards 200 → increases
46
+ expect(yellowHue).toBeGreaterThan(SCALE_PRESETS.yellow.hue);
47
+ expect(yellowHue).toBeLessThan(200);
48
+ });
49
+
50
+ it("handles hue wrap-around correctly", () => {
51
+ // Base hue near 0, target hue near 360
52
+ const palette = makeThematicPalette({
53
+ baseHue: 350, // near red
54
+ baseChroma: 0.15,
55
+ accentHue: 264,
56
+ accentChroma: 0.16,
57
+ });
58
+
59
+ // Red (19) is close to 350 via wrap-around (350 → 360 → 19 = 29° distance)
60
+ // Should shift towards 350, so should decrease or wrap
61
+ const redHue = getHue(palette.red[9]);
62
+ // The shift should take the shortest path
63
+ expect(Math.abs(redHue - 350) < 30 || Math.abs(redHue - 350 + 360) < 30).toBe(true);
64
+ });
65
+
66
+ it("caps shift at 30%", () => {
67
+ const lowChroma = makeThematicPalette({
68
+ baseHue: 150, // green
69
+ baseChroma: 0.05, // 10% shift
70
+ accentHue: 264,
71
+ accentChroma: 0.16,
72
+ });
73
+
74
+ const highChroma = makeThematicPalette({
75
+ baseHue: 150,
76
+ baseChroma: 0.25, // would be 50% but capped at 30%
77
+ accentHue: 264,
78
+ accentChroma: 0.16,
79
+ });
80
+
81
+ const lowShiftYellow = getHue(lowChroma.yellow[9]);
82
+ const highShiftYellow = getHue(highChroma.yellow[9]);
83
+
84
+ // Yellow (100) should shift towards 150 → increases
85
+ // Higher chroma = more shift (up to 30% cap)
86
+ expect(lowShiftYellow).toBeGreaterThan(SCALE_PRESETS.yellow.hue);
87
+ expect(highShiftYellow).toBeGreaterThan(lowShiftYellow);
88
+ // But capped - 30% of 50° = 15° max additional shift
89
+ expect(highShiftYellow).toBeLessThan(150);
90
+ });
91
+ });
@@ -0,0 +1,8 @@
1
+
2
+ // This icon file is generated automatically.
3
+
4
+ import { IconDefinition } from '../types';
5
+
6
+ const ChatBubble: IconDefinition = {"icon":{"type":"root","children":[{"type":"element","tagName":"svg","properties":{"width":20,"height":20,"fill":"none"},"children":[{"type":"element","tagName":"path","properties":{"d":"M13.137 10.235a2 2 0 1 1 0 4 2 2 0 0 1 0-4Z"},"children":[]},{"type":"element","tagName":"path","properties":{"clipRule":"evenodd","d":"M15.251 3c.799 0 1.532.22 2.052.786.509.554.697 1.313.697 2.139v8.15c0 .826-.188 1.585-.697 2.139-.52.566-1.253.786-2.052.786H4.749c-.799 0-1.532-.22-2.052-.786C2.188 15.66 2 14.9 2 14.075v-8.15c0-.826.188-1.585.697-2.139C3.217 3.22 3.95 3 4.75 3h10.502ZM4.749 4.5c-.534 0-.801.143-.947.302-.157.17-.302.499-.302 1.123v8.15c0 .624.145.952.302 1.123.146.16.413.302.947.302h10.502c.534 0 .801-.143.947-.302.157-.17.302-.499.302-1.123v-8.15c0-.624-.145-.952-.302-1.123-.146-.16-.413-.302-.947-.302H4.749Z"},"children":[]}],"metadata":""}]},"name":"chat-bubble"};
7
+
8
+ export default ChatBubble;
@@ -0,0 +1,8 @@
1
+
2
+ // This icon file is generated automatically.
3
+
4
+ import { IconDefinition } from '../types';
5
+
6
+ const ChatFloat: IconDefinition = {"icon":{"type":"root","children":[{"type":"element","tagName":"svg","properties":{"width":20,"height":20,"fill":"none"},"children":[{"type":"element","tagName":"path","properties":{"d":"M14.84 9.05a.75.75 0 0 1 .75.75v4a.75.75 0 0 1-.75.75h-3.5a.75.75 0 0 1-.75-.75v-4a.75.75 0 0 1 .75-.75h3.5Z"},"children":[]},{"type":"element","tagName":"path","properties":{"clipRule":"evenodd","d":"M15.251 3c.799 0 1.532.22 2.052.786.509.554.697 1.313.697 2.139v8.15c0 .826-.188 1.585-.697 2.139-.52.566-1.253.786-2.052.786H4.749c-.799 0-1.532-.22-2.052-.786C2.188 15.66 2 14.9 2 14.075v-8.15c0-.826.188-1.585.697-2.139C3.217 3.22 3.95 3 4.75 3h10.502ZM4.749 4.5c-.534 0-.801.143-.947.302-.157.17-.302.499-.302 1.123v8.15c0 .624.145.952.302 1.123.146.16.413.302.947.302h10.502c.534 0 .801-.143.947-.302.157-.17.302-.499.302-1.123v-8.15c0-.624-.145-.952-.302-1.123-.146-.16-.413-.302-.947-.302H4.749Z"},"children":[]}],"metadata":""}]},"name":"chat-float"};
7
+
8
+ export default ChatFloat;
@@ -0,0 +1,8 @@
1
+
2
+ // This icon file is generated automatically.
3
+
4
+ import { IconDefinition } from '../types';
5
+
6
+ const ChatSidebar: IconDefinition = {"icon":{"type":"root","children":[{"type":"element","tagName":"svg","properties":{"width":20,"height":20,"fill":"none"},"children":[{"type":"element","tagName":"path","properties":{"d":"M14.702 5.35a.75.75 0 0 1 .75.75v7.8a.75.75 0 0 1-.75.75h-3.5a.75.75 0 0 1-.75-.75V6.1a.75.75 0 0 1 .75-.75h3.5Z"},"children":[]},{"type":"element","tagName":"path","properties":{"clipRule":"evenodd","d":"M15.251 3c.799 0 1.532.22 2.052.786.509.554.697 1.313.697 2.139v8.15c0 .826-.188 1.585-.697 2.139-.52.566-1.253.786-2.052.786H4.749c-.799 0-1.532-.22-2.052-.786C2.188 15.66 2 14.9 2 14.075v-8.15c0-.826.188-1.585.697-2.139C3.217 3.22 3.95 3 4.75 3h10.502ZM4.749 4.5c-.534 0-.801.143-.947.302-.157.17-.302.499-.302 1.123v8.15c0 .624.145.952.302 1.123.146.16.413.302.947.302h10.502c.534 0 .801-.143.947-.302.157-.17.302-.499.302-1.123v-8.15c0-.624-.145-.952-.302-1.123-.146-.16-.413-.302-.947-.302H4.749Z"},"children":[]}],"metadata":""}]},"name":"chat-sidebar"};
7
+
8
+ export default ChatSidebar;
@@ -0,0 +1,8 @@
1
+
2
+ // This icon file is generated automatically.
3
+
4
+ import { IconDefinition } from '../types';
5
+
6
+ const FileCounter: IconDefinition = {"icon":{"type":"root","children":[{"type":"element","tagName":"svg","properties":{"viewBox":"0 0 20 20"},"children":[{"type":"element","tagName":"path","properties":{"d":"M11.742 1.5a.75.75 0 0 1 .53.219l4.504 4.497a.752.752 0 0 1-.529 1.281h-3.672a1.583 1.583 0 0 1-1.582-1.584V3H5.745c-.69 0-1.249.56-1.249 1.25v11.5c0 .69.56 1.25 1.25 1.25h3.501a.75.75 0 0 1 0 1.5H5.745a2.749 2.749 0 0 1-2.748-2.75V4.25c0-1.519 1.23-2.75 2.748-2.75h5.997Zm2.173 8c1.668 0 2.795.902 2.795 2.281 0 1.093-.717 1.81-1.647 2.019v.036c1.176.12 1.937.872 1.937 2.06 0 1.565-1.242 2.604-3.074 2.604-1.504 0-2.428-.71-2.8-1.559a1.393 1.393 0 0 1-.126-.567c0-.466.274-.759.727-.759.329 0 .537.144.7.472.28.603.706.962 1.526.962.81 0 1.368-.514 1.368-1.23.006-.837-.558-1.302-1.515-1.302h-.345c-.399 0-.629-.257-.629-.645 0-.376.23-.64.629-.64h.323c.826 0 1.361-.49 1.361-1.194 0-.693-.426-1.152-1.224-1.152-.667 0-1.072.304-1.33.913-.18.406-.371.556-.743.556-.46 0-.695-.293-.695-.729 0-.203.038-.376.12-.58.35-.841 1.247-1.546 2.642-1.546Zm-1.423-3.587a.084.084 0 0 0 .083.084h1.86l-1.943-1.94v1.856Z"},"children":[]}],"metadata":""}]},"name":"file-counter"};
7
+
8
+ export default FileCounter;
@@ -0,0 +1,8 @@
1
+
2
+ // This icon file is generated automatically.
3
+
4
+ import { IconDefinition } from '../types';
5
+
6
+ const FileMultiple: IconDefinition = {"icon":{"type":"root","children":[{"type":"element","tagName":"svg","properties":{"viewBox":"0 0 20 20"},"children":[{"type":"element","tagName":"path","properties":{"d":"M12.742 1a.75.75 0 0 1 .53.219l4.504 4.497c.141.14.22.332.22.531v6.003A2.75 2.75 0 0 1 15.249 15h-.473a2.748 2.748 0 0 1-2.233 2.265l-6.405 1.129a2.749 2.749 0 0 1-3.183-2.23L1.477 7.791a2.75 2.75 0 0 1 2.23-3.186l2.29-.404V3.75A2.75 2.75 0 0 1 8.745 1h3.997ZM3.967 6.084a1.25 1.25 0 0 0-1.013 1.447l1.475 8.371a1.25 1.25 0 0 0 1.448 1.015l6.404-1.13c.45-.08.798-.39.95-.787H8.744a2.75 2.75 0 0 1-2.748-2.75V5.726l-2.03.358ZM8.745 2.5c-.69 0-1.25.56-1.25 1.25v8.5c0 .69.56 1.25 1.25 1.25h6.503c.69 0 1.249-.56 1.249-1.25V6.997h-2.922a1.584 1.584 0 0 1-1.582-1.584V2.5H8.745Zm4.747 2.913c0 .022.009.044.024.06a.085.085 0 0 0 .059.024h1.86l-1.943-1.94v1.856Z"},"children":[]}],"metadata":""}]},"name":"file-multiple"};
7
+
8
+ export default FileMultiple;
@@ -0,0 +1,8 @@
1
+
2
+ // This icon file is generated automatically.
3
+
4
+ import { IconDefinition } from '../types';
5
+
6
+ const FileOther: IconDefinition = {"icon":{"type":"root","children":[{"type":"element","tagName":"svg","properties":{"viewBox":"0 0 20 20"},"children":[{"type":"element","tagName":"path","properties":{"d":"M11.742 1.5a.75.75 0 0 1 .53.219l4.504 4.497c.141.14.221.332.221.531v9.003a2.75 2.75 0 0 1-2.749 2.75H5.745a2.749 2.749 0 0 1-2.748-2.75V4.25c0-1.519 1.23-2.75 2.748-2.75h5.997ZM5.745 3c-.69 0-1.249.56-1.249 1.25v11.5c0 .69.56 1.25 1.25 1.25h8.502c.69 0 1.25-.56 1.25-1.25V7.497h-2.923a1.583 1.583 0 0 1-1.582-1.584V3H5.745Zm6.747 2.913a.084.084 0 0 0 .083.084h1.86l-1.943-1.94v1.856Z"},"children":[]}],"metadata":""}]},"name":"file-other"};
7
+
8
+ export default FileOther;
@@ -0,0 +1,8 @@
1
+
2
+ // This icon file is generated automatically.
3
+
4
+ import { IconDefinition } from '../types';
5
+
6
+ const NoBorder: IconDefinition = {"icon":{"type":"root","children":[{"type":"element","tagName":"svg","properties":{"viewBox":"0 0 20 20"},"children":[{"type":"element","tagName":"path","properties":{"d":"M16.47 2.47a.75.75 0 0 1 1.06 1.06l-14 14a.75.75 0 0 1-1.06-1.06l14-14ZM6.874 15.495a.753.753 0 1 1-.003 1.506.753.753 0 0 1 .003-1.506ZM10 15.495a.754.754 0 0 1 .532 1.284.753.753 0 1 1-.532-1.284ZM13.126 15.495a.754.754 0 0 1 0 1.505.753.753 0 0 1 0-1.505ZM16.252 15.495a.754.754 0 0 1 0 1.505.751.751 0 0 1 0-1.505ZM3.748 12.378a.748.748 0 1 1-.003 1.496.748.748 0 0 1 .003-1.496ZM16.252 12.378a.748.748 0 1 1-.528 1.277.749.749 0 0 1 .528-1.277ZM3.748 9.252a.748.748 0 1 1 0 1.496.748.748 0 0 1 0-1.496ZM16.252 9.252a.75.75 0 1 1 0 1.498.75.75 0 0 1 0-1.498ZM3.748 6.126a.748.748 0 1 1 0 1.496.748.748 0 0 1 0-1.496ZM16.252 6.126a.75.75 0 1 1 0 1.498.75.75 0 0 1 0-1.498ZM3.952 3a.753.753 0 1 1 0 1.507.753.753 0 0 1 0-1.507ZM6.874 3a.752.752 0 1 1-.002 1.503A.752.752 0 0 1 6.874 3ZM10 3a.753.753 0 1 1-.001 1.507A.753.753 0 0 1 10 3ZM13.126 3a.753.753 0 1 1-.001 1.507A.753.753 0 0 1 13.126 3Z"},"children":[]}],"metadata":""}]},"name":"no-border"};
7
+
8
+ export default NoBorder;
@@ -0,0 +1,8 @@
1
+
2
+ // This icon file is generated automatically.
3
+
4
+ import { IconDefinition } from '../types';
5
+
6
+ const NoFill: IconDefinition = {"icon":{"type":"root","children":[{"type":"element","tagName":"svg","properties":{"viewBox":"0 0 20 20"},"children":[{"type":"element","tagName":"path","properties":{"fillRule":"evenodd","clipRule":"evenodd","d":"M13.108 5.892a1 1 0 0 1 1 1v6.216a1 1 0 0 1-.897.995l-.103.005H6.892l-.103-.005a1 1 0 0 1-.893-.892l-.004-.103V6.892a1 1 0 0 1 1-1h6.216Zm-5.716 6.716h5.216V7.392H7.392v5.216Z"},"children":[]},{"type":"element","tagName":"path","properties":{"fillRule":"evenodd","clipRule":"evenodd","d":"M14.25 3A2.75 2.75 0 0 1 17 5.75v8.5A2.75 2.75 0 0 1 14.25 17h-8.5A2.75 2.75 0 0 1 3 14.25v-8.5A2.75 2.75 0 0 1 5.75 3h8.5Zm-8.5 1.5c-.69 0-1.25.56-1.25 1.25v8.5c0 .69.56 1.25 1.25 1.25h8.5c.69 0 1.25-.56 1.25-1.25v-8.5c0-.69-.56-1.25-1.25-1.25h-8.5Z"},"children":[]}],"metadata":""}]},"name":"no-fill"};
7
+
8
+ export default NoFill;