sanity-plugin-media 4.0.0 → 4.1.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.
package/dist/index.mjs CHANGED
@@ -20,13 +20,12 @@ import { Virtuoso, VirtuosoGrid, GroupedVirtuoso } from "react-virtuoso";
20
20
  import { zodResolver } from "@hookform/resolvers/zod";
21
21
  import { Controller, useForm } from "react-hook-form";
22
22
  import * as z from "zod";
23
- import format from "date-fns/format";
23
+ import { format, formatRelative } from "date-fns";
24
24
  import filesize from "filesize";
25
25
  import copy from "copy-to-clipboard";
26
26
  import { useIntentLink } from "sanity/router";
27
27
  import { FileIcon as FileIcon$1, defaultStyles } from "react-file-icon";
28
28
  import CreatableSelect from "react-select/creatable";
29
- import formatRelative from "date-fns/formatRelative";
30
29
  import { useDropzone } from "react-dropzone";
31
30
  function getDefaultExportFromCjs(x) {
32
31
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x.default : x;
@@ -1258,7 +1257,9 @@ const UPLOADS_ACTIONS = {
1258
1257
  originalFilename,
1259
1258
  size,
1260
1259
  source {
1261
- name
1260
+ name,
1261
+ id,
1262
+ url,
1262
1263
  },
1263
1264
  title,
1264
1265
  url
@@ -2361,15 +2362,21 @@ const Container$1 = styled(Box)(({ $scheme, theme }) => css`
2361
2362
  let creditLineExcludeSources;
2362
2363
  return options?.creditLine?.excludeSources && (creditLineExcludeSources = Array.isArray(options?.creditLine?.excludeSources) ? options.creditLine.excludeSources : [options?.creditLine?.excludeSources]), {
2363
2364
  dropzone: { maxSize: options?.maximumUploadSize },
2365
+ components: {
2366
+ details: options?.components?.details
2367
+ },
2364
2368
  creditLine: {
2365
2369
  enabled: options?.creditLine?.enabled || !1,
2366
2370
  excludeSources: creditLineExcludeSources
2367
- }
2371
+ },
2372
+ directUploads: options?.directUploads ?? !0
2368
2373
  };
2369
2374
  }, [
2370
2375
  options?.creditLine?.enabled,
2376
+ options?.components,
2371
2377
  options?.creditLine?.excludeSources,
2372
- options?.maximumUploadSize
2378
+ options?.maximumUploadSize,
2379
+ options?.directUploads
2373
2380
  ]);
2374
2381
  return /* @__PURE__ */ jsx(ToolOptionsContext.Provider, { value, children });
2375
2382
  }, useToolOptions = () => {
@@ -2828,7 +2835,51 @@ const imageDprUrl = (asset, options) => {
2828
2835
  }
2829
2836
  }
2830
2837
  ) : /* @__PURE__ */ jsx(FileIcon, { extension: asset.extension, width: "50%" });
2831
- }, { radius: themeRadius, space: themeSpace } = studioTheme, reactSelectStyles = (scheme) => ({
2838
+ }, FormSubmitButton = (props) => {
2839
+ const { disabled, isValid, lastUpdated, onClick } = props;
2840
+ let content;
2841
+ return isValid ? lastUpdated ? content = /* @__PURE__ */ jsxs(Fragment, { children: [
2842
+ "Last updated",
2843
+ /* @__PURE__ */ jsx("br", {}),
2844
+ " ",
2845
+ format(new Date(lastUpdated), "PPp")
2846
+ ] }) : content = "No unpublished changes" : content = "There are validation errors that need to be fixed before this document can be published", /* @__PURE__ */ jsx(
2847
+ Tooltip,
2848
+ {
2849
+ animate: !0,
2850
+ content: /* @__PURE__ */ jsx(Box, { padding: 3, style: { maxWidth: "185px" }, children: /* @__PURE__ */ jsx(Text, { muted: !0, size: 1, children: content }) }),
2851
+ disabled: "ontouchstart" in window,
2852
+ placement: "top",
2853
+ portal: !0,
2854
+ children: /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(
2855
+ Button,
2856
+ {
2857
+ disabled,
2858
+ fontSize: 1,
2859
+ onClick,
2860
+ text: "Save and close",
2861
+ tone: "primary"
2862
+ }
2863
+ ) })
2864
+ }
2865
+ );
2866
+ }, Image$1 = styled.img`
2867
+ --checkerboard-color: ${(props) => props.$scheme ? getSchemeColor(props.$scheme, "bg2") : "inherit"};
2868
+
2869
+ display: block;
2870
+ width: 100%;
2871
+ height: 100%;
2872
+ object-fit: contain;
2873
+
2874
+ ${(props) => props.$showCheckerboard && css`
2875
+ background-image: linear-gradient(45deg, var(--checkerboard-color) 25%, transparent 25%),
2876
+ linear-gradient(-45deg, var(--checkerboard-color) 25%, transparent 25%),
2877
+ linear-gradient(45deg, transparent 75%, var(--checkerboard-color) 75%),
2878
+ linear-gradient(-45deg, transparent 75%, var(--checkerboard-color) 75%);
2879
+ background-size: 20px 20px;
2880
+ background-position: 0 0, 0 10px, 10px -10px, -10px 0;
2881
+ `}
2882
+ `, { radius: themeRadius, space: themeSpace } = studioTheme, reactSelectStyles = (scheme) => ({
2832
2883
  control: (styles, { isFocused }) => {
2833
2884
  let boxShadow = "inset 0 0 0 1px var(--card-border-color)";
2834
2885
  return isFocused && (boxShadow = `inset 0 0 0 1px ${getSchemeColor(scheme, "inputEnabledBorder")},
@@ -3052,55 +3103,99 @@ const imageDprUrl = (asset, options) => {
3052
3103
  }
3053
3104
  )
3054
3105
  ] });
