sanity-plugin-media
Version:
This version of `sanity-plugin-media` is for Sanity Studio V3.
1,458 lines • 218 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: !0 });
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"), format = require("date-fns/format"), filesize = require("filesize"), copy = require("copy-to-clipboard"), router = require("sanity/router"), reactFileIcon = require("react-file-icon"), CreatableSelect = require("react-select/creatable"), formatRelative = require("date-fns/formatRelative"), reactDropzone = require("react-dropzone");
function _interopDefaultCompat(e) {
return e && typeof e == "object" && "default" in e ? e : { default: e };
}
function _interopNamespaceCompat(e) {
if (e && typeof e == "object" && "default" in e) return e;
var n = /* @__PURE__ */ Object.create(null);
return e && Object.keys(e).forEach(function(k) {
if (k !== "default") {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: !0,
get: function() {
return e[k];
}
});
}
}), n.default = e, Object.freeze(n);
}
var groq__default = /* @__PURE__ */ _interopDefaultCompat(groq), pluralize__default = /* @__PURE__ */ _interopDefaultCompat(pluralize), Select__default = /* @__PURE__ */ _interopDefaultCompat(Select), z__namespace = /* @__PURE__ */ _interopNamespaceCompat(z), format__default = /* @__PURE__ */ _interopDefaultCompat(format), filesize__default = /* @__PURE__ */ _interopDefaultCompat(filesize), copy__default = /* @__PURE__ */ _interopDefaultCompat(copy), CreatableSelect__default = /* @__PURE__ */ _interopDefaultCompat(CreatableSelect), formatRelative__default = /* @__PURE__ */ _interopDefaultCompat(formatRelative);
function getDefaultExportFromCjs(x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x.default : x;
}
var lib = {}, hasRequiredLib;
function requireLib() {
if (hasRequiredLib) return lib;
hasRequiredLib = 1, Object.defineProperty(lib, "__esModule", {
value: !0
});
for (var IS_MAC = typeof window < "u" && /Mac|iPod|iPhone|iPad/.test(window.navigator.platform), MODIFIERS = {
alt: "altKey",
control: "ctrlKey",
meta: "metaKey",
shift: "shiftKey"
}, ALIASES = {
add: "+",
break: "pause",
cmd: "meta",
command: "meta",
ctl: "control",
ctrl: "control",
del: "delete",
down: "arrowdown",
esc: "escape",
ins: "insert",
left: "arrowleft",
mod: IS_MAC ? "meta" : "control",
opt: "alt",
option: "alt",
return: "enter",
right: "arrowright",
space: " ",
spacebar: " ",
up: "arrowup",
win: "meta",
windows: "meta"
}, CODES = {
backspace: 8,
tab: 9,
enter: 13,
shift: 16,
control: 17,
alt: 18,
pause: 19,
capslock: 20,
escape: 27,
" ": 32,
pageup: 33,
pagedown: 34,
end: 35,
home: 36,
arrowleft: 37,
arrowup: 38,
arrowright: 39,
arrowdown: 40,
insert: 45,
delete: 46,
meta: 91,
numlock: 144,
scrolllock: 145,
";": 186,
"=": 187,
",": 188,
"-": 189,
".": 190,
"/": 191,
"`": 192,
"[": 219,
"\\": 220,
"]": 221,
"'": 222
}, f = 1; f < 20; f++)
CODES["f" + f] = 111 + f;
function isHotkey2(hotkey, options, event) {
options && !("byKey" in options) && (event = options, options = null), Array.isArray(hotkey) || (hotkey = [hotkey]);
var array = hotkey.map(function(string) {
return parseHotkey(string, options);
}), check = function(e) {
return array.some(function(object) {
return compareHotkey(object, e);
});
}, ret = event == null ? check : check(event);
return ret;
}
function isCodeHotkey(hotkey, event) {
return isHotkey2(hotkey, event);
}
function isKeyHotkey(hotkey, event) {
return isHotkey2(hotkey, { byKey: !0 }, event);
}
function parseHotkey(hotkey, options) {
var byKey = options && options.byKey, ret = {};
hotkey = hotkey.replace("++", "+add");
var values = hotkey.split("+"), length = values.length;
for (var k in MODIFIERS)
ret[MODIFIERS[k]] = !1;
var _iteratorNormalCompletion = !0, _didIteratorError = !1, _iteratorError = void 0;
try {
for (var _iterator = values[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = !0) {
var value = _step.value, optional = value.endsWith("?") && value.length > 1;
optional && (value = value.slice(0, -1));
var name = toKeyName(value), modifier = MODIFIERS[name];
if (value.length > 1 && !modifier && !ALIASES[value] && !CODES[name])
throw new TypeError('Unknown modifier: "' + value + '"');
(length === 1 || !modifier) && (byKey ? ret.key = name : ret.which = toKeyCode(value)), modifier && (ret[modifier] = optional ? null : !0);
}
} catch (err) {
_didIteratorError = !0, _iteratorError = err;
} finally {
try {
!_iteratorNormalCompletion && _iterator.return && _iterator.return();
} finally {
if (_didIteratorError)
throw _iteratorError;
}
}
return ret;
}
function compareHotkey(object, event) {
for (var key in object) {
var expected = object[key], actual = void 0;
if (expected != null && (key === "key" && event.key != null ? actual = event.key.toLowerCase() : key === "which" ? actual = expected === 91 && event.which === 93 ? 91 : event.which : actual = event[key], !(actual == null && expected === !1) && actual !== expected))
return !1;
}
return !0;
}
function toKeyCode(name) {
name = toKeyName(name);
var code = CODES[name] || name.toUpperCase().charCodeAt(0);
return code;
}
function toKeyName(name) {
return name = name.toLowerCase(), name = ALIASES[name] || name, name;
}
return lib.default = isHotkey2, lib.isHotkey = isHotkey2, lib.isCodeHotkey = isCodeHotkey, lib.isKeyHotkey = isKeyHotkey, lib.parseHotkey = parseHotkey, lib.compareHotkey = compareHotkey, lib.toKeyCode = toKeyCode, lib.toKeyName = toKeyName, lib;
}
var libExports = requireLib(), isHotkey = /* @__PURE__ */ getDefaultExportFromCjs(libExports);
const useKeyPress = (hotkey, onPress) => {
const keyPressed = react.useRef(!1), downHandler = react.useCallback(
(e) => {
isHotkey(hotkey, e) && (keyPressed.current = !0, onPress && onPress());
},
[hotkey, onPress]
), upHandler = react.useCallback(() => {
keyPressed.current = !1;
}, []);
return react.useEffect(() => (window.addEventListener("keydown", downHandler), window.addEventListener("keyup", upHandler), () => {
window.removeEventListener("keydown", downHandler), window.removeEventListener("keyup", upHandler);
}), [downHandler, upHandler]), keyPressed;
}, divider = { type: "divider" }, inputs = {
altText: {
assetTypes: ["file", "image"],
field: "altText",
name: "altText",
operatorType: "empty",
operatorTypes: ["empty", "notEmpty", null, "includes", "doesNotInclude"],
title: "Alt text",
type: "string",
value: ""
},
creditLine: {
assetTypes: ["file", "image"],
field: "creditLine",
name: "creditLine",
operatorType: "empty",
operatorTypes: ["empty", "notEmpty", null, "includes", "doesNotInclude"],
title: "Credit",
type: "string",
value: ""
},
description: {
assetTypes: ["file", "image"],
field: "description",
name: "description",
operatorType: "empty",
operatorTypes: ["empty", "notEmpty", null, "includes", "doesNotInclude"],
title: "Description",
type: "string",
value: ""
},
fileName: {
assetTypes: ["file", "image"],
field: "originalFilename",
name: "filename",
operatorType: "includes",
operatorTypes: ["includes", "doesNotInclude"],
title: "File name",
type: "string",
value: ""
},
height: {
assetTypes: ["image"],
field: "metadata.dimensions.height",
name: "height",
operatorType: "greaterThan",
operatorTypes: [
"greaterThan",
"greaterThanOrEqualTo",
"lessThan",
"lessThanOrEqualTo",
null,
"equalTo"
],
title: "Height",
type: "number",
value: 400
},
inCurrentDocument: {
assetTypes: ["file", "image"],
name: "inCurrentDocument",
operatorType: "is",
options: [
{
name: "true",
title: "True",
value: groq__default.default`_id in $documentAssetIds`
},
{
name: "false",
title: "False",
value: groq__default.default`!(_id in $documentAssetIds)`
}
],
selectOnly: !0,
title: "In use in current document",
type: "select",
value: "true"
},
inUse: {
assetTypes: ["file", "image"],
name: "inUse",
operatorType: "is",
options: [
{
name: "true",
title: "True",
value: groq__default.default`count(*[references(^._id)]) > 0`
},
{
name: "false",
title: "False",
value: groq__default.default`count(*[references(^._id)]) == 0`
}
],
title: "In use",
type: "select",
value: "true"
},
isOpaque: {
assetTypes: ["image"],
field: "metadata.isOpaque",
name: "isOpaque",
operatorType: "equalTo",
options: [
{
name: "true",
title: "True",
value: "false"
},
{
name: "false",
title: "False",
value: "true"
}
],
title: "Has transparency",
type: "select",
value: "true"
},
orientation: {
assetTypes: ["image"],
name: "orientation",
operatorType: "is",
operatorTypes: ["is", "isNot"],
options: [
{
name: "portrait",
title: "Portrait",
value: "metadata.dimensions.aspectRatio < 1"
},
{
name: "landscape",
title: "Landscape",
value: "metadata.dimensions.aspectRatio > 1"
},
{
name: "square",
title: "Square",
value: "metadata.dimensions.aspectRatio == 1"
}
],
title: "Orientation",
type: "select",
value: "portrait"
},
size: {
assetTypes: ["file", "image"],
field: "size",
modifier: "kb",
modifiers: [
{
name: "kb",
title: "KB",
fieldModifier: (fieldName) => `round(${fieldName} / 1000)`
},
{
name: "mb",
title: "MB",
fieldModifier: (fieldName) => `round(${fieldName} / 1000000)`
}
],
name: "size",
operatorType: "greaterThan",
operatorTypes: [
"greaterThan",
"greaterThanOrEqualTo",
"lessThan",
"lessThanOrEqualTo",
null,
"equalTo"
],
title: "File size",
type: "number",
value: 0
},
tag: {
assetTypes: ["file", "image"],
field: "opt.media.tags",
name: "tag",
operatorType: "references",
operatorTypes: ["references", "doesNotReference", null, "empty", "notEmpty"],
title: "Tags",
type: "searchable"
},
title: {
assetTypes: ["file", "image"],
field: "title",
name: "title",
operatorType: "empty",
operatorTypes: ["empty", "notEmpty", null, "includes", "doesNotInclude"],
title: "Title",
type: "string",
value: ""
},
type: {
assetTypes: ["file", "image"],
name: "type",
operatorType: "is",
operatorTypes: ["is", "isNot"],
options: [
{
name: "image",
title: "Image",
value: 'mimeType match "image*"'
},
{
name: "video",
title: "Video",
value: 'mimeType match "video*"'
},
{
name: "audio",
title: "Audio",
value: 'mimeType match "audio*"'
},
{
name: "pdf",
title: "PDF",
value: 'mimeType == "application/pdf"'
}
],
title: "File type",
type: "select",
value: "image"
},
width: {
assetTypes: ["image"],
field: "metadata.dimensions.width",
name: "width",
operatorType: "greaterThan",
operatorTypes: [
"greaterThan",
"greaterThanOrEqualTo",
"lessThan",
"lessThanOrEqualTo",
null,
"equalTo"
],
title: "Width",
type: "number",
value: 400
}
}, operators = {
doesNotInclude: {
fn: (value, field) => value ? `!(${field} match '*${value}*')` : void 0,
label: "does not include"
},
doesNotReference: {
fn: (value, _field) => value ? `!references('${value}')` : void 0,
label: "does not include"
},
empty: {
fn: (_value, field) => `!defined(${field})`,
hideInput: !0,
label: "is empty"
},
equalTo: {
fn: (value, field) => value ? `${field} == ${value}` : void 0,
label: "is equal to"
},
greaterThan: {
fn: (value, field) => value ? `${field} > ${value}` : void 0,
label: "is greater than"
},
greaterThanOrEqualTo: {
fn: (value, field) => value ? `${field} >= ${value}` : void 0,
label: "is greater than or equal to"
},
includes: {
fn: (value, field) => value ? `${field} match '*${value}*'` : void 0,
label: "includes"
},
is: {
fn: (value, _field) => `${value}`,
label: "is"
},
isNot: {
fn: (value, _field) => `!(${value})`,
label: "is not"
},
lessThan: {
fn: (value, field) => value ? `${field} < ${value}` : void 0,
label: "is less than"
},
lessThanOrEqualTo: {
fn: (value, field) => value ? `${field} <= ${value}` : void 0,
label: "is less than or equal to"
},
notEmpty: {
fn: (_value, field) => `defined(${field})`,
hideInput: !0,
label: "is not empty"
},
references: {
fn: (value, _field) => value ? `references('${value}')` : void 0,
label: "includes"
}
}, ORDER_OPTIONS = [
{
direction: "desc",
field: "_createdAt"
},
{
direction: "asc",
field: "_createdAt"
},
// Divider
null,
{
direction: "desc",
field: "_updatedAt"
},
{
direction: "asc",
field: "_updatedAt"
},
// Divider
null,
{
direction: "asc",
field: "originalFilename"
},
{
direction: "desc",
field: "originalFilename"
},
// Divider
null,
{
direction: "desc",
field: "size"
},
{
direction: "asc",
field: "size"
}
], FACETS = [
inputs.tag,
divider,
inputs.inUse,
inputs.inCurrentDocument,
divider,
inputs.title,
inputs.altText,
inputs.creditLine,
inputs.description,
divider,
inputs.isOpaque,
divider,
inputs.fileName,
inputs.size,
inputs.type,
divider,
inputs.orientation,
inputs.width,
inputs.height
], GRID_TEMPLATE_COLUMNS = {
SMALL: "3rem 100px auto 1.5rem",
LARGE: "3rem 100px auto 5.5rem 5.5rem 3.5rem 8.5rem 4.75rem 2rem"
}, PANEL_HEIGHT = 32, TAG_DOCUMENT_NAME = "media.tag", TAGS_PANEL_WIDTH = 250, AssetSourceDispatchContext = react.createContext(void 0), AssetBrowserDispatchProvider = (props) => {
const { children, onSelect } = props, contextValue = {
onSelect
};
return /* @__PURE__ */ jsxRuntime.jsx(AssetSourceDispatchContext.Provider, { value: contextValue, children });
}, useAssetSourceActions = () => {
const context = react.useContext(AssetSourceDispatchContext);
if (context === void 0)
throw new Error("useAssetSourceActions must be used within an AssetSourceDispatchProvider");
return context;
}, useVersionedClient = () => sanity.useClient({ apiVersion: "2022-10-01" }), ORDER_DICTIONARY = {
_createdAt: {
asc: "Last created: Oldest first",
desc: "Last created: Newest first"
},
_updatedAt: {
asc: "Last updated: Oldest first",
desc: "Last updated: Newest first"
},
mimeType: {
asc: "MIME type: A to Z",
desc: "MIME type: Z to A"
},
originalFilename: {
asc: "File name: A to Z",
desc: "File name: Z to A"
},
size: {
asc: "File size: Smallest first",
desc: "File size: Largest first"
}
}, getOrderTitle = (field, direction) => ORDER_DICTIONARY[field][direction], debugThrottle = (throttled) => function(source) {
return rxjs.iif(
() => !!throttled,
source.pipe(
operators$1.delay(3e3),
operators$1.mergeMap((v) => Math.random() > 0.5 ? rxjs.throwError({
message: "Test error",
statusCode: 500
}) : rxjs.of(v))
),
source
);
}, constructFilter = ({
assetTypes,
searchFacets,
searchQuery
}) => {
const documentAssetTypes = assetTypes.map((type) => `sanity.${type}Asset`), baseFilter = groq__default.default`
_type in ${JSON.stringify(documentAssetTypes)} && !(_id in path("drafts.**"))
`, searchFacetFragments = searchFacets.reduce((acc, facet) => {
if (facet.type === "number") {
const { field, modifier, modifiers, operatorType, value } = facet, operator = operators[operatorType], currentModifier = modifiers?.find((m) => m.name === modifier), facetField = currentModifier?.fieldModifier ? currentModifier.fieldModifier(field) : field, fragment = operator.fn(value, facetField);
fragment && acc.push(fragment);
}
if (facet.type === "searchable") {
const { field, operatorType, value } = facet, fragment = operators[operatorType].fn(value?.value, field);
fragment && acc.push(fragment);
}
if (facet.type === "select") {
const { field, operatorType, options, value } = facet, operator = operators[operatorType], currentOptionValue = options?.find((l) => l.name === value)?.value, fragment = operator.fn(currentOptionValue, field);
fragment && acc.push(fragment);
}
if (facet.type === "string") {
const { field, operatorType, value } = facet, fragment = operators[operatorType].fn(value, field);
fragment && acc.push(fragment);
}
return acc;
}, []);
return [
// Base filter
baseFilter,
// Search query (if present)
// NOTE: Currently this only searches direct fields on sanity.fileAsset/sanity.imageAsset and NOT referenced tags
// It's possible to add this by adding the following line to the searchQuery, but it's quite slow
// references(*[_type == "media.tag" && name.current == "${searchQuery.trim()}"]._id)
...searchQuery ? [
groq__default.default`[_id, altText, assetId, creditLine, description, originalFilename, title, url] match '*${searchQuery.trim()}*'`
] : [],
// Search facets
...searchFacetFragments
].join(" && ");
}, checkTagName = (client, name) => function(source) {
return source.pipe(
operators$1.mergeMap(() => rxjs.from(
client.fetch(groq__default.default`count(*[_type == "${TAG_DOCUMENT_NAME}" && name.current == $name])`, {
name
})
)),
operators$1.mergeMap((existingTagCount) => existingTagCount > 0 ? rxjs.throwError({
message: "Tag already exists",
statusCode: 409
}) : rxjs.of(!0))
);
}, getTagSelectOptions = (tags) => tags.reduce((acc, val) => {
const tag = val?.tag;
return tag && acc.push({
label: tag?.name?.current,
value: tag?._id
}), acc;
}, []), ASSETS_ACTIONS = {
tagsAddComplete: toolkit.createAction(
"actions/tagsAddComplete",
function({ assets, tag }) {
return { payload: { assets, tag } };
}
),
tagsAddError: toolkit.createAction(
"actions/tagsAddError",
function({ assets, error, tag }) {
return { payload: { assets, error, tag } };
}
),
tagsAddRequest: toolkit.createAction(
"actions/tagsAddRequest",
function({ assets, tag }) {
return { payload: { assets, tag } };
}
),
tagsRemoveComplete: toolkit.createAction(
"actions/tagsRemoveComplete",
function({ assets, tag }) {
return { payload: { assets, tag } };
}
),
tagsRemoveError: toolkit.createAction(
"actions/tagsRemoveError",
function({ assets, error, tag }) {
return { payload: { assets, error, tag } };
}
),
tagsRemoveRequest: toolkit.createAction(
"actions/tagsRemoveRequest",
function({ assets, tag }) {
return { payload: { assets, tag } };
}
)
}, DIALOG_ACTIONS = {
showTagCreate: toolkit.createAction("dialog/showTagCreate"),
showTagEdit: toolkit.createAction("dialog/showTagEdit", function({ tagId }) {
return {
payload: { tagId }
};
})
}, initialState$7 = {
allIds: [],
byIds: {},
creating: !1,
creatingError: void 0,
fetchCount: -1,
fetching: !1,
fetchingError: void 0,
panelVisible: !0
}, tagsSlice = toolkit.createSlice({
name: "tags",
initialState: initialState$7,
extraReducers: (builder) => {
builder.addCase(DIALOG_ACTIONS.showTagCreate, (state) => {
delete state.creatingError;
}).addCase(DIALOG_ACTIONS.showTagEdit, (state, action) => {
const { tagId } = action.payload;
delete state.byIds[tagId].error;
}).addMatcher(
toolkit.isAnyOf(
ASSETS_ACTIONS.tagsAddComplete,
ASSETS_ACTIONS.tagsAddError,
ASSETS_ACTIONS.tagsRemoveComplete,
ASSETS_ACTIONS.tagsRemoveError
),
(state, action) => {
const { tag } = action.payload;
state.byIds[tag._id].updating = !1;
}
).addMatcher(
toolkit.isAnyOf(ASSETS_ACTIONS.tagsAddRequest, ASSETS_ACTIONS.tagsRemoveRequest),
(state, action) => {
const { tag } = action.payload;
state.byIds[tag._id].updating = !0;
}
);
},
reducers: {
createComplete(state, action) {
const { tag } = action.payload;
state.creating = !1, state.allIds.includes(tag._id) || state.allIds.push(tag._id), state.byIds[tag._id] = {
_type: "tag",
picked: !1,
tag,
updating: !1
};
},
createError(state, action) {
state.creating = !1, state.creatingError = action.payload.error;
},
createRequest(state, _action) {
state.creating = !0, delete state.creatingError;
},
deleteComplete(state, action) {
const { tagId } = action.payload, deleteIndex = state.allIds.indexOf(tagId);
deleteIndex >= 0 && state.allIds.splice(deleteIndex, 1), delete state.byIds[tagId];
},
deleteError(state, action) {
const { error, tag } = action.payload, tagId = tag?._id;
state.byIds[tagId].error = error, state.byIds[tagId].updating = !1;
},
deleteRequest(state, action) {
const tagId = action.payload?.tag?._id;
state.byIds[tagId].picked = !1, state.byIds[tagId].updating = !0, Object.keys(state.byIds).forEach((key) => {
delete state.byIds[key].error;
});
},
fetchComplete(state, action) {
const { tags } = action.payload;
tags?.forEach((tag) => {
state.allIds.push(tag._id), state.byIds[tag._id] = {
_type: "tag",
picked: !1,
tag,
updating: !1
};
}), state.fetching = !1, state.fetchCount = tags.length || 0, delete state.fetchingError;
},
fetchError(state, action) {
const { error } = action.payload;
state.fetching = !1, state.fetchingError = error;
},
fetchRequest: {
reducer: (state, _action) => {
state.fetching = !0, delete state.fetchingError;
},
prepare: () => ({ payload: { query: groq__default.default`
{
"items": *[
_type == "${TAG_DOCUMENT_NAME}"
&& !(_id in path("drafts.**"))
] {
_createdAt,
_updatedAt,
_id,
_rev,
_type,
name
} | order(name.current asc),
}
` } })
},
// Queue batch tag creation
listenerCreateQueue(_state, _action) {
},
// Apply created tags (via sanity real-time events)
listenerCreateQueueComplete(state, action) {
const { tags } = action.payload;
tags?.forEach((tag) => {
state.byIds[tag._id] = {
_type: "tag",
picked: !1,
tag,
updating: !1
}, state.allIds.includes(tag._id) || state.allIds.push(tag._id);
});
},
// Queue batch tag deletion
listenerDeleteQueue(_state, _action) {
},
// Apply deleted tags (via sanity real-time events)
listenerDeleteQueueComplete(state, action) {
const { tagIds } = action.payload;
tagIds?.forEach((tagId) => {
const deleteIndex = state.allIds.indexOf(tagId);
deleteIndex >= 0 && state.allIds.splice(deleteIndex, 1), delete state.byIds[tagId];
});
},
// Queue batch tag updates
listenerUpdateQueue(_state, _action) {
},
// Apply updated tags (via sanity real-time events)
listenerUpdateQueueComplete(state, action) {
const { tags } = action.payload;
tags?.forEach((tag) => {
state.byIds[tag._id] && (state.byIds[tag._id].tag = tag);
});
},
// Set tag panel visibility
panelVisibleSet(state, action) {
const { panelVisible } = action.payload;
state.panelVisible = panelVisible;
},
// Sort all tags by name
sort(state) {
state.allIds.sort((a, b) => {
const tagA = state.byIds[a].tag.name.current, tagB = state.byIds[b].tag.name.current;
return tagA < tagB ? -1 : tagA > tagB ? 1 : 0;
});
},
updateComplete(state, action) {
const { tag } = action.payload;
state.byIds[tag._id].tag = tag, state.byIds[tag._id].updating = !1;
},
updateError(state, action) {
const { error, tag } = action.payload, tagId = tag?._id;
state.byIds[tagId].error = error, state.byIds[tagId].updating = !1;
},
updateRequest(state, action) {
const { tag } = action.payload;
state.byIds[tag?._id].updating = !0;
}
}
}), tagsCreateEpic = (action$, state$, { client }) => action$.pipe(
operators$1.filter(tagsSlice.actions.createRequest.match),
operators$1.withLatestFrom(state$),
operators$1.mergeMap(([action, state]) => {
const { assetId, name } = action.payload;
return rxjs.of(action).pipe(
debugThrottle(state.debug.badConnection),
checkTagName(client, name),
operators$1.mergeMap(
() => client.observable.create({
_type: TAG_DOCUMENT_NAME,
name: {
_type: "slug",
current: name
}
})
),
operators$1.mergeMap((result) => rxjs.of(tagsSlice.actions.createComplete({ assetId, tag: result }))),
operators$1.catchError(
(error) => rxjs.of(
tagsSlice.actions.createError({
error: {
message: error?.message || "Internal error",
statusCode: error?.statusCode || 500
},
name
})
)
)
);
})
), tagsDeleteEpic = (action$, state$, { client }) => action$.pipe(
operators$1.filter(tagsSlice.actions.deleteRequest.match),
operators$1.withLatestFrom(state$),
operators$1.mergeMap(([action, state]) => {
const { tag } = action.payload;
return rxjs.of(action).pipe(
// Optionally throttle
debugThrottle(state.debug.badConnection),
// Fetch assets which reference this tag
operators$1.mergeMap(
() => client.observable.fetch(
groq__default.default`*[
_type in ["sanity.fileAsset", "sanity.imageAsset"]
&& references(*[_type == "media.tag" && name.current == $tagName]._id)
] {
_id,
_rev,
opt
}`,
{ tagName: tag.name.current }
)
),
// Create transaction which remove tag references from all matched assets and delete tag
operators$1.mergeMap((assets) => {
const transaction = assets.map((asset) => ({
id: asset._id,
patch: {
// this will cause the transaction to fail if the document has been modified since it was fetched.
ifRevisionID: asset._rev,
unset: [`opt.media.tags[_ref == "${tag._id}"]`]
}
})).reduce(
(tx, patch) => tx.patch(patch.id, patch.patch),
client.transaction()
);
return transaction.delete(tag._id), rxjs.from(transaction.commit());
}),
// Dispatch complete action
operators$1.mergeMap(() => rxjs.of(tagsSlice.actions.deleteComplete({ tagId: tag._id }))),
operators$1.catchError(
(error) => rxjs.of(
tagsSlice.actions.deleteError({
error: {
message: error?.message || "Internal error",
statusCode: error?.statusCode || 500
},
tag
})
)
)
);
})
), tagsFetchEpic = (action$, state$, { client }) => action$.pipe(
operators$1.filter(tagsSlice.actions.fetchRequest.match),
operators$1.withLatestFrom(state$),
operators$1.switchMap(([action, state]) => {
const { query } = action.payload;
return rxjs.of(action).pipe(
// Optionally throttle
debugThrottle(state.debug.badConnection),
// Fetch tags
operators$1.mergeMap(
() => client.observable.fetch(query)
),
// Dispatch complete action
operators$1.mergeMap((result) => {
const { items } = result;
return rxjs.of(tagsSlice.actions.fetchComplete({ tags: items }));
}),
operators$1.catchError(
(error) => rxjs.of(
tagsSlice.actions.fetchError({
error: {
message: error?.message || "Internal error",
statusCode: error?.statusCode || 500
}
})
)
)
);
})
), tagsListenerCreateQueueEpic = (action$) => action$.pipe(
operators$1.filter(tagsSlice.actions.listenerCreateQueue.match),
operators$1.bufferTime(2e3),
operators$1.filter((actions) => actions.length > 0),
operators$1.mergeMap((actions) => {
const tags = actions?.map((action) => action.payload.tag);
return rxjs.of(tagsSlice.actions.listenerCreateQueueComplete({ tags }));
})
), tagsListenerDeleteQueueEpic = (action$) => action$.pipe(
operators$1.filter(tagsSlice.actions.listenerDeleteQueue.match),
operators$1.bufferTime(2e3),
operators$1.filter((actions) => actions.length > 0),
operators$1.mergeMap((actions) => {
const tagIds = actions?.map((action) => action.payload.tagId);
return rxjs.of(tagsSlice.actions.listenerDeleteQueueComplete({ tagIds }));
})
), tagsListenerUpdateQueueEpic = (action$) => action$.pipe(
operators$1.filter(tagsSlice.actions.listenerUpdateQueue.match),
operators$1.bufferTime(2e3),
operators$1.filter((actions) => actions.length > 0),
operators$1.mergeMap((actions) => {
const tags = actions?.map((action) => action.payload.tag);
return rxjs.of(tagsSlice.actions.listenerUpdateQueueComplete({ tags }));
})
), tagsSortEpic = (action$) => action$.pipe(
reduxObservable.ofType(
tagsSlice.actions.listenerCreateQueueComplete.type,
tagsSlice.actions.listenerUpdateQueueComplete.type
),
operators$1.bufferTime(1e3),
operators$1.filter((actions) => actions.length > 0),
operators$1.mergeMap(() => rxjs.of(tagsSlice.actions.sort()))
), tagsUpdateEpic = (action$, state$, { client }) => action$.pipe(
operators$1.filter(tagsSlice.actions.updateRequest.match),
operators$1.withLatestFrom(state$),
operators$1.mergeMap(([action, state]) => {
const { closeDialogId, formData, tag } = action.payload;
return rxjs.of(action).pipe(
// Optionally throttle
debugThrottle(state.debug.badConnection),
// Check if tag name is available, throw early if not
checkTagName(client, formData?.name?.current),
// Patch document (Update tag)
operators$1.mergeMap(
() => rxjs.from(
client.patch(tag._id).set({ name: { _type: "slug", current: formData?.name.current } }).commit()
)
),
// Dispatch complete action
operators$1.mergeMap((updatedTag) => rxjs.of(
tagsSlice.actions.updateComplete({
closeDialogId,
tag: updatedTag
})
)),
operators$1.catchError(
(error) => rxjs.of(
tagsSlice.actions.updateError({
error: {
message: error?.message || "Internal error",
statusCode: error?.statusCode || 500
},
tag
})
)
)
);
})
), selectTagsByIds = (state) => state.tags.byIds, selectTagsAllIds = (state) => state.tags.allIds, selectTags = toolkit.createSelector(
[selectTagsByIds, selectTagsAllIds],
(byIds, allIds) => allIds.map((id) => byIds[id])
), selectTagById = toolkit.createSelector(
[selectTagsByIds, (_state, tagId) => tagId],
(byIds, tagId) => byIds[tagId]
), selectTagSelectOptions = (asset) => (state) => {
const tags = asset?.opt?.media?.tags?.reduce((acc, v) => {
const tagItem = state.tags.byIds[v._ref];
return tagItem?.tag && acc.push(tagItem), acc;
}, []);
return tags && tags?.length > 0 ? getTagSelectOptions(tags) : null;
}, tagsActions = { ...tagsSlice.actions };
var tagsReducer = tagsSlice.reducer;
const initialState$6 = {
facets: [],
query: ""
}, searchSlice = toolkit.createSlice({
name: "search",
initialState: initialState$6,
reducers: {
// Add search facet
facetsAdd(state, action) {
state.facets.push({ ...action.payload.facet, id: uuid.uuid() });
},
// Clear all search facets
facetsClear(state) {
state.facets = [];
},
// Remove search facet by name
facetsRemoveByName(state, action) {
state.facets = state.facets.filter((facet) => facet.name !== action.payload.facetName);
},
// Remove search facet by name
facetsRemoveByTag(state, action) {
state.facets = state.facets.filter(
(facet) => !(facet.name === "tag" && facet.type === "searchable" && (facet.operatorType === "references" || facet.operatorType === "doesNotReference") && facet.value?.value === action.payload.tagId)
);
},
// Remove search facet by name
facetsRemoveById(state, action) {
state.facets = state.facets.filter((facet) => facet.id !== action.payload.facetId);
},
// Update an existing search facet
facetsUpdate(state, action) {
const { modifier, name, operatorType, value } = action.payload, facet = state.facets.find((f) => f.name === name);
facet && (facet.type === "number" && modifier && (facet.modifier = modifier), operatorType && (facet.operatorType = operatorType), typeof value < "u" && (facet.value = value), state.facets = state.facets.filter((f) => f.name !== facet.name || f.id === facet.id));
},
// Update an existing search facet
facetsUpdateById(state, action) {
const { modifier, id, operatorType, value } = action.payload;
state.facets.forEach((facet, index) => {
facet.id === id && (facet.type === "number" && modifier && (facet.modifier = modifier), operatorType && (facet.operatorType = operatorType), typeof value < "u" && (state.facets[index].value = value));
});
},
// Update existing search query
querySet(state, action) {
state.query = action.payload?.searchQuery;
}
}
}), searchFacetTagUpdateEpic = (action$, state$) => action$.pipe(
operators$1.filter(tagsActions.updateComplete.match),
operators$1.withLatestFrom(state$),
operators$1.mergeMap(([action, state]) => {
const { tag } = action.payload, currentSearchFacetTag = state.search.facets?.find((facet) => facet.name === "tag"), tagItem = state.tags.byIds[tag._id];
return currentSearchFacetTag?.type === "searchable" && currentSearchFacetTag.value?.value === tag._id ? rxjs.of(
searchSlice.actions.facetsUpdate({
name: "tag",
value: {
label: tagItem?.tag?.name?.current,
value: tagItem?.tag?._id
}
})
) : rxjs.EMPTY;
})
), selectIsSearchFacetTag = toolkit.createSelector(
[
(state) => state.search.facets,
(_state, tagId) => tagId
],
(searchFacets, tagId) => searchFacets.some(
(facet) => facet.name === "tag" && facet.type === "searchable" && (facet.operatorType === "references" || facet.operatorType === "doesNotReference") && facet.value?.value === tagId
)
), searchActions = { ...searchSlice.actions };
var searchReducer = searchSlice.reducer;
const UPLOADS_ACTIONS = {
uploadComplete: toolkit.createAction(
"uploads/uploadComplete",
function({ asset }) {
return {
payload: { asset }
};
}
)
}, defaultOrder = ORDER_OPTIONS[0], initialState$5 = {
allIds: [],
assetTypes: [],
byIds: {},
fetchCount: -1,
fetching: !1,
fetchingError: void 0,
lastPicked: void 0,
order: {
direction: defaultOrder.direction,
field: defaultOrder.field,
title: getOrderTitle(defaultOrder.field, defaultOrder.direction)
},
pageIndex: 0,
pageSize: 100,
// totalCount: -1,
view: "grid"
}, assetsSlice = toolkit.createSlice({
name: "assets",
initialState: initialState$5,
extraReducers: (builder) => {
builder.addCase(UPLOADS_ACTIONS.uploadComplete, (state, action) => {
const { asset } = action.payload;
state.byIds[asset._id] = {
_type: "asset",
asset,
picked: !1,
updating: !1
};
}).addCase(ASSETS_ACTIONS.tagsAddComplete, (state, action) => {
const { assets } = action.payload;
assets.forEach((asset) => {
state.byIds[asset.asset._id].updating = !1;
});
}).addCase(ASSETS_ACTIONS.tagsAddError, (state, action) => {
const { assets } = action.payload;
assets.forEach((asset) => {
state.byIds[asset.asset._id].updating = !1;
});
}).addCase(ASSETS_ACTIONS.tagsAddRequest, (state, action) => {
const { assets } = action.payload;
assets.forEach((asset) => {
state.byIds[asset.asset._id].updating = !0;
});
}).addCase(ASSETS_ACTIONS.tagsRemoveComplete, (state, action) => {
const { assets } = action.payload;
assets.forEach((asset) => {
state.byIds[asset.asset._id].updating = !1;
});
}).addCase(ASSETS_ACTIONS.tagsRemoveError, (state, action) => {
const { assets } = action.payload;
assets.forEach((asset) => {
state.byIds[asset.asset._id].updating = !1;
});
}).addCase(ASSETS_ACTIONS.tagsRemoveRequest, (state, action) => {
const { assets } = action.payload;
assets.forEach((asset) => {
state.byIds[asset.asset._id].updating = !0;
});
});
},
reducers: {
// Clear asset order
clear(state) {
state.allIds = [];
},
// Remove assets and update page index
deleteComplete(state, action) {
const { assetIds } = action.payload;
assetIds?.forEach((id) => {
const deleteIndex = state.allIds.indexOf(id);
deleteIndex >= 0 && state.allIds.splice(deleteIndex, 1), delete state.byIds[id];
}), state.pageIndex = Math.floor(state.allIds.length / state.pageSize) - 1;
},
deleteError(state, action) {
const { assetIds, error } = action.payload, itemErrors = error?.response?.body?.error?.items?.map(
(item) => item.error
);
assetIds?.forEach((id) => {
state.byIds[id].updating = !1;
}), itemErrors?.forEach((item) => {
state.byIds[item.id].error = item.description;
});
},
deleteRequest(state, action) {
const { assets } = action.payload;
assets.forEach((asset) => {
state.byIds[asset?._id].updating = !0;
}), Object.keys(state.byIds).forEach((key) => {
delete state.byIds[key].error;
});
},
fetchComplete(state, action) {
const assets = action.payload?.assets || [];
assets && assets.forEach((asset) => {
state.allIds.includes(asset._id) || state.allIds.push(asset._id), state.byIds[asset._id] = {
_type: "asset",
asset,
picked: !1,
updating: !1
};
}), state.fetching = !1, state.fetchCount = assets.length || 0, delete state.fetchingError;
},
fetchError(state, action) {
const error = action.payload;
state.fetching = !1, state.fetchingError = error;
},
fetchRequest: {
reducer: (state, _action) => {
state.fetching = !0, delete state.fetchingError;
},
prepare: ({
params = {},
queryFilter,
selector = "",
sort = groq__default.default`order(_updatedAt desc)`
}) => {
const query = groq__default.default`
{
"items": *[${queryFilter}] {
_id,
_type,
_createdAt,
_updatedAt,
altText,
creditLine,
description,
extension,
metadata {
dimensions,
exif,
isOpaque,
},
mimeType,
opt {
media
},
originalFilename,
size,
source {
name
},
title,
url
} ${sort || selector ? "|" : ""} ${sort} ${selector},
}
`;
return { payload: { params, query } };
}
},
insertUploads(state, action) {
const { results } = action.payload;
Object.entries(results).forEach(([hash, assetId]) => {
assetId && !state.allIds.includes(hash) && state.allIds.push(assetId);
});
},
listenerCreateQueue(_state, _action) {
},
listenerCreateQueueComplete(state, action) {
const { assets } = action.payload;
assets?.forEach((asset) => {
state.byIds[asset?._id]?.asset && (state.byIds[asset._id].asset = asset);
});
},
listenerDeleteQueue(_state, _action) {
},
listenerDeleteQueueComplete(state, action) {
const { assetIds } = action.payload;
assetIds?.forEach((assetId) => {
const deleteIndex = state.allIds.indexOf(assetId);
deleteIndex >= 0 && state.allIds.splice(deleteIndex, 1), delete state.byIds[assetId];
});
},
listenerUpdateQueue(_state, _action) {
},
listenerUpdateQueueComplete(state, action) {
const { assets } = action.payload;
assets?.forEach((asset) => {
state.byIds[asset?._id]?.asset && (state.byIds[asset._id].asset = asset);
});
},
loadNextPage() {
},
loadPageIndex(state, action) {
state.pageIndex = action.payload.pageIndex;
},
orderSet(state, action) {
state.order = action.payload?.order, state.pageIndex = 0;
},
pick(state, action) {
const { assetId, picked } = action.payload;
state.byIds[assetId].picked = picked, state.lastPicked = picked ? assetId : void 0;
},
pickAll(state) {
state.allIds.forEach((id) => {
state.byIds[id].picked = !0;
});
},
pickClear(state) {
state.lastPicked = void 0, Object.values(state.byIds).forEach((asset) => {
state.byIds[asset.asset._id].picked = !1;
});
},
pickRange(state, action) {
const startIndex = state.allIds.findIndex((id) => id === action.payload.startId), endIndex = state.allIds.findIndex((id) => id === action.payload.endId), indices = [startIndex, endIndex].sort((a, b) => a - b);
state.allIds.slice(indices[0], indices[1] + 1).forEach((key) => {
state.byIds[key].picked = !0;
}), state.lastPicked = state.allIds[endIndex];
},
sort(state) {
state.allIds.sort((a, b) => {
const tagA = state.byIds[a].asset[state.order.field], tagB = state.byIds[b].asset[state.order.field];
return tagA < tagB ? state.order.direction === "asc" ? -1 : 1 : tagA > tagB ? state.order.direction === "asc" ? 1 : -1 : 0;
});
},
updateComplete(state, action) {
const { asset } = action.payload;
state.byIds[asset._id].updating = !1, state.byIds[asset._id].asset = asset;
},
updateError(state, action) {
const { asset, error } = action.payload, assetId = asset?._id;
state.byIds[assetId].error = error.message, state.byIds[assetId].updating = !1;
},
updateRequest(state, action) {
const assetId = action.payload?.asset?._id;
state.byIds[assetId].updating = !0;
},
viewSet(state, action) {
state.view = action.payload?.view;
}
}
}), assetsDeleteEpic = (action$, _state$, { client }) => action$.pipe(
operators$1.filter(assetsActions.deleteRequest.match),
operators$1.mergeMap((action) => {
const { assets } = action.payload, assetIds = assets.map((asset) => asset._id);
return rxjs.of(assets).pipe(
operators$1.mergeMap(
() => client.observable.delete({
query: groq__default.default`*[_id in ${JSON.stringify(assetIds)}]`
})
),
operators$1.mergeMap(() => rxjs.of(assetsActions.deleteComplete({ assetIds }))),
operators$1.catchError((error) => rxjs.of(assetsActions.deleteError({ assetIds, error })))
);
})
), assetsFetchEpic = (action$, state$, { client }) => action$.pipe(
operators$1.filter(assetsActions.fetchRequest.match),
operators$1.withLatestFrom(state$),
operators$1.switchMap(([action, state]) => {
const params = action.payload?.params, query = action.payload?.query;
return rxjs.of(action).pipe(
debugThrottle(state.debug.badConnection),
operators$1.mergeMap(
() => client.observable.fetch(query, params)
),
operators$1.mergeMap((result) => {
const {
items
// totalCount
} = result;
return rxjs.of(assetsActions.fetchComplete({ assets: items }));
}),
operators$1.catchError(
(error) => rxjs.of(
assetsActions.fetchError({
message: error?.message || "Internal error",
statusCode: error?.statusCode || 500
})
)
)
);
})
), assetsFetchPageIndexEpic = (action$, state$) => action$.pipe(
operators$1.filter(assetsActions.loadPageIndex.match),
operators$1.withLatestFrom(state$),
operators$1.switchMap(([action, state]) => {
const pageSize = state.assets.pageSize, start = action.payload.pageIndex * pageSize, end = start + pageSize, documentId = state?.selected.document?._id, documentAssetIds = state?.selected?.documentAssetIds, constructedFilter = constructFilter({
assetTypes: state.assets.assetTypes,
searchFacets: state.search.facets,
searchQuery: state.search.query
}), params = {
...documentId ? { documentId } : {},
documentAssetIds
};
return rxjs.of(
assetsActions.fetchRequest({
params,
queryFilter: constructedFilter,
selector: groq__default.default`[${start}...${end}]`,
sort: groq__default.default`order(${state.assets?.order?.field} ${state.assets?.order?.direction})`
})
);
})
), assetsFetchNextPageEpic = (action$, state$) => action$.pipe(
operators$1.filter(assetsActions.loadNextPage.match),
operators$1.withLatestFrom(state$),
operators$1.switchMap(
([_action, state]) => rxjs.of(assetsActions.loadPageIndex({ pageIndex: state.assets.pageIndex + 1 }))
)
), assetsFetchAfterDeleteAllEpic = (action$, state$) => action$.pipe(
operators$1.filter(assetsActions.deleteComplete.match),
operators$1.withLatestFrom(state$),
operators$1.switchMap(([_action, state]) => {
if (state.assets.allIds.length === 0) {
const nextPageIndex = Math.floor(state.assets.allIds.length / state.assets.pageSize);
return rxjs.of(assetsActions.loadPageIndex({ pageIndex: nextPageIndex }));
}
return rxjs.EMPTY;
})
), filterAssetWithoutTag = (tag) => (asset) => (asset?.asset?.opt?.media?.tags?.findIndex((t) => t._ref === tag?._id) ?? -1) < 0, patchOperationTagAppend = ({ tag }) => (patch) => patch.setIfMissing({ opt: {} }).setIfMissing({ "opt.media": {} }).setIfMissing({ "opt.media.tags": [] }).append("opt.media.tags", [{ _key: nanoid.nanoid(), _ref: tag?._id, _type: "reference", _weak: !0 }]), patchOperationTagUnset = ({ asset, tag }) => (patch) => patch.ifRevisionId(asset?.asset?._rev).unset([`opt.media.tags[_ref == "${tag._id}"]`]), assetsOrderSetEpic = (action$) => action$.pipe(
operators$1.filter(assetsActions.orderSet.match),
operators$1.mergeMap(() => rxjs.of(
assetsActions.clear(),
//
assetsActions.loadPageIndex({ pageIndex: 0 })
))
), assetsSearchEpic = (action$) => action$.pipe(
reduxObservable.ofType(
searchActions.facetsAdd.type,
searchActions.facetsClear.type,
searchActions.facetsRemoveById.type,
searchActions.facetsRemoveByName.type,
searchActions.facetsRemoveByTag.type,
searchActions.facetsUpdate.type,
searchActions.facetsUpdateById.type,
searchActions.querySet.type
),
operators$1.debounceTime(400),
operators$1.mergeMap(() => rxjs.of(
assetsActions.clear(),
//
assetsActions.loadPageIndex({ pageIndex: 0 })
))
), assetsListenerCreateQueueEpic = (action$) => action$.pipe(
operators$1.filter(assetsActions.listenerCreateQueue.match),
operators$1.bufferTime(2e3),
operators$1.filter((actions) => actions.length > 0),
operators$1.mergeMap((actions) => {
const assets = actions?.map((action) => action.payload.asset);
return rxjs.of(assetsActions.listenerCreateQueueComplete({ assets }));
})
), assetsListenerDeleteQueueEpic = (action$) => action$.pipe(
operators$1.filter(assetsActions.listenerDeleteQueue.match),
operators$1.bufferTime(2e3),
operators$1.filter((actions) => actions.length > 0),
opera