@strapi/upload 5.52.0 → 5.52.2

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 (198) hide show
  1. package/dist/admin/future/components/TruncatedText.js +76 -0
  2. package/dist/admin/future/components/TruncatedText.js.map +1 -0
  3. package/dist/admin/future/components/TruncatedText.mjs +74 -0
  4. package/dist/admin/future/components/TruncatedText.mjs.map +1 -0
  5. package/dist/admin/future/components/UploadProgressDialog.js +36 -1
  6. package/dist/admin/future/components/UploadProgressDialog.js.map +1 -1
  7. package/dist/admin/future/components/UploadProgressDialog.mjs +37 -2
  8. package/dist/admin/future/components/UploadProgressDialog.mjs.map +1 -1
  9. package/dist/admin/future/hooks/useApiErrorMessage.js +61 -0
  10. package/dist/admin/future/hooks/useApiErrorMessage.js.map +1 -0
  11. package/dist/admin/future/hooks/useApiErrorMessage.mjs +39 -0
  12. package/dist/admin/future/hooks/useApiErrorMessage.mjs.map +1 -0
  13. package/dist/admin/future/hooks/useTracking.js +57 -0
  14. package/dist/admin/future/hooks/useTracking.js.map +1 -0
  15. package/dist/admin/future/hooks/useTracking.mjs +53 -0
  16. package/dist/admin/future/hooks/useTracking.mjs.map +1 -0
  17. package/dist/admin/future/pages/Assets/AssetsPage.js +31 -0
  18. package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
  19. package/dist/admin/future/pages/Assets/AssetsPage.mjs +31 -0
  20. package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
  21. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js +13 -11
  22. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js.map +1 -1
  23. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs +13 -11
  24. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs.map +1 -1
  25. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +62 -51
  26. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
  27. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +62 -51
  28. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
  29. package/dist/admin/future/pages/Assets/components/AssetsGrid.js +3 -4
  30. package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
  31. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +4 -5
  32. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
  33. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js +11 -1
  34. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js.map +1 -1
  35. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs +11 -1
  36. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs.map +1 -1
  37. package/dist/admin/future/pages/Assets/components/AssetsTable.js +6 -4
  38. package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
  39. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +6 -4
  40. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
  41. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +10 -1
  42. package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -1
  43. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +10 -1
  44. package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -1
  45. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js +3 -2
  46. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js.map +1 -1
  47. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs +3 -2
  48. package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs.map +1 -1
  49. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +4 -2
  50. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
  51. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +4 -2
  52. package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
  53. package/dist/admin/future/pages/Assets/components/FilterMenu.js +18 -2
  54. package/dist/admin/future/pages/Assets/components/FilterMenu.js.map +1 -1
  55. package/dist/admin/future/pages/Assets/components/FilterMenu.mjs +18 -2
  56. package/dist/admin/future/pages/Assets/components/FilterMenu.mjs.map +1 -1
  57. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js +9 -7
  58. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js.map +1 -1
  59. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs +9 -7
  60. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs.map +1 -1
  61. package/dist/admin/future/pages/Assets/components/FolderFormDialog.js +12 -0
  62. package/dist/admin/future/pages/Assets/components/FolderFormDialog.js.map +1 -1
  63. package/dist/admin/future/pages/Assets/components/FolderFormDialog.mjs +12 -0
  64. package/dist/admin/future/pages/Assets/components/FolderFormDialog.mjs.map +1 -1
  65. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +21 -38
  66. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
  67. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +23 -40
  68. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
  69. package/dist/admin/future/pages/Assets/components/SortMenu.js +14 -0
  70. package/dist/admin/future/pages/Assets/components/SortMenu.js.map +1 -1
  71. package/dist/admin/future/pages/Assets/components/SortMenu.mjs +14 -0
  72. package/dist/admin/future/pages/Assets/components/SortMenu.mjs.map +1 -1
  73. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js +5 -0
  74. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js.map +1 -1
  75. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs +6 -1
  76. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs.map +1 -1
  77. package/dist/admin/future/pages/Assets/hooks/useIsAssetDetailsOpen.js +30 -0
  78. package/dist/admin/future/pages/Assets/hooks/useIsAssetDetailsOpen.js.map +1 -0
  79. package/dist/admin/future/pages/Assets/hooks/useIsAssetDetailsOpen.mjs +26 -0
  80. package/dist/admin/future/pages/Assets/hooks/useIsAssetDetailsOpen.mjs.map +1 -0
  81. package/dist/admin/future/pages/Assets/utils/selection.js +17 -0
  82. package/dist/admin/future/pages/Assets/utils/selection.js.map +1 -1
  83. package/dist/admin/future/pages/Assets/utils/selection.mjs +17 -1
  84. package/dist/admin/future/pages/Assets/utils/selection.mjs.map +1 -1
  85. package/dist/admin/future/services/api.js +123 -19
  86. package/dist/admin/future/services/api.js.map +1 -1
  87. package/dist/admin/future/services/api.mjs +125 -21
  88. package/dist/admin/future/services/api.mjs.map +1 -1
  89. package/dist/admin/future/services/assets.js +11 -23
  90. package/dist/admin/future/services/assets.js.map +1 -1
  91. package/dist/admin/future/services/assets.mjs +11 -23
  92. package/dist/admin/future/services/assets.mjs.map +1 -1
  93. package/dist/admin/future/services/uploadFileViaXHR.js +9 -3
  94. package/dist/admin/future/services/uploadFileViaXHR.js.map +1 -1
  95. package/dist/admin/future/services/uploadFileViaXHR.mjs +9 -3
  96. package/dist/admin/future/services/uploadFileViaXHR.mjs.map +1 -1
  97. package/dist/admin/future/store/uploadProgress.js +44 -5
  98. package/dist/admin/future/store/uploadProgress.js.map +1 -1
  99. package/dist/admin/future/store/uploadProgress.mjs +43 -6
  100. package/dist/admin/future/store/uploadProgress.mjs.map +1 -1
  101. package/dist/admin/future/utils/getApiErrorMessage.js +20 -0
  102. package/dist/admin/future/utils/getApiErrorMessage.js.map +1 -0
  103. package/dist/admin/future/utils/getApiErrorMessage.mjs +18 -0
  104. package/dist/admin/future/utils/getApiErrorMessage.mjs.map +1 -0
  105. package/dist/admin/hooks/useAIMetadataJob.js +1 -1
  106. package/dist/admin/hooks/useAIMetadataJob.js.map +1 -1
  107. package/dist/admin/hooks/useAIMetadataJob.mjs +1 -1
  108. package/dist/admin/hooks/useAIMetadataJob.mjs.map +1 -1
  109. package/dist/admin/pages/SettingsPage/SettingsPage.js +2 -2
  110. package/dist/admin/pages/SettingsPage/SettingsPage.js.map +1 -1
  111. package/dist/admin/pages/SettingsPage/SettingsPage.mjs +2 -2
  112. package/dist/admin/pages/SettingsPage/SettingsPage.mjs.map +1 -1
  113. package/dist/admin/src/future/components/TruncatedText.d.ts +32 -0
  114. package/dist/admin/src/future/hooks/useApiErrorMessage.d.ts +17 -0
  115. package/dist/admin/src/future/hooks/useTracking.d.ts +29 -0
  116. package/dist/admin/src/future/pages/Assets/components/AssetActionsMenu.d.ts +5 -3
  117. package/dist/admin/src/future/pages/Assets/components/FolderActionsMenu.d.ts +5 -4
  118. package/dist/admin/src/future/pages/Assets/hooks/useAssetSelection.d.ts +2 -0
  119. package/dist/admin/src/future/pages/Assets/hooks/useIsAssetDetailsOpen.d.ts +18 -0
  120. package/dist/admin/src/future/pages/Assets/utils/selection.d.ts +10 -0
  121. package/dist/admin/src/future/services/api.d.ts +6 -7
  122. package/dist/admin/src/future/services/uploadFileViaXHR.d.ts +6 -1
  123. package/dist/admin/src/future/store/uploadProgress.d.ts +16 -1
  124. package/dist/admin/src/future/utils/getApiErrorMessage.d.ts +1 -0
  125. package/dist/admin/translations/ca.json.js +1 -1
  126. package/dist/admin/translations/ca.json.mjs +1 -1
  127. package/dist/admin/translations/de.json.js +1 -1
  128. package/dist/admin/translations/de.json.mjs +1 -1
  129. package/dist/admin/translations/en.json.js +2 -1
  130. package/dist/admin/translations/en.json.js.map +1 -1
  131. package/dist/admin/translations/en.json.mjs +2 -1
  132. package/dist/admin/translations/en.json.mjs.map +1 -1
  133. package/dist/admin/translations/es.json.js +1 -1
  134. package/dist/admin/translations/es.json.mjs +1 -1
  135. package/dist/admin/translations/fr.json.js +1 -1
  136. package/dist/admin/translations/fr.json.mjs +1 -1
  137. package/dist/admin/translations/ja.json.js +1 -1
  138. package/dist/admin/translations/ja.json.mjs +1 -1
  139. package/dist/admin/translations/ko.json.js +1 -1
  140. package/dist/admin/translations/ko.json.mjs +1 -1
  141. package/dist/admin/translations/nl.json.js +1 -1
  142. package/dist/admin/translations/nl.json.mjs +1 -1
  143. package/dist/admin/translations/pl.json.js +1 -1
  144. package/dist/admin/translations/pl.json.mjs +1 -1
  145. package/dist/admin/translations/pt-BR.json.js +1 -1
  146. package/dist/admin/translations/pt-BR.json.mjs +1 -1
  147. package/dist/admin/translations/ru.json.js +1 -1
  148. package/dist/admin/translations/ru.json.mjs +1 -1
  149. package/dist/admin/translations/sk.json.js +1 -1
  150. package/dist/admin/translations/sk.json.mjs +1 -1
  151. package/dist/admin/translations/tr.json.js +1 -1
  152. package/dist/admin/translations/tr.json.mjs +1 -1
  153. package/dist/admin/translations/uk.json.js +1 -1
  154. package/dist/admin/translations/uk.json.mjs +1 -1
  155. package/dist/admin/translations/zh-Hans.json.js +1 -1
  156. package/dist/admin/translations/zh-Hans.json.mjs +1 -1
  157. package/dist/admin/translations/zh.json.js +1 -1
  158. package/dist/admin/translations/zh.json.mjs +1 -1
  159. package/dist/server/controllers/admin-file.js +27 -9
  160. package/dist/server/controllers/admin-file.js.map +1 -1
  161. package/dist/server/controllers/admin-file.mjs +27 -9
  162. package/dist/server/controllers/admin-file.mjs.map +1 -1
  163. package/dist/server/controllers/admin-upload.js +24 -8
  164. package/dist/server/controllers/admin-upload.js.map +1 -1
  165. package/dist/server/controllers/admin-upload.mjs +24 -8
  166. package/dist/server/controllers/admin-upload.mjs.map +1 -1
  167. package/dist/server/routes/admin.js +41 -21
  168. package/dist/server/routes/admin.js.map +1 -1
  169. package/dist/server/routes/admin.mjs +41 -21
  170. package/dist/server/routes/admin.mjs.map +1 -1
  171. package/dist/server/services/ai-metadata.js +25 -5
  172. package/dist/server/services/ai-metadata.js.map +1 -1
  173. package/dist/server/services/ai-metadata.mjs +1 -1
  174. package/dist/server/services/ai-metadata.mjs.map +1 -1
  175. package/dist/server/services/upload.js +4 -0
  176. package/dist/server/services/upload.js.map +1 -1
  177. package/dist/server/services/upload.mjs +4 -0
  178. package/dist/server/services/upload.mjs.map +1 -1
  179. package/dist/server/src/controllers/admin-file.d.ts +29 -8
  180. package/dist/server/src/controllers/admin-file.d.ts.map +1 -1
  181. package/dist/server/src/controllers/admin-upload.d.ts +20 -4
  182. package/dist/server/src/controllers/admin-upload.d.ts.map +1 -1
  183. package/dist/server/src/controllers/index.d.ts +4 -4
  184. package/dist/server/src/index.d.ts +4 -4
  185. package/dist/server/src/routes/admin.d.ts.map +1 -1
  186. package/dist/server/src/services/ai-metadata.d.ts +2 -2
  187. package/dist/server/src/services/ai-metadata.d.ts.map +1 -1
  188. package/dist/server/src/services/upload.d.ts.map +1 -1
  189. package/dist/shared/contracts/ai-metadata-jobs.d.ts +10 -32
  190. package/dist/shared/contracts/ai-metadata-jobs.d.ts.map +1 -1
  191. package/dist/shared/contracts/files.d.ts +10 -10
  192. package/dist/shared/contracts/files.d.ts.map +1 -1
  193. package/package.json +7 -7
  194. package/dist/admin/future/utils/getBulkMoveErrorMessage.js +0 -23
  195. package/dist/admin/future/utils/getBulkMoveErrorMessage.js.map +0 -1
  196. package/dist/admin/future/utils/getBulkMoveErrorMessage.mjs +0 -21
  197. package/dist/admin/future/utils/getBulkMoveErrorMessage.mjs.map +0 -1
  198. package/dist/admin/src/future/utils/getBulkMoveErrorMessage.d.ts +0 -1
