sanity-plugin-media 4.1.1 → 4.3.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 (66) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +107 -3
  3. package/dist/index.d.mts +227 -56
  4. package/dist/index.d.ts +227 -56
  5. package/dist/index.js +473 -184
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +476 -187
  8. package/dist/index.mjs.map +1 -1
  9. package/package.json +9 -2
  10. package/src/__tests__/fixtures/createEpicTestStore.ts +27 -0
  11. package/src/__tests__/fixtures/listenMock.ts +9 -0
  12. package/src/__tests__/fixtures/mockSanityClient.ts +84 -0
  13. package/src/__tests__/fixtures/renderWithProviders.tsx +54 -0
  14. package/src/__tests__/fixtures/rootState.ts +27 -0
  15. package/src/__tests__/fixtures/withinDialog.ts +28 -0
  16. package/src/components/AutoTagInputWrapper/index.tsx +82 -0
  17. package/src/components/Browser/Browser.test.tsx +44 -0
  18. package/src/components/Browser/index.tsx +12 -69
  19. package/src/components/Browser/useBrowserInit.ts +126 -0
  20. package/src/components/CardAsset/CardAsset.test.tsx +322 -0
  21. package/src/components/DialogAssetEdit/Details.tsx +123 -44
  22. package/src/components/DialogAssetEdit/DialogAssetEdit.test.tsx +215 -0
  23. package/src/components/DialogAssetEdit/index.tsx +138 -30
  24. package/src/components/DialogTagCreate/DialogTagCreate.test.tsx +120 -0
  25. package/src/components/DialogTagEdit/DialogTagEdit.test.tsx +164 -0
  26. package/src/components/FormBuilderTool/FormBuilderTool.test.tsx +62 -0
  27. package/src/components/FormBuilderTool/index.tsx +1 -1
  28. package/src/components/UploadDropzone/UploadDropzone.test.tsx +39 -0
  29. package/src/contexts/ToolOptionsContext.tsx +9 -3
  30. package/src/formSchema/index.test.ts +55 -0
  31. package/src/formSchema/index.ts +28 -12
  32. package/src/hooks/useVersionedClient.ts +1 -1
  33. package/src/index.ts +4 -1
  34. package/src/modules/assets/deleteAndUpdateEpics.test.ts +86 -0
  35. package/src/modules/assets/fetchEpic.test.ts +72 -0
  36. package/src/modules/assets/reducer.test.ts +90 -0
  37. package/src/modules/assets/tagsAndListenerEpics.test.ts +205 -0
  38. package/src/modules/dialog/epics.test.ts +167 -0
  39. package/src/modules/dialog/reducer.test.ts +184 -0
  40. package/src/modules/notifications/epics.test.ts +373 -0
  41. package/src/modules/notifications/index.ts +24 -4
  42. package/src/modules/notifications/reducer.test.ts +53 -0
  43. package/src/modules/search/index.test.ts +35 -0
  44. package/src/modules/selectors.test.ts +20 -0
  45. package/src/modules/tags/epics.test.ts +95 -0
  46. package/src/modules/tags/index.test.ts +41 -0
  47. package/src/modules/uploads/epics.test.ts +108 -0
  48. package/src/modules/uploads/index.test.ts +58 -0
  49. package/src/operators/checkTagName.test.ts +28 -0
  50. package/src/types/index.ts +25 -7
  51. package/src/utils/applyMediaTags.ts +86 -0
  52. package/src/utils/blocksToText.test.ts +42 -0
  53. package/src/utils/constructFilter.test.ts +119 -0
  54. package/src/utils/generatePreviewBlobUrl.test.ts +69 -0
  55. package/src/utils/getAssetResolution.test.ts +12 -0
  56. package/src/utils/getDocumentAssetIds.test.ts +49 -0
  57. package/src/utils/getSchemeColor.test.ts +11 -0
  58. package/src/utils/getTagSelectOptions.test.ts +43 -0
  59. package/src/utils/getUniqueDocuments.test.ts +25 -0
  60. package/src/utils/imageDprUrl.test.ts +45 -0
  61. package/src/utils/isSupportedAssetType.test.ts +15 -0
  62. package/src/utils/mediaField.ts +72 -0
  63. package/src/utils/sanitizeFormData.test.ts +58 -0
  64. package/src/utils/typeGuards.test.ts +17 -0
  65. package/src/utils/uploadSanityAsset.test.ts +28 -0
  66. package/src/utils/withMaxConcurrency.test.ts +42 -0
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: !0 });
3
- var jsxRuntime = require("react/jsx-runtime"), sanity = require("sanity"), icons = require("@sanity/icons"), ui = require("@sanity/ui"), react = require("react"), groq = require("groq"), reactRedux = require("react-redux"), toolkit = require("@reduxjs/toolkit"), nanoid = require("nanoid"), reduxObservable = require("redux-observable"), rxjs = require("rxjs"), operators$1 = require("rxjs/operators"), uuid = require("@sanity/uuid"), styledComponents = require("styled-components"), pluralize = require("pluralize"), reactNprogress = require("@tanem/react-nprogress"), color = require("@sanity/color"), Select = require("react-select"), reactVirtuoso = require("react-virtuoso"), zod = require("@hookform/resolvers/zod"), reactHookForm = require("react-hook-form"), z = require("zod"), dateFns = require("date-fns"), filesize = require("filesize"), copy = require("copy-to-clipboard"), router = require("sanity/router"), reactFileIcon = require("react-file-icon"), CreatableSelect = require("react-select/creatable"), reactDropzone = require("react-dropzone");
3
+ var jsxRuntime = require("react/jsx-runtime"), sanity = require("sanity"), icons = require("@sanity/icons"), ui = require("@sanity/ui"), react = require("react"), styledComponents = require("styled-components"), reactRedux = require("react-redux"), toolkit = require("@reduxjs/toolkit"), pluralize = require("pluralize"), reduxObservable = require("redux-observable"), rxjs = require("rxjs"), operators$1 = require("rxjs/operators"), groq = require("groq"), nanoid = require("nanoid"), uuid = require("@sanity/uuid"), reactNprogress = require("@tanem/react-nprogress"), color = require("@sanity/color"), Select = require("react-select"), reactVirtuoso = require("react-virtuoso"), zod = require("@hookform/resolvers/zod"), reactHookForm = require("react-hook-form"), z = require("zod"), dateFns = require("date-fns"), filesize = require("filesize"), copy = require("copy-to-clipboard"), router = require("sanity/router"), reactFileIcon = require("react-file-icon"), CreatableSelect = require("react-select/creatable"), reactDropzone = require("react-dropzone");
4
4
  function _interopDefaultCompat(e) {
5
5
  return e && typeof e == "object" && "default" in e ? e : { default: e };
6
6
  }