3055
- }), FormSubmitButton = (props) => {
3056
- const { disabled, isValid, lastUpdated, onClick } = props;
3057
- let content;
3058
- return isValid ? lastUpdated ? content = /* @__PURE__ */ jsxs(Fragment, { children: [
3059
- "Last updated",
3060
- /* @__PURE__ */ jsx("br", {}),
3061
- " ",
3062
- format(new Date(lastUpdated), "PPp")
3063
- ] }) : content = "No unpublished changes" : content = "There are validation errors that need to be fixed before this document can be published", /* @__PURE__ */ jsx(
3064
- Tooltip,
3065
- {
3066
- animate: !0,
3067
- content: /* @__PURE__ */ jsx(Box, { padding: 3, style: { maxWidth: "185px" }, children: /* @__PURE__ */ jsx(Text, { muted: !0, size: 1, children: content }) }),
3068
- disabled: "ontouchstart" in window,
3069
- placement: "top",
3070
- portal: !0,
3071
- children: /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(
3072
- Button,
3073
- {
3074
- disabled,
3075
- fontSize: 1,
3076
- onClick,
3077
- text: "Save and close",
3078
- tone: "primary"
3079
- }
3080
- ) })
3081
- }
3082
- );
3083
- }, Image$1 = styled.img`
3084
- --checkerboard-color: ${(props) => props.$scheme ? getSchemeColor(props.$scheme, "bg2") : "inherit"};
3085
-
3086
- display: block;
3087
- width: 100%;
3088
- height: 100%;
3089
- object-fit: contain;
3090
-
3091
- ${(props) => props.$showCheckerboard && css`
3092
- background-image: linear-gradient(45deg, var(--checkerboard-color) 25%, transparent 25%),
3093
- linear-gradient(-45deg, var(--checkerboard-color) 25%, transparent 25%),
3094
- linear-gradient(45deg, transparent 75%, var(--checkerboard-color) 75%),
3095
- linear-gradient(-45deg, transparent 75%, var(--checkerboard-color) 75%);
3096
- background-size: 20px 20px;
3097
- background-position: 0 0, 0 10px, 10px -10px, -10px 0;
3098
- `}
3099
- `, DialogAssetEdit = (props) => {
3106
+ });
3107
+ function Details({
3108
+ formUpdating,
3109
+ handleCreateTag,
3110
+ control,
3111
+ errors,
3112
+ register,
3113
+ allTagOptions,
3114
+ assetTagOptions,
3115
+ currentAsset,
3116
+ creditLine
3117
+ }) {
3118
+ return /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
3119
+ /* @__PURE__ */ jsx(
3120
+ FormFieldInputTags,
3121
+ {
3122
+ control,
3123
+ disabled: formUpdating,
3124
+ error: errors?.opt?.media?.tags?.message,
3125
+ label: "Tags",
3126
+ name: "opt.media.tags",
3127
+ onCreateTag: handleCreateTag,
3128
+ options: allTagOptions,
3129
+ placeholder: "Select or create...",
3130
+ value: assetTagOptions
3131
+ }
3132
+ ),
3133
+ /* @__PURE__ */ jsx(
3134
+ FormFieldInputText,
3135
+ {
3136
+ ...register("originalFilename"),
3137
+ disabled: formUpdating,
3138
+ error: errors?.originalFilename?.message,
3139
+ label: "Filename",
3140
+ name: "originalFilename",
3141
+ value: currentAsset?.originalFilename
3142
+ }
3143
+ ),
3144
+ /* @__PURE__ */ jsx(
3145
+ FormFieldInputText,
3146
+ {
3147
+ ...register("title"),
3148
+ disabled: formUpdating,
3149
+ error: errors?.title?.message,
3150
+ label: "Title",
3151
+ name: "title",
3152
+ value: currentAsset?.title
3153
+ }
3154
+ ),
3155
+ /* @__PURE__ */ jsx(
3156
+ FormFieldInputText,
3157
+ {
3158
+ ...register("altText"),
3159
+ disabled: formUpdating,
3160
+ error: errors?.altText?.message,
3161
+ label: "Alt Text",
3162
+ name: "altText",
3163
+ value: currentAsset?.altText
3164
+ }
3165
+ ),
3166
+ /* @__PURE__ */ jsx(
3167
+ FormFieldInputTextarea,
3168
+ {
3169
+ ...register("description"),
3170
+ disabled: formUpdating,
3171
+ error: errors?.description?.message,
3172
+ label: "Description",
3173
+ name: "description",
3174
+ rows: 5,
3175
+ value: currentAsset?.description
3176
+ }
3177
+ ),
3178
+ creditLine?.enabled && /* @__PURE__ */ jsx(
3179
+ FormFieldInputText,
3180
+ {
3181
+ ...register("creditLine"),
3182
+ error: errors?.creditLine?.message,
3183
+ label: "Credit",
3184
+ name: "creditLine",
3185
+ value: currentAsset?.creditLine,
3186
+ disabled: formUpdating || creditLine?.excludeSources?.includes(currentAsset?.source?.name)
3187
+ }
3188
+ )
3189
+ ] });
3190
+ }
3191
+ function renderDefaultDetails(props) {
3192
+ return /* @__PURE__ */ jsx(Details, { ...props });
3193
+ }
3194
+ const DialogAssetEdit = (props) => {
3100
3195
  const {
3101
3196
  children,
3102
3197
  dialog: { assetId, id, lastCreatedTag, lastRemovedTagIds }
3103
- } = props, client = useVersionedClient(), scheme = useColorSchemeValue(), documentStore = useDocumentStore(), dispatch = useDispatch(), assetItem = useTypedSelector((state) => selectAssetById(state, String(assetId))), tags = useTypedSelector(selectTags), assetUpdatedPrev = useRef(void 0), [assetSnapshot, setAssetSnapshot] = useState(assetItem?.asset), [tabSection, setTabSection] = useState("details"), currentAsset = assetItem ? assetItem?.asset : assetSnapshot, allTagOptions = getTagSelectOptions(tags), assetTagOptions = useTypedSelector(selectTagSelectOptions(currentAsset)), { creditLine } = useToolOptions(), generateDefaultValues = useCallback(
3198
+ } = props, client = useVersionedClient(), scheme = useColorSchemeValue(), documentStore = useDocumentStore(), dispatch = useDispatch(), assetItem = useTypedSelector((state) => selectAssetById(state, String(assetId))), tags = useTypedSelector(selectTags), assetUpdatedPrev = useRef(void 0), [assetSnapshot, setAssetSnapshot] = useState(assetItem?.asset), [tabSection, setTabSection] = useState("details"), currentAsset = assetItem ? assetItem?.asset : assetSnapshot, allTagOptions = getTagSelectOptions(tags), assetTagOptions = useTypedSelector(selectTagSelectOptions(currentAsset)), { creditLine, components: { details: CustomDetails } = {} } = useToolOptions(), generateDefaultValues = useCallback(
3104
3199
  (asset) => ({
3105
3200
  altText: asset?.altText || "",
3106
3201
  creditLine: asset?.creditLine || "",
@@ -3173,7 +3268,7 @@ const imageDprUrl = (asset, options) => {
3173
3268
  },
3174
3269
  [assetItem?.asset, dispatch]
3175
3270
  );
3176
- return useEffect(() => {
3271
+ useEffect(() => {
3177
3272
  if (!assetItem?.asset)
3178
3273
  return;
3179
3274
  const subscriptionAsset = client.listen(groq`*[_id == $id]`, { id: assetItem?.asset._id }).subscribe(handleAssetUpdate);
@@ -3192,32 +3287,48 @@ const imageDprUrl = (asset, options) => {
3192
3287
  }
3193
3288
  }, [getValues, lastRemovedTagIds, setValue]), useEffect(() => {
3194
3289
  assetUpdatedPrev.current !== assetItem?.asset._updatedAt && reset(generateDefaultValues(assetItem?.asset)), assetUpdatedPrev.current = assetItem?.asset._updatedAt;
3195
- }, [assetItem?.asset, generateDefaultValues, reset]), currentAsset ? /* @__PURE__ */ jsxs(
3290
+ }, [assetItem?.asset, generateDefaultValues, reset]);
3291
+ const Footer = () => /* @__PURE__ */ jsx(Box, { padding: 3, children: /* @__PURE__ */ jsxs(Flex, { justify: "space-between", children: [
3292
+ /* @__PURE__ */ jsx(
3293
+ Button,
3294
+ {
3295
+ disabled: formUpdating,
3296
+ fontSize: 1,
3297
+ mode: "bleed",
3298
+ onClick: handleDelete,
3299
+ text: "Delete",
3300
+ tone: "critical"
3301
+ }
3302
+ ),
3303
+ /* @__PURE__ */ jsx(
3304
+ FormSubmitButton,
3305
+ {
3306
+ disabled: formUpdating || !isDirty || !isValid,
3307
+ isValid,
3308
+ lastUpdated: currentAsset?._updatedAt,
3309
+ onClick: handleSubmit(onSubmit)
3310
+ }
3311
+ )
3312
+ ] }) });
3313
+ if (!currentAsset)
3314
+ return null;
3315
+ const detailsProps = {
3316
+ control,
3317
+ errors,
3318
+ formUpdating,
3319
+ register,
3320
+ setValue,
3321
+ assetTagOptions,
3322
+ allTagOptions,
3323
+ handleCreateTag,
3324
+ currentAsset,
3325
+ creditLine
3326
+ };
3327
+ return /* @__PURE__ */ jsxs(
3196
3328
  Dialog,
3197
3329
  {
3198
3330
  animate: !0,
3199
- footer: /* @__PURE__ */ jsx(() => /* @__PURE__ */ jsx(Box, { padding: 3, children: /* @__PURE__ */ jsxs(Flex, { justify: "space-between", children: [
3200
- /* @__PURE__ */ jsx(
3201
- Button,
3202
- {
3203
- disabled: formUpdating,
3204
- fontSize: 1,
3205
- mode: "bleed",
3206
- onClick: handleDelete,
3207
- text: "Delete",
3208
- tone: "critical"
3209
- }
3210
- ),
3211
- /* @__PURE__ */ jsx(
3212
- FormSubmitButton,
3213
- {
3214
- disabled: formUpdating || !isDirty || !isValid,
3215
- isValid,
3216
- lastUpdated: currentAsset?._updatedAt,
3217
- onClick: handleSubmit(onSubmit)
3218
- }
3219
- )
3220
- ] }) }), {}),
3331
+ footer: /* @__PURE__ */ jsx(Footer, {}),
3221
3332
  header: "Asset details",
3222
3333
  id,
3223
3334
  onClose: handleClose,
@@ -3262,78 +3373,13 @@ const imageDprUrl = (asset, options) => {
3262
3373
  "aria-labelledby": "details",
3263
3374
  hidden: tabSection !== "details",
3264
3375
  id: "details-panel",
3265
- children: /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
3266
- /* @__PURE__ */ jsx(
3267
- FormFieldInputTags,
3268
- {
3269
- control,
3270
- disabled: formUpdating,
3271
- error: errors?.opt?.media?.tags?.message,
3272
- label: "Tags",
3273
- name: "opt.media.tags",
3274
- onCreateTag: handleCreateTag,
3275
- options: allTagOptions,
3276
- placeholder: "Select or create...",
3277
- value: assetTagOptions
3278
- }
3279
- ),
3280
- /* @__PURE__ */ jsx(
3281
- FormFieldInputText,
3282
- {
3283
- ...register("originalFilename"),
3284
- disabled: formUpdating,
3285
- error: errors?.originalFilename?.message,
3286
- label: "Filename",
3287
- name: "originalFilename",
3288
- value: currentAsset?.originalFilename
3289
- }
3290
- ),
3291
- /* @__PURE__ */ jsx(
3292
- FormFieldInputText,
3293
- {
3294
- ...register("title"),
3295
- disabled: formUpdating,
3296
- error: errors?.title?.message,
3297
- label: "Title",
3298
- name: "title",
3299
- value: currentAsset?.title
3300
- }
3301
- ),
3302
- /* @__PURE__ */ jsx(
3303
- FormFieldInputText,
3304
- {
3305
- ...register("altText"),
3306
- disabled: formUpdating,
3307
- error: errors?.altText?.message,
3308
- label: "Alt Text",
3309
- name: "altText",
3310
- value: currentAsset?.altText
3311
- }
3312
- ),
3313
- /* @__PURE__ */ jsx(
3314
- FormFieldInputTextarea,
3315
- {
3316
- ...register("description"),
3317
- disabled: formUpdating,
3318
- error: errors?.description?.message,
3319
- label: "Description",
3320
- name: "description",
3321
- rows: 5,
3322
- value: currentAsset?.description
3323
- }
3324
- ),
3325
- creditLine?.enabled && /* @__PURE__ */ jsx(
3326
- FormFieldInputText,
3327
- {
3328
- ...register("creditLine"),
3329
- error: errors?.creditLine?.message,
3330
- label: "Credit",
3331
- name: "creditLine",
3332
- value: currentAsset?.creditLine,
3333
- disabled: formUpdating || creditLine?.excludeSources?.includes(currentAsset?.source?.name)
3334
- }
3335
- )
3336
- ] })
3376
+ children: CustomDetails ? /* @__PURE__ */ jsx(
3377
+ CustomDetails,
3378
+ {
3379
+ ...detailsProps,
3380
+ renderDefaultDetails
3381
+ }
3382
+ ) : /* @__PURE__ */ jsx(Details, { ...detailsProps })
3337
3383
  }
3338
3384
  ),
3339
3385
  /* @__PURE__ */ jsx(
@@ -3373,7 +3419,7 @@ const imageDprUrl = (asset, options) => {
3373
3419
  children
3374
3420
  ]
3375
3421
  }
3376
- ) : null;
3422
+ );
3377
3423
  }, DialogConfirm = (props) => {
3378
3424
  const { children, dialog } = props, dispatch = useDispatch(), handleClose = () => {
3379
3425
  dispatch(dialogActions.remove({ id: dialog?.id }));
@@ -3860,7 +3906,7 @@ const imageDprUrl = (asset, options) => {
3860
3906
  throw new Error("useDropzoneActions must be used within an DropzoneDispatchProvider");
3861
3907
  return context;
3862
3908
  }, Header = (props) => {
3863
- const { onClose } = props, { open } = useDropzoneActions(), { onSelect } = useAssetSourceActions(), assetTypes = useTypedSelector((state) => state.assets.assetTypes), selectedDocument = useTypedSelector((state) => state.selected.document);
3909
+ const { onClose } = props, { open } = useDropzoneActions(), { onSelect } = useAssetSourceActions(), assetTypes = useTypedSelector((state) => state.assets.assetTypes), selectedDocument = useTypedSelector((state) => state.selected.document), { directUploads } = useToolOptions();
3864
3910
  return /* @__PURE__ */ jsx(Box, { paddingY: 2, children: /* @__PURE__ */ jsxs(Flex, { align: "center", justify: "space-between", children: [
3865
3911
  /* @__PURE__ */ jsx(Box, { flex: 1, marginX: 3, children: /* @__PURE__ */ jsxs(Inline, { style: { whiteSpace: "nowrap" }, children: [
3866
3912
  /* @__PURE__ */ jsx(Text, { textOverflow: "ellipsis", weight: "semibold", children: /* @__PURE__ */ jsx("span", { children: onSelect ? `Insert ${assetTypes.join(" or ")}` : "Browse Assets" }) }),
@@ -3870,15 +3916,14 @@ const imageDprUrl = (asset, options) => {
3870
3916
  ] }) })
3871
3917
  ] }) }),
3872
3918
  /* @__PURE__ */ jsxs(Flex, { marginX: 2, children: [
3873
- /* @__PURE__ */ jsx(
3919
+ directUploads && /* @__PURE__ */ jsx(
3874
3920
  Button,
3875
3921
  {
3876
3922
  fontSize: 1,
3877
3923
  icon: UploadIcon,
3878
3924
  mode: "bleed",
3879
3925
  onClick: open,
3880
- text: `Upload ${assetTypes.length === 1 ? pluralize(assetTypes[0]) : "assets"}`,
3881
- tone: "primary"
3926
+ text: `Upload ${assetTypes.length === 1 ? pluralize(assetTypes[0]) : "assets"}`
3882
3927
  }
3883
3928
  ),
3884
3929
  onClose && /* @__PURE__ */ jsx(Box, { style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx(
@@ -5346,7 +5391,8 @@ async function filterFiles(fileList) {
5346
5391
  }
5347
5392
  const UploadDropzone = (props) => {
5348
5393
  const { children } = props, {
5349
- dropzone: { maxSize }
5394
+ dropzone: { maxSize },
5395
+ directUploads
5350
5396
  } = useToolOptions(), { onSelect } = useAssetSourceActions(), dispatch = useDispatch(), assetTypes = useTypedSelector((state) => state.assets.assetTypes), isImageAssetType = assetTypes.length === 1 && assetTypes[0] === "image", handleDrop = async (acceptedFiles) => {
5351
5397
  acceptedFiles.forEach(
5352
5398
  (file) => dispatch(
@@ -5387,7 +5433,8 @@ const UploadDropzone = (props) => {
5387
5433
  noDrag: !!onSelect,
5388
5434
  onDrop: handleDrop,
5389
5435
  maxSize,
5390
- onDropRejected: handleDropRejected
5436
+ onDropRejected: handleDropRejected,
5437
+ disabled: !directUploads
5391
5438
  });
5392
5439
  return /* @__PURE__ */ jsx(DropzoneDispatchProvider, { open, children: /* @__PURE__ */ jsxs(UploadContainer, { ...getRootProps(), children: [
5393
5440
  /* @__PURE__ */ jsx("input", { ...getInputProps() }),