@orthacms/media-admin 0.0.0-reserve.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 (214) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +7 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +1 -0
  6. package/dist/lib/components/AssetActionsMenu/index.d.ts +37 -0
  7. package/dist/lib/components/AssetActionsMenu/index.d.ts.map +1 -0
  8. package/dist/lib/components/AssetActionsMenu/index.js +57 -0
  9. package/dist/lib/components/AssetDetailDrawer/MetaRow/index.d.ts +11 -0
  10. package/dist/lib/components/AssetDetailDrawer/MetaRow/index.d.ts.map +1 -0
  11. package/dist/lib/components/AssetDetailDrawer/MetaRow/index.js +9 -0
  12. package/dist/lib/components/AssetDetailDrawer/index.d.ts +28 -0
  13. package/dist/lib/components/AssetDetailDrawer/index.d.ts.map +1 -0
  14. package/dist/lib/components/AssetDetailDrawer/index.js +93 -0
  15. package/dist/lib/components/EntryMediaTab/MediaFieldSection/index.d.ts +28 -0
  16. package/dist/lib/components/EntryMediaTab/MediaFieldSection/index.d.ts.map +1 -0
  17. package/dist/lib/components/EntryMediaTab/MediaFieldSection/index.js +52 -0
  18. package/dist/lib/components/EntryMediaTab/index.d.ts +11 -0
  19. package/dist/lib/components/EntryMediaTab/index.d.ts.map +1 -0
  20. package/dist/lib/components/EntryMediaTab/index.js +31 -0
  21. package/dist/lib/components/FolderActionsMenu/index.d.ts +21 -0
  22. package/dist/lib/components/FolderActionsMenu/index.d.ts.map +1 -0
  23. package/dist/lib/components/FolderActionsMenu/index.js +25 -0
  24. package/dist/lib/components/MediaAltTextWidget/index.d.ts +9 -0
  25. package/dist/lib/components/MediaAltTextWidget/index.d.ts.map +1 -0
  26. package/dist/lib/components/MediaAltTextWidget/index.js +66 -0
  27. package/dist/lib/components/MediaBreadcrumbs/index.d.ts +11 -0
  28. package/dist/lib/components/MediaBreadcrumbs/index.d.ts.map +1 -0
  29. package/dist/lib/components/MediaEmptyState/index.d.ts +13 -0
  30. package/dist/lib/components/MediaEmptyState/index.d.ts.map +1 -0
  31. package/dist/lib/components/MediaEmptyState/index.js +38 -0
  32. package/dist/lib/components/MediaFieldControl/MediaFieldItem/index.d.ts +34 -0
  33. package/dist/lib/components/MediaFieldControl/MediaFieldItem/index.d.ts.map +1 -0
  34. package/dist/lib/components/MediaFieldControl/MediaFieldItem/index.js +117 -0
  35. package/dist/lib/components/MediaFieldControl/index.d.ts +61 -0
  36. package/dist/lib/components/MediaFieldControl/index.d.ts.map +1 -0
  37. package/dist/lib/components/MediaFieldControl/index.js +422 -0
  38. package/dist/lib/components/MediaFoldersNav/MediaFolderTreeItem/index.d.ts +17 -0
  39. package/dist/lib/components/MediaFoldersNav/MediaFolderTreeItem/index.d.ts.map +1 -0
  40. package/dist/lib/components/MediaFoldersNav/MediaFolderTreeItem/index.js +37 -0
  41. package/dist/lib/components/MediaFoldersNav/index.d.ts +22 -0
  42. package/dist/lib/components/MediaFoldersNav/index.d.ts.map +1 -0
  43. package/dist/lib/components/MediaFoldersNav/index.js +40 -0
  44. package/dist/lib/components/MediaGrid/MediaAssetCard/index.d.ts +18 -0
  45. package/dist/lib/components/MediaGrid/MediaAssetCard/index.d.ts.map +1 -0
  46. package/dist/lib/components/MediaGrid/MediaAssetCard/index.js +29 -0
  47. package/dist/lib/components/MediaGrid/MediaFolderCard/index.d.ts +16 -0
  48. package/dist/lib/components/MediaGrid/MediaFolderCard/index.d.ts.map +1 -0
  49. package/dist/lib/components/MediaGrid/MediaFolderCard/index.js +25 -0
  50. package/dist/lib/components/MediaGrid/index.d.ts +24 -0
  51. package/dist/lib/components/MediaGrid/index.d.ts.map +1 -0
  52. package/dist/lib/components/MediaGrid/index.js +20 -0
  53. package/dist/lib/components/MediaKindBadge/index.d.ts +10 -0
  54. package/dist/lib/components/MediaKindBadge/index.d.ts.map +1 -0
  55. package/dist/lib/components/MediaKindBadge/index.js +30 -0
  56. package/dist/lib/components/MediaKindIcon/index.d.ts +11 -0
  57. package/dist/lib/components/MediaKindIcon/index.d.ts.map +1 -0
  58. package/dist/lib/components/MediaKindIcon/index.js +20 -0
  59. package/dist/lib/components/MediaLibrarySkeleton/index.d.ts +18 -0
  60. package/dist/lib/components/MediaLibrarySkeleton/index.d.ts.map +1 -0
  61. package/dist/lib/components/MediaLibrarySkeleton/index.js +43 -0
  62. package/dist/lib/components/MediaPagination/index.d.ts +26 -0
  63. package/dist/lib/components/MediaPagination/index.d.ts.map +1 -0
  64. package/dist/lib/components/MediaPagination/index.js +52 -0
  65. package/dist/lib/components/MediaPickerDialog/MediaPickerTile/index.d.ts +17 -0
  66. package/dist/lib/components/MediaPickerDialog/MediaPickerTile/index.d.ts.map +1 -0
  67. package/dist/lib/components/MediaPickerDialog/MediaPickerTile/index.js +43 -0
  68. package/dist/lib/components/MediaPickerDialog/index.d.ts +21 -0
  69. package/dist/lib/components/MediaPickerDialog/index.d.ts.map +1 -0
  70. package/dist/lib/components/MediaPickerDialog/index.js +208 -0
  71. package/dist/lib/components/MediaSelectionBar/index.d.ts +18 -0
  72. package/dist/lib/components/MediaSelectionBar/index.d.ts.map +1 -0
  73. package/dist/lib/components/MediaSelectionBar/index.js +26 -0
  74. package/dist/lib/components/MediaStorageStat/index.d.ts +9 -0
  75. package/dist/lib/components/MediaStorageStat/index.d.ts.map +1 -0
  76. package/dist/lib/components/MediaStorageStat/index.js +31 -0
  77. package/dist/lib/components/MediaStorageWidget/index.d.ts +10 -0
  78. package/dist/lib/components/MediaStorageWidget/index.d.ts.map +1 -0
  79. package/dist/lib/components/MediaStorageWidget/index.js +93 -0
  80. package/dist/lib/components/MediaTable/MediaAssetRow/index.d.ts +17 -0
  81. package/dist/lib/components/MediaTable/MediaAssetRow/index.d.ts.map +1 -0
  82. package/dist/lib/components/MediaTable/MediaFolderRow/index.d.ts +16 -0
  83. package/dist/lib/components/MediaTable/MediaFolderRow/index.d.ts.map +1 -0
  84. package/dist/lib/components/MediaTable/index.d.ts +26 -0
  85. package/dist/lib/components/MediaTable/index.d.ts.map +1 -0
  86. package/dist/lib/components/MediaThumbnail/index.d.ts +19 -0
  87. package/dist/lib/components/MediaThumbnail/index.d.ts.map +1 -0
  88. package/dist/lib/components/MediaThumbnail/index.js +26 -0
  89. package/dist/lib/components/MediaToolbar/index.d.ts +19 -0
  90. package/dist/lib/components/MediaToolbar/index.d.ts.map +1 -0
  91. package/dist/lib/components/MediaToolbar/index.js +62 -0
  92. package/dist/lib/components/MediaTopBar/index.d.ts +20 -0
  93. package/dist/lib/components/MediaTopBar/index.d.ts.map +1 -0
  94. package/dist/lib/components/MediaTopBar/index.js +35 -0
  95. package/dist/lib/components/MediaUploadBanner/MediaUploadRow/index.d.ts +14 -0
  96. package/dist/lib/components/MediaUploadBanner/MediaUploadRow/index.d.ts.map +1 -0
  97. package/dist/lib/components/MediaUploadBanner/MediaUploadRow/index.js +51 -0
  98. package/dist/lib/components/MediaUploadBanner/index.d.ts +24 -0
  99. package/dist/lib/components/MediaUploadBanner/index.d.ts.map +1 -0
  100. package/dist/lib/components/MediaUploadBanner/index.js +99 -0
  101. package/dist/lib/components/MediaUploadsWidget/index.d.ts +7 -0
  102. package/dist/lib/components/MediaUploadsWidget/index.d.ts.map +1 -0
  103. package/dist/lib/components/MediaUploadsWidget/index.js +70 -0
  104. package/dist/lib/components/MoveAssetsDialog/DestinationOption/index.d.ts +19 -0
  105. package/dist/lib/components/MoveAssetsDialog/DestinationOption/index.d.ts.map +1 -0
  106. package/dist/lib/components/MoveAssetsDialog/DestinationOption/index.js +10 -0
  107. package/dist/lib/components/MoveAssetsDialog/index.d.ts +18 -0
  108. package/dist/lib/components/MoveAssetsDialog/index.d.ts.map +1 -0
  109. package/dist/lib/components/MoveAssetsDialog/index.js +41 -0
  110. package/dist/lib/components/NewFolderDialog/index.d.ts +13 -0
  111. package/dist/lib/components/NewFolderDialog/index.d.ts.map +1 -0
  112. package/dist/lib/components/NewFolderDialog/index.js +45 -0
  113. package/dist/lib/components/RenameDialog/index.d.ts +13 -0
  114. package/dist/lib/components/RenameDialog/index.d.ts.map +1 -0
  115. package/dist/lib/components/RenameDialog/index.js +37 -0
  116. package/dist/lib/components/UploadDialog/StagedFileRow/index.d.ts +25 -0
  117. package/dist/lib/components/UploadDialog/StagedFileRow/index.d.ts.map +1 -0
  118. package/dist/lib/components/UploadDialog/StagedFileRow/index.js +82 -0
  119. package/dist/lib/components/UploadDialog/index.d.ts +50 -0
  120. package/dist/lib/components/UploadDialog/index.d.ts.map +1 -0
  121. package/dist/lib/components/UploadDialog/index.js +120 -0
  122. package/dist/lib/components/WysiwygLibrarySource/index.d.ts +14 -0
  123. package/dist/lib/components/WysiwygLibrarySource/index.d.ts.map +1 -0
  124. package/dist/lib/components/WysiwygLibrarySource/index.js +28 -0
  125. package/dist/lib/components/WysiwygUploadSource/index.d.ts +25 -0
  126. package/dist/lib/components/WysiwygUploadSource/index.d.ts.map +1 -0
  127. package/dist/lib/components/WysiwygUploadSource/index.js +102 -0
  128. package/dist/lib/constants/index.d.ts +82 -0
  129. package/dist/lib/constants/index.d.ts.map +1 -0
  130. package/dist/lib/constants/index.js +75 -0
  131. package/dist/lib/hooks/useMediaInsights/index.d.ts +32 -0
  132. package/dist/lib/hooks/useMediaInsights/index.d.ts.map +1 -0
  133. package/dist/lib/hooks/useMediaInsights/index.js +85 -0
  134. package/dist/lib/hooks/useMediaLibrary/index.d.ts +83 -0
  135. package/dist/lib/hooks/useMediaLibrary/index.d.ts.map +1 -0
  136. package/dist/lib/hooks/useMediaLibrary/index.js +341 -0
  137. package/dist/lib/hooks/usePendingMediaUploads/index.d.ts +26 -0
  138. package/dist/lib/hooks/usePendingMediaUploads/index.d.ts.map +1 -0
  139. package/dist/lib/hooks/usePendingMediaUploads/index.js +217 -0
  140. package/dist/lib/hooks/useUploadQueue/index.d.ts +33 -0
  141. package/dist/lib/hooks/useUploadQueue/index.d.ts.map +1 -0
  142. package/dist/lib/hooks/useUploadQueue/index.js +218 -0
  143. package/dist/lib/hooks/useWysiwygAssetPicker/index.d.ts +23 -0
  144. package/dist/lib/hooks/useWysiwygAssetPicker/index.d.ts.map +1 -0
  145. package/dist/lib/infrastructure/apiMessage/index.d.ts +17 -0
  146. package/dist/lib/infrastructure/apiMessage/index.d.ts.map +1 -0
  147. package/dist/lib/infrastructure/apiMessage/index.js +30 -0
  148. package/dist/lib/infrastructure/httpMediaGateway/index.d.ts +9 -0
  149. package/dist/lib/infrastructure/httpMediaGateway/index.d.ts.map +1 -0
  150. package/dist/lib/infrastructure/httpMediaGateway/index.js +157 -0
  151. package/dist/lib/infrastructure/httpMediaInsightsGateway/index.d.ts +8 -0
  152. package/dist/lib/infrastructure/httpMediaInsightsGateway/index.d.ts.map +1 -0
  153. package/dist/lib/infrastructure/httpMediaInsightsGateway/index.js +35 -0
  154. package/dist/lib/infrastructure/mediaGateway/index.d.ts +120 -0
  155. package/dist/lib/infrastructure/mediaGateway/index.d.ts.map +1 -0
  156. package/dist/lib/infrastructure/mediaGateway/index.js +1 -0
  157. package/dist/lib/infrastructure/mediaInsightsGateway/index.d.ts +46 -0
  158. package/dist/lib/infrastructure/mediaInsightsGateway/index.d.ts.map +1 -0
  159. package/dist/lib/infrastructure/mediaInsightsGateway/index.js +6 -0
  160. package/dist/lib/infrastructure/mediaKeys/index.d.ts +23 -0
  161. package/dist/lib/infrastructure/mediaKeys/index.d.ts.map +1 -0
  162. package/dist/lib/infrastructure/mediaKeys/index.js +11 -0
  163. package/dist/lib/infrastructure/mediaMapper/index.d.ts +50 -0
  164. package/dist/lib/infrastructure/mediaMapper/index.d.ts.map +1 -0
  165. package/dist/lib/infrastructure/mediaMapper/index.js +39 -0
  166. package/dist/lib/pages/MediaLibraryPage/index.d.ts +23 -0
  167. package/dist/lib/pages/MediaLibraryPage/index.d.ts.map +1 -0
  168. package/dist/lib/pages/MediaLibraryPage/index.js +425 -0
  169. package/dist/lib/types/mediaAsset/index.d.ts +44 -0
  170. package/dist/lib/types/mediaAsset/index.d.ts.map +1 -0
  171. package/dist/lib/types/mediaAsset/index.js +1 -0
  172. package/dist/lib/types/mediaFieldDisplay/index.d.ts +48 -0
  173. package/dist/lib/types/mediaFieldDisplay/index.d.ts.map +1 -0
  174. package/dist/lib/types/mediaFieldDisplay/index.js +1 -0
  175. package/dist/lib/types/mediaFolder/index.d.ts +16 -0
  176. package/dist/lib/types/mediaFolder/index.d.ts.map +1 -0
  177. package/dist/lib/types/mediaFolder/index.js +1 -0
  178. package/dist/lib/types/pendingUpload/index.d.ts +38 -0
  179. package/dist/lib/types/pendingUpload/index.d.ts.map +1 -0
  180. package/dist/lib/types/pendingUpload/index.js +1 -0
  181. package/dist/lib/types/upload/index.d.ts +37 -0
  182. package/dist/lib/types/upload/index.d.ts.map +1 -0
  183. package/dist/lib/types/upload/index.js +1 -0
  184. package/dist/lib/utils/assetGradient/index.d.ts +8 -0
  185. package/dist/lib/utils/assetGradient/index.d.ts.map +1 -0
  186. package/dist/lib/utils/assetGradient/index.js +16 -0
  187. package/dist/lib/utils/assetHandlers/index.d.ts +11 -0
  188. package/dist/lib/utils/assetHandlers/index.d.ts.map +1 -0
  189. package/dist/lib/utils/assetHandlers/index.js +17 -0
  190. package/dist/lib/utils/folderContents/index.d.ts +17 -0
  191. package/dist/lib/utils/folderContents/index.d.ts.map +1 -0
  192. package/dist/lib/utils/folderContents/index.js +35 -0
  193. package/dist/lib/utils/folderTree/index.d.ts +13 -0
  194. package/dist/lib/utils/folderTree/index.d.ts.map +1 -0
  195. package/dist/lib/utils/folderTree/index.js +15 -0
  196. package/dist/lib/utils/formatBytes/index.d.ts +6 -0
  197. package/dist/lib/utils/formatBytes/index.d.ts.map +1 -0
  198. package/dist/lib/utils/formatBytes/index.js +17 -0
  199. package/dist/lib/utils/formatDuration/index.d.ts +6 -0
  200. package/dist/lib/utils/formatDuration/index.d.ts.map +1 -0
  201. package/dist/lib/utils/formatDuration/index.js +13 -0
  202. package/dist/lib/utils/kindFromMime/index.d.ts +10 -0
  203. package/dist/lib/utils/kindFromMime/index.d.ts.map +1 -0
  204. package/dist/lib/utils/kindFromMime/index.js +27 -0
  205. package/dist/lib/utils/mediaAccept/index.d.ts +30 -0
  206. package/dist/lib/utils/mediaAccept/index.d.ts.map +1 -0
  207. package/dist/lib/utils/mediaAccept/index.js +63 -0
  208. package/dist/lib/utils/mediaPlugin/index.d.ts +15 -0
  209. package/dist/lib/utils/mediaPlugin/index.d.ts.map +1 -0
  210. package/dist/lib/utils/mediaPlugin/index.js +177 -0
  211. package/dist/lib/utils/toWysiwygEmbed/index.d.ts +23 -0
  212. package/dist/lib/utils/toWysiwygEmbed/index.d.ts.map +1 -0
  213. package/dist/lib/utils/toWysiwygEmbed/index.js +44 -0
  214. package/package.json +50 -0
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The bulk-action bar shown once one or more assets are selected — a "{n}
3
+ * selected" count and, permission-gated, Download / Duplicate / Move / Delete,
4
+ * with a Clear control. The count is visual only (the bar mounts/unmounts with
5
+ * the selection); the page owns any spoken announcement.
6
+ */
7
+ export declare function MediaSelectionBar({ count, onDownload, onDuplicate, onMove, onDelete, onClear, canCreate, canUpdate, canDelete }: {
8
+ count: number;
9
+ onDownload: () => void;
10
+ onDuplicate: () => void;
11
+ onMove: () => void;
12
+ onDelete: () => void;
13
+ onClear: () => void;
14
+ canCreate: boolean;
15
+ canUpdate: boolean;
16
+ canDelete: boolean;
17
+ }): import("react").JSX.Element;
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/MediaSelectionBar/index.tsx"],"names":[],"mappings":"AAiBA;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,EAC9B,KAAK,EACL,UAAU,EACV,WAAW,EACX,MAAM,EACN,QAAQ,EACR,OAAO,EACP,SAAS,EACT,SAAS,EACT,SAAS,EACZ,EAAE;IACC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;CACtB,+BAgEA"}
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { defineMessages, useIntl } from 'react-intl';
3
+ import { Button, Separator } from '@orthacms/design-system';
4
+ import { Copy, Download, FolderInput, Trash2, X } from 'lucide-react';
5
+ /** Intl descriptors for {@link MediaSelectionBar}, co-located. */
6
+ const messages = defineMessages({
7
+ count: {
8
+ id: 'media.selection.count',
9
+ defaultMessage: '{count, plural, one {# selected} other {# selected}}'
10
+ },
11
+ download: { id: 'media.selection.download', defaultMessage: 'Download' },
12
+ duplicate: { id: 'media.selection.duplicate', defaultMessage: 'Duplicate' },
13
+ move: { id: 'media.selection.move', defaultMessage: 'Move' },
14
+ delete: { id: 'media.selection.delete', defaultMessage: 'Delete' },
15
+ clear: { id: 'media.selection.clear', defaultMessage: 'Clear selection' }
16
+ });
17
+ /**
18
+ * The bulk-action bar shown once one or more assets are selected — a "{n}
19
+ * selected" count and, permission-gated, Download / Duplicate / Move / Delete,
20
+ * with a Clear control. The count is visual only (the bar mounts/unmounts with
21
+ * the selection); the page owns any spoken announcement.
22
+ */
23
+ export function MediaSelectionBar({ count, onDownload, onDuplicate, onMove, onDelete, onClear, canCreate, canUpdate, canDelete }) {
24
+ const intl = useIntl();
25
+ return (_jsxs("div", { className: "mb-4 flex flex-wrap items-center justify-between gap-3 rounded-xl border bg-muted/40 px-4 py-2", children: [_jsx("span", { className: "text-sm font-medium", children: intl.formatMessage(messages.count, { count }) }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", className: "shadow-none", onClick: onDownload, children: [_jsx(Download, { "aria-hidden": true }), intl.formatMessage(messages.download)] }), canCreate ? (_jsxs(Button, { variant: "outline", size: "sm", className: "shadow-none", onClick: onDuplicate, children: [_jsx(Copy, { "aria-hidden": true }), intl.formatMessage(messages.duplicate)] })) : null, canUpdate ? (_jsxs(Button, { variant: "outline", size: "sm", className: "shadow-none", onClick: onMove, children: [_jsx(FolderInput, { "aria-hidden": true }), intl.formatMessage(messages.move)] })) : null, canDelete ? (_jsxs(Button, { variant: "outline", size: "sm", className: "text-destructive shadow-none hover:text-destructive", onClick: onDelete, children: [_jsx(Trash2, { "aria-hidden": true }), intl.formatMessage(messages.delete)] })) : null, _jsx(Separator, { orientation: "vertical", className: "h-6" }), _jsxs(Button, { variant: "ghost", size: "sm", className: "shadow-none", onClick: onClear, children: [_jsx(X, { "aria-hidden": true }), intl.formatMessage(messages.clear)] })] })] }));
26
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Total media storage for the workspace.
3
+ *
4
+ * `formatBytes` returns a value and its unit as one string, so the figure and
5
+ * the unit are split here rather than passed to `unit` — keeping the number in
6
+ * the tabular figure style while the unit stays secondary.
7
+ */
8
+ export declare function MediaStorageStat(): import("react").JSX.Element;
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/MediaStorageStat/index.tsx"],"names":[],"mappings":"AAiBA;;;;;;GAMG;AACH,wBAAgB,gBAAgB,gCAmB/B"}
@@ -0,0 +1,31 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { defineMessages, useIntl } from 'react-intl';
3
+ import { StatWidget } from '@orthacms/insights-admin';
4
+ import { useMediaStorage } from '../../hooks/useMediaInsights';
5
+ import { formatBytes } from '../../utils/formatBytes';
6
+ /** Intl descriptors for the storage stat tile, co-located here. */
7
+ const messages = defineMessages({
8
+ label: {
9
+ id: 'media.insights.storage.label',
10
+ defaultMessage: 'Media storage'
11
+ },
12
+ assets: {
13
+ id: 'media.insights.storage.assets',
14
+ defaultMessage: '{count, number} assets'
15
+ }
16
+ });
17
+ /**
18
+ * Total media storage for the workspace.
19
+ *
20
+ * `formatBytes` returns a value and its unit as one string, so the figure and
21
+ * the unit are split here rather than passed to `unit` — keeping the number in
22
+ * the tabular figure style while the unit stays secondary.
23
+ */
24
+ export function MediaStorageStat() {
25
+ const intl = useIntl();
26
+ const { data, isPending, isError } = useMediaStorage();
27
+ const [amount, unit] = formatBytes(data?.totalBytes ?? 0).split(' ');
28
+ return (_jsx(StatWidget, { label: intl.formatMessage(messages.label), value: amount, unit: unit, delta: intl.formatMessage(messages.assets, {
29
+ count: data?.totalCount ?? 0
30
+ }), deltaTone: "flat", isPending: isPending, isError: isError }));
31
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Storage by media kind, measured in **bytes**.
3
+ *
4
+ * Bars are bytes and only bytes. An earlier draft scaled the bar by asset count
5
+ * while the readout showed size, which put two different measures on one row and
6
+ * made the widget's whole point — that a few videos outweigh thousands of images
7
+ * — impossible to see.
8
+ */
9
+ export declare function MediaStorageWidget(): import("react").JSX.Element;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/MediaStorageWidget/index.tsx"],"names":[],"mappings":"AAsDA;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,gCA4DjC"}
@@ -0,0 +1,93 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { defineMessages, useIntl } from 'react-intl';
3
+ import { BarRows, WidgetCard } from '@orthacms/insights-admin';
4
+ import { useMediaStorage } from '../../hooks/useMediaInsights';
5
+ import { formatBytes } from '../../utils/formatBytes';
6
+ /** Intl descriptors for the storage widget, co-located here. */
7
+ const messages = defineMessages({
8
+ title: {
9
+ id: 'media.insights.storageBreakdown.title',
10
+ defaultMessage: "What's using the storage"
11
+ },
12
+ description: {
13
+ id: 'media.insights.storageBreakdown.description',
14
+ defaultMessage: '{bytes} across {count, number} assets'
15
+ },
16
+ tip: {
17
+ id: 'media.insights.storageBreakdown.tip',
18
+ defaultMessage: '{kind} — {bytes} across {count, number} assets'
19
+ },
20
+ footer: {
21
+ id: 'media.insights.storageBreakdown.footer',
22
+ defaultMessage: '{kind} is {countShare}% of the assets and {byteShare}% of the storage.'
23
+ },
24
+ image: { id: 'media.insights.kind.image', defaultMessage: 'Image' },
25
+ video: { id: 'media.insights.kind.video', defaultMessage: 'Video' },
26
+ audio: { id: 'media.insights.kind.audio', defaultMessage: 'Audio' },
27
+ document: {
28
+ id: 'media.insights.kind.document',
29
+ defaultMessage: 'Document'
30
+ },
31
+ archive: { id: 'media.insights.kind.archive', defaultMessage: 'Archive' }
32
+ });
33
+ /**
34
+ * Ramp steps by rank, deepest first.
35
+ *
36
+ * Here the ramp genuinely does encode magnitude — the rows arrive sorted by
37
+ * bytes — so colour and length say the same thing. That is the one case where
38
+ * ranking the ramp is correct rather than misleading.
39
+ */
40
+ const RANK_TONES = ['q5', 'q4', 'q3', 'q2', 'q1'];
41
+ /** Falls back to the raw kind if the server ever adds one the UI doesn't know. */
42
+ function kindLabel(kind, intl) {
43
+ const descriptor = messages[kind];
44
+ return descriptor ? intl.formatMessage(descriptor) : kind;
45
+ }
46
+ /**
47
+ * Storage by media kind, measured in **bytes**.
48
+ *
49
+ * Bars are bytes and only bytes. An earlier draft scaled the bar by asset count
50
+ * while the readout showed size, which put two different measures on one row and
51
+ * made the widget's whole point — that a few videos outweigh thousands of images
52
+ * — impossible to see.
53
+ */
54
+ export function MediaStorageWidget() {
55
+ const intl = useIntl();
56
+ const { data, isPending, isError } = useMediaStorage();
57
+ const kinds = data?.kinds ?? [];
58
+ const max = Math.max(...kinds.map((kind) => kind.bytes), 0);
59
+ const rows = kinds.map((kind, index) => {
60
+ const label = kindLabel(kind.kind, intl);
61
+ return {
62
+ id: kind.kind,
63
+ label,
64
+ segments: [
65
+ {
66
+ id: kind.kind,
67
+ value: kind.bytes,
68
+ tone: RANK_TONES[index] ?? 'q1',
69
+ label: intl.formatMessage(messages.tip, {
70
+ kind: label,
71
+ bytes: formatBytes(kind.bytes),
72
+ count: kind.count
73
+ })
74
+ }
75
+ ],
76
+ readout: formatBytes(kind.bytes)
77
+ };
78
+ });
79
+ // The heaviest kind is the one worth calling out, and it is first because
80
+ // the server sorts by bytes.
81
+ const heaviest = kinds[0];
82
+ const footer = heaviest && data && data.totalBytes > 0 && data.totalCount > 0
83
+ ? intl.formatMessage(messages.footer, {
84
+ kind: kindLabel(heaviest.kind, intl),
85
+ countShare: Math.round((heaviest.count / data.totalCount) * 100),
86
+ byteShare: Math.round((heaviest.bytes / data.totalBytes) * 100)
87
+ })
88
+ : undefined;
89
+ return (_jsx(WidgetCard, { title: intl.formatMessage(messages.title), description: intl.formatMessage(messages.description, {
90
+ bytes: formatBytes(data?.totalBytes ?? 0),
91
+ count: data?.totalCount ?? 0
92
+ }), footer: footer, isPending: isPending, isError: isError, isEmpty: kinds.length === 0, skeletonRows: 5, children: _jsx(BarRows, { rows: rows, max: max }) }));
93
+ }
@@ -0,0 +1,17 @@
1
+ import type { MediaAsset } from '../../../types/mediaAsset';
2
+ import { type AssetActionKind } from '../../AssetActionsMenu';
3
+ /**
4
+ * One asset row in the list view — a leading select checkbox, a mini thumbnail +
5
+ * clickable name, the kind badge, size, dimensions, and last-modified date, plus
6
+ * the shared ⋯ menu. Clicking the name opens the detail drawer.
7
+ */
8
+ export declare function MediaAssetRow({ asset, selected, onToggleSelect, onAction, canCreate, canUpdate, canDelete }: {
9
+ asset: MediaAsset;
10
+ selected: boolean;
11
+ onToggleSelect: (id: string) => void;
12
+ onAction: (kind: AssetActionKind, asset: MediaAsset) => void;
13
+ canCreate: boolean;
14
+ canUpdate: boolean;
15
+ canDelete: boolean;
16
+ }): import("react").JSX.Element;
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/MediaTable/MediaAssetRow/index.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAG5D,OAAO,EAEH,KAAK,eAAe,EACvB,MAAM,wBAAwB,CAAC;AAWhC;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAC1B,KAAK,EACL,QAAQ,EACR,cAAc,EACd,QAAQ,EACR,SAAS,EACT,SAAS,EACT,SAAS,EACZ,EAAE;IACC,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,cAAc,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,QAAQ,EAAE,CAAC,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IAC7D,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;CACtB,+BA6EA"}
@@ -0,0 +1,16 @@
1
+ import type { MediaFolder } from '../../../types/mediaFolder';
2
+ /**
3
+ * One folder row in the list view — a folder glyph + clickable name that opens
4
+ * the folder, a "Folder" badge, and its item count, with the shared folder ⋯
5
+ * menu. Folders aren't selectable, so the leading checkbox cell stays empty.
6
+ */
7
+ export declare function MediaFolderRow({ folder, assetCount, onOpen, onRename, onDelete, canUpdate, canDelete }: {
8
+ folder: MediaFolder;
9
+ assetCount: number;
10
+ onOpen: (folderId: string) => void;
11
+ onRename: (folder: MediaFolder) => void;
12
+ onDelete: (folder: MediaFolder) => void;
13
+ canUpdate: boolean;
14
+ canDelete: boolean;
15
+ }): import("react").JSX.Element;
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/MediaTable/MediaFolderRow/index.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAc9D;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAC3B,MAAM,EACN,UAAU,EACV,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,SAAS,EACZ,EAAE;IACC,MAAM,EAAE,WAAW,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,QAAQ,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC;IACxC,QAAQ,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC;IACxC,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;CACtB,+BA+DA"}
@@ -0,0 +1,26 @@
1
+ import type { MediaAsset } from '../../types/mediaAsset';
2
+ import type { MediaFolder } from '../../types/mediaFolder';
3
+ import type { AssetActionKind } from '../AssetActionsMenu';
4
+ /**
5
+ * The list view of the open folder — a table with folder rows above asset rows.
6
+ * The header carries a select-all checkbox (toggling every visible asset) and
7
+ * columns that progressively hide on narrower viewports. Purely presentational:
8
+ * selection and actions dispatch up to the page.
9
+ */
10
+ export declare function MediaTable({ folders, assets, folderCounts, selectedIds, allSelected, onToggleAll, onOpenFolder, onRenameFolder, onDeleteFolder, onToggleSelect, onAssetAction, canCreate, canUpdate, canDelete }: {
11
+ folders: MediaFolder[];
12
+ assets: MediaAsset[];
13
+ folderCounts: Map<string, number>;
14
+ selectedIds: Set<string>;
15
+ allSelected: boolean;
16
+ onToggleAll: () => void;
17
+ onOpenFolder: (folderId: string) => void;
18
+ onRenameFolder: (folder: MediaFolder) => void;
19
+ onDeleteFolder: (folder: MediaFolder) => void;
20
+ onToggleSelect: (id: string) => void;
21
+ onAssetAction: (kind: AssetActionKind, asset: MediaAsset) => void;
22
+ canCreate: boolean;
23
+ canUpdate: boolean;
24
+ canDelete: boolean;
25
+ }): import("react").JSX.Element;
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/MediaTable/index.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAe3D;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,EACvB,OAAO,EACP,MAAM,EACN,YAAY,EACZ,WAAW,EACX,WAAW,EACX,WAAW,EACX,YAAY,EACZ,cAAc,EACd,cAAc,EACd,cAAc,EACd,aAAa,EACb,SAAS,EACT,SAAS,EACT,SAAS,EACZ,EAAE;IACC,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,YAAY,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,cAAc,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC;IAC9C,cAAc,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC;IAC9C,cAAc,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACrC,aAAa,EAAE,CAAC,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IAClE,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;CACtB,+BAuEA"}
@@ -0,0 +1,19 @@
1
+ import type { MediaAsset } from '../../types/mediaAsset';
2
+ /**
3
+ * The visual preview for an asset. Image assets render a real thumbnail served
4
+ * from the media API — the small `thumb` derivative by default, or the larger
5
+ * `preview` when `size="preview"` (the detail drawer) — falling back to the
6
+ * original when no derivative exists (e.g. SVG). A deterministic gradient tile
7
+ * sits underneath as the backdrop / loading state; non-image kinds show it with
8
+ * a centred kind glyph. Video/audio get a play affordance and a duration pill.
9
+ * Sized entirely by the parent via `className` (aspect + radius), so it serves
10
+ * tiles, list rows, and the drawer.
11
+ */
12
+ export declare function MediaThumbnail({ asset, className, iconClassName, size }: {
13
+ asset: MediaAsset;
14
+ className?: string;
15
+ iconClassName?: string;
16
+ /** Which derivative to prefer — the grid uses `thumb`, the drawer `preview`. */
17
+ size?: 'thumb' | 'preview';
18
+ }): import("react").JSX.Element;
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/MediaThumbnail/index.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAKzD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,EAC3B,KAAK,EACL,SAAS,EACT,aAAa,EACb,IAAc,EACjB,EAAE;IACC,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gFAAgF;IAChF,IAAI,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC9B,+BA4CA"}
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Play } from 'lucide-react';
3
+ import { cn } from '@orthacms/design-system';
4
+ import { MEDIA_KIND } from '../../constants';
5
+ import { assetGradient } from '../../utils/assetGradient';
6
+ import { formatDuration } from '../../utils/formatDuration';
7
+ import { MediaKindIcon } from '../MediaKindIcon';
8
+ /**
9
+ * The visual preview for an asset. Image assets render a real thumbnail served
10
+ * from the media API — the small `thumb` derivative by default, or the larger
11
+ * `preview` when `size="preview"` (the detail drawer) — falling back to the
12
+ * original when no derivative exists (e.g. SVG). A deterministic gradient tile
13
+ * sits underneath as the backdrop / loading state; non-image kinds show it with
14
+ * a centred kind glyph. Video/audio get a play affordance and a duration pill.
15
+ * Sized entirely by the parent via `className` (aspect + radius), so it serves
16
+ * tiles, list rows, and the drawer.
17
+ */
18
+ export function MediaThumbnail({ asset, className, iconClassName, size = 'thumb' }) {
19
+ const isImage = asset.kind === MEDIA_KIND.Image;
20
+ const isPlayable = asset.kind === MEDIA_KIND.Video || asset.kind === MEDIA_KIND.Audio;
21
+ // Prefer the requested derivative, then the other, then the original.
22
+ const imageSrc = size === 'preview'
23
+ ? (asset.previewUrl ?? asset.thumbUrl ?? asset.url)
24
+ : (asset.thumbUrl ?? asset.previewUrl ?? asset.url);
25
+ return (_jsxs("div", { className: cn('relative flex items-center justify-center overflow-hidden bg-muted', className), style: { backgroundImage: assetGradient(asset.id) }, children: [_jsx("div", { className: "absolute inset-0 bg-black/5", "aria-hidden": true }), isImage ? (_jsx("img", { src: imageSrc, alt: asset.alt ?? '', loading: "lazy", className: "absolute inset-0 size-full object-cover" })) : (_jsx(MediaKindIcon, { kind: asset.kind, className: cn('relative size-8 text-white/90 drop-shadow-sm', iconClassName) })), isPlayable ? (_jsxs("span", { className: "absolute bottom-1.5 right-1.5 inline-flex items-center gap-1 rounded-md bg-black/55 px-1.5 py-0.5 text-[11px] font-medium text-white", children: [_jsx(Play, { className: "size-3 fill-current", "aria-hidden": true }), asset.duration ? formatDuration(asset.duration) : null] })) : null] }));
26
+ }
@@ -0,0 +1,19 @@
1
+ import { type MediaSort } from '../../constants';
2
+ import type { KindFilter } from '../../hooks/useMediaLibrary';
3
+ /**
4
+ * The Media Library's control bar — a search box, a type filter, a sort select,
5
+ * and (permission-gated) New folder + Upload actions. Fully controlled: every
6
+ * value lives in the page's store and changes dispatch straight back up.
7
+ */
8
+ export declare function MediaToolbar({ search, onSearchChange, kindFilter, onKindFilterChange, sort, onSortChange, onNewFolder, onUpload, canCreate }: {
9
+ search: string;
10
+ onSearchChange: (value: string) => void;
11
+ kindFilter: KindFilter;
12
+ onKindFilterChange: (value: KindFilter) => void;
13
+ sort: MediaSort;
14
+ onSortChange: (value: MediaSort) => void;
15
+ onNewFolder: () => void;
16
+ onUpload: () => void;
17
+ canCreate: boolean;
18
+ }): import("react").JSX.Element;
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/MediaToolbar/index.tsx"],"names":[],"mappings":"AAaA,OAAO,EAIH,KAAK,SAAS,EACjB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAmD9D;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EACzB,MAAM,EACN,cAAc,EACd,UAAU,EACV,kBAAkB,EAClB,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,QAAQ,EACR,SAAS,EACZ,EAAE;IACC,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,UAAU,EAAE,UAAU,CAAC;IACvB,kBAAkB,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IAChD,IAAI,EAAE,SAAS,CAAC;IAChB,YAAY,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IACzC,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;CACtB,+BA0GA"}
@@ -0,0 +1,62 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { defineMessages, useIntl } from 'react-intl';
3
+ import { Button, InputGroup, InputGroupAddon, InputGroupInput, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@orthacms/design-system';
4
+ import { FolderPlus, Search, UploadCloud } from 'lucide-react';
5
+ import { KIND_FILTER_ALL, MEDIA_KIND, MEDIA_SORT } from '../../constants';
6
+ /** Intl descriptors for {@link MediaToolbar}, co-located. */
7
+ const messages = defineMessages({
8
+ search: { id: 'media.toolbar.search', defaultMessage: 'Search assets' },
9
+ searchPlaceholder: {
10
+ id: 'media.toolbar.searchPlaceholder',
11
+ defaultMessage: 'Search name or tag…'
12
+ },
13
+ kindLabel: {
14
+ id: 'media.toolbar.kindLabel',
15
+ defaultMessage: 'Filter by type'
16
+ },
17
+ kindAll: { id: 'media.toolbar.kindAll', defaultMessage: 'All types' },
18
+ kindImage: { id: 'media.toolbar.kindImage', defaultMessage: 'Images' },
19
+ kindVideo: { id: 'media.toolbar.kindVideo', defaultMessage: 'Videos' },
20
+ kindAudio: { id: 'media.toolbar.kindAudio', defaultMessage: 'Audio' },
21
+ kindDocument: {
22
+ id: 'media.toolbar.kindDocument',
23
+ defaultMessage: 'Documents'
24
+ },
25
+ kindArchive: {
26
+ id: 'media.toolbar.kindArchive',
27
+ defaultMessage: 'Archives'
28
+ },
29
+ sortLabel: { id: 'media.toolbar.sortLabel', defaultMessage: 'Sort' },
30
+ sortNewest: {
31
+ id: 'media.toolbar.sortNewest',
32
+ defaultMessage: 'Newest first'
33
+ },
34
+ sortOldest: {
35
+ id: 'media.toolbar.sortOldest',
36
+ defaultMessage: 'Oldest first'
37
+ },
38
+ sortNameAsc: {
39
+ id: 'media.toolbar.sortNameAsc',
40
+ defaultMessage: 'Name A–Z'
41
+ },
42
+ sortNameDesc: {
43
+ id: 'media.toolbar.sortNameDesc',
44
+ defaultMessage: 'Name Z–A'
45
+ },
46
+ sortLargest: { id: 'media.toolbar.sortLargest', defaultMessage: 'Largest' },
47
+ sortSmallest: {
48
+ id: 'media.toolbar.sortSmallest',
49
+ defaultMessage: 'Smallest'
50
+ },
51
+ newFolder: { id: 'media.toolbar.newFolder', defaultMessage: 'New folder' },
52
+ upload: { id: 'media.toolbar.upload', defaultMessage: 'Upload' }
53
+ });
54
+ /**
55
+ * The Media Library's control bar — a search box, a type filter, a sort select,
56
+ * and (permission-gated) New folder + Upload actions. Fully controlled: every
57
+ * value lives in the page's store and changes dispatch straight back up.
58
+ */
59
+ export function MediaToolbar({ search, onSearchChange, kindFilter, onKindFilterChange, sort, onSortChange, onNewFolder, onUpload, canCreate }) {
60
+ const intl = useIntl();
61
+ return (_jsxs("div", { className: "mb-4 flex flex-wrap items-center gap-2 sm:gap-3", children: [_jsxs(InputGroup, { className: "w-full shadow-none sm:w-auto sm:max-w-[280px] sm:flex-1", children: [_jsx(InputGroupAddon, { children: _jsx(Search, {}) }), _jsx(InputGroupInput, { type: "search", value: search, onChange: (event) => onSearchChange(event.target.value), "aria-label": intl.formatMessage(messages.search), placeholder: intl.formatMessage(messages.searchPlaceholder) })] }), _jsxs(Select, { value: kindFilter, onValueChange: (value) => onKindFilterChange(value), children: [_jsx(SelectTrigger, { className: "w-[150px] shadow-none", "aria-label": intl.formatMessage(messages.kindLabel), children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: KIND_FILTER_ALL, children: intl.formatMessage(messages.kindAll) }), _jsx(SelectItem, { value: MEDIA_KIND.Image, children: intl.formatMessage(messages.kindImage) }), _jsx(SelectItem, { value: MEDIA_KIND.Video, children: intl.formatMessage(messages.kindVideo) }), _jsx(SelectItem, { value: MEDIA_KIND.Audio, children: intl.formatMessage(messages.kindAudio) }), _jsx(SelectItem, { value: MEDIA_KIND.Document, children: intl.formatMessage(messages.kindDocument) }), _jsx(SelectItem, { value: MEDIA_KIND.Archive, children: intl.formatMessage(messages.kindArchive) })] })] }), _jsxs(Select, { value: sort, onValueChange: (value) => onSortChange(value), children: [_jsx(SelectTrigger, { className: "w-[150px] shadow-none", "aria-label": intl.formatMessage(messages.sortLabel), children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: MEDIA_SORT.Newest, children: intl.formatMessage(messages.sortNewest) }), _jsx(SelectItem, { value: MEDIA_SORT.Oldest, children: intl.formatMessage(messages.sortOldest) }), _jsx(SelectItem, { value: MEDIA_SORT.NameAsc, children: intl.formatMessage(messages.sortNameAsc) }), _jsx(SelectItem, { value: MEDIA_SORT.NameDesc, children: intl.formatMessage(messages.sortNameDesc) }), _jsx(SelectItem, { value: MEDIA_SORT.Largest, children: intl.formatMessage(messages.sortLargest) }), _jsx(SelectItem, { value: MEDIA_SORT.Smallest, children: intl.formatMessage(messages.sortSmallest) })] })] }), _jsx("div", { className: "ml-auto flex items-center gap-2 sm:gap-3", children: canCreate ? (_jsxs(_Fragment, { children: [_jsxs(Button, { variant: "outline", className: "shadow-none", onClick: onNewFolder, children: [_jsx(FolderPlus, { "aria-hidden": true }), _jsx("span", { className: "hidden sm:inline", children: intl.formatMessage(messages.newFolder) })] }), _jsxs(Button, { onClick: onUpload, children: [_jsx(UploadCloud, { "aria-hidden": true }), intl.formatMessage(messages.upload)] })] })) : null })] }));
62
+ }
@@ -0,0 +1,20 @@
1
+ import type { MediaFolder } from '../../types/mediaFolder';
2
+ /**
3
+ * The page top bar over the Media Library — the teal `Image` tile (matching the
4
+ * plugin's workspace-nav entry) beside a breadcrumb tracing the open location:
5
+ * Media Library › each ancestor folder › the open folder. Sticky to the top of
6
+ * the page, spanning the folder sidebar and the browser both, the same
7
+ * page-context header every other admin page carries.
8
+ *
9
+ * Unlike the Content Library's bar this one is **not** route-derived: folder
10
+ * navigation here is component state, not a URL segment, so the crumbs come from
11
+ * the store's `breadcrumbs` and each one dispatches `onNavigate` rather than
12
+ * linking. They are still `BreadcrumbLink`s over real `<button>`s — a control
13
+ * that changes view without changing location is a button, not a link.
14
+ */
15
+ export declare function MediaTopBar({ breadcrumbs, onNavigate }: {
16
+ /** Ancestor chain of the open folder, root-first (excludes the root itself). */
17
+ breadcrumbs: MediaFolder[];
18
+ onNavigate: (folderId: string) => void;
19
+ }): import("react").JSX.Element;
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/MediaTopBar/index.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAQ3D;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,EACxB,WAAW,EACX,UAAU,EACb,EAAE;IACC,gFAAgF;IAChF,WAAW,EAAE,WAAW,EAAE,CAAC;IAC3B,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CAC1C,+BA4CA"}
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { defineMessages, useIntl } from 'react-intl';
3
+ import { Image } from 'lucide-react';
4
+ import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, TopBar, TopBarIcon } from '@orthacms/design-system';
5
+ import { ROOT_FOLDER_ID } from '../../constants';
6
+ /** Intl descriptors for {@link MediaTopBar}, co-located. */
7
+ const messages = defineMessages({
8
+ nav: { id: 'media.topbar.nav', defaultMessage: 'Breadcrumb' },
9
+ root: { id: 'media.topbar.root', defaultMessage: 'Media Library' }
10
+ });
11
+ /**
12
+ * The page top bar over the Media Library — the teal `Image` tile (matching the
13
+ * plugin's workspace-nav entry) beside a breadcrumb tracing the open location:
14
+ * Media Library › each ancestor folder › the open folder. Sticky to the top of
15
+ * the page, spanning the folder sidebar and the browser both, the same
16
+ * page-context header every other admin page carries.
17
+ *
18
+ * Unlike the Content Library's bar this one is **not** route-derived: folder
19
+ * navigation here is component state, not a URL segment, so the crumbs come from
20
+ * the store's `breadcrumbs` and each one dispatches `onNavigate` rather than
21
+ * linking. They are still `BreadcrumbLink`s over real `<button>`s — a control
22
+ * that changes view without changing location is a button, not a link.
23
+ */
24
+ export function MediaTopBar({ breadcrumbs, onNavigate }) {
25
+ const intl = useIntl();
26
+ const crumbs = [
27
+ { id: ROOT_FOLDER_ID, name: intl.formatMessage(messages.root) },
28
+ ...breadcrumbs.map((folder) => ({ id: folder.id, name: folder.name }))
29
+ ];
30
+ const last = crumbs.length - 1;
31
+ return (_jsxs(TopBar, { children: [_jsx(TopBarIcon, { className: "bg-teal-soft text-teal-soft-foreground", children: _jsx(Image, {}) }), _jsx(Breadcrumb, { "aria-label": intl.formatMessage(messages.nav), children: _jsx(BreadcrumbList, { className: "flex-nowrap font-medium", children: crumbs.map((crumb, index) => [
32
+ index > 0 ? (_jsx(BreadcrumbSeparator, {}, `${crumb.id}-sep`)) : null,
33
+ _jsx(BreadcrumbItem, { className: "min-w-0 whitespace-nowrap", children: index === last ? (_jsx(BreadcrumbPage, { className: "flex min-w-0 items-center font-medium", children: crumb.name })) : (_jsx(BreadcrumbLink, { asChild: true, children: _jsx("button", { type: "button", onClick: () => onNavigate(crumb.id), children: crumb.name }) })) }, crumb.id)
34
+ ]) }) })] }));
35
+ }
@@ -0,0 +1,14 @@
1
+ import type { UploadItem } from '../../../types/upload';
2
+ /**
3
+ * One file's row in the upload banner: its name and size, a right-hand status
4
+ * (a live percentage, a tick, or the failure reason), and — while it is running
5
+ * — a thin progress bar. A failed or cancelled row offers **Retry**; a pending
6
+ * or in-flight one offers **Cancel**. A settled row offers neither, so the
7
+ * control column doesn't flicker as the batch drains.
8
+ */
9
+ export declare function MediaUploadRow({ item, onRetry, onCancel }: {
10
+ item: UploadItem;
11
+ onRetry: (id: string) => void;
12
+ onCancel: (id: string) => void;
13
+ }): import("react").JSX.Element;
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/lib/components/MediaUploadBanner/MediaUploadRow/index.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAiBxD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,EAC3B,IAAI,EACJ,OAAO,EACP,QAAQ,EACX,EAAE;IACC,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC,+BA6FA"}
@@ -0,0 +1,51 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { defineMessages, useIntl } from 'react-intl';
3
+ import { Button, Progress, Spinner, cn } from '@orthacms/design-system';
4
+ import { AlertCircle, Check, RotateCcw, X } from 'lucide-react';
5
+ import { UPLOAD_STATUS } from '../../../constants';
6
+ import { formatBytes } from '../../../utils/formatBytes';
7
+ /** Intl descriptors for {@link MediaUploadRow}, co-located. */
8
+ const messages = defineMessages({
9
+ waiting: { id: 'media.uploadRow.waiting', defaultMessage: 'Waiting…' },
10
+ done: { id: 'media.uploadRow.done', defaultMessage: 'Uploaded' },
11
+ cancelled: { id: 'media.uploadRow.cancelled', defaultMessage: 'Cancelled' },
12
+ percent: { id: 'media.uploadRow.percent', defaultMessage: '{percent}%' },
13
+ progressLabel: {
14
+ id: 'media.uploadRow.progressLabel',
15
+ defaultMessage: 'Upload progress for {name}'
16
+ },
17
+ retry: { id: 'media.uploadRow.retry', defaultMessage: 'Retry {name}' },
18
+ cancel: { id: 'media.uploadRow.cancel', defaultMessage: 'Cancel {name}' }
19
+ });
20
+ /**
21
+ * One file's row in the upload banner: its name and size, a right-hand status
22
+ * (a live percentage, a tick, or the failure reason), and — while it is running
23
+ * — a thin progress bar. A failed or cancelled row offers **Retry**; a pending
24
+ * or in-flight one offers **Cancel**. A settled row offers neither, so the
25
+ * control column doesn't flicker as the batch drains.
26
+ */
27
+ export function MediaUploadRow({ item, onRetry, onCancel }) {
28
+ const intl = useIntl();
29
+ const isFailed = item.status === UPLOAD_STATUS.Failed;
30
+ const isDone = item.status === UPLOAD_STATUS.Done;
31
+ const isCancelled = item.status === UPLOAD_STATUS.Cancelled;
32
+ const isUploading = item.status === UPLOAD_STATUS.Uploading;
33
+ const isPending = item.status === UPLOAD_STATUS.Pending;
34
+ return (_jsxs("li", { className: "flex flex-col gap-1 py-1.5", children: [_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [isDone ? (_jsx(Check, { className: "size-4 shrink-0 text-primary", "aria-hidden": true })) : isFailed ? (_jsx(AlertCircle, { className: "size-4 shrink-0 text-destructive", "aria-hidden": true })) : isUploading ? (_jsx(Spinner, { className: "size-4 shrink-0", "aria-hidden": true })) : (_jsx("span", { className: "size-4 shrink-0", "aria-hidden": true })), _jsx("span", { className: cn('min-w-0 flex-1 truncate', isCancelled && 'text-muted-foreground line-through'), children: item.name }), _jsx("span", { className: cn('shrink-0 text-xs tabular-nums text-muted-foreground', isFailed && 'text-destructive'), children: isFailed
35
+ ? (item.error ?? formatBytes(item.size))
36
+ : isDone
37
+ ? intl.formatMessage(messages.done)
38
+ : isCancelled
39
+ ? intl.formatMessage(messages.cancelled)
40
+ : isPending
41
+ ? intl.formatMessage(messages.waiting)
42
+ : intl.formatMessage(messages.percent, {
43
+ percent: item.progress
44
+ }) }), isFailed || isCancelled ? (_jsx(Button, { variant: "ghost", size: "icon", className: "size-7 shrink-0 shadow-none", "aria-label": intl.formatMessage(messages.retry, {
45
+ name: item.name
46
+ }), onClick: () => onRetry(item.id), children: _jsx(RotateCcw, { "aria-hidden": true }) })) : isDone ? null : (_jsx(Button, { variant: "ghost", size: "icon", className: "size-7 shrink-0 shadow-none", "aria-label": intl.formatMessage(messages.cancel, {
47
+ name: item.name
48
+ }), onClick: () => onCancel(item.id), children: _jsx(X, { "aria-hidden": true }) }))] }), isUploading ? (_jsx(Progress, { value: item.progress, className: "h-1", "aria-label": intl.formatMessage(messages.progressLabel, {
49
+ name: item.name
50
+ }) })) : null] }));
51
+ }
@@ -0,0 +1,24 @@
1
+ import type { UploadItem, UploadSummary } from '../../types/upload';
2
+ /**
3
+ * The upload progress banner — the batch headline ("Uploading 3 of 5 · 47%"), an
4
+ * overall size-weighted bar while anything is running, and one
5
+ * {@link MediaUploadRow} per file. It sits above the browser rather than inside
6
+ * the upload dialog, so the dialog can close on submit and the user keeps
7
+ * browsing (and can queue more) while bytes move.
8
+ *
9
+ * `role="status"` sits on the **headline alone**, not the whole banner: a polite
10
+ * live region re-announces its entire contents on every change, so wrapping the
11
+ * file list would read all N rows aloud on each progress tick. Scoped to the
12
+ * headline, a screen-reader user hears "Uploading 3 of 5 · 47%" and then the
13
+ * completion, while per-file detail stays available to read on demand.
14
+ *
15
+ * Renders nothing when the queue is empty.
16
+ */
17
+ export declare function MediaUploadBanner({ items, summary, onRetry, onCancel, onDismiss }: {
18
+ items: UploadItem[];
19
+ summary: UploadSummary;
20
+ onRetry: (id: string) => void;
21
+ onCancel: (id: string) => void;
22
+ onDismiss: () => void;
23
+ }): import("react").JSX.Element | null;
24
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/MediaUploadBanner/index.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAqCpE;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,EAC9B,KAAK,EACL,OAAO,EACP,OAAO,EACP,QAAQ,EACR,SAAS,EACZ,EAAE;IACC,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,SAAS,EAAE,MAAM,IAAI,CAAC;CACzB,sCAwGA"}