@@ -19,7 +19,7 @@ function _interopNamespaceCompat(e) {
19
19
  }
20
20
  }), n.default = e, Object.freeze(n);
21
21
  }
22
- var groq__default = /* @__PURE__ */ _interopDefaultCompat(groq), pluralize__default = /* @__PURE__ */ _interopDefaultCompat(pluralize), Select__default = /* @__PURE__ */ _interopDefaultCompat(Select), z__namespace = /* @__PURE__ */ _interopNamespaceCompat(z), filesize__default = /* @__PURE__ */ _interopDefaultCompat(filesize), copy__default = /* @__PURE__ */ _interopDefaultCompat(copy), CreatableSelect__default = /* @__PURE__ */ _interopDefaultCompat(CreatableSelect);
22
+ var pluralize__default = /* @__PURE__ */ _interopDefaultCompat(pluralize), groq__default = /* @__PURE__ */ _interopDefaultCompat(groq), Select__default = /* @__PURE__ */ _interopDefaultCompat(Select), z__namespace = /* @__PURE__ */ _interopNamespaceCompat(z), filesize__default = /* @__PURE__ */ _interopDefaultCompat(filesize), copy__default = /* @__PURE__ */ _interopDefaultCompat(copy), CreatableSelect__default = /* @__PURE__ */ _interopDefaultCompat(CreatableSelect);
23
23
  function getDefaultExportFromCjs(x) {
24
24
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x.default : x;
25
25
  }
