@strapi/upload 5.52.0 → 5.52.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.
Files changed (111) hide show
  1. package/dist/admin/future/components/TruncatedText.js +76 -0
  2. package/dist/admin/future/components/TruncatedText.js.map +1 -0
  3. package/dist/admin/future/components/TruncatedText.mjs +74 -0
  4. package/dist/admin/future/components/TruncatedText.mjs.map +1 -0
  5. package/dist/admin/future/hooks/useTracking.js +57 -0
  6. package/dist/admin/future/hooks/useTracking.js.map +1 -0
  7. package/dist/admin/future/hooks/useTracking.mjs +53 -0
  8. package/dist/admin/future/hooks/useTracking.mjs.map +1 -0
  9. package/dist/admin/future/pages/Assets/AssetsPage.js +31 -0
  10. package/dist/admin/future/pages/Assets/AssetsPage.js.map +1 -1
  11. package/dist/admin/future/pages/Assets/AssetsPage.mjs +31 -0
  12. package/dist/admin/future/pages/Assets/AssetsPage.mjs.map +1 -1
  13. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js +9 -6
  14. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.js.map +1 -1
  15. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs +9 -6
  16. package/dist/admin/future/pages/Assets/components/AssetActionsMenu.mjs.map +1 -1
  17. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js +21 -1
  18. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.js.map +1 -1
  19. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs +21 -1
  20. package/dist/admin/future/pages/Assets/components/AssetDetails/AssetDetailsDrawer.mjs.map +1 -1
  21. package/dist/admin/future/pages/Assets/components/AssetsGrid.js +3 -4
  22. package/dist/admin/future/pages/Assets/components/AssetsGrid.js.map +1 -1
  23. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs +4 -5
  24. package/dist/admin/future/pages/Assets/components/AssetsGrid.mjs.map +1 -1
  25. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js +11 -1
  26. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.js.map +1 -1
  27. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs +11 -1
  28. package/dist/admin/future/pages/Assets/components/AssetsSearchInput.mjs.map +1 -1
  29. package/dist/admin/future/pages/Assets/components/AssetsTable.js +6 -4
  30. package/dist/admin/future/pages/Assets/components/AssetsTable.js.map +1 -1
  31. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs +6 -4
  32. package/dist/admin/future/pages/Assets/components/AssetsTable.mjs.map +1 -1
  33. package/dist/admin/future/pages/Assets/components/FilterMenu.js +18 -2
  34. package/dist/admin/future/pages/Assets/components/FilterMenu.js.map +1 -1
  35. package/dist/admin/future/pages/Assets/components/FilterMenu.mjs +18 -2
  36. package/dist/admin/future/pages/Assets/components/FilterMenu.mjs.map +1 -1
  37. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js +9 -7
  38. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.js.map +1 -1
  39. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs +9 -7
  40. package/dist/admin/future/pages/Assets/components/FolderActionsMenu.mjs.map +1 -1
  41. package/dist/admin/future/pages/Assets/components/FolderFormDialog.js +12 -0
  42. package/dist/admin/future/pages/Assets/components/FolderFormDialog.js.map +1 -1
  43. package/dist/admin/future/pages/Assets/components/FolderFormDialog.mjs +12 -0
  44. package/dist/admin/future/pages/Assets/components/FolderFormDialog.mjs.map +1 -1
  45. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js +21 -38
  46. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.js.map +1 -1
  47. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs +23 -40
  48. package/dist/admin/future/pages/Assets/components/FolderTree/FolderTree.mjs.map +1 -1
  49. package/dist/admin/future/pages/Assets/components/SortMenu.js +14 -0
  50. package/dist/admin/future/pages/Assets/components/SortMenu.js.map +1 -1
  51. package/dist/admin/future/pages/Assets/components/SortMenu.mjs +14 -0
  52. package/dist/admin/future/pages/Assets/components/SortMenu.mjs.map +1 -1
  53. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js +5 -0
  54. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.js.map +1 -1
  55. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs +6 -1
  56. package/dist/admin/future/pages/Assets/hooks/useAssetSelection.mjs.map +1 -1
  57. package/dist/admin/future/pages/Assets/utils/selection.js +17 -0
  58. package/dist/admin/future/pages/Assets/utils/selection.js.map +1 -1
  59. package/dist/admin/future/pages/Assets/utils/selection.mjs +17 -1
  60. package/dist/admin/future/pages/Assets/utils/selection.mjs.map +1 -1
  61. package/dist/admin/future/services/api.js +7 -8
  62. package/dist/admin/future/services/api.js.map +1 -1
  63. package/dist/admin/future/services/api.mjs +7 -8
  64. package/dist/admin/future/services/api.mjs.map +1 -1
  65. package/dist/admin/future/services/assets.js +11 -23
  66. package/dist/admin/future/services/assets.js.map +1 -1
  67. package/dist/admin/future/services/assets.mjs +11 -23
  68. package/dist/admin/future/services/assets.mjs.map +1 -1
  69. package/dist/admin/hooks/useAIMetadataJob.js +1 -1
  70. package/dist/admin/hooks/useAIMetadataJob.js.map +1 -1
  71. package/dist/admin/hooks/useAIMetadataJob.mjs +1 -1
  72. package/dist/admin/hooks/useAIMetadataJob.mjs.map +1 -1
  73. package/dist/admin/pages/SettingsPage/SettingsPage.js +2 -2
  74. package/dist/admin/pages/SettingsPage/SettingsPage.js.map +1 -1
  75. package/dist/admin/pages/SettingsPage/SettingsPage.mjs +2 -2
  76. package/dist/admin/pages/SettingsPage/SettingsPage.mjs.map +1 -1
  77. package/dist/admin/src/future/components/TruncatedText.d.ts +32 -0
  78. package/dist/admin/src/future/hooks/useTracking.d.ts +29 -0
  79. package/dist/admin/src/future/pages/Assets/components/AssetActionsMenu.d.ts +5 -3
  80. package/dist/admin/src/future/pages/Assets/components/FolderActionsMenu.d.ts +5 -4
  81. package/dist/admin/src/future/pages/Assets/hooks/useAssetSelection.d.ts +2 -0
  82. package/dist/admin/src/future/pages/Assets/utils/selection.d.ts +10 -0
  83. package/dist/admin/src/future/services/api.d.ts +6 -7
  84. package/dist/server/controllers/admin-file.js +27 -9
  85. package/dist/server/controllers/admin-file.js.map +1 -1
  86. package/dist/server/controllers/admin-file.mjs +27 -9
  87. package/dist/server/controllers/admin-file.mjs.map +1 -1
  88. package/dist/server/controllers/admin-upload.js +24 -8
  89. package/dist/server/controllers/admin-upload.js.map +1 -1
  90. package/dist/server/controllers/admin-upload.mjs +24 -8
  91. package/dist/server/controllers/admin-upload.mjs.map +1 -1
  92. package/dist/server/routes/admin.js +41 -21
  93. package/dist/server/routes/admin.js.map +1 -1
  94. package/dist/server/routes/admin.mjs +41 -21
  95. package/dist/server/routes/admin.mjs.map +1 -1
  96. package/dist/server/services/ai-metadata.js.map +1 -1
  97. package/dist/server/services/ai-metadata.mjs.map +1 -1
  98. package/dist/server/src/controllers/admin-file.d.ts +29 -8
  99. package/dist/server/src/controllers/admin-file.d.ts.map +1 -1
  100. package/dist/server/src/controllers/admin-upload.d.ts +20 -4
  101. package/dist/server/src/controllers/admin-upload.d.ts.map +1 -1
  102. package/dist/server/src/controllers/index.d.ts +4 -4
  103. package/dist/server/src/index.d.ts +4 -4
  104. package/dist/server/src/routes/admin.d.ts.map +1 -1
  105. package/dist/server/src/services/ai-metadata.d.ts +2 -2
  106. package/dist/server/src/services/ai-metadata.d.ts.map +1 -1
  107. package/dist/shared/contracts/ai-metadata-jobs.d.ts +10 -32
  108. package/dist/shared/contracts/ai-metadata-jobs.d.ts.map +1 -1
  109. package/dist/shared/contracts/files.d.ts +10 -10
  110. package/dist/shared/contracts/files.d.ts.map +1 -1
  111. package/package.json +6 -6
