@strapi/upload 5.60.0 → 5.61.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/admin/components/AssetPickerDialog/MediaLibraryPickerDialog.js +29 -0
  2. package/dist/admin/components/AssetPickerDialog/MediaLibraryPickerDialog.js.map +1 -0
  3. package/dist/admin/components/AssetPickerDialog/MediaLibraryPickerDialog.mjs +27 -0
  4. package/dist/admin/components/AssetPickerDialog/MediaLibraryPickerDialog.mjs.map +1 -0
  5. package/dist/admin/components/Drawer.js +12 -7
  6. package/dist/admin/components/Drawer.js.map +1 -1
  7. package/dist/admin/components/Drawer.mjs +12 -7
  8. package/dist/admin/components/Drawer.mjs.map +1 -1
  9. package/dist/admin/components/EditorFileDropZone/EditorFileDropZone.js +188 -0
  10. package/dist/admin/components/EditorFileDropZone/EditorFileDropZone.js.map +1 -0
  11. package/dist/admin/components/EditorFileDropZone/EditorFileDropZone.mjs +186 -0
  12. package/dist/admin/components/EditorFileDropZone/EditorFileDropZone.mjs.map +1 -0
  13. package/dist/admin/components/MediaLibraryInput/AddAssetMenu.js +5 -1
  14. package/dist/admin/components/MediaLibraryInput/AddAssetMenu.js.map +1 -1
  15. package/dist/admin/components/MediaLibraryInput/AddAssetMenu.mjs +5 -1
  16. package/dist/admin/components/MediaLibraryInput/AddAssetMenu.mjs.map +1 -1
  17. package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.js +37 -27
  18. package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.js.map +1 -1
  19. package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.mjs +37 -27
  20. package/dist/admin/components/MediaLibraryInput/MediaLibraryInput.mjs.map +1 -1
  21. package/dist/admin/components/MediaLibraryInput/openAssetDetails.js +46 -0
  22. package/dist/admin/components/MediaLibraryInput/openAssetDetails.js.map +1 -0
  23. package/dist/admin/components/MediaLibraryInput/openAssetDetails.mjs +43 -0
  24. package/dist/admin/components/MediaLibraryInput/openAssetDetails.mjs.map +1 -0
  25. package/dist/admin/index.js +10 -1
  26. package/dist/admin/index.js.map +1 -1
  27. package/dist/admin/index.mjs +10 -1
  28. package/dist/admin/index.mjs.map +1 -1
  29. package/dist/admin/pages/Assets/AssetsPage.js +1 -1
  30. package/dist/admin/pages/Assets/AssetsPage.js.map +1 -1
  31. package/dist/admin/pages/Assets/AssetsPage.mjs +2 -2
  32. package/dist/admin/pages/Assets/AssetsPage.mjs.map +1 -1
  33. package/dist/admin/pages/Assets/components/AssetDetails/AssetCropEditor.mjs +2 -2
  34. package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +181 -65
  35. package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
  36. package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +184 -69
  37. package/dist/admin/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
  38. package/dist/admin/pages/Assets/components/AssetDetails/AssetPreview.mjs +1 -1
  39. package/dist/admin/pages/Assets/hooks/useIsAssetDetailsOpen.js +2 -6
  40. package/dist/admin/pages/Assets/hooks/useIsAssetDetailsOpen.js.map +1 -1
  41. package/dist/admin/pages/Assets/hooks/useIsAssetDetailsOpen.mjs +2 -6
  42. package/dist/admin/pages/Assets/hooks/useIsAssetDetailsOpen.mjs.map +1 -1
  43. package/dist/admin/src/components/AssetPickerDialog/MediaLibraryPickerDialog.d.ts +19 -0
  44. package/dist/admin/src/components/Drawer.d.ts +8 -0
  45. package/dist/admin/src/components/EditorFileDropZone/EditorFileDropZone.d.ts +21 -0
  46. package/dist/admin/src/components/MediaLibraryInput/AddAssetMenu.d.ts +7 -1
  47. package/dist/admin/src/components/MediaLibraryInput/openAssetDetails.d.ts +10 -0
  48. package/dist/admin/src/pages/Assets/components/AssetDetails/AssetDetailsDrawer.d.ts +41 -5
  49. package/dist/admin/src/pages/Assets/hooks/useIsAssetDetailsOpen.d.ts +2 -6
  50. package/dist/admin/translations/en.json.js +4 -0
  51. package/dist/admin/translations/en.json.js.map +1 -1
  52. package/dist/admin/translations/en.json.mjs +4 -0
  53. package/dist/admin/translations/en.json.mjs.map +1 -1
  54. package/dist/server/audit-logs.js +264 -0
  55. package/dist/server/audit-logs.js.map +1 -0
  56. package/dist/server/audit-logs.mjs +255 -0
  57. package/dist/server/audit-logs.mjs.map +1 -0
  58. package/dist/server/bootstrap.js +4 -0
  59. package/dist/server/bootstrap.js.map +1 -1
  60. package/dist/server/bootstrap.mjs +4 -0
  61. package/dist/server/bootstrap.mjs.map +1 -1
  62. package/dist/server/controllers/admin-folder-file.js +23 -1
  63. package/dist/server/controllers/admin-folder-file.js.map +1 -1
  64. package/dist/server/controllers/admin-folder-file.mjs +23 -1
  65. package/dist/server/controllers/admin-folder-file.mjs.map +1 -1
  66. package/dist/server/services/folder.js +48 -1
  67. package/dist/server/services/folder.js.map +1 -1
  68. package/dist/server/services/folder.mjs +48 -1
  69. package/dist/server/services/folder.mjs.map +1 -1
  70. package/dist/server/services/image-manipulation.js +12 -5
  71. package/dist/server/services/image-manipulation.js.map +1 -1
  72. package/dist/server/services/image-manipulation.mjs +12 -5
  73. package/dist/server/services/image-manipulation.mjs.map +1 -1
  74. package/dist/server/services/upload.js +100 -16
  75. package/dist/server/services/upload.js.map +1 -1
  76. package/dist/server/services/upload.mjs +102 -18
  77. package/dist/server/services/upload.mjs.map +1 -1
  78. package/dist/server/src/audit-logs.d.ts +142 -0
  79. package/dist/server/src/audit-logs.d.ts.map +1 -0
  80. package/dist/server/src/bootstrap.d.ts.map +1 -1
  81. package/dist/server/src/controllers/admin-folder-file.d.ts.map +1 -1
  82. package/dist/server/src/index.d.ts +3 -3
  83. package/dist/server/src/index.d.ts.map +1 -1
  84. package/dist/server/src/mcp/ambient-instance.d.ts.map +1 -1
  85. package/dist/server/src/services/folder.d.ts.map +1 -1
  86. package/dist/server/src/services/image-manipulation.d.ts +3 -3
  87. package/dist/server/src/services/image-manipulation.d.ts.map +1 -1
  88. package/dist/server/src/services/index.d.ts +3 -3
  89. package/dist/server/src/services/index.d.ts.map +1 -1
  90. package/dist/server/src/services/upload.d.ts.map +1 -1
  91. package/package.json +9 -16