@@ -169,7 +169,71 @@ const useKeyPress = (hotkey, onPress) => {
169
169
  return react.useEffect(() => (window.addEventListener("keydown", downHandler), window.addEventListener("keyup", upHandler), () => {
170
170
  window.removeEventListener("keydown", downHandler), window.removeEventListener("keyup", upHandler);
171
171
  }), [downHandler, upHandler]), keyPressed;
172
- }, divider = { type: "divider" }, inputs = {
172
+ }, AssetSourceDispatchContext = react.createContext(void 0), AssetBrowserDispatchProvider = (props) => {
173
+ const { children, onSelect } = props, contextValue = {
174
+ onSelect
175
+ };
176
+ return /* @__PURE__ */ jsxRuntime.jsx(AssetSourceDispatchContext.Provider, { value: contextValue, children });
177
+ }, useAssetSourceActions = () => {
178
+ const context = react.useContext(AssetSourceDispatchContext);
179
+ if (context === void 0)
180
+ throw new Error("useAssetSourceActions must be used within an AssetSourceDispatchProvider");
181
+ return context;
182
+ }, useVersionedClient = () => sanity.useClient({ apiVersion: "2025-10-02" }), customScrollbar = styledComponents.css`
183
+ ::-webkit-scrollbar {
184
+ width: 14px;
185
+ }
186
+
187
+ ::-webkit-scrollbar-thumb {
188
+ border-radius: 10px;
189
+ border: 4px solid rgba(0, 0, 0, 0);
190
+ background: var(--card-border-color);
191
+ background-clip: padding-box;
192
+
193
+ &:hover {
194
+ background: var(--card-muted-fg-color);
195
+ background-clip: padding-box;
196
+ }
197
+ }
198
+ `, GlobalStyle = styledComponents.createGlobalStyle`
199
+ .media__custom-scrollbar {
200
+ ${customScrollbar}
201
+ }
202
+
203
+ // @sanity/ui overrides
204
+
205
+ // Custom scrollbar on Box (used in Dialogs)
206
+ div[data-ui="Box"] {
207
+ ${customScrollbar}
208
+ }
209
+
210
+ // Dialog background color
211
+ div[data-ui="Dialog"] {
212
+ background-color: rgba(15, 17, 18, 0.9);
213
+ }
214
+
215
+ `, useTypedSelector = reactRedux.useSelector, ORDER_DICTIONARY = {
216
+ _createdAt: {
217
+ asc: "Last created: Oldest first",
218
+ desc: "Last created: Newest first"
219
+ },
220
+ _updatedAt: {
221
+ asc: "Last updated: Oldest first",
222
+ desc: "Last updated: Newest first"
223
+ },
224
+ mimeType: {
225
+ asc: "MIME type: A to Z",
226
+ desc: "MIME type: Z to A"
227
+ },
228
+ originalFilename: {
229
+ asc: "File name: A to Z",
230
+ desc: "File name: Z to A"
231
+ },
232
+ size: {
233
+ asc: "File size: Smallest first",
234
+ desc: "File size: Largest first"
235
+ }
236
+ }, getOrderTitle = (field, direction) => ORDER_DICTIONARY[field][direction], divider = { type: "divider" }, inputs = {
173
237
  altText: {
174
238
  assetTypes: ["file", "image"],
175
239
  field: "altText",
@@ -532,38 +596,7 @@ const useKeyPress = (hotkey, onPress) => {
532
596
  ], GRID_TEMPLATE_COLUMNS = {
533
597
  SMALL: "3rem 100px auto 1.5rem",
534
598
  LARGE: "3rem 100px auto 5.5rem 5.5rem 3.5rem 8.5rem 4.75rem 2rem"
535
- }, PANEL_HEIGHT = 32, TAG_DOCUMENT_NAME = "media.tag", TAGS_PANEL_WIDTH = 250, AssetSourceDispatchContext = react.createContext(void 0), AssetBrowserDispatchProvider = (props) => {
536
- const { children, onSelect } = props, contextValue = {
537
- onSelect
538
- };
539
- return /* @__PURE__ */ jsxRuntime.jsx(AssetSourceDispatchContext.Provider, { value: contextValue, children });
540
- }, useAssetSourceActions = () => {
541
- const context = react.useContext(AssetSourceDispatchContext);
542
- if (context === void 0)
543
- throw new Error("useAssetSourceActions must be used within an AssetSourceDispatchProvider");
544
- return context;
545
- }, useVersionedClient = () => sanity.useClient({ apiVersion: "2022-10-01" }), ORDER_DICTIONARY = {
546
- _createdAt: {
547
- asc: "Last created: Oldest first",
548
- desc: "Last created: Newest first"
549
- },
550
- _updatedAt: {
551
- asc: "Last updated: Oldest first",
552
- desc: "Last updated: Newest first"
553
- },
554
- mimeType: {
555
- asc: "MIME type: A to Z",
556
- desc: "MIME type: Z to A"
557
- },
558
- originalFilename: {
559
- asc: "File name: A to Z",
560
- desc: "File name: Z to A"
561
- },
562
- size: {
563
- asc: "File size: Smallest first",
564
- desc: "File size: Largest first"
565
- }
566
- }, getOrderTitle = (field, direction) => ORDER_DICTIONARY[field][direction], debugThrottle = (throttled) => function(source) {
599
+ }, PANEL_HEIGHT = 32, TAG_DOCUMENT_NAME = "media.tag", TAGS_PANEL_WIDTH = 250, debugThrottle = (throttled) => function(source) {
567
600
  return rxjs.iif(
568
601
  () => !!throttled,
569
602
  source.pipe(
@@ -1607,40 +1640,7 @@ const UPLOADS_ACTIONS = {
1607
1640
  (assetsPicked) => assetsPicked.length
1608
1641
  ), assetsActions = { ...assetsSlice.actions };
1609
1642
  var assetsReducer = assetsSlice.reducer;
1610
- const customScrollbar = styledComponents.css`
1611
- ::-webkit-scrollbar {
1612
- width: 14px;
1613
- }
1614
-
1615
- ::-webkit-scrollbar-thumb {
1616
- border-radius: 10px;
1617
- border: 4px solid rgba(0, 0, 0, 0);
1618
- background: var(--card-border-color);
1619
- background-clip: padding-box;
1620
-
1621
- &:hover {
1622
- background: var(--card-muted-fg-color);
1623
- background-clip: padding-box;
1624
- }
1625
- }
1626
- `, GlobalStyle = styledComponents.createGlobalStyle`
1627
- .media__custom-scrollbar {
1628
- ${customScrollbar}
1629
- }
1630
-
1631
- // @sanity/ui overrides
1632
-
1633
- // Custom scrollbar on Box (used in Dialogs)
1634
- div[data-ui="Box"] {
1635
- ${customScrollbar}
1636
- }
1637
-
1638
- // Dialog background color
1639
- div[data-ui="Dialog"] {
1640
- background-color: rgba(15, 17, 18, 0.9);
1641
- }
1642
-
1643
- `, useTypedSelector = reactRedux.useSelector, initialState$4 = {
1643
+ const initialState$4 = {
1644
1644
  items: []
1645
1645
  }, dialogSlice = toolkit.createSlice({
1646
1646
  name: "dialog",
@@ -2361,18 +2361,22 @@ const Container$1 = styledComponents.styled(ui.Box)(({ $scheme, theme }) => styl
2361
2361
  components: {
2362
2362
  details: options?.components?.details
2363
2363
  },
2364
+ createTagsOnUpload: options?.createTagsOnUpload ?? !0,
2364
2365
  creditLine: {
2365
2366
  enabled: options?.creditLine?.enabled || !1,
2366
2367
  excludeSources: creditLineExcludeSources
2367
2368
  },
2368
- directUploads: options?.directUploads ?? !0
2369
+ directUploads: options?.directUploads ?? !0,
2370
+ locales: options?.locales
2369
2371
  };
2370
2372
  }, [
2371
2373
  options?.creditLine?.enabled,
2372
2374
  options?.components,
2375
+ options?.createTagsOnUpload,
2373
2376
  options?.creditLine?.excludeSources,
2374
2377
  options?.maximumUploadSize,
2375
- options?.directUploads
2378
+ options?.directUploads,
2379
+ options?.locales
2376
2380
  ]);
2377
2381
  return /* @__PURE__ */ jsxRuntime.jsx(ToolOptionsContext.Provider, { value, children });
2378
2382
  }, useToolOptions = () => {
@@ -2651,21 +2655,35 @@ const DebugControls = () => {
2651
2655
  ] })
2652
2656
  }
2653
2657
  ) : null;
2654
- }, tagOptionSchema = z__namespace.object({
2658
+ };
2659
+ function localizedStringSchema(locales) {
2660
+ if (!locales || locales.length === 0)
2661
+ return z__namespace.string().trim().optional();
2662
+ const shape = {};
2663
+ for (const locale of locales)
2664
+ shape[locale.id] = z__namespace.string().trim().optional();
2665
+ return z__namespace.object(shape).passthrough();
2666
+ }
2667
+ const tagOptionSchema = z__namespace.object({
2655
2668
  label: z__namespace.string().trim().min(1, { message: "Label cannot be empty" }),
2656
2669
  value: z__namespace.string().trim().min(1, { message: "Value cannot be empty" })
2657
- }), assetFormSchema = z__namespace.object({
2658
- altText: z__namespace.string().trim().optional(),
2659
- creditLine: z__namespace.string().trim().optional(),
2660
- description: z__namespace.string().trim().optional(),
2661
- opt: z__namespace.object({
2662
- media: z__namespace.object({
2663
- tags: z__namespace.array(tagOptionSchema).nullable()
2664
- })
2665
- }),
2666
- originalFilename: z__namespace.string().trim().min(1, { message: "Filename cannot be empty" }),
2667
- title: z__namespace.string().trim().optional()
2668
- }), tagFormSchema = z__namespace.object({
2670
+ });
2671
+ function getAssetFormSchema(locales) {
2672
+ return z__namespace.object({
2673
+ altText: localizedStringSchema(locales),
2674
+ creditLine: localizedStringSchema(locales),
2675
+ description: localizedStringSchema(locales),
2676
+ opt: z__namespace.object({
2677
+ media: z__namespace.object({
2678
+ tags: z__namespace.array(tagOptionSchema).nullable()
2679
+ })
2680
+ }),
2681
+ originalFilename: z__namespace.string().trim().min(1, { message: "Filename cannot be empty" }),
2682
+ title: localizedStringSchema(locales)
2683
+ });
2684
+ }
2685
+ getAssetFormSchema();
2686
+ const tagFormSchema = z__namespace.object({
2669
2687
  name: z__namespace.string().min(1, { message: "Name cannot be empty" })
2670
2688
  });
2671
2689
  function getUniqueDocuments(documents) {
@@ -3100,6 +3118,11 @@ const imageDprUrl = (asset, options) => {
3100
3118
  )
3101
3119
  ] });