@@ -3,6 +3,7 @@ import { Menu } from '@strapi/design-system';
3
3
  import { ChevronDown, Check } from '@strapi/icons';
4
4
  import { useIntl } from 'react-intl';
5
5
  import { styled } from 'styled-components';
6
+ import { useTracking, MEDIA_LIBRARY_LOCATION } from '../../../hooks/useTracking.mjs';
6
7
  import { getTranslationKey } from '../../../utils/translations.mjs';
7
8
 
8
9
  const SORT_BY_LABELS = {
@@ -68,6 +69,7 @@ const GroupLabel = styled(Menu.Label)`
68
69
  * own `role="menuitem"`.
69
70
  */ const SortMenu = ({ sort, showFoldersGroup = true })=>{
70
71
  const { formatMessage } = useIntl();
72
+ const { trackUsage } = useTracking();
71
73
  const triggerLabel = formatMessage({
72
74
  id: getTranslationKey('list.sort.trigger'),
73
75
  defaultMessage: 'Sort: {active}'
@@ -106,6 +108,12 @@ const GroupLabel = styled(Menu.Label)`
106
108
  "aria-checked": sort.sortBy === key,
107
109
  onSelect: (e)=>{
108
110
  e.preventDefault();
111
+ if (sort.sortBy !== key) {
112
+ trackUsage('didSortMediaLibraryElements', {
113
+ location: MEDIA_LIBRARY_LOCATION,
114
+ sort: key
115
+ });
116
+ }
109
117
  sort.setSortBy(sort.sortBy === key ? null : key);
110
118
  },
111
119
  endIcon: sort.sortBy === key ? checkmark : null,
@@ -116,6 +124,12 @@ const GroupLabel = styled(Menu.Label)`
116
124
  "aria-checked": sort.direction === key,
117
125
  onSelect: (e)=>{
118
126
  e.preventDefault();
127
+ if (sort.direction !== key) {
128
+ trackUsage('didSortMediaLibraryElements', {
129
+ location: MEDIA_LIBRARY_LOCATION,
130
+ sort: key
131
+ });
132
+ }
119
133
  sort.setDirection(sort.direction === key ? null : key);
120
134
  },
121
135
  endIcon: sort.direction === key ? checkmark : null,
@@ -1 +1 @@
1
- {"version":3,"file":"SortMenu.mjs","sources":["../../../../../../admin/src/future/pages/Assets/components/SortMenu.tsx"],"sourcesContent":["import { Menu } from '@strapi/design-system';\nimport { Check, ChevronDown } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { styled } from 'styled-components';\n\nimport { getTranslationKey } from '../../../utils/translations';\nimport {\n type FoldersPosition,\n type ListSort,\n type SortByKey,\n type SortDirectionKey,\n} from '../hooks/useListSort';\n\nimport type { MessageDescriptor } from 'react-intl';\n\nconst SORT_BY_LABELS: Record<SortByKey, MessageDescriptor> = {\n oldestUploads: {\n id: getTranslationKey('list.sort.oldest-uploads'),\n defaultMessage: 'Oldest uploads',\n },\n mostRecentUpdates: {\n id: getTranslationKey('list.sort.most-recent-updates'),\n defaultMessage: 'Most recent updates',\n },\n};\n\nconst SORT_DIRECTION_LABELS: Record<SortDirectionKey, MessageDescriptor> = {\n nameAsc: { id: getTranslationKey('list.sort.name-asc'), defaultMessage: 'A to Z' },\n nameDesc: { id: getTranslationKey('list.sort.name-desc'), defaultMessage: 'Z to A' },\n sizeAsc: {\n id: getTranslationKey('list.sort.size-asc'),\n defaultMessage: 'File size ascending',\n },\n sizeDesc: {\n id: getTranslationKey('list.sort.size-desc'),\n defaultMessage: 'File size descending',\n },\n};\n\nconst FOLDERS_LABELS: Record<FoldersPosition, MessageDescriptor> = {\n top: { id: getTranslationKey('list.sort.folders-on-top'), defaultMessage: 'On top' },\n mixed: {\n id: getTranslationKey('list.sort.folders-mixed'),\n defaultMessage: 'Mixed with files',\n },\n};\n\n// Stretch to the toolbar row height so the trigger matches the view toggle\n// (the parent Flex uses alignItems=\"stretch\").\nconst SortTrigger = styled(Menu.Trigger)``;\n\n// Full-width section band, like the design mock (sits inside the Menu.Content\n// padding, so the band stops short of the panel edges).\nconst GroupLabel = styled(Menu.Label)`\n width: 100%;\n display: block;\n background: ${({ theme }) =>\n theme.colorScheme === 'dark' ? theme.colors.neutral150 : theme.colors.neutral100};\n padding-inline: ${({ theme }) => theme.spaces[3]};\n border-radius: ${({ theme }) => theme.borderRadius};\n`;\n\ninterface SortMenuProps {\n sort: ListSort;\n /**\n * The grid view forces folders on top (mixing folder cards with asset cards\n * is not supported there), so it hides the Folders group entirely.\n */\n showFoldersGroup?: boolean;\n}\n\n/**\n * Toolbar \"Sort\" dropdown: single-select sections (Sort / Folders). Picking an\n * option keeps the menu open (`onSelect` preventDefault) so several facets can\n * be tuned in one visit; clicking a checked facet clears it (the hook\n * guarantees at least one sort rule stays active).\n *\n * Each section is single-select, so its items are radios: the checkmark icon is\n * decorative, and `aria-checked` is what assistive tech reads. The DS `Menu`\n * exposes no `RadioItem`, but `Menu.Item` forwards extra props onto the element\n * it renders through Radix's `asChild`, where they take precedence over Radix's\n * own `role=\"menuitem\"`.\n */\nexport const SortMenu = ({ sort, showFoldersGroup = true }: SortMenuProps) => {\n const { formatMessage } = useIntl();\n\n const triggerLabel = formatMessage(\n { id: getTranslationKey('list.sort.trigger'), defaultMessage: 'Sort: {active}' },\n {\n active: sort.sortBy\n ? formatMessage(SORT_BY_LABELS[sort.sortBy])\n : formatMessage(SORT_DIRECTION_LABELS[sort.direction!]),\n }\n );\n\n const checkmark = <Check aria-hidden width=\"1.6rem\" height=\"1.6rem\" fill=\"primary600\" />;\n\n return (\n <Menu.Root>\n <SortTrigger variant=\"ghost\" endIcon={<ChevronDown aria-hidden />}>\n {triggerLabel}\n </SortTrigger>\n {/* The DS default maxHeight (15rem) folds everything after the first\n group behind an invisible scroll — all groups must be visible at\n once. 70vh keeps a scroll on very short viewports. */}\n <Menu.Content popoverPlacement=\"bottom-end\" zIndex={2} maxHeight=\"70vh\" minWidth=\"25rem\">\n {/* One single-select \"Sort\" section: the two rule families were always\n mutually exclusive (one checkmark total), so they share a group. */}\n <GroupLabel>\n {formatMessage({ id: getTranslationKey('list.sort.section'), defaultMessage: 'Sort' })}\n </GroupLabel>\n {(Object.keys(SORT_BY_LABELS) as SortByKey[]).map((key) => (\n <Menu.Item\n key={key}\n role=\"menuitemradio\"\n aria-checked={sort.sortBy === key}\n onSelect={(e: Event) => {\n e.preventDefault();\n sort.setSortBy(sort.sortBy === key ? null : key);\n }}\n endIcon={sort.sortBy === key ? checkmark : null}\n >\n {formatMessage(SORT_BY_LABELS[key])}\n </Menu.Item>\n ))}\n {(Object.keys(SORT_DIRECTION_LABELS) as SortDirectionKey[]).map((key) => (\n <Menu.Item\n key={key}\n role=\"menuitemradio\"\n aria-checked={sort.direction === key}\n onSelect={(e: Event) => {\n e.preventDefault();\n sort.setDirection(sort.direction === key ? null : key);\n }}\n endIcon={sort.direction === key ? checkmark : null}\n >\n {formatMessage(SORT_DIRECTION_LABELS[key])}\n </Menu.Item>\n ))}\n\n {showFoldersGroup && (\n <>\n <Menu.Separator />\n <GroupLabel>\n {formatMessage({\n id: getTranslationKey('list.sort.folders'),\n defaultMessage: 'Folders',\n })}\n </GroupLabel>\n {(Object.keys(FOLDERS_LABELS) as FoldersPosition[]).map((position) => (\n <Menu.Item\n key={position}\n role=\"menuitemradio\"\n aria-checked={sort.foldersPosition === position}\n onSelect={(e: Event) => {\n e.preventDefault();\n sort.setFoldersPosition(position);\n }}\n endIcon={sort.foldersPosition === position ? checkmark : null}\n >\n {formatMessage(FOLDERS_LABELS[position])}\n </Menu.Item>\n ))}\n </>\n )}\n </Menu.Content>\n </Menu.Root>\n );\n};\n"],"names":["SORT_BY_LABELS","oldestUploads","id","getTranslationKey","defaultMessage","mostRecentUpdates","SORT_DIRECTION_LABELS","nameAsc","nameDesc","sizeAsc","sizeDesc","FOLDERS_LABELS","top","mixed","SortTrigger","styled","Menu","Trigger","GroupLabel","Label","theme","colorScheme","colors","neutral150","neutral100","spaces","borderRadius","SortMenu","sort","showFoldersGroup","formatMessage","useIntl","triggerLabel","active","sortBy","direction","checkmark","_jsx","Check","aria-hidden","width","height","fill","_jsxs","Root","variant","endIcon","ChevronDown","Content","popoverPlacement","zIndex","maxHeight","minWidth","Object","keys","map","key","Item","role","aria-checked","onSelect","e","preventDefault","setSortBy","setDirection","_Fragment","Separator","position","foldersPosition","setFoldersPosition"],"mappings":"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"}
1
+ {"version":3,"file":"SortMenu.mjs","sources":["../../../../../../admin/src/future/pages/Assets/components/SortMenu.tsx"],"sourcesContent":["import { Menu } from '@strapi/design-system';\nimport { Check, ChevronDown } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { styled } from 'styled-components';\n\nimport { useTracking, MEDIA_LIBRARY_LOCATION } from '../../../hooks/useTracking';\nimport { getTranslationKey } from '../../../utils/translations';\nimport {\n type FoldersPosition,\n type ListSort,\n type SortByKey,\n type SortDirectionKey,\n} from '../hooks/useListSort';\n\nimport type { MessageDescriptor } from 'react-intl';\n\nconst SORT_BY_LABELS: Record<SortByKey, MessageDescriptor> = {\n oldestUploads: {\n id: getTranslationKey('list.sort.oldest-uploads'),\n defaultMessage: 'Oldest uploads',\n },\n mostRecentUpdates: {\n id: getTranslationKey('list.sort.most-recent-updates'),\n defaultMessage: 'Most recent updates',\n },\n};\n\nconst SORT_DIRECTION_LABELS: Record<SortDirectionKey, MessageDescriptor> = {\n nameAsc: { id: getTranslationKey('list.sort.name-asc'), defaultMessage: 'A to Z' },\n nameDesc: { id: getTranslationKey('list.sort.name-desc'), defaultMessage: 'Z to A' },\n sizeAsc: {\n id: getTranslationKey('list.sort.size-asc'),\n defaultMessage: 'File size ascending',\n },\n sizeDesc: {\n id: getTranslationKey('list.sort.size-desc'),\n defaultMessage: 'File size descending',\n },\n};\n\nconst FOLDERS_LABELS: Record<FoldersPosition, MessageDescriptor> = {\n top: { id: getTranslationKey('list.sort.folders-on-top'), defaultMessage: 'On top' },\n mixed: {\n id: getTranslationKey('list.sort.folders-mixed'),\n defaultMessage: 'Mixed with files',\n },\n};\n\n// Stretch to the toolbar row height so the trigger matches the view toggle\n// (the parent Flex uses alignItems=\"stretch\").\nconst SortTrigger = styled(Menu.Trigger)``;\n\n// Full-width section band, like the design mock (sits inside the Menu.Content\n// padding, so the band stops short of the panel edges).\nconst GroupLabel = styled(Menu.Label)`\n width: 100%;\n display: block;\n background: ${({ theme }) =>\n theme.colorScheme === 'dark' ? theme.colors.neutral150 : theme.colors.neutral100};\n padding-inline: ${({ theme }) => theme.spaces[3]};\n border-radius: ${({ theme }) => theme.borderRadius};\n`;\n\ninterface SortMenuProps {\n sort: ListSort;\n /**\n * The grid view forces folders on top (mixing folder cards with asset cards\n * is not supported there), so it hides the Folders group entirely.\n */\n showFoldersGroup?: boolean;\n}\n\n/**\n * Toolbar \"Sort\" dropdown: single-select sections (Sort / Folders). Picking an\n * option keeps the menu open (`onSelect` preventDefault) so several facets can\n * be tuned in one visit; clicking a checked facet clears it (the hook\n * guarantees at least one sort rule stays active).\n *\n * Each section is single-select, so its items are radios: the checkmark icon is\n * decorative, and `aria-checked` is what assistive tech reads. The DS `Menu`\n * exposes no `RadioItem`, but `Menu.Item` forwards extra props onto the element\n * it renders through Radix's `asChild`, where they take precedence over Radix's\n * own `role=\"menuitem\"`.\n */\nexport const SortMenu = ({ sort, showFoldersGroup = true }: SortMenuProps) => {\n const { formatMessage } = useIntl();\n const { trackUsage } = useTracking();\n\n const triggerLabel = formatMessage(\n { id: getTranslationKey('list.sort.trigger'), defaultMessage: 'Sort: {active}' },\n {\n active: sort.sortBy\n ? formatMessage(SORT_BY_LABELS[sort.sortBy])\n : formatMessage(SORT_DIRECTION_LABELS[sort.direction!]),\n }\n );\n\n const checkmark = <Check aria-hidden width=\"1.6rem\" height=\"1.6rem\" fill=\"primary600\" />;\n\n return (\n <Menu.Root>\n <SortTrigger variant=\"ghost\" endIcon={<ChevronDown aria-hidden />}>\n {triggerLabel}\n </SortTrigger>\n {/* The DS default maxHeight (15rem) folds everything after the first\n group behind an invisible scroll — all groups must be visible at\n once. 70vh keeps a scroll on very short viewports. */}\n <Menu.Content popoverPlacement=\"bottom-end\" zIndex={2} maxHeight=\"70vh\" minWidth=\"25rem\">\n {/* One single-select \"Sort\" section: the two rule families were always\n mutually exclusive (one checkmark total), so they share a group. */}\n <GroupLabel>\n {formatMessage({ id: getTranslationKey('list.sort.section'), defaultMessage: 'Sort' })}\n </GroupLabel>\n {(Object.keys(SORT_BY_LABELS) as SortByKey[]).map((key) => (\n <Menu.Item\n key={key}\n role=\"menuitemradio\"\n aria-checked={sort.sortBy === key}\n onSelect={(e: Event) => {\n e.preventDefault();\n if (sort.sortBy !== key) {\n trackUsage('didSortMediaLibraryElements', {\n location: MEDIA_LIBRARY_LOCATION,\n sort: key,\n });\n }\n sort.setSortBy(sort.sortBy === key ? null : key);\n }}\n endIcon={sort.sortBy === key ? checkmark : null}\n >\n {formatMessage(SORT_BY_LABELS[key])}\n </Menu.Item>\n ))}\n {(Object.keys(SORT_DIRECTION_LABELS) as SortDirectionKey[]).map((key) => (\n <Menu.Item\n key={key}\n role=\"menuitemradio\"\n aria-checked={sort.direction === key}\n onSelect={(e: Event) => {\n e.preventDefault();\n if (sort.direction !== key) {\n trackUsage('didSortMediaLibraryElements', {\n location: MEDIA_LIBRARY_LOCATION,\n sort: key,\n });\n }\n sort.setDirection(sort.direction === key ? null : key);\n }}\n endIcon={sort.direction === key ? checkmark : null}\n >\n {formatMessage(SORT_DIRECTION_LABELS[key])}\n </Menu.Item>\n ))}\n\n {showFoldersGroup && (\n <>\n <Menu.Separator />\n <GroupLabel>\n {formatMessage({\n id: getTranslationKey('list.sort.folders'),\n defaultMessage: 'Folders',\n })}\n </GroupLabel>\n {(Object.keys(FOLDERS_LABELS) as FoldersPosition[]).map((position) => (\n <Menu.Item\n key={position}\n role=\"menuitemradio\"\n aria-checked={sort.foldersPosition === position}\n onSelect={(e: Event) => {\n e.preventDefault();\n sort.setFoldersPosition(position);\n }}\n endIcon={sort.foldersPosition === position ? checkmark : null}\n >\n {formatMessage(FOLDERS_LABELS[position])}\n </Menu.Item>\n ))}\n </>\n )}\n </Menu.Content>\n </Menu.Root>\n );\n};\n"],"names":["SORT_BY_LABELS","oldestUploads","id","getTranslationKey","defaultMessage","mostRecentUpdates","SORT_DIRECTION_LABELS","nameAsc","nameDesc","sizeAsc","sizeDesc","FOLDERS_LABELS","top","mixed","SortTrigger","styled","Menu","Trigger","GroupLabel","Label","theme","colorScheme","colors","neutral150","neutral100","spaces","borderRadius","SortMenu","sort","showFoldersGroup","formatMessage","useIntl","trackUsage","useTracking","triggerLabel","active","sortBy","direction","checkmark","_jsx","Check","aria-hidden","width","height","fill","_jsxs","Root","variant","endIcon","ChevronDown","Content","popoverPlacement","zIndex","maxHeight","minWidth","Object","keys","map","key","Item","role","aria-checked","onSelect","e","preventDefault","location","MEDIA_LIBRARY_LOCATION","setSortBy","setDirection","_Fragment","Separator","position","foldersPosition","setFoldersPosition"],"mappings":"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"}
@@ -28,6 +28,9 @@ const AssetSelectionProvider = ({ children, disabled = false })=>{
28
28
  }, [
29
29
  disabled
30
30
  ]);
