@driftime/sanity-plugin-link 0.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.js ADDED
@@ -0,0 +1,1010 @@
1
+ import { d as readPath, f as defaultTitleField, i as searchParamTypeName, l as convertCase, m as pluginName, n as linkMarkTypeName, o as composeAnchorHref, r as linkTypeName, u as isDefined } from "./types-BR08fVY8.js";
2
+ import { ChangeIndicator, FieldActionsProvider, FieldActionsResolver, FormField, defineArrayMember, defineField, definePlugin, defineType, getPublishedId, insert, set, setIfMissing, unset, useDocumentPreviewStore, useFormValue } from "sanity";
3
+ import { c } from "react/compiler-runtime";
4
+ import { createElement, useEffect, useState } from "react";
5
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
+ import { Box, Button, Dialog, Flex, Stack, Tab, TabList, TabPanel, TextInput } from "@sanity/ui";
7
+ import { CloseIcon } from "@sanity/icons/Close";
8
+ import { stegaClean } from "@sanity/client/stega";
9
+ import { AddIcon } from "@sanity/icons/Add";
10
+ import { randomKey } from "@sanity/util/content";
11
+ import { useEditor } from "@portabletext/editor";
12
+ import { defineBehavior, raise } from "@portabletext/editor/behaviors";
13
+ import { isSelectionExpanded } from "@portabletext/editor/selectors";
14
+ import { htmlToPortableText } from "@portabletext/html";
15
+ /**
16
+ * Checks whether a form node holds members of its own, as an object or an array of objects does.
17
+ *
18
+ * @param node - The form node to check.
19
+ * @returns True if the node holds members.
20
+ */
21
+ function hasMembers(node) {
22
+ return "members" in node;
23
+ }
24
+ /**
25
+ * Gathers the validation reported on a form node and on anything inside it.
26
+ *
27
+ * @param node - The form node to gather validation from.
28
+ * @returns Every validation reported on the node or within it.
29
+ */
30
+ function collectNodeValidation(node) {
31
+ return [...node.validation, ...hasMembers(node) ? collectMemberValidation(node.members) : []];
32
+ }
33
+ /**
34
+ * Gathers the validation reported anywhere inside a field's members. The form hands a field only what
35
+ * is reported at its own path, which suits a frame drawing its members inline but leaves one that
36
+ * hides them showing nothing they report.
37
+ *
38
+ * @param members - The members to gather validation from.
39
+ * @returns Every validation reported on a member or within one.
40
+ */
41
+ function collectMemberValidation(members) {
42
+ return members.flatMap((member) => member.kind === "field" ? collectNodeValidation(member.field) : member.kind === "fieldSet" ? collectMemberValidation(member.fieldSet.members) : member.kind === "item" ? collectNodeValidation(member.item) : []);
43
+ }
44
+ function Field(t0) {
45
+ let $ = c(40), { actions, schemaType, title, description, presence, validation, level, inputId, path, changed, children, inputProps } = t0, t1;
46
+ $[0] === Symbol.for("react.memo_cache_sentinel") ? (t1 = [], $[0] = t1) : t1 = $[0];
47
+ let [headerActions, setHeaderActions] = useState(t1), t2;
48
+ $[1] === Symbol.for("react.memo_cache_sentinel") ? (t2 = ["_id"], $[1] = t2) : t2 = $[1];
49
+ let documentId = useFormValue(t2), fieldValidation, focused, t3;
50
+ if ($[2] !== actions || $[3] !== documentId || $[4] !== inputProps.focused || $[5] !== inputProps.members || $[6] !== path || $[7] !== schemaType || $[8] !== validation) {
51
+ let publishedId = typeof documentId == "string" ? getPublishedId(documentId) : void 0;
52
+ focused = inputProps.focused === !0;
53
+ let t4;
54
+ $[12] !== inputProps.members || $[13] !== validation ? (t4 = [...validation, ...collectMemberValidation(inputProps.members)], $[12] = inputProps.members, $[13] = validation, $[14] = t4) : t4 = $[14], fieldValidation = t4, t3 = isDefined(publishedId) && isDefined(actions) && /* @__PURE__ */ jsx(FieldActionsResolver, {
55
+ actions,
56
+ documentId: publishedId,
57
+ documentType: schemaType.name,
58
+ onActions: setHeaderActions,
59
+ path,
60
+ schemaType
61
+ }), $[2] = actions, $[3] = documentId, $[4] = inputProps.focused, $[5] = inputProps.members, $[6] = path, $[7] = schemaType, $[8] = validation, $[9] = fieldValidation, $[10] = focused, $[11] = t3;
62
+ } else fieldValidation = $[9], focused = $[10], t3 = $[11];
63
+ let t4;
64
+ $[15] !== changed || $[16] !== children || $[17] !== focused || $[18] !== path ? (t4 = /* @__PURE__ */ jsx(ChangeIndicator, {
65
+ path,
66
+ hasFocus: focused,
67
+ isChanged: changed,
68
+ children
69
+ }), $[15] = changed, $[16] = children, $[17] = focused, $[18] = path, $[19] = t4) : t4 = $[19];
70
+ let t5;
71
+ $[20] !== description || $[21] !== fieldValidation || $[22] !== headerActions || $[23] !== inputId || $[24] !== inputProps.readOnly || $[25] !== level || $[26] !== path || $[27] !== presence || $[28] !== schemaType.deprecated || $[29] !== t4 || $[30] !== title ? (t5 = /* @__PURE__ */ jsx(FormField, {
72
+ __unstable_headerActions: headerActions,
73
+ __unstable_presence: presence,
74
+ description,
75
+ inputId,
76
+ level,
77
+ title,
78
+ validation: fieldValidation,
79
+ deprecated: schemaType.deprecated,
80
+ path,
81
+ readOnly: inputProps.readOnly,
82
+ children: t4
83
+ }), $[20] = description, $[21] = fieldValidation, $[22] = headerActions, $[23] = inputId, $[24] = inputProps.readOnly, $[25] = level, $[26] = path, $[27] = presence, $[28] = schemaType.deprecated, $[29] = t4, $[30] = title, $[31] = t5) : t5 = $[31];
84
+ let t6;
85
+ $[32] !== focused || $[33] !== headerActions || $[34] !== path || $[35] !== t5 ? (t6 = /* @__PURE__ */ jsx(FieldActionsProvider, {
86
+ actions: headerActions,
87
+ focused,
88
+ path,
89
+ children: t5
90
+ }), $[32] = focused, $[33] = headerActions, $[34] = path, $[35] = t5, $[36] = t6) : t6 = $[36];
91
+ let t7;
92
+ return $[37] !== t3 || $[38] !== t6 ? (t7 = /* @__PURE__ */ jsxs(Fragment, { children: [t3, t6] }), $[37] = t3, $[38] = t6, $[39] = t7) : t7 = $[39], t7;
93
+ }
94
+ /**
95
+ * Properties the plugin's own icons are drawn with. Icons taken from `@sanity/icons` are left alone,
96
+ * since they already carry the sizing and stroke the rest of the Studio uses.
97
+ */
98
+ const defaultIconProps = {
99
+ width: "1em",
100
+ height: "1em",
101
+ strokeWidth: 1.5
102
+ };
103
+ /**
104
+ * Creates a Sanity Studio icon component from an SVG icon.
105
+ *
106
+ * @param icon - The icon component to wrap.
107
+ * @param props - Additional props merged over the default properties a Studio icon is drawn with.
108
+ * @returns A Studio icon component.
109
+ */
110
+ function createSanityIcon(icon, props) {
111
+ let Icon = icon;
112
+ function SanityIcon() {
113
+ return createElement(Icon, {
114
+ ...defaultIconProps,
115
+ ...props
116
+ });
117
+ }
118
+ return SanityIcon;
119
+ }
120
+ function HashIcon(props) {
121
+ let $ = c(6), t0, t1, t2, t3;
122
+ $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = /* @__PURE__ */ jsx("line", {
123
+ x1: "4",
124
+ x2: "20",
125
+ y1: "9",
126
+ y2: "9"
127
+ }), t1 = /* @__PURE__ */ jsx("line", {
128
+ x1: "4",
129
+ x2: "20",
130
+ y1: "15",
131
+ y2: "15"
132
+ }), t2 = /* @__PURE__ */ jsx("line", {
133
+ x1: "10",
134
+ x2: "8",
135
+ y1: "3",
136
+ y2: "21"
137
+ }), t3 = /* @__PURE__ */ jsx("line", {
138
+ x1: "16",
139
+ x2: "14",
140
+ y1: "3",
141
+ y2: "21"
142
+ }), $[0] = t0, $[1] = t1, $[2] = t2, $[3] = t3) : (t0 = $[0], t1 = $[1], t2 = $[2], t3 = $[3]);
143
+ let t4;
144
+ return $[4] === props ? t4 = $[5] : (t4 = /* @__PURE__ */ jsxs("svg", {
145
+ xmlns: "http://www.w3.org/2000/svg",
146
+ width: 24,
147
+ height: 24,
148
+ viewBox: "0 0 24 24",
149
+ fill: "none",
150
+ stroke: "currentColor",
151
+ strokeWidth: 2,
152
+ strokeLinecap: "round",
153
+ strokeLinejoin: "round",
154
+ ...props,
155
+ children: [
156
+ t0,
157
+ t1,
158
+ t2,
159
+ t3
160
+ ]
161
+ }), $[4] = props, $[5] = t4), t4;
162
+ }
163
+ function Anchor(t0) {
164
+ let $ = c(8), { elementProps, value, onChange, readOnly } = t0, t1;
165
+ $[0] === onChange ? t1 = $[1] : (t1 = function handleChange(event) {
166
+ let next = event.currentTarget.value;
167
+ onChange(isDefined(next) ? set(next) : unset());
168
+ }, $[0] = onChange, $[1] = t1);
169
+ let handleChange = t1, t2;
170
+ $[2] === Symbol.for("react.memo_cache_sentinel") ? (t2 = /* @__PURE__ */ jsx(HashIcon, {
171
+ ...defaultIconProps,
172
+ width: "0.8em",
173
+ height: "0.8em"
174
+ }), $[2] = t2) : t2 = $[2];
175
+ let t3 = value ?? "", t4;
176
+ return $[3] !== elementProps || $[4] !== handleChange || $[5] !== readOnly || $[6] !== t3 ? (t4 = /* @__PURE__ */ jsx(TextInput, {
177
+ ...elementProps,
178
+ icon: t2,
179
+ value: t3,
180
+ readOnly,
181
+ onChange: handleChange,
182
+ placeholder: "our-values"
183
+ }), $[3] = elementProps, $[4] = handleChange, $[5] = readOnly, $[6] = t3, $[7] = t4) : t4 = $[7], t4;
184
+ }
185
+ function Hidden() {
186
+ return null;
187
+ }
188
+ /**
189
+ * Reads the host a web address points at, so a link to a file buried three folders deep still reads
190
+ * as the site it came from. The subdomain everyone ignores goes with it.
191
+ *
192
+ * @param url - The address to read.
193
+ * @returns The host, or undefined when the address cannot be read as one.
194
+ */
195
+ function getDomain(url) {
196
+ let address = stegaClean(url);
197
+ if (isDefined(address)) try {
198
+ return new URL(address).hostname.replace(/^www\./u, "");
199
+ } catch {
200
+ return;
201
+ }
202
+ }
203
+ /**
204
+ * Describes where a link leads using only what the document already holds, which covers every
205
+ * destination an author typed out in full.
206
+ *
207
+ * @param value - The link being authored.
208
+ * @returns The description, or undefined when the destination is stored as a pointer.
209
+ */
210
+ function getLocalDetail(value) {
211
+ if (isDefined(value)) {
212
+ if (value.type === "anchor") return composeAnchorHref(value.anchor);
213
+ if (value.type === "url") return getDomain(value.url);
214
+ if (value.type === "email") return stegaClean(value.email);
215
+ if (value.type === "phone") return stegaClean(value.phone);
216
+ }
217
+ }
218
+ /**
219
+ * Names the document a link leads to, for the two destinations stored as a pointer rather than as
220
+ * something an author wrote.
221
+ *
222
+ * @param value - The link being authored.
223
+ * @returns The document's identifier, or undefined when the destination holds none.
224
+ */
225
+ function getReferencedId(value) {
226
+ if (isDefined(value)) {
227
+ if (value.type === "page") {
228
+ let { reference } = value;
229
+ return isDefined(reference) && "_ref" in reference ? reference._ref : void 0;
230
+ }
231
+ if (value.type === "file") {
232
+ let asset = value.file?.asset;
233
+ return isDefined(asset) && "_ref" in asset ? asset._ref : void 0;
234
+ }
235
+ }
236
+ }
237
+ /**
238
+ * Builds the one line the closed control reads, putting what an author wrote first and where it
239
+ * leads in brackets behind it. Either alone stands on its own.
240
+ *
241
+ * @param label - Text the author wrote.
242
+ * @param detail - Where the link leads.
243
+ * @returns The line, or undefined when neither is known.
244
+ */
245
+ function composeSummary(label, detail) {
246
+ let written = stegaClean(label);
247
+ return isDefined(written) && isDefined(detail) ? `${written} (${detail})` : written ?? detail;
248
+ }
249
+ /**
250
+ * Describes where a link leads, reading the title or filename held by the document it points at.
251
+ * Read through the Studio's preview store, so links share one subscription and a rename updates them.
252
+ *
253
+ * @param value - The link being authored.
254
+ * @param titleField - Field an internal link's destination holds its title in.
255
+ * @returns Where the link leads, or undefined while it is being read or when there is nothing to read.
256
+ */
257
+ function useDestinationDetail(value, titleField) {
258
+ let $ = c(13), previewStore = useDocumentPreviewStore(), t0;
259
+ $[0] === value ? t0 = $[1] : (t0 = getLocalDetail(value), $[0] = value, $[1] = t0);
260
+ let local = t0, t1;
261
+ $[2] === value ? t1 = $[3] : (t1 = getReferencedId(value), $[2] = value, $[3] = t1);
262
+ let id = t1, isFile = value?.type === "file", [read, setRead] = useState(), t2, t3;
263
+ $[4] !== id || $[5] !== isFile || $[6] !== previewStore || $[7] !== titleField ? (t2 = () => {
264
+ if (!isDefined(id)) return;
265
+ let destinationId = id, path = (isFile ? "originalFilename" : titleField).split("."), subscription = previewStore.observePaths({ _ref: destinationId }, [path]).subscribe((document) => {
266
+ let detail = readPath(document, path);
267
+ setRead({
268
+ id: destinationId,
269
+ detail: typeof detail == "string" ? detail : void 0
270
+ });
271
+ });
272
+ return () => {
273
+ subscription.unsubscribe();
274
+ };
275
+ }, t3 = [
276
+ previewStore,
277
+ id,
278
+ isFile,
279
+ titleField
280
+ ], $[4] = id, $[5] = isFile, $[6] = previewStore, $[7] = titleField, $[8] = t2, $[9] = t3) : (t2 = $[8], t3 = $[9]), useEffect(t2, t3);
281
+ let t4;
282
+ return $[10] !== id || $[11] !== read ? (t4 = isDefined(read) && read.id === id ? read.detail : void 0, $[10] = id, $[11] = read, $[12] = t4) : t4 = $[12], local ?? t4;
283
+ }
284
+ function LinkIcon(props) {
285
+ let $ = c(4), t0, t1;
286
+ $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = /* @__PURE__ */ jsx("path", { d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" }), t1 = /* @__PURE__ */ jsx("path", { d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" }), $[0] = t0, $[1] = t1) : (t0 = $[0], t1 = $[1]);
287
+ let t2;
288
+ return $[2] === props ? t2 = $[3] : (t2 = /* @__PURE__ */ jsxs("svg", {
289
+ xmlns: "http://www.w3.org/2000/svg",
290
+ width: 24,
291
+ height: 24,
292
+ viewBox: "0 0 24 24",
293
+ fill: "none",
294
+ stroke: "currentColor",
295
+ strokeWidth: 2,
296
+ strokeLinecap: "round",
297
+ strokeLinejoin: "round",
298
+ ...props,
299
+ children: [t0, t1]
300
+ }), $[2] = props, $[3] = t2), t2;
301
+ }
302
+ function ExternalLinkIcon(props) {
303
+ let $ = c(5), t0, t1, t2;
304
+ $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = /* @__PURE__ */ jsx("path", { d: "M15 3h6v6" }), t1 = /* @__PURE__ */ jsx("path", { d: "M10 14 21 3" }), t2 = /* @__PURE__ */ jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }), $[0] = t0, $[1] = t1, $[2] = t2) : (t0 = $[0], t1 = $[1], t2 = $[2]);
305
+ let t3;
306
+ return $[3] === props ? t3 = $[4] : (t3 = /* @__PURE__ */ jsxs("svg", {
307
+ xmlns: "http://www.w3.org/2000/svg",
308
+ width: 24,
309
+ height: 24,
310
+ viewBox: "0 0 24 24",
311
+ fill: "none",
312
+ stroke: "currentColor",
313
+ strokeWidth: 2,
314
+ strokeLinecap: "round",
315
+ strokeLinejoin: "round",
316
+ ...props,
317
+ children: [
318
+ t0,
319
+ t1,
320
+ t2
321
+ ]
322
+ }), $[3] = props, $[4] = t3), t3;
323
+ }
324
+ function FileDownIcon(props) {
325
+ let $ = c(6), t0, t1, t2, t3;
326
+ $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = /* @__PURE__ */ jsx("path", { d: "M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" }), t1 = /* @__PURE__ */ jsx("path", { d: "M14 2v5a1 1 0 0 0 1 1h5" }), t2 = /* @__PURE__ */ jsx("path", { d: "M12 18v-6" }), t3 = /* @__PURE__ */ jsx("path", { d: "m9 15 3 3 3-3" }), $[0] = t0, $[1] = t1, $[2] = t2, $[3] = t3) : (t0 = $[0], t1 = $[1], t2 = $[2], t3 = $[3]);
327
+ let t4;
328
+ return $[4] === props ? t4 = $[5] : (t4 = /* @__PURE__ */ jsxs("svg", {
329
+ xmlns: "http://www.w3.org/2000/svg",
330
+ width: 24,
331
+ height: 24,
332
+ viewBox: "0 0 24 24",
333
+ fill: "none",
334
+ stroke: "currentColor",
335
+ strokeWidth: 2,
336
+ strokeLinecap: "round",
337
+ strokeLinejoin: "round",
338
+ ...props,
339
+ children: [
340
+ t0,
341
+ t1,
342
+ t2,
343
+ t3
344
+ ]
345
+ }), $[4] = props, $[5] = t4), t4;
346
+ }
347
+ function FileSymlinkIcon(props) {
348
+ let $ = c(5), t0, t1, t2;
349
+ $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = /* @__PURE__ */ jsx("path", { d: "M4 11V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h7" }), t1 = /* @__PURE__ */ jsx("path", { d: "M14 2v5a1 1 0 0 0 1 1h5" }), t2 = /* @__PURE__ */ jsx("path", { d: "m10 18 3-3-3-3" }), $[0] = t0, $[1] = t1, $[2] = t2) : (t0 = $[0], t1 = $[1], t2 = $[2]);
350
+ let t3;
351
+ return $[3] === props ? t3 = $[4] : (t3 = /* @__PURE__ */ jsxs("svg", {
352
+ xmlns: "http://www.w3.org/2000/svg",
353
+ width: 24,
354
+ height: 24,
355
+ viewBox: "0 0 24 24",
356
+ fill: "none",
357
+ stroke: "currentColor",
358
+ strokeWidth: 2,
359
+ strokeLinecap: "round",
360
+ strokeLinejoin: "round",
361
+ ...props,
362
+ children: [
363
+ t0,
364
+ t1,
365
+ t2
366
+ ]
367
+ }), $[3] = props, $[4] = t3), t3;
368
+ }
369
+ function MailIcon(props) {
370
+ let $ = c(4), t0, t1;
371
+ $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = /* @__PURE__ */ jsx("path", { d: "m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7" }), t1 = /* @__PURE__ */ jsx("rect", {
372
+ x: "2",
373
+ y: "4",
374
+ width: "20",
375
+ height: "16",
376
+ rx: "2"
377
+ }), $[0] = t0, $[1] = t1) : (t0 = $[0], t1 = $[1]);
378
+ let t2;
379
+ return $[2] === props ? t2 = $[3] : (t2 = /* @__PURE__ */ jsxs("svg", {
380
+ xmlns: "http://www.w3.org/2000/svg",
381
+ width: 24,
382
+ height: 24,
383
+ viewBox: "0 0 24 24",
384
+ fill: "none",
385
+ stroke: "currentColor",
386
+ strokeWidth: 2,
387
+ strokeLinecap: "round",
388
+ strokeLinejoin: "round",
389
+ ...props,
390
+ children: [t0, t1]
391
+ }), $[2] = props, $[3] = t2), t2;
392
+ }
393
+ function PhoneIcon(props) {
394
+ let $ = c(3), t0;
395
+ $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = /* @__PURE__ */ jsx("path", { d: "M13.832 16.568a1 1 0 0 0 1.213-.303l.355-.465A2 2 0 0 1 17 15h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2A18 18 0 0 1 2 4a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-.8 1.6l-.468.351a1 1 0 0 0-.292 1.233 14 14 0 0 0 6.392 6.384" }), $[0] = t0) : t0 = $[0];
396
+ let t1;
397
+ return $[1] === props ? t1 = $[2] : (t1 = /* @__PURE__ */ jsx("svg", {
398
+ xmlns: "http://www.w3.org/2000/svg",
399
+ width: 24,
400
+ height: 24,
401
+ viewBox: "0 0 24 24",
402
+ fill: "none",
403
+ stroke: "currentColor",
404
+ strokeWidth: 2,
405
+ strokeLinecap: "round",
406
+ strokeLinejoin: "round",
407
+ ...props,
408
+ children: t0
409
+ }), $[1] = props, $[2] = t1), t1;
410
+ }
411
+ /**
412
+ * The kinds of destination a link may point at, offered in this order. The stored union, the schema's
413
+ * fields, the tabs, and the resolver all follow it.
414
+ */
415
+ const linkTypes = [
416
+ {
417
+ name: "page",
418
+ icon: FileSymlinkIcon,
419
+ label: "Page",
420
+ fields: [
421
+ "reference",
422
+ "anchor",
423
+ "searchParams"
424
+ ]
425
+ },
426
+ {
427
+ name: "anchor",
428
+ icon: HashIcon,
429
+ label: "Anchor",
430
+ fields: ["anchor"]
431
+ },
432
+ {
433
+ name: "url",
434
+ icon: ExternalLinkIcon,
435
+ label: "URL",
436
+ fields: ["url"]
437
+ },
438
+ {
439
+ name: "email",
440
+ icon: MailIcon,
441
+ label: "Email",
442
+ fields: ["email", "subject"]
443
+ },
444
+ {
445
+ name: "phone",
446
+ icon: PhoneIcon,
447
+ label: "Phone",
448
+ fields: ["phone"]
449
+ },
450
+ {
451
+ name: "file",
452
+ icon: FileDownIcon,
453
+ label: "File",
454
+ fields: ["file"]
455
+ }
456
+ ], defaultDestinations = linkTypes.map((linkType) => linkType.name);
457
+ /**
458
+ * Reads which kind of destination a stored value names, so the tabs and the preview describe the same
459
+ * choice. A value the plugin no longer offers reads as no choice at all.
460
+ *
461
+ * @param name - The stored type value.
462
+ * @returns The matching link type, or undefined when nothing recognisable was stored.
463
+ */
464
+ function getLinkType(name) {
465
+ return linkTypes.find((linkType) => linkType.name === name);
466
+ }
467
+ /**
468
+ * Names the fields every other destination owns, so moving to one clears what the last one wrote. A
469
+ * field the destination being moved to also owns is kept, since a page and a section share the anchor.
470
+ *
471
+ * @param type - The destination being moved to.
472
+ * @returns Fields belonging to the destinations being left.
473
+ */
474
+ function getStaleFields(type) {
475
+ let kept = new Set(getLinkType(type)?.fields);
476
+ return linkTypes.filter((linkType) => linkType.name !== type).flatMap((linkType) => [...linkType.fields]).filter((field) => !kept.has(field));
477
+ }
478
+ /**
479
+ * Checks whether the destination a link points at shows a given field, so a field's visibility is
480
+ * read from the one table that says which destination owns what.
481
+ *
482
+ * @param parent - The link the field belongs to.
483
+ * @param field - Name of the field to check for.
484
+ * @returns Whether the link's destination shows that field.
485
+ */
486
+ function showsField(parent, field) {
487
+ return linkTypes.some((linkType) => linkType.fields.some((owned) => owned === field) && isLinkType(parent, linkType.name));
488
+ }
489
+ /**
490
+ * Checks the kind of destination the link a field belongs to points at, so a field is shown and
491
+ * required only for the link type that uses it.
492
+ *
493
+ * @param parent - The link the field belongs to.
494
+ * @param type - The link type to check for.
495
+ * @returns Whether the link points at that kind of destination.
496
+ */
497
+ function isLinkType(parent, type) {
498
+ return typeof parent == "object" && !!parent && "type" in parent && parent.type === type;
499
+ }
500
+ /**
501
+ * Checks whether the link a field belongs to was authored as an annotation, where the text it wraps
502
+ * supplies what the field would otherwise hold.
503
+ *
504
+ * @param parent - The link the field belongs to.
505
+ * @returns Whether the link is a text annotation.
506
+ */
507
+ function isLinkMark(parent) {
508
+ return typeof parent == "object" && !!parent && "_type" in parent && parent._type === "linkMark";
509
+ }
510
+ /**
511
+ * Settles what a link field offers. The list says what is on and replaces whatever the plugin was
512
+ * given, so one line tells you the whole answer.
513
+ *
514
+ * @param options - Options the field itself was given.
515
+ * @param config - Configuration the plugin was given.
516
+ * @returns The settings the field runs on.
517
+ */
518
+ function resolveLinkOptions(options, config) {
519
+ return { destinations: options?.destinations ?? config.destinations ?? defaultDestinations };
520
+ }
521
+ /**
522
+ * Narrows the destinations to those on offer, in the order they were named. A name this version does
523
+ * not recognise is skipped, and a set that leaves nothing offers them all rather than a field with
524
+ * nowhere to point.
525
+ *
526
+ * @param destinations - Destinations to offer.
527
+ * @returns The destinations on offer, of which there is always at least one.
528
+ */
529
+ function getOfferedLinkTypes(destinations) {
530
+ let [offered, ...rest] = [...new Set(destinations)].flatMap((name) => {
531
+ let linkType = getLinkType(name);
532
+ return isDefined(linkType) ? [linkType] : [];
533
+ }), [everything, ...others] = linkTypes;
534
+ return isDefined(offered) ? [offered, ...rest] : [everything, ...others];
535
+ }
536
+ function Destinations(t0) {
537
+ let $ = c(16), { id, offered, selected, showTabs, readOnly, onSelect, children } = t0, t1;
538
+ $[0] !== id || $[1] !== offered || $[2] !== onSelect || $[3] !== readOnly || $[4] !== selected || $[5] !== showTabs ? (t1 = showTabs && /* @__PURE__ */ jsx(TabList, {
539
+ gap: 1,
540
+ children: offered.map((linkType) => /* @__PURE__ */ jsx(Tab, {
541
+ id: `${id}-tab-${linkType.name}`,
542
+ "aria-controls": `${id}-panel`,
543
+ icon: /* @__PURE__ */ jsx(linkType.icon, { ...defaultIconProps }),
544
+ label: linkType.label,
545
+ selected: linkType.name === selected.name,
546
+ disabled: readOnly,
547
+ onClick: () => {
548
+ onSelect(linkType.name);
549
+ }
550
+ }, linkType.name))
551
+ }), $[0] = id, $[1] = offered, $[2] = onSelect, $[3] = readOnly, $[4] = selected, $[5] = showTabs, $[6] = t1) : t1 = $[6];
552
+ let t2;
553
+ $[7] !== children || $[8] !== id || $[9] !== selected || $[10] !== showTabs ? (t2 = showTabs && /* @__PURE__ */ jsx(TabPanel, {
554
+ id: `${id}-panel`,
555
+ "aria-labelledby": `${id}-tab-${selected.name}`,
556
+ paddingTop: 3,
557
+ children
558
+ }), $[7] = children, $[8] = id, $[9] = selected, $[10] = showTabs, $[11] = t2) : t2 = $[11];
559
+ let t3 = !showTabs && children, t4;
560
+ return $[12] !== t1 || $[13] !== t2 || $[14] !== t3 ? (t4 = /* @__PURE__ */ jsxs(Stack, {
561
+ gap: 4,
562
+ children: [
563
+ t1,
564
+ t2,
565
+ t3
566
+ ]
567
+ }), $[12] = t1, $[13] = t2, $[14] = t3, $[15] = t4) : t4 = $[15], t4;
568
+ }
569
+ /**
570
+ * Builds the patches that move a link to a destination, clearing whatever the last one wrote. A
571
+ * destination moved away from would otherwise stay in the document unseen, and reappear the moment
572
+ * an author moved the tab back.
573
+ *
574
+ * @param typeName - Name the link type is registered under.
575
+ * @param type - The destination being moved to.
576
+ * @returns The patches to apply.
577
+ */
578
+ function createDestinationPatches(typeName, type) {
579
+ return [
580
+ setIfMissing({ _type: typeName }),
581
+ set(type, ["type"]),
582
+ ...getStaleFields(type).map((field) => unset([field]))
583
+ ];
584
+ }
585
+ /**
586
+ * Creates the input a link field is drawn with, holding what the plugin was configured with so a
587
+ * field naming its own destinations replaces them rather than adding to them.
588
+ *
589
+ * @param titleField - Field an internal link's destination holds its title in.
590
+ * @param config - Configuration the plugin was given.
591
+ * @returns The input component.
592
+ */
593
+ function createInput(titleField, config) {
594
+ function Input(props) {
595
+ let $ = c(22), { id, path, schemaType, value, onChange, readOnly, renderDefault } = props, [open, setOpen] = useState(!1), { destinations } = resolveLinkOptions(schemaType.options, config), offered = getOfferedLinkTypes(destinations), [defaultLinkType] = offered, stored = getLinkType(value?.type), selected = stored ?? defaultLinkType, detail = useDestinationDetail(value, titleField), summary = composeSummary(value?.label, detail) ?? stored?.label, Icon = stored?.icon ?? LinkIcon, t0;
596
+ $[0] === path ? t0 = $[1] : (t0 = path.at(-1), $[0] = path, $[1] = t0);
597
+ let isArrayItem = typeof t0 == "object", showTabs = offered.length > 1, t1;
598
+ $[2] !== onChange || $[3] !== schemaType.name ? (t1 = function handleSelectType(type) {
599
+ onChange(createDestinationPatches(schemaType.name, type));
600
+ }, $[2] = onChange, $[3] = schemaType.name, $[4] = t1) : t1 = $[4];
601
+ let handleSelectType = t1;
602
+ if (useEffect(() => {
603
+ isArrayItem && !isDefined(stored) && onChange(createDestinationPatches(schemaType.name, selected.name));
604
+ }, [
605
+ isArrayItem,
606
+ stored,
607
+ selected,
608
+ onChange,
609
+ schemaType.name
610
+ ]), isArrayItem) {
611
+ let t2;
612
+ return $[5] !== props || $[6] !== renderDefault ? (t2 = renderDefault(props), $[5] = props, $[6] = renderDefault, $[7] = t2) : t2 = $[7], /* @__PURE__ */ jsx(Destinations, {
613
+ id,
614
+ offered,
615
+ selected,
616
+ showTabs,
617
+ readOnly,
618
+ onSelect: handleSelectType,
619
+ children: t2
620
+ });
621
+ }
622
+ let t2;
623
+ $[8] !== onChange || $[9] !== readOnly || $[10] !== value ? (t2 = isDefined(value) && /* @__PURE__ */ jsx(Button, {
624
+ type: "button",
625
+ mode: "ghost",
626
+ tone: "critical",
627
+ icon: /* @__PURE__ */ jsx(CloseIcon, {}),
628
+ text: "Clear",
629
+ disabled: readOnly,
630
+ onClick: () => {
631
+ onChange(unset());
632
+ },
633
+ "aria-label": "Clear this link"
634
+ }), $[8] = onChange, $[9] = readOnly, $[10] = value, $[11] = t2) : t2 = $[11];
635
+ let t3;
636
+ return $[12] !== handleSelectType || $[13] !== id || $[14] !== offered || $[15] !== open || $[16] !== props || $[17] !== readOnly || $[18] !== renderDefault || $[19] !== selected || $[20] !== showTabs ? (t3 = open && /* @__PURE__ */ jsx(Dialog, {
637
+ id: `${id}-dialog`,
638
+ header: "Link",
639
+ width: 1,
640
+ onClose: () => {
641
+ setOpen(!1);
642
+ },
643
+ onClickOutside: () => {
644
+ setOpen(!1);
645
+ },
646
+ children: /* @__PURE__ */ jsx(Box, {
647
+ padding: 4,
648
+ children: /* @__PURE__ */ jsx(Destinations, {
649
+ id,
650
+ offered,
651
+ selected,
652
+ showTabs,
653
+ readOnly,
654
+ onSelect: handleSelectType,
655
+ children: renderDefault(props)
656
+ })
657
+ })
658
+ }), $[12] = handleSelectType, $[13] = id, $[14] = offered, $[15] = open, $[16] = props, $[17] = readOnly, $[18] = renderDefault, $[19] = selected, $[20] = showTabs, $[21] = t3) : t3 = $[21], /* @__PURE__ */ jsxs(Flex, {
659
+ gap: 2,
660
+ children: [
661
+ /* @__PURE__ */ jsx(Button, {
662
+ id,
663
+ type: "button",
664
+ mode: "ghost",
665
+ icon: /* @__PURE__ */ jsx(Icon, { ...defaultIconProps }),
666
+ text: summary ?? "Add link",
667
+ disabled: readOnly,
668
+ onClick: () => {
669
+ isDefined(stored) || handleSelectType(selected.name), setOpen(!0);
670
+ },
671
+ "aria-label": isDefined(summary) ? `Edit link: ${summary}` : "Add link"
672
+ }),
673
+ t2,
674
+ t3
675
+ ]
676
+ });
677
+ }
678
+ return Input;
679
+ }
680
+ /**
681
+ * Reads a row's pair, which arrives as the bare keyed item the array input is typed with rather than
682
+ * as the parameter it holds.
683
+ *
684
+ * @param param - The stored row.
685
+ * @returns The name and value the row carries, either of which may be unwritten.
686
+ */
687
+ function readParam(param) {
688
+ return {
689
+ key: "key" in param && typeof param.key == "string" ? param.key : void 0,
690
+ value: "value" in param && typeof param.value == "string" ? param.value : void 0
691
+ };
692
+ }
693
+ function SearchParams(t0) {
694
+ let $ = c(24), { id, value, onChange, readOnly } = t0, t1;
695
+ $[0] === value ? t1 = $[1] : (t1 = value ?? [], $[0] = value, $[1] = t1);
696
+ let params = t1, t2;
697
+ $[2] === onChange ? t2 = $[3] : (t2 = function handleEdit(key, field, next) {
698
+ onChange(isDefined(next) ? set(next, [{ _key: key }, field]) : unset([{ _key: key }, field]));
699
+ }, $[2] = onChange, $[3] = t2);
700
+ let handleEdit = t2, t3;
701
+ if ($[4] !== handleEdit || $[5] !== id || $[6] !== onChange || $[7] !== params || $[8] !== readOnly) {
702
+ let t4;
703
+ $[10] !== handleEdit || $[11] !== id || $[12] !== onChange || $[13] !== readOnly ? (t4 = (param, index) => {
704
+ let { key: key_0, value: parameterValue } = readParam(param), position = String(index + 1);
705
+ return /* @__PURE__ */ jsxs(Flex, {
706
+ gap: 2,
707
+ align: "center",
708
+ children: [
709
+ /* @__PURE__ */ jsx(Box, {
710
+ flex: 1,
711
+ children: /* @__PURE__ */ jsx(TextInput, {
712
+ id: index === 0 ? id : void 0,
713
+ value: key_0 ?? "",
714
+ placeholder: "Name",
715
+ readOnly,
716
+ onChange: (event) => {
717
+ handleEdit(param._key, "key", event.currentTarget.value);
718
+ },
719
+ "aria-label": `Name of parameter ${position}`
720
+ })
721
+ }),
722
+ /* @__PURE__ */ jsx(Box, {
723
+ flex: 1,
724
+ children: /* @__PURE__ */ jsx(TextInput, {
725
+ value: parameterValue ?? "",
726
+ placeholder: "Value",
727
+ readOnly,
728
+ onChange: (event_0) => {
729
+ handleEdit(param._key, "value", event_0.currentTarget.value);
730
+ },
731
+ "aria-label": `Value of parameter ${position}`
732
+ })
733
+ }),
734
+ /* @__PURE__ */ jsx(Button, {
735
+ type: "button",
736
+ mode: "bleed",
737
+ tone: "critical",
738
+ icon: /* @__PURE__ */ jsx(CloseIcon, {}),
739
+ disabled: readOnly,
740
+ onClick: () => {
741
+ onChange(unset([{ _key: param._key }]));
742
+ },
743
+ "aria-label": `Remove parameter ${position}`
744
+ })
745
+ ]
746
+ }, param._key);
747
+ }, $[10] = handleEdit, $[11] = id, $[12] = onChange, $[13] = readOnly, $[14] = t4) : t4 = $[14], t3 = params.map(t4), $[4] = handleEdit, $[5] = id, $[6] = onChange, $[7] = params, $[8] = readOnly, $[9] = t3;
748
+ } else t3 = $[9];
749
+ let t4;
750
+ $[15] === Symbol.for("react.memo_cache_sentinel") ? (t4 = /* @__PURE__ */ jsx(AddIcon, {}), $[15] = t4) : t4 = $[15];
751
+ let t5;
752
+ $[16] === onChange ? t5 = $[17] : (t5 = () => {
753
+ onChange([setIfMissing([]), insert([{
754
+ _type: searchParamTypeName,
755
+ _key: randomKey(12)
756
+ }], "after", [-1])]);
757
+ }, $[16] = onChange, $[17] = t5);
758
+ let t6;
759
+ $[18] !== readOnly || $[19] !== t5 ? (t6 = /* @__PURE__ */ jsx(Flex, { children: /* @__PURE__ */ jsx(Button, {
760
+ type: "button",
761
+ mode: "ghost",
762
+ icon: t4,
763
+ text: "Add parameter",
764
+ disabled: readOnly,
765
+ onClick: t5
766
+ }) }), $[18] = readOnly, $[19] = t5, $[20] = t6) : t6 = $[20];
767
+ let t7;
768
+ return $[21] !== t3 || $[22] !== t6 ? (t7 = /* @__PURE__ */ jsxs(Stack, {
769
+ gap: 2,
770
+ children: [t3, t6]
771
+ }), $[21] = t3, $[22] = t6, $[23] = t7) : t7 = $[23], t7;
772
+ }
773
+ /**
774
+ * Creates the object type a link is stored as, offering internal pages among the document types it
775
+ * is given and borrowing a label from whichever field those documents hold their title in.
776
+ *
777
+ * @param config - Configuration the plugin was given.
778
+ * @returns An object type definition for a stored link.
779
+ */
780
+ function createLinkType(config) {
781
+ let { documentTypes, title: { field: titleField = defaultTitleField } = {} } = config;
782
+ return defineType({
783
+ name: linkTypeName,
784
+ type: "object",
785
+ icon: createSanityIcon(LinkIcon),
786
+ description: "Link to a page, a URL, an email address, a phone number, or a file.",
787
+ components: {
788
+ field: Field,
789
+ input: createInput(titleField, config)
790
+ },
791
+ validation: (rule) => rule.custom((value) => !isDefined(value) || isDefined(value.type) ? !0 : "Choose a destination."),
792
+ preview: {
793
+ select: {
794
+ type: "type",
795
+ title: `reference.${titleField}`,
796
+ label: "label"
797
+ },
798
+ prepare(selection) {
799
+ let { type, title, label } = selection, linkType = getLinkType(type);
800
+ return {
801
+ title: label ?? title ?? linkType?.label ?? "Link",
802
+ media: createSanityIcon(linkType?.icon ?? LinkIcon)
803
+ };
804
+ }
805
+ },
806
+ fields: [
807
+ defineField({
808
+ name: "type",
809
+ type: "string",
810
+ description: "Which kind of destination this link points at, chosen from the tabs above.",
811
+ components: { field: Hidden }
812
+ }),
813
+ defineField({
814
+ name: "reference",
815
+ type: "reference",
816
+ description: "Page on this site to link to, which keeps working if its address changes.",
817
+ to: documentTypes.map((type) => ({ type })),
818
+ hidden: ({ parent }) => !showsField(parent, "reference"),
819
+ validation: (rule) => rule.custom((value, context) => isLinkType(context.parent, "page") && !isDefined(value) ? "Choose the page this link opens." : !0)
820
+ }),
821
+ defineField({
822
+ name: "url",
823
+ type: "url",
824
+ title: "URL",
825
+ description: "Full web address to link to, including the https:// in front of it.",
826
+ hidden: ({ parent }) => !showsField(parent, "url"),
827
+ validation: (rule) => rule.uri({ scheme: ["http", "https"] }).custom((value, context) => isLinkType(context.parent, "url") && !isDefined(value) ? "Enter the web address this link opens." : !0)
828
+ }),
829
+ defineField({
830
+ name: "email",
831
+ type: "string",
832
+ description: "Address a message opens to when a visitor follows this link.",
833
+ hidden: ({ parent }) => !showsField(parent, "email"),
834
+ validation: (rule) => rule.email().custom((value, context) => isLinkType(context.parent, "email") && !isDefined(value) ? "Enter the address this link opens a message to." : !0)
835
+ }),
836
+ defineField({
837
+ name: "subject",
838
+ type: "string",
839
+ description: "Optional subject line, filled in for the visitor before they start writing.",
840
+ hidden: ({ parent }) => !showsField(parent, "subject")
841
+ }),
842
+ defineField({
843
+ name: "phone",
844
+ type: "string",
845
+ description: "Number to call, written however it reads best. Spacing is ignored.",
846
+ hidden: ({ parent }) => !showsField(parent, "phone"),
847
+ validation: (rule) => rule.regex(/^\+?[\d\s()-]{6,}$/u, { name: "phone number" }).custom((value, context) => isLinkType(context.parent, "phone") && !isDefined(value) ? "Enter the number this link calls." : !0)
848
+ }),
849
+ defineField({
850
+ name: "file",
851
+ type: "file",
852
+ description: "File a visitor downloads, uploaded here so it stays with the link.",
853
+ hidden: ({ parent }) => !showsField(parent, "file"),
854
+ validation: (rule) => rule.custom((value, context) => isLinkType(context.parent, "file") && !isDefined(value) ? "Upload the file this link serves." : !0)
855
+ }),
856
+ defineField({
857
+ name: "anchor",
858
+ type: "string",
859
+ description: "Section of the page to send the visitor to, named without its leading hash.",
860
+ hidden: ({ parent }) => !showsField(parent, "anchor"),
861
+ components: { input: Anchor },
862
+ validation: (rule) => rule.custom((value, context) => isDefined(value) ? value === convertCase(value, "kebab") || "Write the section name in lowercase, with hyphens instead of spaces." : !isLinkType(context.parent, "anchor") || "Name the section this link scrolls to.")
863
+ }),
864
+ defineField({
865
+ name: "searchParams",
866
+ type: "array",
867
+ title: "Search Parameters",
868
+ description: "Extra values carried in the address, often recording where a visitor came from.",
869
+ hidden: ({ parent }) => !showsField(parent, "searchParams"),
870
+ components: { input: SearchParams },
871
+ validation: (rule) => rule.custom((value) => !isDefined(value) || value.every(({ key, value: carried }) => !isDefined(carried) || isDefined(key)) ? !0 : "Name every parameter that carries a value."),
872
+ of: [defineArrayMember({
873
+ name: searchParamTypeName,
874
+ type: "object",
875
+ preview: {
876
+ select: {
877
+ key: "key",
878
+ value: "value"
879
+ },
880
+ prepare(selection) {
881
+ let { key, value } = selection;
882
+ return {
883
+ title: key ?? "Parameter",
884
+ subtitle: value
885
+ };
886
+ }
887
+ },
888
+ fields: [defineField({
889
+ name: "key",
890
+ type: "string",
891
+ description: "Name this value is read under."
892
+ }), defineField({
893
+ name: "value",
894
+ type: "string",
895
+ description: "Value carried under that name."
896
+ })]
897
+ })]
898
+ }),
899
+ defineField({
900
+ name: "label",
901
+ type: "string",
902
+ description: "Text a visitor clicks, which should still make sense out of context.",
903
+ hidden: ({ parent }) => isLinkMark(parent) || !linkTypes.some(({ name }) => isLinkType(parent, name)),
904
+ validation: (rule) => rule.custom((value, context) => {
905
+ let { parent } = context;
906
+ return isLinkMark(parent) || isLinkType(parent, "page") ? !0 : linkTypes.some(({ name }) => isLinkType(parent, name)) && !isDefined(value) ? "Write the text a visitor clicks." : !0;
907
+ })
908
+ })
909
+ ]
910
+ });
911
+ }
912
+ /**
913
+ * Creates the link field type for Sanity Studio, offering a page, a section of it, an address, an
914
+ * email, a phone number, or a file behind one control, and resolving nothing about routing itself.
915
+ *
916
+ * @param config - Plugin configuration.
917
+ * @returns Sanity plugin definition.
918
+ * @public
919
+ */
920
+ const linkPlugin = definePlugin((config) => ({
921
+ name: pluginName,
922
+ schema: { types: [createLinkType(config)] }
923
+ })), linkAnnotation = defineArrayMember({
924
+ name: linkMarkTypeName,
925
+ type: linkTypeName,
926
+ title: "Link",
927
+ icon: createSanityIcon(LinkIcon)
928
+ });
929
+ /**
930
+ * Builds the annotation a pasted address becomes, so an address arriving as markup lands on the link
931
+ * type this plugin registers.
932
+ *
933
+ * @param schema - Schema of the editor the paste landed in.
934
+ * @param url - Address the pasted link points at.
935
+ * @returns The annotation to apply, or undefined when the editor offers no link annotation.
936
+ */
937
+ function createUrlLink(schema, url) {
938
+ let annotation = schema.annotations.find(({ name }) => name === linkMarkTypeName);
939
+ if (isDefined(annotation)) return {
940
+ _type: linkMarkTypeName,
941
+ type: "url",
942
+ url
943
+ };
944
+ }
945
+ /**
946
+ * Keeps hyperlinks when formatted text is pasted. Sanity's own handling recognises only its default
947
+ * annotation shape, so a pasted link would otherwise arrive as plain text.
948
+ */
949
+ const pasteUrlBehavior = defineBehavior({
950
+ on: "clipboard.paste",
951
+ guard: ({ snapshot, event }) => {
952
+ let html = event.originEvent.dataTransfer.getData("text/html");
953
+ return /<a\s[^>]*href/iu.test(html) ? { blocks: htmlToPortableText(html, {
954
+ schema: snapshot.context.schema,
955
+ keyGenerator: snapshot.context.keyGenerator,
956
+ rules: [{ deserialize(element, next) {
957
+ if (!(element instanceof Element) || element.nodeName.toLowerCase() !== "a") return;
958
+ let url = element.getAttribute("href"), link = isDefined(url) ? createUrlLink(snapshot.context.schema, url) : void 0;
959
+ return isDefined(link) ? {
960
+ _type: "__annotation",
961
+ markDef: {
962
+ ...link,
963
+ _key: snapshot.context.keyGenerator()
964
+ },
965
+ children: next(element.childNodes)
966
+ } : next(element.childNodes);
967
+ } }]
968
+ }) } : !1;
969
+ },
970
+ actions: [({ snapshot }, { blocks }) => [...isSelectionExpanded(snapshot) ? [raise({ type: "delete" })] : [], raise({
971
+ type: "insert.blocks",
972
+ blocks,
973
+ placement: "auto"
974
+ })]]
975
+ });
976
+ /**
977
+ * Writes this plugin's link when an author pastes one, from formatted markup or a bare address. Pass
978
+ * it to a text type's `components.portableText.plugins`.
979
+ *
980
+ * @param props - Portable Text plugin props the Studio supplies.
981
+ * @returns The editor's own plugins, with link pasting replaced.
982
+ * @public
983
+ */
984
+ function PortableTextLinkPlugins(props) {
985
+ let $ = c(5), editor = useEditor(), t0, t1;
986
+ $[0] === editor ? (t0 = $[1], t1 = $[2]) : (t0 = () => {
987
+ let unregister = editor.registerBehavior({ behavior: pasteUrlBehavior });
988
+ return () => {
989
+ unregister();
990
+ };
991
+ }, t1 = [editor], $[0] = editor, $[1] = t0, $[2] = t1), useEffect(t0, t1);
992
+ let t2;
993
+ return $[3] === props ? t2 = $[4] : (t2 = props.renderDefault({
994
+ ...props,
995
+ plugins: {
996
+ ...props.plugins,
997
+ pasteLink: {
998
+ ...props.plugins.pasteLink,
999
+ link: _temp
1000
+ }
1001
+ }
1002
+ }), $[3] = props, $[4] = t2), t2;
1003
+ }
1004
+ function _temp(t0) {
1005
+ let { context, value } = t0;
1006
+ return createUrlLink(context.schema, value.href);
1007
+ }
1008
+ export { PortableTextLinkPlugins, linkAnnotation, linkPlugin };
1009
+
1010
+ //# sourceMappingURL=index.js.map