3102
3120
  });
3121
+ function toStringField(value) {
3122
+ if (typeof value == "string") return value;
3123
+ if (typeof value == "object" && value !== null)
3124
+ return Object.values(value).find((v) => v) || void 0;
3125
+ }
3103
3126
  function Details({
3104
3127
  formUpdating,
3105
3128
  handleCreateTag,
@@ -3109,8 +3132,10 @@ function Details({
3109
3132
  allTagOptions,
3110
3133
  assetTagOptions,
3111
3134
  currentAsset,
3112
- creditLine
3135
+ creditLine,
3136
+ locales
3113
3137
  }) {
3138
+ const hasLocales = locales && locales.length > 0, [activeLocaleTab, setActiveLocaleTab] = react.useState(0);
3114
3139
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
3115
3140
  /* @__PURE__ */ jsxRuntime.jsx(
3116
3141
  FormFieldInputTags,
@@ -3137,51 +3162,117 @@ function Details({
3137
3162
  value: currentAsset?.originalFilename
3138
3163
  }
3139
3164
  ),
3140
- /* @__PURE__ */ jsxRuntime.jsx(
3141
- FormFieldInputText,
3142
- {
3143
- ...register("title"),
3144
- disabled: formUpdating,
3145
- error: errors?.title?.message,
3146
- label: "Title",
3147
- name: "title",
3148
- value: currentAsset?.title
3149
- }
3150
- ),
3151
- /* @__PURE__ */ jsxRuntime.jsx(
3152
- FormFieldInputText,
3153
- {
3154
- ...register("altText"),
3155
- disabled: formUpdating,
3156
- error: errors?.altText?.message,
3157
- label: "Alt Text",
3158
- name: "altText",
3159
- value: currentAsset?.altText
3160
- }
3161
- ),
3162
- /* @__PURE__ */ jsxRuntime.jsx(
3163
- FormFieldInputTextarea,
3164
- {
3165
- ...register("description"),
3166
- disabled: formUpdating,
3167
- error: errors?.description?.message,
3168
- label: "Description",
3169
- name: "description",
3170
- rows: 5,
3171
- value: currentAsset?.description
3172
- }
3173
- ),
3174
- creditLine?.enabled && /* @__PURE__ */ jsxRuntime.jsx(
3175
- FormFieldInputText,
3176
- {
3177
- ...register("creditLine"),
3178
- error: errors?.creditLine?.message,
3179
- label: "Credit",
3180
- name: "creditLine",
3181
- value: currentAsset?.creditLine,
3182
- disabled: formUpdating || creditLine?.excludeSources?.includes(currentAsset?.source?.name)
3183
- }
3184
- )
3165
+ hasLocales ? /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { marginTop: 2, shadow: 1, padding: 3, radius: 1, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 2, children: [
3166
+ /* @__PURE__ */ jsxRuntime.jsx(ui.TabList, { space: 2, children: locales.map((locale, idx) => /* @__PURE__ */ jsxRuntime.jsx(
3167
+ ui.Tab,
3168
+ {
3169
+ id: `locale-tab-${locale.id}`,
3170
+ "aria-controls": `locale-panel-${locale.id}`,
3171
+ selected: activeLocaleTab === idx,
3172
+ onClick: () => setActiveLocaleTab(idx),
3173
+ label: locale.title
3174
+ },
3175
+ locale.id
3176
+ )) }),
3177
+ locales.map((locale, idx) => /* @__PURE__ */ jsxRuntime.jsx(
3178
+ ui.TabPanel,
3179
+ {
3180
+ id: `locale-panel-${locale.id}`,
3181
+ "aria-labelledby": `locale-tab-${locale.id}`,
3182
+ hidden: activeLocaleTab !== idx,
3183
+ children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
3184
+ /* @__PURE__ */ jsxRuntime.jsx(
3185
+ FormFieldInputText,
3186
+ {
3187
+ ...register(`title.${locale.id}`),
3188
+ disabled: formUpdating,
3189
+ error: errors?.title?.[locale.id]?.message,
3190
+ label: "Title",
3191
+ name: `title.${locale.id}`
3192
+ }
3193
+ ),
3194
+ /* @__PURE__ */ jsxRuntime.jsx(
3195
+ FormFieldInputText,
3196
+ {
3197
+ ...register(`altText.${locale.id}`),
3198
+ disabled: formUpdating,
3199
+ error: errors?.altText?.[locale.id]?.message,
3200
+ label: "Alt Text",
3201
+ name: `altText.${locale.id}`
3202
+ }
3203
+ ),
3204
+ /* @__PURE__ */ jsxRuntime.jsx(
3205
+ FormFieldInputTextarea,
3206
+ {
3207
+ ...register(`description.${locale.id}`),
3208
+ disabled: formUpdating,
3209
+ error: errors?.description?.[locale.id]?.message,
3210
+ label: "Description",
3211
+ name: `description.${locale.id}`,
3212
+ rows: 5
3213
+ }
3214
+ ),
3215
+ creditLine?.enabled && /* @__PURE__ */ jsxRuntime.jsx(
3216
+ FormFieldInputText,
3217
+ {
3218
+ ...register(`creditLine.${locale.id}`),
3219
+ error: errors?.creditLine?.[locale.id]?.message,
3220
+ label: "Credit",
3221
+ name: `creditLine.${locale.id}`,
3222
+ disabled: formUpdating || creditLine?.excludeSources?.includes(currentAsset?.source?.name)
3223
+ }
3224
+ )
3225
+ ] })
3226
+ },
3227
+ locale.id
3228
+ ))
3229
+ ] }) }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3230
+ /* @__PURE__ */ jsxRuntime.jsx(
3231
+ FormFieldInputText,
3232
+ {
3233
+ ...register("title"),
3234
+ disabled: formUpdating,
3235
+ error: errors?.title?.message,
3236
+ label: "Title",
3237
+ name: "title",
3238
+ value: toStringField(currentAsset?.title)
3239
+ }
3240
+ ),
3241
+ /* @__PURE__ */ jsxRuntime.jsx(
3242
+ FormFieldInputText,
3243
+ {
3244
+ ...register("altText"),
3245
+ disabled: formUpdating,
3246
+ error: errors?.altText?.message,
3247
+ label: "Alt Text",
3248
+ name: "altText",
3249
+ value: toStringField(currentAsset?.altText)
3250
+ }
3251
+ ),
3252
+ /* @__PURE__ */ jsxRuntime.jsx(
3253
+ FormFieldInputTextarea,
3254
+ {
3255
+ ...register("description"),
3256
+ disabled: formUpdating,
3257
+ error: errors?.description?.message,
3258
+ label: "Description",
3259
+ name: "description",
3260
+ rows: 5,
3261
+ value: toStringField(currentAsset?.description)
3262
+ }
3263
+ ),
3264
+ creditLine?.enabled && /* @__PURE__ */ jsxRuntime.jsx(
3265
+ FormFieldInputText,
3266
+ {
3267
+ ...register("creditLine"),
3268
+ error: errors?.creditLine?.message,
3269
+ label: "Credit",
3270
+ name: "creditLine",
3271
+ value: toStringField(currentAsset?.creditLine),
3272
+ disabled: formUpdating || creditLine?.excludeSources?.includes(currentAsset?.source?.name)
3273
+ }
3274
+ )
3275
+ ] })
3185
3276
  ] });