@@ -0,0 +1,29 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var reactIntl = require('react-intl');
5
+ var translations = require('../../utils/translations.js');
6
+ var AssetPickerDialog = require('./AssetPickerDialog.js');
7
+
8
+ /**
9
+ * The current asset picker behind the `media-library` component. The editors
10
+ * insert assets rather than hold a value, and don't pass a field label, so the
11
+ * title is generic.
12
+ */ const MediaLibraryPickerDialog = ({ allowedTypes, multiple = true, initiallySelectedAssets, onClose, onSelectAssets })=>{
13
+ const { formatMessage } = reactIntl.useIntl();
14
+ return /*#__PURE__*/ jsxRuntime.jsx(AssetPickerDialog.AssetPickerDialog, {
15
+ open: true,
16
+ title: formatMessage({
17
+ id: translations.getTranslationKey('asset-picker.title.insert'),
18
+ defaultMessage: 'Add assets'
19
+ }),
20
+ initialSelection: initiallySelectedAssets,
21
+ multiple: multiple,
22
+ allowedTypes: allowedTypes,
23
+ onClose: onClose,
24
+ onConfirm: onSelectAssets
25
+ });
26
+ };
27
+
28
+ exports.MediaLibraryPickerDialog = MediaLibraryPickerDialog;
29
+ //# sourceMappingURL=MediaLibraryPickerDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MediaLibraryPickerDialog.js","sources":["../../../../admin/src/components/AssetPickerDialog/MediaLibraryPickerDialog.tsx"],"sourcesContent":["import { useIntl } from 'react-intl';\n\nimport { getTranslationKey } from '../../utils/translations';\n\nimport { AssetPickerDialog } from './AssetPickerDialog';\n\nimport type { File } from '../../../../shared/contracts/files';\nimport type { MediaAllowedTypes } from '../../utils/isAllowedAssetType';\n\n/**\n * The `media-library` component contract, which the Wysiwyg and Blocks editors\n * render to insert images. Kept identical to the legacy `MediaLibraryDialog`.\n */\nexport interface MediaLibraryPickerDialogProps {\n allowedTypes?: MediaAllowedTypes;\n multiple?: boolean;\n initiallySelectedAssets?: File[];\n onClose: () => void;\n onSelectAssets: (selectedAssets: File[]) => void;\n}\n\n/**\n * The current asset picker behind the `media-library` component. The editors\n * insert assets rather than hold a value, and don't pass a field label, so the\n * title is generic.\n */\nexport const MediaLibraryPickerDialog = ({\n allowedTypes,\n multiple = true,\n initiallySelectedAssets,\n onClose,\n onSelectAssets,\n}: MediaLibraryPickerDialogProps) => {\n const { formatMessage } = useIntl();\n\n return (\n <AssetPickerDialog\n open\n title={formatMessage({\n id: getTranslationKey('asset-picker.title.insert'),\n defaultMessage: 'Add assets',\n })}\n initialSelection={initiallySelectedAssets}\n multiple={multiple}\n allowedTypes={allowedTypes}\n onClose={onClose}\n onConfirm={onSelectAssets}\n />\n );\n};\n"],"names":["MediaLibraryPickerDialog","allowedTypes","multiple","initiallySelectedAssets","onClose","onSelectAssets","formatMessage","useIntl","_jsx","AssetPickerDialog","open","title","id","getTranslationKey","defaultMessage","initialSelection","onConfirm"],"mappings":";;;;;;;AAqBA;;;;AAIC,IACM,MAAMA,wBAAAA,GAA2B,CAAC,EACvCC,YAAY,EACZC,QAAAA,GAAW,IAAI,EACfC,uBAAuB,EACvBC,OAAO,EACPC,cAAc,EACgB,GAAA;IAC9B,MAAM,EAAEC,aAAa,EAAE,GAAGC,iBAAAA,EAAAA;AAE1B,IAAA,qBACEC,cAAA,CAACC,mCAAAA,EAAAA;QACCC,IAAI,EAAA,IAAA;AACJC,QAAAA,KAAAA,EAAOL,aAAAA,CAAc;AACnBM,YAAAA,EAAAA,EAAIC,8BAAAA,CAAkB,2BAAA,CAAA;YACtBC,cAAAA,EAAgB;AAClB,SAAA,CAAA;QACAC,gBAAAA,EAAkBZ,uBAAAA;QAClBD,QAAAA,EAAUA,QAAAA;QACVD,YAAAA,EAAcA,YAAAA;QACdG,OAAAA,EAASA,OAAAA;QACTY,SAAAA,EAAWX;;AAGjB;;;;"}
@@ -0,0 +1,27 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useIntl } from 'react-intl';
3
+ import { getTranslationKey } from '../../utils/translations.mjs';
4
+ import { AssetPickerDialog } from './AssetPickerDialog.mjs';
5
+
6
+ /**
7
+ * The current asset picker behind the `media-library` component. The editors
8
+ * insert assets rather than hold a value, and don't pass a field label, so the
9
+ * title is generic.
10
+ */ const MediaLibraryPickerDialog = ({ allowedTypes, multiple = true, initiallySelectedAssets, onClose, onSelectAssets })=>{
11
+ const { formatMessage } = useIntl();
12
+ return /*#__PURE__*/ jsx(AssetPickerDialog, {
13
+ open: true,
14
+ title: formatMessage({
15
+ id: getTranslationKey('asset-picker.title.insert'),
16
+ defaultMessage: 'Add assets'
17
+ }),
18
+ initialSelection: initiallySelectedAssets,
19
+ multiple: multiple,
20
+ allowedTypes: allowedTypes,
21
+ onClose: onClose,
22
+ onConfirm: onSelectAssets
23
+ });
24
+ };
25
+
26
+ export { MediaLibraryPickerDialog };
27
+ //# sourceMappingURL=MediaLibraryPickerDialog.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MediaLibraryPickerDialog.mjs","sources":["../../../../admin/src/components/AssetPickerDialog/MediaLibraryPickerDialog.tsx"],"sourcesContent":["import { useIntl } from 'react-intl';\n\nimport { getTranslationKey } from '../../utils/translations';\n\nimport { AssetPickerDialog } from './AssetPickerDialog';\n\nimport type { File } from '../../../../shared/contracts/files';\nimport type { MediaAllowedTypes } from '../../utils/isAllowedAssetType';\n\n/**\n * The `media-library` component contract, which the Wysiwyg and Blocks editors\n * render to insert images. Kept identical to the legacy `MediaLibraryDialog`.\n */\nexport interface MediaLibraryPickerDialogProps {\n allowedTypes?: MediaAllowedTypes;\n multiple?: boolean;\n initiallySelectedAssets?: File[];\n onClose: () => void;\n onSelectAssets: (selectedAssets: File[]) => void;\n}\n\n/**\n * The current asset picker behind the `media-library` component. The editors\n * insert assets rather than hold a value, and don't pass a field label, so the\n * title is generic.\n */\nexport const MediaLibraryPickerDialog = ({\n allowedTypes,\n multiple = true,\n initiallySelectedAssets,\n onClose,\n onSelectAssets,\n}: MediaLibraryPickerDialogProps) => {\n const { formatMessage } = useIntl();\n\n return (\n <AssetPickerDialog\n open\n title={formatMessage({\n id: getTranslationKey('asset-picker.title.insert'),\n defaultMessage: 'Add assets',\n })}\n initialSelection={initiallySelectedAssets}\n multiple={multiple}\n allowedTypes={allowedTypes}\n onClose={onClose}\n onConfirm={onSelectAssets}\n />\n );\n};\n"],"names":["MediaLibraryPickerDialog","allowedTypes","multiple","initiallySelectedAssets","onClose","onSelectAssets","formatMessage","useIntl","_jsx","AssetPickerDialog","open","title","id","getTranslationKey","defaultMessage","initialSelection","onConfirm"],"mappings":";;;;;AAqBA;;;;AAIC,IACM,MAAMA,wBAAAA,GAA2B,CAAC,EACvCC,YAAY,EACZC,QAAAA,GAAW,IAAI,EACfC,uBAAuB,EACvBC,OAAO,EACPC,cAAc,EACgB,GAAA;IAC9B,MAAM,EAAEC,aAAa,EAAE,GAAGC,OAAAA,EAAAA;AAE1B,IAAA,qBACEC,GAAA,CAACC,iBAAAA,EAAAA;QACCC,IAAI,EAAA,IAAA;AACJC,QAAAA,KAAAA,EAAOL,aAAAA,CAAc;AACnBM,YAAAA,EAAAA,EAAIC,iBAAAA,CAAkB,2BAAA,CAAA;YACtBC,cAAAA,EAAgB;AAClB,SAAA,CAAA;QACAC,gBAAAA,EAAkBZ,uBAAAA;QAClBD,QAAAA,EAAUA,QAAAA;QACVD,YAAAA,EAAcA,YAAAA;QACdG,OAAAA,EAASA,OAAAA;QACTY,SAAAA,EAAWX;;AAGjB;;;;"}
@@ -88,12 +88,15 @@ const DrawerContainer = styledComponents.styled(designSystem.Flex)`
88
88
  width: 100%;
89
89
  padding: ${({ theme })=>theme.spaces[2]};
90
90
  max-width: 100%;
91
- /* Sit just below the overlay token (300) so that:
92
- - popovers (500) and tooltips (1000) rendered from descendant components
93
- surface above the drawer panel,
94
- - AlertDialog overlays (300) and contents (310) opened from inside the
95
- drawer (e.g. the asset details "delete" confirm) cover the drawer. */
96
- z-index: 200;
91
+ /* On the overlay token, so ties resolve by DOM order, as they do between DS
92
+ modals: a drawer opened from inside a modal (e.g. a media field in the
93
+ content manager's relation modal) portals in after it and sits above its
94
+ overlay, while a dialog opened from inside the drawer (e.g. the asset
95
+ details "delete" confirm) portals in after the drawer and covers it.
96
+ Not the modal token (310): a DS dialog's content stacks inside its own
97
+ overlay at 300, so the drawer would cover its own confirmations.
98
+ Popovers (500) and tooltips (1000) still surface above the panel. */
99
+ z-index: ${({ theme })=>theme.zIndices.overlay};
97
100
  overflow: hidden;
98
101
  /* dvh, not vh: anchored to the bottom, a 100vh cap on mobile (visual
99
102
  viewport < 100vh under the URL bar) would push the drawer top off-screen. */
@@ -162,11 +165,13 @@ const CloseIconButton = styledComponents.styled(designSystem.IconButton)`
162
165
  }