@@ -0,0 +1,76 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var React = require('react');
5
+ var designSystem = require('@strapi/design-system');
6
+
7
+ /**
8
+ * Text that ellipsizes, with a tooltip carrying the full value once it actually
9
+ * does. Used for folder and asset names, which are user-supplied and routinely
10
+ * longer than the column, card or rail holding them.
11
+ *
12
+ * The tooltip is conditional rather than always-on: a permanent tooltip would
13
+ * make every short name hoverable for no reason, and would announce a
14
+ * description that merely repeats the visible text.
15
+ *
16
+ * Whether the text is truncated can't be derived from the string — it depends on
17
+ * the rendered width — so it has to be measured. That measurement happens when
18
+ * the pointer or focus arrives, not at mount.
19
+ *
20
+ * Measuring at mount is the obvious approach and it does not work here. The
21
+ * reading is only correct once the row's width constraint has reached this span,
22
+ * and the tree remounts its rows during first render: the instance that survives
23
+ * can take its one measurement while still unconstrained, conclude it fits, and
24
+ * be stuck there. Nothing corrects it afterwards, because a ResizeObserver on
25
+ * this span never fires again — an ancestor clamps the row, so the text
26
+ * overflows inside a box whose own size stops changing.
27
+ *
28
+ * Reading on hover sidesteps all of that. It is the only moment the answer is
29
+ * needed, the layout is settled by then, and nothing is cached across remounts
30
+ * or resizes that could go stale.
31
+ */ const TruncatedText = ({ children, ...props })=>{
32
+ const textRef = React.useRef(null);
33
+ const [isTruncated, setIsTruncated] = React.useState(false);
34
+ const checkTruncation = ()=>{
35
+ const el = textRef.current;
36
+ if (el) {
37
+ setIsTruncated(el.scrollWidth > el.clientWidth);
38
+ }
39
+ };
40
+ // A ref callback rather than useRef alone, so the first measurement happens
41
+ // when the node attaches and again whenever it is replaced. The tree remounts
42
+ // its rows during first render, and a value measured against the discarded
43
+ // node would never be revisited — this re-runs against the node that survives.
44
+ const attachRef = (el)=>{
45
+ textRef.current = el;
46
+ if (el) {
47
+ checkTruncation();
48
+ }
49
+ };
50
+ // The Tooltip stays mounted and the *label* is what's conditional, rather than
51
+ // swapping between a wrapped and unwrapped span. Swapping loses the first
52
+ // hover: the measurement that decides to show a tooltip is itself triggered by
53
+ // the pointer arriving, so the trigger would mount underneath a pointer that
54
+ // has already entered, and Radix — which starts watching at mount — sees no
55
+ // enter event and stays closed until the pointer leaves and returns.
56
+ //
57
+ // With `label` empty the DS Tooltip renders its child as-is and attaches
58
+ // nothing, so untruncated text still costs no DOM node and announces no
59
+ // description duplicating what is already on screen.
60
+ return /*#__PURE__*/ jsxRuntime.jsx(designSystem.Tooltip, {
61
+ label: isTruncated ? children : undefined,
62
+ children: /*#__PURE__*/ jsxRuntime.jsx(designSystem.Typography, {
63
+ ref: attachRef,
64
+ ellipsis: true,
65
+ // Focus is covered as well as hover: the tooltip has to be reachable by
66
+ // keyboard, and these rows are buttons.
67
+ onPointerEnter: checkTruncation,
68
+ onFocus: checkTruncation,
69
+ ...props,
70
+ children: children
71
+ })
72
+ });
73
+ };
74
+
75
+ exports.TruncatedText = TruncatedText;
76
+ //# sourceMappingURL=TruncatedText.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TruncatedText.js","sources":["../../../../admin/src/future/components/TruncatedText.tsx"],"sourcesContent":["import { useRef, useState } from 'react';\n\nimport { Tooltip, Typography, type TypographyProps } from '@strapi/design-system';\n\ninterface TruncatedTextProps extends Omit<TypographyProps, 'children' | 'ellipsis'> {\n /** The full text. Doubles as the tooltip content once it no longer fits. */\n children: string;\n}\n\n/**\n * Text that ellipsizes, with a tooltip carrying the full value once it actually\n * does. Used for folder and asset names, which are user-supplied and routinely\n * longer than the column, card or rail holding them.\n *\n * The tooltip is conditional rather than always-on: a permanent tooltip would\n * make every short name hoverable for no reason, and would announce a\n * description that merely repeats the visible text.\n *\n * Whether the text is truncated can't be derived from the string — it depends on\n * the rendered width — so it has to be measured. That measurement happens when\n * the pointer or focus arrives, not at mount.\n *\n * Measuring at mount is the obvious approach and it does not work here. The\n * reading is only correct once the row's width constraint has reached this span,\n * and the tree remounts its rows during first render: the instance that survives\n * can take its one measurement while still unconstrained, conclude it fits, and\n * be stuck there. Nothing corrects it afterwards, because a ResizeObserver on\n * this span never fires again — an ancestor clamps the row, so the text\n * overflows inside a box whose own size stops changing.\n *\n * Reading on hover sidesteps all of that. It is the only moment the answer is\n * needed, the layout is settled by then, and nothing is cached across remounts\n * or resizes that could go stale.\n */\nexport const TruncatedText = ({ children, ...props }: TruncatedTextProps) => {\n const textRef = useRef<HTMLSpanElement | null>(null);\n const [isTruncated, setIsTruncated] = useState(false);\n\n const checkTruncation = () => {\n const el = textRef.current;\n\n if (el) {\n setIsTruncated(el.scrollWidth > el.clientWidth);\n }\n };\n\n // A ref callback rather than useRef alone, so the first measurement happens\n // when the node attaches and again whenever it is replaced. The tree remounts\n // its rows during first render, and a value measured against the discarded\n // node would never be revisited — this re-runs against the node that survives.\n const attachRef = (el: HTMLSpanElement | null) => {\n textRef.current = el;\n\n if (el) {\n checkTruncation();\n }\n };\n\n // The Tooltip stays mounted and the *label* is what's conditional, rather than\n // swapping between a wrapped and unwrapped span. Swapping loses the first\n // hover: the measurement that decides to show a tooltip is itself triggered by\n // the pointer arriving, so the trigger would mount underneath a pointer that\n // has already entered, and Radix — which starts watching at mount — sees no\n // enter event and stays closed until the pointer leaves and returns.\n //\n // With `label` empty the DS Tooltip renders its child as-is and attaches\n // nothing, so untruncated text still costs no DOM node and announces no\n // description duplicating what is already on screen.\n return (\n <Tooltip label={isTruncated ? children : undefined}>\n <Typography\n ref={attachRef}\n ellipsis\n // Focus is covered as well as hover: the tooltip has to be reachable by\n // keyboard, and these rows are buttons.\n onPointerEnter={checkTruncation}\n onFocus={checkTruncation}\n {...props}\n >\n {children}\n </Typography>\n </Tooltip>\n );\n};\n"],"names":["TruncatedText","children","props","textRef","useRef","isTruncated","setIsTruncated","useState","checkTruncation","el","current","scrollWidth","clientWidth","attachRef","_jsx","Tooltip","label","undefined","Typography","ref","ellipsis","onPointerEnter","onFocus"],"mappings":";;;;;;AASA;;;;;;;;;;;;;;;;;;;;;;;;UAyBaA,aAAAA,GAAgB,CAAC,EAAEC,QAAQ,EAAE,GAAGC,KAAAA,EAA2B,GAAA;AACtE,IAAA,MAAMC,UAAUC,YAAAA,CAA+B,IAAA,CAAA;AAC/C,IAAA,MAAM,CAACC,WAAAA,EAAaC,cAAAA,CAAe,GAAGC,cAAAA,CAAS,KAAA,CAAA;AAE/C,IAAA,MAAMC,eAAAA,GAAkB,IAAA;QACtB,MAAMC,EAAAA,GAAKN,QAAQO,OAAO;AAE1B,QAAA,IAAID,EAAAA,EAAI;AACNH,YAAAA,cAAAA,CAAeG,EAAAA,CAAGE,WAAW,GAAGF,EAAAA,CAAGG,WAAW,CAAA;AAChD,QAAA;AACF,IAAA,CAAA;;;;;AAMA,IAAA,MAAMC,YAAY,CAACJ,EAAAA,GAAAA;AACjBN,QAAAA,OAAAA,CAAQO,OAAO,GAAGD,EAAAA;AAElB,QAAA,IAAIA,EAAAA,EAAI;AACND,YAAAA,eAAAA,EAAAA;AACF,QAAA;AACF,IAAA,CAAA;;;;;;;;;;;AAYA,IAAA,qBACEM,cAAA,CAACC,oBAAAA,EAAAA;AAAQC,QAAAA,KAAAA,EAAOX,cAAcJ,QAAAA,GAAWgB,SAAAA;AACvC,QAAA,QAAA,gBAAAH,cAAA,CAACI,uBAAAA,EAAAA;YACCC,GAAAA,EAAKN,SAAAA;YACLO,QAAQ,EAAA,IAAA;;;YAGRC,cAAAA,EAAgBb,eAAAA;YAChBc,OAAAA,EAASd,eAAAA;AACR,YAAA,GAAGN,KAAK;AAERD,YAAAA,QAAAA,EAAAA;;;AAIT;;;;"}
@@ -0,0 +1,74 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useRef, useState } from 'react';
3
+ import { Tooltip, Typography } from '@strapi/design-system';
4
+
5
+ /**
6
+ * Text that ellipsizes, with a tooltip carrying the full value once it actually
7
+ * does. Used for folder and asset names, which are user-supplied and routinely
8
+ * longer than the column, card or rail holding them.
9
+ *
10
+ * The tooltip is conditional rather than always-on: a permanent tooltip would
11
+ * make every short name hoverable for no reason, and would announce a
12
+ * description that merely repeats the visible text.
13
+ *
14
+ * Whether the text is truncated can't be derived from the string — it depends on
15
+ * the rendered width — so it has to be measured. That measurement happens when
16
+ * the pointer or focus arrives, not at mount.
17
+ *
18
+ * Measuring at mount is the obvious approach and it does not work here. The
19
+ * reading is only correct once the row's width constraint has reached this span,
20
+ * and the tree remounts its rows during first render: the instance that survives
21
+ * can take its one measurement while still unconstrained, conclude it fits, and
22
+ * be stuck there. Nothing corrects it afterwards, because a ResizeObserver on
23
+ * this span never fires again — an ancestor clamps the row, so the text
24
+ * overflows inside a box whose own size stops changing.
25
+ *
26
+ * Reading on hover sidesteps all of that. It is the only moment the answer is
27
+ * needed, the layout is settled by then, and nothing is cached across remounts
28
+ * or resizes that could go stale.
29
+ */ const TruncatedText = ({ children, ...props })=>{
30
+ const textRef = useRef(null);
31
+ const [isTruncated, setIsTruncated] = useState(false);
32
+ const checkTruncation = ()=>{
33
+ const el = textRef.current;
34
+ if (el) {
35
+ setIsTruncated(el.scrollWidth > el.clientWidth);
36
+ }
37
+ };
38
+ // A ref callback rather than useRef alone, so the first measurement happens
39
+ // when the node attaches and again whenever it is replaced. The tree remounts
40
+ // its rows during first render, and a value measured against the discarded
41
+ // node would never be revisited — this re-runs against the node that survives.
42
+ const attachRef = (el)=>{
43
+ textRef.current = el;
44
+ if (el) {
45
+ checkTruncation();
46
+ }
47
+ };
48
+ // The Tooltip stays mounted and the *label* is what's conditional, rather than
49
+ // swapping between a wrapped and unwrapped span. Swapping loses the first
50
+ // hover: the measurement that decides to show a tooltip is itself triggered by
51
+ // the pointer arriving, so the trigger would mount underneath a pointer that
52
+ // has already entered, and Radix — which starts watching at mount — sees no
53
+ // enter event and stays closed until the pointer leaves and returns.
54
+ //
55
+ // With `label` empty the DS Tooltip renders its child as-is and attaches
56
+ // nothing, so untruncated text still costs no DOM node and announces no
57
+ // description duplicating what is already on screen.
58
+ return /*#__PURE__*/ jsx(Tooltip, {
59
+ label: isTruncated ? children : undefined,
60
+ children: /*#__PURE__*/ jsx(Typography, {
61
+ ref: attachRef,
62
+ ellipsis: true,
63
+ // Focus is covered as well as hover: the tooltip has to be reachable by
64
+ // keyboard, and these rows are buttons.
65
+ onPointerEnter: checkTruncation,
66
+ onFocus: checkTruncation,
67
+ ...props,
68
+ children: children
69
+ })
70
+ });
71
+ };
72
+
73
+ export { TruncatedText };
74
+ //# sourceMappingURL=TruncatedText.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TruncatedText.mjs","sources":["../../../../admin/src/future/components/TruncatedText.tsx"],"sourcesContent":["import { useRef, useState } from 'react';\n\nimport { Tooltip, Typography, type TypographyProps } from '@strapi/design-system';\n\ninterface TruncatedTextProps extends Omit<TypographyProps, 'children' | 'ellipsis'> {\n /** The full text. Doubles as the tooltip content once it no longer fits. */\n children: string;\n}\n\n/**\n * Text that ellipsizes, with a tooltip carrying the full value once it actually\n * does. Used for folder and asset names, which are user-supplied and routinely\n * longer than the column, card or rail holding them.\n *\n * The tooltip is conditional rather than always-on: a permanent tooltip would\n * make every short name hoverable for no reason, and would announce a\n * description that merely repeats the visible text.\n *\n * Whether the text is truncated can't be derived from the string — it depends on\n * the rendered width — so it has to be measured. That measurement happens when\n * the pointer or focus arrives, not at mount.\n *\n * Measuring at mount is the obvious approach and it does not work here. The\n * reading is only correct once the row's width constraint has reached this span,\n * and the tree remounts its rows during first render: the instance that survives\n * can take its one measurement while still unconstrained, conclude it fits, and\n * be stuck there. Nothing corrects it afterwards, because a ResizeObserver on\n * this span never fires again — an ancestor clamps the row, so the text\n * overflows inside a box whose own size stops changing.\n *\n * Reading on hover sidesteps all of that. It is the only moment the answer is\n * needed, the layout is settled by then, and nothing is cached across remounts\n * or resizes that could go stale.\n */\nexport const TruncatedText = ({ children, ...props }: TruncatedTextProps) => {\n const textRef = useRef<HTMLSpanElement | null>(null);\n const [isTruncated, setIsTruncated] = useState(false);\n\n const checkTruncation = () => {\n const el = textRef.current;\n\n if (el) {\n setIsTruncated(el.scrollWidth > el.clientWidth);\n }\n };\n\n // A ref callback rather than useRef alone, so the first measurement happens\n // when the node attaches and again whenever it is replaced. The tree remounts\n // its rows during first render, and a value measured against the discarded\n // node would never be revisited — this re-runs against the node that survives.\n const attachRef = (el: HTMLSpanElement | null) => {\n textRef.current = el;\n\n if (el) {\n checkTruncation();\n }\n };\n\n // The Tooltip stays mounted and the *label* is what's conditional, rather than\n // swapping between a wrapped and unwrapped span. Swapping loses the first\n // hover: the measurement that decides to show a tooltip is itself triggered by\n // the pointer arriving, so the trigger would mount underneath a pointer that\n // has already entered, and Radix — which starts watching at mount — sees no\n // enter event and stays closed until the pointer leaves and returns.\n //\n // With `label` empty the DS Tooltip renders its child as-is and attaches\n // nothing, so untruncated text still costs no DOM node and announces no\n // description duplicating what is already on screen.\n return (\n <Tooltip label={isTruncated ? children : undefined}>\n <Typography\n ref={attachRef}\n ellipsis\n // Focus is covered as well as hover: the tooltip has to be reachable by\n // keyboard, and these rows are buttons.\n onPointerEnter={checkTruncation}\n onFocus={checkTruncation}\n {...props}\n >\n {children}\n </Typography>\n </Tooltip>\n );\n};\n"],"names":["TruncatedText","children","props","textRef","useRef","isTruncated","setIsTruncated","useState","checkTruncation","el","current","scrollWidth","clientWidth","attachRef","_jsx","Tooltip","label","undefined","Typography","ref","ellipsis","onPointerEnter","onFocus"],"mappings":";;;;AASA;;;;;;;;;;;;;;;;;;;;;;;;UAyBaA,aAAAA,GAAgB,CAAC,EAAEC,QAAQ,EAAE,GAAGC,KAAAA,EAA2B,GAAA;AACtE,IAAA,MAAMC,UAAUC,MAAAA,CAA+B,IAAA,CAAA;AAC/C,IAAA,MAAM,CAACC,WAAAA,EAAaC,cAAAA,CAAe,GAAGC,QAAAA,CAAS,KAAA,CAAA;AAE/C,IAAA,MAAMC,eAAAA,GAAkB,IAAA;QACtB,MAAMC,EAAAA,GAAKN,QAAQO,OAAO;AAE1B,QAAA,IAAID,EAAAA,EAAI;AACNH,YAAAA,cAAAA,CAAeG,EAAAA,CAAGE,WAAW,GAAGF,EAAAA,CAAGG,WAAW,CAAA;AAChD,QAAA;AACF,IAAA,CAAA;;;;;AAMA,IAAA,MAAMC,YAAY,CAACJ,EAAAA,GAAAA;AACjBN,QAAAA,OAAAA,CAAQO,OAAO,GAAGD,EAAAA;AAElB,QAAA,IAAIA,EAAAA,EAAI;AACND,YAAAA,eAAAA,EAAAA;AACF,QAAA;AACF,IAAA,CAAA;;;;;;;;;;;AAYA,IAAA,qBACEM,GAAA,CAACC,OAAAA,EAAAA;AAAQC,QAAAA,KAAAA,EAAOX,cAAcJ,QAAAA,GAAWgB,SAAAA;AACvC,QAAA,QAAA,gBAAAH,GAAA,CAACI,UAAAA,EAAAA;YACCC,GAAAA,EAAKN,SAAAA;YACLO,QAAQ,EAAA,IAAA;;;YAGRC,cAAAA,EAAgBb,eAAAA;YAChBc,OAAAA,EAASd,eAAAA;AACR,YAAA,GAAGN,KAAK;AAERD,YAAAA,QAAAA,EAAAA;;;AAIT;;;;"}
@@ -0,0 +1,57 @@
1
+ 'use strict';
2
+
3
+ var React = require('react');
4
+ var strapiAdmin = require('@strapi/admin/strapi-admin');
5
+ var ee = require('@strapi/admin/strapi-admin/ee');
6
+ var settings = require('../services/settings.js');
7
+
8
+ /**
9
+ * Media Library version tag stamped on every event fired from the new
10
+ * (`src/future`) Media Library. Legacy events omit it, so analytics can filter
11
+ * new usage from the old plan while both trees emit the SAME event names (see
12
+ * CMS-246). Bump this if a future revamp ever needs its own bucket.
13
+ */ const MEDIA_LIBRARY_VERSION = 'v2';
14
+ /**
15
+ * `location` value for every new Media Library event. The new tree is the
16
+ * standalone Media Library page, which the legacy plan tags `'upload'` (as
17
+ * opposed to `'content-manager'` for the asset-picker modal). Kept identical so
18
+ * migrated events line up with the legacy ones on the `location` axis.
19
+ */ const MEDIA_LIBRARY_LOCATION = 'upload';
20
+ /**
21
+ * Tracking wrapper scoped to the new Media Library. Mirrors the legacy
22
+ * `admin/src/hooks/useTracking` — it forwards to the admin `trackUsage` and
23
+ * injects `isAiMediaLibraryConfigured` when AI is available — and additionally
24
+ * stamps `mediaLibraryVersion` on every event so the migrated plan stays
25
+ * distinguishable from the legacy one.
26
+ *
27
+ * The cast mirrors the legacy wrapper: these two properties are cross-cutting
28
+ * media-tracking metadata that the admin `TrackingEvent` type does not (and
29
+ * should not) enumerate per event.
30
+ */ const useTracking = ()=>{
31
+ const { trackUsage: trackStrapiUsage } = strapiAdmin.useTracking();
32
+ const { data } = settings.useGetUploadSettingsQuery();
33
+ const isAiAvailable = ee.useAIAvailability();
34
+ // Memoised so consumers can safely list `trackUsage` in effect deps (the
35
+ // search-input debounce commit does) without re-firing every render.
36
+ const trackUsage = React.useCallback((event, properties)=>{
37
+ return trackStrapiUsage(event, {
38
+ ...properties,
39
+ ...isAiAvailable ? {
40
+ isAiMediaLibraryConfigured: Boolean(data?.data?.aiMetadata)
41
+ } : {},
42
+ mediaLibraryVersion: MEDIA_LIBRARY_VERSION
43
+ });
44
+ }, [
45
+ trackStrapiUsage,
46
+ isAiAvailable,
47
+ data
48
+ ]);
49
+ return {
50
+ trackUsage
51
+ };
52
+ };
53
+
54
+ exports.MEDIA_LIBRARY_LOCATION = MEDIA_LIBRARY_LOCATION;
55
+ exports.MEDIA_LIBRARY_VERSION = MEDIA_LIBRARY_VERSION;
56
+ exports.useTracking = useTracking;
57
+ //# sourceMappingURL=useTracking.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTracking.js","sources":["../../../../admin/src/future/hooks/useTracking.ts"],"sourcesContent":["import { useCallback } from 'react';\n\nimport { useTracking as useStrapiTracking, type TrackingEvent } from '@strapi/admin/strapi-admin';\nimport { useAIAvailability } from '@strapi/admin/strapi-admin/ee';\n\nimport { useGetUploadSettingsQuery } from '../services/settings';\n\n/**\n * Media Library version tag stamped on every event fired from the new\n * (`src/future`) Media Library. Legacy events omit it, so analytics can filter\n * new usage from the old plan while both trees emit the SAME event names (see\n * CMS-246). Bump this if a future revamp ever needs its own bucket.\n */\nexport const MEDIA_LIBRARY_VERSION = 'v2';\n\n/**\n * `location` value for every new Media Library event. The new tree is the\n * standalone Media Library page, which the legacy plan tags `'upload'` (as\n * opposed to `'content-manager'` for the asset-picker modal). Kept identical so\n * migrated events line up with the legacy ones on the `location` axis.\n */\nexport const MEDIA_LIBRARY_LOCATION = 'upload';\n\n/**\n * Tracking wrapper scoped to the new Media Library. Mirrors the legacy\n * `admin/src/hooks/useTracking` — it forwards to the admin `trackUsage` and\n * injects `isAiMediaLibraryConfigured` when AI is available — and additionally\n * stamps `mediaLibraryVersion` on every event so the migrated plan stays\n * distinguishable from the legacy one.\n *\n * The cast mirrors the legacy wrapper: these two properties are cross-cutting\n * media-tracking metadata that the admin `TrackingEvent` type does not (and\n * should not) enumerate per event.\n */\nexport const useTracking = () => {\n const { trackUsage: trackStrapiUsage } = useStrapiTracking();\n const { data } = useGetUploadSettingsQuery();\n const isAiAvailable = useAIAvailability();\n\n // Memoised so consumers can safely list `trackUsage` in effect deps (the\n // search-input debounce commit does) without re-firing every render.\n const trackUsage = useCallback(\n <TEvent extends TrackingEvent>(event: TEvent['name'], properties?: TEvent['properties']) => {\n return trackStrapiUsage(event, {\n ...properties,\n ...(isAiAvailable ? { isAiMediaLibraryConfigured: Boolean(data?.data?.aiMetadata) } : {}),\n mediaLibraryVersion: MEDIA_LIBRARY_VERSION,\n } as TEvent['properties']);\n },\n [trackStrapiUsage, isAiAvailable, data]\n );\n\n return { trackUsage };\n};\n"],"names":["MEDIA_LIBRARY_VERSION","MEDIA_LIBRARY_LOCATION","useTracking","trackUsage","trackStrapiUsage","useStrapiTracking","data","useGetUploadSettingsQuery","isAiAvailable","useAIAvailability","useCallback","event","properties","isAiMediaLibraryConfigured","Boolean","aiMetadata","mediaLibraryVersion"],"mappings":";;;;;;;AAOA;;;;;IAMO,MAAMA,qBAAAA,GAAwB;AAErC;;;;;IAMO,MAAMC,sBAAAA,GAAyB;AAEtC;;;;;;;;;;UAWaC,WAAAA,GAAc,IAAA;AACzB,IAAA,MAAM,EAAEC,UAAAA,EAAYC,gBAAgB,EAAE,GAAGC,uBAAAA,EAAAA;IACzC,MAAM,EAAEC,IAAI,EAAE,GAAGC,kCAAAA,EAAAA;AACjB,IAAA,MAAMC,aAAAA,GAAgBC,oBAAAA,EAAAA;;;IAItB,MAAMN,UAAAA,GAAaO,iBAAAA,CACjB,CAA+BC,KAAAA,EAAuBC,UAAAA,GAAAA;AACpD,QAAA,OAAOR,iBAAiBO,KAAAA,EAAO;AAC7B,YAAA,GAAGC,UAAU;AACb,YAAA,GAAIJ,aAAAA,GAAgB;gBAAEK,0BAAAA,EAA4BC,OAAAA,CAAQR,MAAMA,IAAAA,EAAMS,UAAAA;AAAY,aAAA,GAAI,EAAE;YACxFC,mBAAAA,EAAqBhB;AACvB,SAAA,CAAA;IACF,CAAA,EACA;AAACI,QAAAA,gBAAAA;AAAkBI,QAAAA,aAAAA;AAAeF,QAAAA;AAAK,KAAA,CAAA;IAGzC,OAAO;AAAEH,QAAAA;AAAW,KAAA;AACtB;;;;;;"}
@@ -0,0 +1,53 @@
1
+ import { useCallback } from 'react';
2
+ import { useTracking as useTracking$1 } from '@strapi/admin/strapi-admin';
3
+ import { useAIAvailability } from '@strapi/admin/strapi-admin/ee';
4
+ import { useGetUploadSettingsQuery } from '../services/settings.mjs';
5
+
6
+ /**
7
+ * Media Library version tag stamped on every event fired from the new
8
+ * (`src/future`) Media Library. Legacy events omit it, so analytics can filter
9
+ * new usage from the old plan while both trees emit the SAME event names (see
10
+ * CMS-246). Bump this if a future revamp ever needs its own bucket.
11
+ */ const MEDIA_LIBRARY_VERSION = 'v2';
12
+ /**
13
+ * `location` value for every new Media Library event. The new tree is the
14
+ * standalone Media Library page, which the legacy plan tags `'upload'` (as
15
+ * opposed to `'content-manager'` for the asset-picker modal). Kept identical so
16
+ * migrated events line up with the legacy ones on the `location` axis.
17
+ */ const MEDIA_LIBRARY_LOCATION = 'upload';
18
+ /**
19
+ * Tracking wrapper scoped to the new Media Library. Mirrors the legacy
20
+ * `admin/src/hooks/useTracking` — it forwards to the admin `trackUsage` and
21
+ * injects `isAiMediaLibraryConfigured` when AI is available — and additionally
22
+ * stamps `mediaLibraryVersion` on every event so the migrated plan stays
23
+ * distinguishable from the legacy one.
24
+ *
25
+ * The cast mirrors the legacy wrapper: these two properties are cross-cutting
26
+ * media-tracking metadata that the admin `TrackingEvent` type does not (and
27
+ * should not) enumerate per event.
28
+ */ const useTracking = ()=>{
29
+ const { trackUsage: trackStrapiUsage } = useTracking$1();
30
+ const { data } = useGetUploadSettingsQuery();
31
+ const isAiAvailable = useAIAvailability();
32
+ // Memoised so consumers can safely list `trackUsage` in effect deps (the
33
+ // search-input debounce commit does) without re-firing every render.
34
+ const trackUsage = useCallback((event, properties)=>{
35
+ return trackStrapiUsage(event, {
36
+ ...properties,
37
+ ...isAiAvailable ? {
38
+ isAiMediaLibraryConfigured: Boolean(data?.data?.aiMetadata)
39
+ } : {},
40
+ mediaLibraryVersion: MEDIA_LIBRARY_VERSION
41
+ });
42
+ }, [
43
+ trackStrapiUsage,
44
+ isAiAvailable,
45
+ data
46
+ ]);
47
+ return {
48
+ trackUsage
49
+ };
50
+ };
51
+
52
+ export { MEDIA_LIBRARY_LOCATION, MEDIA_LIBRARY_VERSION, useTracking };
53
+ //# sourceMappingURL=useTracking.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTracking.mjs","sources":["../../../../admin/src/future/hooks/useTracking.ts"],"sourcesContent":["import { useCallback } from 'react';\n\nimport { useTracking as useStrapiTracking, type TrackingEvent } from '@strapi/admin/strapi-admin';\nimport { useAIAvailability } from '@strapi/admin/strapi-admin/ee';\n\nimport { useGetUploadSettingsQuery } from '../services/settings';\n\n/**\n * Media Library version tag stamped on every event fired from the new\n * (`src/future`) Media Library. Legacy events omit it, so analytics can filter\n * new usage from the old plan while both trees emit the SAME event names (see\n * CMS-246). Bump this if a future revamp ever needs its own bucket.\n */\nexport const MEDIA_LIBRARY_VERSION = 'v2';\n\n/**\n * `location` value for every new Media Library event. The new tree is the\n * standalone Media Library page, which the legacy plan tags `'upload'` (as\n * opposed to `'content-manager'` for the asset-picker modal). Kept identical so\n * migrated events line up with the legacy ones on the `location` axis.\n */\nexport const MEDIA_LIBRARY_LOCATION = 'upload';\n\n/**\n * Tracking wrapper scoped to the new Media Library. Mirrors the legacy\n * `admin/src/hooks/useTracking` — it forwards to the admin `trackUsage` and\n * injects `isAiMediaLibraryConfigured` when AI is available — and additionally\n * stamps `mediaLibraryVersion` on every event so the migrated plan stays\n * distinguishable from the legacy one.\n *\n * The cast mirrors the legacy wrapper: these two properties are cross-cutting\n * media-tracking metadata that the admin `TrackingEvent` type does not (and\n * should not) enumerate per event.\n */\nexport const useTracking = () => {\n const { trackUsage: trackStrapiUsage } = useStrapiTracking();\n const { data } = useGetUploadSettingsQuery();\n const isAiAvailable = useAIAvailability();\n\n // Memoised so consumers can safely list `trackUsage` in effect deps (the\n // search-input debounce commit does) without re-firing every render.\n const trackUsage = useCallback(\n <TEvent extends TrackingEvent>(event: TEvent['name'], properties?: TEvent['properties']) => {\n return trackStrapiUsage(event, {\n ...properties,\n ...(isAiAvailable ? { isAiMediaLibraryConfigured: Boolean(data?.data?.aiMetadata) } : {}),\n mediaLibraryVersion: MEDIA_LIBRARY_VERSION,\n } as TEvent['properties']);\n },\n [trackStrapiUsage, isAiAvailable, data]\n );\n\n return { trackUsage };\n};\n"],"names":["MEDIA_LIBRARY_VERSION","MEDIA_LIBRARY_LOCATION","useTracking","trackUsage","trackStrapiUsage","useStrapiTracking","data","useGetUploadSettingsQuery","isAiAvailable","useAIAvailability","useCallback","event","properties","isAiMediaLibraryConfigured","Boolean","aiMetadata","mediaLibraryVersion"],"mappings":";;;;;AAOA;;;;;IAMO,MAAMA,qBAAAA,GAAwB;AAErC;;;;;IAMO,MAAMC,sBAAAA,GAAyB;AAEtC;;;;;;;;;;UAWaC,WAAAA,GAAc,IAAA;AACzB,IAAA,MAAM,EAAEC,UAAAA,EAAYC,gBAAgB,EAAE,GAAGC,aAAAA,EAAAA;IACzC,MAAM,EAAEC,IAAI,EAAE,GAAGC,yBAAAA,EAAAA;AACjB,IAAA,MAAMC,aAAAA,GAAgBC,iBAAAA,EAAAA;;;IAItB,MAAMN,UAAAA,GAAaO,WAAAA,CACjB,CAA+BC,KAAAA,EAAuBC,UAAAA,GAAAA;AACpD,QAAA,OAAOR,iBAAiBO,KAAAA,EAAO;AAC7B,YAAA,GAAGC,UAAU;AACb,YAAA,GAAIJ,aAAAA,GAAgB;gBAAEK,0BAAAA,EAA4BC,OAAAA,CAAQR,MAAMA,IAAAA,EAAMS,UAAAA;AAAY,aAAA,GAAI,EAAE;YACxFC,mBAAAA,EAAqBhB;AACvB,SAAA,CAAA;IACF,CAAA,EACA;AAACI,QAAAA,gBAAAA;AAAkBI,QAAAA,aAAAA;AAAeF,QAAAA;AAAK,KAAA,CAAA;IAGzC,OAAO;AAAEH,QAAAA;AAAW,KAAA;AACtB;;;;"}
@@ -8,9 +8,11 @@ var designSystem = require('@strapi/design-system');
8
8
  var icons = require('@strapi/icons');