3186
3277
  }
3187
3278
  function renderDefaultDetails(props) {
@@ -3191,16 +3282,37 @@ const DialogAssetEdit = (props) => {
3191
3282
  const {
3192
3283
  children,
3193
3284
  dialog: { assetId, id, lastCreatedTag, lastRemovedTagIds }
3194
- } = props, client = useVersionedClient(), scheme = sanity.useColorSchemeValue(), documentStore = sanity.useDocumentStore(), dispatch = reactRedux.useDispatch(), assetItem = useTypedSelector((state) => selectAssetById(state, String(assetId))), tags = useTypedSelector(selectTags), assetUpdatedPrev = react.useRef(void 0), [assetSnapshot, setAssetSnapshot] = react.useState(assetItem?.asset), [tabSection, setTabSection] = react.useState("details"), currentAsset = assetItem ? assetItem?.asset : assetSnapshot, allTagOptions = getTagSelectOptions(tags), assetTagOptions = useTypedSelector(selectTagSelectOptions(currentAsset)), { creditLine, components: { details: CustomDetails } = {} } = useToolOptions(), generateDefaultValues = react.useCallback(
3195
- (asset) => ({
3196
- altText: asset?.altText || "",
3197
- creditLine: asset?.creditLine || "",
3198
- description: asset?.description || "",
3199
- originalFilename: asset?.originalFilename || "",
3200
- opt: { media: { tags: assetTagOptions } },
3201
- title: asset?.title || ""
3202
- }),
3203
- [assetTagOptions]
3285
+ } = props, client = useVersionedClient(), scheme = sanity.useColorSchemeValue(), documentStore = sanity.useDocumentStore(), dispatch = reactRedux.useDispatch(), assetItem = useTypedSelector((state) => selectAssetById(state, String(assetId))), tags = useTypedSelector(selectTags), assetUpdatedPrev = react.useRef(void 0), [assetSnapshot, setAssetSnapshot] = react.useState(assetItem?.asset), [tabSection, setTabSection] = react.useState("details"), currentAsset = assetItem ? assetItem?.asset : assetSnapshot, allTagOptions = getTagSelectOptions(tags), assetTagOptions = useTypedSelector(selectTagSelectOptions(currentAsset)), { creditLine, components: { details: CustomDetails } = {}, locales } = useToolOptions(), generateDefaultValues = react.useCallback(
3286
+ (asset) => {
3287
+ if (locales && locales.length > 0) {
3288
+ const makeLocaleObj = (field) => {
3289
+ const obj = {};
3290
+ for (let i = 0; i < locales.length; i++) {
3291
+ const locale = locales[i];
3292
+ typeof field == "object" && field && field[locale.id] ? obj[locale.id] = field[locale.id] : typeof field == "string" ? obj[locale.id] = i === 0 ? field : "" : obj[locale.id] = "";
3293
+ }
3294
+ return obj;
3295
+ };
3296
+ return {
3297
+ altText: makeLocaleObj(asset?.altText),
3298
+ creditLine: makeLocaleObj(asset?.creditLine),
3299
+ description: makeLocaleObj(asset?.description),
3300
+ originalFilename: asset?.originalFilename || "",
3301
+ opt: { media: { tags: assetTagOptions } },
3302
+ title: makeLocaleObj(asset?.title)
3303
+ };
3304
+ }
3305
+ const flattenField = (field) => typeof field == "string" ? field : typeof field == "object" && field !== null && Object.values(field).find((v) => v) || "";
3306
+ return {
3307
+ altText: flattenField(asset?.altText),
3308
+ creditLine: flattenField(asset?.creditLine),
3309
+ description: flattenField(asset?.description),
3310
+ originalFilename: asset?.originalFilename || "",
3311
+ opt: { media: { tags: assetTagOptions } },
3312
+ title: flattenField(asset?.title)
3313
+ };
3314
+ },
3315
+ [assetTagOptions, locales]
3204
3316
  ), {
3205
3317
  control,
3206
3318
  // Read the formState before render to subscribe the form state through Proxy
@@ -3213,7 +3325,7 @@ const DialogAssetEdit = (props) => {
3213
3325
  } = reactHookForm.useForm({
3214
3326
  defaultValues: generateDefaultValues(assetItem?.asset),
3215
3327
  mode: "onChange",
3216
- resolver: zod.zodResolver(assetFormSchema)
3328
+ resolver: zod.zodResolver(getAssetFormSchema(locales))
3217
3329
  }), formUpdating = !assetItem || assetItem?.updating, handleClose = react.useCallback(() => {
3218
3330
  dispatch(dialogActions.remove({ id }));
3219
3331
  }, [dispatch, id]), handleDelete = react.useCallback(() => {
@@ -3236,7 +3348,39 @@ const DialogAssetEdit = (props) => {
3236
3348
  );
3237
3349
  },
3238
3350
  [currentAsset?._id, dispatch]
3239
- ), onSubmit = react.useCallback(
3351
+ ), hasOrphanedLocales = react.useMemo(() => {
3352
+ if (!currentAsset) return !1;
3353
+ const isLocaleObj = (v) => typeof v == "object" && v !== null && !Array.isArray(v), fields = [
3354
+ currentAsset.title,
3355
+ currentAsset.altText,
3356
+ currentAsset.description,
3357
+ ...currentAsset._type === "sanity.imageAsset" ? [currentAsset.creditLine] : []
3358
+ ];
3359
+ if (!fields.some((f) => isLocaleObj(f))) return !1;
3360
+ if (!locales || locales.length === 0) return !0;
3361
+ const configuredIds = new Set(locales.map((l) => l.id));
3362
+ return fields.some((f) => isLocaleObj(f) ? Object.keys(f).some((k) => !configuredIds.has(k)) : !1);
3363
+ }, [currentAsset, locales]), handleCleanupLocales = react.useCallback(async () => {
3364
+ if (!currentAsset) return;
3365
+ const cleanField = (field) => {
3366
+ if (typeof field != "object" || field === null || Array.isArray(field)) return field;
3367
+ const obj = field;
3368
+ if (!locales || locales.length === 0)
3369
+ return Object.keys(obj).sort().map((k) => obj[k]).find((v) => v) || "";
3370
+ const configuredIds = new Set(locales.map((l) => l.id)), cleaned = {};
3371
+ for (const [key, val] of Object.entries(obj))
3372
+ configuredIds.has(key) && (cleaned[key] = val);
3373
+ return cleaned;
3374
+ };
3375
+ await client.patch(currentAsset._id).set({
3376
+ title: cleanField(currentAsset.title),
3377
+ altText: cleanField(currentAsset.altText),
3378
+ description: cleanField(currentAsset.description),
3379
+ ...currentAsset._type === "sanity.imageAsset" && {
3380
+ creditLine: cleanField(currentAsset.creditLine)
3381
+ }
3382
+ }).commit();
3383
+ }, [client, currentAsset, locales]), onSubmit = react.useCallback(
3240
3384
  (formData) => {
3241
3385
  if (!assetItem?.asset)
3242
3386
  return;
@@ -3284,27 +3428,42 @@ const DialogAssetEdit = (props) => {
3284
3428
  }, [getValues, lastRemovedTagIds, setValue]), react.useEffect(() => {
3285
3429
  assetUpdatedPrev.current !== assetItem?.asset._updatedAt && reset(generateDefaultValues(assetItem?.asset)), assetUpdatedPrev.current = assetItem?.asset._updatedAt;
3286
3430
  }, [assetItem?.asset, generateDefaultValues, reset]);
3287
- const Footer = () => /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { padding: 3, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { justify: "space-between", children: [
3288
- /* @__PURE__ */ jsxRuntime.jsx(
3289
- ui.Button,
3290
- {
3291
- disabled: formUpdating,
3292
- fontSize: 1,
3293
- mode: "bleed",
3294
- onClick: handleDelete,
3295
- text: "Delete",
3296
- tone: "critical"
3297
- }
3298
- ),
3299
- /* @__PURE__ */ jsxRuntime.jsx(
3300
- FormSubmitButton,
3301
- {
3302
- disabled: formUpdating || !isDirty || !isValid,
3303
- isValid,
3304
- lastUpdated: currentAsset?._updatedAt,
3305
- onClick: handleSubmit(onSubmit)
3306
- }
3307
- )
3431
+ const Footer = () => /* @__PURE__ */ jsxRuntime.jsx(ui.Box, { padding: 3, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 3, children: [
3432
+ hasOrphanedLocales && /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { padding: 3, radius: 2, shadow: 1, tone: "caution", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { align: "center", justify: "space-between", gap: 3, children: [
3433
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 1, children: "This asset has localized fields that are no longer configured. Clean them up to avoid validation errors." }),
3434
+ /* @__PURE__ */ jsxRuntime.jsx(
3435
+ ui.Button,
3436
+ {
3437
+ fontSize: 1,
3438
+ mode: "ghost",
3439
+ onClick: handleCleanupLocales,
3440
+ text: "Cleanup localized fields",
3441
+ tone: "caution"
3442
+ }
3443
+ )
3444
+ ] }) }),
3445
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { justify: "space-between", children: [
3446
+ /* @__PURE__ */ jsxRuntime.jsx(
3447
+ ui.Button,
3448
+ {
3449
+ disabled: formUpdating,
3450
+ fontSize: 1,
3451
+ mode: "bleed",
3452
+ onClick: handleDelete,
3453
+ text: "Delete",
3454
+ tone: "critical"
3455
+ }
3456
+ ),
3457
+ /* @__PURE__ */ jsxRuntime.jsx(
3458
+ FormSubmitButton,
3459
+ {
3460
+ disabled: formUpdating || !isDirty || !isValid || hasOrphanedLocales,
3461
+ isValid,
3462
+ lastUpdated: currentAsset?._updatedAt,
3463
+ onClick: handleSubmit(onSubmit)
3464
+ }
3465
+ )
3466
+ ] })
3308
3467
  ] }) });
