@strapi/upload 5.51.0 → 5.51.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/future/pages/Assets/AssetsPage.js +198 -129
- package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
- package/dist/admin/future/pages/Assets/AssetsPage.mjs +199 -130
- package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js +158 -19
- package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +159 -20
- package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsTable.js +212 -37
- package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +213 -38
- package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js +252 -0
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs +250 -0
- package/dist/admin/future/pages/Assets/components/BulkActionsBar.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js +209 -0
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs +207 -0
- package/dist/admin/future/pages/Assets/components/BulkMoveDialog.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js +111 -36
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs +113 -38
- package/dist/admin/future/pages/Assets/components/Dnd/AssetsDndProvider.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.js +120 -13
- package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.mjs +120 -13
- package/dist/admin/future/pages/Assets/components/Dnd/DragOverlayChip.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js +71 -0
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs +69 -0
- package/dist/admin/future/pages/Assets/components/Dnd/buildDragSet.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.js +34 -0
- package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.mjs +32 -0
- package/dist/admin/future/pages/Assets/components/Dnd/computeValidDropTargets.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/dndIds.js +15 -0
- package/dist/admin/future/pages/Assets/components/Dnd/dndIds.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/dndIds.mjs +13 -1
- package/dist/admin/future/pages/Assets/components/Dnd/dndIds.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js +19 -25
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs +19 -25
- package/dist/admin/future/pages/Assets/components/Dnd/useAssetDnd.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.js +35 -0
- package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.mjs +33 -0
- package/dist/admin/future/pages/Assets/components/Dnd/useFolderTreeDroppable.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +120 -22
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +121 -23
- package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
- package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.js +22 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.mjs +19 -0
- package/dist/admin/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.js +165 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.js.map +1 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.mjs +163 -0
- package/dist/admin/future/pages/Assets/components/SortMenu.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js +59 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs +55 -0
- package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useListSort.js +124 -0
- package/dist/admin/future/pages/Assets/hooks/useListSort.js.map +1 -0
- package/dist/admin/future/pages/Assets/hooks/useListSort.mjs +120 -0
- package/dist/admin/future/pages/Assets/hooks/useListSort.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/utils/listQueryKey.js +29 -0
- package/dist/admin/future/pages/Assets/utils/listQueryKey.js.map +1 -0
- package/dist/admin/future/pages/Assets/utils/listQueryKey.mjs +27 -0
- package/dist/admin/future/pages/Assets/utils/listQueryKey.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/utils/mergeMixedList.js +84 -0
- package/dist/admin/future/pages/Assets/utils/mergeMixedList.js.map +1 -0
- package/dist/admin/future/pages/Assets/utils/mergeMixedList.mjs +81 -0
- package/dist/admin/future/pages/Assets/utils/mergeMixedList.mjs.map +1 -0
- package/dist/admin/future/pages/Assets/utils/selection.js +104 -0
- package/dist/admin/future/pages/Assets/utils/selection.js.map +1 -0
- package/dist/admin/future/pages/Assets/utils/selection.mjs +94 -0
- package/dist/admin/future/pages/Assets/utils/selection.mjs.map +1 -0
- package/dist/admin/future/services/assets.js +33 -1
- package/dist/admin/future/services/assets.js.map +1 -1
- package/dist/admin/future/services/assets.mjs +33 -2
- package/dist/admin/future/services/assets.mjs.map +1 -1
- package/dist/admin/future/services/folders.js +3 -3
- package/dist/admin/future/services/folders.js.map +1 -1
- package/dist/admin/future/services/folders.mjs +3 -3
- package/dist/admin/future/services/folders.mjs.map +1 -1
- package/dist/admin/future/utils/buildDragSetFromSelection.js +33 -0
- package/dist/admin/future/utils/buildDragSetFromSelection.js.map +1 -0
- package/dist/admin/future/utils/buildDragSetFromSelection.mjs +31 -0
- package/dist/admin/future/utils/buildDragSetFromSelection.mjs.map +1 -0
- package/dist/admin/future/utils/canDropItemOnFolder.js +4 -0
- package/dist/admin/future/utils/canDropItemOnFolder.js.map +1 -1
- package/dist/admin/future/utils/canDropItemOnFolder.mjs +4 -0
- package/dist/admin/future/utils/canDropItemOnFolder.mjs.map +1 -1
- package/dist/admin/future/utils/flattenFolderStructure.js +28 -0
- package/dist/admin/future/utils/flattenFolderStructure.js.map +1 -0
- package/dist/admin/future/utils/flattenFolderStructure.mjs +26 -0
- package/dist/admin/future/utils/flattenFolderStructure.mjs.map +1 -0
- package/dist/admin/future/utils/formatMoveSuccessMessage.js +19 -0
- package/dist/admin/future/utils/formatMoveSuccessMessage.js.map +1 -0
- package/dist/admin/future/utils/formatMoveSuccessMessage.mjs +17 -0
- package/dist/admin/future/utils/formatMoveSuccessMessage.mjs.map +1 -0
- package/dist/admin/future/utils/getFolderLabel.js +31 -0
- package/dist/admin/future/utils/getFolderLabel.js.map +1 -0
- package/dist/admin/future/utils/getFolderLabel.mjs +29 -0
- package/dist/admin/future/utils/getFolderLabel.mjs.map +1 -0
- package/dist/admin/src/future/pages/Assets/components/AssetsTable.d.ts +7 -1
- package/dist/admin/src/future/pages/Assets/components/BulkActionsBar.d.ts +1 -0
- package/dist/admin/src/future/pages/Assets/components/BulkMoveDialog.d.ts +13 -0
- package/dist/admin/src/future/pages/Assets/components/Dnd/AssetsDndProvider.d.ts +2 -0
- package/dist/admin/src/future/pages/Assets/components/Dnd/DragOverlayChip.d.ts +2 -2
- package/dist/admin/src/future/pages/Assets/components/Dnd/buildDragSet.d.ts +17 -0
- package/dist/admin/src/future/pages/Assets/components/Dnd/computeValidDropTargets.d.ts +7 -0
- package/dist/admin/src/future/pages/Assets/components/Dnd/dndIds.d.ts +3 -0
- package/dist/admin/src/future/pages/Assets/components/Dnd/useAssetDnd.d.ts +3 -1
- package/dist/admin/src/future/pages/Assets/components/Dnd/useFolderTreeDroppable.d.ts +19 -0
- package/dist/admin/src/future/pages/Assets/components/FolderTree/useSpringLoadedExpand.d.ts +6 -0
- package/dist/admin/src/future/pages/Assets/components/SortMenu.d.ts +17 -0
- package/dist/admin/src/future/pages/Assets/hooks/useAssetSelection.d.ts +39 -0
- package/dist/admin/src/future/pages/Assets/hooks/useListSort.d.ts +47 -0
- package/dist/admin/src/future/pages/Assets/utils/listQueryKey.d.ts +30 -0
- package/dist/admin/src/future/pages/Assets/utils/mergeMixedList.d.ts +40 -0
- package/dist/admin/src/future/pages/Assets/utils/selection.d.ts +50 -0
- package/dist/admin/src/future/services/assets.d.ts +4 -1
- package/dist/admin/src/future/services/folders.d.ts +4 -1
- package/dist/admin/src/future/types/dnd.d.ts +5 -0
- package/dist/admin/src/future/utils/buildDragSetFromSelection.d.ts +11 -0
- package/dist/admin/src/future/utils/canDropItemOnFolder.d.ts +1 -1
- package/dist/admin/src/future/utils/flattenFolderStructure.d.ts +18 -0
- package/dist/admin/src/future/utils/formatMoveSuccessMessage.d.ts +14 -0
- package/dist/admin/src/future/utils/getFolderLabel.d.ts +11 -0
- package/dist/admin/translations/en.json.js +33 -0
- package/dist/admin/translations/en.json.js.map +1 -1
- package/dist/admin/translations/en.json.mjs +33 -0
- package/dist/admin/translations/en.json.mjs.map +1 -1
- package/dist/shared/contracts/folders.d.ts +1 -1
- package/package.json +7 -7
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SortMenu.js","sources":["../../../../../../admin/src/future/pages/Assets/components/SortMenu.tsx"],"sourcesContent":["import { Menu, VisuallyHidden } 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 height: auto;\n`;\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 */\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 // The checkmark icon is aria-hidden, so the active option carries a\n // visually-hidden suffix for screen readers instead (menuitem does not\n // allow aria-checked).\n const activeMark = (\n <VisuallyHidden>\n {' '}\n {formatMessage({ id: getTranslationKey('list.sort.active'), defaultMessage: '(active)' })}\n </VisuallyHidden>\n );\n\n return (\n <Menu.Root>\n <SortTrigger variant=\"tertiary\" 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\" width=\"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 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 {sort.sortBy === key && activeMark}\n </Menu.Item>\n ))}\n {(Object.keys(SORT_DIRECTION_LABELS) as SortDirectionKey[]).map((key) => (\n <Menu.Item\n key={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 {sort.direction === key && activeMark}\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 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 {sort.foldersPosition === position && activeMark}\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","activeMark","_jsxs","VisuallyHidden","Root","variant","endIcon","ChevronDown","Content","popoverPlacement","zIndex","maxHeight","Object","keys","map","key","Item","onSelect","e","preventDefault","setSortBy","setDirection","_Fragment","Separator","position","setFoldersPosition","foldersPosition"],"mappings":"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"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { VisuallyHidden, Menu } from '@strapi/design-system';
|
|
3
|
+
import { ChevronDown, Check } from '@strapi/icons';
|
|
4
|
+
import { useIntl } from 'react-intl';
|
|
5
|
+
import { styled } from 'styled-components';
|
|
6
|
+
import { getTranslationKey } from '../../../utils/translations.mjs';
|
|
7
|
+
|
|
8
|
+
const SORT_BY_LABELS = {
|
|
9
|
+
oldestUploads: {
|
|
10
|
+
id: getTranslationKey('list.sort.oldest-uploads'),
|
|
11
|
+
defaultMessage: 'Oldest uploads'
|
|
12
|
+
},
|
|
13
|
+
mostRecentUpdates: {
|
|
14
|
+
id: getTranslationKey('list.sort.most-recent-updates'),
|
|
15
|
+
defaultMessage: 'Most recent updates'
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
const SORT_DIRECTION_LABELS = {
|
|
19
|
+
nameAsc: {
|
|
20
|
+
id: getTranslationKey('list.sort.name-asc'),
|
|
21
|
+
defaultMessage: 'A to Z'
|
|
22
|
+
},
|
|
23
|
+
nameDesc: {
|
|
24
|
+
id: getTranslationKey('list.sort.name-desc'),
|
|
25
|
+
defaultMessage: 'Z to A'
|
|
26
|
+
},
|
|
27
|
+
sizeAsc: {
|
|
28
|
+
id: getTranslationKey('list.sort.size-asc'),
|
|
29
|
+
defaultMessage: 'File size ascending'
|
|
30
|
+
},
|
|
31
|
+
sizeDesc: {
|
|
32
|
+
id: getTranslationKey('list.sort.size-desc'),
|
|
33
|
+
defaultMessage: 'File size descending'
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
const FOLDERS_LABELS = {
|
|
37
|
+
top: {
|
|
38
|
+
id: getTranslationKey('list.sort.folders-on-top'),
|
|
39
|
+
defaultMessage: 'On top'
|
|
40
|
+
},
|
|
41
|
+
mixed: {
|
|
42
|
+
id: getTranslationKey('list.sort.folders-mixed'),
|
|
43
|
+
defaultMessage: 'Mixed with files'
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
// Stretch to the toolbar row height so the trigger matches the view toggle
|
|
47
|
+
// (the parent Flex uses alignItems="stretch").
|
|
48
|
+
const SortTrigger = styled(Menu.Trigger)`
|
|
49
|
+
height: auto;
|
|
50
|
+
`;
|
|
51
|
+
// Full-width section band, like the design mock (sits inside the Menu.Content
|
|
52
|
+
// padding, so the band stops short of the panel edges).
|
|
53
|
+
const GroupLabel = styled(Menu.Label)`
|
|
54
|
+
width: 100%;
|
|
55
|
+
display: block;
|
|
56
|
+
background: ${({ theme })=>theme.colorScheme === 'dark' ? theme.colors.neutral150 : theme.colors.neutral100};
|
|
57
|
+
padding-inline: ${({ theme })=>theme.spaces[3]};
|
|
58
|
+
border-radius: ${({ theme })=>theme.borderRadius};
|
|
59
|
+
`;
|
|
60
|
+
/**
|
|
61
|
+
* Toolbar "Sort" dropdown: single-select sections (Sort / Folders). Picking an
|
|
62
|
+
* option keeps the menu open (`onSelect` preventDefault) so several facets can
|
|
63
|
+
* be tuned in one visit; clicking a checked facet clears it (the hook
|
|
64
|
+
* guarantees at least one sort rule stays active).
|
|
65
|
+
*/ const SortMenu = ({ sort, showFoldersGroup = true })=>{
|
|
66
|
+
const { formatMessage } = useIntl();
|
|
67
|
+
const triggerLabel = formatMessage({
|
|
68
|
+
id: getTranslationKey('list.sort.trigger'),
|
|
69
|
+
defaultMessage: 'Sort: {active}'
|
|
70
|
+
}, {
|
|
71
|
+
active: sort.sortBy ? formatMessage(SORT_BY_LABELS[sort.sortBy]) : formatMessage(SORT_DIRECTION_LABELS[sort.direction])
|
|
72
|
+
});
|
|
73
|
+
const checkmark = /*#__PURE__*/ jsx(Check, {
|
|
74
|
+
"aria-hidden": true,
|
|
75
|
+
width: "1.6rem",
|
|
76
|
+
height: "1.6rem",
|
|
77
|
+
fill: "primary600"
|
|
78
|
+
});
|
|
79
|
+
// The checkmark icon is aria-hidden, so the active option carries a
|
|
80
|
+
// visually-hidden suffix for screen readers instead (menuitem does not
|
|
81
|
+
// allow aria-checked).
|
|
82
|
+
const activeMark = /*#__PURE__*/ jsxs(VisuallyHidden, {
|
|
83
|
+
children: [
|
|
84
|
+
' ',
|
|
85
|
+
formatMessage({
|
|
86
|
+
id: getTranslationKey('list.sort.active'),
|
|
87
|
+
defaultMessage: '(active)'
|
|
88
|
+
})
|
|
89
|
+
]
|
|
90
|
+
});
|
|
91
|
+
return /*#__PURE__*/ jsxs(Menu.Root, {
|
|
92
|
+
children: [
|
|
93
|
+
/*#__PURE__*/ jsx(SortTrigger, {
|
|
94
|
+
variant: "tertiary",
|
|
95
|
+
endIcon: /*#__PURE__*/ jsx(ChevronDown, {
|
|
96
|
+
"aria-hidden": true
|
|
97
|
+
}),
|
|
98
|
+
children: triggerLabel
|
|
99
|
+
}),
|
|
100
|
+
/*#__PURE__*/ jsxs(Menu.Content, {
|
|
101
|
+
popoverPlacement: "bottom-end",
|
|
102
|
+
zIndex: 2,
|
|
103
|
+
maxHeight: "70vh",
|
|
104
|
+
width: "25rem",
|
|
105
|
+
children: [
|
|
106
|
+
/*#__PURE__*/ jsx(GroupLabel, {
|
|
107
|
+
children: formatMessage({
|
|
108
|
+
id: getTranslationKey('list.sort.section'),
|
|
109
|
+
defaultMessage: 'Sort'
|
|
110
|
+
})
|
|
111
|
+
}),
|
|
112
|
+
Object.keys(SORT_BY_LABELS).map((key)=>/*#__PURE__*/ jsxs(Menu.Item, {
|
|
113
|
+
onSelect: (e)=>{
|
|
114
|
+
e.preventDefault();
|
|
115
|
+
sort.setSortBy(sort.sortBy === key ? null : key);
|
|
116
|
+
},
|
|
117
|
+
endIcon: sort.sortBy === key ? checkmark : null,
|
|
118
|
+
children: [
|
|
119
|
+
formatMessage(SORT_BY_LABELS[key]),
|
|
120
|
+
sort.sortBy === key && activeMark
|
|
121
|
+
]
|
|
122
|
+
}, key)),
|
|
123
|
+
Object.keys(SORT_DIRECTION_LABELS).map((key)=>/*#__PURE__*/ jsxs(Menu.Item, {
|
|
124
|
+
onSelect: (e)=>{
|
|
125
|
+
e.preventDefault();
|
|
126
|
+
sort.setDirection(sort.direction === key ? null : key);
|
|
127
|
+
},
|
|
128
|
+
endIcon: sort.direction === key ? checkmark : null,
|
|
129
|
+
children: [
|
|
130
|
+
formatMessage(SORT_DIRECTION_LABELS[key]),
|
|
131
|
+
sort.direction === key && activeMark
|
|
132
|
+
]
|
|
133
|
+
}, key)),
|
|
134
|
+
showFoldersGroup && /*#__PURE__*/ jsxs(Fragment, {
|
|
135
|
+
children: [
|
|
136
|
+
/*#__PURE__*/ jsx(Menu.Separator, {}),
|
|
137
|
+
/*#__PURE__*/ jsx(GroupLabel, {
|
|
138
|
+
children: formatMessage({
|
|
139
|
+
id: getTranslationKey('list.sort.folders'),
|
|
140
|
+
defaultMessage: 'Folders'
|
|
141
|
+
})
|
|
142
|
+
}),
|
|
143
|
+
Object.keys(FOLDERS_LABELS).map((position)=>/*#__PURE__*/ jsxs(Menu.Item, {
|
|
144
|
+
onSelect: (e)=>{
|
|
145
|
+
e.preventDefault();
|
|
146
|
+
sort.setFoldersPosition(position);
|
|
147
|
+
},
|
|
148
|
+
endIcon: sort.foldersPosition === position ? checkmark : null,
|
|
149
|
+
children: [
|
|
150
|
+
formatMessage(FOLDERS_LABELS[position]),
|
|
151
|
+
sort.foldersPosition === position && activeMark
|
|
152
|
+
]
|
|
153
|
+
}, position))
|
|
154
|
+
]
|
|
155
|
+
})
|
|
156
|
+
]
|
|
157
|
+
})
|
|
158
|
+
]
|
|
159
|
+
});
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export { SortMenu };
|
|
163
|
+
//# sourceMappingURL=SortMenu.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SortMenu.mjs","sources":["../../../../../../admin/src/future/pages/Assets/components/SortMenu.tsx"],"sourcesContent":["import { Menu, VisuallyHidden } 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 height: auto;\n`;\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 */\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 // The checkmark icon is aria-hidden, so the active option carries a\n // visually-hidden suffix for screen readers instead (menuitem does not\n // allow aria-checked).\n const activeMark = (\n <VisuallyHidden>\n {' '}\n {formatMessage({ id: getTranslationKey('list.sort.active'), defaultMessage: '(active)' })}\n </VisuallyHidden>\n );\n\n return (\n <Menu.Root>\n <SortTrigger variant=\"tertiary\" 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\" width=\"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 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 {sort.sortBy === key && activeMark}\n </Menu.Item>\n ))}\n {(Object.keys(SORT_DIRECTION_LABELS) as SortDirectionKey[]).map((key) => (\n <Menu.Item\n key={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 {sort.direction === key && activeMark}\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 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 {sort.foldersPosition === position && activeMark}\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","activeMark","_jsxs","VisuallyHidden","Root","variant","endIcon","ChevronDown","Content","popoverPlacement","zIndex","maxHeight","Object","keys","map","key","Item","onSelect","e","preventDefault","setSortBy","setDirection","_Fragment","Separator","position","setFoldersPosition","foldersPosition"],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var React = require('react');
|
|
4
|
+
var selection = require('../utils/selection.js');
|
|
5
|
+
|
|
6
|
+
const AssetSelectionContext = React.createContext(null);
|
|
7
|
+
const AssetSelectionProvider = ({ children })=>{
|
|
8
|
+
const [state, setState] = React.useState(selection.createEmptySelection);
|
|
9
|
+
const isSelected = React.useCallback((key)=>state.selectedKeys.has(key), [
|
|
10
|
+
state.selectedKeys
|
|
11
|
+
]);
|
|
12
|
+
const toggle = React.useCallback((key)=>setState((prev)=>selection.toggleSelection(prev, key)), []);
|
|
13
|
+
const selectRange = React.useCallback((orderedKeys, targetKey)=>setState((prev)=>selection.selectRange(prev, orderedKeys, targetKey)), []);
|
|
14
|
+
const selectAll = React.useCallback((orderedKeys)=>setState(selection.selectAll(orderedKeys)), []);
|
|
15
|
+
const clear = React.useCallback(()=>setState(selection.clearSelection()), []);
|
|
16
|
+
const selectedIds = React.useMemo(()=>selection.getIdsOfKind(state.selectedKeys, 'asset'), [
|
|
17
|
+
state.selectedKeys
|
|
18
|
+
]);
|
|
19
|
+
const selectedFolderIds = React.useMemo(()=>selection.getIdsOfKind(state.selectedKeys, 'folder'), [
|
|
20
|
+
state.selectedKeys
|
|
21
|
+
]);
|
|
22
|
+
const value = React.useMemo(()=>({
|
|
23
|
+
selectedKeys: state.selectedKeys,
|
|
24
|
+
selectedIds,
|
|
25
|
+
selectedFolderIds,
|
|
26
|
+
anchorKey: state.anchorKey,
|
|
27
|
+
isSelected,
|
|
28
|
+
toggle,
|
|
29
|
+
selectRange,
|
|
30
|
+
selectAll,
|
|
31
|
+
clear
|
|
32
|
+
}), [
|
|
33
|
+
state.selectedKeys,
|
|
34
|
+
selectedIds,
|
|
35
|
+
selectedFolderIds,
|
|
36
|
+
state.anchorKey,
|
|
37
|
+
isSelected,
|
|
38
|
+
toggle,
|
|
39
|
+
selectRange,
|
|
40
|
+
selectAll,
|
|
41
|
+
clear
|
|
42
|
+
]);
|
|
43
|
+
return React.createElement(AssetSelectionContext.Provider, {
|
|
44
|
+
value
|
|
45
|
+
}, children);
|
|
46
|
+
};
|
|
47
|
+
const useAssetSelection = ()=>{
|
|
48
|
+
const context = React.useContext(AssetSelectionContext);
|
|
49
|
+
if (!context) {
|
|
50
|
+
throw new Error('useAssetSelection must be used within an AssetSelectionProvider');
|
|
51
|
+
}
|
|
52
|
+
return context;
|
|
53
|
+
};
|
|
54
|
+
const useAssetSelectionOptional = ()=>React.useContext(AssetSelectionContext);
|
|
55
|
+
|
|
56
|
+
exports.AssetSelectionProvider = AssetSelectionProvider;
|
|
57
|
+
exports.useAssetSelection = useAssetSelection;
|
|
58
|
+
exports.useAssetSelectionOptional = useAssetSelectionOptional;
|
|
59
|
+
//# sourceMappingURL=useAssetSelection.js.map
|
|
@@ -0,0 +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\nexport const AssetSelectionProvider = ({ children }: AssetSelectionProviderProps) => {\n const [state, setState] = useState<SelectionState>(createEmptySelection);\n\n const isSelected = useCallback(\n (key: ItemKey) => state.selectedKeys.has(key),\n [state.selectedKeys]\n );\n\n const toggle = useCallback((key: ItemKey) => setState((prev) => toggleSelection(prev, key)), []);\n\n const selectRange = useCallback(\n (orderedKeys: ItemKey[], targetKey: ItemKey) =>\n setState((prev) => selectRangeState(prev, orderedKeys, targetKey)),\n []\n );\n\n const selectAll = useCallback(\n (orderedKeys: ItemKey[]) => setState(selectAllState(orderedKeys)),\n []\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","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":";;;;;AAoDA,MAAMA,wBAAwBC,mBAAAA,CAAqC,IAAA,CAAA;AAM5D,MAAMC,sBAAAA,GAAyB,CAAC,EAAEC,QAAQ,EAA+B,GAAA;AAC9E,IAAA,MAAM,CAACC,KAAAA,EAAOC,QAAAA,CAAS,GAAGC,cAAAA,CAAyBC,8BAAAA,CAAAA;IAEnD,MAAMC,UAAAA,GAAaC,kBACjB,CAACC,GAAAA,GAAiBN,MAAMO,YAAY,CAACC,GAAG,CAACF,GAAAA,CAAAA,EACzC;AAACN,QAAAA,KAAAA,CAAMO;AAAa,KAAA,CAAA;IAGtB,MAAME,MAAAA,GAASJ,iBAAAA,CAAY,CAACC,GAAAA,GAAiBL,QAAAA,CAAS,CAACS,IAAAA,GAASC,yBAAAA,CAAgBD,IAAAA,EAAMJ,GAAAA,CAAAA,CAAAA,EAAO,EAAE,CAAA;AAE/F,IAAA,MAAMM,WAAAA,GAAcP,iBAAAA,CAClB,CAACQ,WAAAA,EAAwBC,SAAAA,GACvBb,QAAAA,CAAS,CAACS,IAAAA,GAASK,qBAAAA,CAAiBL,IAAAA,EAAMG,WAAAA,EAAaC,SAAAA,CAAAA,CAAAA,EACzD,EAAE,CAAA;AAGJ,IAAA,MAAME,YAAYX,iBAAAA,CAChB,CAACQ,cAA2BZ,QAAAA,CAASgB,mBAAAA,CAAeJ,eACpD,EAAE,CAAA;AAGJ,IAAA,MAAMK,KAAAA,GAAQb,iBAAAA,CAAY,IAAMJ,QAAAA,CAASkB,6BAAmB,EAAE,CAAA;AAE9D,IAAA,MAAMC,cAAcC,aAAAA,CAClB,IAAMC,uBAAatB,KAAAA,CAAMO,YAAY,EAAE,OAAA,CAAA,EACvC;AAACP,QAAAA,KAAAA,CAAMO;AAAa,KAAA,CAAA;AAEtB,IAAA,MAAMgB,oBAAoBF,aAAAA,CACxB,IAAMC,uBAAatB,KAAAA,CAAMO,YAAY,EAAE,QAAA,CAAA,EACvC;AAACP,QAAAA,KAAAA,CAAMO;AAAa,KAAA,CAAA;IAGtB,MAAMiB,KAAAA,GAAQH,aAAAA,CACZ,KAAO;AACLd,YAAAA,YAAAA,EAAcP,MAAMO,YAAY;AAChCa,YAAAA,WAAAA;AACAG,YAAAA,iBAAAA;AACAE,YAAAA,SAAAA,EAAWzB,MAAMyB,SAAS;AAC1BrB,YAAAA,UAAAA;AACAK,YAAAA,MAAAA;AACAG,YAAAA,WAAAA;AACAI,YAAAA,SAAAA;AACAE,YAAAA;AACF,SAAA,CAAA,EACA;AACElB,QAAAA,KAAAA,CAAMO,YAAY;AAClBa,QAAAA,WAAAA;AACAG,QAAAA,iBAAAA;AACAvB,QAAAA,KAAAA,CAAMyB,SAAS;AACfrB,QAAAA,UAAAA;AACAK,QAAAA,MAAAA;AACAG,QAAAA,WAAAA;AACAI,QAAAA,SAAAA;AACAE,QAAAA;AACD,KAAA,CAAA;IAGH,OAAOQ,mBAAAA,CAAc9B,qBAAAA,CAAsB+B,QAAQ,EAAE;AAAEH,QAAAA;KAAM,EAAGzB,QAAAA,CAAAA;AAClE;MAEa6B,iBAAAA,GAAoB,IAAA;AAC/B,IAAA,MAAMC,UAAUC,gBAAAA,CAAWlC,qBAAAA,CAAAA;AAE3B,IAAA,IAAI,CAACiC,OAAAA,EAAS;AACZ,QAAA,MAAM,IAAIE,KAAAA,CAAM,iEAAA,CAAA;AAClB,IAAA;IAEA,OAAOF,OAAAA;AACT;AAEO,MAAMG,yBAAAA,GAA4B,IAAMF,gBAAAA,CAAWlC,qBAAAA;;;;;;"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { useState, useCallback, useMemo, createElement, createContext, useContext } from 'react';
|
|
2
|
+
import { createEmptySelection, toggleSelection, selectRange, selectAll, clearSelection, getIdsOfKind } from '../utils/selection.mjs';
|
|
3
|
+
|
|
4
|
+
const AssetSelectionContext = createContext(null);
|
|
5
|
+
const AssetSelectionProvider = ({ children })=>{
|
|
6
|
+
const [state, setState] = useState(createEmptySelection);
|
|
7
|
+
const isSelected = useCallback((key)=>state.selectedKeys.has(key), [
|
|
8
|
+
state.selectedKeys
|
|
9
|
+
]);
|
|
10
|
+
const toggle = useCallback((key)=>setState((prev)=>toggleSelection(prev, key)), []);
|
|
11
|
+
const selectRange$1 = useCallback((orderedKeys, targetKey)=>setState((prev)=>selectRange(prev, orderedKeys, targetKey)), []);
|
|
12
|
+
const selectAll$1 = useCallback((orderedKeys)=>setState(selectAll(orderedKeys)), []);
|
|
13
|
+
const clear = useCallback(()=>setState(clearSelection()), []);
|
|
14
|
+
const selectedIds = useMemo(()=>getIdsOfKind(state.selectedKeys, 'asset'), [
|
|
15
|
+
state.selectedKeys
|
|
16
|
+
]);
|
|
17
|
+
const selectedFolderIds = useMemo(()=>getIdsOfKind(state.selectedKeys, 'folder'), [
|
|
18
|
+
state.selectedKeys
|
|
19
|
+
]);
|
|
20
|
+
const value = useMemo(()=>({
|
|
21
|
+
selectedKeys: state.selectedKeys,
|
|
22
|
+
selectedIds,
|
|
23
|
+
selectedFolderIds,
|
|
24
|
+
anchorKey: state.anchorKey,
|
|
25
|
+
isSelected,
|
|
26
|
+
toggle,
|
|
27
|
+
selectRange: selectRange$1,
|
|
28
|
+
selectAll: selectAll$1,
|
|
29
|
+
clear
|
|
30
|
+
}), [
|
|
31
|
+
state.selectedKeys,
|
|
32
|
+
selectedIds,
|
|
33
|
+
selectedFolderIds,
|
|
34
|
+
state.anchorKey,
|
|
35
|
+
isSelected,
|
|
36
|
+
toggle,
|
|
37
|
+
selectRange$1,
|
|
38
|
+
selectAll$1,
|
|
39
|
+
clear
|
|
40
|
+
]);
|
|
41
|
+
return createElement(AssetSelectionContext.Provider, {
|
|
42
|
+
value
|
|
43
|
+
}, children);
|
|
44
|
+
};
|
|
45
|
+
const useAssetSelection = ()=>{
|
|
46
|
+
const context = useContext(AssetSelectionContext);
|
|
47
|
+
if (!context) {
|
|
48
|
+
throw new Error('useAssetSelection must be used within an AssetSelectionProvider');
|
|
49
|
+
}
|
|
50
|
+
return context;
|
|
51
|
+
};
|
|
52
|
+
const useAssetSelectionOptional = ()=>useContext(AssetSelectionContext);
|
|
53
|
+
|
|
54
|
+
export { AssetSelectionProvider, useAssetSelection, useAssetSelectionOptional };
|
|
55
|
+
//# sourceMappingURL=useAssetSelection.mjs.map
|
|
@@ -0,0 +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\nexport const AssetSelectionProvider = ({ children }: AssetSelectionProviderProps) => {\n const [state, setState] = useState<SelectionState>(createEmptySelection);\n\n const isSelected = useCallback(\n (key: ItemKey) => state.selectedKeys.has(key),\n [state.selectedKeys]\n );\n\n const toggle = useCallback((key: ItemKey) => setState((prev) => toggleSelection(prev, key)), []);\n\n const selectRange = useCallback(\n (orderedKeys: ItemKey[], targetKey: ItemKey) =>\n setState((prev) => selectRangeState(prev, orderedKeys, targetKey)),\n []\n );\n\n const selectAll = useCallback(\n (orderedKeys: ItemKey[]) => setState(selectAllState(orderedKeys)),\n []\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","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":";;;AAoDA,MAAMA,wBAAwBC,aAAAA,CAAqC,IAAA,CAAA;AAM5D,MAAMC,sBAAAA,GAAyB,CAAC,EAAEC,QAAQ,EAA+B,GAAA;AAC9E,IAAA,MAAM,CAACC,KAAAA,EAAOC,QAAAA,CAAS,GAAGC,QAAAA,CAAyBC,oBAAAA,CAAAA;IAEnD,MAAMC,UAAAA,GAAaC,YACjB,CAACC,GAAAA,GAAiBN,MAAMO,YAAY,CAACC,GAAG,CAACF,GAAAA,CAAAA,EACzC;AAACN,QAAAA,KAAAA,CAAMO;AAAa,KAAA,CAAA;IAGtB,MAAME,MAAAA,GAASJ,WAAAA,CAAY,CAACC,GAAAA,GAAiBL,QAAAA,CAAS,CAACS,IAAAA,GAASC,eAAAA,CAAgBD,IAAAA,EAAMJ,GAAAA,CAAAA,CAAAA,EAAO,EAAE,CAAA;AAE/F,IAAA,MAAMM,aAAAA,GAAcP,WAAAA,CAClB,CAACQ,WAAAA,EAAwBC,SAAAA,GACvBb,QAAAA,CAAS,CAACS,IAAAA,GAASK,WAAAA,CAAiBL,IAAAA,EAAMG,WAAAA,EAAaC,SAAAA,CAAAA,CAAAA,EACzD,EAAE,CAAA;AAGJ,IAAA,MAAME,cAAYX,WAAAA,CAChB,CAACQ,cAA2BZ,QAAAA,CAASgB,SAAAA,CAAeJ,eACpD,EAAE,CAAA;AAGJ,IAAA,MAAMK,KAAAA,GAAQb,WAAAA,CAAY,IAAMJ,QAAAA,CAASkB,mBAAmB,EAAE,CAAA;AAE9D,IAAA,MAAMC,cAAcC,OAAAA,CAClB,IAAMC,aAAatB,KAAAA,CAAMO,YAAY,EAAE,OAAA,CAAA,EACvC;AAACP,QAAAA,KAAAA,CAAMO;AAAa,KAAA,CAAA;AAEtB,IAAA,MAAMgB,oBAAoBF,OAAAA,CACxB,IAAMC,aAAatB,KAAAA,CAAMO,YAAY,EAAE,QAAA,CAAA,EACvC;AAACP,QAAAA,KAAAA,CAAMO;AAAa,KAAA,CAAA;IAGtB,MAAMiB,KAAAA,GAAQH,OAAAA,CACZ,KAAO;AACLd,YAAAA,YAAAA,EAAcP,MAAMO,YAAY;AAChCa,YAAAA,WAAAA;AACAG,YAAAA,iBAAAA;AACAE,YAAAA,SAAAA,EAAWzB,MAAMyB,SAAS;AAC1BrB,YAAAA,UAAAA;AACAK,YAAAA,MAAAA;AACAG,yBAAAA,aAAAA;AACAI,uBAAAA,WAAAA;AACAE,YAAAA;AACF,SAAA,CAAA,EACA;AACElB,QAAAA,KAAAA,CAAMO,YAAY;AAClBa,QAAAA,WAAAA;AACAG,QAAAA,iBAAAA;AACAvB,QAAAA,KAAAA,CAAMyB,SAAS;AACfrB,QAAAA,UAAAA;AACAK,QAAAA,MAAAA;AACAG,QAAAA,aAAAA;AACAI,QAAAA,WAAAA;AACAE,QAAAA;AACD,KAAA,CAAA;IAGH,OAAOQ,aAAAA,CAAc9B,qBAAAA,CAAsB+B,QAAQ,EAAE;AAAEH,QAAAA;KAAM,EAAGzB,QAAAA,CAAAA;AAClE;MAEa6B,iBAAAA,GAAoB,IAAA;AAC/B,IAAA,MAAMC,UAAUC,UAAAA,CAAWlC,qBAAAA,CAAAA;AAE3B,IAAA,IAAI,CAACiC,OAAAA,EAAS;AACZ,QAAA,MAAM,IAAIE,KAAAA,CAAM,iEAAA,CAAA;AAClB,IAAA;IAEA,OAAOF,OAAAA;AACT;AAEO,MAAMG,yBAAAA,GAA4B,IAAMF,UAAAA,CAAWlC,qBAAAA;;;;"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var strapiAdmin = require('@strapi/admin/strapi-admin');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Sort state for the assets list, backed by URL query params so the current
|
|
7
|
+
* ordering is shareable and survives Back/refresh:
|
|
8
|
+
*
|
|
9
|
+
* - `?sort=<rule>` — a single whitelisted rule. Omitted → default (most
|
|
10
|
+
* recent updates).
|
|
11
|
+
* - `?folders=mixed` — folders interleaved with files. Omitted → folders on top.
|
|
12
|
+
*
|
|
13
|
+
* The date rules and the direction rules are mutually exclusive: exactly one
|
|
14
|
+
* rule is active at a time (one checkmark total, like the design mock). Picking
|
|
15
|
+
* an option in one group clears the other; clearing the active option falls
|
|
16
|
+
* back to the default — an unsorted list is not a state the UI offers.
|
|
17
|
+
*/ const SORT_BY_RULES = {
|
|
18
|
+
oldestUploads: 'createdAt:ASC',
|
|
19
|
+
mostRecentUpdates: 'updatedAt:DESC'
|
|
20
|
+
};
|
|
21
|
+
const SORT_DIRECTION_RULES = {
|
|
22
|
+
nameAsc: 'name:ASC',
|
|
23
|
+
nameDesc: 'name:DESC',
|
|
24
|
+
sizeAsc: 'size:ASC',
|
|
25
|
+
sizeDesc: 'size:DESC'
|
|
26
|
+
};
|
|
27
|
+
const DEFAULT_SORT_BY = 'mostRecentUpdates';
|
|
28
|
+
const SORT_BY_BY_RULE = Object.fromEntries(Object.entries(SORT_BY_RULES).map(([key, rule])=>[
|
|
29
|
+
rule,
|
|
30
|
+
key
|
|
31
|
+
]));
|
|
32
|
+
const SORT_DIRECTION_BY_RULE = Object.fromEntries(Object.entries(SORT_DIRECTION_RULES).map(([key, rule])=>[
|
|
33
|
+
rule,
|
|
34
|
+
key
|
|
35
|
+
]));
|
|
36
|
+
/** Parses `?sort=` — the first recognized rule wins, unknown rules are dropped. */ const parseSortParam = (raw)=>{
|
|
37
|
+
for (const rule of (raw ?? '').split(',')){
|
|
38
|
+
if (rule in SORT_BY_BY_RULE) {
|
|
39
|
+
return {
|
|
40
|
+
sortBy: SORT_BY_BY_RULE[rule],
|
|
41
|
+
direction: null,
|
|
42
|
+
isExplicit: true
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (rule in SORT_DIRECTION_BY_RULE) {
|
|
46
|
+
return {
|
|
47
|
+
sortBy: null,
|
|
48
|
+
direction: SORT_DIRECTION_BY_RULE[rule],
|
|
49
|
+
isExplicit: true
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
sortBy: DEFAULT_SORT_BY,
|
|
55
|
+
direction: null,
|
|
56
|
+
isExplicit: false
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
const serializeSort = (sortBy, direction)=>{
|
|
60
|
+
const rules = [
|
|
61
|
+
sortBy && SORT_BY_RULES[sortBy],
|
|
62
|
+
direction && SORT_DIRECTION_RULES[direction]
|
|
63
|
+
];
|
|
64
|
+
return rules.filter((rule)=>Boolean(rule)).join(',');
|
|
65
|
+
};
|
|
66
|
+
const useListSort = ()=>{
|
|
67
|
+
const [{ query }, setQuery] = strapiAdmin.useQueryParams();
|
|
68
|
+
const { sortBy, direction, isExplicit } = parseSortParam(query?.sort);
|
|
69
|
+
const foldersPosition = query?.folders === 'mixed' ? 'mixed' : 'top';
|
|
70
|
+
const writeSort = (nextSortBy, nextDirection)=>{
|
|
71
|
+
// Exactly one active rule: clearing the active one falls back to default.
|
|
72
|
+
if (nextSortBy === null && nextDirection === null) {
|
|
73
|
+
nextSortBy = DEFAULT_SORT_BY;
|
|
74
|
+
}
|
|
75
|
+
const serialized = serializeSort(nextSortBy, nextDirection);
|
|
76
|
+
if (nextSortBy === DEFAULT_SORT_BY && nextDirection === null) {
|
|
77
|
+
setQuery({
|
|
78
|
+
sort: ''
|
|
79
|
+
}, 'remove');
|
|
80
|
+
} else {
|
|
81
|
+
setQuery({
|
|
82
|
+
sort: serialized
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
// Groups are mutually exclusive — picking in one clears the other.
|
|
87
|
+
const setSortBy = (key)=>writeSort(key, null);
|
|
88
|
+
const setDirection = (key)=>writeSort(null, key);
|
|
89
|
+
const setFoldersPosition = (position)=>{
|
|
90
|
+
if (position === 'mixed') {
|
|
91
|
+
setQuery({
|
|
92
|
+
folders: 'mixed'
|
|
93
|
+
});
|
|
94
|
+
} else {
|
|
95
|
+
setQuery({
|
|
96
|
+
folders: ''
|
|
97
|
+
}, 'remove');
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
const assetsSort = serializeSort(sortBy, direction);
|
|
101
|
+
const folderRuleCandidates = [
|
|
102
|
+
sortBy && SORT_BY_RULES[sortBy],
|
|
103
|
+
direction && !direction.startsWith('size') ? SORT_DIRECTION_RULES[direction] : null
|
|
104
|
+
];
|
|
105
|
+
const folderRules = folderRuleCandidates.filter((rule)=>Boolean(rule));
|
|
106
|
+
// Default state keeps folders alphabetical (matches the sidebar tree) — only
|
|
107
|
+
// an explicit user sort reorders the folders band.
|
|
108
|
+
const foldersSort = isExplicit && folderRules.length > 0 ? folderRules.join(',') : 'name:ASC';
|
|
109
|
+
return {
|
|
110
|
+
sortBy,
|
|
111
|
+
direction,
|
|
112
|
+
foldersPosition,
|
|
113
|
+
assetsSort,
|
|
114
|
+
foldersSort,
|
|
115
|
+
setSortBy,
|
|
116
|
+
setDirection,
|
|
117
|
+
setFoldersPosition
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
exports.SORT_BY_RULES = SORT_BY_RULES;
|
|
122
|
+
exports.SORT_DIRECTION_RULES = SORT_DIRECTION_RULES;
|
|
123
|
+
exports.useListSort = useListSort;
|
|
124
|
+
//# sourceMappingURL=useListSort.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useListSort.js","sources":["../../../../../../admin/src/future/pages/Assets/hooks/useListSort.ts"],"sourcesContent":["import { useQueryParams } from '@strapi/admin/strapi-admin';\n\n/**\n * Sort state for the assets list, backed by URL query params so the current\n * ordering is shareable and survives Back/refresh:\n *\n * - `?sort=<rule>` — a single whitelisted rule. Omitted → default (most\n * recent updates).\n * - `?folders=mixed` — folders interleaved with files. Omitted → folders on top.\n *\n * The date rules and the direction rules are mutually exclusive: exactly one\n * rule is active at a time (one checkmark total, like the design mock). Picking\n * an option in one group clears the other; clearing the active option falls\n * back to the default — an unsorted list is not a state the UI offers.\n */\n\nexport const SORT_BY_RULES = {\n oldestUploads: 'createdAt:ASC',\n mostRecentUpdates: 'updatedAt:DESC',\n} as const;\n\nexport const SORT_DIRECTION_RULES = {\n nameAsc: 'name:ASC',\n nameDesc: 'name:DESC',\n sizeAsc: 'size:ASC',\n sizeDesc: 'size:DESC',\n} as const;\n\nexport type SortByKey = keyof typeof SORT_BY_RULES;\nexport type SortDirectionKey = keyof typeof SORT_DIRECTION_RULES;\nexport type FoldersPosition = 'top' | 'mixed';\n\nconst DEFAULT_SORT_BY: SortByKey = 'mostRecentUpdates';\n\nconst SORT_BY_BY_RULE = Object.fromEntries(\n Object.entries(SORT_BY_RULES).map(([key, rule]) => [rule, key])\n) as Record<string, SortByKey>;\n\nconst SORT_DIRECTION_BY_RULE = Object.fromEntries(\n Object.entries(SORT_DIRECTION_RULES).map(([key, rule]) => [rule, key])\n) as Record<string, SortDirectionKey>;\n\ninterface ParsedSort {\n sortBy: SortByKey | null;\n direction: SortDirectionKey | null;\n /** False when the state is the fallback default (no recognized rule in the URL). */\n isExplicit: boolean;\n}\n\n/** Parses `?sort=` — the first recognized rule wins, unknown rules are dropped. */\nconst parseSortParam = (raw: string | undefined): ParsedSort => {\n for (const rule of (raw ?? '').split(',')) {\n if (rule in SORT_BY_BY_RULE) {\n return { sortBy: SORT_BY_BY_RULE[rule], direction: null, isExplicit: true };\n }\n if (rule in SORT_DIRECTION_BY_RULE) {\n return { sortBy: null, direction: SORT_DIRECTION_BY_RULE[rule], isExplicit: true };\n }\n }\n\n return { sortBy: DEFAULT_SORT_BY, direction: null, isExplicit: false };\n};\n\nconst serializeSort = (sortBy: SortByKey | null, direction: SortDirectionKey | null): string => {\n const rules: (string | null)[] = [\n sortBy && SORT_BY_RULES[sortBy],\n direction && SORT_DIRECTION_RULES[direction],\n ];\n\n return rules.filter((rule): rule is string => Boolean(rule)).join(',');\n};\n\nexport interface ListSort {\n sortBy: SortByKey | null;\n direction: SortDirectionKey | null;\n foldersPosition: FoldersPosition;\n /**\n * The active rule for the files query, e.g. `updatedAt:DESC` — always a\n * single rule since the sort groups are mutually exclusive.\n */\n assetsSort: string;\n /**\n * Rule applicable to the folders query. In the default state (no sort in the\n * URL) folders keep their alphabetical order so the band matches the sidebar\n * tree; an explicit user sort applies, except size rules (folders have no\n * `size` column), which fall back to alphabetical.\n */\n foldersSort: string;\n setSortBy: (key: SortByKey | null) => void;\n setDirection: (key: SortDirectionKey | null) => void;\n setFoldersPosition: (position: FoldersPosition) => void;\n}\n\nexport const useListSort = (): ListSort => {\n const [{ query }, setQuery] = useQueryParams<{ sort?: string; folders?: string }>();\n\n const { sortBy, direction, isExplicit } = parseSortParam(query?.sort);\n const foldersPosition: FoldersPosition = query?.folders === 'mixed' ? 'mixed' : 'top';\n\n const writeSort = (nextSortBy: SortByKey | null, nextDirection: SortDirectionKey | null) => {\n // Exactly one active rule: clearing the active one falls back to default.\n if (nextSortBy === null && nextDirection === null) {\n nextSortBy = DEFAULT_SORT_BY;\n }\n\n const serialized = serializeSort(nextSortBy, nextDirection);\n\n if (nextSortBy === DEFAULT_SORT_BY && nextDirection === null) {\n setQuery({ sort: '' }, 'remove');\n } else {\n setQuery({ sort: serialized });\n }\n };\n\n // Groups are mutually exclusive — picking in one clears the other.\n const setSortBy = (key: SortByKey | null) => writeSort(key, null);\n const setDirection = (key: SortDirectionKey | null) => writeSort(null, key);\n\n const setFoldersPosition = (position: FoldersPosition) => {\n if (position === 'mixed') {\n setQuery({ folders: 'mixed' });\n } else {\n setQuery({ folders: '' }, 'remove');\n }\n };\n\n const assetsSort = serializeSort(sortBy, direction);\n const folderRuleCandidates: (string | null)[] = [\n sortBy && SORT_BY_RULES[sortBy],\n direction && !direction.startsWith('size') ? SORT_DIRECTION_RULES[direction] : null,\n ];\n const folderRules = folderRuleCandidates.filter((rule): rule is string => Boolean(rule));\n // Default state keeps folders alphabetical (matches the sidebar tree) — only\n // an explicit user sort reorders the folders band.\n const foldersSort = isExplicit && folderRules.length > 0 ? folderRules.join(',') : 'name:ASC';\n\n return {\n sortBy,\n direction,\n foldersPosition,\n assetsSort,\n foldersSort,\n setSortBy,\n setDirection,\n setFoldersPosition,\n };\n};\n"],"names":["SORT_BY_RULES","oldestUploads","mostRecentUpdates","SORT_DIRECTION_RULES","nameAsc","nameDesc","sizeAsc","sizeDesc","DEFAULT_SORT_BY","SORT_BY_BY_RULE","Object","fromEntries","entries","map","key","rule","SORT_DIRECTION_BY_RULE","parseSortParam","raw","split","sortBy","direction","isExplicit","serializeSort","rules","filter","Boolean","join","useListSort","query","setQuery","useQueryParams","sort","foldersPosition","folders","writeSort","nextSortBy","nextDirection","serialized","setSortBy","setDirection","setFoldersPosition","position","assetsSort","folderRuleCandidates","startsWith","folderRules","foldersSort","length"],"mappings":"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"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { useQueryParams } from '@strapi/admin/strapi-admin';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Sort state for the assets list, backed by URL query params so the current
|
|
5
|
+
* ordering is shareable and survives Back/refresh:
|
|
6
|
+
*
|
|
7
|
+
* - `?sort=<rule>` — a single whitelisted rule. Omitted → default (most
|
|
8
|
+
* recent updates).
|
|
9
|
+
* - `?folders=mixed` — folders interleaved with files. Omitted → folders on top.
|
|
10
|
+
*
|
|
11
|
+
* The date rules and the direction rules are mutually exclusive: exactly one
|
|
12
|
+
* rule is active at a time (one checkmark total, like the design mock). Picking
|
|
13
|
+
* an option in one group clears the other; clearing the active option falls
|
|
14
|
+
* back to the default — an unsorted list is not a state the UI offers.
|
|
15
|
+
*/ const SORT_BY_RULES = {
|
|
16
|
+
oldestUploads: 'createdAt:ASC',
|
|
17
|
+
mostRecentUpdates: 'updatedAt:DESC'
|
|
18
|
+
};
|
|
19
|
+
const SORT_DIRECTION_RULES = {
|
|
20
|
+
nameAsc: 'name:ASC',
|
|
21
|
+
nameDesc: 'name:DESC',
|
|
22
|
+
sizeAsc: 'size:ASC',
|
|
23
|
+
sizeDesc: 'size:DESC'
|
|
24
|
+
};
|
|
25
|
+
const DEFAULT_SORT_BY = 'mostRecentUpdates';
|
|
26
|
+
const SORT_BY_BY_RULE = Object.fromEntries(Object.entries(SORT_BY_RULES).map(([key, rule])=>[
|
|
27
|
+
rule,
|
|
28
|
+
key
|
|
29
|
+
]));
|
|
30
|
+
const SORT_DIRECTION_BY_RULE = Object.fromEntries(Object.entries(SORT_DIRECTION_RULES).map(([key, rule])=>[
|
|
31
|
+
rule,
|
|
32
|
+
key
|
|
33
|
+
]));
|
|
34
|
+
/** Parses `?sort=` — the first recognized rule wins, unknown rules are dropped. */ const parseSortParam = (raw)=>{
|
|
35
|
+
for (const rule of (raw ?? '').split(',')){
|
|
36
|
+
if (rule in SORT_BY_BY_RULE) {
|
|
37
|
+
return {
|
|
38
|
+
sortBy: SORT_BY_BY_RULE[rule],
|
|
39
|
+
direction: null,
|
|
40
|
+
isExplicit: true
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (rule in SORT_DIRECTION_BY_RULE) {
|
|
44
|
+
return {
|
|
45
|
+
sortBy: null,
|
|
46
|
+
direction: SORT_DIRECTION_BY_RULE[rule],
|
|
47
|
+
isExplicit: true
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
sortBy: DEFAULT_SORT_BY,
|
|
53
|
+
direction: null,
|
|
54
|
+
isExplicit: false
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
const serializeSort = (sortBy, direction)=>{
|
|
58
|
+
const rules = [
|
|
59
|
+
sortBy && SORT_BY_RULES[sortBy],
|
|
60
|
+
direction && SORT_DIRECTION_RULES[direction]
|
|
61
|
+
];
|
|
62
|
+
return rules.filter((rule)=>Boolean(rule)).join(',');
|
|
63
|
+
};
|
|
64
|
+
const useListSort = ()=>{
|
|
65
|
+
const [{ query }, setQuery] = useQueryParams();
|
|
66
|
+
const { sortBy, direction, isExplicit } = parseSortParam(query?.sort);
|
|
67
|
+
const foldersPosition = query?.folders === 'mixed' ? 'mixed' : 'top';
|
|
68
|
+
const writeSort = (nextSortBy, nextDirection)=>{
|
|
69
|
+
// Exactly one active rule: clearing the active one falls back to default.
|
|
70
|
+
if (nextSortBy === null && nextDirection === null) {
|
|
71
|
+
nextSortBy = DEFAULT_SORT_BY;
|
|
72
|
+
}
|
|
73
|
+
const serialized = serializeSort(nextSortBy, nextDirection);
|
|
74
|
+
if (nextSortBy === DEFAULT_SORT_BY && nextDirection === null) {
|
|
75
|
+
setQuery({
|
|
76
|
+
sort: ''
|
|
77
|
+
}, 'remove');
|
|
78
|
+
} else {
|
|
79
|
+
setQuery({
|
|
80
|
+
sort: serialized
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
// Groups are mutually exclusive — picking in one clears the other.
|
|
85
|
+
const setSortBy = (key)=>writeSort(key, null);
|
|
86
|
+
const setDirection = (key)=>writeSort(null, key);
|
|
87
|
+
const setFoldersPosition = (position)=>{
|
|
88
|
+
if (position === 'mixed') {
|
|
89
|
+
setQuery({
|
|
90
|
+
folders: 'mixed'
|
|
91
|
+
});
|
|
92
|
+
} else {
|
|
93
|
+
setQuery({
|
|
94
|
+
folders: ''
|
|
95
|
+
}, 'remove');
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
const assetsSort = serializeSort(sortBy, direction);
|
|
99
|
+
const folderRuleCandidates = [
|
|
100
|
+
sortBy && SORT_BY_RULES[sortBy],
|
|
101
|
+
direction && !direction.startsWith('size') ? SORT_DIRECTION_RULES[direction] : null
|
|
102
|
+
];
|
|
103
|
+
const folderRules = folderRuleCandidates.filter((rule)=>Boolean(rule));
|
|
104
|
+
// Default state keeps folders alphabetical (matches the sidebar tree) — only
|
|
105
|
+
// an explicit user sort reorders the folders band.
|
|
106
|
+
const foldersSort = isExplicit && folderRules.length > 0 ? folderRules.join(',') : 'name:ASC';
|
|
107
|
+
return {
|
|
108
|
+
sortBy,
|
|
109
|
+
direction,
|
|
110
|
+
foldersPosition,
|
|
111
|
+
assetsSort,
|
|
112
|
+
foldersSort,
|
|
113
|
+
setSortBy,
|
|
114
|
+
setDirection,
|
|
115
|
+
setFoldersPosition
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export { SORT_BY_RULES, SORT_DIRECTION_RULES, useListSort };
|
|
120
|
+
//# sourceMappingURL=useListSort.mjs.map
|