31
+ // No `disabled` guard, like `clear`: this only ever shrinks the selection, and a
32
+ // disabled provider has nothing in it (every additive path is guarded).
33
+ const deselect = React.useCallback((key)=>setState((prev)=>selection.deselect(prev, key)), []);
31
34
  const clear = React.useCallback(()=>setState(selection.clearSelection()), []);
32
35
  const selectedIds = React.useMemo(()=>selection.getIdsOfKind(state.selectedKeys, 'asset'), [
33
36
  state.selectedKeys
@@ -44,6 +47,7 @@ const AssetSelectionProvider = ({ children, disabled = false })=>{
44
47
  toggle,
45
48
  selectRange,
46
49
  selectAll,
50
+ deselect,
47
51
  clear
48
52
  }), [
49
53
  state.selectedKeys,
@@ -54,6 +58,7 @@ const AssetSelectionProvider = ({ children, disabled = false })=>{
54
58
  toggle,
55
59
  selectRange,
56
60
  selectAll,
61
+ deselect,
57
62
  clear
58
63
  ]);
59
64
  return React.createElement(AssetSelectionContext.Provider, {
@@ -1 +1 @@
1
- {"version":3,"file":"useAssetSelection.js","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useAssetSelection.ts"],"sourcesContent":["import {\n createContext,\n createElement,\n useCallback,\n useContext,\n useMemo,\n useState,\n type ReactNode,\n} from 'react';\n\nimport {\n clearSelection,\n createEmptySelection,\n getIdsOfKind,\n selectAll as selectAllState,\n selectRange as selectRangeState,\n toggleSelection,\n type ItemKey,\n type SelectionState,\n} from '../utils/selection';\n\n/**\n * Ephemeral, view-state-only multi-select for the future Media Library. Selection\n * is page-scoped and reset on folder navigation or sort/filter changes (the\n * table/grid toggle keeps it — same list, different presentation).\n *\n * The actual computation lives in `../utils/selection.ts` (pure + unit-tested);\n * this hook is a thin React wrapper. Assets and folders share one mechanism —\n * items are tracked as namespaced keys (`asset:1` / `folder:1`) so toggle, range\n * and select-all behave the same for both. Range / select-all receive the\n * ordered key array from the caller (the view owns the rendered order), keeping\n * the context data-source-agnostic.\n */\nexport interface AssetSelection {\n /** Every selected item key (assets and folders), render-order agnostic. */\n selectedKeys: Set<ItemKey>;\n /** Selected asset ids, derived from {@link selectedKeys}. */\n selectedIds: Set<number>;\n /** Selected folder ids, derived from {@link selectedKeys}. */\n selectedFolderIds: Set<number>;\n anchorKey: ItemKey | null;\n isSelected: (key: ItemKey) => boolean;\n /** Additive toggle (Cmd/Ctrl+click, item checkbox). */\n toggle: (key: ItemKey) => void;\n /** Shift+click — selects the contiguous range from the anchor to the target. */\n selectRange: (orderedKeys: ItemKey[], targetKey: ItemKey) => void;\n /** Header checkbox — selects every rendered item (folders and assets). */\n selectAll: (orderedKeys: ItemKey[]) => void;\n /** Close button / folder navigation / list-identity changes. */\n clear: () => void;\n}\n\nconst AssetSelectionContext = createContext<AssetSelection | null>(null);\n\ninterface AssetSelectionProviderProps {\n children: ReactNode;\n /**\n * When true, selection is inert: every mutator is a no-op and nothing ever\n * reads as selected. Every bulk action is gated on `assets.update`, so a user\n * without it has nothing to select for — the views also hide the checkboxes,\n * and this makes the remaining paths (click-to-select, Space key) do nothing.\n */\n disabled?: boolean;\n}\n\nexport const AssetSelectionProvider = ({\n children,\n disabled = false,\n}: AssetSelectionProviderProps) => {\n const [state, setState] = useState<SelectionState>(createEmptySelection);\n\n const isSelected = useCallback(\n (key: ItemKey) => !disabled && state.selectedKeys.has(key),\n [disabled, state.selectedKeys]\n );\n\n const toggle = useCallback(\n (key: ItemKey) => {\n if (disabled) return;\n setState((prev) => toggleSelection(prev, key));\n },\n [disabled]\n );\n\n const selectRange = useCallback(\n (orderedKeys: ItemKey[], targetKey: ItemKey) => {\n if (disabled) return;\n setState((prev) => selectRangeState(prev, orderedKeys, targetKey));\n },\n [disabled]\n );\n\n const selectAll = useCallback(\n (orderedKeys: ItemKey[]) => {\n if (disabled) return;\n setState(selectAllState(orderedKeys));\n },\n [disabled]\n );\n\n const clear = useCallback(() => setState(clearSelection()), []);\n\n const selectedIds = useMemo(\n () => getIdsOfKind(state.selectedKeys, 'asset'),\n [state.selectedKeys]\n );\n const selectedFolderIds = useMemo(\n () => getIdsOfKind(state.selectedKeys, 'folder'),\n [state.selectedKeys]\n );\n\n const value = useMemo<AssetSelection>(\n () => ({\n selectedKeys: state.selectedKeys,\n selectedIds,\n selectedFolderIds,\n anchorKey: state.anchorKey,\n isSelected,\n toggle,\n selectRange,\n selectAll,\n clear,\n }),\n [\n state.selectedKeys,\n selectedIds,\n selectedFolderIds,\n state.anchorKey,\n isSelected,\n toggle,\n selectRange,\n selectAll,\n clear,\n ]\n );\n\n return createElement(AssetSelectionContext.Provider, { value }, children);\n};\n\nexport const useAssetSelection = () => {\n const context = useContext(AssetSelectionContext);\n\n if (!context) {\n throw new Error('useAssetSelection must be used within an AssetSelectionProvider');\n }\n\n return context;\n};\n\nexport const useAssetSelectionOptional = () => useContext(AssetSelectionContext);\n"],"names":["AssetSelectionContext","createContext","AssetSelectionProvider","children","disabled","state","setState","useState","createEmptySelection","isSelected","useCallback","key","selectedKeys","has","toggle","prev","toggleSelection","selectRange","orderedKeys","targetKey","selectRangeState","selectAll","selectAllState","clear","clearSelection","selectedIds","useMemo","getIdsOfKind","selectedFolderIds","value","anchorKey","createElement","Provider","useAssetSelection","context","useContext","Error","useAssetSelectionOptional"],"mappings":"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"}
1
+ {"version":3,"file":"useAssetSelection.js","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useAssetSelection.ts"],"sourcesContent":["import {\n createContext,\n createElement,\n useCallback,\n useContext,\n useMemo,\n useState,\n type ReactNode,\n} from 'react';\n\nimport {\n clearSelection,\n createEmptySelection,\n deselect as deselectState,\n getIdsOfKind,\n selectAll as selectAllState,\n selectRange as selectRangeState,\n toggleSelection,\n type ItemKey,\n type SelectionState,\n} from '../utils/selection';\n\n/**\n * Ephemeral, view-state-only multi-select for the future Media Library. Selection\n * is page-scoped and reset on folder navigation or sort/filter changes (the\n * table/grid toggle keeps it — same list, different presentation).\n *\n * The actual computation lives in `../utils/selection.ts` (pure + unit-tested);\n * this hook is a thin React wrapper. Assets and folders share one mechanism —\n * items are tracked as namespaced keys (`asset:1` / `folder:1`) so toggle, range\n * and select-all behave the same for both. Range / select-all receive the\n * ordered key array from the caller (the view owns the rendered order), keeping\n * the context data-source-agnostic.\n */\nexport interface AssetSelection {\n /** Every selected item key (assets and folders), render-order agnostic. */\n selectedKeys: Set<ItemKey>;\n /** Selected asset ids, derived from {@link selectedKeys}. */\n selectedIds: Set<number>;\n /** Selected folder ids, derived from {@link selectedKeys}. */\n selectedFolderIds: Set<number>;\n anchorKey: ItemKey | null;\n isSelected: (key: ItemKey) => boolean;\n /** Additive toggle (Cmd/Ctrl+click, item checkbox). */\n toggle: (key: ItemKey) => void;\n /** Shift+click — selects the contiguous range from the anchor to the target. */\n selectRange: (orderedKeys: ItemKey[], targetKey: ItemKey) => void;\n /** Header checkbox — selects every rendered item (folders and assets). */\n selectAll: (orderedKeys: ItemKey[]) => void;\n /** Row-level \"...\" menu — drops one key when its item leaves the list (move, delete). */\n deselect: (key: ItemKey) => void;\n /** Close button / folder navigation / list-identity changes. */\n clear: () => void;\n}\n\nconst AssetSelectionContext = createContext<AssetSelection | null>(null);\n\ninterface AssetSelectionProviderProps {\n children: ReactNode;\n /**\n * When true, selection is inert: every mutator is a no-op and nothing ever\n * reads as selected. Every bulk action is gated on `assets.update`, so a user\n * without it has nothing to select for — the views also hide the checkboxes,\n * and this makes the remaining paths (click-to-select, Space key) do nothing.\n */\n disabled?: boolean;\n}\n\nexport const AssetSelectionProvider = ({\n children,\n disabled = false,\n}: AssetSelectionProviderProps) => {\n const [state, setState] = useState<SelectionState>(createEmptySelection);\n\n const isSelected = useCallback(\n (key: ItemKey) => !disabled && state.selectedKeys.has(key),\n [disabled, state.selectedKeys]\n );\n\n const toggle = useCallback(\n (key: ItemKey) => {\n if (disabled) return;\n setState((prev) => toggleSelection(prev, key));\n },\n [disabled]\n );\n\n const selectRange = useCallback(\n (orderedKeys: ItemKey[], targetKey: ItemKey) => {\n if (disabled) return;\n setState((prev) => selectRangeState(prev, orderedKeys, targetKey));\n },\n [disabled]\n );\n\n const selectAll = useCallback(\n (orderedKeys: ItemKey[]) => {\n if (disabled) return;\n setState(selectAllState(orderedKeys));\n },\n [disabled]\n );\n\n // No `disabled` guard, like `clear`: this only ever shrinks the selection, and a\n // disabled provider has nothing in it (every additive path is guarded).\n const deselect = useCallback((key: ItemKey) => setState((prev) => deselectState(prev, key)), []);\n\n const clear = useCallback(() => setState(clearSelection()), []);\n\n const selectedIds = useMemo(\n () => getIdsOfKind(state.selectedKeys, 'asset'),\n [state.selectedKeys]\n );\n const selectedFolderIds = useMemo(\n () => getIdsOfKind(state.selectedKeys, 'folder'),\n [state.selectedKeys]\n );\n\n const value = useMemo<AssetSelection>(\n () => ({\n selectedKeys: state.selectedKeys,\n selectedIds,\n selectedFolderIds,\n anchorKey: state.anchorKey,\n isSelected,\n toggle,\n selectRange,\n selectAll,\n deselect,\n clear,\n }),\n [\n state.selectedKeys,\n selectedIds,\n selectedFolderIds,\n state.anchorKey,\n isSelected,\n toggle,\n selectRange,\n selectAll,\n deselect,\n clear,\n ]\n );\n\n return createElement(AssetSelectionContext.Provider, { value }, children);\n};\n\nexport const useAssetSelection = () => {\n const context = useContext(AssetSelectionContext);\n\n if (!context) {\n throw new Error('useAssetSelection must be used within an AssetSelectionProvider');\n }\n\n return context;\n};\n\nexport const useAssetSelectionOptional = () => useContext(AssetSelectionContext);\n"],"names":["AssetSelectionContext","createContext","AssetSelectionProvider","children","disabled","state","setState","useState","createEmptySelection","isSelected","useCallback","key","selectedKeys","has","toggle","prev","toggleSelection","selectRange","orderedKeys","targetKey","selectRangeState","selectAll","selectAllState","deselect","deselectState","clear","clearSelection","selectedIds","useMemo","getIdsOfKind","selectedFolderIds","value","anchorKey","createElement","Provider","useAssetSelection","context","useContext","Error","useAssetSelectionOptional"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { useState, useCallback, useMemo, createElement, createContext, useContext } from 'react';
2
- import { createEmptySelection, toggleSelection, selectRange, selectAll, clearSelection, getIdsOfKind } from '../utils/selection.mjs';
2
+ import { createEmptySelection, toggleSelection, selectRange, selectAll, deselect, clearSelection, getIdsOfKind } from '../utils/selection.mjs';
3
3
 
4
4
  const AssetSelectionContext = createContext(null);
5
5
  const AssetSelectionProvider = ({ children, disabled = false })=>{
@@ -26,6 +26,9 @@ const AssetSelectionProvider = ({ children, disabled = false })=>{
26
26
  }, [
27
27
  disabled
28
28
  ]);
29
+ // No `disabled` guard, like `clear`: this only ever shrinks the selection, and a
30
+ // disabled provider has nothing in it (every additive path is guarded).
31
+ const deselect$1 = useCallback((key)=>setState((prev)=>deselect(prev, key)), []);
29
32
  const clear = useCallback(()=>setState(clearSelection()), []);
30
33
  const selectedIds = useMemo(()=>getIdsOfKind(state.selectedKeys, 'asset'), [
31
34
  state.selectedKeys
@@ -42,6 +45,7 @@ const AssetSelectionProvider = ({ children, disabled = false })=>{
42
45
  toggle,
43
46
  selectRange: selectRange$1,
44
47
  selectAll: selectAll$1,
48
+ deselect: deselect$1,
45
49
  clear
46
50
  }), [
47
51
  state.selectedKeys,
@@ -52,6 +56,7 @@ const AssetSelectionProvider = ({ children, disabled = false })=>{
52
56
  toggle,
53
57
  selectRange$1,
54
58
  selectAll$1,
59
+ deselect$1,
55
60
  clear
56
61
  ]);
57
62
  return createElement(AssetSelectionContext.Provider, {
@@ -1 +1 @@
1
- {"version":3,"file":"useAssetSelection.mjs","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useAssetSelection.ts"],"sourcesContent":["import {\n createContext,\n createElement,\n useCallback,\n useContext,\n useMemo,\n useState,\n type ReactNode,\n} from 'react';\n\nimport {\n clearSelection,\n createEmptySelection,\n getIdsOfKind,\n selectAll as selectAllState,\n selectRange as selectRangeState,\n toggleSelection,\n type ItemKey,\n type SelectionState,\n} from '../utils/selection';\n\n/**\n * Ephemeral, view-state-only multi-select for the future Media Library. Selection\n * is page-scoped and reset on folder navigation or sort/filter changes (the\n * table/grid toggle keeps it — same list, different presentation).\n *\n * The actual computation lives in `../utils/selection.ts` (pure + unit-tested);\n * this hook is a thin React wrapper. Assets and folders share one mechanism —\n * items are tracked as namespaced keys (`asset:1` / `folder:1`) so toggle, range\n * and select-all behave the same for both. Range / select-all receive the\n * ordered key array from the caller (the view owns the rendered order), keeping\n * the context data-source-agnostic.\n */\nexport interface AssetSelection {\n /** Every selected item key (assets and folders), render-order agnostic. */\n selectedKeys: Set<ItemKey>;\n /** Selected asset ids, derived from {@link selectedKeys}. */\n selectedIds: Set<number>;\n /** Selected folder ids, derived from {@link selectedKeys}. */\n selectedFolderIds: Set<number>;\n anchorKey: ItemKey | null;\n isSelected: (key: ItemKey) => boolean;\n /** Additive toggle (Cmd/Ctrl+click, item checkbox). */\n toggle: (key: ItemKey) => void;\n /** Shift+click — selects the contiguous range from the anchor to the target. */\n selectRange: (orderedKeys: ItemKey[], targetKey: ItemKey) => void;\n /** Header checkbox — selects every rendered item (folders and assets). */\n selectAll: (orderedKeys: ItemKey[]) => void;\n /** Close button / folder navigation / list-identity changes. */\n clear: () => void;\n}\n\nconst AssetSelectionContext = createContext<AssetSelection | null>(null);\n\ninterface AssetSelectionProviderProps {\n children: ReactNode;\n /**\n * When true, selection is inert: every mutator is a no-op and nothing ever\n * reads as selected. Every bulk action is gated on `assets.update`, so a user\n * without it has nothing to select for — the views also hide the checkboxes,\n * and this makes the remaining paths (click-to-select, Space key) do nothing.\n */\n disabled?: boolean;\n}\n\nexport const AssetSelectionProvider = ({\n children,\n disabled = false,\n}: AssetSelectionProviderProps) => {\n const [state, setState] = useState<SelectionState>(createEmptySelection);\n\n const isSelected = useCallback(\n (key: ItemKey) => !disabled && state.selectedKeys.has(key),\n [disabled, state.selectedKeys]\n );\n\n const toggle = useCallback(\n (key: ItemKey) => {\n if (disabled) return;\n setState((prev) => toggleSelection(prev, key));\n },\n [disabled]\n );\n\n const selectRange = useCallback(\n (orderedKeys: ItemKey[], targetKey: ItemKey) => {\n if (disabled) return;\n setState((prev) => selectRangeState(prev, orderedKeys, targetKey));\n },\n [disabled]\n );\n\n const selectAll = useCallback(\n (orderedKeys: ItemKey[]) => {\n if (disabled) return;\n setState(selectAllState(orderedKeys));\n },\n [disabled]\n );\n\n const clear = useCallback(() => setState(clearSelection()), []);\n\n const selectedIds = useMemo(\n () => getIdsOfKind(state.selectedKeys, 'asset'),\n [state.selectedKeys]\n );\n const selectedFolderIds = useMemo(\n () => getIdsOfKind(state.selectedKeys, 'folder'),\n [state.selectedKeys]\n );\n\n const value = useMemo<AssetSelection>(\n () => ({\n selectedKeys: state.selectedKeys,\n selectedIds,\n selectedFolderIds,\n anchorKey: state.anchorKey,\n isSelected,\n toggle,\n selectRange,\n selectAll,\n clear,\n }),\n [\n state.selectedKeys,\n selectedIds,\n selectedFolderIds,\n state.anchorKey,\n isSelected,\n toggle,\n selectRange,\n selectAll,\n clear,\n ]\n );\n\n return createElement(AssetSelectionContext.Provider, { value }, children);\n};\n\nexport const useAssetSelection = () => {\n const context = useContext(AssetSelectionContext);\n\n if (!context) {\n throw new Error('useAssetSelection must be used within an AssetSelectionProvider');\n }\n\n return context;\n};\n\nexport const useAssetSelectionOptional = () => useContext(AssetSelectionContext);\n"],"names":["AssetSelectionContext","createContext","AssetSelectionProvider","children","disabled","state","setState","useState","createEmptySelection","isSelected","useCallback","key","selectedKeys","has","toggle","prev","toggleSelection","selectRange","orderedKeys","targetKey","selectRangeState","selectAll","selectAllState","clear","clearSelection","selectedIds","useMemo","getIdsOfKind","selectedFolderIds","value","anchorKey","createElement","Provider","useAssetSelection","context","useContext","Error","useAssetSelectionOptional"],"mappings":"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"}
1
+ {"version":3,"file":"useAssetSelection.mjs","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useAssetSelection.ts"],"sourcesContent":["import {\n createContext,\n createElement,\n useCallback,\n useContext,\n useMemo,\n useState,\n type ReactNode,\n} from 'react';\n\nimport {\n clearSelection,\n createEmptySelection,\n deselect as deselectState,\n getIdsOfKind,\n selectAll as selectAllState,\n selectRange as selectRangeState,\n toggleSelection,\n type ItemKey,\n type SelectionState,\n} from '../utils/selection';\n\n/**\n * Ephemeral, view-state-only multi-select for the future Media Library. Selection\n * is page-scoped and reset on folder navigation or sort/filter changes (the\n * table/grid toggle keeps it — same list, different presentation).\n *\n * The actual computation lives in `../utils/selection.ts` (pure + unit-tested);\n * this hook is a thin React wrapper. Assets and folders share one mechanism —\n * items are tracked as namespaced keys (`asset:1` / `folder:1`) so toggle, range\n * and select-all behave the same for both. Range / select-all receive the\n * ordered key array from the caller (the view owns the rendered order), keeping\n * the context data-source-agnostic.\n */\nexport interface AssetSelection {\n /** Every selected item key (assets and folders), render-order agnostic. */\n selectedKeys: Set<ItemKey>;\n /** Selected asset ids, derived from {@link selectedKeys}. */\n selectedIds: Set<number>;\n /** Selected folder ids, derived from {@link selectedKeys}. */\n selectedFolderIds: Set<number>;\n anchorKey: ItemKey | null;\n isSelected: (key: ItemKey) => boolean;\n /** Additive toggle (Cmd/Ctrl+click, item checkbox). */\n toggle: (key: ItemKey) => void;\n /** Shift+click — selects the contiguous range from the anchor to the target. */\n selectRange: (orderedKeys: ItemKey[], targetKey: ItemKey) => void;\n /** Header checkbox — selects every rendered item (folders and assets). */\n selectAll: (orderedKeys: ItemKey[]) => void;\n /** Row-level \"...\" menu — drops one key when its item leaves the list (move, delete). */\n deselect: (key: ItemKey) => void;\n /** Close button / folder navigation / list-identity changes. */\n clear: () => void;\n}\n\nconst AssetSelectionContext = createContext<AssetSelection | null>(null);\n\ninterface AssetSelectionProviderProps {\n children: ReactNode;\n /**\n * When true, selection is inert: every mutator is a no-op and nothing ever\n * reads as selected. Every bulk action is gated on `assets.update`, so a user\n * without it has nothing to select for — the views also hide the checkboxes,\n * and this makes the remaining paths (click-to-select, Space key) do nothing.\n */\n disabled?: boolean;\n}\n\nexport const AssetSelectionProvider = ({\n children,\n disabled = false,\n}: AssetSelectionProviderProps) => {\n const [state, setState] = useState<SelectionState>(createEmptySelection);\n\n const isSelected = useCallback(\n (key: ItemKey) => !disabled && state.selectedKeys.has(key),\n [disabled, state.selectedKeys]\n );\n\n const toggle = useCallback(\n (key: ItemKey) => {\n if (disabled) return;\n setState((prev) => toggleSelection(prev, key));\n },\n [disabled]\n );\n\n const selectRange = useCallback(\n (orderedKeys: ItemKey[], targetKey: ItemKey) => {\n if (disabled) return;\n setState((prev) => selectRangeState(prev, orderedKeys, targetKey));\n },\n [disabled]\n );\n\n const selectAll = useCallback(\n (orderedKeys: ItemKey[]) => {\n if (disabled) return;\n setState(selectAllState(orderedKeys));\n },\n [disabled]\n );\n\n // No `disabled` guard, like `clear`: this only ever shrinks the selection, and a\n // disabled provider has nothing in it (every additive path is guarded).\n const deselect = useCallback((key: ItemKey) => setState((prev) => deselectState(prev, key)), []);\n\n const clear = useCallback(() => setState(clearSelection()), []);\n\n const selectedIds = useMemo(\n () => getIdsOfKind(state.selectedKeys, 'asset'),\n [state.selectedKeys]\n );\n const selectedFolderIds = useMemo(\n () => getIdsOfKind(state.selectedKeys, 'folder'),\n [state.selectedKeys]\n );\n\n const value = useMemo<AssetSelection>(\n () => ({\n selectedKeys: state.selectedKeys,\n selectedIds,\n selectedFolderIds,\n anchorKey: state.anchorKey,\n isSelected,\n toggle,\n selectRange,\n selectAll,\n deselect,\n clear,\n }),\n [\n state.selectedKeys,\n selectedIds,\n selectedFolderIds,\n state.anchorKey,\n isSelected,\n toggle,\n selectRange,\n selectAll,\n deselect,\n clear,\n ]\n );\n\n return createElement(AssetSelectionContext.Provider, { value }, children);\n};\n\nexport const useAssetSelection = () => {\n const context = useContext(AssetSelectionContext);\n\n if (!context) {\n throw new Error('useAssetSelection must be used within an AssetSelectionProvider');\n }\n\n return context;\n};\n\nexport const useAssetSelectionOptional = () => useContext(AssetSelectionContext);\n"],"names":["AssetSelectionContext","createContext","AssetSelectionProvider","children","disabled","state","setState","useState","createEmptySelection","isSelected","useCallback","key","selectedKeys","has","toggle","prev","toggleSelection","selectRange","orderedKeys","targetKey","selectRangeState","selectAll","selectAllState","deselect","deselectState","clear","clearSelection","selectedIds","useMemo","getIdsOfKind","selectedFolderIds","value","anchorKey","createElement","Provider","useAssetSelection","context","useContext","Error","useAssetSelectionOptional"],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ 'use strict';
2
+
3
+ var strapiAdmin = require('@strapi/admin/strapi-admin');
4
+
5
+ /** Name of the parameter to look for in the URL to open the asset details drawer. */ const ASSET_DETAILS_URL_PARAM = 'assetId';
6
+ /**
7
+ * The asset id in the URL, or `null` when the param is absent or unparseable.
8
+ *
9
+ * One definition shared by the drawer (which needs the id) and the "is it open?"
10
+ * hook below, so the two can never disagree about what counts as open.
11
+ */ const parseAssetDetailsId = (raw)=>{
12
+ const parsed = raw ? parseInt(raw, 10) : NaN;
13
+ return Number.isNaN(parsed) ? null : parsed;
14
+ };
15
+ /**
16
+ * Whether the asset details drawer is open, derived purely from the URL.
17
+ *
18
+ * Deliberately separate from `useAssetDetailsParam`, which additionally owns the
19
+ * drawer's mount lifecycle: its `shouldRenderDrawer` only stays correct in the
20
+ * component wired to the close animation, so a second caller would strand it at
21
+ * `true`. Anything that just needs the answer reads this instead.
22
+ */ const useIsAssetDetailsOpen = ()=>{
23
+ const [{ query }] = strapiAdmin.useQueryParams();
24
+ return parseAssetDetailsId(query?.[ASSET_DETAILS_URL_PARAM]) !== null;
25
+ };
26
+
27
+ exports.ASSET_DETAILS_URL_PARAM = ASSET_DETAILS_URL_PARAM;
28
+ exports.parseAssetDetailsId = parseAssetDetailsId;
29
+ exports.useIsAssetDetailsOpen = useIsAssetDetailsOpen;
30
+ //# sourceMappingURL=useIsAssetDetailsOpen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useIsAssetDetailsOpen.js","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useIsAssetDetailsOpen.ts"],"sourcesContent":["import { useQueryParams } from '@strapi/admin/strapi-admin';\n\n/** Name of the parameter to look for in the URL to open the asset details drawer. */\nexport const ASSET_DETAILS_URL_PARAM = 'assetId';\n\n/**\n * The asset id in the URL, or `null` when the param is absent or unparseable.\n *\n * One definition shared by the drawer (which needs the id) and the \"is it open?\"\n * hook below, so the two can never disagree about what counts as open.\n */\nexport const parseAssetDetailsId = (raw: string | undefined): number | null => {\n const parsed = raw ? parseInt(raw, 10) : NaN;\n\n return Number.isNaN(parsed) ? null : parsed;\n};\n\n/**\n * Whether the asset details drawer is open, derived purely from the URL.\n *\n * Deliberately separate from `useAssetDetailsParam`, which additionally owns the\n * drawer's mount lifecycle: its `shouldRenderDrawer` only stays correct in the\n * component wired to the close animation, so a second caller would strand it at\n * `true`. Anything that just needs the answer reads this instead.\n */\nexport const useIsAssetDetailsOpen = (): boolean => {\n const [{ query }] = useQueryParams<{ [ASSET_DETAILS_URL_PARAM]?: string }>();\n\n return parseAssetDetailsId(query?.[ASSET_DETAILS_URL_PARAM]) !== null;\n};\n"],"names":["ASSET_DETAILS_URL_PARAM","parseAssetDetailsId","raw","parsed","parseInt","NaN","Number","isNaN","useIsAssetDetailsOpen","query","useQueryParams"],"mappings":";;;;AAEA,sFACO,MAAMA,uBAAAA,GAA0B;AAEvC;;;;;IAMO,MAAMC,mBAAAA,GAAsB,CAACC,GAAAA,GAAAA;AAClC,IAAA,MAAMC,MAAAA,GAASD,GAAAA,GAAME,QAAAA,CAASF,GAAAA,EAAK,EAAA,CAAA,GAAMG,GAAAA;AAEzC,IAAA,OAAOC,MAAAA,CAAOC,KAAK,CAACJ,MAAAA,CAAAA,GAAU,IAAA,GAAOA,MAAAA;AACvC;AAEA;;;;;;;UAQaK,qBAAAA,GAAwB,IAAA;AACnC,IAAA,MAAM,CAAC,EAAEC,KAAK,EAAE,CAAC,GAAGC,0BAAAA,EAAAA;AAEpB,IAAA,OAAOT,mBAAAA,CAAoBQ,KAAAA,GAAQT,uBAAAA,CAAwB,CAAA,KAAM,IAAA;AACnE;;;;;;"}
@@ -0,0 +1,26 @@
1
+ import { useQueryParams } from '@strapi/admin/strapi-admin';
2
+
3
+ /** Name of the parameter to look for in the URL to open the asset details drawer. */ const ASSET_DETAILS_URL_PARAM = 'assetId';
4
+ /**
5
+ * The asset id in the URL, or `null` when the param is absent or unparseable.
6
+ *
7
+ * One definition shared by the drawer (which needs the id) and the "is it open?"
8
+ * hook below, so the two can never disagree about what counts as open.
9
+ */ const parseAssetDetailsId = (raw)=>{
10
+ const parsed = raw ? parseInt(raw, 10) : NaN;
11
+ return Number.isNaN(parsed) ? null : parsed;
12
+ };
13
+ /**
14
+ * Whether the asset details drawer is open, derived purely from the URL.
15
+ *
16
+ * Deliberately separate from `useAssetDetailsParam`, which additionally owns the
17
+ * drawer's mount lifecycle: its `shouldRenderDrawer` only stays correct in the
18
+ * component wired to the close animation, so a second caller would strand it at
19
+ * `true`. Anything that just needs the answer reads this instead.
20
+ */ const useIsAssetDetailsOpen = ()=>{
21
+ const [{ query }] = useQueryParams();
22
+ return parseAssetDetailsId(query?.[ASSET_DETAILS_URL_PARAM]) !== null;
23
+ };
24
+
25
+ export { ASSET_DETAILS_URL_PARAM, parseAssetDetailsId, useIsAssetDetailsOpen };
26
+ //# sourceMappingURL=useIsAssetDetailsOpen.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useIsAssetDetailsOpen.mjs","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useIsAssetDetailsOpen.ts"],"sourcesContent":["import { useQueryParams } from '@strapi/admin/strapi-admin';\n\n/** Name of the parameter to look for in the URL to open the asset details drawer. */\nexport const ASSET_DETAILS_URL_PARAM = 'assetId';\n\n/**\n * The asset id in the URL, or `null` when the param is absent or unparseable.\n *\n * One definition shared by the drawer (which needs the id) and the \"is it open?\"\n * hook below, so the two can never disagree about what counts as open.\n */\nexport const parseAssetDetailsId = (raw: string | undefined): number | null => {\n const parsed = raw ? parseInt(raw, 10) : NaN;\n\n return Number.isNaN(parsed) ? null : parsed;\n};\n\n/**\n * Whether the asset details drawer is open, derived purely from the URL.\n *\n * Deliberately separate from `useAssetDetailsParam`, which additionally owns the\n * drawer's mount lifecycle: its `shouldRenderDrawer` only stays correct in the\n * component wired to the close animation, so a second caller would strand it at\n * `true`. Anything that just needs the answer reads this instead.\n */\nexport const useIsAssetDetailsOpen = (): boolean => {\n const [{ query }] = useQueryParams<{ [ASSET_DETAILS_URL_PARAM]?: string }>();\n\n return parseAssetDetailsId(query?.[ASSET_DETAILS_URL_PARAM]) !== null;\n};\n"],"names":["ASSET_DETAILS_URL_PARAM","parseAssetDetailsId","raw","parsed","parseInt","NaN","Number","isNaN","useIsAssetDetailsOpen","query","useQueryParams"],"mappings":";;AAEA,sFACO,MAAMA,uBAAAA,GAA0B;AAEvC;;;;;IAMO,MAAMC,mBAAAA,GAAsB,CAACC,GAAAA,GAAAA;AAClC,IAAA,MAAMC,MAAAA,GAASD,GAAAA,GAAME,QAAAA,CAASF,GAAAA,EAAK,EAAA,CAAA,GAAMG,GAAAA;AAEzC,IAAA,OAAOC,MAAAA,CAAOC,KAAK,CAACJ,MAAAA,CAAAA,GAAU,IAAA,GAAOA,MAAAA;AACvC;AAEA;;;;;;;UAQaK,qBAAAA,GAAwB,IAAA;AACnC,IAAA,MAAM,CAAC,EAAEC,KAAK,EAAE,CAAC,GAAGC,cAAAA,EAAAA;AAEpB,IAAA,OAAOT,mBAAAA,CAAoBQ,KAAAA,GAAQT,uBAAAA,CAAwB,CAAA,KAAM,IAAA;AACnE;;;;"}
@@ -39,6 +39,22 @@ const createEmptySelection = ()=>({
39
39
  anchorKey: key
40
40
  };
41
41
  };
42
+ /**
43
+ * Subtractive counterpart to {@link toggleSelection} — removes a key without the
44
+ * add-if-absent ambiguity, leaving the rest of the selection alone. Used when a
45
+ * single-item affordance (a row's "..." menu) makes exactly one key stale.
46
+ *
47
+ * Idempotent: removing a key that was never selected is a no-op. The anchor is
48
+ * dropped only when it is the removed key, so a Shift+click range built around
49
+ * other rows survives.
50
+ */ const deselect = (state, key)=>{
51
+ const selectedKeys = new Set(state.selectedKeys);
52
+ selectedKeys.delete(key);
53
+ return {
54
+ selectedKeys,
55
+ anchorKey: state.anchorKey === key ? null : state.anchorKey
56
+ };
57
+ };
42
58
  /**
43
59
  * Shift+click — selects the contiguous range between the current anchor and the
44
60
  * target in render order, replacing the current selection. The anchor stays put.
@@ -95,6 +111,7 @@ const clearSelection = ()=>createEmptySelection();
95
111
  exports.assetKey = assetKey;
96
112
  exports.clearSelection = clearSelection;
97
113
  exports.createEmptySelection = createEmptySelection;
114
+ exports.deselect = deselect;
98
115
  exports.folderKey = folderKey;
99
116
  exports.getIdsOfKind = getIdsOfKind;
100
117
  exports.getSelectAllState = getSelectAllState;
@@ -1 +1 @@
1
- {"version":3,"file":"selection.js","sources":["../../../../../../admin/src/future/pages/Assets/utils/selection.ts"],"sourcesContent":["/**\n * Pure, view-agnostic selection logic for the future Media Library multi-select.\n *\n * Selection covers both assets and folders. Since their numeric ids live in\n * different tables (and can collide), items are tracked as namespaced keys\n * (`asset:1` / `folder:1`). Views build their rendered order as a key array so\n * range / select-all work across the folder and asset sections seamlessly.\n */\n\nexport type SelectableKind = 'asset' | 'folder';\n\nexport type ItemKey = `${SelectableKind}:${number}`;\n\nexport const assetKey = (id: number): ItemKey => `asset:${id}`;\n\nexport const folderKey = (id: number): ItemKey => `folder:${id}`;\n\n/** Extracts the numeric ids of one kind from a set of item keys. */\nexport const getIdsOfKind = (keys: Set<ItemKey>, kind: SelectableKind): Set<number> => {\n const ids = new Set<number>();\n\n keys.forEach((key) => {\n const [keyKind, id] = key.split(':');\n if (keyKind === kind) {\n ids.add(Number(id));\n }\n });\n\n return ids;\n};\n\nexport interface SelectionState {\n /** Currently selected item keys (assets and folders). */\n selectedKeys: Set<ItemKey>;\n /** Anchor used as the start of a Shift+click range. */\n anchorKey: ItemKey | null;\n}\n\nexport const createEmptySelection = (): SelectionState => ({\n selectedKeys: new Set<ItemKey>(),\n anchorKey: null,\n});\n\n/**\n * Additive toggle — adds the key if absent, removes it otherwise, without\n * touching the rest of the selection. Used by the item checkbox and Cmd/Ctrl+click.\n * The toggled key becomes the new anchor.\n */\nexport const toggleSelection = (state: SelectionState, key: ItemKey): SelectionState => {\n const selectedKeys = new Set(state.selectedKeys);\n\n if (selectedKeys.has(key)) {\n selectedKeys.delete(key);\n } else {\n selectedKeys.add(key);\n }\n\n return { selectedKeys, anchorKey: key };\n};\n\n/**\n * Shift+click — selects the contiguous range between the current anchor and the\n * target in render order, replacing the current selection. The anchor stays put.\n *\n * Falls back to a single selection when there is no usable anchor (e.g. first\n * click was a Shift+click, or the anchor is no longer in the list).\n */\nexport const selectRange = (\n state: SelectionState,\n orderedKeys: ItemKey[],\n targetKey: ItemKey\n): SelectionState => {\n const targetIndex = orderedKeys.indexOf(targetKey);\n\n if (targetIndex === -1) {\n return state;\n }\n\n const anchorIndex = state.anchorKey === null ? -1 : orderedKeys.indexOf(state.anchorKey);\n\n if (anchorIndex === -1) {\n return { selectedKeys: new Set<ItemKey>([targetKey]), anchorKey: targetKey };\n }\n\n const start = Math.min(anchorIndex, targetIndex);\n const end = Math.max(anchorIndex, targetIndex);\n\n return {\n selectedKeys: new Set(orderedKeys.slice(start, end + 1)),\n anchorKey: state.anchorKey,\n };\n};\n\n/**\n * Select-all — selects every key in the provided ordered array. Used by the header\n * checkbox when not everything is already selected.\n */\nexport const selectAll = (orderedKeys: ItemKey[]): SelectionState => ({\n selectedKeys: new Set(orderedKeys),\n anchorKey: orderedKeys.length > 0 ? orderedKeys[orderedKeys.length - 1] : null,\n});\n\nexport const clearSelection = (): SelectionState => createEmptySelection();\n\nexport interface SelectAllState {\n allSelected: boolean;\n isIndeterminate: boolean;\n}\n\n/**\n * Derives the header checkbox state: everything selected, some (indeterminate),\n * or none. Only considers the currently-rendered items (orderedKeys).\n */\nexport const getSelectAllState = (\n selectedKeys: Set<ItemKey>,\n orderedKeys: ItemKey[]\n): SelectAllState => {\n if (orderedKeys.length === 0) {\n return { allSelected: false, isIndeterminate: false };\n }\n\n const selectedCount = orderedKeys.reduce(\n (count, key) => (selectedKeys.has(key) ? count + 1 : count),\n 0\n );\n const allSelected = selectedCount === orderedKeys.length;\n\n return {\n allSelected,\n isIndeterminate: selectedCount > 0 && !allSelected,\n };\n};\n"],"names":["assetKey","id","folderKey","getIdsOfKind","keys","kind","ids","Set","forEach","key","keyKind","split","add","Number","createEmptySelection","selectedKeys","anchorKey","toggleSelection","state","has","delete","selectRange","orderedKeys","targetKey","targetIndex","indexOf","anchorIndex","start","Math","min","end","max","slice","selectAll","length","clearSelection","getSelectAllState","allSelected","isIndeterminate","selectedCount","reduce","count"],"mappings":"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"}
1
+ {"version":3,"file":"selection.js","sources":["../../../../../../admin/src/future/pages/Assets/utils/selection.ts"],"sourcesContent":["/**\n * Pure, view-agnostic selection logic for the future Media Library multi-select.\n *\n * Selection covers both assets and folders. Since their numeric ids live in\n * different tables (and can collide), items are tracked as namespaced keys\n * (`asset:1` / `folder:1`). Views build their rendered order as a key array so\n * range / select-all work across the folder and asset sections seamlessly.\n */\n\nexport type SelectableKind = 'asset' | 'folder';\n\nexport type ItemKey = `${SelectableKind}:${number}`;\n\nexport const assetKey = (id: number): ItemKey => `asset:${id}`;\n\nexport const folderKey = (id: number): ItemKey => `folder:${id}`;\n\n/** Extracts the numeric ids of one kind from a set of item keys. */\nexport const getIdsOfKind = (keys: Set<ItemKey>, kind: SelectableKind): Set<number> => {\n const ids = new Set<number>();\n\n keys.forEach((key) => {\n const [keyKind, id] = key.split(':');\n if (keyKind === kind) {\n ids.add(Number(id));\n }\n });\n\n return ids;\n};\n\nexport interface SelectionState {\n /** Currently selected item keys (assets and folders). */\n selectedKeys: Set<ItemKey>;\n /** Anchor used as the start of a Shift+click range. */\n anchorKey: ItemKey | null;\n}\n\nexport const createEmptySelection = (): SelectionState => ({\n selectedKeys: new Set<ItemKey>(),\n anchorKey: null,\n});\n\n/**\n * Additive toggle — adds the key if absent, removes it otherwise, without\n * touching the rest of the selection. Used by the item checkbox and Cmd/Ctrl+click.\n * The toggled key becomes the new anchor.\n */\nexport const toggleSelection = (state: SelectionState, key: ItemKey): SelectionState => {\n const selectedKeys = new Set(state.selectedKeys);\n\n if (selectedKeys.has(key)) {\n selectedKeys.delete(key);\n } else {\n selectedKeys.add(key);\n }\n\n return { selectedKeys, anchorKey: key };\n};\n\n/**\n * Subtractive counterpart to {@link toggleSelection} — removes a key without the\n * add-if-absent ambiguity, leaving the rest of the selection alone. Used when a\n * single-item affordance (a row's \"...\" menu) makes exactly one key stale.\n *\n * Idempotent: removing a key that was never selected is a no-op. The anchor is\n * dropped only when it is the removed key, so a Shift+click range built around\n * other rows survives.\n */\nexport const deselect = (state: SelectionState, key: ItemKey): SelectionState => {\n const selectedKeys = new Set(state.selectedKeys);\n selectedKeys.delete(key);\n\n return {\n selectedKeys,\n anchorKey: state.anchorKey === key ? null : state.anchorKey,\n };\n};\n\n/**\n * Shift+click — selects the contiguous range between the current anchor and the\n * target in render order, replacing the current selection. The anchor stays put.\n *\n * Falls back to a single selection when there is no usable anchor (e.g. first\n * click was a Shift+click, or the anchor is no longer in the list).\n */\nexport const selectRange = (\n state: SelectionState,\n orderedKeys: ItemKey[],\n targetKey: ItemKey\n): SelectionState => {\n const targetIndex = orderedKeys.indexOf(targetKey);\n\n if (targetIndex === -1) {\n return state;\n }\n\n const anchorIndex = state.anchorKey === null ? -1 : orderedKeys.indexOf(state.anchorKey);\n\n if (anchorIndex === -1) {\n return { selectedKeys: new Set<ItemKey>([targetKey]), anchorKey: targetKey };\n }\n\n const start = Math.min(anchorIndex, targetIndex);\n const end = Math.max(anchorIndex, targetIndex);\n\n return {\n selectedKeys: new Set(orderedKeys.slice(start, end + 1)),\n anchorKey: state.anchorKey,\n };\n};\n\n/**\n * Select-all — selects every key in the provided ordered array. Used by the header\n * checkbox when not everything is already selected.\n */\nexport const selectAll = (orderedKeys: ItemKey[]): SelectionState => ({\n selectedKeys: new Set(orderedKeys),\n anchorKey: orderedKeys.length > 0 ? orderedKeys[orderedKeys.length - 1] : null,\n});\n\nexport const clearSelection = (): SelectionState => createEmptySelection();\n\nexport interface SelectAllState {\n allSelected: boolean;\n isIndeterminate: boolean;\n}\n\n/**\n * Derives the header checkbox state: everything selected, some (indeterminate),\n * or none. Only considers the currently-rendered items (orderedKeys).\n */\nexport const getSelectAllState = (\n selectedKeys: Set<ItemKey>,\n orderedKeys: ItemKey[]\n): SelectAllState => {\n if (orderedKeys.length === 0) {\n return { allSelected: false, isIndeterminate: false };\n }\n\n const selectedCount = orderedKeys.reduce(\n (count, key) => (selectedKeys.has(key) ? count + 1 : count),\n 0\n );\n const allSelected = selectedCount === orderedKeys.length;\n\n return {\n allSelected,\n isIndeterminate: selectedCount > 0 && !allSelected,\n };\n};\n"],"names":["assetKey","id","folderKey","getIdsOfKind","keys","kind","ids","Set","forEach","key","keyKind","split","add","Number","createEmptySelection","selectedKeys","anchorKey","toggleSelection","state","has","delete","deselect","selectRange","orderedKeys","targetKey","targetIndex","indexOf","anchorIndex","start","Math","min","end","max","slice","selectAll","length","clearSelection","getSelectAllState","allSelected","isIndeterminate","selectedCount","reduce","count"],"mappings":"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"}
@@ -37,6 +37,22 @@ const createEmptySelection = ()=>({
37
37
  anchorKey: key
38
38
  };
39
39
  };
40
+ /**
41
+ * Subtractive counterpart to {@link toggleSelection} — removes a key without the
42
+ * add-if-absent ambiguity, leaving the rest of the selection alone. Used when a
43
+ * single-item affordance (a row's "..." menu) makes exactly one key stale.
44
+ *
45
+ * Idempotent: removing a key that was never selected is a no-op. The anchor is
46
+ * dropped only when it is the removed key, so a Shift+click range built around
47
+ * other rows survives.
48
+ */ const deselect = (state, key)=>{
49
+ const selectedKeys = new Set(state.selectedKeys);
50
+ selectedKeys.delete(key);
51
+ return {
52
+ selectedKeys,
53
+ anchorKey: state.anchorKey === key ? null : state.anchorKey
54
+ };
55
+ };
40
56
  /**
41
57
  * Shift+click — selects the contiguous range between the current anchor and the
42
58
  * target in render order, replacing the current selection. The anchor stays put.
@@ -90,5 +106,5 @@ const clearSelection = ()=>createEmptySelection();
90
106
  };
91
107
  };
92
108
 
93
- export { assetKey, clearSelection, createEmptySelection, folderKey, getIdsOfKind, getSelectAllState, selectAll, selectRange, toggleSelection };
109
+ export { assetKey, clearSelection, createEmptySelection, deselect, folderKey, getIdsOfKind, getSelectAllState, selectAll, selectRange, toggleSelection };
94
110
  //# sourceMappingURL=selection.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"selection.mjs","sources":["../../../../../../admin/src/future/pages/Assets/utils/selection.ts"],"sourcesContent":["/**\n * Pure, view-agnostic selection logic for the future Media Library multi-select.\n *\n * Selection covers both assets and folders. Since their numeric ids live in\n * different tables (and can collide), items are tracked as namespaced keys\n * (`asset:1` / `folder:1`). Views build their rendered order as a key array so\n * range / select-all work across the folder and asset sections seamlessly.\n */\n\nexport type SelectableKind = 'asset' | 'folder';\n\nexport type ItemKey = `${SelectableKind}:${number}`;\n\nexport const assetKey = (id: number): ItemKey => `asset:${id}`;\n\nexport const folderKey = (id: number): ItemKey => `folder:${id}`;\n\n/** Extracts the numeric ids of one kind from a set of item keys. */\nexport const getIdsOfKind = (keys: Set<ItemKey>, kind: SelectableKind): Set<number> => {\n const ids = new Set<number>();\n\n keys.forEach((key) => {\n const [keyKind, id] = key.split(':');\n if (keyKind === kind) {\n ids.add(Number(id));\n }\n });\n\n return ids;\n};\n\nexport interface SelectionState {\n /** Currently selected item keys (assets and folders). */\n selectedKeys: Set<ItemKey>;\n /** Anchor used as the start of a Shift+click range. */\n anchorKey: ItemKey | null;\n}\n\nexport const createEmptySelection = (): SelectionState => ({\n selectedKeys: new Set<ItemKey>(),\n anchorKey: null,\n});\n\n/**\n * Additive toggle — adds the key if absent, removes it otherwise, without\n * touching the rest of the selection. Used by the item checkbox and Cmd/Ctrl+click.\n * The toggled key becomes the new anchor.\n */\nexport const toggleSelection = (state: SelectionState, key: ItemKey): SelectionState => {\n const selectedKeys = new Set(state.selectedKeys);\n\n if (selectedKeys.has(key)) {\n selectedKeys.delete(key);\n } else {\n selectedKeys.add(key);\n }\n\n return { selectedKeys, anchorKey: key };\n};\n\n/**\n * Shift+click — selects the contiguous range between the current anchor and the\n * target in render order, replacing the current selection. The anchor stays put.\n *\n * Falls back to a single selection when there is no usable anchor (e.g. first\n * click was a Shift+click, or the anchor is no longer in the list).\n */\nexport const selectRange = (\n state: SelectionState,\n orderedKeys: ItemKey[],\n targetKey: ItemKey\n): SelectionState => {\n const targetIndex = orderedKeys.indexOf(targetKey);\n\n if (targetIndex === -1) {\n return state;\n }\n\n const anchorIndex = state.anchorKey === null ? -1 : orderedKeys.indexOf(state.anchorKey);\n\n if (anchorIndex === -1) {\n return { selectedKeys: new Set<ItemKey>([targetKey]), anchorKey: targetKey };\n }\n\n const start = Math.min(anchorIndex, targetIndex);\n const end = Math.max(anchorIndex, targetIndex);\n\n return {\n selectedKeys: new Set(orderedKeys.slice(start, end + 1)),\n anchorKey: state.anchorKey,\n };\n};\n\n/**\n * Select-all — selects every key in the provided ordered array. Used by the header\n * checkbox when not everything is already selected.\n */\nexport const selectAll = (orderedKeys: ItemKey[]): SelectionState => ({\n selectedKeys: new Set(orderedKeys),\n anchorKey: orderedKeys.length > 0 ? orderedKeys[orderedKeys.length - 1] : null,\n});\n\nexport const clearSelection = (): SelectionState => createEmptySelection();\n\nexport interface SelectAllState {\n allSelected: boolean;\n isIndeterminate: boolean;\n}\n\n/**\n * Derives the header checkbox state: everything selected, some (indeterminate),\n * or none. Only considers the currently-rendered items (orderedKeys).\n */\nexport const getSelectAllState = (\n selectedKeys: Set<ItemKey>,\n orderedKeys: ItemKey[]\n): SelectAllState => {\n if (orderedKeys.length === 0) {\n return { allSelected: false, isIndeterminate: false };\n }\n\n const selectedCount = orderedKeys.reduce(\n (count, key) => (selectedKeys.has(key) ? count + 1 : count),\n 0\n );\n const allSelected = selectedCount === orderedKeys.length;\n\n return {\n allSelected,\n isIndeterminate: selectedCount > 0 && !allSelected,\n };\n};\n"],"names":["assetKey","id","folderKey","getIdsOfKind","keys","kind","ids","Set","forEach","key","keyKind","split","add","Number","createEmptySelection","selectedKeys","anchorKey","toggleSelection","state","has","delete","selectRange","orderedKeys","targetKey","targetIndex","indexOf","anchorIndex","start","Math","min","end","max","slice","selectAll","length","clearSelection","getSelectAllState","allSelected","isIndeterminate","selectedCount","reduce","count"],"mappings":"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"}
1
+ {"version":3,"file":"selection.mjs","sources":["../../../../../../admin/src/future/pages/Assets/utils/selection.ts"],"sourcesContent":["/**\n * Pure, view-agnostic selection logic for the future Media Library multi-select.\n *\n * Selection covers both assets and folders. Since their numeric ids live in\n * different tables (and can collide), items are tracked as namespaced keys\n * (`asset:1` / `folder:1`). Views build their rendered order as a key array so\n * range / select-all work across the folder and asset sections seamlessly.\n */\n\nexport type SelectableKind = 'asset' | 'folder';\n\nexport type ItemKey = `${SelectableKind}:${number}`;\n\nexport const assetKey = (id: number): ItemKey => `asset:${id}`;\n\nexport const folderKey = (id: number): ItemKey => `folder:${id}`;\n\n/** Extracts the numeric ids of one kind from a set of item keys. */\nexport const getIdsOfKind = (keys: Set<ItemKey>, kind: SelectableKind): Set<number> => {\n const ids = new Set<number>();\n\n keys.forEach((key) => {\n const [keyKind, id] = key.split(':');\n if (keyKind === kind) {\n ids.add(Number(id));\n }\n });\n\n return ids;\n};\n\nexport interface SelectionState {\n /** Currently selected item keys (assets and folders). */\n selectedKeys: Set<ItemKey>;\n /** Anchor used as the start of a Shift+click range. */\n anchorKey: ItemKey | null;\n}\n\nexport const createEmptySelection = (): SelectionState => ({\n selectedKeys: new Set<ItemKey>(),\n anchorKey: null,\n});\n\n/**\n * Additive toggle — adds the key if absent, removes it otherwise, without\n * touching the rest of the selection. Used by the item checkbox and Cmd/Ctrl+click.\n * The toggled key becomes the new anchor.\n */\nexport const toggleSelection = (state: SelectionState, key: ItemKey): SelectionState => {\n const selectedKeys = new Set(state.selectedKeys);\n\n if (selectedKeys.has(key)) {\n selectedKeys.delete(key);\n } else {\n selectedKeys.add(key);\n }\n\n return { selectedKeys, anchorKey: key };\n};\n\n/**\n * Subtractive counterpart to {@link toggleSelection} — removes a key without the\n * add-if-absent ambiguity, leaving the rest of the selection alone. Used when a\n * single-item affordance (a row's \"...\" menu) makes exactly one key stale.\n *\n * Idempotent: removing a key that was never selected is a no-op. The anchor is\n * dropped only when it is the removed key, so a Shift+click range built around\n * other rows survives.\n */\nexport const deselect = (state: SelectionState, key: ItemKey): SelectionState => {\n const selectedKeys = new Set(state.selectedKeys);\n selectedKeys.delete(key);\n\n return {\n selectedKeys,\n anchorKey: state.anchorKey === key ? null : state.anchorKey,\n };\n};\n\n/**\n * Shift+click — selects the contiguous range between the current anchor and the\n * target in render order, replacing the current selection. The anchor stays put.\n *\n * Falls back to a single selection when there is no usable anchor (e.g. first\n * click was a Shift+click, or the anchor is no longer in the list).\n */\nexport const selectRange = (\n state: SelectionState,\n orderedKeys: ItemKey[],\n targetKey: ItemKey\n): SelectionState => {\n const targetIndex = orderedKeys.indexOf(targetKey);\n\n if (targetIndex === -1) {\n return state;\n }\n\n const anchorIndex = state.anchorKey === null ? -1 : orderedKeys.indexOf(state.anchorKey);\n\n if (anchorIndex === -1) {\n return { selectedKeys: new Set<ItemKey>([targetKey]), anchorKey: targetKey };\n }\n\n const start = Math.min(anchorIndex, targetIndex);\n const end = Math.max(anchorIndex, targetIndex);\n\n return {\n selectedKeys: new Set(orderedKeys.slice(start, end + 1)),\n anchorKey: state.anchorKey,\n };\n};\n\n/**\n * Select-all — selects every key in the provided ordered array. Used by the header\n * checkbox when not everything is already selected.\n */\nexport const selectAll = (orderedKeys: ItemKey[]): SelectionState => ({\n selectedKeys: new Set(orderedKeys),\n anchorKey: orderedKeys.length > 0 ? orderedKeys[orderedKeys.length - 1] : null,\n});\n\nexport const clearSelection = (): SelectionState => createEmptySelection();\n\nexport interface SelectAllState {\n allSelected: boolean;\n isIndeterminate: boolean;\n}\n\n/**\n * Derives the header checkbox state: everything selected, some (indeterminate),\n * or none. Only considers the currently-rendered items (orderedKeys).\n */\nexport const getSelectAllState = (\n selectedKeys: Set<ItemKey>,\n orderedKeys: ItemKey[]\n): SelectAllState => {\n if (orderedKeys.length === 0) {\n return { allSelected: false, isIndeterminate: false };\n }\n\n const selectedCount = orderedKeys.reduce(\n (count, key) => (selectedKeys.has(key) ? count + 1 : count),\n 0\n );\n const allSelected = selectedCount === orderedKeys.length;\n\n return {\n allSelected,\n isIndeterminate: selectedCount > 0 && !allSelected,\n };\n};\n"],"names":["assetKey","id","folderKey","getIdsOfKind","keys","kind","ids","Set","forEach","key","keyKind","split","add","Number","createEmptySelection","selectedKeys","anchorKey","toggleSelection","state","has","delete","deselect","selectRange","orderedKeys","targetKey","targetIndex","indexOf","anchorIndex","start","Math","min","end","max","slice","selectAll","length","clearSelection","getSelectAllState","allSelected","isIndeterminate","selectedCount","reduce","count"],"mappings":"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"}