3309
3468
  if (!currentAsset)
3310
3469
  return null;
@@ -3318,7 +3477,8 @@ const DialogAssetEdit = (props) => {
3318
3477
  allTagOptions,
3319
3478
  handleCreateTag,
3320
3479
  currentAsset,
3321
- creditLine
3480
+ creditLine,
3481
+ locales
3322
3482
  };
3323
3483
  return /* @__PURE__ */ jsxRuntime.jsxs(
3324
3484
  Dialog,
@@ -5104,6 +5264,9 @@ const TableRowUpload = (props) => {
5104
5264
  reducers: {}
5105
5265
  });
5106
5266
  var selectedReducer = selectedSlice.reducer;
5267
+ function messageFromGenericErrorPayload(payload) {
5268
+ return !payload || typeof payload != "object" ? "Unknown error" : "error" in payload && payload.error && typeof payload.error == "object" && payload.error !== null && "message" in payload.error ? String(payload.error.message) : "message" in payload && typeof payload.message == "string" ? String(payload.message) : "Unknown error";
5269
+ }
5107
5270
  const initialState = {
5108
5271
  items: []
5109
5272
  }, notificationsSlice = toolkit.createSlice({
@@ -5190,11 +5353,11 @@ const initialState = {
5190
5353
  uploadsActions.uploadError.type
5191
5354
  ),
5192
5355
  operators$1.mergeMap((action) => {
5193
- const error = action.payload?.error;
5356
+ const title = `An error occurred: ${messageFromGenericErrorPayload(action.payload)}`;
5194
5357
  return rxjs.of(
5195
5358
  notificationsSlice.actions.add({
5196
5359
  status: "error",
5197
- title: `An error occured: ${error.message}`
5360
+ title
5198
5361
  })
5199
5362
  );
5200
5363
  })
@@ -5442,24 +5605,81 @@ const UploadDropzone = (props) => {
5442
5605
  isDragActive && /* @__PURE__ */ jsxRuntime.jsx(DragActiveContainer, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, { direction: "column", justify: "center", style: { color: color.white.hex }, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: 3, style: { color: "inherit" }, children: "Drop files to upload" }) }) }),