163
166
  `;
164
167
  /** Vetoes an outside interaction that would otherwise dismiss the drawer. */ const keepOpen = (event)=>event.preventDefault();
165
- const DrawerBody = /*#__PURE__*/ React__namespace.forwardRef(({ animationDirection, width, maxHeight, onPointerDownOutside = keepOpen, children, ...props }, ref)=>/*#__PURE__*/ jsxRuntime.jsx(Dialog__namespace.Content, {
168
+ const DrawerBody = /*#__PURE__*/ React__namespace.forwardRef(({ animationDirection, width, maxHeight, onPointerDownOutside = keepOpen, onOpenAutoFocus, onCloseAutoFocus, children, ...props }, ref)=>/*#__PURE__*/ jsxRuntime.jsx(Dialog__namespace.Content, {
166
169
  ref: ref,
167
170
  forceMount: true,
168
171
  asChild: true,
169
172
  onPointerDownOutside: onPointerDownOutside,
173
+ onOpenAutoFocus: onOpenAutoFocus,
174
+ onCloseAutoFocus: onCloseAutoFocus,
170
175
  // Non-modal, so focus legitimately moves outside (tabbing past the last
171
176
  // field); only a pointer press should ever dismiss.
172
177
  onFocusOutside: keepOpen,
@@ -1 +1 @@
1
- {"version":3,"file":"Drawer.js","sources":["../../../admin/src/components/Drawer.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport * as Dialog from '@radix-ui/react-dialog';\nimport { Box, ScrollArea, IconButton, Flex, FlexProps } from '@strapi/design-system';\nimport { Cross } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { keyframes, styled } from 'styled-components';\n\n/** Duration of the close animation in ms. Use for timing cleanup (e.g. removing URL params). */\nexport const DRAWER_CLOSE_ANIMATION_MS = 300;\n\n/* -------------------------------------------------------------------------------------------------\n * Animations\n * -----------------------------------------------------------------------------------------------*/\n\n// Direction: up\nconst slideUpFromBottomIn = keyframes`\n from {\n opacity: 0;\n transform: translateY(100%);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n`;\n\nconst slideUpFromBottomOut = keyframes`\n from {\n opacity: 1;\n transform: translateY(0);\n }\n to {\n opacity: 0;\n transform: translateY(100%);\n }\n`;\n\n// Direction: left\nconst slideLeftFromRightIn = keyframes`\n from {\n opacity: 0;\n transform: translateX(100%);\n }\n to {\n opacity: 1;\n transform: translateX(0);\n }\n`;\n\nconst slideLeftFromRightOut = keyframes`\n from {\n opacity: 1;\n transform: translateX(0);\n }\n to {\n opacity: 0;\n transform: translateX(100%);\n }\n`;\n\n/* -------------------------------------------------------------------------------------------------\n * Styled components\n * -----------------------------------------------------------------------------------------------*/\n\ninterface DrawerContainerProps {\n $animationDirection?: DrawerBodyProps['animationDirection'];\n $width?: string;\n $maxHeight?: string;\n}\n\nconst DrawerContainer = styled(Flex)<DrawerContainerProps>`\n flex-direction: column;\n position: fixed;\n bottom: 0;\n /* Mobile: full-bleed sheet. Anchoring to the right at a fixed width left a\n dead gutter on every phone wider than the panel, and the panel only filled\n the screen once the viewport happened to be narrower than it. Below the\n medium breakpoint the panel is the screen; medium+ restores the\n right-anchored rail. */\n left: 0;\n right: 0;\n width: 100%;\n padding: ${({ theme }) => theme.spaces[2]};\n max-width: 100%;\n /* Sit just below the overlay token (300) so that:\n - popovers (500) and tooltips (1000) rendered from descendant components\n surface above the drawer panel,\n - AlertDialog overlays (300) and contents (310) opened from inside the\n drawer (e.g. the asset details \"delete\" confirm) cover the drawer. */\n z-index: 200;\n overflow: hidden;\n /* dvh, not vh: anchored to the bottom, a 100vh cap on mobile (visual\n viewport < 100vh under the URL bar) would push the drawer top off-screen. */\n max-height: ${({ $maxHeight }) => $maxHeight ?? '100dvh'};\n\n ${({ theme }) => theme.breakpoints.medium} {\n left: auto;\n width: ${({ $width }) => $width ?? '400px'};\n }\n\n &:focus {\n outline: none;\n }\n\n @media (prefers-reduced-motion: no-preference) {\n &[data-state='open'] {\n animation: ${({ $animationDirection }) =>\n $animationDirection === 'up' ? slideUpFromBottomIn : slideLeftFromRightIn}\n ${DRAWER_CLOSE_ANIMATION_MS}ms cubic-bezier(0.32, 0.72, 0, 1) forwards;\n }\n\n &[data-state='closed'] {\n animation: ${({ $animationDirection }) =>\n $animationDirection === 'up' ? slideUpFromBottomOut : slideLeftFromRightOut}\n ${DRAWER_CLOSE_ANIMATION_MS}ms cubic-bezier(0.32, 0.72, 0, 1) forwards;\n pointer-events: none;\n }\n }\n`;\n\nconst DrawerContent = styled(Box)`\n display: flex;\n flex-direction: column;\n flex: 1;\n min-height: 0;\n width: 100%;\n background-color: ${({ theme }) => theme.colors.neutral0};\n border-radius: ${({ theme }) => theme.borderRadius};\n box-shadow: ${({ theme }) => theme.shadows.popupShadow};\n overflow: hidden;\n border: 1px solid ${({ theme }) => theme.colors.neutral150};\n`;\n\ninterface CollapsibleContentProps {\n $isVisible: boolean;\n}\n\nconst CollapsibleContent = styled(Box)<CollapsibleContentProps>`\n display: grid;\n flex: 1;\n min-height: 0;\n grid-template-rows: ${({ $isVisible }) => ($isVisible ? '1fr' : '0fr')};\n transition: grid-template-rows 0.3s ease-in-out;\n\n > div {\n overflow: hidden;\n min-height: 0;\n }\n\n /* The scroll area wraps its children in a div styled inline as\n display: table, min-width: 100%. A table box grows to its content, so a\n single unbreakable string (a long file name) widens it past the scrollport\n and the panel gains a horizontal scrollbar — with nothing left to clamp the\n row, the name never truncates. Overriding to a block box makes the\n scrollport the width again, which is what the vertical-only list wants.\n Inline styles, hence the important. */\n [data-radix-scroll-area-viewport] > div {\n display: block !important;\n }\n`;\n\nconst CloseIconButton = styled(IconButton)`\n &:hover {\n background: transparent;\n }\n`;\n\n/* -------------------------------------------------------------------------------------------------\n * Drawer.Body\n * -----------------------------------------------------------------------------------------------*/\n\ntype PointerDownOutsideHandler = NonNullable<Dialog.DialogContentProps['onPointerDownOutside']>;\n\n/** Vetoes an outside interaction that would otherwise dismiss the drawer. */\nconst keepOpen = (event: { preventDefault: () => void }) => event.preventDefault();\n\ninterface DrawerBodyProps extends Omit<FlexProps, 'width' | 'maxHeight'> {\n animationDirection?: 'up' | 'left';\n /** Width of the panel from the medium breakpoint up. Mobile is always full-bleed. */\n width?: string;\n /** Cap on the panel height. Defaults to the dynamic viewport height. */\n maxHeight?: string;\n /**\n * Opt in to dismiss-on-outside-click. Omitted, an outside pointer press never\n * closes the drawer — it must be closed explicitly, as long-running panels\n * like the upload progress dialog need. Passed, the handler can still veto\n * individual presses via `event.preventDefault()`. Content rendered from\n * inside the panel but portaled elsewhere (DS dialogs, popovers, tooltips)\n * never counts as outside — Radix reads the React tree, not the DOM.\n */\n onPointerDownOutside?: PointerDownOutsideHandler;\n children: React.ReactNode;\n}\n\nconst DrawerBody = React.forwardRef<HTMLDivElement, DrawerBodyProps>(\n (\n { animationDirection, width, maxHeight, onPointerDownOutside = keepOpen, children, ...props },\n ref\n ) => (\n <Dialog.Content\n ref={ref}\n forceMount\n asChild\n onPointerDownOutside={onPointerDownOutside}\n // Non-modal, so focus legitimately moves outside (tabbing past the last\n // field); only a pointer press should ever dismiss.\n onFocusOutside={keepOpen}\n data-animation-direction={animationDirection}\n >\n {/* width/maxHeight go through transient props so the DS Flex never emits a\n competing `width` rule that would outrank the mobile full-bleed one. */}\n <DrawerContainer\n $animationDirection={animationDirection}\n $width={width}\n $maxHeight={maxHeight}\n {...props}\n >\n <DrawerContent>{children}</DrawerContent>\n </DrawerContainer>\n </Dialog.Content>\n )\n);\nDrawerBody.displayName = 'DrawerBody';\n\n/* -------------------------------------------------------------------------------------------------\n * Drawer.Root\n * -----------------------------------------------------------------------------------------------*/\n\ninterface DrawerRootProps {\n isVisible?: boolean;\n onClose?: () => void;\n children: React.ReactNode;\n}\n\nconst DrawerRoot = ({ isVisible, onClose, children }: DrawerRootProps): React.ReactElement => (\n <Dialog.Root\n open={isVisible}\n onOpenChange={(nextVisible) => !nextVisible && onClose?.()}\n modal={false}\n >\n <Dialog.Portal>{children}</Dialog.Portal>\n </Dialog.Root>\n);\n\n/* -------------------------------------------------------------------------------------------------\n * Drawer.Content - composable content slot (collapsible when isContentExpanded is used)\n * Contains a scrollable area\n * -----------------------------------------------------------------------------------------------*/\n\ninterface DrawerScrollableContentProps {\n children: React.ReactNode;\n /** When provided, content can collapse/expand (e.g. for minimize). Omit to always show. */\n isContentExpanded?: boolean;\n}\n\nconst DrawerScrollableContent = ({\n children,\n isContentExpanded = true,\n}: DrawerScrollableContentProps) => (\n <CollapsibleContent $isVisible={isContentExpanded} data-collapsed={!isContentExpanded}>\n <ScrollArea>{children}</ScrollArea>\n </CollapsibleContent>\n);\n\n/* -------------------------------------------------------------------------------------------------\n * Drawer.CloseButton - composable close icon button (Cross icon by default)\n * -----------------------------------------------------------------------------------------------*/\n\ninterface DrawerCloseButtonProps extends React.PropsWithChildren {\n onClose: () => void;\n label?: string;\n}\n\nconst DrawerCloseButton = ({ onClose, label, children }: DrawerCloseButtonProps) => {\n const { formatMessage } = useIntl();\n const labelMessage = label ?? formatMessage({ id: 'global.close', defaultMessage: 'Close' });\n return (\n <CloseIconButton onClick={onClose} label={labelMessage} variant=\"ghost\">\n {children ?? <Cross />}\n </CloseIconButton>\n );\n};\n\nconst Drawer = {\n Root: DrawerRoot,\n Body: DrawerBody,\n ScrollableContent: DrawerScrollableContent,\n CloseButton: DrawerCloseButton,\n Title: Dialog.Title,\n Description: Dialog.Description,\n};\n\nexport { Drawer };\n"],"names":["DRAWER_CLOSE_ANIMATION_MS","slideUpFromBottomIn","keyframes","slideUpFromBottomOut","slideLeftFromRightIn","slideLeftFromRightOut","DrawerContainer","styled","Flex","theme","spaces","$maxHeight","breakpoints","medium","$width","$animationDirection","DrawerContent","Box","colors","neutral0","borderRadius","shadows","popupShadow","neutral150","CollapsibleContent","$isVisible","CloseIconButton","IconButton","keepOpen","event","preventDefault","DrawerBody","React","forwardRef","animationDirection","width","maxHeight","onPointerDownOutside","children","props","ref","_jsx","Dialog","Content","forceMount","asChild","onFocusOutside","data-animation-direction","displayName","DrawerRoot","isVisible","onClose","Root","open","onOpenChange","nextVisible","modal","Portal","DrawerScrollableContent","isContentExpanded","data-collapsed","ScrollArea","DrawerCloseButton","label","formatMessage","useIntl","labelMessage","id","defaultMessage","onClick","variant","Cross","Drawer","Body","ScrollableContent","CloseButton","Title","Description"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAQA,iGACO,MAAMA,yBAAAA,GAA4B;AAEzC;;AAEkG;AAGlG,MAAMC,mBAAAA,GAAsBC,0BAAS;;;;;;;;;AASrC,CAAC;AAED,MAAMC,oBAAAA,GAAuBD,0BAAS;;;;;;;;;AAStC,CAAC;AAED;AACA,MAAME,oBAAAA,GAAuBF,0BAAS;;;;;;;;;AAStC,CAAC;AAED,MAAMG,qBAAAA,GAAwBH,0BAAS;;;;;;;;;AASvC,CAAC;AAYD,MAAMI,eAAAA,GAAkBC,uBAAAA,CAAOC,iBAAAA,CAA2B;;;;;;;;;;;;WAY/C,EAAE,CAAC,EAAEC,KAAK,EAAE,GAAKA,KAAAA,CAAMC,MAAM,CAAC,CAAA,CAAE,CAAC;;;;;;;;;;;AAW9B,cAAA,EAAE,CAAC,EAAEC,UAAU,EAAE,GAAKA,cAAc,QAAA,CAAS;;EAEzD,EAAE,CAAC,EAAEF,KAAK,EAAE,GAAKA,KAAAA,CAAMG,WAAW,CAACC,MAAM,CAAC;;AAEjC,WAAA,EAAE,CAAC,EAAEC,MAAM,EAAE,GAAKA,UAAU,OAAA,CAAQ;;;;;;;;;iBAS9B,EAAE,CAAC,EAAEC,mBAAmB,EAAE,GACjCA,mBAAAA,KAAwB,IAAA,GAAOd,sBAAsBG,oBAAAA;AACvD,QAAA,EAAEJ,yBAAAA,CAA0B;;;;iBAInB,EAAE,CAAC,EAAEe,mBAAmB,EAAE,GACjCA,mBAAAA,KAAwB,IAAA,GAAOZ,uBAAuBE,qBAAAA;AACxD,QAAA,EAAEL,yBAAAA,CAA0B;;;;AAIpC,CAAC;AAED,MAAMgB,aAAAA,GAAgBT,uBAAAA,CAAOU,gBAAAA,CAAI;;;;;;oBAMb,EAAE,CAAC,EAAER,KAAK,EAAE,GAAKA,KAAAA,CAAMS,MAAM,CAACC,QAAQ,CAAC;AAC1C,iBAAA,EAAE,CAAC,EAAEV,KAAK,EAAE,GAAKA,KAAAA,CAAMW,YAAY,CAAC;cACvC,EAAE,CAAC,EAAEX,KAAK,EAAE,GAAKA,KAAAA,CAAMY,OAAO,CAACC,WAAW,CAAC;;oBAErC,EAAE,CAAC,EAAEb,KAAK,EAAE,GAAKA,KAAAA,CAAMS,MAAM,CAACK,UAAU,CAAC;AAC7D,CAAC;AAMD,MAAMC,kBAAAA,GAAqBjB,uBAAAA,CAAOU,gBAAAA,CAA6B;;;;AAIzC,sBAAA,EAAE,CAAC,EAAEQ,UAAU,EAAE,GAAMA,UAAAA,GAAa,QAAQ,KAAA,CAAO;;;;;;;;;;;;;;;;;;AAkBzE,CAAC;AAED,MAAMC,eAAAA,GAAkBnB,uBAAAA,CAAOoB,uBAAAA,CAAW;;;;AAI1C,CAAC;AAQD,8EACA,MAAMC,QAAAA,GAAW,CAACC,KAAAA,GAA0CA,MAAMC,cAAc,EAAA;AAoBhF,MAAMC,UAAAA,iBAAaC,iBAAMC,UAAU,CACjC,CACE,EAAEC,kBAAkB,EAAEC,KAAK,EAAEC,SAAS,EAAEC,oBAAAA,GAAuBT,QAAQ,EAAEU,QAAQ,EAAE,GAAGC,OAAO,EAC7FC,GAAAA,iBAEAC,cAAA,CAACC,iBAAAA,CAAOC,OAAO,EAAA;QACbH,GAAAA,EAAKA,GAAAA;QACLI,UAAU,EAAA,IAAA;QACVC,OAAO,EAAA,IAAA;QACPR,oBAAAA,EAAsBA,oBAAAA;;;QAGtBS,cAAAA,EAAgBlB,QAAAA;QAChBmB,0BAAAA,EAA0Bb,kBAAAA;AAI1B,QAAA,QAAA,gBAAAO,cAAA,CAACnC,eAAAA,EAAAA;YACCS,mBAAAA,EAAqBmB,kBAAAA;YACrBpB,MAAAA,EAAQqB,KAAAA;YACRxB,UAAAA,EAAYyB,SAAAA;AACX,YAAA,GAAGG,KAAK;AAET,YAAA,QAAA,gBAAAE,cAAA,CAACzB,aAAAA,EAAAA;AAAesB,gBAAAA,QAAAA,EAAAA;;;;AAKxBP,UAAAA,CAAWiB,WAAW,GAAG,YAAA;AAYzB,MAAMC,UAAAA,GAAa,CAAC,EAAEC,SAAS,EAAEC,OAAO,EAAEb,QAAQ,EAAmB,iBACnEG,cAAA,CAACC,iBAAAA,CAAOU,IAAI,EAAA;QACVC,IAAAA,EAAMH,SAAAA;QACNI,YAAAA,EAAc,CAACC,WAAAA,GAAgB,CAACA,WAAAA,IAAeJ,OAAAA,IAAAA;QAC/CK,KAAAA,EAAO,KAAA;gCAEPf,cAAA,CAACC,kBAAOe,MAAM,EAAA;AAAEnB,YAAAA,QAAAA,EAAAA;;;AAepB,MAAMoB,uBAAAA,GAA0B,CAAC,EAC/BpB,QAAQ,EACRqB,iBAAAA,GAAoB,IAAI,EACK,iBAC7BlB,cAAA,CAACjB,kBAAAA,EAAAA;QAAmBC,UAAAA,EAAYkC,iBAAAA;AAAmBC,QAAAA,gBAAAA,EAAgB,CAACD,iBAAAA;AAClE,QAAA,QAAA,gBAAAlB,cAAA,CAACoB,uBAAAA,EAAAA;AAAYvB,YAAAA,QAAAA,EAAAA;;;AAajB,MAAMwB,iBAAAA,GAAoB,CAAC,EAAEX,OAAO,EAAEY,KAAK,EAAEzB,QAAQ,EAA0B,GAAA;IAC7E,MAAM,EAAE0B,aAAa,EAAE,GAAGC,iBAAAA,EAAAA;IAC1B,MAAMC,YAAAA,GAAeH,SAASC,aAAAA,CAAc;QAAEG,EAAAA,EAAI,cAAA;QAAgBC,cAAAA,EAAgB;AAAQ,KAAA,CAAA;AAC1F,IAAA,qBACE3B,cAAA,CAACf,eAAAA,EAAAA;QAAgB2C,OAAAA,EAASlB,OAAAA;QAASY,KAAAA,EAAOG,YAAAA;QAAcI,OAAAA,EAAQ,OAAA;AAC7DhC,QAAAA,QAAAA,EAAAA,QAAAA,kBAAYG,cAAA,CAAC8B,WAAAA,EAAAA,EAAAA;;AAGpB,CAAA;AAEA,MAAMC,MAAAA,GAAS;IACbpB,IAAAA,EAAMH,UAAAA;IACNwB,IAAAA,EAAM1C,UAAAA;IACN2C,iBAAAA,EAAmBhB,uBAAAA;IACnBiB,WAAAA,EAAab,iBAAAA;AACbc,IAAAA,KAAAA,EAAOlC,kBAAOkC,KAAK;AACnBC,IAAAA,WAAAA,EAAanC,kBAAOmC;AACtB;;;;;"}
1
+ {"version":3,"file":"Drawer.js","sources":["../../../admin/src/components/Drawer.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport * as Dialog from '@radix-ui/react-dialog';\nimport { Box, ScrollArea, IconButton, Flex, FlexProps } from '@strapi/design-system';\nimport { Cross } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { keyframes, styled } from 'styled-components';\n\n/** Duration of the close animation in ms. Use for timing cleanup (e.g. removing URL params). */\nexport const DRAWER_CLOSE_ANIMATION_MS = 300;\n\n/* -------------------------------------------------------------------------------------------------\n * Animations\n * -----------------------------------------------------------------------------------------------*/\n\n// Direction: up\nconst slideUpFromBottomIn = keyframes`\n from {\n opacity: 0;\n transform: translateY(100%);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n`;\n\nconst slideUpFromBottomOut = keyframes`\n from {\n opacity: 1;\n transform: translateY(0);\n }\n to {\n opacity: 0;\n transform: translateY(100%);\n }\n`;\n\n// Direction: left\nconst slideLeftFromRightIn = keyframes`\n from {\n opacity: 0;\n transform: translateX(100%);\n }\n to {\n opacity: 1;\n transform: translateX(0);\n }\n`;\n\nconst slideLeftFromRightOut = keyframes`\n from {\n opacity: 1;\n transform: translateX(0);\n }\n to {\n opacity: 0;\n transform: translateX(100%);\n }\n`;\n\n/* -------------------------------------------------------------------------------------------------\n * Styled components\n * -----------------------------------------------------------------------------------------------*/\n\ninterface DrawerContainerProps {\n $animationDirection?: DrawerBodyProps['animationDirection'];\n $width?: string;\n $maxHeight?: string;\n}\n\nconst DrawerContainer = styled(Flex)<DrawerContainerProps>`\n flex-direction: column;\n position: fixed;\n bottom: 0;\n /* Mobile: full-bleed sheet. Anchoring to the right at a fixed width left a\n dead gutter on every phone wider than the panel, and the panel only filled\n the screen once the viewport happened to be narrower than it. Below the\n medium breakpoint the panel is the screen; medium+ restores the\n right-anchored rail. */\n left: 0;\n right: 0;\n width: 100%;\n padding: ${({ theme }) => theme.spaces[2]};\n max-width: 100%;\n /* On the overlay token, so ties resolve by DOM order, as they do between DS\n modals: a drawer opened from inside a modal (e.g. a media field in the\n content manager's relation modal) portals in after it and sits above its\n overlay, while a dialog opened from inside the drawer (e.g. the asset\n details \"delete\" confirm) portals in after the drawer and covers it.\n Not the modal token (310): a DS dialog's content stacks inside its own\n overlay at 300, so the drawer would cover its own confirmations.\n Popovers (500) and tooltips (1000) still surface above the panel. */\n z-index: ${({ theme }) => theme.zIndices.overlay};\n overflow: hidden;\n /* dvh, not vh: anchored to the bottom, a 100vh cap on mobile (visual\n viewport < 100vh under the URL bar) would push the drawer top off-screen. */\n max-height: ${({ $maxHeight }) => $maxHeight ?? '100dvh'};\n\n ${({ theme }) => theme.breakpoints.medium} {\n left: auto;\n width: ${({ $width }) => $width ?? '400px'};\n }\n\n &:focus {\n outline: none;\n }\n\n @media (prefers-reduced-motion: no-preference) {\n &[data-state='open'] {\n animation: ${({ $animationDirection }) =>\n $animationDirection === 'up' ? slideUpFromBottomIn : slideLeftFromRightIn}\n ${DRAWER_CLOSE_ANIMATION_MS}ms cubic-bezier(0.32, 0.72, 0, 1) forwards;\n }\n\n &[data-state='closed'] {\n animation: ${({ $animationDirection }) =>\n $animationDirection === 'up' ? slideUpFromBottomOut : slideLeftFromRightOut}\n ${DRAWER_CLOSE_ANIMATION_MS}ms cubic-bezier(0.32, 0.72, 0, 1) forwards;\n pointer-events: none;\n }\n }\n`;\n\nconst DrawerContent = styled(Box)`\n display: flex;\n flex-direction: column;\n flex: 1;\n min-height: 0;\n width: 100%;\n background-color: ${({ theme }) => theme.colors.neutral0};\n border-radius: ${({ theme }) => theme.borderRadius};\n box-shadow: ${({ theme }) => theme.shadows.popupShadow};\n overflow: hidden;\n border: 1px solid ${({ theme }) => theme.colors.neutral150};\n`;\n\ninterface CollapsibleContentProps {\n $isVisible: boolean;\n}\n\nconst CollapsibleContent = styled(Box)<CollapsibleContentProps>`\n display: grid;\n flex: 1;\n min-height: 0;\n grid-template-rows: ${({ $isVisible }) => ($isVisible ? '1fr' : '0fr')};\n transition: grid-template-rows 0.3s ease-in-out;\n\n > div {\n overflow: hidden;\n min-height: 0;\n }\n\n /* The scroll area wraps its children in a div styled inline as\n display: table, min-width: 100%. A table box grows to its content, so a\n single unbreakable string (a long file name) widens it past the scrollport\n and the panel gains a horizontal scrollbar — with nothing left to clamp the\n row, the name never truncates. Overriding to a block box makes the\n scrollport the width again, which is what the vertical-only list wants.\n Inline styles, hence the important. */\n [data-radix-scroll-area-viewport] > div {\n display: block !important;\n }\n`;\n\nconst CloseIconButton = styled(IconButton)`\n &:hover {\n background: transparent;\n }\n`;\n\n/* -------------------------------------------------------------------------------------------------\n * Drawer.Body\n * -----------------------------------------------------------------------------------------------*/\n\ntype PointerDownOutsideHandler = NonNullable<Dialog.DialogContentProps['onPointerDownOutside']>;\ntype AutoFocusHandler = NonNullable<Dialog.DialogContentProps['onOpenAutoFocus']>;\n\n/** Vetoes an outside interaction that would otherwise dismiss the drawer. */\nconst keepOpen = (event: { preventDefault: () => void }) => event.preventDefault();\n\ninterface DrawerBodyProps extends Omit<FlexProps, 'width' | 'maxHeight'> {\n animationDirection?: 'up' | 'left';\n /** Width of the panel from the medium breakpoint up. Mobile is always full-bleed. */\n width?: string;\n /** Cap on the panel height. Defaults to the dynamic viewport height. */\n maxHeight?: string;\n /**\n * Opt in to dismiss-on-outside-click. Omitted, an outside pointer press never\n * closes the drawer — it must be closed explicitly, as long-running panels\n * like the upload progress dialog need. Passed, the handler can still veto\n * individual presses via `event.preventDefault()`. Content rendered from\n * inside the panel but portaled elsewhere (DS dialogs, popovers, tooltips)\n * never counts as outside — Radix reads the React tree, not the DOM.\n */\n onPointerDownOutside?: PointerDownOutsideHandler;\n /** Radix's hook as focus moves into the panel on open. */\n onOpenAutoFocus?: AutoFocusHandler;\n /**\n * Radix's hook as the panel gives focus back on close. Opened from state\n * rather than a `Dialog.Trigger`, it has nowhere to send it unless this does.\n */\n onCloseAutoFocus?: AutoFocusHandler;\n children: React.ReactNode;\n}\n\nconst DrawerBody = React.forwardRef<HTMLDivElement, DrawerBodyProps>(\n (\n {\n animationDirection,\n width,\n maxHeight,\n onPointerDownOutside = keepOpen,\n onOpenAutoFocus,\n onCloseAutoFocus,\n children,\n ...props\n },\n ref\n ) => (\n <Dialog.Content\n ref={ref}\n forceMount\n asChild\n onPointerDownOutside={onPointerDownOutside}\n onOpenAutoFocus={onOpenAutoFocus}\n onCloseAutoFocus={onCloseAutoFocus}\n // Non-modal, so focus legitimately moves outside (tabbing past the last\n // field); only a pointer press should ever dismiss.\n onFocusOutside={keepOpen}\n data-animation-direction={animationDirection}\n >\n {/* width/maxHeight go through transient props so the DS Flex never emits a\n competing `width` rule that would outrank the mobile full-bleed one. */}\n <DrawerContainer\n $animationDirection={animationDirection}\n $width={width}\n $maxHeight={maxHeight}\n {...props}\n >\n <DrawerContent>{children}</DrawerContent>\n </DrawerContainer>\n </Dialog.Content>\n )\n);\nDrawerBody.displayName = 'DrawerBody';\n\n/* -------------------------------------------------------------------------------------------------\n * Drawer.Root\n * -----------------------------------------------------------------------------------------------*/\n\ninterface DrawerRootProps {\n isVisible?: boolean;\n onClose?: () => void;\n children: React.ReactNode;\n}\n\nconst DrawerRoot = ({ isVisible, onClose, children }: DrawerRootProps): React.ReactElement => (\n <Dialog.Root\n open={isVisible}\n onOpenChange={(nextVisible) => !nextVisible && onClose?.()}\n modal={false}\n >\n <Dialog.Portal>{children}</Dialog.Portal>\n </Dialog.Root>\n);\n\n/* -------------------------------------------------------------------------------------------------\n * Drawer.Content - composable content slot (collapsible when isContentExpanded is used)\n * Contains a scrollable area\n * -----------------------------------------------------------------------------------------------*/\n\ninterface DrawerScrollableContentProps {\n children: React.ReactNode;\n /** When provided, content can collapse/expand (e.g. for minimize). Omit to always show. */\n isContentExpanded?: boolean;\n}\n\nconst DrawerScrollableContent = ({\n children,\n isContentExpanded = true,\n}: DrawerScrollableContentProps) => (\n <CollapsibleContent $isVisible={isContentExpanded} data-collapsed={!isContentExpanded}>\n <ScrollArea>{children}</ScrollArea>\n </CollapsibleContent>\n);\n\n/* -------------------------------------------------------------------------------------------------\n * Drawer.CloseButton - composable close icon button (Cross icon by default)\n * -----------------------------------------------------------------------------------------------*/\n\ninterface DrawerCloseButtonProps extends React.PropsWithChildren {\n onClose: () => void;\n label?: string;\n}\n\nconst DrawerCloseButton = ({ onClose, label, children }: DrawerCloseButtonProps) => {\n const { formatMessage } = useIntl();\n const labelMessage = label ?? formatMessage({ id: 'global.close', defaultMessage: 'Close' });\n return (\n <CloseIconButton onClick={onClose} label={labelMessage} variant=\"ghost\">\n {children ?? <Cross />}\n </CloseIconButton>\n );\n};\n\nconst Drawer = {\n Root: DrawerRoot,\n Body: DrawerBody,\n ScrollableContent: DrawerScrollableContent,\n CloseButton: DrawerCloseButton,\n Title: Dialog.Title,\n Description: Dialog.Description,\n};\n\nexport { Drawer };\n"],"names":["DRAWER_CLOSE_ANIMATION_MS","slideUpFromBottomIn","keyframes","slideUpFromBottomOut","slideLeftFromRightIn","slideLeftFromRightOut","DrawerContainer","styled","Flex","theme","spaces","zIndices","overlay","$maxHeight","breakpoints","medium","$width","$animationDirection","DrawerContent","Box","colors","neutral0","borderRadius","shadows","popupShadow","neutral150","CollapsibleContent","$isVisible","CloseIconButton","IconButton","keepOpen","event","preventDefault","DrawerBody","React","forwardRef","animationDirection","width","maxHeight","onPointerDownOutside","onOpenAutoFocus","onCloseAutoFocus","children","props","ref","_jsx","Dialog","Content","forceMount","asChild","onFocusOutside","data-animation-direction","displayName","DrawerRoot","isVisible","onClose","Root","open","onOpenChange","nextVisible","modal","Portal","DrawerScrollableContent","isContentExpanded","data-collapsed","ScrollArea","DrawerCloseButton","label","formatMessage","useIntl","labelMessage","id","defaultMessage","onClick","variant","Cross","Drawer","Body","ScrollableContent","CloseButton","Title","Description"],"mappings":"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"}
@@ -65,12 +65,15 @@ const DrawerContainer = styled(Flex)`
65
65
  width: 100%;
66
66
  padding: ${({ theme })=>theme.spaces[2]};
67
67
  max-width: 100%;
68
- /* Sit just below the overlay token (300) so that:
69
- - popovers (500) and tooltips (1000) rendered from descendant components
70
- surface above the drawer panel,
71
- - AlertDialog overlays (300) and contents (310) opened from inside the
72
- drawer (e.g. the asset details "delete" confirm) cover the drawer. */
73
- z-index: 200;
68
+ /* On the overlay token, so ties resolve by DOM order, as they do between DS
69
+ modals: a drawer opened from inside a modal (e.g. a media field in the
70
+ content manager's relation modal) portals in after it and sits above its
71
+ overlay, while a dialog opened from inside the drawer (e.g. the asset
72
+ details "delete" confirm) portals in after the drawer and covers it.
73
+ Not the modal token (310): a DS dialog's content stacks inside its own
74
+ overlay at 300, so the drawer would cover its own confirmations.
75
+ Popovers (500) and tooltips (1000) still surface above the panel. */
76
+ z-index: ${({ theme })=>theme.zIndices.overlay};
74
77
  overflow: hidden;
75
78
  /* dvh, not vh: anchored to the bottom, a 100vh cap on mobile (visual
76
79
  viewport < 100vh under the URL bar) would push the drawer top off-screen. */
@@ -139,11 +142,13 @@ const CloseIconButton = styled(IconButton)`
139
142
  }
140
143
  `;
141
144
  /** Vetoes an outside interaction that would otherwise dismiss the drawer. */ const keepOpen = (event)=>event.preventDefault();
142
- const DrawerBody = /*#__PURE__*/ React.forwardRef(({ animationDirection, width, maxHeight, onPointerDownOutside = keepOpen, children, ...props }, ref)=>/*#__PURE__*/ jsx(Dialog.Content, {
145
+ const DrawerBody = /*#__PURE__*/ React.forwardRef(({ animationDirection, width, maxHeight, onPointerDownOutside = keepOpen, onOpenAutoFocus, onCloseAutoFocus, children, ...props }, ref)=>/*#__PURE__*/ jsx(Dialog.Content, {
143
146
  ref: ref,
144
147
  forceMount: true,
145
148
  asChild: true,
146
149
  onPointerDownOutside: onPointerDownOutside,
150
+ onOpenAutoFocus: onOpenAutoFocus,
151
+ onCloseAutoFocus: onCloseAutoFocus,
147
152
  // Non-modal, so focus legitimately moves outside (tabbing past the last
148
153
  // field); only a pointer press should ever dismiss.
149
154
  onFocusOutside: keepOpen,
@@ -1 +1 @@
1
- {"version":3,"file":"Drawer.mjs","sources":["../../../admin/src/components/Drawer.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport * as Dialog from '@radix-ui/react-dialog';\nimport { Box, ScrollArea, IconButton, Flex, FlexProps } from '@strapi/design-system';\nimport { Cross } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { keyframes, styled } from 'styled-components';\n\n/** Duration of the close animation in ms. Use for timing cleanup (e.g. removing URL params). */\nexport const DRAWER_CLOSE_ANIMATION_MS = 300;\n\n/* -------------------------------------------------------------------------------------------------\n * Animations\n * -----------------------------------------------------------------------------------------------*/\n\n// Direction: up\nconst slideUpFromBottomIn = keyframes`\n from {\n opacity: 0;\n transform: translateY(100%);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n`;\n\nconst slideUpFromBottomOut = keyframes`\n from {\n opacity: 1;\n transform: translateY(0);\n }\n to {\n opacity: 0;\n transform: translateY(100%);\n }\n`;\n\n// Direction: left\nconst slideLeftFromRightIn = keyframes`\n from {\n opacity: 0;\n transform: translateX(100%);\n }\n to {\n opacity: 1;\n transform: translateX(0);\n }\n`;\n\nconst slideLeftFromRightOut = keyframes`\n from {\n opacity: 1;\n transform: translateX(0);\n }\n to {\n opacity: 0;\n transform: translateX(100%);\n }\n`;\n\n/* -------------------------------------------------------------------------------------------------\n * Styled components\n * -----------------------------------------------------------------------------------------------*/\n\ninterface DrawerContainerProps {\n $animationDirection?: DrawerBodyProps['animationDirection'];\n $width?: string;\n $maxHeight?: string;\n}\n\nconst DrawerContainer = styled(Flex)<DrawerContainerProps>`\n flex-direction: column;\n position: fixed;\n bottom: 0;\n /* Mobile: full-bleed sheet. Anchoring to the right at a fixed width left a\n dead gutter on every phone wider than the panel, and the panel only filled\n the screen once the viewport happened to be narrower than it. Below the\n medium breakpoint the panel is the screen; medium+ restores the\n right-anchored rail. */\n left: 0;\n right: 0;\n width: 100%;\n padding: ${({ theme }) => theme.spaces[2]};\n max-width: 100%;\n /* Sit just below the overlay token (300) so that:\n - popovers (500) and tooltips (1000) rendered from descendant components\n surface above the drawer panel,\n - AlertDialog overlays (300) and contents (310) opened from inside the\n drawer (e.g. the asset details \"delete\" confirm) cover the drawer. */\n z-index: 200;\n overflow: hidden;\n /* dvh, not vh: anchored to the bottom, a 100vh cap on mobile (visual\n viewport < 100vh under the URL bar) would push the drawer top off-screen. */\n max-height: ${({ $maxHeight }) => $maxHeight ?? '100dvh'};\n\n ${({ theme }) => theme.breakpoints.medium} {\n left: auto;\n width: ${({ $width }) => $width ?? '400px'};\n }\n\n &:focus {\n outline: none;\n }\n\n @media (prefers-reduced-motion: no-preference) {\n &[data-state='open'] {\n animation: ${({ $animationDirection }) =>\n $animationDirection === 'up' ? slideUpFromBottomIn : slideLeftFromRightIn}\n ${DRAWER_CLOSE_ANIMATION_MS}ms cubic-bezier(0.32, 0.72, 0, 1) forwards;\n }\n\n &[data-state='closed'] {\n animation: ${({ $animationDirection }) =>\n $animationDirection === 'up' ? slideUpFromBottomOut : slideLeftFromRightOut}\n ${DRAWER_CLOSE_ANIMATION_MS}ms cubic-bezier(0.32, 0.72, 0, 1) forwards;\n pointer-events: none;\n }\n }\n`;\n\nconst DrawerContent = styled(Box)`\n display: flex;\n flex-direction: column;\n flex: 1;\n min-height: 0;\n width: 100%;\n background-color: ${({ theme }) => theme.colors.neutral0};\n border-radius: ${({ theme }) => theme.borderRadius};\n box-shadow: ${({ theme }) => theme.shadows.popupShadow};\n overflow: hidden;\n border: 1px solid ${({ theme }) => theme.colors.neutral150};\n`;\n\ninterface CollapsibleContentProps {\n $isVisible: boolean;\n}\n\nconst CollapsibleContent = styled(Box)<CollapsibleContentProps>`\n display: grid;\n flex: 1;\n min-height: 0;\n grid-template-rows: ${({ $isVisible }) => ($isVisible ? '1fr' : '0fr')};\n transition: grid-template-rows 0.3s ease-in-out;\n\n > div {\n overflow: hidden;\n min-height: 0;\n }\n\n /* The scroll area wraps its children in a div styled inline as\n display: table, min-width: 100%. A table box grows to its content, so a\n single unbreakable string (a long file name) widens it past the scrollport\n and the panel gains a horizontal scrollbar — with nothing left to clamp the\n row, the name never truncates. Overriding to a block box makes the\n scrollport the width again, which is what the vertical-only list wants.\n Inline styles, hence the important. */\n [data-radix-scroll-area-viewport] > div {\n display: block !important;\n }\n`;\n\nconst CloseIconButton = styled(IconButton)`\n &:hover {\n background: transparent;\n }\n`;\n\n/* -------------------------------------------------------------------------------------------------\n * Drawer.Body\n * -----------------------------------------------------------------------------------------------*/\n\ntype PointerDownOutsideHandler = NonNullable<Dialog.DialogContentProps['onPointerDownOutside']>;\n\n/** Vetoes an outside interaction that would otherwise dismiss the drawer. */\nconst keepOpen = (event: { preventDefault: () => void }) => event.preventDefault();\n\ninterface DrawerBodyProps extends Omit<FlexProps, 'width' | 'maxHeight'> {\n animationDirection?: 'up' | 'left';\n /** Width of the panel from the medium breakpoint up. Mobile is always full-bleed. */\n width?: string;\n /** Cap on the panel height. Defaults to the dynamic viewport height. */\n maxHeight?: string;\n /**\n * Opt in to dismiss-on-outside-click. Omitted, an outside pointer press never\n * closes the drawer — it must be closed explicitly, as long-running panels\n * like the upload progress dialog need. Passed, the handler can still veto\n * individual presses via `event.preventDefault()`. Content rendered from\n * inside the panel but portaled elsewhere (DS dialogs, popovers, tooltips)\n * never counts as outside — Radix reads the React tree, not the DOM.\n */\n onPointerDownOutside?: PointerDownOutsideHandler;\n children: React.ReactNode;\n}\n\nconst DrawerBody = React.forwardRef<HTMLDivElement, DrawerBodyProps>(\n (\n { animationDirection, width, maxHeight, onPointerDownOutside = keepOpen, children, ...props },\n ref\n ) => (\n <Dialog.Content\n ref={ref}\n forceMount\n asChild\n onPointerDownOutside={onPointerDownOutside}\n // Non-modal, so focus legitimately moves outside (tabbing past the last\n // field); only a pointer press should ever dismiss.\n onFocusOutside={keepOpen}\n data-animation-direction={animationDirection}\n >\n {/* width/maxHeight go through transient props so the DS Flex never emits a\n competing `width` rule that would outrank the mobile full-bleed one. */}\n <DrawerContainer\n $animationDirection={animationDirection}\n $width={width}\n $maxHeight={maxHeight}\n {...props}\n >\n <DrawerContent>{children}</DrawerContent>\n </DrawerContainer>\n </Dialog.Content>\n )\n);\nDrawerBody.displayName = 'DrawerBody';\n\n/* -------------------------------------------------------------------------------------------------\n * Drawer.Root\n * -----------------------------------------------------------------------------------------------*/\n\ninterface DrawerRootProps {\n isVisible?: boolean;\n onClose?: () => void;\n children: React.ReactNode;\n}\n\nconst DrawerRoot = ({ isVisible, onClose, children }: DrawerRootProps): React.ReactElement => (\n <Dialog.Root\n open={isVisible}\n onOpenChange={(nextVisible) => !nextVisible && onClose?.()}\n modal={false}\n >\n <Dialog.Portal>{children}</Dialog.Portal>\n </Dialog.Root>\n);\n\n/* -------------------------------------------------------------------------------------------------\n * Drawer.Content - composable content slot (collapsible when isContentExpanded is used)\n * Contains a scrollable area\n * -----------------------------------------------------------------------------------------------*/\n\ninterface DrawerScrollableContentProps {\n children: React.ReactNode;\n /** When provided, content can collapse/expand (e.g. for minimize). Omit to always show. */\n isContentExpanded?: boolean;\n}\n\nconst DrawerScrollableContent = ({\n children,\n isContentExpanded = true,\n}: DrawerScrollableContentProps) => (\n <CollapsibleContent $isVisible={isContentExpanded} data-collapsed={!isContentExpanded}>\n <ScrollArea>{children}</ScrollArea>\n </CollapsibleContent>\n);\n\n/* -------------------------------------------------------------------------------------------------\n * Drawer.CloseButton - composable close icon button (Cross icon by default)\n * -----------------------------------------------------------------------------------------------*/\n\ninterface DrawerCloseButtonProps extends React.PropsWithChildren {\n onClose: () => void;\n label?: string;\n}\n\nconst DrawerCloseButton = ({ onClose, label, children }: DrawerCloseButtonProps) => {\n const { formatMessage } = useIntl();\n const labelMessage = label ?? formatMessage({ id: 'global.close', defaultMessage: 'Close' });\n return (\n <CloseIconButton onClick={onClose} label={labelMessage} variant=\"ghost\">\n {children ?? <Cross />}\n </CloseIconButton>\n );\n};\n\nconst Drawer = {\n Root: DrawerRoot,\n Body: DrawerBody,\n ScrollableContent: DrawerScrollableContent,\n CloseButton: DrawerCloseButton,\n Title: Dialog.Title,\n Description: Dialog.Description,\n};\n\nexport { Drawer };\n"],"names":["DRAWER_CLOSE_ANIMATION_MS","slideUpFromBottomIn","keyframes","slideUpFromBottomOut","slideLeftFromRightIn","slideLeftFromRightOut","DrawerContainer","styled","Flex","theme","spaces","$maxHeight","breakpoints","medium","$width","$animationDirection","DrawerContent","Box","colors","neutral0","borderRadius","shadows","popupShadow","neutral150","CollapsibleContent","$isVisible","CloseIconButton","IconButton","keepOpen","event","preventDefault","DrawerBody","React","forwardRef","animationDirection","width","maxHeight","onPointerDownOutside","children","props","ref","_jsx","Dialog","Content","forceMount","asChild","onFocusOutside","data-animation-direction","displayName","DrawerRoot","isVisible","onClose","Root","open","onOpenChange","nextVisible","modal","Portal","DrawerScrollableContent","isContentExpanded","data-collapsed","ScrollArea","DrawerCloseButton","label","formatMessage","useIntl","labelMessage","id","defaultMessage","onClick","variant","Cross","Drawer","Body","ScrollableContent","CloseButton","Title","Description"],"mappings":"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"}
1
+ {"version":3,"file":"Drawer.mjs","sources":["../../../admin/src/components/Drawer.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport * as Dialog from '@radix-ui/react-dialog';\nimport { Box, ScrollArea, IconButton, Flex, FlexProps } from '@strapi/design-system';\nimport { Cross } from '@strapi/icons';\nimport { useIntl } from 'react-intl';\nimport { keyframes, styled } from 'styled-components';\n\n/** Duration of the close animation in ms. Use for timing cleanup (e.g. removing URL params). */\nexport const DRAWER_CLOSE_ANIMATION_MS = 300;\n\n/* -------------------------------------------------------------------------------------------------\n * Animations\n * -----------------------------------------------------------------------------------------------*/\n\n// Direction: up\nconst slideUpFromBottomIn = keyframes`\n from {\n opacity: 0;\n transform: translateY(100%);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n`;\n\nconst slideUpFromBottomOut = keyframes`\n from {\n opacity: 1;\n transform: translateY(0);\n }\n to {\n opacity: 0;\n transform: translateY(100%);\n }\n`;\n\n// Direction: left\nconst slideLeftFromRightIn = keyframes`\n from {\n opacity: 0;\n transform: translateX(100%);\n }\n to {\n opacity: 1;\n transform: translateX(0);\n }\n`;\n\nconst slideLeftFromRightOut = keyframes`\n from {\n opacity: 1;\n transform: translateX(0);\n }\n to {\n opacity: 0;\n transform: translateX(100%);\n }\n`;\n\n/* -------------------------------------------------------------------------------------------------\n * Styled components\n * -----------------------------------------------------------------------------------------------*/\n\ninterface DrawerContainerProps {\n $animationDirection?: DrawerBodyProps['animationDirection'];\n $width?: string;\n $maxHeight?: string;\n}\n\nconst DrawerContainer = styled(Flex)<DrawerContainerProps>`\n flex-direction: column;\n position: fixed;\n bottom: 0;\n /* Mobile: full-bleed sheet. Anchoring to the right at a fixed width left a\n dead gutter on every phone wider than the panel, and the panel only filled\n the screen once the viewport happened to be narrower than it. Below the\n medium breakpoint the panel is the screen; medium+ restores the\n right-anchored rail. */\n left: 0;\n right: 0;\n width: 100%;\n padding: ${({ theme }) => theme.spaces[2]};\n max-width: 100%;\n /* On the overlay token, so ties resolve by DOM order, as they do between DS\n modals: a drawer opened from inside a modal (e.g. a media field in the\n content manager's relation modal) portals in after it and sits above its\n overlay, while a dialog opened from inside the drawer (e.g. the asset\n details \"delete\" confirm) portals in after the drawer and covers it.\n Not the modal token (310): a DS dialog's content stacks inside its own\n overlay at 300, so the drawer would cover its own confirmations.\n Popovers (500) and tooltips (1000) still surface above the panel. */\n z-index: ${({ theme }) => theme.zIndices.overlay};\n overflow: hidden;\n /* dvh, not vh: anchored to the bottom, a 100vh cap on mobile (visual\n viewport < 100vh under the URL bar) would push the drawer top off-screen. */\n max-height: ${({ $maxHeight }) => $maxHeight ?? '100dvh'};\n\n ${({ theme }) => theme.breakpoints.medium} {\n left: auto;\n width: ${({ $width }) => $width ?? '400px'};\n }\n\n &:focus {\n outline: none;\n }\n\n @media (prefers-reduced-motion: no-preference) {\n &[data-state='open'] {\n animation: ${({ $animationDirection }) =>\n $animationDirection === 'up' ? slideUpFromBottomIn : slideLeftFromRightIn}\n ${DRAWER_CLOSE_ANIMATION_MS}ms cubic-bezier(0.32, 0.72, 0, 1) forwards;\n }\n\n &[data-state='closed'] {\n animation: ${({ $animationDirection }) =>\n $animationDirection === 'up' ? slideUpFromBottomOut : slideLeftFromRightOut}\n ${DRAWER_CLOSE_ANIMATION_MS}ms cubic-bezier(0.32, 0.72, 0, 1) forwards;\n pointer-events: none;\n }\n }\n`;\n\nconst DrawerContent = styled(Box)`\n display: flex;\n flex-direction: column;\n flex: 1;\n min-height: 0;\n width: 100%;\n background-color: ${({ theme }) => theme.colors.neutral0};\n border-radius: ${({ theme }) => theme.borderRadius};\n box-shadow: ${({ theme }) => theme.shadows.popupShadow};\n overflow: hidden;\n border: 1px solid ${({ theme }) => theme.colors.neutral150};\n`;\n\ninterface CollapsibleContentProps {\n $isVisible: boolean;\n}\n\nconst CollapsibleContent = styled(Box)<CollapsibleContentProps>`\n display: grid;\n flex: 1;\n min-height: 0;\n grid-template-rows: ${({ $isVisible }) => ($isVisible ? '1fr' : '0fr')};\n transition: grid-template-rows 0.3s ease-in-out;\n\n > div {\n overflow: hidden;\n min-height: 0;\n }\n\n /* The scroll area wraps its children in a div styled inline as\n display: table, min-width: 100%. A table box grows to its content, so a\n single unbreakable string (a long file name) widens it past the scrollport\n and the panel gains a horizontal scrollbar — with nothing left to clamp the\n row, the name never truncates. Overriding to a block box makes the\n scrollport the width again, which is what the vertical-only list wants.\n Inline styles, hence the important. */\n [data-radix-scroll-area-viewport] > div {\n display: block !important;\n }\n`;\n\nconst CloseIconButton = styled(IconButton)`\n &:hover {\n background: transparent;\n }\n`;\n\n/* -------------------------------------------------------------------------------------------------\n * Drawer.Body\n * -----------------------------------------------------------------------------------------------*/\n\ntype PointerDownOutsideHandler = NonNullable<Dialog.DialogContentProps['onPointerDownOutside']>;\ntype AutoFocusHandler = NonNullable<Dialog.DialogContentProps['onOpenAutoFocus']>;\n\n/** Vetoes an outside interaction that would otherwise dismiss the drawer. */\nconst keepOpen = (event: { preventDefault: () => void }) => event.preventDefault();\n\ninterface DrawerBodyProps extends Omit<FlexProps, 'width' | 'maxHeight'> {\n animationDirection?: 'up' | 'left';\n /** Width of the panel from the medium breakpoint up. Mobile is always full-bleed. */\n width?: string;\n /** Cap on the panel height. Defaults to the dynamic viewport height. */\n maxHeight?: string;\n /**\n * Opt in to dismiss-on-outside-click. Omitted, an outside pointer press never\n * closes the drawer — it must be closed explicitly, as long-running panels\n * like the upload progress dialog need. Passed, the handler can still veto\n * individual presses via `event.preventDefault()`. Content rendered from\n * inside the panel but portaled elsewhere (DS dialogs, popovers, tooltips)\n * never counts as outside — Radix reads the React tree, not the DOM.\n */\n onPointerDownOutside?: PointerDownOutsideHandler;\n /** Radix's hook as focus moves into the panel on open. */\n onOpenAutoFocus?: AutoFocusHandler;\n /**\n * Radix's hook as the panel gives focus back on close. Opened from state\n * rather than a `Dialog.Trigger`, it has nowhere to send it unless this does.\n */\n onCloseAutoFocus?: AutoFocusHandler;\n children: React.ReactNode;\n}\n\nconst DrawerBody = React.forwardRef<HTMLDivElement, DrawerBodyProps>(\n (\n {\n animationDirection,\n width,\n maxHeight,\n onPointerDownOutside = keepOpen,\n onOpenAutoFocus,\n onCloseAutoFocus,\n children,\n ...props\n },\n ref\n ) => (\n <Dialog.Content\n ref={ref}\n forceMount\n asChild\n onPointerDownOutside={onPointerDownOutside}\n onOpenAutoFocus={onOpenAutoFocus}\n onCloseAutoFocus={onCloseAutoFocus}\n // Non-modal, so focus legitimately moves outside (tabbing past the last\n // field); only a pointer press should ever dismiss.\n onFocusOutside={keepOpen}\n data-animation-direction={animationDirection}\n >\n {/* width/maxHeight go through transient props so the DS Flex never emits a\n competing `width` rule that would outrank the mobile full-bleed one. */}\n <DrawerContainer\n $animationDirection={animationDirection}\n $width={width}\n $maxHeight={maxHeight}\n {...props}\n >\n <DrawerContent>{children}</DrawerContent>\n </DrawerContainer>\n </Dialog.Content>\n )\n);\nDrawerBody.displayName = 'DrawerBody';\n\n/* -------------------------------------------------------------------------------------------------\n * Drawer.Root\n * -----------------------------------------------------------------------------------------------*/\n\ninterface DrawerRootProps {\n isVisible?: boolean;\n onClose?: () => void;\n children: React.ReactNode;\n}\n\nconst DrawerRoot = ({ isVisible, onClose, children }: DrawerRootProps): React.ReactElement => (\n <Dialog.Root\n open={isVisible}\n onOpenChange={(nextVisible) => !nextVisible && onClose?.()}\n modal={false}\n >\n <Dialog.Portal>{children}</Dialog.Portal>\n </Dialog.Root>\n);\n\n/* -------------------------------------------------------------------------------------------------\n * Drawer.Content - composable content slot (collapsible when isContentExpanded is used)\n * Contains a scrollable area\n * -----------------------------------------------------------------------------------------------*/\n\ninterface DrawerScrollableContentProps {\n children: React.ReactNode;\n /** When provided, content can collapse/expand (e.g. for minimize). Omit to always show. */\n isContentExpanded?: boolean;\n}\n\nconst DrawerScrollableContent = ({\n children,\n isContentExpanded = true,\n}: DrawerScrollableContentProps) => (\n <CollapsibleContent $isVisible={isContentExpanded} data-collapsed={!isContentExpanded}>\n <ScrollArea>{children}</ScrollArea>\n </CollapsibleContent>\n);\n\n/* -------------------------------------------------------------------------------------------------\n * Drawer.CloseButton - composable close icon button (Cross icon by default)\n * -----------------------------------------------------------------------------------------------*/\n\ninterface DrawerCloseButtonProps extends React.PropsWithChildren {\n onClose: () => void;\n label?: string;\n}\n\nconst DrawerCloseButton = ({ onClose, label, children }: DrawerCloseButtonProps) => {\n const { formatMessage } = useIntl();\n const labelMessage = label ?? formatMessage({ id: 'global.close', defaultMessage: 'Close' });\n return (\n <CloseIconButton onClick={onClose} label={labelMessage} variant=\"ghost\">\n {children ?? <Cross />}\n </CloseIconButton>\n );\n};\n\nconst Drawer = {\n Root: DrawerRoot,\n Body: DrawerBody,\n ScrollableContent: DrawerScrollableContent,\n CloseButton: DrawerCloseButton,\n Title: Dialog.Title,\n Description: Dialog.Description,\n};\n\nexport { Drawer };\n"],"names":["DRAWER_CLOSE_ANIMATION_MS","slideUpFromBottomIn","keyframes","slideUpFromBottomOut","slideLeftFromRightIn","slideLeftFromRightOut","DrawerContainer","styled","Flex","theme","spaces","zIndices","overlay","$maxHeight","breakpoints","medium","$width","$animationDirection","DrawerContent","Box","colors","neutral0","borderRadius","shadows","popupShadow","neutral150","CollapsibleContent","$isVisible","CloseIconButton","IconButton","keepOpen","event","preventDefault","DrawerBody","React","forwardRef","animationDirection","width","maxHeight","onPointerDownOutside","onOpenAutoFocus","onCloseAutoFocus","children","props","ref","_jsx","Dialog","Content","forceMount","asChild","onFocusOutside","data-animation-direction","displayName","DrawerRoot","isVisible","onClose","Root","open","onOpenChange","nextVisible","modal","Portal","DrawerScrollableContent","isContentExpanded","data-collapsed","ScrollArea","DrawerCloseButton","label","formatMessage","useIntl","labelMessage","id","defaultMessage","onClick","variant","Cross","Drawer","Body","ScrollableContent","CloseButton","Title","Description"],"mappings":"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"}
@@ -0,0 +1,188 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var React = require('react');
5
+ var strapiAdmin = require('@strapi/admin/strapi-admin');
6
+ var designSystem = require('@strapi/design-system');
7
+ var reactIntl = require('react-intl');
8
+ var styledComponents = require('styled-components');
9
+ var useAIMetadataEnabled = require('../../hooks/useAIMetadataEnabled.js');
10
+ var useMediaLibraryPermissions = require('../../hooks/useMediaLibraryPermissions.js');
11
+ var useTracking = require('../../hooks/useTracking.js');
12
+ var api = require('../../services/api.js');
13
+ var settings = require('../../services/settings.js');
14
+ var allowedMediaTypes = require('../../utils/allowedMediaTypes.js');
15
+ var translations = require('../../utils/translations.js');
16
+ var typeFromMime = require('../../utils/typeFromMime.js');
17
+
18
+ const FRAME_COLORS = {
19
+ accept: {
20
+ border: 'primary600',
21
+ background: 'primary100',
22
+ text: 'primary600'
23
+ },
24
+ reject: {
25
+ border: 'neutral300',
26
+ background: 'neutral150',
27
+ text: 'neutral600'
28
+ }
29
+ };
30
+ // Takes the place of the editor's content in its layout, so it fills the same
31
+ // space: the editor stacks its parts against the start, sized to their content.
32
+ const Root = styledComponents.styled.div`
33
+ position: relative;
34
+ display: flex;
35
+ flex-direction: column;
36
+ flex: 1;
37
+ width: 100%;
38
+ min-height: 0;
39
+ `;
40
+ const Frame = styledComponents.styled.div`
41
+ position: absolute;
42
+ inset: ${({ theme })=>theme.spaces[2]};
43
+ z-index: 2;
44
+ display: flex;
45
+ align-items: center;
46
+ justify-content: center;
47
+ border: 1px solid ${({ theme, $dragState })=>theme.colors[FRAME_COLORS[$dragState].border]};
48
+ border-radius: ${({ theme })=>theme.borderRadius};
49
+ background: ${({ theme, $dragState })=>theme.colors[FRAME_COLORS[$dragState].background]};
50
+ pointer-events: none;
51
+ `;
52
+ const hasFiles = (event)=>event.dataTransfer.types.includes('Files');
53
+ /**
54
+ * Lets an editor take files dropped onto it: they upload to the root of the
55
+ * Media Library, through the global upload progress dialog, and are handed
56
+ * back to the editor one by one. Drags that carry no file, such as an editor
57
+ * reordering its own blocks, pass through untouched.
58
+ */ const EditorFileDropZone = ({ children, disabled = false, allowedTypes, onDropFiles })=>{
59
+ const { formatMessage } = reactIntl.useIntl();
60
+ const { toggleNotification } = strapiAdmin.useNotification();
61
+ const { trackUsage } = useTracking.useTracking();
62
+ const [uploadFiles] = api.useUploadFilesMutation();
63
+ const { canRead, canCreate, isLoading } = useMediaLibraryPermissions.useMediaLibraryPermissions();
64
+ const { data: settings$1 } = settings.useGetUploadSettingsQuery(undefined, {
65
+ skip: !canRead
66
+ });
67
+ const isAiMetadataEnabled = useAIMetadataEnabled.useAIMetadataEnabled();
68
+ const [dragState, setDragState] = React.useState('idle');
69
+ // Drag events fire on every descendant crossed, so count enter/leave pairs.
70
+ const dragDepth = React.useRef(0);
71
+ const canUpload = !disabled && !isLoading && canCreate;
72
+ const handleDragEnter = (event)=>{
73
+ if (!hasFiles(event)) {
74
+ return;
75
+ }
76
+ event.preventDefault();
77
+ dragDepth.current += 1;
78
+ setDragState(canUpload ? 'accept' : 'reject');
79
+ };
80
+ const handleDragLeave = (event)=>{
81
+ if (!hasFiles(event)) {
82
+ return;
83
+ }
84
+ dragDepth.current -= 1;
85
+ if (dragDepth.current <= 0) {
86
+ dragDepth.current = 0;
87
+ setDragState('idle');
88
+ }
89
+ };
90
+ const handleDragOver = (event)=>{
91
+ if (!hasFiles(event)) {
92
+ return;
93
+ }
94
+ // Without it the browser opens the file in the tab.
95
+ event.preventDefault();
96
+ // `none` shows the no-drop cursor, and the browser then fires no `drop`.
97
+ event.dataTransfer.dropEffect = canUpload ? 'copy' : 'none';
98
+ };
99
+ const handleDrop = async (event)=>{
100
+ if (!hasFiles(event)) {
101
+ return;
102
+ }
103
+ event.preventDefault();
104
+ dragDepth.current = 0;
105
+ setDragState('idle');
106
+ if (!canUpload) {
107
+ return;
108
+ }
109
+ const files = Array.from(event.dataTransfer.files);
110
+ const allowedFiles = allowedMediaTypes.filterAllowedFiles(allowedTypes, files);
111
+ if (allowedFiles.length < files.length) {
112
+ toggleNotification({
113
+ type: 'danger',
114
+ timeout: 4000,
115
+ message: formatMessage({
116
+ id: translations.getTranslationKey('input.notification.not-supported'),
117
+ defaultMessage: `You can't upload this type of file, only the following types are accepted – {fileTypes}`
118
+ }, {
119
+ fileTypes: (allowedTypes ?? []).join(',')
120
+ })
121
+ });
122
+ }
123
+ if (allowedFiles.length === 0) {
124
+ return;
125
+ }
126
+ const onFileUploaded = onDropFiles(event);
127
+ const assetsCountByType = allowedFiles.reduce((acc, file)=>{
128
+ const type = typeFromMime.typeFromMime(file.type);
129
+ acc[type] = (acc[type] ?? 0) + 1;
130
+ return acc;
131
+ }, {});
132
+ trackUsage('didSelectFile', {
133
+ source: 'computer',
134
+ location: 'content-manager'
135
+ });
136
+ trackUsage('willAddMediaLibraryAssets', {
137
+ location: 'content-manager',
138
+ ...assetsCountByType
139
+ });
140
+ const formData = new FormData();
141
+ allowedFiles.forEach((file)=>formData.append('files', file));
142
+ formData.append('fileInfo', JSON.stringify(allowedFiles.map((file)=>({
143
+ name: file.name,
144
+ caption: null,
145
+ alternativeText: null,
146
+ folder: null
147
+ }))));
148
+ try {
149
+ await uploadFiles({
150
+ formData,
151
+ totalFiles: allowedFiles.length,
152
+ concurrency: settings$1?.data?.concurrentUploadRequests ?? 1,
153
+ generateAiMetadata: Boolean(isAiMetadataEnabled),
154
+ onFileUploaded
155
+ }).unwrap();
156
+ } catch {
157
+ // Errors reach the user through the progress dialog, which the mutation
158
+ // populates itself.
159
+ }
160
+ };
161
+ return /*#__PURE__*/ jsxRuntime.jsxs(Root, {
162
+ onDragEnter: handleDragEnter,
163
+ onDragLeave: handleDragLeave,
164
+ onDragOver: handleDragOver,
165
+ onDrop: handleDrop,
166
+ children: [
167
+ children,
168
+ dragState !== 'idle' && /*#__PURE__*/ jsxRuntime.jsx(Frame, {
169
+ $dragState: dragState,
170
+ "data-testid": "editor-file-drop-zone",
171
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
172
+ fontWeight: "semiBold",
173
+ textColor: FRAME_COLORS[dragState].text,
174
+ children: dragState === 'accept' ? formatMessage({
175
+ id: translations.getTranslationKey('editor-drop-zone.accept'),
176
+ defaultMessage: 'Drop to upload and insert'
177
+ }) : formatMessage({
178
+ id: translations.getTranslationKey('editor-drop-zone.reject'),
179
+ defaultMessage: "You don't have permission to upload files"
180
+ })
181
+ })
182
+ })
183
+ ]
184
+ });
185
+ };
186
+
187
+ exports.EditorFileDropZone = EditorFileDropZone;
188
+ //# sourceMappingURL=EditorFileDropZone.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EditorFileDropZone.js","sources":["../../../../admin/src/components/EditorFileDropZone/EditorFileDropZone.tsx"],"sourcesContent":["import { useRef, useState, type DragEvent, type ReactNode } from 'react';\n\nimport { useNotification } from '@strapi/admin/strapi-admin';\nimport { Typography } from '@strapi/design-system';\nimport { useIntl } from 'react-intl';\nimport { styled } from 'styled-components';\n\nimport { useAIMetadataEnabled } from '../../hooks/useAIMetadataEnabled';\nimport { useMediaLibraryPermissions } from '../../hooks/useMediaLibraryPermissions';\nimport { useTracking } from '../../hooks/useTracking';\nimport { useUploadFilesMutation } from '../../services/api';\nimport { useGetUploadSettingsQuery } from '../../services/settings';\nimport { filterAllowedFiles, type AllowedMediaType } from '../../utils/allowedMediaTypes';\nimport { getTranslationKey } from '../../utils/translations';\nimport { typeFromMime } from '../../utils/typeFromMime';\n\nimport type { File } from '../../../../shared/contracts/files';\n\ntype DragState = 'idle' | 'accept' | 'reject';\n\nconst FRAME_COLORS = {\n accept: { border: 'primary600', background: 'primary100', text: 'primary600' },\n reject: { border: 'neutral300', background: 'neutral150', text: 'neutral600' },\n} as const;\n\n// Takes the place of the editor's content in its layout, so it fills the same\n// space: the editor stacks its parts against the start, sized to their content.\nconst Root = styled.div`\n position: relative;\n display: flex;\n flex-direction: column;\n flex: 1;\n width: 100%;\n min-height: 0;\n`;\n\nconst Frame = styled.div<{ $dragState: Exclude<DragState, 'idle'> }>`\n position: absolute;\n inset: ${({ theme }) => theme.spaces[2]};\n z-index: 2;\n display: flex;\n align-items: center;\n justify-content: center;\n border: 1px solid ${({ theme, $dragState }) => theme.colors[FRAME_COLORS[$dragState].border]};\n border-radius: ${({ theme }) => theme.borderRadius};\n background: ${({ theme, $dragState }) => theme.colors[FRAME_COLORS[$dragState].background]};\n pointer-events: none;\n`;\n\nexport interface EditorFileDropZoneProps {\n children: ReactNode;\n disabled?: boolean;\n allowedTypes?: AllowedMediaType[];\n /**\n * Called once per accepted drop, while the drop event is still current. It\n * returns what receives each of that drop's files once uploaded, including\n * files a Retry in the progress dialog uploads later.\n */\n onDropFiles: (event: DragEvent<HTMLElement>) => (file: File) => void;\n}\n\nconst hasFiles = (event: DragEvent<HTMLElement>) => event.dataTransfer.types.includes('Files');\n\n/**\n * Lets an editor take files dropped onto it: they upload to the root of the\n * Media Library, through the global upload progress dialog, and are handed\n * back to the editor one by one. Drags that carry no file, such as an editor\n * reordering its own blocks, pass through untouched.\n */\nexport const EditorFileDropZone = ({\n children,\n disabled = false,\n allowedTypes,\n onDropFiles,\n}: EditorFileDropZoneProps) => {\n const { formatMessage } = useIntl();\n const { toggleNotification } = useNotification();\n const { trackUsage } = useTracking();\n const [uploadFiles] = useUploadFilesMutation();\n const { canRead, canCreate, isLoading } = useMediaLibraryPermissions();\n const { data: settings } = useGetUploadSettingsQuery(undefined, { skip: !canRead });\n const isAiMetadataEnabled = useAIMetadataEnabled();\n const [dragState, setDragState] = useState<DragState>('idle');\n // Drag events fire on every descendant crossed, so count enter/leave pairs.\n const dragDepth = useRef(0);\n\n const canUpload = !disabled && !isLoading && canCreate;\n\n const handleDragEnter = (event: DragEvent<HTMLElement>) => {\n if (!hasFiles(event)) {\n return;\n }\n\n event.preventDefault();\n dragDepth.current += 1;\n setDragState(canUpload ? 'accept' : 'reject');\n };\n\n const handleDragLeave = (event: DragEvent<HTMLElement>) => {\n if (!hasFiles(event)) {\n return;\n }\n\n dragDepth.current -= 1;\n\n if (dragDepth.current <= 0) {\n dragDepth.current = 0;\n setDragState('idle');\n }\n };\n\n const handleDragOver = (event: DragEvent<HTMLElement>) => {\n if (!hasFiles(event)) {\n return;\n }\n\n // Without it the browser opens the file in the tab.\n event.preventDefault();\n // `none` shows the no-drop cursor, and the browser then fires no `drop`.\n event.dataTransfer.dropEffect = canUpload ? 'copy' : 'none';\n };\n\n const handleDrop = async (event: DragEvent<HTMLElement>) => {\n if (!hasFiles(event)) {\n return;\n }\n\n event.preventDefault();\n dragDepth.current = 0;\n setDragState('idle');\n\n if (!canUpload) {\n return;\n }\n\n const files = Array.from(event.dataTransfer.files);\n const allowedFiles = filterAllowedFiles(allowedTypes, files);\n\n if (allowedFiles.length < files.length) {\n toggleNotification({\n type: 'danger',\n timeout: 4000,\n message: formatMessage(\n {\n id: getTranslationKey('input.notification.not-supported'),\n defaultMessage: `You can't upload this type of file, only the following types are accepted – {fileTypes}`,\n },\n { fileTypes: (allowedTypes ?? []).join(',') }\n ),\n });\n }\n\n if (allowedFiles.length === 0) {\n return;\n }\n\n const onFileUploaded = onDropFiles(event);\n\n const assetsCountByType = allowedFiles.reduce<Record<string, number>>((acc, file) => {\n const type = typeFromMime(file.type);\n acc[type] = (acc[type] ?? 0) + 1;\n return acc;\n }, {});\n trackUsage('didSelectFile', { source: 'computer', location: 'content-manager' });\n trackUsage('willAddMediaLibraryAssets', { location: 'content-manager', ...assetsCountByType });\n\n const formData = new FormData();\n allowedFiles.forEach((file) => formData.append('files', file));\n formData.append(\n 'fileInfo',\n JSON.stringify(\n allowedFiles.map((file) => ({\n name: file.name,\n caption: null,\n alternativeText: null,\n folder: null,\n }))\n )\n );\n\n try {\n await uploadFiles({\n formData,\n totalFiles: allowedFiles.length,\n concurrency: settings?.data?.concurrentUploadRequests ?? 1,\n generateAiMetadata: Boolean(isAiMetadataEnabled),\n onFileUploaded,\n }).unwrap();\n } catch {\n // Errors reach the user through the progress dialog, which the mutation\n // populates itself.\n }\n };\n\n return (\n <Root\n onDragEnter={handleDragEnter}\n onDragLeave={handleDragLeave}\n onDragOver={handleDragOver}\n onDrop={handleDrop}\n >\n {children}\n {dragState !== 'idle' && (\n <Frame $dragState={dragState} data-testid=\"editor-file-drop-zone\">\n <Typography fontWeight=\"semiBold\" textColor={FRAME_COLORS[dragState].text}>\n {dragState === 'accept'\n ? formatMessage({\n id: getTranslationKey('editor-drop-zone.accept'),\n defaultMessage: 'Drop to upload and insert',\n })\n : formatMessage({\n id: getTranslationKey('editor-drop-zone.reject'),\n defaultMessage: \"You don't have permission to upload files\",\n })}\n </Typography>\n </Frame>\n )}\n </Root>\n );\n};\n"],"names":["FRAME_COLORS","accept","border","background","text","reject","Root","styled","div","Frame","theme","spaces","$dragState","colors","borderRadius","hasFiles","event","dataTransfer","types","includes","EditorFileDropZone","children","disabled","allowedTypes","onDropFiles","formatMessage","useIntl","toggleNotification","useNotification","trackUsage","useTracking","uploadFiles","useUploadFilesMutation","canRead","canCreate","isLoading","useMediaLibraryPermissions","data","settings","useGetUploadSettingsQuery","undefined","skip","isAiMetadataEnabled","useAIMetadataEnabled","dragState","setDragState","useState","dragDepth","useRef","canUpload","handleDragEnter","preventDefault","current","handleDragLeave","handleDragOver","dropEffect","handleDrop","files","Array","from","allowedFiles","filterAllowedFiles","length","type","timeout","message","id","getTranslationKey","defaultMessage","fileTypes","join","onFileUploaded","assetsCountByType","reduce","acc","file","typeFromMime","source","location","formData","FormData","forEach","append","JSON","stringify","map","name","caption","alternativeText","folder","totalFiles","concurrency","concurrentUploadRequests","generateAiMetadata","Boolean","unwrap","_jsxs","onDragEnter","onDragLeave","onDragOver","onDrop","_jsx","data-testid","Typography","fontWeight","textColor"],"mappings":"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"}