sanity-plugin-taxonomy-manager
Version:
Create and manage SKOS compliant taxonomies, thesauri, and classification schemes in Sanity Studio.
1 lines • 187 kB
Source Map (JSON)
{"version":3,"file":"index.esm.mjs","sources":["../src/components/inputs/Identifier.tsx","../node_modules/sanity-plugin-utils/lib/index.js","../src/context.ts","../src/queries.ts","../src/styles.ts","../node_modules/@sanity/uuid/node_modules/uuid/dist/esm-browser/rng.js","../node_modules/@sanity/uuid/node_modules/uuid/dist/esm-browser/stringify.js","../node_modules/@sanity/uuid/node_modules/uuid/dist/esm-browser/regex.js","../node_modules/@sanity/uuid/node_modules/uuid/dist/esm-browser/v4.js","../node_modules/@sanity/uuid/node_modules/uuid/dist/esm-browser/validate.js","../src/hooks/useCreateConcept.tsx","../src/hooks/useOpenNewConceptPane.tsx","../src/hooks/useRemoveConcept.tsx","../src/hooks/useLinkColorScheme.tsx","../src/components/interactions/ConceptDetailDialogue.tsx","../src/components/interactions/ConceptDetailLink.tsx","../src/components/interactions/ConceptSelectLink.tsx","../src/components/Children.tsx","../src/components/ChildConcepts.tsx","../src/components/Concepts.tsx","../src/components/guides/NewScheme.tsx","../src/hooks/useAddTitle.tsx","../src/components/guides/NoConcepts.tsx","../src/components/TopConcepts.tsx","../src/components/TreeStructure.tsx","../src/components/inputs/InputHierarchy.tsx","../src/components/Hierarchy.tsx","../src/components/TreeView.tsx","../src/components/inputs/ReferenceHierarchyInput.tsx","../src/components/inputs/ArrayHierarchyInput.tsx","../src/components/inputs/ManagementControls.tsx","../src/static/NodeTree.tsx","../src/helpers/schemeFilter.ts","../src/helpers/branchFilter.ts","../src/helpers/baseIriField.tsx","../src/components/inputs/RdfUri.tsx","../src/skosConcept.tsx","../src/skosConceptScheme.tsx","../src/structure.ts","../src/index.ts"],"sourcesContent":["import {Button, Inline, Stack, useToast} from '@sanity/ui'\nimport {nanoid} from 'nanoid'\nimport {useCallback} from 'react'\nimport {set} from 'sanity'\nimport type {StringInputProps} from 'sanity'\n\n/**\n * #### Create Unique Identifier\n * For schemes and concepts created in previous versions of the\n * plugin.\n * - Input is only visible if no identifier has been assigned\n * - Input disappears once an ID is generated\n */\nexport const Identifier = (props: StringInputProps) => {\n const {onChange} = props\n const toast = useToast()\n\n const handleChange = useCallback(() => {\n onChange(set(nanoid(6)))\n toast.push({\n status: 'success',\n title: 'Identifier created.',\n closable: true,\n })\n }, [onChange, toast])\n\n return (\n <Stack space={2}>\n <Inline space={[3, 3, 4]}>\n <Button tone=\"primary\" fontSize={2} onClick={handleChange} text=\"Generate Identifier\" />\n </Inline>\n </Stack>\n )\n}\n","import { jsx, jsxs } from \"react/jsx-runtime\";\nimport { Card, Flex, Box, Stack, Text, Menu, MenuItem, TextInput, Badge } from \"@sanity/ui\";\nimport { styled, css } from \"styled-components\";\nimport { RemoveCircleIcon, AddCircleIcon, RestoreIcon } from \"@sanity/icons\";\nimport { useState, useRef, useMemo, useEffect, useContext, useCallback } from \"react\";\nimport { UserAvatar, useClient, useDocumentStore, useWorkspace } from \"sanity\";\nimport isEqual from \"react-fast-compare\";\nimport { distinctUntilChanged, catchError } from \"rxjs/operators\";\nimport { RouterContext } from \"sanity/router\";\nimport { usePaneRouter } from \"sanity/structure\";\nvar __defProp$3 = Object.defineProperty, __getOwnPropSymbols$3 = Object.getOwnPropertySymbols, __hasOwnProp$3 = Object.prototype.hasOwnProperty, __propIsEnum$3 = Object.prototype.propertyIsEnumerable, __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: !0, configurable: !0, writable: !0, value }) : obj[key] = value, __spreadValues$3 = (a, b) => {\n for (var prop in b || (b = {}))\n __hasOwnProp$3.call(b, prop) && __defNormalProp$3(a, prop, b[prop]);\n if (__getOwnPropSymbols$3)\n for (var prop of __getOwnPropSymbols$3(b))\n __propIsEnum$3.call(b, prop) && __defNormalProp$3(a, prop, b[prop]);\n return a;\n};\nconst DEFAULT_PROPS = {\n tone: \"primary\"\n};\nfunction Feedback(props) {\n const { title, description, icon, tone, children } = __spreadValues$3(__spreadValues$3({}, DEFAULT_PROPS), props);\n return /* @__PURE__ */ jsx(Card, { tone, padding: 4, radius: 3, border: !0, children: /* @__PURE__ */ jsxs(Flex, { children: [\n icon ? \"display icon\" : null,\n children || /* @__PURE__ */ jsx(Box, { flex: 1, children: /* @__PURE__ */ jsxs(Stack, { space: 4, children: [\n title ? /* @__PURE__ */ jsx(Text, { weight: \"semibold\", children: title }) : null,\n description ? /* @__PURE__ */ jsx(Text, { size: 2, children: description }) : null\n ] }) })\n ] }) });\n}\nvar __defProp$2 = Object.defineProperty, __defProps$1 = Object.defineProperties, __getOwnPropDescs$1 = Object.getOwnPropertyDescriptors, __getOwnPropSymbols$2 = Object.getOwnPropertySymbols, __hasOwnProp$2 = Object.prototype.hasOwnProperty, __propIsEnum$2 = Object.prototype.propertyIsEnumerable, __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: !0, configurable: !0, writable: !0, value }) : obj[key] = value, __spreadValues$2 = (a, b) => {\n for (var prop in b || (b = {}))\n __hasOwnProp$2.call(b, prop) && __defNormalProp$2(a, prop, b[prop]);\n if (__getOwnPropSymbols$2)\n for (var prop of __getOwnPropSymbols$2(b))\n __propIsEnum$2.call(b, prop) && __defNormalProp$2(a, prop, b[prop]);\n return a;\n}, __spreadProps$1 = (a, b) => __defProps$1(a, __getOwnPropDescs$1(b)), __objRest = (source, exclude) => {\n var target = {};\n for (var prop in source)\n __hasOwnProp$2.call(source, prop) && exclude.indexOf(prop) < 0 && (target[prop] = source[prop]);\n if (source != null && __getOwnPropSymbols$2)\n for (var prop of __getOwnPropSymbols$2(source))\n exclude.indexOf(prop) < 0 && __propIsEnum$2.call(source, prop) && (target[prop] = source[prop]);\n return target;\n};\nconst TableWrapper = (props = {}) => /* @__PURE__ */ jsx(Card, __spreadValues$2({ as: \"table\" }, props)), StyledTable = styled(TableWrapper)(\n () => css`\n display: table;\n width: 100%;\n border-collapse: collapse;\n\n &:not([hidden]) {\n display: table;\n border-collapse: collapse;\n }\n `\n);\nfunction Table(props) {\n const _a = props, { children } = _a, rest = __objRest(_a, [\"children\"]);\n return /* @__PURE__ */ jsx(StyledTable, __spreadProps$1(__spreadValues$2({}, rest), { children }));\n}\nconst RowWrapper = (props = {}) => /* @__PURE__ */ jsx(Card, __spreadValues$2({ as: \"tr\" }, props)), StyledRow = styled(RowWrapper)(\n () => css`\n display: table-row;\n\n &:not([hidden]) {\n display: table-row;\n }\n `\n);\nfunction Row(props) {\n const _a = props, { children } = _a, rest = __objRest(_a, [\"children\"]);\n return /* @__PURE__ */ jsx(StyledRow, __spreadProps$1(__spreadValues$2({}, rest), { children }));\n}\nconst CellWrapper = (props = {}) => /* @__PURE__ */ jsx(Card, __spreadValues$2({ as: \"td\" }, props)), StyledCell = styled(CellWrapper)(\n () => css`\n display: table-cell;\n\n &:not([hidden]) {\n display: table-cell;\n }\n `\n);\nfunction Cell(props) {\n const _a = props, { children } = _a, rest = __objRest(_a, [\"children\"]);\n return /* @__PURE__ */ jsx(StyledCell, __spreadProps$1(__spreadValues$2({}, rest), { children }));\n}\nvar __defProp$1 = Object.defineProperty, __getOwnPropSymbols$1 = Object.getOwnPropertySymbols, __hasOwnProp$1 = Object.prototype.hasOwnProperty, __propIsEnum$1 = Object.prototype.propertyIsEnumerable, __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: !0, configurable: !0, writable: !0, value }) : obj[key] = value, __spreadValues$1 = (a, b) => {\n for (var prop in b || (b = {}))\n __hasOwnProp$1.call(b, prop) && __defNormalProp$1(a, prop, b[prop]);\n if (__getOwnPropSymbols$1)\n for (var prop of __getOwnPropSymbols$1(b))\n __propIsEnum$1.call(b, prop) && __defNormalProp$1(a, prop, b[prop]);\n return a;\n};\nfunction searchUsers(users, searchString) {\n return users.filter((user) => !!((user.displayName || \"\").toLowerCase().startsWith(searchString) || (user.givenName || \"\").toLowerCase().startsWith(searchString) || (user.middleName || \"\").toLowerCase().startsWith(searchString) || (user.familyName || \"\").toLowerCase().startsWith(searchString)));\n}\nconst LABELS = {\n addMe: \"Assign myself\",\n removeMe: \"Unassign myself\",\n clear: \"Clear assignees\",\n searchPlaceholder: \"Search users\",\n notFound: \"No users found\"\n};\nfunction UserSelectMenu(props) {\n const {\n value = [],\n userList = [],\n onAdd,\n onRemove,\n onClear,\n style = {}\n } = props, labels = props != null && props.labels ? __spreadValues$1(__spreadValues$1({}, LABELS), props.labels) : LABELS, [searchString, setSearchString] = useState(\"\"), searchResults = searchUsers(userList || [], searchString), me = userList.find((u) => u.isCurrentUser), meAssigned = me && value.includes(me.id), input = useRef(null), handleSearchChange = (event) => {\n setSearchString(event.target.value);\n }, handleSelect = (isChecked, user) => {\n isChecked ? onRemove && onRemove(user.id) : onAdd && onAdd(user.id);\n }, handleAssignMyself = () => {\n me && onAdd && onAdd(me.id);\n }, handleUnassignMyself = () => {\n me && onRemove && onRemove(me.id);\n }, handleClearAssigneesClick = () => {\n onClear && onClear();\n };\n return /* @__PURE__ */ jsxs(Menu, { style, children: [\n meAssigned ? /* @__PURE__ */ jsx(\n MenuItem,\n {\n tone: \"caution\",\n disabled: !me,\n onClick: handleUnassignMyself,\n icon: RemoveCircleIcon,\n text: labels.removeMe\n }\n ) : /* @__PURE__ */ jsx(\n MenuItem,\n {\n tone: \"positive\",\n onClick: handleAssignMyself,\n icon: AddCircleIcon,\n text: labels.addMe\n }\n ),\n /* @__PURE__ */ jsx(\n MenuItem,\n {\n tone: \"critical\",\n disabled: value.length === 0,\n onClick: handleClearAssigneesClick,\n icon: RestoreIcon,\n text: labels.clear\n }\n ),\n /* @__PURE__ */ jsx(Box, { padding: 1, children: /* @__PURE__ */ jsx(\n TextInput,\n {\n ref: input,\n onChange: handleSearchChange,\n placeholder: labels.searchPlaceholder,\n value: searchString\n }\n ) }),\n searchString && (searchResults == null ? void 0 : searchResults.length) === 0 && /* @__PURE__ */ jsx(MenuItem, { disabled: !0, text: labels.notFound }),\n searchResults && searchResults.map((user) => /* @__PURE__ */ jsx(\n MenuItem,\n {\n pressed: value.includes(user.id),\n onClick: () => handleSelect(value.indexOf(user.id) > -1, user),\n children: /* @__PURE__ */ jsxs(Flex, { align: \"center\", children: [\n /* @__PURE__ */ jsx(UserAvatar, { user, size: 1 }),\n /* @__PURE__ */ jsx(Box, { paddingX: 2, flex: 1, children: /* @__PURE__ */ jsx(Text, { children: user.displayName }) }),\n user.isCurrentUser && /* @__PURE__ */ jsx(Badge, { fontSize: 1, tone: \"positive\", mode: \"outline\", children: \"Me\" })\n ] })\n },\n user.id\n ))\n ] });\n}\nfunction getDefaultExportFromCjs(x) {\n return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, \"default\") ? x.default : x;\n}\nvar builder = {}, urlForImage = {}, parseAssetId = {}, hasRequiredParseAssetId;\nfunction requireParseAssetId() {\n if (hasRequiredParseAssetId) return parseAssetId;\n hasRequiredParseAssetId = 1, Object.defineProperty(parseAssetId, \"__esModule\", { value: !0 });\n var example = \"image-Tb9Ew8CXIwaY6R1kjMvI0uRR-2000x3000-jpg\";\n function parseAssetId$1(ref) {\n var _a = ref.split(\"-\"), id = _a[1], dimensionString = _a[2], format = _a[3];\n if (!id || !dimensionString || !format)\n throw new Error(\"Malformed asset _ref '\".concat(ref, `'. Expected an id like \"`).concat(example, '\".'));\n var _b = dimensionString.split(\"x\"), imgWidthStr = _b[0], imgHeightStr = _b[1], width = +imgWidthStr, height = +imgHeightStr, isValidAssetId = isFinite(width) && isFinite(height);\n if (!isValidAssetId)\n throw new Error(\"Malformed asset _ref '\".concat(ref, `'. Expected an id like \"`).concat(example, '\".'));\n return { id, width, height, format };\n }\n return parseAssetId.default = parseAssetId$1, parseAssetId;\n}\nvar parseSource = {}, hasRequiredParseSource;\nfunction requireParseSource() {\n if (hasRequiredParseSource) return parseSource;\n hasRequiredParseSource = 1;\n var __assign = parseSource && parseSource.__assign || function() {\n return __assign = Object.assign || function(t) {\n for (var s, i = 1, n = arguments.length; i < n; i++) {\n s = arguments[i];\n for (var p in s) Object.prototype.hasOwnProperty.call(s, p) && (t[p] = s[p]);\n }\n return t;\n }, __assign.apply(this, arguments);\n };\n Object.defineProperty(parseSource, \"__esModule\", { value: !0 });\n var isRef = function(src) {\n var source = src;\n return source ? typeof source._ref == \"string\" : !1;\n }, isAsset = function(src) {\n var source = src;\n return source ? typeof source._id == \"string\" : !1;\n }, isAssetStub = function(src) {\n var source = src;\n return source && source.asset ? typeof source.asset.url == \"string\" : !1;\n };\n function parseSource$1(source) {\n if (!source)\n return null;\n var image;\n if (typeof source == \"string\" && isUrl(source))\n image = {\n asset: { _ref: urlToId(source) }\n };\n else if (typeof source == \"string\")\n image = {\n asset: { _ref: source }\n };\n else if (isRef(source))\n image = {\n asset: source\n };\n else if (isAsset(source))\n image = {\n asset: {\n _ref: source._id || \"\"\n }\n };\n else if (isAssetStub(source))\n image = {\n asset: {\n _ref: urlToId(source.asset.url)\n }\n };\n else if (typeof source.asset == \"object\")\n image = __assign({}, source);\n else\n return null;\n var img = source;\n return img.crop && (image.crop = img.crop), img.hotspot && (image.hotspot = img.hotspot), applyDefaults(image);\n }\n parseSource.default = parseSource$1;\n function isUrl(url) {\n return /^https?:\\/\\//.test(\"\".concat(url));\n }\n function urlToId(url) {\n var parts = url.split(\"/\").slice(-1);\n return \"image-\".concat(parts[0]).replace(/\\.([a-z]+)$/, \"-$1\");\n }\n function applyDefaults(image) {\n if (image.crop && image.hotspot)\n return image;\n var result = __assign({}, image);\n return result.crop || (result.crop = {\n left: 0,\n top: 0,\n bottom: 0,\n right: 0\n }), result.hotspot || (result.hotspot = {\n x: 0.5,\n y: 0.5,\n height: 1,\n width: 1\n }), result;\n }\n return parseSource;\n}\nvar hasRequiredUrlForImage;\nfunction requireUrlForImage() {\n return hasRequiredUrlForImage || (hasRequiredUrlForImage = 1, function(exports) {\n var __assign = urlForImage && urlForImage.__assign || function() {\n return __assign = Object.assign || function(t) {\n for (var s, i = 1, n = arguments.length; i < n; i++) {\n s = arguments[i];\n for (var p in s) Object.prototype.hasOwnProperty.call(s, p) && (t[p] = s[p]);\n }\n return t;\n }, __assign.apply(this, arguments);\n }, __importDefault = urlForImage && urlForImage.__importDefault || function(mod) {\n return mod && mod.__esModule ? mod : { default: mod };\n };\n Object.defineProperty(exports, \"__esModule\", { value: !0 }), exports.parseSource = exports.SPEC_NAME_TO_URL_NAME_MAPPINGS = void 0;\n var parseAssetId_1 = __importDefault(requireParseAssetId()), parseSource_1 = __importDefault(requireParseSource());\n exports.parseSource = parseSource_1.default, exports.SPEC_NAME_TO_URL_NAME_MAPPINGS = [\n [\"width\", \"w\"],\n [\"height\", \"h\"],\n [\"format\", \"fm\"],\n [\"download\", \"dl\"],\n [\"blur\", \"blur\"],\n [\"sharpen\", \"sharp\"],\n [\"invert\", \"invert\"],\n [\"orientation\", \"or\"],\n [\"minHeight\", \"min-h\"],\n [\"maxHeight\", \"max-h\"],\n [\"minWidth\", \"min-w\"],\n [\"maxWidth\", \"max-w\"],\n [\"quality\", \"q\"],\n [\"fit\", \"fit\"],\n [\"crop\", \"crop\"],\n [\"saturation\", \"sat\"],\n [\"auto\", \"auto\"],\n [\"dpr\", \"dpr\"],\n [\"pad\", \"pad\"]\n ];\n function urlForImage$1(options) {\n var spec = __assign({}, options || {}), source = spec.source;\n delete spec.source;\n var image = (0, parseSource_1.default)(source);\n if (!image)\n throw new Error(\"Unable to resolve image URL from source (\".concat(JSON.stringify(source), \")\"));\n var id = image.asset._ref || image.asset._id || \"\", asset = (0, parseAssetId_1.default)(id), cropLeft = Math.round(image.crop.left * asset.width), cropTop = Math.round(image.crop.top * asset.height), crop = {\n left: cropLeft,\n top: cropTop,\n width: Math.round(asset.width - image.crop.right * asset.width - cropLeft),\n height: Math.round(asset.height - image.crop.bottom * asset.height - cropTop)\n }, hotSpotVerticalRadius = image.hotspot.height * asset.height / 2, hotSpotHorizontalRadius = image.hotspot.width * asset.width / 2, hotSpotCenterX = image.hotspot.x * asset.width, hotSpotCenterY = image.hotspot.y * asset.height, hotspot = {\n left: hotSpotCenterX - hotSpotHorizontalRadius,\n top: hotSpotCenterY - hotSpotVerticalRadius,\n right: hotSpotCenterX + hotSpotHorizontalRadius,\n bottom: hotSpotCenterY + hotSpotVerticalRadius\n };\n return spec.rect || spec.focalPoint || spec.ignoreImageParams || spec.crop || (spec = __assign(__assign({}, spec), fit({ crop, hotspot }, spec))), specToImageUrl(__assign(__assign({}, spec), { asset }));\n }\n exports.default = urlForImage$1;\n function specToImageUrl(spec) {\n var cdnUrl = (spec.baseUrl || \"https://cdn.sanity.io\").replace(/\\/+$/, \"\"), filename = \"\".concat(spec.asset.id, \"-\").concat(spec.asset.width, \"x\").concat(spec.asset.height, \".\").concat(spec.asset.format), baseUrl = \"\".concat(cdnUrl, \"/images/\").concat(spec.projectId, \"/\").concat(spec.dataset, \"/\").concat(filename), params = [];\n if (spec.rect) {\n var _a = spec.rect, left = _a.left, top_1 = _a.top, width = _a.width, height = _a.height, isEffectiveCrop = left !== 0 || top_1 !== 0 || height !== spec.asset.height || width !== spec.asset.width;\n isEffectiveCrop && params.push(\"rect=\".concat(left, \",\").concat(top_1, \",\").concat(width, \",\").concat(height));\n }\n spec.bg && params.push(\"bg=\".concat(spec.bg)), spec.focalPoint && (params.push(\"fp-x=\".concat(spec.focalPoint.x)), params.push(\"fp-y=\".concat(spec.focalPoint.y)));\n var flip = [spec.flipHorizontal && \"h\", spec.flipVertical && \"v\"].filter(Boolean).join(\"\");\n return flip && params.push(\"flip=\".concat(flip)), exports.SPEC_NAME_TO_URL_NAME_MAPPINGS.forEach(function(mapping) {\n var specName = mapping[0], param = mapping[1];\n typeof spec[specName] < \"u\" ? params.push(\"\".concat(param, \"=\").concat(encodeURIComponent(spec[specName]))) : typeof spec[param] < \"u\" && params.push(\"\".concat(param, \"=\").concat(encodeURIComponent(spec[param])));\n }), params.length === 0 ? baseUrl : \"\".concat(baseUrl, \"?\").concat(params.join(\"&\"));\n }\n function fit(source, spec) {\n var cropRect, imgWidth = spec.width, imgHeight = spec.height;\n if (!(imgWidth && imgHeight))\n return { width: imgWidth, height: imgHeight, rect: source.crop };\n var crop = source.crop, hotspot = source.hotspot, desiredAspectRatio = imgWidth / imgHeight, cropAspectRatio = crop.width / crop.height;\n if (cropAspectRatio > desiredAspectRatio) {\n var height = Math.round(crop.height), width = Math.round(height * desiredAspectRatio), top_2 = Math.max(0, Math.round(crop.top)), hotspotXCenter = Math.round((hotspot.right - hotspot.left) / 2 + hotspot.left), left = Math.max(0, Math.round(hotspotXCenter - width / 2));\n left < crop.left ? left = crop.left : left + width > crop.left + crop.width && (left = crop.left + crop.width - width), cropRect = { left, top: top_2, width, height };\n } else {\n var width = crop.width, height = Math.round(width / desiredAspectRatio), left = Math.max(0, Math.round(crop.left)), hotspotYCenter = Math.round((hotspot.bottom - hotspot.top) / 2 + hotspot.top), top_3 = Math.max(0, Math.round(hotspotYCenter - height / 2));\n top_3 < crop.top ? top_3 = crop.top : top_3 + height > crop.top + crop.height && (top_3 = crop.top + crop.height - height), cropRect = { left, top: top_3, width, height };\n }\n return {\n width: imgWidth,\n height: imgHeight,\n rect: cropRect\n };\n }\n }(urlForImage)), urlForImage;\n}\nvar hasRequiredBuilder;\nfunction requireBuilder() {\n if (hasRequiredBuilder) return builder;\n hasRequiredBuilder = 1;\n var __assign = builder && builder.__assign || function() {\n return __assign = Object.assign || function(t) {\n for (var s, i = 1, n = arguments.length; i < n; i++) {\n s = arguments[i];\n for (var p in s) Object.prototype.hasOwnProperty.call(s, p) && (t[p] = s[p]);\n }\n return t;\n }, __assign.apply(this, arguments);\n }, __createBinding = builder && builder.__createBinding || (Object.create ? function(o, m, k, k2) {\n k2 === void 0 && (k2 = k), Object.defineProperty(o, k2, { enumerable: !0, get: function() {\n return m[k];\n } });\n } : function(o, m, k, k2) {\n k2 === void 0 && (k2 = k), o[k2] = m[k];\n }), __setModuleDefault = builder && builder.__setModuleDefault || (Object.create ? function(o, v) {\n Object.defineProperty(o, \"default\", { enumerable: !0, value: v });\n } : function(o, v) {\n o.default = v;\n }), __importStar = builder && builder.__importStar || function(mod) {\n if (mod && mod.__esModule) return mod;\n var result = {};\n if (mod != null) for (var k in mod) k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k) && __createBinding(result, mod, k);\n return __setModuleDefault(result, mod), result;\n };\n Object.defineProperty(builder, \"__esModule\", { value: !0 }), builder.ImageUrlBuilder = void 0;\n var urlForImage_1 = __importStar(requireUrlForImage()), validFits = [\"clip\", \"crop\", \"fill\", \"fillmax\", \"max\", \"scale\", \"min\"], validCrops = [\"top\", \"bottom\", \"left\", \"right\", \"center\", \"focalpoint\", \"entropy\"], validAutoModes = [\"format\"];\n function isSanityModernClientLike(client) {\n return client && \"config\" in client ? typeof client.config == \"function\" : !1;\n }\n function isSanityClientLike(client) {\n return client && \"clientConfig\" in client ? typeof client.clientConfig == \"object\" : !1;\n }\n function rewriteSpecName(key) {\n for (var specs = urlForImage_1.SPEC_NAME_TO_URL_NAME_MAPPINGS, _i = 0, specs_1 = specs; _i < specs_1.length; _i++) {\n var entry = specs_1[_i], specName = entry[0], param = entry[1];\n if (key === specName || key === param)\n return specName;\n }\n return key;\n }\n function urlBuilder(options) {\n if (isSanityModernClientLike(options)) {\n var _a = options.config(), apiUrl = _a.apiHost, projectId = _a.projectId, dataset = _a.dataset, apiHost = apiUrl || \"https://api.sanity.io\";\n return new ImageUrlBuilder(null, {\n baseUrl: apiHost.replace(/^https:\\/\\/api\\./, \"https://cdn.\"),\n projectId,\n dataset\n });\n }\n var client = options;\n if (isSanityClientLike(client)) {\n var _b = client.clientConfig, apiUrl = _b.apiHost, projectId = _b.projectId, dataset = _b.dataset, apiHost = apiUrl || \"https://api.sanity.io\";\n return new ImageUrlBuilder(null, {\n baseUrl: apiHost.replace(/^https:\\/\\/api\\./, \"https://cdn.\"),\n projectId,\n dataset\n });\n }\n return new ImageUrlBuilder(null, options);\n }\n builder.default = urlBuilder;\n var ImageUrlBuilder = (\n /** @class */\n function() {\n function ImageUrlBuilder2(parent, options) {\n this.options = parent ? __assign(__assign({}, parent.options || {}), options || {}) : __assign({}, options || {});\n }\n return ImageUrlBuilder2.prototype.withOptions = function(options) {\n var baseUrl = options.baseUrl || this.options.baseUrl, newOptions = { baseUrl };\n for (var key in options)\n if (options.hasOwnProperty(key)) {\n var specKey = rewriteSpecName(key);\n newOptions[specKey] = options[key];\n }\n return new ImageUrlBuilder2(this, __assign({ baseUrl }, newOptions));\n }, ImageUrlBuilder2.prototype.image = function(source) {\n return this.withOptions({ source });\n }, ImageUrlBuilder2.prototype.dataset = function(dataset) {\n return this.withOptions({ dataset });\n }, ImageUrlBuilder2.prototype.projectId = function(projectId) {\n return this.withOptions({ projectId });\n }, ImageUrlBuilder2.prototype.bg = function(bg) {\n return this.withOptions({ bg });\n }, ImageUrlBuilder2.prototype.dpr = function(dpr) {\n return this.withOptions(dpr && dpr !== 1 ? { dpr } : {});\n }, ImageUrlBuilder2.prototype.width = function(width) {\n return this.withOptions({ width });\n }, ImageUrlBuilder2.prototype.height = function(height) {\n return this.withOptions({ height });\n }, ImageUrlBuilder2.prototype.focalPoint = function(x, y) {\n return this.withOptions({ focalPoint: { x, y } });\n }, ImageUrlBuilder2.prototype.maxWidth = function(maxWidth) {\n return this.withOptions({ maxWidth });\n }, ImageUrlBuilder2.prototype.minWidth = function(minWidth) {\n return this.withOptions({ minWidth });\n }, ImageUrlBuilder2.prototype.maxHeight = function(maxHeight) {\n return this.withOptions({ maxHeight });\n }, ImageUrlBuilder2.prototype.minHeight = function(minHeight) {\n return this.withOptions({ minHeight });\n }, ImageUrlBuilder2.prototype.size = function(width, height) {\n return this.withOptions({ width, height });\n }, ImageUrlBuilder2.prototype.blur = function(blur) {\n return this.withOptions({ blur });\n }, ImageUrlBuilder2.prototype.sharpen = function(sharpen) {\n return this.withOptions({ sharpen });\n }, ImageUrlBuilder2.prototype.rect = function(left, top, width, height) {\n return this.withOptions({ rect: { left, top, width, height } });\n }, ImageUrlBuilder2.prototype.format = function(format) {\n return this.withOptions({ format });\n }, ImageUrlBuilder2.prototype.invert = function(invert) {\n return this.withOptions({ invert });\n }, ImageUrlBuilder2.prototype.orientation = function(orientation) {\n return this.withOptions({ orientation });\n }, ImageUrlBuilder2.prototype.quality = function(quality) {\n return this.withOptions({ quality });\n }, ImageUrlBuilder2.prototype.forceDownload = function(download) {\n return this.withOptions({ download });\n }, ImageUrlBuilder2.prototype.flipHorizontal = function() {\n return this.withOptions({ flipHorizontal: !0 });\n }, ImageUrlBuilder2.prototype.flipVertical = function() {\n return this.withOptions({ flipVertical: !0 });\n }, ImageUrlBuilder2.prototype.ignoreImageParams = function() {\n return this.withOptions({ ignoreImageParams: !0 });\n }, ImageUrlBuilder2.prototype.fit = function(value) {\n if (validFits.indexOf(value) === -1)\n throw new Error('Invalid fit mode \"'.concat(value, '\"'));\n return this.withOptions({ fit: value });\n }, ImageUrlBuilder2.prototype.crop = function(value) {\n if (validCrops.indexOf(value) === -1)\n throw new Error('Invalid crop mode \"'.concat(value, '\"'));\n return this.withOptions({ crop: value });\n }, ImageUrlBuilder2.prototype.saturation = function(saturation) {\n return this.withOptions({ saturation });\n }, ImageUrlBuilder2.prototype.auto = function(value) {\n if (validAutoModes.indexOf(value) === -1)\n throw new Error('Invalid auto mode \"'.concat(value, '\"'));\n return this.withOptions({ auto: value });\n }, ImageUrlBuilder2.prototype.pad = function(pad) {\n return this.withOptions({ pad });\n }, ImageUrlBuilder2.prototype.url = function() {\n return (0, urlForImage_1.default)(this.options);\n }, ImageUrlBuilder2.prototype.toString = function() {\n return this.url();\n }, ImageUrlBuilder2;\n }()\n );\n return builder.ImageUrlBuilder = ImageUrlBuilder, builder;\n}\nvar node, hasRequiredNode;\nfunction requireNode() {\n if (hasRequiredNode) return node;\n hasRequiredNode = 1;\n var __importDefault = node && node.__importDefault || function(mod) {\n return mod && mod.__esModule ? mod : { default: mod };\n }, builder_1 = __importDefault(requireBuilder());\n return node = builder_1.default, node;\n}\nvar nodeExports = /* @__PURE__ */ requireNode(), createImageUrlBuilder = /* @__PURE__ */ getDefaultExportFromCjs(nodeExports);\nfunction useImageUrlBuilder(clientOptions) {\n const client = useClient(clientOptions);\n return useMemo(() => createImageUrlBuilder(client), [client]);\n}\nfunction useImageUrlBuilderImage(source, clientOptions) {\n const builder2 = useImageUrlBuilder(clientOptions);\n return useMemo(\n () => source && builder2 ? builder2.image(source) : null,\n [builder2, source]\n );\n}\nconst DEFAULT_PARAMS = {}, DEFAULT_OPTIONS = { apiVersion: \"v2023-05-01\" }, DEFAULT_INITIAL_VALUE = null;\nfunction useParams(params) {\n const stringifiedParams = useMemo(\n () => JSON.stringify(params || {}),\n [params]\n );\n return useMemo(() => JSON.parse(stringifiedParams), [stringifiedParams]);\n}\nfunction useListeningQuery(query, {\n params = DEFAULT_PARAMS,\n options = DEFAULT_OPTIONS,\n initialValue = DEFAULT_INITIAL_VALUE\n}) {\n const [loading, setLoading] = useState(!0), [error, setError] = useState(!1), [data, setData] = useState(initialValue), memoParams = useParams(params), memoOptions = useParams(options), subscription = useRef(null), documentStore = useDocumentStore();\n return useEffect(() => {\n if (query && !error && !subscription.current)\n try {\n subscription.current = documentStore.listenQuery(query, memoParams, memoOptions).pipe(\n distinctUntilChanged(isEqual),\n catchError((err) => (console.error(err), setError(err), setLoading(!1), setData(null), err))\n ).subscribe((documents) => {\n setData(\n (current) => isEqual(current, documents) ? current : documents\n ), setLoading(!1), setError(!1);\n });\n } catch (err) {\n console.error(err), setLoading(!1), setError(err);\n }\n return error && subscription.current && subscription.current.unsubscribe(), () => {\n var _a;\n subscription.current && ((_a = subscription == null ? void 0 : subscription.current) == null || _a.unsubscribe(), subscription.current = null);\n };\n }, [query, error, memoParams, memoOptions, documentStore]), { data, loading, error };\n}\nfunction useOpenInNewPane(id, type) {\n const routerContext = useContext(RouterContext), { routerPanesState, groupIndex } = usePaneRouter();\n return useCallback(() => {\n if (!routerContext || !id || !type)\n return;\n const panes = [...routerPanesState];\n panes.splice(groupIndex + 1, 0, [\n {\n id,\n params: { type }\n }\n ]);\n const href = routerContext.resolvePathFromState({ panes });\n routerContext.navigateUrl({ path: href });\n }, [id, type, routerContext, routerPanesState, groupIndex]);\n}\nvar __defProp = Object.defineProperty, __defProps = Object.defineProperties, __getOwnPropDescs = Object.getOwnPropertyDescriptors, __getOwnPropSymbols = Object.getOwnPropertySymbols, __hasOwnProp = Object.prototype.hasOwnProperty, __propIsEnum = Object.prototype.propertyIsEnumerable, __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: !0, configurable: !0, writable: !0, value }) : obj[key] = value, __spreadValues = (a, b) => {\n for (var prop in b || (b = {}))\n __hasOwnProp.call(b, prop) && __defNormalProp(a, prop, b[prop]);\n if (__getOwnPropSymbols)\n for (var prop of __getOwnPropSymbols(b))\n __propIsEnum.call(b, prop) && __defNormalProp(a, prop, b[prop]);\n return a;\n}, __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));\nfunction chunkArray(array, size) {\n const chunks = [];\n for (let i = 0; i < array.length; i += size)\n chunks.push(array.slice(i, i + size));\n return chunks;\n}\nfunction useProjectUsers({ apiVersion }) {\n const { currentUser } = useWorkspace(), client = useClient({ apiVersion: apiVersion != null ? apiVersion : \"2023-01-01\" }), [users, setUsers] = useState([]);\n return useEffect(() => {\n const { projectId } = client.config();\n async function getUsersWithRoles() {\n try {\n const aclData = await client.request({\n url: `/projects/${projectId}/acl`\n }), userIds = aclData.map((user) => user.projectUserId), userIdChunks = chunkArray(userIds, 200);\n let usersData = [];\n for (const chunk of userIdChunks) {\n const chunkedUserIds = chunk.join(\",\"), response = await client.request({\n url: `/projects/${projectId}/users/${chunkedUserIds}`\n });\n usersData = [...usersData, ...response];\n }\n const usersWithRoles = usersData.map((user) => {\n var _a;\n const userRoles = ((_a = aclData.find(\n (aclUser) => aclUser.projectUserId === user.id\n )) == null ? void 0 : _a.roles) || [];\n return __spreadProps(__spreadValues({}, user), {\n isCurrentUser: user.id === (currentUser == null ? void 0 : currentUser.id),\n roles: userRoles\n });\n });\n setUsers(usersWithRoles);\n } catch (err) {\n console.error(\"Failed to fetch users:\", err);\n }\n }\n users.length || getUsersWithRoles();\n }, [client, currentUser == null ? void 0 : currentUser.id, users.length]), users;\n}\nexport {\n Cell,\n Feedback,\n Row,\n Table,\n UserSelectMenu,\n useImageUrlBuilder,\n useImageUrlBuilderImage,\n useListeningQuery,\n useOpenInNewPane,\n useProjectUsers\n};\n//# sourceMappingURL=index.js.map\n","import {createContext} from 'react'\n\nimport type {ConceptSchemeDocument} from './types'\n\ntype TreeContextType = {\n globalVisibility?: {treeId: string; treeVisibility: string}\n editControls?: boolean\n setEditControls?: (value: boolean) => void\n}\n\nexport type ReleaseContextType = {\n isPublished?: boolean\n isInRelease: boolean\n releaseName?: string\n documentId: string\n versionId?: string\n}\n\nexport const SchemeContext = createContext<ConceptSchemeDocument | null>(null)\nexport const TreeContext = createContext<TreeContextType>({editControls: false})\n// export const ReleaseContext = createContext<ReleaseContextType>({\n// isInRelease: false,\n// documentId: '',\n// })\nexport const ReleaseContext = createContext<any>(undefined)\n","/**\n * ### Tree Builder\n * Recursive function to build out successive branches of the hierarchy\n * up to five levels deep.\n */\n\n/**\n * #### Branch Builder\n * Recursive function to build out successive branches of the hierarchy\n * up to five levels deep.\n */\nconst branchBuilder = (level = 1): string | void => {\n let reference = '^.^.concepts[]._ref'\n let i = 1\n while (level > i) {\n reference = `^.${reference}`\n i++\n }\n if (level > 6) {\n return ''\n }\n return `\"childConcepts\": *[_id in ${reference} && ^._id in broader[]._ref]|order(prefLabel)\n {\n \"id\": _id,\n \"level\": ${level},\n _originalId,\n prefLabel,\n definition,\n example,\n scopeNote,\n ${branchBuilder(level + 1) || ''}\n }`\n}\n\n/**\n * #### Trunk Builder\n * Fetch the top concepts, their child concepts, and orphan concepts\n * and their child concepts.\n *\n * To get orphans:\n * - filter to concepts in this scheme only\n * - filter out concepts that reference a topConcept in this scheme as\n * a broader term\n * - filter out concepts that reference other concepts in this scheme\n * as a broader term\n *\n * Used in Hierarchy.tsx\n */\nexport const trunkBuilder = (): string => {\n return `*[_id == $id][0] {\n _updatedAt,\n \"topConcepts\": topConcepts[]->|order(prefLabel) {\n \"id\": _id,\n \"level\": 0,\n _originalId,\n prefLabel,\n definition,\n example,\n scopeNote,\n ${branchBuilder() || ''}\n },\n \"concepts\": concepts[]->|order(prefLabel) {\n \"id\": _id,\n \"level\": 0,\n \"isOrphan\": \n coalesce(\n !array::intersects(\n broader[]._ref, (\n coalesce(^.concepts[]._ref, []) + coalesce(^.topConcepts[]._ref, [])\n )\n ),\n true),\n _originalId,\n prefLabel,\n definition,\n example,\n scopeNote,\n ${branchBuilder() || ''}\n }\n }`\n}\n\n/**\n * #### Input Builder\n * Accept a branchId parameter, and filter to topConcepts and Concepts\n * in that branch only. Then call branchBuilder recursively — it will\n * only build terms in the scheme referenced by that concept.\n *\n * - branchBuilder() is called in Hierarchy.tsx\n * - inputBuilder() is called in InputHierarchy.tsx\n */\nexport const inputBuilder = (): string => {\n return `\n select($branchId != null => \n *[_id == $id][0] {\n _updatedAt,\n \"topConcepts\":*[_type == \"skosConcept\" && conceptId == $branchId]{\n \"id\": _id,\n \"level\": 0,\n _originalId,\n prefLabel,\n definition,\n example,\n scopeNote,\n ${branchBuilder() || ''} \n }, \n \"concepts\": []\n },\n *[_id == $id][0] {\n _updatedAt,\n \"topConcepts\":topConcepts[]->|order(prefLabel){\n \"id\": _id,\n \"level\": 0,\n _originalId,\n prefLabel,\n definition,\n example,\n scopeNote,\n ${branchBuilder() || ''}\n }, \n \"concepts\": concepts[]->|order(prefLabel){\n \"id\": _id,\n \"level\": 0,\n \"isOrphan\": \n coalesce(\n !array::intersects(\n broader[]._ref, (\n coalesce(^.concepts[]._ref, []) + coalesce(^.topConcepts[]._ref, [])\n )\n ), \n true),\n _originalId,\n prefLabel,\n definition,\n example,\n scopeNote,\n ${branchBuilder() || ''}\n }\n }\n )`\n}\n","import {hues} from '@sanity/color'\nimport styled from 'styled-components'\n\nexport const StyledDescription = styled.details`\n summary {\n cursor: pointer;\n }\n div {\n margin-top: 0.5rem;\n margin-left: 0.75rem;\n svg {\n padding-right: 0.25rem;\n }\n }\n`\nexport const InlineHelp = styled.div`\n margin-top: 2rem;\n`\n\nexport const StyledTree = styled.ul`\n list-style: none;\n padding-left: 0;\n margin-block-start: 0;\n li svg.info {\n height: 1.2rem;\n width: 1.2rem;\n color: ${hues.gray[800].hex};\n border-radius: 3px;\n transition: all 0.1s ease-in-out;\n &.warning:hover {\n color: ${hues.gray[100].hex};\n background-color: ${hues.yellow[500].hex};\n }\n &.error {\n color: ${hues.red[500].hex};\n &:hover {\n color: ${hues.gray[100].hex};\n background-color: ${hues.red[500].hex};\n }\n }\n }\n li svg.spacer {\n height: 1.5rem;\n width: 1.5rem;\n visibility: hidden;\n }\n`\nexport const HierarchyButton = styled.button`\n background: none;\n border: none;\n padding: 0.5rem;\n border-radius: 3px;\n cursor: pointer;\n svg {\n padding-right: 0.25rem;\n }\n &:hover {\n background-color: ${hues.gray[50].hex};\n }\n &.add:hover {\n background-color: ${hues.green[500].hex};\n span,\n svg {\n color: white;\n }\n }\n`\nexport const StyledTreeButton = styled.button`\n background: none;\n border: none;\n padding: 0.2rem 0 0;\n cursor: pointer;\n svg {\n border-radius: 3px;\n transition: all 0.1s ease-in-out;\n }\n &.toggle svg {\n height: 1.5rem;\n width: 1.5rem;\n }\n &.action svg {\n height: 1.2rem;\n height: 1.2rem;\n width: 1.2rem;\n &:hover {\n color: ${hues.gray[100].hex} !important;\n }\n &.info:hover {\n background-color: ${hues.blue[500].hex};\n }\n &.add:hover {\n background-color: ${hues.green[500].hex};\n }\n &.remove:hover {\n background-color: ${hues.red[500].hex};\n }\n }\n`\nexport const StyledTreeToggle = styled.button`\n background: none;\n border: none;\n padding: 0.2rem 0 0;\n cursor: pointer;\n svg {\n height: 1.5rem;\n width: 1.5rem;\n }\n`\n\nexport const StyledTopConcept = styled.li`\n font-weight: bold;\n margin-top: 1rem;\n .untitled {\n color: ${hues.gray[400].hex};\n font-weight: normal;\n }\n button[aria-expanded='true'] svg {\n rotate: 90deg;\n }\n &.closed ul {\n display: none;\n }\n`\nexport const StyledConcept = styled.li`\n padding-top: 0.5rem;\n font-weight: normal;\n margin-top: 1rem;\n .untitled {\n color: ${hues.gray[400].hex};\n }\n button[aria-expanded='true'] svg {\n rotate: 90deg;\n }\n &.closed ul {\n display: none;\n }\n`\nexport const StyledChildConcepts = styled.ul`\n list-style: none;\n`\nexport const StyledChildConcept = styled.li`\n font-weight: normal;\n margin-top: 1rem;\n button[aria-expanded='true'] svg {\n rotate: 90deg;\n }\n &.closed ul {\n display: none;\n }\n`\nexport const StyledConceptLink = styled.a`\n text-decoration: none;\n &:hover {\n text-decoration: underline;\n }\n`\nexport const StyledConceptTitle = styled.p``\n","// Unique ID creation requires a high quality random # generator. In the browser we therefore\n// require the crypto API and do not support built-in fallback to lower quality random number\n// generators (like Math.random()).\nvar getRandomValues;\nvar rnds8 = new Uint8Array(16);\nexport default function rng() {\n // lazy load so that environments that need to polyfill have a chance to do so\n if (!getRandomValues) {\n // getRandomValues needs to be invoked in a context where \"this\" is a Crypto implementation. Also,\n // find the complete implementation of crypto (msCrypto) on IE11.\n getRandomValues = typeof crypto !== 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto) || typeof msCrypto !== 'undefined' && typeof msCrypto.getRandomValues === 'function' && msCrypto.getRandomValues.bind(msCrypto);\n\n if (!getRandomValues) {\n throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported');\n }\n }\n\n return getRandomValues(rnds8);\n}","import validate from './validate.js';\n/**\n * Convert array of 16 byte values to UUID string format of the form:\n * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX\n */\n\nvar byteToHex = [];\n\nfor (var i = 0; i < 256; ++i) {\n byteToHex.push((i + 0x100).toString(16).substr(1));\n}\n\nfunction stringify(arr) {\n var offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;\n // Note: Be careful editing this code! It's been tuned for performance\n // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434\n var uuid = (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); // Consistency check for valid UUID. If this throws, it's likely due to one\n // of the following:\n // - One or more input array values don't map to a hex octet (leading to\n // \"undefined\" in the uuid)\n // - Invalid input values for the RFC `version` or `variant` fields\n\n if (!validate(uuid)) {\n throw TypeError('Stringified UUID is invalid');\n }\n\n return uuid;\n}\n\nexport default stringify;","export default /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;","import rng from './rng.js';\nimport stringify from './stringify.js';\n\nfunction v4(options, buf, offset) {\n options = options || {};\n var rnds = options.random || (options.rng || rng)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`\n\n rnds[6] = rnds[6] & 0x0f | 0x40;\n rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided\n\n if (buf) {\n offset = offset || 0;\n\n for (var i = 0; i < 16; ++i) {\n buf[offset + i] = rnds[i];\n }\n\n return buf;\n }\n\n return stringify(rnds);\n}\n\nexport default v4;","import REGEX from './regex.js';\n\nfunction validate(uuid) {\n return typeof uuid === 'string' && REGEX.test(uuid);\n}\n\nexport default validate;","import {\n DocumentId,\n getDraftId,\n getVersionId,\n getVersionNameFromId,\n isVersionId,\n getPublishedId,\n type VersionId,\n} from '@sanity/id-utils'\nimport {useToast} from '@sanity/ui'\nimport {uuid} from '@sanity/uuid'\nimport {nanoid} from 'nanoid'\nimport {useCallback} from 'react'\nimport {isPublishedId, useClient} from 'sanity'\n\nimport type {SkosConceptDocument, SkosConceptReference, ConceptSchemeDocument} from '../types'\n\nimport {useOpenNewConceptPane} from './useOpenNewConceptPane'\n\n/**\n * #### Concept Creation Hook\n * Used for creating concepts and top concepts from the\n * Concept Scheme hierarchy view.\n */\nexport function useCreateConcept(document: ConceptSchemeDocument) {\n const toast = useToast()\n const client = useClient({apiVersion: '2025-02-19'})\n const openInNewPane = useOpenNewConceptPane()\n\n const schemaBaseIri = document.displayed.baseIri\n\n const createConcept = useCallback(\n (\n conceptType: 'topConcept' | 'concept',\n concept?: {\n id: string\n _originalId?: string\n }\n ) => {\n // destructure IDs and rename for this context\n const {id: broaderConceptId, _originalId: broaderConceptOriginalId = ''} = concept || {}\n // check if the skosConceptScheme is in a release\n const isInRelease = isVersionId(document.displayed._id as DocumentId)\n // if so, get the release name\n const releaseName = isInRelease\n ? getVersionNameFromId(document.displayed._id as VersionId)\n : undefined\n\n // create a scheme ID based on context\n const schemeId = isInRelease\n ? getVersionId(DocumentId(document.displayed._id), releaseName as string)\n : getDraftId(DocumentId(document.displayed._id))\n\n // Generate the appropriate concept ID based on context\n const newConceptId = isInRelease\n ? getVersionId(DocumentId(uuid()), releaseName as string)\n : getDraftId(DocumentId(uuid()))\n\n // create the new skosConcept document\n const skosConcept: SkosConceptDocument = {\n _id: newConceptId, // either a draft ID or a release ID\n _type: 'skosConcept',\n conceptId: `${nanoid(6)}`,\n prefLabel: '',\n baseIri: schemaBaseIri,\n broader: [],\n related: [],\n }\n\n // if a broader concept ID is provided, add it to the skosConcept\n if (broaderConceptId) {\n // check if the broader concept is published\n const isPublished = isPublishedId(DocumentId(broaderConceptOriginalId))\n // add broader as _strengthenOnPublish if it's not published\n skosConcept.broader = [\n {\n _key: uuid(),\n _ref: getPublishedId(DocumentId(broaderConceptId)),\n _type: 'reference',\n _weak: !isPublished,\n _strengthenOnPublish: isPublished\n ? undefined\n : {\n type: 'skosConcept',\n template: {id: 'skosConcept'},\n },\n },\n ]\n }\n\n const skosConceptReference: SkosConceptReference = {\n _ref: getPublishedId(newConceptId),\n _type: 'reference',\n _key: uuid(),\n _strengthenOnPublish: {\n type: 'skosConcept',\n template: {id: 'skosConcept'},\n },\n _weak: true,\n }\n\n client\n .transaction()\n .createIfNotExists({...document.displayed, _id: schemeId})\n .create(skosConcept)\n .patch(schemeId, (patch) => {\n if (conceptType === 'topConcept') {\n return patch\n .setIfMissing({topConcepts: []})\n .append('topConcepts', [skosConceptReference])\n }\n return patch.setIfMissing({concepts: []}).append('concepts', [skosConceptReference])\n })\n .commit({autoGenerateArrayKey