9
9
  var reactIntl = require('react-intl');
10
10
  var styledComponents = require('styled-components');
11
+ var typeFromMime = require('../../../utils/typeFromMime.js');
11
12
  var BetaNotice = require('../../components/BetaNotice.js');
12
13
  var useAIMetadataEnabled = require('../../hooks/useAIMetadataEnabled.js');
13
14
  var useMediaLibraryPermissions = require('../../hooks/useMediaLibraryPermissions.js');
15
+ var useTracking = require('../../hooks/useTracking.js');
14
16
  var api = require('../../services/api.js');
15
17
  var folders = require('../../services/folders.js');
16
18
  var settings = require('../../services/settings.js');
@@ -488,8 +490,20 @@ const AssetsPage = ()=>{
488
490
  // dialog. No mime argument: the files are not known until they are picked,
489
491
  // and the server filters the batch on the same allowlist anyway.
490
492
  const isAiMetadataEnabled = useAIMetadataEnabled.useAIMetadataEnabled();
493
+ const { trackUsage } = useTracking.useTracking();
491
494
  const uploadFilesToFolder = async (files, folderId)=>{
492
495
  if (files.length === 0) return;
496
+ // Mirror the legacy `willAddMediaLibraryAssets` payload: the count of files
497
+ // about to upload, broken down by asset type.
498
+ const assetsCountByType = files.reduce((acc, file)=>{
499
+ const type = typeFromMime.typeFromMime(file.type);
500
+ acc[type] = (acc[type] ?? 0) + 1;
501
+ return acc;
502
+ }, {});
503
+ trackUsage('willAddMediaLibraryAssets', {
504
+ location: useTracking.MEDIA_LIBRARY_LOCATION,
505
+ ...assetsCountByType
506
+ });
493
507
  const formData = new FormData();
494
508
  const fileInfoArray = [];
495
509
  files.forEach((file)=>{
@@ -519,6 +533,10 @@ const AssetsPage = ()=>{
519
533
  const handleFileChange = async (e)=>{
520
534
  const files = e.target.files;
521
535
  if (files && files.length > 0) {
536
+ trackUsage('didSelectFile', {
537
+ source: 'computer',
538
+ location: useTracking.MEDIA_LIBRARY_LOCATION
539
+ });
522
540
  await uploadFilesToFolder(Array.from(files), currentFolderId);
523
541
  }
524
542
  e.target.value = '';
@@ -527,9 +545,22 @@ const AssetsPage = ()=>{
527
545
  // Defence in depth: the provider is `disabled` without `assets.create`
528
546
  // (so onDrop won't fire), but guard here too in case it's ever wired live.
529
547
  if (!canCreate) return;
548
+ trackUsage('didSelectFile', {
549
+ source: 'computer',
550
+ location: useTracking.MEDIA_LIBRARY_LOCATION
551
+ });
530
552
  await uploadFilesToFolder(files, currentFolderId);
531
553
  };
532
554
  const handleUrlUpload = async (urls)=>{
555
+ trackUsage('didSelectFile', {
556
+ source: 'url',
557
+ location: useTracking.MEDIA_LIBRARY_LOCATION
558
+ });
559
+ // Counts-by-type aren't available client-side for the URL flow (the server
560
+ // fetches the files), so this fires the event with location only.
561
+ trackUsage('willAddMediaLibraryAssets', {
562
+ location: useTracking.MEDIA_LIBRARY_LOCATION
563
+ });
533
564
  try {
534
565
  await uploadFromUrls({
535
566
  urls,