@madebywild/sanity-link-field 0.3.21 → 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Made by Wild
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1 +1,59 @@
1
+ > [!IMPORTANT]
2
+ > This package is primarily intended for internal use.
3
+
1
4
  # @madebywild/sanity-link-field
5
+
6
+ Sanity object field for internal, external, email, phone, and file links.
7
+
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pnpm add @madebywild/sanity-link-field
13
+ ```
14
+
15
+ ## Configure Plugin
16
+
17
+ ```ts
18
+ import { wildSanityLinkFieldPlugin } from "@madebywild/sanity-link-field";
19
+
20
+ export default defineConfig({
21
+ plugins: [
22
+ wildSanityLinkFieldPlugin({
23
+ internalLinkSchemaTypes: [{ type: "page" }, { type: "post" }],
24
+ // Defaults to true.
25
+ weakReferences: true,
26
+ // Optional section target selector config for internal links.
27
+ sectionTarget: {
28
+ enabled: true,
29
+ query: `
30
+ *[_id == $pageId][0]{
31
+ "sections": array::compact(contentBuilder.blocks[]{
32
+ "value": _key,
33
+ "label": coalesce(settings.title, _type),
34
+ }),
35
+ }.sections
36
+ `,
37
+ },
38
+ }),
39
+ ],
40
+ });
41
+ ```
42
+
43
+ ## Use in Schema
44
+
45
+ ```ts
46
+ import { requireLink } from "@madebywild/sanity-link-field";
47
+
48
+ defineField({
49
+ name: "cta",
50
+ type: "wild.link",
51
+ options: {
52
+ extensions: {
53
+ // Optional custom text field
54
+ customText: true,
55
+ },
56
+ },
57
+ validation: (R) => requireLink(R),
58
+ });
59
+ ```
package/dist/index.cjs CHANGED
@@ -17,14 +17,7 @@ function _interopNamespaceCompat(e) {
17
17
  }), n.default = e, Object.freeze(n);
18
18
  }
19
19
  var changeCase__namespace = /* @__PURE__ */ _interopNamespaceCompat(changeCase), React__namespace = /* @__PURE__ */ _interopNamespaceCompat(React);
20
- function FieldInput(props) {
21
- const $ = compilerRuntime.c(5), extensions = props.schemaType.options?.extensions;
22
- let t0;
23
- $[0] !== extensions || $[1] !== props ? (t0 = props.members.map((member) => member.kind !== "field" || !extensions?.includes("customText") && member.name === "customText" ? null : /* @__PURE__ */ jsxRuntime.jsx(sanity.MemberField, { member, ...props }, member.key)), $[0] = extensions, $[1] = props, $[2] = t0) : t0 = $[2];
24
- let t1;
25
- return $[3] !== t0 ? (t1 = /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 4, children: t0 }), $[3] = t0, $[4] = t1) : t1 = $[4], t1;
26
- }
27
- const SectionsQuery = `
20
+ const DefaultSectionsQuery = `
28
21
  *[_id == $pageId && defined(pageBuilder.sectionsArray)][0]{
29
22
  "sections": array::compact(pageBuilder.sectionsArray[]{
30
23
  "value": _key,
@@ -32,26 +25,62 @@ const SectionsQuery = `
32
25
  }),
33
26
  }.sections