5443
5606
  children
5444
5607
  ] }) });
5445
- }, BrowserContent = ({ onClose }) => {
5446
- const client = useVersionedClient(), [portalElement, setPortalElement] = react.useState(null), dispatch = reactRedux.useDispatch();
5447
- return react.useEffect(() => {
5448
- const handleAssetUpdate = (update) => {
5449
- const { documentId, result, transition } = update;
5450
- transition === "appear" && dispatch(assetsActions.listenerCreateQueue({ asset: result })), transition === "disappear" && dispatch(assetsActions.listenerDeleteQueue({ assetId: documentId })), transition === "update" && dispatch(assetsActions.listenerUpdateQueue({ asset: result }));
5451
- }, handleTagUpdate = (update) => {
5452
- const { documentId, result, transition } = update;
5453
- transition === "appear" && dispatch(tagsActions.listenerCreateQueue({ tag: result })), transition === "disappear" && dispatch(tagsActions.listenerDeleteQueue({ tagId: documentId })), transition === "update" && dispatch(tagsActions.listenerUpdateQueue({ tag: result }));
5454
- };
5455
- dispatch(assetsActions.loadPageIndex({ pageIndex: 0 })), dispatch(tagsActions.fetchRequest());
5456
- const subscriptionAsset = client.listen(
5608
+ };
5609
+ function getMediaTagNames(schemaType) {
5610
+ const mediaTags = schemaType?.options?.mediaTags;
5611
+ if (!mediaTags?.length) return [];
5612
+ const unique = new Set(
5613
+ mediaTags.map((t) => t?.trim()).filter((t) => !!t?.length)
5614
+ );
5615
+ return Array.from(unique);
5616
+ }
5617
+ function createAssetHandler(dispatch) {
5618
+ return (update) => {
5619
+ const { documentId, result, transition } = update;
5620
+ switch (transition) {
5621
+ case "appear":
5622
+ dispatch(assetsActions.listenerCreateQueue({ asset: result }));
5623
+ break;
5624
+ case "disappear":
5625
+ dispatch(assetsActions.listenerDeleteQueue({ assetId: documentId }));
5626
+ break;
5627
+ case "update":
5628
+ dispatch(assetsActions.listenerUpdateQueue({ asset: result }));
5629
+ break;
5630
+ }
5631
+ };
5632
+ }
5633
+ function createTagHandler(dispatch) {
5634
+ return (update) => {
5635
+ const { documentId, result, transition } = update;
5636
+ switch (transition) {
5637
+ case "appear":
5638
+ dispatch(tagsActions.listenerCreateQueue({ tag: result }));
5639
+ break;
5640
+ case "disappear":
5641
+ dispatch(tagsActions.listenerDeleteQueue({ tagId: documentId }));
5642
+ break;
5643
+ case "update":
5644
+ dispatch(tagsActions.listenerUpdateQueue({ tag: result }));
5645
+ break;
5646
+ }
5647
+ };
5648
+ }
5649
+ function useBrowserInit(client, schemaType) {
5650
+ const dispatch = reactRedux.useDispatch(), tagsByIds = reactRedux.useSelector((state) => state.tags.byIds), tagsFetchCount = reactRedux.useSelector((state) => state.tags.fetchCount), tagNames = getMediaTagNames(schemaType), hasMediaTags = tagNames.length > 0;
5651
+ react.useEffect(() => {
5652
+ hasMediaTags || dispatch(searchActions.facetsClear()), dispatch(tagsActions.fetchRequest());
5653
+ const assetSubscription = client.listen(
5457
5654
  groq__default.default`*[_type in ["sanity.fileAsset", "sanity.imageAsset"] && !(_id in path("drafts.**"))]`
5458
- ).subscribe(handleAssetUpdate), subscriptionTag = client.listen(groq__default.default`*[_type == "${TAG_DOCUMENT_NAME}" && !(_id in path("drafts.**"))]`).subscribe(handleTagUpdate);
5655
+ ).subscribe(createAssetHandler(dispatch)), tagSubscription = client.listen(groq__default.default`*[_type == "${TAG_DOCUMENT_NAME}" && !(_id in path("drafts.**"))]`).subscribe(createTagHandler(dispatch));
5459
5656
  return () => {
5460
- subscriptionAsset?.unsubscribe(), subscriptionTag?.unsubscribe();
5657
+ assetSubscription.unsubscribe(), tagSubscription.unsubscribe();
5461
5658
  };
5462
- }, [client, dispatch]), /* @__PURE__ */ jsxRuntime.jsx(ui.PortalProvider, { element: portalElement, children: /* @__PURE__ */ jsxRuntime.jsxs(UploadDropzone, { children: [
5659
+ }, [client, dispatch, hasMediaTags]), react.useEffect(() => {
5660
+ if (!hasMediaTags || tagsFetchCount < 0) return;
5661
+ const tagFacetInput = inputs.tag;
5662
+ if (tagFacetInput.type !== "searchable") return;
5663
+ const resolvedTags = tagNames.map((name) => Object.values(tagsByIds).find((item) => item.tag.name.current === name)).filter((item) => !!item);
5664
+ dispatch(searchActions.facetsClear());
5665
+ for (const tagItem of resolvedTags)
5666
+ dispatch(
5667
+ searchActions.facetsAdd({
5668
+ facet: {
5669
+ ...tagFacetInput,
5670
+ operatorType: "references",
5671
+ value: { label: tagItem.tag.name.current, value: tagItem.tag._id }
5672
+ }
5673
+ })
5674
+ );
5675
+ }, [tagsFetchCount, hasMediaTags]);
5676
+ }
5677
+ const BrowserContent = ({
5678
+ onClose,
5679
+ schemaType
5680
+ }) => {
5681
+ const client = useVersionedClient(), [portalElement, setPortalElement] = react.useState(null);
5682
+ return useBrowserInit(client, schemaType), /* @__PURE__ */ jsxRuntime.jsx(ui.PortalProvider, { element: portalElement, children: /* @__PURE__ */ jsxRuntime.jsxs(UploadDropzone, { children: [
5463
5683
  /* @__PURE__ */ jsxRuntime.jsx(Dialogs, {}),
5464
5684
  /* @__PURE__ */ jsxRuntime.jsx(Notifications, {}),
5465
5685
  /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { display: "flex", height: "fill", ref: setPortalElement, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, { direction: "column", flex: 1, children: [
@@ -5486,7 +5706,7 @@ const UploadDropzone = (props) => {
5486
5706
  selectedAssets: props?.selectedAssets,
5487
5707
  children: /* @__PURE__ */ jsxRuntime.jsxs(AssetBrowserDispatchProvider, { onSelect: props?.onSelect, children: [
5488
5708
  /* @__PURE__ */ jsxRuntime.jsx(GlobalStyle, {}),
5489
- /* @__PURE__ */ jsxRuntime.jsx(BrowserContent, { onClose: props?.onClose })
5709
+ /* @__PURE__ */ jsxRuntime.jsx(BrowserContent, { onClose: props?.onClose, schemaType: props?.schemaType })
5490
5710
  ] })
5491
5711
  }
5492
5712
  );
@@ -5513,7 +5733,7 @@ const UploadDropzone = (props) => {
5513
5733
  width: "100%",
5514
5734
  zIndex
5515
5735
  },
5516
- children: /* @__PURE__ */ jsxRuntime.jsx(Browser, { document: currentDocument, ...props })
5736
+ children: /* @__PURE__ */ jsxRuntime.jsx(Browser, { document: currentDocument, schemaType: props.schemaType, ...props })
5517
5737
  }
5518
5738
  ) }) });
5519
5739
  }, useRootPortalElement = () => {
@@ -5578,7 +5798,76 @@ const plugin = {
5578
5798
  types: [mediaTag]
5579
5799
  },
5580
5800
  tools: (prev) => [...prev, tool]
5581
- }));
5801
+ })), pendingByAsset = /* @__PURE__ */ new Map();
5802
+ function applyMediaTags(options) {
5803
+ const { assetId } = options, chain = (pendingByAsset.get(assetId) ?? Promise.resolve()).then(
5804
+ () => doApplyMediaTags(options)
5805
+ ), cleanup = chain.catch(() => {
5806
+ }).finally(() => {
5807
+ pendingByAsset.get(assetId) === cleanup && pendingByAsset.delete(assetId);
5808
+ });
5809
+ return pendingByAsset.set(assetId, cleanup), chain;
5810
+ }
5811
+ async function doApplyMediaTags({
5812
+ client,
5813
+ assetId,
5814
+ mediaTags,
5815
+ createTagsOnUpload = !0
5816
+ }) {
5817
+ if (!mediaTags || mediaTags.length === 0) return;
5818
+ const validTags = (await Promise.all(
5819
+ mediaTags.map(async (tagName) => await client.fetch(
5820
+ groq__default.default`*[_type == "${TAG_DOCUMENT_NAME}" && name.current == $tagName][0]`,
5821
+ { tagName }
5822
+ ) || (createTagsOnUpload ? await client.create({
5823
+ _type: TAG_DOCUMENT_NAME,
5824
+ name: { _type: "slug", current: tagName }
5825
+ }) : null))
5826
+ )).filter((tag) => tag !== null);
5827
+ if (validTags.length === 0) return;
5828
+ const existing = await client.fetch(
5829
+ groq__default.default`*[_id == $assetId][0]{'tagIds': opt.media.tags[]._ref}`,
5830
+ { assetId },
5831
+ { useCdn: !1 }
5832
+ // bypass CDN cache so we see the latest committed tag refs
5833
+ ), existingIds = new Set(existing?.tagIds ?? []), tagReferences = validTags.filter((tag) => !existingIds.has(tag._id)).map((tag) => ({
5834
+ _key: nanoid.nanoid(),
5835
+ _ref: tag._id,
5836
+ _type: "reference",
5837
+ _weak: !0
5838
+ }));
5839
+ tagReferences.length !== 0 && await client.patch(assetId).setIfMissing({ opt: {} }).setIfMissing({ "opt.media": {} }).setIfMissing({ "opt.media.tags": [] }).append("opt.media.tags", tagReferences).commit();
5840
+ }
5841
+ function AutoTagInput(props) {
5842
+ const { renderDefault, schemaType, value, mediaTags: mediaTagsProp } = props, toast = ui.useToast(), mediaTags = mediaTagsProp ?? schemaType?.options?.mediaTags, client = useVersionedClient(), { createTagsOnUpload } = useToolOptions(), prevAssetRef = react.useRef(void 0), isInitialMount = react.useRef(!0), currentAssetRef = value?.asset?._ref;
5843
+ return react.useEffect(() => {
5844
+ if (isInitialMount.current) {
5845
+ isInitialMount.current = !1, prevAssetRef.current = currentAssetRef;
5846
+ return;
5847
+ }
5848
+ const previousRef = prevAssetRef.current;
5849
+ prevAssetRef.current = currentAssetRef, !(!mediaTags?.length || !currentAssetRef || currentAssetRef === previousRef) && applyMediaTags({
5850
+ client,
5851
+ assetId: currentAssetRef,
5852
+ mediaTags,
5853
+ createTagsOnUpload
5854
+ }).catch((err) => {
5855
+ console.error("[sanity-plugin-media] Failed to apply auto-tags:", err);
5856
+ const label = mediaTags.length === 1 ? "tag" : "tags";
5857
+ toast.push({ closable: !0, status: "error", title: `Failed to apply the media ${label} ${mediaTags.join(", ")}` });
5858
+ });
5859
+ }, [currentAssetRef, mediaTags, client, createTagsOnUpload]), renderDefault(props);
5860
+ }
5861
+ function mediaField(config) {
5862
+ const { mediaTags, options, components, ...rest } = config;
5863
+ return {
5864
+ ...rest,
5865
+ options: { ...options, mediaTags },
5866
+ components: { ...components, input: AutoTagInput }
5867
+ };
5868
+ }
5869
+ exports.AutoTagInput = AutoTagInput;
5582
5870
  exports.media = media;
5583
5871
  exports.mediaAssetSource = mediaAssetSource;
5872
+ exports.mediaField = mediaField;
5584
5873
  //# sourceMappingURL=index.js.map