34
27
  `;
35
- function InternalLinkInput(props) {
36
- const linkFieldMember = sanityUtils.useFieldMember(props.members, "link"), selectedLink = props.value?.link, selectedSection = props.value?.sectionTarget;
28
+ function isExtensionEnabled(extensions, key) {
29
+ return extensions[key] && (extensions[key] === !0 || extensions[key].enabled !== !1);
30
+ }
31
+ function getSectionTargetConfig(sectionTarget) {
32
+ return sectionTarget === void 0 || sectionTarget === !0 ? {
33
+ enabled: !0,
34
+ query: DefaultSectionsQuery
35
+ } : sectionTarget === !1 ? {
36
+ enabled: !1,
37
+ query: DefaultSectionsQuery
38
+ } : {
39
+ enabled: sectionTarget.enabled !== !1,
40
+ query: sectionTarget.query ?? DefaultSectionsQuery
41
+ };
42
+ }
43
+ function useFieldMember(members, fieldName) {
44
+ const $ = compilerRuntime.c(5);
45
+ let t0;
46
+ if ($[0] !== fieldName || $[1] !== members) {
47
+ let t1;
48
+ $[3] !== fieldName ? (t1 = (member) => member.kind === "field" && member.name === fieldName, $[3] = fieldName, $[4] = t1) : t1 = $[4], t0 = members.find(t1), $[0] = fieldName, $[1] = members, $[2] = t0;
49
+ } else
50
+ t0 = $[2];
51
+ return t0;
52
+ }
53
+ function FieldInput(props) {
54
+ const $ = compilerRuntime.c(5), extensions = props.schemaType.options?.extensions;
55
+ let t0;
56
+ $[0] !== extensions || $[1] !== props ? (t0 = props.members.map((member) => member.kind !== "field" || !(extensions && isExtensionEnabled(extensions, "customText")) && member.name === "customText" ? null : /* @__PURE__ */ jsxRuntime.jsx(sanity.MemberField, { member, ...props }, member.key)), $[0] = extensions, $[1] = props, $[2] = t0) : t0 = $[2];
57
+ let t1;
58
+ return $[3] !== t0 ? (t1 = /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, { space: 4, children: t0 }), $[3] = t0, $[4] = t1) : t1 = $[4], t1;
59
+ }
60
+ function InternalLinkInput({
61
+ pluginConfig,
62
+ ...props
63
+ }) {
64
+ const linkFieldMember = useFieldMember(props.members, "link"), selectedLink = props.value?.link, selectedSection = props.value?.sectionTarget, sectionTargetConfig = getSectionTargetConfig(pluginConfig?.sectionTarget), sectionTargetEnabled = sectionTargetConfig.enabled;
37
65
  React__namespace.useEffect(() => {
38
- !selectedLink?._ref && selectedSection !== void 0 && props.onChange(sanity.unset(["sectionTarget"]));
39
- }, [selectedLink, selectedSection, props.onChange]);
66
+ (!selectedLink?._ref || !sectionTargetEnabled) && selectedSection !== void 0 && props.onChange(sanity.unset(["sectionTarget"]));
67
+ }, [selectedLink, selectedSection, sectionTargetEnabled, props.onChange]);
40
68
  const client = sanity.useClient({
41
69
  apiVersion: "2025-10-21"
42
70
  }), fetchSections = React__namespace.useCallback(async () => {
43
- if (!selectedLink?._ref) return [];
71
+ if (!selectedLink?._ref || !sectionTargetEnabled) return [];
44
72
  try {
45
- return await client.fetch(SectionsQuery, {
73
+ const items = await client.fetch(sectionTargetConfig.query, {
46
74
  pageId: selectedLink._ref
47
75
  });
76
+ return Array.isArray(items) ? items : items && typeof items == "object" && "sections" in items && Array.isArray(items.sections) ? items.sections : [];
48
77
  } catch {
49
78
  return [];
50
79
  }
51
- }, [client, selectedLink?._ref]);
80
+ }, [client, selectedLink?._ref, sectionTargetEnabled, sectionTargetConfig.query]);
52
81
  return /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, { space: 2, children: [
53
82
  linkFieldMember && /* @__PURE__ */ jsxRuntime.jsx(sanity.MemberField, { member: linkFieldMember, ...props }),
54
- selectedLink?._ref && /* @__PURE__ */ jsxRuntime.jsx(asyncAutocomplete.AsyncAutocomplete, { placeholder: "Select section", noOptionsPlaceholder: "No sections found", listItems: fetchSections, value: selectedSection, renderValue: (value, opt) => opt?.label ? changeCase__namespace.capitalCase(opt.label) : value, onChange: (value_0) => {
83
+ sectionTargetEnabled && selectedLink?._ref && /* @__PURE__ */ jsxRuntime.jsx(asyncAutocomplete.AsyncAutocomplete, { placeholder: "Select section", noOptionsPlaceholder: "No sections found", listItems: fetchSections, value: selectedSection, renderValue: (value, opt) => opt?.label ? changeCase__namespace.capitalCase(opt.label) : value, onChange: (value_0) => {
55
84
  const next = value_0 ? sanity.set(value_0, ["sectionTarget"]) : sanity.unset(["sectionTarget"]);
56
85
  return props.onChange(next);
57
86
  }, renderOption: ({
@@ -70,8 +99,9 @@ function selectLinkPreview(namespace) {
70
99
  email: maybeNamespace("email", namespace),
71
100
  externalUrl: maybeNamespace("external", namespace),
72
101
  customText: maybeNamespace("customText", namespace),
73
- internalTitle: maybeNamespace("internal.title", namespace),
74
- internalUri: maybeNamespace("internal.uri.current", namespace),
102
+ internalTitle: maybeNamespace("internal.link.title", namespace),
103
+ internalUri: maybeNamespace("internal.link.uri.current", namespace),
104
+ internalSlug: maybeNamespace("internal.link.slug.current", namespace),
75
105
  fileName: maybeNamespace("file.asset.originalFilename", namespace)
76
106
  };
77
107
  }
@@ -82,6 +112,7 @@ function prepareLinkPreview({
82
112
  fileName,
83
113
  customText,
84
114
  internalUri,
115
+ internalSlug,
85
116
  externalUrl,
86
117
  internalTitle
87
118
  }) {
@@ -89,7 +120,7 @@ function prepareLinkPreview({
89
120
  case "internal":
90
121
  return {
91
122
  title: customText ?? internalTitle ?? "Internal Link",
92
- subtitle: internalUri,
123
+ subtitle: internalUri ?? (internalSlug ? `/${internalSlug}` : void 0),
93
124
  media: () => /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: "\u{1F4C4}" })
94
125
  };
95
126
  case "external":
@@ -202,7 +233,7 @@ const typeName = "wild.link", LinkKind = {
202
233
  },
203
234
  components: {
204
235
  field: (props) => /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: props.children }),
205
- input: (props) => /* @__PURE__ */ jsxRuntime.jsx(InternalLinkInput, { ...props })
236
+ input: (props) => /* @__PURE__ */ jsxRuntime.jsx(InternalLinkInput, { pluginConfig: config, ...props })
206
237
  },
207
238
  fields: [sanity.defineField({
208
239
  type: "reference",
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/input.tsx","../src/preview.tsx","../src/types.tsx","../src/index.tsx"],"sourcesContent":["import { useFieldMember } from \"@madebywild/sanity-utils\";\nimport { AsyncAutocomplete } from \"@madebywild/sanity-utils/async-autocomplete\";\nimport { Box, Card, Stack, Text } from \"@sanity/ui\";\nimport * as changeCase from \"change-case\";\nimport * as React from \"react\";\nimport type { ObjectInputProps, ObjectMember, Reference } from \"sanity\";\nimport { MemberField, set, unset, useClient } from \"sanity\";\nimport type { FieldOptions } from \"./types\";\n\nfunction FieldInput(props: ObjectInputProps) {\n const options = props.schemaType.options as FieldOptions | undefined;\n const extensions = options?.extensions;\n\n return (\n <Stack space={4}>\n {props.members.map((member: ObjectMember) => {\n if (member.kind !== \"field\") return null;\n\n // Only show enabled extensions.\n if (!extensions?.includes(\"customText\") && member.name === \"customText\") return null;\n\n return <MemberField key={member.key} member={member} {...props} />;\n })}\n </Stack>\n );\n}\n\nconst SectionsQuery = `\n *[_id == $pageId && defined(pageBuilder.sectionsArray)][0]{\n \"sections\": array::compact(pageBuilder.sectionsArray[]{\n \"value\": _key,\n \"label\": coalesce(sectionSettings.sectionTitle, _type),\n }),\n }.sections\n`;\n\nfunction InternalLinkInput(props: ObjectInputProps) {\n const linkFieldMember = useFieldMember(props.members, \"link\");\n\n const selectedLink = props.value?.link as Reference | undefined;\n const selectedSection = props.value?.sectionTarget as string | undefined;\n\n // Reset sectionTarget if link is removed.\n React.useEffect(() => {\n if (!selectedLink?._ref && selectedSection !== undefined) {\n props.onChange(unset([\"sectionTarget\"]));\n }\n }, [selectedLink, selectedSection, props.onChange]);\n\n const client = useClient({ apiVersion: \"2025-10-21\" });\n\n const fetchSections = React.useCallback(async () => {\n if (!selectedLink?._ref) return [];\n\n try {\n const items = await client.fetch(SectionsQuery, { pageId: selectedLink._ref });\n return items;\n } catch {\n return [];\n }\n }, [client, selectedLink?._ref]);\n\n return (\n <Stack space={2}>\n {linkFieldMember && <MemberField member={linkFieldMember} {...props} />}\n {selectedLink?._ref && (\n <AsyncAutocomplete\n placeholder=\"Select section\"\n noOptionsPlaceholder=\"No sections found\"\n listItems={fetchSections}\n value={selectedSection}\n renderValue={(value, opt) => {\n return opt?.label ? changeCase.capitalCase(opt.label) : value;\n }}\n onChange={(value) => {\n const next = value ? set(value, [\"sectionTarget\"]) : unset([\"sectionTarget\"]);\n return props.onChange(next);\n }}\n renderOption={({ label, value }) => {\n return (\n <Card as=\"button\">\n <Box flex={1} padding={3}>\n <Text size={2}>{changeCase.capitalCase(label ?? value)}</Text>\n </Box>\n </Card>\n );\n }}\n />\n )}\n </Stack>\n );\n}\n\nexport { FieldInput, InternalLinkInput };\n","import type { LinkKind } from \"./types\";\n\ntype PreviewCtx = {\n kind?: string;\n email?: string;\n phone?: string;\n fileName?: string;\n customText?: string;\n externalUrl?: string;\n internalUri?: string;\n internalTitle?: string;\n};\n\nfunction maybeNamespace(field: string, ns?: string) {\n return ns ? `${ns}.${field}` : field;\n}\n\n/** @public */\nexport function selectLinkPreview(namespace?: string) {\n return {\n kind: maybeNamespace(\"kind\", namespace),\n phone: maybeNamespace(\"phone\", namespace),\n email: maybeNamespace(\"email\", namespace),\n externalUrl: maybeNamespace(\"external\", namespace),\n customText: maybeNamespace(\"customText\", namespace),\n internalTitle: maybeNamespace(\"internal.title\", namespace),\n internalUri: maybeNamespace(\"internal.uri.current\", namespace),\n fileName: maybeNamespace(\"file.asset.originalFilename\", namespace),\n } as const satisfies PreviewCtx;\n}\n\n/** @public */\nexport function prepareLinkPreview({\n kind,\n email,\n phone,\n fileName,\n customText,\n internalUri,\n externalUrl,\n internalTitle,\n}: PreviewCtx) {\n switch (kind as keyof typeof LinkKind) {\n case \"internal\": {\n return {\n title: customText ?? internalTitle ?? \"Internal Link\",\n subtitle: internalUri,\n media: () => <>📄</>,\n };\n }\n case \"external\":\n return {\n title: customText ?? \"External Link\",\n subtitle: externalUrl,\n media: () => <>🌍</>,\n };\n case \"email\":\n return {\n title: customText ?? \"Email Link\",\n subtitle: email,\n media: () => <>📧</>,\n };\n case \"phone\":\n return {\n title: customText ?? \"Phone Link\",\n subtitle: phone,\n media: () => <>☎️</>,\n };\n case \"file\":\n return {\n title: customText ?? \"File Link\",\n subtitle: fileName,\n media: () => <>📃</>,\n };\n default:\n return {\n title: customText ?? \"Empty Link\",\n media: () => <>⛓️‍💥</>,\n };\n }\n}\n","import type { ListItem } from \"@madebywild/sanity-utils/async-autocomplete\";\nimport type { ObjectDefinition, ObjectOptions, ReferenceTo } from \"sanity\";\n\n/** @public */\nexport const typeName = \"wild.link\" as const;\n\n/** @public */\nexport const LinkKind = {\n internal: \"internal\",\n external: \"external\",\n email: \"email\",\n phone: \"phone\",\n file: \"file\",\n} as const;\n\n/** @public */\nexport const LinkExtension = {\n customText: \"customText\",\n} as const;\n\n/** @public */\nexport type SectionQueryResult = ListItem[];\n\n/** @public */\nexport type PluginConfig = {\n /**\n * Whether references to internal links will be weak references.\n * Default is true.\n */\n weakReferences?: boolean;\n /**\n * Schema types that can be linked to internally.\n */\n internalLinkSchemaTypes: ReferenceTo;\n};\n\n/** @public */\nexport type FieldOptions = ObjectOptions & {\n /**\n * List of extension that add additional behaviors to the link field.\n * - `customText` - Allows setting custom text for the link.\n */\n extensions?: (keyof typeof LinkExtension)[];\n};\n\n// Add the custom field definition to Sanity's intrinsic definitions\n// so that type checking works correctly when using this field type.\ndeclare module \"sanity\" {\n export interface IntrinsicDefinitions {\n [typeName]: Omit<ObjectDefinition, \"type\" | \"fields\" | \"options\" | \"components\"> & {\n type: typeof typeName;\n options?: FieldOptions;\n };\n }\n}\n","import { requiredIf, visibleIf } from \"@madebywild/sanity-utils\";\nimport type { ObjectRule } from \"sanity\";\nimport { defineField, definePlugin, defineType } from \"sanity\";\nimport { FieldInput, InternalLinkInput } from \"./input\";\nimport { prepareLinkPreview, selectLinkPreview } from \"./preview\";\nimport { type FieldOptions, LinkKind, type PluginConfig, type SectionQueryResult, typeName } from \"./types\";\n\nconst visibleIfKind = visibleIf(\"kind\");\nconst requiredIfKind = requiredIf(\"kind\");\n\n/** @public */\nconst wildSanityLinkFieldPlugin = definePlugin<PluginConfig>((config) => {\n return {\n name: \"@madebywild/sanity-link-field\",\n schema: {\n types: [\n defineType({\n name: typeName,\n type: \"object\",\n title: \"Link\",\n description: \"Link to an internal page, external URL and more.\",\n icon: () => <>🔗</>,\n components: {\n input: (props) => <FieldInput {...props} />,\n },\n fields: [\n defineField({\n name: \"kind\",\n type: \"string\",\n title: \"Link Kind\",\n description: \"Select the kind of link.\",\n options: {\n layout: \"dropdown\",\n list: [\n { title: \"📄 Internal\", value: LinkKind.internal },\n { title: \"🌍 External\", value: LinkKind.external },\n { title: \"📧 Email\", value: LinkKind.email },\n { title: \"☎️ Phone\", value: LinkKind.phone },\n { title: \"📃 File\", value: LinkKind.file },\n ],\n },\n }),\n defineField({\n name: LinkKind.external,\n type: \"url\",\n title: \"External Link\",\n ...visibleIfKind(LinkKind.external),\n ...requiredIfKind(LinkKind.external),\n }),\n defineField({\n name: LinkKind.email,\n type: \"string\",\n title: \"Email\",\n ...visibleIfKind(LinkKind.email),\n ...requiredIfKind(LinkKind.email),\n }),\n defineField({\n name: LinkKind.phone,\n type: \"string\",\n title: \"Phone\",\n ...visibleIfKind(LinkKind.phone),\n ...requiredIfKind(LinkKind.phone),\n }),\n defineField({\n name: LinkKind.file,\n type: \"file\",\n title: \"File\",\n ...visibleIfKind(LinkKind.file),\n ...requiredIfKind(LinkKind.file),\n }),\n defineField({\n name: LinkKind.internal,\n type: \"object\",\n ...visibleIfKind(LinkKind.internal),\n ...requiredIfKind(LinkKind.internal, { path: [\"link\"] }),\n options: { collapsed: false, collapsible: false },\n components: {\n field: (props) => <>{props.children}</>,\n input: (props) => <InternalLinkInput {...props} />,\n },\n fields: [\n defineField({\n type: \"reference\",\n name: \"link\",\n title: \"Internal Link\",\n description: \"Select a page and an optional section target.\",\n weak: config.weakReferences ?? true,\n to: config.internalLinkSchemaTypes,\n options: {\n disableNew: true,\n },\n }),\n defineField({\n type: \"string\",\n name: \"sectionTarget\",\n title: \"Section Target\",\n }),\n ],\n }),\n defineField({\n name: \"customText\",\n type: \"string\",\n title: \"Custom text\",\n description: \"Will take precedence over inferred text.\",\n }),\n defineField({\n name: \"canDownload\",\n type: \"boolean\",\n title: \"Downloadable\",\n initialValue: true,\n description: \"The file will be downloaded when the link is clicked.\",\n options: { layout: \"switch\" },\n ...visibleIfKind(LinkKind.file),\n }),\n defineField({\n name: \"openInNewTab\",\n type: \"boolean\",\n title: \"Open in new tab\",\n description: \"Open the link in a new tab.\",\n initialValue: false,\n options: { layout: \"switch\" },\n ...visibleIfKind([LinkKind.external, LinkKind.internal]),\n }),\n ],\n preview: {\n select: selectLinkPreview(),\n prepare: (props) => prepareLinkPreview(props),\n },\n }),\n ],\n },\n };\n});\n\n/** @public */\nfunction requireLink(R: ObjectRule) {\n return R.custom((ctx) => (!ctx?.kind ? { message: \"Select the kind of link.\", path: [\"kind\"] } : true));\n}\n\nexport {\n wildSanityLinkFieldPlugin,\n typeName,\n LinkKind,\n requireLink,\n selectLinkPreview,\n prepareLinkPreview,\n type PluginConfig,\n type FieldOptions,\n type SectionQueryResult,\n};\n"],"names":["FieldInput","props","$","_c","extensions","schemaType","options","t0","members","map","member","kind","includes","name","jsx","MemberField","key","t1","Stack","SectionsQuery","InternalLinkInput","linkFieldMember","useFieldMember","selectedLink","value","link","selectedSection","sectionTarget","React","useEffect","_ref","undefined","onChange","unset","client","useClient","apiVersion","fetchSections","useCallback","fetch","pageId","jsxs","AsyncAutocomplete","opt","label","changeCase","capitalCase","next","set","Card","Box","Text","maybeNamespace","field","ns","selectLinkPreview","namespace","phone","email","externalUrl","customText","internalTitle","internalUri","fileName","prepareLinkPreview","title","subtitle","media","Fragment","typeName","LinkKind","internal","external","file","visibleIfKind","visibleIf","requiredIfKind","requiredIf","wildSanityLinkFieldPlugin","definePlugin","config","schema","types","defineType","type","description","icon","components","input","fields","defineField","layout","list","path","collapsed","collapsible","children","weak","weakReferences","to","internalLinkSchemaTypes","disableNew","initialValue","preview","select","prepare","requireLink","R","custom","ctx","message"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/input.tsx","../src/preview.tsx","../src/types.tsx","../src/index.tsx"],"sourcesContent":["import { AsyncAutocomplete } from \"@madebywild/sanity-utils/async-autocomplete\";\nimport { Box, Card, Stack, Text } from \"@sanity/ui\";\nimport * as changeCase from \"change-case\";\nimport * as React from \"react\";\nimport type { FieldMember, ObjectInputProps, ObjectMember, Reference } from \"sanity\";\nimport { MemberField, set, unset, useClient } from \"sanity\";\nimport type { FieldOptions, LinkExtensions, PluginConfig } from \"./types\";\n\nconst DefaultSectionsQuery = `\n *[_id == $pageId && defined(pageBuilder.sectionsArray)][0]{\n \"sections\": array::compact(pageBuilder.sectionsArray[]{\n \"value\": _key,\n \"label\": coalesce(sectionSettings.sectionTitle, _type),\n }),\n }.sections\n`;\n\nfunction isExtensionEnabled(extensions: LinkExtensions, key: keyof LinkExtensions) {\n return extensions[key] && (extensions[key] === true || extensions[key].enabled !== false);\n}\n\nfunction getSectionTargetConfig(sectionTarget?: PluginConfig[\"sectionTarget\"]) {\n if (sectionTarget === undefined || sectionTarget === true) {\n return { enabled: true, query: DefaultSectionsQuery };\n }\n\n if (sectionTarget === false) {\n return { enabled: false, query: DefaultSectionsQuery };\n }\n\n return {\n enabled: sectionTarget.enabled !== false,\n query: sectionTarget.query ?? DefaultSectionsQuery,\n };\n}\n\nfunction useFieldMember(members: ObjectMember[], fieldName: string): FieldMember | undefined {\n return React.useMemo(\n () => members.find((member): member is FieldMember => member.kind === \"field\" && member.name === fieldName),\n [members, fieldName]\n );\n}\n\nfunction FieldInput(props: ObjectInputProps) {\n const options = props.schemaType.options as FieldOptions | undefined;\n const extensions = options?.extensions;\n\n return (\n <Stack space={4}>\n {props.members.map((member: ObjectMember) => {\n if (member.kind !== \"field\") return null;\n\n // Custom text extension.\n const customTextEnabled = extensions && isExtensionEnabled(extensions, \"customText\");\n if (!customTextEnabled && member.name === \"customText\") return null;\n\n return <MemberField key={member.key} member={member} {...props} />;\n })}\n </Stack>\n );\n}\n\nfunction InternalLinkInput({\n pluginConfig,\n ...props\n}: ObjectInputProps & {\n pluginConfig?: PluginConfig;\n}) {\n const linkFieldMember = useFieldMember(props.members, \"link\");\n\n const selectedLink = props.value?.link as Reference | undefined;\n const selectedSection = props.value?.sectionTarget as string | undefined;\n const sectionTargetConfig = getSectionTargetConfig(pluginConfig?.sectionTarget);\n const sectionTargetEnabled = sectionTargetConfig.enabled;\n\n // Reset sectionTarget if link is removed or feature is disabled.\n React.useEffect(() => {\n if ((!selectedLink?._ref || !sectionTargetEnabled) && selectedSection !== undefined) {\n props.onChange(unset([\"sectionTarget\"]));\n }\n }, [selectedLink, selectedSection, sectionTargetEnabled, props.onChange]);\n\n const client = useClient({ apiVersion: \"2025-10-21\" });\n\n const fetchSections = React.useCallback(async () => {\n if (!selectedLink?._ref || !sectionTargetEnabled) return [];\n\n try {\n const items = await client.fetch(sectionTargetConfig.query, { pageId: selectedLink._ref });\n if (Array.isArray(items)) return items;\n if (items && typeof items === \"object\" && \"sections\" in items && Array.isArray(items.sections)) {\n return items.sections;\n }\n return [];\n } catch {\n return [];\n }\n }, [client, selectedLink?._ref, sectionTargetEnabled, sectionTargetConfig.query]);\n\n return (\n <Stack space={2}>\n {linkFieldMember && <MemberField member={linkFieldMember} {...props} />}\n {sectionTargetEnabled && selectedLink?._ref && (\n <AsyncAutocomplete\n placeholder=\"Select section\"\n noOptionsPlaceholder=\"No sections found\"\n listItems={fetchSections}\n value={selectedSection}\n renderValue={(value, opt) => {\n return opt?.label ? changeCase.capitalCase(opt.label) : value;\n }}\n onChange={(value) => {\n const next = value ? set(value, [\"sectionTarget\"]) : unset([\"sectionTarget\"]);\n return props.onChange(next);\n }}\n renderOption={({ label, value }) => {\n return (\n <Card as=\"button\">\n <Box flex={1} padding={3}>\n <Text size={2}>{changeCase.capitalCase(label ?? value)}</Text>\n </Box>\n </Card>\n );\n }}\n />\n )}\n </Stack>\n );\n}\n\nexport { FieldInput, InternalLinkInput };\n","import type { LinkKind } from \"./types\";\n\ntype PreviewCtx = {\n kind?: string;\n email?: string;\n phone?: string;\n fileName?: string;\n customText?: string;\n externalUrl?: string;\n internalUri?: string;\n internalSlug?: string;\n internalTitle?: string;\n};\n\nfunction maybeNamespace(field: string, ns?: string) {\n return ns ? `${ns}.${field}` : field;\n}\n\n/** @public */\nexport function selectLinkPreview(namespace?: string) {\n return {\n kind: maybeNamespace(\"kind\", namespace),\n phone: maybeNamespace(\"phone\", namespace),\n email: maybeNamespace(\"email\", namespace),\n externalUrl: maybeNamespace(\"external\", namespace),\n customText: maybeNamespace(\"customText\", namespace),\n internalTitle: maybeNamespace(\"internal.link.title\", namespace),\n internalUri: maybeNamespace(\"internal.link.uri.current\", namespace),\n internalSlug: maybeNamespace(\"internal.link.slug.current\", namespace),\n fileName: maybeNamespace(\"file.asset.originalFilename\", namespace),\n } as const satisfies PreviewCtx;\n}\n\n/** @public */\nexport function prepareLinkPreview({\n kind,\n email,\n phone,\n fileName,\n customText,\n internalUri,\n internalSlug,\n externalUrl,\n internalTitle,\n}: PreviewCtx) {\n switch (kind as keyof typeof LinkKind) {\n case \"internal\": {\n return {\n title: customText ?? internalTitle ?? \"Internal Link\",\n subtitle: internalUri ?? (internalSlug ? `/${internalSlug}` : undefined),\n media: () => <>📄</>,\n };\n }\n case \"external\":\n return {\n title: customText ?? \"External Link\",\n subtitle: externalUrl,\n media: () => <>🌍</>,\n };\n case \"email\":\n return {\n title: customText ?? \"Email Link\",\n subtitle: email,\n media: () => <>📧</>,\n };\n case \"phone\":\n return {\n title: customText ?? \"Phone Link\",\n subtitle: phone,\n media: () => <>☎️</>,\n };\n case \"file\":\n return {\n title: customText ?? \"File Link\",\n subtitle: fileName,\n media: () => <>📃</>,\n };\n default:\n return {\n title: customText ?? \"Empty Link\",\n media: () => <>⛓️‍💥</>,\n };\n }\n}\n","import type { ListItem } from \"@madebywild/sanity-utils/async-autocomplete\";\nimport type { ObjectDefinition, ObjectOptions, ReferenceTo } from \"sanity\";\n\n/** @public */\nexport const typeName = \"wild.link\" as const;\n\n/** @public */\nexport const LinkKind = {\n internal: \"internal\",\n external: \"external\",\n email: \"email\",\n phone: \"phone\",\n file: \"file\",\n} as const;\n\ntype SectionTargetConfig = {\n /**\n * Enable or disable section target selection.\n * Defaults to `true`.\n */\n enabled?: boolean;\n /**\n * GROQ query used to fetch section targets for the selected internal page.\n * The query should accept a `$pageId` param and return `{value, label}` items.\n */\n query?: string;\n};\n\n/** @public */\nexport type LinkExtensions = Partial<{\n /** Allows setting custom text for the link. */\n customText: boolean | { enabled?: boolean };\n}>;\n\n/** @public */\nexport type SectionQueryResult = ListItem[];\n\n/** @public */\nexport type PluginConfig = {\n /**\n * Whether references to internal links will be weak references.\n * Default is true.\n */\n weakReferences?: boolean;\n /**\n * Schema types that can be linked to internally.\n */\n internalLinkSchemaTypes: ReferenceTo;\n /**\n * Configure section target selection for internal links.\n */\n sectionTarget?: boolean | SectionTargetConfig;\n};\n\n/** @public */\nexport type FieldOptions = ObjectOptions & {\n /**\n * List of extension that add additional behaviors to the link field.\n */\n extensions?: LinkExtensions;\n};\n\n// Add the custom field definition to Sanity's intrinsic definitions\n// so that type checking works correctly when using this field type.\ndeclare module \"sanity\" {\n export interface IntrinsicDefinitions {\n [typeName]: Omit<ObjectDefinition, \"type\" | \"fields\" | \"options\" | \"components\"> & {\n type: typeof typeName;\n options?: FieldOptions;\n };\n }\n}\n","import { requiredIf, visibleIf } from \"@madebywild/sanity-utils\";\nimport type { ObjectRule } from \"sanity\";\nimport { defineField, definePlugin, defineType } from \"sanity\";\nimport { FieldInput, InternalLinkInput } from \"./input\";\nimport { prepareLinkPreview, selectLinkPreview } from \"./preview\";\nimport { type FieldOptions, type LinkExtensions, LinkKind, type PluginConfig, type SectionQueryResult, typeName } from \"./types\";\n\nconst visibleIfKind = visibleIf(\"kind\");\nconst requiredIfKind = requiredIf(\"kind\");\n\n/** @public */\nconst wildSanityLinkFieldPlugin = definePlugin<PluginConfig>((config) => {\n return {\n name: \"@madebywild/sanity-link-field\",\n schema: {\n types: [\n defineType({\n name: typeName,\n type: \"object\",\n title: \"Link\",\n description: \"Link to an internal page, external URL and more.\",\n icon: () => <>🔗</>,\n components: {\n input: (props) => <FieldInput {...props} />,\n },\n fields: [\n defineField({\n name: \"kind\",\n type: \"string\",\n title: \"Link Kind\",\n description: \"Select the kind of link.\",\n options: {\n layout: \"dropdown\",\n list: [\n { title: \"📄 Internal\", value: LinkKind.internal },\n { title: \"🌍 External\", value: LinkKind.external },\n { title: \"📧 Email\", value: LinkKind.email },\n { title: \"☎️ Phone\", value: LinkKind.phone },\n { title: \"📃 File\", value: LinkKind.file },\n ],\n },\n }),\n defineField({\n name: LinkKind.external,\n type: \"url\",\n title: \"External Link\",\n ...visibleIfKind(LinkKind.external),\n ...requiredIfKind(LinkKind.external),\n }),\n defineField({\n name: LinkKind.email,\n type: \"string\",\n title: \"Email\",\n ...visibleIfKind(LinkKind.email),\n ...requiredIfKind(LinkKind.email),\n }),\n defineField({\n name: LinkKind.phone,\n type: \"string\",\n title: \"Phone\",\n ...visibleIfKind(LinkKind.phone),\n ...requiredIfKind(LinkKind.phone),\n }),\n defineField({\n name: LinkKind.file,\n type: \"file\",\n title: \"File\",\n ...visibleIfKind(LinkKind.file),\n ...requiredIfKind(LinkKind.file),\n }),\n defineField({\n name: LinkKind.internal,\n type: \"object\",\n ...visibleIfKind(LinkKind.internal),\n ...requiredIfKind(LinkKind.internal, { path: [\"link\"] }),\n options: { collapsed: false, collapsible: false },\n components: {\n field: (props) => <>{props.children}</>,\n input: (props) => <InternalLinkInput pluginConfig={config} {...props} />,\n },\n fields: [\n defineField({\n type: \"reference\",\n name: \"link\",\n title: \"Internal Link\",\n description: \"Select a page and an optional section target.\",\n weak: config.weakReferences ?? true,\n to: config.internalLinkSchemaTypes,\n options: {\n disableNew: true,\n },\n }),\n defineField({\n type: \"string\",\n name: \"sectionTarget\",\n title: \"Section Target\",\n }),\n ],\n }),\n defineField({\n name: \"customText\",\n type: \"string\",\n title: \"Custom text\",\n description: \"Will take precedence over inferred text.\",\n }),\n defineField({\n name: \"canDownload\",\n type: \"boolean\",\n title: \"Downloadable\",\n initialValue: true,\n description: \"The file will be downloaded when the link is clicked.\",\n options: { layout: \"switch\" },\n ...visibleIfKind(LinkKind.file),\n }),\n defineField({\n name: \"openInNewTab\",\n type: \"boolean\",\n title: \"Open in new tab\",\n description: \"Open the link in a new tab.\",\n initialValue: false,\n options: { layout: \"switch\" },\n ...visibleIfKind([LinkKind.external, LinkKind.internal]),\n }),\n ],\n preview: {\n select: selectLinkPreview(),\n prepare: (props) => prepareLinkPreview(props),\n },\n }),\n ],\n },\n };\n});\n\n/** @public */\nfunction requireLink(R: ObjectRule) {\n return R.custom((ctx) => (!ctx?.kind ? { message: \"Select the kind of link.\", path: [\"kind\"] } : true));\n}\n\nexport {\n wildSanityLinkFieldPlugin,\n typeName,\n LinkKind,\n requireLink,\n selectLinkPreview,\n prepareLinkPreview,\n type LinkExtensions,\n type PluginConfig,\n type FieldOptions,\n type SectionQueryResult,\n};\n"],"names":["DefaultSectionsQuery","isExtensionEnabled","extensions","key","enabled","getSectionTargetConfig","sectionTarget","undefined","query","useFieldMember","members","fieldName","$","_c","t0","t1","member","kind","name","find","FieldInput","props","schemaType","options","map","jsx","MemberField","Stack","InternalLinkInput","pluginConfig","linkFieldMember","selectedLink","value","link","selectedSection","sectionTargetConfig","sectionTargetEnabled","React","useEffect","_ref","onChange","unset","client","useClient","apiVersion","fetchSections","useCallback","items","fetch","pageId","Array","isArray","sections","jsxs","AsyncAutocomplete","opt","label","changeCase","capitalCase","next","set","Card","Box","Text","maybeNamespace","field","ns","selectLinkPreview","namespace","phone","email","externalUrl","customText","internalTitle","internalUri","internalSlug","fileName","prepareLinkPreview","title","subtitle","media","Fragment","typeName","LinkKind","internal","external","file","visibleIfKind","visibleIf","requiredIfKind","requiredIf","wildSanityLinkFieldPlugin","definePlugin","config","schema","types","defineType","type","description","icon","components","input","fields","defineField","layout","list","path","collapsed","collapsible","children","weak","weakReferences","to","internalLinkSchemaTypes","disableNew","initialValue","preview","select","prepare","requireLink","R","custom","ctx","message"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -9,6 +9,7 @@ type PreviewCtx = {
9
9
  customText?: string;
10
10
  externalUrl?: string;
11
11
  internalUri?: string;
12
+ internalSlug?: string;
12
13
  internalTitle?: string;
13
14
  };
14
15
  /** @public */
@@ -20,6 +21,7 @@ declare function selectLinkPreview(namespace?: string): {
20
21
  readonly customText: string;
21
22
  readonly internalTitle: string;
22
23
  readonly internalUri: string;
24
+ readonly internalSlug: string;
23
25
  readonly fileName: string;
24
26
  };
25
27
  /** @public */
@@ -30,6 +32,7 @@ declare function prepareLinkPreview({
30
32
  fileName,
31
33
  customText,
32
34
  internalUri,
35
+ internalSlug,
33
36
  externalUrl,
34
37
  internalTitle
35
38
  }: PreviewCtx): {
@@ -57,11 +60,26 @@ declare const LinkKind: {
57
60
  readonly phone: "phone";
58
61
  readonly file: "file";
59
62
  };
60
- /** @public */
61
- declare const LinkExtension: {
62
- readonly customText: "customText";
63
+ type SectionTargetConfig = {
64
+ /**
65
+ * Enable or disable section target selection.
66
+ * Defaults to `true`.
67
+ */
68
+ enabled?: boolean;
69
+ /**
70
+ * GROQ query used to fetch section targets for the selected internal page.
71
+ * The query should accept a `$pageId` param and return `{value, label}` items.
72
+ */
73
+ query?: string;
63
74
  };
64
75
  /** @public */
76
+ type LinkExtensions = Partial<{
77
+ /** Allows setting custom text for the link. */
78
+ customText: boolean | {
79
+ enabled?: boolean;
80
+ };
81
+ }>;
82
+ /** @public */
65
83
  type SectionQueryResult = ListItem[];
66
84
  /** @public */
67
85
  type PluginConfig = {
@@ -74,14 +92,17 @@ type PluginConfig = {
74
92
  * Schema types that can be linked to internally.
75
93
  */
76
94
  internalLinkSchemaTypes: ReferenceTo;
95
+ /**
96
+ * Configure section target selection for internal links.
97
+ */
98
+ sectionTarget?: boolean | SectionTargetConfig;
77
99
  };
78
100
  /** @public */
79
101
  type FieldOptions = ObjectOptions & {
80
102
  /**
81
103
  * List of extension that add additional behaviors to the link field.
82
- * - `customText` - Allows setting custom text for the link.
83
104
  */
84
- extensions?: (keyof typeof LinkExtension)[];
105
+ extensions?: LinkExtensions;
85
106
  };
86
107
  declare module "sanity" {
87
108
  interface IntrinsicDefinitions {
@@ -95,4 +116,4 @@ declare module "sanity" {
95
116
  declare const wildSanityLinkFieldPlugin: sanity0.Plugin<PluginConfig>;
96
117
  /** @public */
97
118
  declare function requireLink(R: ObjectRule): ObjectRule;
98
- export { type FieldOptions, LinkKind, type PluginConfig, type SectionQueryResult, prepareLinkPreview, requireLink, selectLinkPreview, typeName, wildSanityLinkFieldPlugin };
119
+ export { type FieldOptions, type LinkExtensions, LinkKind, type PluginConfig, type SectionQueryResult, prepareLinkPreview, requireLink, selectLinkPreview, typeName, wildSanityLinkFieldPlugin };
package/dist/index.d.ts CHANGED
@@ -9,6 +9,7 @@ type PreviewCtx = {
9
9
  customText?: string;
10
10
  externalUrl?: string;
11
11
  internalUri?: string;
12
+ internalSlug?: string;
12
13
  internalTitle?: string;
13
14
  };
14
15
  /** @public */
@@ -20,6 +21,7 @@ declare function selectLinkPreview(namespace?: string): {
20
21
  readonly customText: string;
21
22
  readonly internalTitle: string;
22
23
  readonly internalUri: string;
24
+ readonly internalSlug: string;
23
25
  readonly fileName: string;
24
26
  };
25
27
  /** @public */
@@ -30,6 +32,7 @@ declare function prepareLinkPreview({
30
32
  fileName,
31
33
  customText,
32
34
  internalUri,
35
+ internalSlug,
33
36
  externalUrl,
34
37
  internalTitle
35
38
  }: PreviewCtx): {
@@ -57,11 +60,26 @@ declare const LinkKind: {
57
60
  readonly phone: "phone";
58
61
  readonly file: "file";
59
62
  };
60
- /** @public */
61
- declare const LinkExtension: {
62
- readonly customText: "customText";
63
+ type SectionTargetConfig = {
64
+ /**
65
+ * Enable or disable section target selection.
66
+ * Defaults to `true`.
67
+ */
68
+ enabled?: boolean;
69
+ /**
70
+ * GROQ query used to fetch section targets for the selected internal page.
71
+ * The query should accept a `$pageId` param and return `{value, label}` items.
72
+ */
73
+ query?: string;
63
74
  };
64
75
  /** @public */
76
+ type LinkExtensions = Partial<{
77
+ /** Allows setting custom text for the link. */
78
+ customText: boolean | {
79
+ enabled?: boolean;
80
+ };
81
+ }>;
82
+ /** @public */
65
83
  type SectionQueryResult = ListItem[];
66
84
  /** @public */
67
85
  type PluginConfig = {
@@ -74,14 +92,17 @@ type PluginConfig = {
74
92
  * Schema types that can be linked to internally.
75
93
  */
76
94
  internalLinkSchemaTypes: ReferenceTo;
95
+ /**
96
+ * Configure section target selection for internal links.
97
+ */
98
+ sectionTarget?: boolean | SectionTargetConfig;
77
99
  };
78
100
  /** @public */
79
101
  type FieldOptions = ObjectOptions & {
80
102
  /**
81
103
  * List of extension that add additional behaviors to the link field.
82
- * - `customText` - Allows setting custom text for the link.
83
104
  */
84
- extensions?: (keyof typeof LinkExtension)[];
105
+ extensions?: LinkExtensions;
85
106
  };
86
107
  declare module "sanity" {
87
108
  interface IntrinsicDefinitions {
@@ -95,4 +116,4 @@ declare module "sanity" {
95
116
  declare const wildSanityLinkFieldPlugin: sanity0.Plugin<PluginConfig>;
96
117
  /** @public */
97
118
  declare function requireLink(R: ObjectRule): ObjectRule;
98
- export { type FieldOptions, LinkKind, type PluginConfig, type SectionQueryResult, prepareLinkPreview, requireLink, selectLinkPreview, typeName, wildSanityLinkFieldPlugin };
119
+ export { type FieldOptions, type LinkExtensions, LinkKind, type PluginConfig, type SectionQueryResult, prepareLinkPreview, requireLink, selectLinkPreview, typeName, wildSanityLinkFieldPlugin };
package/dist/index.js CHANGED
@@ -1,19 +1,12 @@
1
1
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
- import { useFieldMember, visibleIf, requiredIf } from "@madebywild/sanity-utils";
2
+ import { visibleIf, requiredIf } from "@madebywild/sanity-utils";
3
3
  import { unset, useClient, MemberField, set, definePlugin, defineType, defineField } from "sanity";
4
4
  import { c } from "react/compiler-runtime";
5
5
  import { AsyncAutocomplete } from "@madebywild/sanity-utils/async-autocomplete";
6
6
  import { Stack, Card, Box, Text } from "@sanity/ui";
7
7
  import * as changeCase from "change-case";
8
8
  import * as React from "react";
9
- function FieldInput(props) {
10
- const $ = c(5), extensions = props.schemaType.options?.extensions;
11
- let t0;
12
- $[0] !== extensions || $[1] !== props ? (t0 = props.members.map((member) => member.kind !== "field" || !extensions?.includes("customText") && member.name === "customText" ? null : /* @__PURE__ */ jsx(MemberField, { member, ...props }, member.key)), $[0] = extensions, $[1] = props, $[2] = t0) : t0 = $[2];
13
- let t1;
14
- return $[3] !== t0 ? (t1 = /* @__PURE__ */ jsx(Stack, { space: 4, children: t0 }), $[3] = t0, $[4] = t1) : t1 = $[4], t1;
15
- }
16
- const SectionsQuery = `
9
+ const DefaultSectionsQuery = `
17
10
  *[_id == $pageId && defined(pageBuilder.sectionsArray)][0]{
18
11
  "sections": array::compact(pageBuilder.sectionsArray[]{
19
12
  "value": _key,
@@ -21,26 +14,62 @@ const SectionsQuery = `
21
14
  }),
22
15
  }.sections
23
16
  `;
24
- function InternalLinkInput(props) {
25
- const linkFieldMember = useFieldMember(props.members, "link"), selectedLink = props.value?.link, selectedSection = props.value?.sectionTarget;
17
+ function isExtensionEnabled(extensions, key) {
18
+ return extensions[key] && (extensions[key] === !0 || extensions[key].enabled !== !1);
19
+ }
20
+ function getSectionTargetConfig(sectionTarget) {
21
+ return sectionTarget === void 0 || sectionTarget === !0 ? {
22
+ enabled: !0,
23
+ query: DefaultSectionsQuery
24
+ } : sectionTarget === !1 ? {
25
+ enabled: !1,
26
+ query: DefaultSectionsQuery
27
+ } : {
28
+ enabled: sectionTarget.enabled !== !1,
29
+ query: sectionTarget.query ?? DefaultSectionsQuery
30
+ };
31
+ }
32
+ function useFieldMember(members, fieldName) {
33
+ const $ = c(5);
34
+ let t0;
35
+ if ($[0] !== fieldName || $[1] !== members) {
36
+ let t1;
37
+ $[3] !== fieldName ? (t1 = (member) => member.kind === "field" && member.name === fieldName, $[3] = fieldName, $[4] = t1) : t1 = $[4], t0 = members.find(t1), $[0] = fieldName, $[1] = members, $[2] = t0;
38
+ } else
39
+ t0 = $[2];
40
+ return t0;
41
+ }
42
+ function FieldInput(props) {
43
+ const $ = c(5), extensions = props.schemaType.options?.extensions;
44
+ let t0;
45
+ $[0] !== extensions || $[1] !== props ? (t0 = props.members.map((member) => member.kind !== "field" || !(extensions && isExtensionEnabled(extensions, "customText")) && member.name === "customText" ? null : /* @__PURE__ */ jsx(MemberField, { member, ...props }, member.key)), $[0] = extensions, $[1] = props, $[2] = t0) : t0 = $[2];
46
+ let t1;
47
+ return $[3] !== t0 ? (t1 = /* @__PURE__ */ jsx(Stack, { space: 4, children: t0 }), $[3] = t0, $[4] = t1) : t1 = $[4], t1;
48
+ }
49
+ function InternalLinkInput({
50
+ pluginConfig,
51
+ ...props
52
+ }) {
53
+ const linkFieldMember = useFieldMember(props.members, "link"), selectedLink = props.value?.link, selectedSection = props.value?.sectionTarget, sectionTargetConfig = getSectionTargetConfig(pluginConfig?.sectionTarget), sectionTargetEnabled = sectionTargetConfig.enabled;
26
54
  React.useEffect(() => {
27
- !selectedLink?._ref && selectedSection !== void 0 && props.onChange(unset(["sectionTarget"]));
28
- }, [selectedLink, selectedSection, props.onChange]);
55
+ (!selectedLink?._ref || !sectionTargetEnabled) && selectedSection !== void 0 && props.onChange(unset(["sectionTarget"]));
56
+ }, [selectedLink, selectedSection, sectionTargetEnabled, props.onChange]);
29
57
  const client = useClient({
30
58
  apiVersion: "2025-10-21"
31
59
  }), fetchSections = React.useCallback(async () => {
32
- if (!selectedLink?._ref) return [];
60
+ if (!selectedLink?._ref || !sectionTargetEnabled) return [];
33
61
  try {
34
- return await client.fetch(SectionsQuery, {
62
+ const items = await client.fetch(sectionTargetConfig.query, {
35
63
  pageId: selectedLink._ref
36
64
  });
65
+ return Array.isArray(items) ? items : items && typeof items == "object" && "sections" in items && Array.isArray(items.sections) ? items.sections : [];
37
66
  } catch {
38
67
  return [];
39
68
  }
40
- }, [client, selectedLink?._ref]);
69
+ }, [client, selectedLink?._ref, sectionTargetEnabled, sectionTargetConfig.query]);
41
70
  return /* @__PURE__ */ jsxs(Stack, { space: 2, children: [
42
71
  linkFieldMember && /* @__PURE__ */ jsx(MemberField, { member: linkFieldMember, ...props }),
43
- selectedLink?._ref && /* @__PURE__ */ jsx(AsyncAutocomplete, { placeholder: "Select section", noOptionsPlaceholder: "No sections found", listItems: fetchSections, value: selectedSection, renderValue: (value, opt) => opt?.label ? changeCase.capitalCase(opt.label) : value, onChange: (value_0) => {
72
+ sectionTargetEnabled && selectedLink?._ref && /* @__PURE__ */ jsx(AsyncAutocomplete, { placeholder: "Select section", noOptionsPlaceholder: "No sections found", listItems: fetchSections, value: selectedSection, renderValue: (value, opt) => opt?.label ? changeCase.capitalCase(opt.label) : value, onChange: (value_0) => {
44
73
  const next = value_0 ? set(value_0, ["sectionTarget"]) : unset(["sectionTarget"]);
45
74
  return props.onChange(next);
46
75
  }, renderOption: ({
@@ -59,8 +88,9 @@ function selectLinkPreview(namespace) {
59
88
  email: maybeNamespace("email", namespace),
60
89
  externalUrl: maybeNamespace("external", namespace),
61
90
  customText: maybeNamespace("customText", namespace),
62
- internalTitle: maybeNamespace("internal.title", namespace),
63
- internalUri: maybeNamespace("internal.uri.current", namespace),
91
+ internalTitle: maybeNamespace("internal.link.title", namespace),
92
+ internalUri: maybeNamespace("internal.link.uri.current", namespace),
93
+ internalSlug: maybeNamespace("internal.link.slug.current", namespace),
64
94
  fileName: maybeNamespace("file.asset.originalFilename", namespace)
65
95
  };
66
96
  }
@@ -71,6 +101,7 @@ function prepareLinkPreview({
71
101
  fileName,
72
102
  customText,
73
103
  internalUri,
104
+ internalSlug,
74
105
  externalUrl,
75
106
  internalTitle
76
107
  }) {
@@ -78,7 +109,7 @@ function prepareLinkPreview({
78
109
  case "internal":
79
110
  return {
80
111
  title: customText ?? internalTitle ?? "Internal Link",
81
- subtitle: internalUri,
112
+ subtitle: internalUri ?? (internalSlug ? `/${internalSlug}` : void 0),
82
113
  media: () => /* @__PURE__ */ jsx(Fragment, { children: "\u{1F4C4}" })
83
114
  };
84
115
  case "external":
@@ -191,7 +222,7 @@ const typeName = "wild.link", LinkKind = {
191
222
  },
192
223
  components: {
193
224
  field: (props) => /* @__PURE__ */ jsx(Fragment, { children: props.children }),
194
- input: (props) => /* @__PURE__ */ jsx(InternalLinkInput, { ...props })
225
+ input: (props) => /* @__PURE__ */ jsx(InternalLinkInput, { pluginConfig: config, ...props })
195
226
  },
196
227
  fields: [defineField({
197
228
  type: "reference",
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/input.tsx","../src/preview.tsx","../src/types.tsx","../src/index.tsx"],"sourcesContent":["import { useFieldMember } from \"@madebywild/sanity-utils\";\nimport { AsyncAutocomplete } from \"@madebywild/sanity-utils/async-autocomplete\";\nimport { Box, Card, Stack, Text } from \"@sanity/ui\";\nimport * as changeCase from \"change-case\";\nimport * as React from \"react\";\nimport type { ObjectInputProps, ObjectMember, Reference } from \"sanity\";\nimport { MemberField, set, unset, useClient } from \"sanity\";\nimport type { FieldOptions } from \"./types\";\n\nfunction FieldInput(props: ObjectInputProps) {\n const options = props.schemaType.options as FieldOptions | undefined;\n const extensions = options?.extensions;\n\n return (\n <Stack space={4}>\n {props.members.map((member: ObjectMember) => {\n if (member.kind !== \"field\") return null;\n\n // Only show enabled extensions.\n if (!extensions?.includes(\"customText\") && member.name === \"customText\") return null;\n\n return <MemberField key={member.key} member={member} {...props} />;\n })}\n </Stack>\n );\n}\n\nconst SectionsQuery = `\n *[_id == $pageId && defined(pageBuilder.sectionsArray)][0]{\n \"sections\": array::compact(pageBuilder.sectionsArray[]{\n \"value\": _key,\n \"label\": coalesce(sectionSettings.sectionTitle, _type),\n }),\n }.sections\n`;\n\nfunction InternalLinkInput(props: ObjectInputProps) {\n const linkFieldMember = useFieldMember(props.members, \"link\");\n\n const selectedLink = props.value?.link as Reference | undefined;\n const selectedSection = props.value?.sectionTarget as string | undefined;\n\n // Reset sectionTarget if link is removed.\n React.useEffect(() => {\n if (!selectedLink?._ref && selectedSection !== undefined) {\n props.onChange(unset([\"sectionTarget\"]));\n }\n }, [selectedLink, selectedSection, props.onChange]);\n\n const client = useClient({ apiVersion: \"2025-10-21\" });\n\n const fetchSections = React.useCallback(async () => {\n if (!selectedLink?._ref) return [];\n\n try {\n const items = await client.fetch(SectionsQuery, { pageId: selectedLink._ref });\n return items;\n } catch {\n return [];\n }\n }, [client, selectedLink?._ref]);\n\n return (\n <Stack space={2}>\n {linkFieldMember && <MemberField member={linkFieldMember} {...props} />}\n {selectedLink?._ref && (\n <AsyncAutocomplete\n placeholder=\"Select section\"\n noOptionsPlaceholder=\"No sections found\"\n listItems={fetchSections}\n value={selectedSection}\n renderValue={(value, opt) => {\n return opt?.label ? changeCase.capitalCase(opt.label) : value;\n }}\n onChange={(value) => {\n const next = value ? set(value, [\"sectionTarget\"]) : unset([\"sectionTarget\"]);\n return props.onChange(next);\n }}\n renderOption={({ label, value }) => {\n return (\n <Card as=\"button\">\n <Box flex={1} padding={3}>\n <Text size={2}>{changeCase.capitalCase(label ?? value)}</Text>\n </Box>\n </Card>\n );\n }}\n />\n )}\n </Stack>\n );\n}\n\nexport { FieldInput, InternalLinkInput };\n","import type { LinkKind } from \"./types\";\n\ntype PreviewCtx = {\n kind?: string;\n email?: string;\n phone?: string;\n fileName?: string;\n customText?: string;\n externalUrl?: string;\n internalUri?: string;\n internalTitle?: string;\n};\n\nfunction maybeNamespace(field: string, ns?: string) {\n return ns ? `${ns}.${field}` : field;\n}\n\n/** @public */\nexport function selectLinkPreview(namespace?: string) {\n return {\n kind: maybeNamespace(\"kind\", namespace),\n phone: maybeNamespace(\"phone\", namespace),\n email: maybeNamespace(\"email\", namespace),\n externalUrl: maybeNamespace(\"external\", namespace),\n customText: maybeNamespace(\"customText\", namespace),\n internalTitle: maybeNamespace(\"internal.title\", namespace),\n internalUri: maybeNamespace(\"internal.uri.current\", namespace),\n fileName: maybeNamespace(\"file.asset.originalFilename\", namespace),\n } as const satisfies PreviewCtx;\n}\n\n/** @public */\nexport function prepareLinkPreview({\n kind,\n email,\n phone,\n fileName,\n customText,\n internalUri,\n externalUrl,\n internalTitle,\n}: PreviewCtx) {\n switch (kind as keyof typeof LinkKind) {\n case \"internal\": {\n return {\n title: customText ?? internalTitle ?? \"Internal Link\",\n subtitle: internalUri,\n media: () => <>📄</>,\n };\n }\n case \"external\":\n return {\n title: customText ?? \"External Link\",\n subtitle: externalUrl,\n media: () => <>🌍</>,\n };\n case \"email\":\n return {\n title: customText ?? \"Email Link\",\n subtitle: email,\n media: () => <>📧</>,\n };\n case \"phone\":\n return {\n title: customText ?? \"Phone Link\",\n subtitle: phone,\n media: () => <>☎️</>,\n };\n case \"file\":\n return {\n title: customText ?? \"File Link\",\n subtitle: fileName,\n media: () => <>📃</>,\n };\n default:\n return {\n title: customText ?? \"Empty Link\",\n media: () => <>⛓️‍💥</>,\n };\n }\n}\n","import type { ListItem } from \"@madebywild/sanity-utils/async-autocomplete\";\nimport type { ObjectDefinition, ObjectOptions, ReferenceTo } from \"sanity\";\n\n/** @public */\nexport const typeName = \"wild.link\" as const;\n\n/** @public */\nexport const LinkKind = {\n internal: \"internal\",\n external: \"external\",\n email: \"email\",\n phone: \"phone\",\n file: \"file\",\n} as const;\n\n/** @public */\nexport const LinkExtension = {\n customText: \"customText\",\n} as const;\n\n/** @public */\nexport type SectionQueryResult = ListItem[];\n\n/** @public */\nexport type PluginConfig = {\n /**\n * Whether references to internal links will be weak references.\n * Default is true.\n */\n weakReferences?: boolean;\n /**\n * Schema types that can be linked to internally.\n */\n internalLinkSchemaTypes: ReferenceTo;\n};\n\n/** @public */\nexport type FieldOptions = ObjectOptions & {\n /**\n * List of extension that add additional behaviors to the link field.\n * - `customText` - Allows setting custom text for the link.\n */\n extensions?: (keyof typeof LinkExtension)[];\n};\n\n// Add the custom field definition to Sanity's intrinsic definitions\n// so that type checking works correctly when using this field type.\ndeclare module \"sanity\" {\n export interface IntrinsicDefinitions {\n [typeName]: Omit<ObjectDefinition, \"type\" | \"fields\" | \"options\" | \"components\"> & {\n type: typeof typeName;\n options?: FieldOptions;\n };\n }\n}\n","import { requiredIf, visibleIf } from \"@madebywild/sanity-utils\";\nimport type { ObjectRule } from \"sanity\";\nimport { defineField, definePlugin, defineType } from \"sanity\";\nimport { FieldInput, InternalLinkInput } from \"./input\";\nimport { prepareLinkPreview, selectLinkPreview } from \"./preview\";\nimport { type FieldOptions, LinkKind, type PluginConfig, type SectionQueryResult, typeName } from \"./types\";\n\nconst visibleIfKind = visibleIf(\"kind\");\nconst requiredIfKind = requiredIf(\"kind\");\n\n/** @public */\nconst wildSanityLinkFieldPlugin = definePlugin<PluginConfig>((config) => {\n return {\n name: \"@madebywild/sanity-link-field\",\n schema: {\n types: [\n defineType({\n name: typeName,\n type: \"object\",\n title: \"Link\",\n description: \"Link to an internal page, external URL and more.\",\n icon: () => <>🔗</>,\n components: {\n input: (props) => <FieldInput {...props} />,\n },\n fields: [\n defineField({\n name: \"kind\",\n type: \"string\",\n title: \"Link Kind\",\n description: \"Select the kind of link.\",\n options: {\n layout: \"dropdown\",\n list: [\n { title: \"📄 Internal\", value: LinkKind.internal },\n { title: \"🌍 External\", value: LinkKind.external },\n { title: \"📧 Email\", value: LinkKind.email },\n { title: \"☎️ Phone\", value: LinkKind.phone },\n { title: \"📃 File\", value: LinkKind.file },\n ],\n },\n }),\n defineField({\n name: LinkKind.external,\n type: \"url\",\n title: \"External Link\",\n ...visibleIfKind(LinkKind.external),\n ...requiredIfKind(LinkKind.external),\n }),\n defineField({\n name: LinkKind.email,\n type: \"string\",\n title: \"Email\",\n ...visibleIfKind(LinkKind.email),\n ...requiredIfKind(LinkKind.email),\n }),\n defineField({\n name: LinkKind.phone,\n type: \"string\",\n title: \"Phone\",\n ...visibleIfKind(LinkKind.phone),\n ...requiredIfKind(LinkKind.phone),\n }),\n defineField({\n name: LinkKind.file,\n type: \"file\",\n title: \"File\",\n ...visibleIfKind(LinkKind.file),\n ...requiredIfKind(LinkKind.file),\n }),\n defineField({\n name: LinkKind.internal,\n type: \"object\",\n ...visibleIfKind(LinkKind.internal),\n ...requiredIfKind(LinkKind.internal, { path: [\"link\"] }),\n options: { collapsed: false, collapsible: false },\n components: {\n field: (props) => <>{props.children}</>,\n input: (props) => <InternalLinkInput {...props} />,\n },\n fields: [\n defineField({\n type: \"reference\",\n name: \"link\",\n title: \"Internal Link\",\n description: \"Select a page and an optional section target.\",\n weak: config.weakReferences ?? true,\n to: config.internalLinkSchemaTypes,\n options: {\n disableNew: true,\n },\n }),\n defineField({\n type: \"string\",\n name: \"sectionTarget\",\n title: \"Section Target\",\n }),\n ],\n }),\n defineField({\n name: \"customText\",\n type: \"string\",\n title: \"Custom text\",\n description: \"Will take precedence over inferred text.\",\n }),\n defineField({\n name: \"canDownload\",\n type: \"boolean\",\n title: \"Downloadable\",\n initialValue: true,\n description: \"The file will be downloaded when the link is clicked.\",\n options: { layout: \"switch\" },\n ...visibleIfKind(LinkKind.file),\n }),\n defineField({\n name: \"openInNewTab\",\n type: \"boolean\",\n title: \"Open in new tab\",\n description: \"Open the link in a new tab.\",\n initialValue: false,\n options: { layout: \"switch\" },\n ...visibleIfKind([LinkKind.external, LinkKind.internal]),\n }),\n ],\n preview: {\n select: selectLinkPreview(),\n prepare: (props) => prepareLinkPreview(props),\n },\n }),\n ],\n },\n };\n});\n\n/** @public */\nfunction requireLink(R: ObjectRule) {\n return R.custom((ctx) => (!ctx?.kind ? { message: \"Select the kind of link.\", path: [\"kind\"] } : true));\n}\n\nexport {\n wildSanityLinkFieldPlugin,\n typeName,\n LinkKind,\n requireLink,\n selectLinkPreview,\n prepareLinkPreview,\n type PluginConfig,\n type FieldOptions,\n type SectionQueryResult,\n};\n"],"names":["FieldInput","props","$","_c","extensions","schemaType","options","t0","members","map","member","kind","includes","name","key","t1","SectionsQuery","InternalLinkInput","linkFieldMember","useFieldMember","selectedLink","value","link","selectedSection","sectionTarget","React","useEffect","_ref","undefined","onChange","unset","client","useClient","apiVersion","fetchSections","useCallback","fetch","pageId","opt","label","changeCase","capitalCase","next","set","maybeNamespace","field","ns","selectLinkPreview","namespace","phone","email","externalUrl","customText","internalTitle","internalUri","fileName","prepareLinkPreview","title","subtitle","media","typeName","LinkKind","internal","external","file","visibleIfKind","visibleIf","requiredIfKind","requiredIf","wildSanityLinkFieldPlugin","definePlugin","config","schema","types","defineType","type","description","icon","components","input","fields","defineField","layout","list","path","collapsed","collapsible","children","weak","weakReferences","to","internalLinkSchemaTypes","disableNew","initialValue","preview","select","prepare","requireLink","R","custom","ctx","message"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/input.tsx","../src/preview.tsx","../src/types.tsx","../src/index.tsx"],"sourcesContent":["import { AsyncAutocomplete } from \"@madebywild/sanity-utils/async-autocomplete\";\nimport { Box, Card, Stack, Text } from \"@sanity/ui\";\nimport * as changeCase from \"change-case\";\nimport * as React from \"react\";\nimport type { FieldMember, ObjectInputProps, ObjectMember, Reference } from \"sanity\";\nimport { MemberField, set, unset, useClient } from \"sanity\";\nimport type { FieldOptions, LinkExtensions, PluginConfig } from \"./types\";\n\nconst DefaultSectionsQuery = `\n *[_id == $pageId && defined(pageBuilder.sectionsArray)][0]{\n \"sections\": array::compact(pageBuilder.sectionsArray[]{\n \"value\": _key,\n \"label\": coalesce(sectionSettings.sectionTitle, _type),\n }),\n }.sections\n`;\n\nfunction isExtensionEnabled(extensions: LinkExtensions, key: keyof LinkExtensions) {\n return extensions[key] && (extensions[key] === true || extensions[key].enabled !== false);\n}\n\nfunction getSectionTargetConfig(sectionTarget?: PluginConfig[\"sectionTarget\"]) {\n if (sectionTarget === undefined || sectionTarget === true) {\n return { enabled: true, query: DefaultSectionsQuery };\n }\n\n if (sectionTarget === false) {\n return { enabled: false, query: DefaultSectionsQuery };\n }\n\n return {\n enabled: sectionTarget.enabled !== false,\n query: sectionTarget.query ?? DefaultSectionsQuery,\n };\n}\n\nfunction useFieldMember(members: ObjectMember[], fieldName: string): FieldMember | undefined {\n return React.useMemo(\n () => members.find((member): member is FieldMember => member.kind === \"field\" && member.name === fieldName),\n [members, fieldName]\n );\n}\n\nfunction FieldInput(props: ObjectInputProps) {\n const options = props.schemaType.options as FieldOptions | undefined;\n const extensions = options?.extensions;\n\n return (\n <Stack space={4}>\n {props.members.map((member: ObjectMember) => {\n if (member.kind !== \"field\") return null;\n\n // Custom text extension.\n const customTextEnabled = extensions && isExtensionEnabled(extensions, \"customText\");\n if (!customTextEnabled && member.name === \"customText\") return null;\n\n return <MemberField key={member.key} member={member} {...props} />;\n })}\n </Stack>\n );\n}\n\nfunction InternalLinkInput({\n pluginConfig,\n ...props\n}: ObjectInputProps & {\n pluginConfig?: PluginConfig;\n}) {\n const linkFieldMember = useFieldMember(props.members, \"link\");\n\n const selectedLink = props.value?.link as Reference | undefined;\n const selectedSection = props.value?.sectionTarget as string | undefined;\n const sectionTargetConfig = getSectionTargetConfig(pluginConfig?.sectionTarget);\n const sectionTargetEnabled = sectionTargetConfig.enabled;\n\n // Reset sectionTarget if link is removed or feature is disabled.\n React.useEffect(() => {\n if ((!selectedLink?._ref || !sectionTargetEnabled) && selectedSection !== undefined) {\n props.onChange(unset([\"sectionTarget\"]));\n }\n }, [selectedLink, selectedSection, sectionTargetEnabled, props.onChange]);\n\n const client = useClient({ apiVersion: \"2025-10-21\" });\n\n const fetchSections = React.useCallback(async () => {\n if (!selectedLink?._ref || !sectionTargetEnabled) return [];\n\n try {\n const items = await client.fetch(sectionTargetConfig.query, { pageId: selectedLink._ref });\n if (Array.isArray(items)) return items;\n if (items && typeof items === \"object\" && \"sections\" in items && Array.isArray(items.sections)) {\n return items.sections;\n }\n return [];\n } catch {\n return [];\n }\n }, [client, selectedLink?._ref, sectionTargetEnabled, sectionTargetConfig.query]);\n\n return (\n <Stack space={2}>\n {linkFieldMember && <MemberField member={linkFieldMember} {...props} />}\n {sectionTargetEnabled && selectedLink?._ref && (\n <AsyncAutocomplete\n placeholder=\"Select section\"\n noOptionsPlaceholder=\"No sections found\"\n listItems={fetchSections}\n value={selectedSection}\n renderValue={(value, opt) => {\n return opt?.label ? changeCase.capitalCase(opt.label) : value;\n }}\n onChange={(value) => {\n const next = value ? set(value, [\"sectionTarget\"]) : unset([\"sectionTarget\"]);\n return props.onChange(next);\n }}\n renderOption={({ label, value }) => {\n return (\n <Card as=\"button\">\n <Box flex={1} padding={3}>\n <Text size={2}>{changeCase.capitalCase(label ?? value)}</Text>\n </Box>\n </Card>\n );\n }}\n />\n )}\n </Stack>\n );\n}\n\nexport { FieldInput, InternalLinkInput };\n","import type { LinkKind } from \"./types\";\n\ntype PreviewCtx = {\n kind?: string;\n email?: string;\n phone?: string;\n fileName?: string;\n customText?: string;\n externalUrl?: string;\n internalUri?: string;\n internalSlug?: string;\n internalTitle?: string;\n};\n\nfunction maybeNamespace(field: string, ns?: string) {\n return ns ? `${ns}.${field}` : field;\n}\n\n/** @public */\nexport function selectLinkPreview(namespace?: string) {\n return {\n kind: maybeNamespace(\"kind\", namespace),\n phone: maybeNamespace(\"phone\", namespace),\n email: maybeNamespace(\"email\", namespace),\n externalUrl: maybeNamespace(\"external\", namespace),\n customText: maybeNamespace(\"customText\", namespace),\n internalTitle: maybeNamespace(\"internal.link.title\", namespace),\n internalUri: maybeNamespace(\"internal.link.uri.current\", namespace),\n internalSlug: maybeNamespace(\"internal.link.slug.current\", namespace),\n fileName: maybeNamespace(\"file.asset.originalFilename\", namespace),\n } as const satisfies PreviewCtx;\n}\n\n/** @public */\nexport function prepareLinkPreview({\n kind,\n email,\n phone,\n fileName,\n customText,\n internalUri,\n internalSlug,\n externalUrl,\n internalTitle,\n}: PreviewCtx) {\n switch (kind as keyof typeof LinkKind) {\n case \"internal\": {\n return {\n title: customText ?? internalTitle ?? \"Internal Link\",\n subtitle: internalUri ?? (internalSlug ? `/${internalSlug}` : undefined),\n media: () => <>📄</>,\n };\n }\n case \"external\":\n return {\n title: customText ?? \"External Link\",\n subtitle: externalUrl,\n media: () => <>🌍</>,\n };\n case \"email\":\n return {\n title: customText ?? \"Email Link\",\n subtitle: email,\n media: () => <>📧</>,\n };\n case \"phone\":\n return {\n title: customText ?? \"Phone Link\",\n subtitle: phone,\n media: () => <>☎️</>,\n };\n case \"file\":\n return {\n title: customText ?? \"File Link\",\n subtitle: fileName,\n media: () => <>📃</>,\n };\n default:\n return {\n title: customText ?? \"Empty Link\",\n media: () => <>⛓️‍💥</>,\n };\n }\n}\n","import type { ListItem } from \"@madebywild/sanity-utils/async-autocomplete\";\nimport type { ObjectDefinition, ObjectOptions, ReferenceTo } from \"sanity\";\n\n/** @public */\nexport const typeName = \"wild.link\" as const;\n\n/** @public */\nexport const LinkKind = {\n internal: \"internal\",\n external: \"external\",\n email: \"email\",\n phone: \"phone\",\n file: \"file\",\n} as const;\n\ntype SectionTargetConfig = {\n /**\n * Enable or disable section target selection.\n * Defaults to `true`.\n */\n enabled?: boolean;\n /**\n * GROQ query used to fetch section targets for the selected internal page.\n * The query should accept a `$pageId` param and return `{value, label}` items.\n */\n query?: string;\n};\n\n/** @public */\nexport type LinkExtensions = Partial<{\n /** Allows setting custom text for the link. */\n customText: boolean | { enabled?: boolean };\n}>;\n\n/** @public */\nexport type SectionQueryResult = ListItem[];\n\n/** @public */\nexport type PluginConfig = {\n /**\n * Whether references to internal links will be weak references.\n * Default is true.\n */\n weakReferences?: boolean;\n /**\n * Schema types that can be linked to internally.\n */\n internalLinkSchemaTypes: ReferenceTo;\n /**\n * Configure section target selection for internal links.\n */\n sectionTarget?: boolean | SectionTargetConfig;\n};\n\n/** @public */\nexport type FieldOptions = ObjectOptions & {\n /**\n * List of extension that add additional behaviors to the link field.\n */\n extensions?: LinkExtensions;\n};\n\n// Add the custom field definition to Sanity's intrinsic definitions\n// so that type checking works correctly when using this field type.\ndeclare module \"sanity\" {\n export interface IntrinsicDefinitions {\n [typeName]: Omit<ObjectDefinition, \"type\" | \"fields\" | \"options\" | \"components\"> & {\n type: typeof typeName;\n options?: FieldOptions;\n };\n }\n}\n","import { requiredIf, visibleIf } from \"@madebywild/sanity-utils\";\nimport type { ObjectRule } from \"sanity\";\nimport { defineField, definePlugin, defineType } from \"sanity\";\nimport { FieldInput, InternalLinkInput } from \"./input\";\nimport { prepareLinkPreview, selectLinkPreview } from \"./preview\";\nimport { type FieldOptions, type LinkExtensions, LinkKind, type PluginConfig, type SectionQueryResult, typeName } from \"./types\";\n\nconst visibleIfKind = visibleIf(\"kind\");\nconst requiredIfKind = requiredIf(\"kind\");\n\n/** @public */\nconst wildSanityLinkFieldPlugin = definePlugin<PluginConfig>((config) => {\n return {\n name: \"@madebywild/sanity-link-field\",\n schema: {\n types: [\n defineType({\n name: typeName,\n type: \"object\",\n title: \"Link\",\n description: \"Link to an internal page, external URL and more.\",\n icon: () => <>🔗</>,\n components: {\n input: (props) => <FieldInput {...props} />,\n },\n fields: [\n defineField({\n name: \"kind\",\n type: \"string\",\n title: \"Link Kind\",\n description: \"Select the kind of link.\",\n options: {\n layout: \"dropdown\",\n list: [\n { title: \"📄 Internal\", value: LinkKind.internal },\n { title: \"🌍 External\", value: LinkKind.external },\n { title: \"📧 Email\", value: LinkKind.email },\n { title: \"☎️ Phone\", value: LinkKind.phone },\n { title: \"📃 File\", value: LinkKind.file },\n ],\n },\n }),\n defineField({\n name: LinkKind.external,\n type: \"url\",\n title: \"External Link\",\n ...visibleIfKind(LinkKind.external),\n ...requiredIfKind(LinkKind.external),\n }),\n defineField({\n name: LinkKind.email,\n type: \"string\",\n title: \"Email\",\n ...visibleIfKind(LinkKind.email),\n ...requiredIfKind(LinkKind.email),\n }),\n defineField({\n name: LinkKind.phone,\n type: \"string\",\n title: \"Phone\",\n ...visibleIfKind(LinkKind.phone),\n ...requiredIfKind(LinkKind.phone),\n }),\n defineField({\n name: LinkKind.file,\n type: \"file\",\n title: \"File\",\n ...visibleIfKind(LinkKind.file),\n ...requiredIfKind(LinkKind.file),\n }),\n defineField({\n name: LinkKind.internal,\n type: \"object\",\n ...visibleIfKind(LinkKind.internal),\n ...requiredIfKind(LinkKind.internal, { path: [\"link\"] }),\n options: { collapsed: false, collapsible: false },\n components: {\n field: (props) => <>{props.children}</>,\n input: (props) => <InternalLinkInput pluginConfig={config} {...props} />,\n },\n fields: [\n defineField({\n type: \"reference\",\n name: \"link\",\n title: \"Internal Link\",\n description: \"Select a page and an optional section target.\",\n weak: config.weakReferences ?? true,\n to: config.internalLinkSchemaTypes,\n options: {\n disableNew: true,\n },\n }),\n defineField({\n type: \"string\",\n name: \"sectionTarget\",\n title: \"Section Target\",\n }),\n ],\n }),\n defineField({\n name: \"customText\",\n type: \"string\",\n title: \"Custom text\",\n description: \"Will take precedence over inferred text.\",\n }),\n defineField({\n name: \"canDownload\",\n type: \"boolean\",\n title: \"Downloadable\",\n initialValue: true,\n description: \"The file will be downloaded when the link is clicked.\",\n options: { layout: \"switch\" },\n ...visibleIfKind(LinkKind.file),\n }),\n defineField({\n name: \"openInNewTab\",\n type: \"boolean\",\n title: \"Open in new tab\",\n description: \"Open the link in a new tab.\",\n initialValue: false,\n options: { layout: \"switch\" },\n ...visibleIfKind([LinkKind.external, LinkKind.internal]),\n }),\n ],\n preview: {\n select: selectLinkPreview(),\n prepare: (props) => prepareLinkPreview(props),\n },\n }),\n ],\n },\n };\n});\n\n/** @public */\nfunction requireLink(R: ObjectRule) {\n return R.custom((ctx) => (!ctx?.kind ? { message: \"Select the kind of link.\", path: [\"kind\"] } : true));\n}\n\nexport {\n wildSanityLinkFieldPlugin,\n typeName,\n LinkKind,\n requireLink,\n selectLinkPreview,\n prepareLinkPreview,\n type LinkExtensions,\n type PluginConfig,\n type FieldOptions,\n type SectionQueryResult,\n};\n"],"names":["DefaultSectionsQuery","isExtensionEnabled","extensions","key","enabled","getSectionTargetConfig","sectionTarget","undefined","query","useFieldMember","members","fieldName","$","_c","t0","t1","member","kind","name","find","FieldInput","props","schemaType","options","map","InternalLinkInput","pluginConfig","linkFieldMember","selectedLink","value","link","selectedSection","sectionTargetConfig","sectionTargetEnabled","React","useEffect","_ref","onChange","unset","client","useClient","apiVersion","fetchSections","useCallback","items","fetch","pageId","Array","isArray","sections","opt","label","changeCase","capitalCase","next","set","maybeNamespace","field","ns","selectLinkPreview","namespace","phone","email","externalUrl","customText","internalTitle","internalUri","internalSlug","fileName","prepareLinkPreview","title","subtitle","media","typeName","LinkKind","internal","external","file","visibleIfKind","visibleIf","requiredIfKind","requiredIf","wildSanityLinkFieldPlugin","definePlugin","config","schema","types","defineType","type","description","icon","components","input","fields","defineField","layout","list","path","collapsed","collapsible","children","weak","weakReferences","to","internalLinkSchemaTypes","disableNew","initialValue","preview","select","prepare","requireLink","R","custom","ctx","message"],"mappings":";;;;;;;;AAQA,MAAMA,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAS7B,SAASC,mBAAmBC,YAA4BC,KAA2B;AACjF,SAAOD,WAAWC,GAAG,MAAMD,WAAWC,GAAG,MAAM,MAAQD,WAAWC,GAAG,EAAEC,YAAY;AACrF;AAEA,SAASC,uBAAuBC,eAA+C;AAC7E,SAAIA,kBAAkBC,UAAaD,kBAAkB,KAC5C;AAAA,IAAEF,SAAS;AAAA,IAAMI,OAAOR;AAAAA,EAAAA,IAG7BM,kBAAkB,KACb;AAAA,IAAEF,SAAS;AAAA,IAAOI,OAAOR;AAAAA,EAAAA,IAG3B;AAAA,IACLI,SAASE,cAAcF,YAAY;AAAA,IACnCI,OAAOF,cAAcE,SAASR;AAAAA,EAAAA;AAElC;AAEA,SAAAS,eAAAC,SAAAC,WAAA;AAAA,QAAAC,IAAAC,EAAA,CAAA;AAAA,MAAAC;AAAA,MAAAF,EAAA,CAAA,MAAAD,aAAAC,SAAAF,SAAA;AAAA,QAAAK;AAAAH,aAAAD,aAEuBI,KAAAC,YAAmCA,OAAMC,SAAU,WAAWD,OAAME,SAAUP,WAASC,OAAAD,WAAAC,OAAAG,MAAAA,KAAAH,EAAA,CAAA,GAApGE,KAAAJ,QAAOS,KAAMJ,EAAuF,GAACH,OAAAD,WAAAC,OAAAF,SAAAE,OAAAE;AAAAA,EAAA;AAAAA,SAAAF,EAAA,CAAA;AAAA,SAArGE;AAAqG;AAK/G,SAAAM,WAAAC,OAAA;AAAA,QAAAT,IAAAC,EAAA,CAAA,GAEEX,aADgBmB,MAAKC,WAAWC,SACNrB;AAAa,MAAAY;AAAAF,IAAA,CAAA,MAAAV,cAAAU,SAAAS,SAIlCP,KAAAO,MAAKX,QAAQc,IAAKR,CAAAA,WACbA,OAAMC,SAAU,WAIhB,EADsBf,cAAcD,mBAAmBC,YAAY,YAAY,MACzDc,OAAME,SAAU,eAAqB,OAExD,oBAAC,eAAqCF,QAAM,GAAMK,SAAhCL,OAAMb,GAA+B,CAC/D,GAACS,OAAAV,YAAAU,OAAAS,OAAAT,OAAAE,MAAAA,KAAAF,EAAA,CAAA;AAAA,MAAAG;AAAA,SAAAH,SAAAE,MATJC,KAAA,oBAAC,OAAA,EAAa,OAAA,GACXD,UAAAA,GAAAA,CASH,GAAQF,OAAAE,IAAAF,OAAAG,MAAAA,KAAAH,EAAA,CAAA,GAVRG;AAUQ;AAIZ,SAASU,kBAAkB;AAAA,EACzBC;AAAAA,EACA,GAAGL;AAGL,GAAG;AACD,QAAMM,kBAAkBlB,eAAeY,MAAMX,SAAS,MAAM,GAEtDkB,eAAeP,MAAMQ,OAAOC,MAC5BC,kBAAkBV,MAAMQ,OAAOvB,eAC/B0B,sBAAsB3B,uBAAuBqB,cAAcpB,aAAa,GACxE2B,uBAAuBD,oBAAoB5B;AAGjD8B,QAAMC,UAAU,MAAM;AACpB,KAAK,CAACP,cAAcQ,QAAQ,CAACH,yBAAyBF,oBAAoBxB,UACxEc,MAAMgB,SAASC,MAAM,CAAC,eAAe,CAAC,CAAC;AAAA,EAE3C,GAAG,CAACV,cAAcG,iBAAiBE,sBAAsBZ,MAAMgB,QAAQ,CAAC;AAExE,QAAME,SAASC,UAAU;AAAA,IAAEC,YAAY;AAAA,EAAA,CAAc,GAE/CC,gBAAgBR,MAAMS,YAAY,YAAY;AAClD,QAAI,CAACf,cAAcQ,QAAQ,CAACH,6BAA6B,CAAA;AAEzD,QAAI;AACF,YAAMW,QAAQ,MAAML,OAAOM,MAAMb,oBAAoBxB,OAAO;AAAA,QAAEsC,QAAQlB,aAAaQ;AAAAA,MAAAA,CAAM;AACzF,aAAIW,MAAMC,QAAQJ,KAAK,IAAUA,QAC7BA,SAAS,OAAOA,SAAU,YAAY,cAAcA,SAASG,MAAMC,QAAQJ,MAAMK,QAAQ,IACpFL,MAAMK,WAER,CAAA;AAAA,IACT,QAAQ;AACN,aAAO,CAAA;AAAA,IACT;AAAA,EACF,GAAG,CAACV,QAAQX,cAAcQ,MAAMH,sBAAsBD,oBAAoBxB,KAAK,CAAC;AAEhF,SACE,qBAAC,OAAA,EAAM,OAAO,GACXmB,UAAAA;AAAAA,IAAAA,mBAAmB,oBAAC,aAAA,EAAY,QAAQA,iBAAiB,GAAIN,OAAM;AAAA,IACnEY,wBAAwBL,cAAcQ,QACrC,oBAAC,mBAAA,EACC,aAAY,kBACZ,sBAAqB,qBACrB,WAAWM,eACX,OAAOX,iBACP,aAAa,CAACF,OAAOqB,QACZA,KAAKC,QAAQC,WAAWC,YAAYH,IAAIC,KAAK,IAAItB,OAE1D,UAAWA,CAAAA,YAAU;AACnB,YAAMyB,OAAOzB,UAAQ0B,IAAI1B,SAAO,CAAC,eAAe,CAAC,IAAIS,MAAM,CAAC,eAAe,CAAC;AAC5E,aAAOjB,MAAMgB,SAASiB,IAAI;AAAA,IAC5B,GACA,cAAc,CAAC;AAAA,MAAEH;AAAAA,MAAOtB,OAAAA;AAAAA,IAAAA,0BAEnB,MAAA,EAAK,IAAG,UACP,UAAA,oBAAC,KAAA,EAAI,MAAM,GAAG,SAAS,GACrB,8BAAC,MAAA,EAAK,MAAM,GAAIuB,UAAAA,WAAWC,YAAYF,SAAStB,OAAK,EAAA,CAAE,EAAA,CACzD,EAAA,CACF,EAAA,CAEF;AAAA,EAAA,GAGR;AAEJ;AClHA,SAAS2B,eAAeC,OAAeC,IAAa;AAClD,SAAOA,KAAK,GAAGA,EAAE,IAAID,KAAK,KAAKA;AACjC;AAGO,SAASE,kBAAkBC,WAAoB;AACpD,SAAO;AAAA,IACL3C,MAAMuC,eAAe,QAAQI,SAAS;AAAA,IACtCC,OAAOL,eAAe,SAASI,SAAS;AAAA,IACxCE,OAAON,eAAe,SAASI,SAAS;AAAA,IACxCG,aAAaP,eAAe,YAAYI,SAAS;AAAA,IACjDI,YAAYR,eAAe,cAAcI,SAAS;AAAA,IAClDK,eAAeT,eAAe,uBAAuBI,SAAS;AAAA,IAC9DM,aAAaV,eAAe,6BAA6BI,SAAS;AAAA,IAClEO,cAAcX,eAAe,8BAA8BI,SAAS;AAAA,IACpEQ,UAAUZ,eAAe,+BAA+BI,SAAS;AAAA,EAAA;AAErE;AAGO,SAASS,mBAAmB;AAAA,EACjCpD;AAAAA,EACA6C;AAAAA,EACAD;AAAAA,EACAO;AAAAA,EACAJ;AAAAA,EACAE;AAAAA,EACAC;AAAAA,EACAJ;AAAAA,EACAE;AACU,GAAG;AACb,UAAQhD,MAAAA;AAAAA,IACN,KAAK;AACH,aAAO;AAAA,QACLqD,OAAON,cAAcC,iBAAiB;AAAA,QACtCM,UAAUL,gBAAgBC,eAAe,IAAIA,YAAY,KAAK5D;AAAAA,QAC9DiE,OAAOA,MAAM,oBAAA,UAAA,EAAE,UAAA,YAAA,CAAE;AAAA,MAAA;AAAA,IAGrB,KAAK;AACH,aAAO;AAAA,QACLF,OAAON,cAAc;AAAA,QACrBO,UAAUR;AAAAA,QACVS,OAAOA,MAAM,oBAAA,UAAA,EAAE,UAAA,YAAA,CAAE;AAAA,MAAA;AAAA,IAErB,KAAK;AACH,aAAO;AAAA,QACLF,OAAON,cAAc;AAAA,QACrBO,UAAUT;AAAAA,QACVU,OAAOA,MAAM,oBAAA,UAAA,EAAE,UAAA,YAAA,CAAE;AAAA,MAAA;AAAA,IAErB,KAAK;AACH,aAAO;AAAA,QACLF,OAAON,cAAc;AAAA,QACrBO,UAAUV;AAAAA,QACVW,OAAOA,MAAM,oBAAA,UAAA,EAAE,UAAA,eAAA,CAAE;AAAA,MAAA;AAAA,IAErB,KAAK;AACH,aAAO;AAAA,QACLF,OAAON,cAAc;AAAA,QACrBO,UAAUH;AAAAA,QACVI,OAAOA,MAAM,oBAAA,UAAA,EAAE,UAAA,YAAA,CAAE;AAAA,MAAA;AAAA,IAErB;AACE,aAAO;AAAA,QACLF,OAAON,cAAc;AAAA,QACrBQ,OAAOA,MAAM,oBAAA,UAAA,EAAE,UAAA,8BAAA,CAAK;AAAA,MAAA;AAAA,EACtB;AAEN;AC/EO,MAAMC,WAAW,aAGXC,WAAW;AAAA,EACtBC,UAAU;AAAA,EACVC,UAAU;AAAA,EACVd,OAAO;AAAA,EACPD,OAAO;AAAA,EACPgB,MAAM;AACR,GCNMC,gBAAgBC,UAAU,MAAM,GAChCC,iBAAiBC,WAAW,MAAM,GAGlCC,4BAA4BC,aAA4BC,CAAAA,YACrD;AAAA,EACLlE,MAAM;AAAA,EACNmE,QAAQ;AAAA,IACNC,OAAO,CACLC,WAAW;AAAA,MACTrE,MAAMuD;AAAAA,MACNe,MAAM;AAAA,MACNlB,OAAO;AAAA,MACPmB,aAAa;AAAA,MACbC,MAAMA,MAAM,oBAAA,UAAA,EAAE,UAAA,YAAA,CAAE;AAAA,MAChBC,YAAY;AAAA,QACVC,OAAQvE,CAAAA,UAAU,oBAAC,YAAA,EAAW,GAAIA,MAAAA,CAAM;AAAA,MAAA;AAAA,MAE1CwE,QAAQ,CACNC,YAAY;AAAA,QACV5E,MAAM;AAAA,QACNsE,MAAM;AAAA,QACNlB,OAAO;AAAA,QACPmB,aAAa;AAAA,QACblE,SAAS;AAAA,UACPwE,QAAQ;AAAA,UACRC,MAAM,CACJ;AAAA,YAAE1B,OAAO;AAAA,YAAezC,OAAO6C,SAASC;AAAAA,UAAAA,GACxC;AAAA,YAAEL,OAAO;AAAA,YAAezC,OAAO6C,SAASE;AAAAA,UAAAA,GACxC;AAAA,YAAEN,OAAO;AAAA,YAAYzC,OAAO6C,SAASZ;AAAAA,UAAAA,GACrC;AAAA,YAAEQ,OAAO;AAAA,YAAYzC,OAAO6C,SAASb;AAAAA,UAAAA,GACrC;AAAA,YAAES,OAAO;AAAA,YAAWzC,OAAO6C,SAASG;AAAAA,UAAAA,CAAM;AAAA,QAAA;AAAA,MAE9C,CACD,GACDiB,YAAY;AAAA,QACV5E,MAAMwD,SAASE;AAAAA,QACfY,MAAM;AAAA,QACNlB,OAAO;AAAA,QACP,GAAGQ,cAAcJ,SAASE,QAAQ;AAAA,QAClC,GAAGI,eAAeN,SAASE,QAAQ;AAAA,MAAA,CACpC,GACDkB,YAAY;AAAA,QACV5E,MAAMwD,SAASZ;AAAAA,QACf0B,MAAM;AAAA,QACNlB,OAAO;AAAA,QACP,GAAGQ,cAAcJ,SAASZ,KAAK;AAAA,QAC/B,GAAGkB,eAAeN,SAASZ,KAAK;AAAA,MAAA,CACjC,GACDgC,YAAY;AAAA,QACV5E,MAAMwD,SAASb;AAAAA,QACf2B,MAAM;AAAA,QACNlB,OAAO;AAAA,QACP,GAAGQ,cAAcJ,SAASb,KAAK;AAAA,QAC/B,GAAGmB,eAAeN,SAASb,KAAK;AAAA,MAAA,CACjC,GACDiC,YAAY;AAAA,QACV5E,MAAMwD,SAASG;AAAAA,QACfW,MAAM;AAAA,QACNlB,OAAO;AAAA,QACP,GAAGQ,cAAcJ,SAASG,IAAI;AAAA,QAC9B,GAAGG,eAAeN,SAASG,IAAI;AAAA,MAAA,CAChC,GACDiB,YAAY;AAAA,QACV5E,MAAMwD,SAASC;AAAAA,QACfa,MAAM;AAAA,QACN,GAAGV,cAAcJ,SAASC,QAAQ;AAAA,QAClC,GAAGK,eAAeN,SAASC,UAAU;AAAA,UAAEsB,MAAM,CAAC,MAAM;AAAA,QAAA,CAAG;AAAA,QACvD1E,SAAS;AAAA,UAAE2E,WAAW;AAAA,UAAOC,aAAa;AAAA,QAAA;AAAA,QAC1CR,YAAY;AAAA,UACVlC,OAAQpC,CAAAA,UAAU,oBAAA,UAAA,EAAGA,UAAAA,MAAM+E,UAAS;AAAA,UACpCR,OAAQvE,CAAAA,UAAU,oBAAC,qBAAkB,cAAc+D,QAAQ,GAAI/D,MAAAA,CAAM;AAAA,QAAA;AAAA,QAEvEwE,QAAQ,CACNC,YAAY;AAAA,UACVN,MAAM;AAAA,UACNtE,MAAM;AAAA,UACNoD,OAAO;AAAA,UACPmB,aAAa;AAAA,UACbY,MAAMjB,OAAOkB,kBAAkB;AAAA,UAC/BC,IAAInB,OAAOoB;AAAAA,UACXjF,SAAS;AAAA,YACPkF,YAAY;AAAA,UAAA;AAAA,QACd,CACD,GACDX,YAAY;AAAA,UACVN,MAAM;AAAA,UACNtE,MAAM;AAAA,UACNoD,OAAO;AAAA,QAAA,CACR,CAAC;AAAA,MAAA,CAEL,GACDwB,YAAY;AAAA,QACV5E,MAAM;AAAA,QACNsE,MAAM;AAAA,QACNlB,OAAO;AAAA,QACPmB,aAAa;AAAA,MAAA,CACd,GACDK,YAAY;AAAA,QACV5E,MAAM;AAAA,QACNsE,MAAM;AAAA,QACNlB,OAAO;AAAA,QACPoC,cAAc;AAAA,QACdjB,aAAa;AAAA,QACblE,SAAS;AAAA,UAAEwE,QAAQ;AAAA,QAAA;AAAA,QACnB,GAAGjB,cAAcJ,SAASG,IAAI;AAAA,MAAA,CAC/B,GACDiB,YAAY;AAAA,QACV5E,MAAM;AAAA,QACNsE,MAAM;AAAA,QACNlB,OAAO;AAAA,QACPmB,aAAa;AAAA,QACbiB,cAAc;AAAA,QACdnF,SAAS;AAAA,UAAEwE,QAAQ;AAAA,QAAA;AAAA,QACnB,GAAGjB,cAAc,CAACJ,SAASE,UAAUF,SAASC,QAAQ,CAAC;AAAA,MAAA,CACxD,CAAC;AAAA,MAEJgC,SAAS;AAAA,QACPC,QAAQjD,kBAAAA;AAAAA,QACRkD,SAAUxF,CAAAA,UAAUgD,mBAAmBhD,KAAK;AAAA,MAAA;AAAA,IAC9C,CACD,CAAC;AAAA,EAAA;AAGR,EACD;AAGD,SAASyF,YAAYC,GAAe;AAClC,SAAOA,EAAEC,OAAQC,CAAAA,QAAUA,KAAKhG,OAAiE,KAA1D;AAAA,IAAEiG,SAAS;AAAA,IAA4BjB,MAAM,CAAC,MAAM;AAAA,EAAA,CAAW;AACxG;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@madebywild/sanity-link-field",
3
- "version": "0.3.21",
3
+ "version": "1.0.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "license": "UNLICENSED",
@@ -22,21 +22,21 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "change-case": "^5.4.4",
25
- "@madebywild/sanity-utils": "0.3.17"
25
+ "@madebywild/sanity-utils": "1.0.0"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "@sanity/ui": "^3.1",
29
- "react": "^19",
30
- "react-dom": "^19",
29
+ "react": "^19.2.2",
30
+ "react-dom": "^19.2.2",
31
31
  "sanity": "^4.17 || ^5.0.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@sanity/pkg-utils": "^9.2",
35
- "@types/react": "^19",
36
- "@types/react-dom": "^19",
35
+ "@types/react": "^19.2.2",
36
+ "@types/react-dom": "^19.2.2",
37
37
  "babel-plugin-react-compiler": "1.0.0",
38
- "react": "^19",
39
- "react-dom": "^19",
38
+ "react": "^19.2.2",
39
+ "react-dom": "^19.2.2",
40
40
  "sanity": "^4.17 || ^5.0.0",
41
41
  "typescript": "^5"
42
42
  },
package/src/index.tsx CHANGED
@@ -3,7 +3,7 @@ import type { ObjectRule } from "sanity";
3
3
  import { defineField, definePlugin, defineType } from "sanity";
4
4
  import { FieldInput, InternalLinkInput } from "./input";
5
5
  import { prepareLinkPreview, selectLinkPreview } from "./preview";
6
- import { type FieldOptions, LinkKind, type PluginConfig, type SectionQueryResult, typeName } from "./types";
6
+ import { type FieldOptions, type LinkExtensions, LinkKind, type PluginConfig, type SectionQueryResult, typeName } from "./types";
7
7
 
8
8
  const visibleIfKind = visibleIf("kind");
9
9
  const requiredIfKind = requiredIf("kind");
@@ -76,7 +76,7 @@ const wildSanityLinkFieldPlugin = definePlugin<PluginConfig>((config) => {
76
76
  options: { collapsed: false, collapsible: false },
77
77
  components: {
78
78
  field: (props) => <>{props.children}</>,
79
- input: (props) => <InternalLinkInput {...props} />,
79
+ input: (props) => <InternalLinkInput pluginConfig={config} {...props} />,
80
80
  },
81
81
  fields: [
82
82
  defineField({
@@ -144,6 +144,7 @@ export {
144
144
  requireLink,
145
145
  selectLinkPreview,
146
146
  prepareLinkPreview,
147
+ type LinkExtensions,
147
148
  type PluginConfig,
148
149
  type FieldOptions,
149
150
  type SectionQueryResult,
package/src/input.tsx CHANGED
@@ -1,11 +1,45 @@
1
- import { useFieldMember } from "@madebywild/sanity-utils";
2
1
  import { AsyncAutocomplete } from "@madebywild/sanity-utils/async-autocomplete";
3
2
  import { Box, Card, Stack, Text } from "@sanity/ui";
4
3
  import * as changeCase from "change-case";
5
4
  import * as React from "react";
6
- import type { ObjectInputProps, ObjectMember, Reference } from "sanity";
5
+ import type { FieldMember, ObjectInputProps, ObjectMember, Reference } from "sanity";
7
6
  import { MemberField, set, unset, useClient } from "sanity";
8
- import type { FieldOptions } from "./types";
7
+ import type { FieldOptions, LinkExtensions, PluginConfig } from "./types";
8
+
9
+ const DefaultSectionsQuery = `
10
+ *[_id == $pageId && defined(pageBuilder.sectionsArray)][0]{
11
+ "sections": array::compact(pageBuilder.sectionsArray[]{
12
+ "value": _key,
13
+ "label": coalesce(sectionSettings.sectionTitle, _type),
14
+ }),
15
+ }.sections
16
+ `;
17
+
18
+ function isExtensionEnabled(extensions: LinkExtensions, key: keyof LinkExtensions) {
19
+ return extensions[key] && (extensions[key] === true || extensions[key].enabled !== false);
20
+ }
21
+
22
+ function getSectionTargetConfig(sectionTarget?: PluginConfig["sectionTarget"]) {
23
+ if (sectionTarget === undefined || sectionTarget === true) {
24
+ return { enabled: true, query: DefaultSectionsQuery };
25
+ }
26
+
27
+ if (sectionTarget === false) {
28
+ return { enabled: false, query: DefaultSectionsQuery };
29
+ }
30
+
31
+ return {
32
+ enabled: sectionTarget.enabled !== false,
33
+ query: sectionTarget.query ?? DefaultSectionsQuery,
34
+ };
35
+ }
36
+
37
+ function useFieldMember(members: ObjectMember[], fieldName: string): FieldMember | undefined {
38
+ return React.useMemo(
39
+ () => members.find((member): member is FieldMember => member.kind === "field" && member.name === fieldName),
40
+ [members, fieldName]
41
+ );
42
+ }
9
43
 
10
44
  function FieldInput(props: ObjectInputProps) {
11
45
  const options = props.schemaType.options as FieldOptions | undefined;
@@ -16,8 +50,9 @@ function FieldInput(props: ObjectInputProps) {
16
50
  {props.members.map((member: ObjectMember) => {
17
51
  if (member.kind !== "field") return null;
18
52
 
19
- // Only show enabled extensions.
20
- if (!extensions?.includes("customText") && member.name === "customText") return null;
53
+ // Custom text extension.
54
+ const customTextEnabled = extensions && isExtensionEnabled(extensions, "customText");
55
+ if (!customTextEnabled && member.name === "customText") return null;
21
56
 
22
57
  return <MemberField key={member.key} member={member} {...props} />;
23
58
  })}
@@ -25,45 +60,47 @@ function FieldInput(props: ObjectInputProps) {
25
60
  );
26
61
  }
27
62
 
28
- const SectionsQuery = `
29
- *[_id == $pageId && defined(pageBuilder.sectionsArray)][0]{
30
- "sections": array::compact(pageBuilder.sectionsArray[]{
31
- "value": _key,
32
- "label": coalesce(sectionSettings.sectionTitle, _type),
33
- }),
34
- }.sections
35
- `;
36
-
37
- function InternalLinkInput(props: ObjectInputProps) {
63
+ function InternalLinkInput({
64
+ pluginConfig,
65
+ ...props
66
+ }: ObjectInputProps & {
67
+ pluginConfig?: PluginConfig;
68
+ }) {
38
69
  const linkFieldMember = useFieldMember(props.members, "link");
39
70
 
40
71
  const selectedLink = props.value?.link as Reference | undefined;
41
72
  const selectedSection = props.value?.sectionTarget as string | undefined;
73
+ const sectionTargetConfig = getSectionTargetConfig(pluginConfig?.sectionTarget);
74
+ const sectionTargetEnabled = sectionTargetConfig.enabled;
42
75
 
43
- // Reset sectionTarget if link is removed.
76
+ // Reset sectionTarget if link is removed or feature is disabled.
44
77
  React.useEffect(() => {
45
- if (!selectedLink?._ref && selectedSection !== undefined) {
78
+ if ((!selectedLink?._ref || !sectionTargetEnabled) && selectedSection !== undefined) {
46
79
  props.onChange(unset(["sectionTarget"]));
47
80
  }
48
- }, [selectedLink, selectedSection, props.onChange]);
81
+ }, [selectedLink, selectedSection, sectionTargetEnabled, props.onChange]);
49
82
 
50
83
  const client = useClient({ apiVersion: "2025-10-21" });
51
84
 
52
85
  const fetchSections = React.useCallback(async () => {
53
- if (!selectedLink?._ref) return [];
86
+ if (!selectedLink?._ref || !sectionTargetEnabled) return [];
54
87
 
55
88
  try {
56
- const items = await client.fetch(SectionsQuery, { pageId: selectedLink._ref });
57
- return items;
89
+ const items = await client.fetch(sectionTargetConfig.query, { pageId: selectedLink._ref });
90
+ if (Array.isArray(items)) return items;
91
+ if (items && typeof items === "object" && "sections" in items && Array.isArray(items.sections)) {
92
+ return items.sections;
93
+ }
94
+ return [];
58
95
  } catch {
59
96
  return [];
60
97
  }
61
- }, [client, selectedLink?._ref]);
98
+ }, [client, selectedLink?._ref, sectionTargetEnabled, sectionTargetConfig.query]);
62
99
 
63
100
  return (
64
101
  <Stack space={2}>
65
102
  {linkFieldMember && <MemberField member={linkFieldMember} {...props} />}
66
- {selectedLink?._ref && (
103
+ {sectionTargetEnabled && selectedLink?._ref && (
67
104
  <AsyncAutocomplete
68
105
  placeholder="Select section"
69
106
  noOptionsPlaceholder="No sections found"
package/src/preview.tsx CHANGED
@@ -8,6 +8,7 @@ type PreviewCtx = {
8
8
  customText?: string;
9
9
  externalUrl?: string;
10
10
  internalUri?: string;
11
+ internalSlug?: string;
11
12
  internalTitle?: string;
12
13
  };
13
14
 
@@ -23,8 +24,9 @@ export function selectLinkPreview(namespace?: string) {
23
24
  email: maybeNamespace("email", namespace),
24
25
  externalUrl: maybeNamespace("external", namespace),
25
26
  customText: maybeNamespace("customText", namespace),
26
- internalTitle: maybeNamespace("internal.title", namespace),
27
- internalUri: maybeNamespace("internal.uri.current", namespace),
27
+ internalTitle: maybeNamespace("internal.link.title", namespace),
28
+ internalUri: maybeNamespace("internal.link.uri.current", namespace),
29
+ internalSlug: maybeNamespace("internal.link.slug.current", namespace),
28
30
  fileName: maybeNamespace("file.asset.originalFilename", namespace),
29
31
  } as const satisfies PreviewCtx;
30
32
  }
@@ -37,6 +39,7 @@ export function prepareLinkPreview({
37
39
  fileName,
38
40
  customText,
39
41
  internalUri,
42
+ internalSlug,
40
43
  externalUrl,
41
44
  internalTitle,
42
45
  }: PreviewCtx) {
@@ -44,7 +47,7 @@ export function prepareLinkPreview({
44
47
  case "internal": {
45
48
  return {
46
49
  title: customText ?? internalTitle ?? "Internal Link",
47
- subtitle: internalUri,
50
+ subtitle: internalUri ?? (internalSlug ? `/${internalSlug}` : undefined),
48
51
  media: () => <>📄</>,
49
52
  };
50
53
  }
package/src/types.tsx CHANGED
@@ -13,10 +13,24 @@ export const LinkKind = {
13
13
  file: "file",
14
14
  } as const;
15
15
 
16
+ type SectionTargetConfig = {
17
+ /**
18
+ * Enable or disable section target selection.
19
+ * Defaults to `true`.
20
+ */
21
+ enabled?: boolean;
22
+ /**
23
+ * GROQ query used to fetch section targets for the selected internal page.
24
+ * The query should accept a `$pageId` param and return `{value, label}` items.
25
+ */
26
+ query?: string;
27
+ };
28
+
16
29
  /** @public */
17
- export const LinkExtension = {
18
- customText: "customText",
19
- } as const;
30
+ export type LinkExtensions = Partial<{
31
+ /** Allows setting custom text for the link. */
32
+ customText: boolean | { enabled?: boolean };
33
+ }>;
20
34
 
21
35
  /** @public */
22
36
  export type SectionQueryResult = ListItem[];
@@ -32,15 +46,18 @@ export type PluginConfig = {
32
46
  * Schema types that can be linked to internally.
33
47
  */
34
48
  internalLinkSchemaTypes: ReferenceTo;
49
+ /**
50
+ * Configure section target selection for internal links.
51
+ */
52
+ sectionTarget?: boolean | SectionTargetConfig;
35
53
  };
36
54
 
37
55
  /** @public */
38
56
  export type FieldOptions = ObjectOptions & {
39
57
  /**
40
58
  * List of extension that add additional behaviors to the link field.
41
- * - `customText` - Allows setting custom text for the link.
42
59
  */
43
- extensions?: (keyof typeof LinkExtension)[];
60
+ extensions?: LinkExtensions;
44
61
  };
45
62
 
46
63
  // Add the custom field definition to Sanity's intrinsic definitions