@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 +21 -0
- package/README.md +58 -0
- package/dist/index.cjs +51 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -6
- package/dist/index.d.ts +27 -6
- package/dist/index.js +52 -21
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
- package/src/index.tsx +3 -2
- package/src/input.tsx +60 -23
- package/src/preview.tsx +6 -3
- package/src/types.tsx +22 -5
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
|
-
|
|
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
|
|
36
|
-
|
|
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
|
-
|
|
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",
|
package/dist/index.cjs.map
CHANGED
|
@@ -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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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?:
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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?:
|
|
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 {
|
|
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
|
-
|
|
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
|
|
25
|
-
|
|
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
|
-
|
|
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":";;;;;;;;AASA,SAAAA,WAAAC,OAAA;AAAA,QAAAC,IAAAC,EAAA,CAAA,GAEEC,aADgBH,MAAKI,WAAWC,SACNF;AAAa,MAAAG;AAAAL,IAAA,CAAA,MAAAE,cAAAF,SAAAD,SAIlCM,KAAAN,MAAKO,QAAQC,IAAKC,CAAAA,WACbA,OAAMC,SAAU,WAGhB,CAACP,YAAUQ,SAAW,YAAY,KAAKF,OAAMG,SAAU,eAAqB,OAEzE,oBAAC,aAAA,EAAqCH,QAAM,GAAMT,SAAhCS,OAAMI,GAA+B,CAC/D,GAACZ,OAAAE,YAAAF,OAAAD,OAAAC,OAAAK,MAAAA,KAAAL,EAAA,CAAA;AAAA,MAAAa;AAAA,SAAAb,SAAAK,MARJQ,KAAA,oBAAC,OAAA,EAAa,OAAA,GACXR,UAAAA,GAAAA,CAQH,GAAQL,OAAAK,IAAAL,OAAAa,MAAAA,KAAAb,EAAA,CAAA,GATRa;AASQ;AAIZ,MAAMC,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAStB,SAASC,kBAAkBhB,OAAyB;AAClD,QAAMiB,kBAAkBC,eAAelB,MAAMO,SAAS,MAAM,GAEtDY,eAAenB,MAAMoB,OAAOC,MAC5BC,kBAAkBtB,MAAMoB,OAAOG;AAGrCC,QAAMC,UAAU,MAAM;AAChB,KAACN,cAAcO,QAAQJ,oBAAoBK,UAC7C3B,MAAM4B,SAASC,MAAM,CAAC,eAAe,CAAC,CAAC;AAAA,EAE3C,GAAG,CAACV,cAAcG,iBAAiBtB,MAAM4B,QAAQ,CAAC;AAElD,QAAME,SAASC,UAAU;AAAA,IAAEC,YAAY;AAAA,EAAA,CAAc,GAE/CC,gBAAgBT,MAAMU,YAAY,YAAY;AAClD,QAAI,CAACf,cAAcO,KAAM,QAAO,CAAA;AAEhC,QAAI;AAEF,aADc,MAAMI,OAAOK,MAAMpB,eAAe;AAAA,QAAEqB,QAAQjB,aAAaO;AAAAA,MAAAA,CAAM;AAAA,IAE/E,QAAQ;AACN,aAAO,CAAA;AAAA,IACT;AAAA,EACF,GAAG,CAACI,QAAQX,cAAcO,IAAI,CAAC;AAE/B,SACE,qBAAC,OAAA,EAAM,OAAO,GACXT,UAAAA;AAAAA,IAAAA,mBAAmB,oBAAC,aAAA,EAAY,QAAQA,iBAAiB,GAAIjB,OAAM;AAAA,IACnEmB,cAAcO,QACb,oBAAC,mBAAA,EACC,aAAY,kBACZ,sBAAqB,qBACrB,WAAWO,eACX,OAAOX,iBACP,aAAa,CAACF,OAAOiB,QACZA,KAAKC,QAAQC,WAAWC,YAAYH,IAAIC,KAAK,IAAIlB,OAE1D,UAAWA,CAAAA,YAAU;AACnB,YAAMqB,OAAOrB,UAAQsB,IAAItB,SAAO,CAAC,eAAe,CAAC,IAAIS,MAAM,CAAC,eAAe,CAAC;AAC5E,aAAO7B,MAAM4B,SAASa,IAAI;AAAA,IAC5B,GACA,cAAc,CAAC;AAAA,MAAEH;AAAAA,MAAOlB,OAAAA;AAAAA,IAAAA,0BAEnB,MAAA,EAAK,IAAG,UACP,UAAA,oBAAC,KAAA,EAAI,MAAM,GAAG,SAAS,GACrB,8BAAC,MAAA,EAAK,MAAM,GAAImB,UAAAA,WAAWC,YAAYF,SAASlB,OAAK,EAAA,CAAE,EAAA,CACzD,EAAA,CACF,EAAA,CAEF;AAAA,EAAA,GAGR;AAEJ;AC9EA,SAASuB,eAAeC,OAAeC,IAAa;AAClD,SAAOA,KAAK,GAAGA,EAAE,IAAID,KAAK,KAAKA;AACjC;AAGO,SAASE,kBAAkBC,WAAoB;AACpD,SAAO;AAAA,IACLrC,MAAMiC,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,kBAAkBI,SAAS;AAAA,IACzDM,aAAaV,eAAe,wBAAwBI,SAAS;AAAA,IAC7DO,UAAUX,eAAe,+BAA+BI,SAAS;AAAA,EAAA;AAErE;AAGO,SAASQ,mBAAmB;AAAA,EACjC7C;AAAAA,EACAuC;AAAAA,EACAD;AAAAA,EACAM;AAAAA,EACAH;AAAAA,EACAE;AAAAA,EACAH;AAAAA,EACAE;AACU,GAAG;AACb,UAAQ1C,MAAAA;AAAAA,IACN,KAAK;AACH,aAAO;AAAA,QACL8C,OAAOL,cAAcC,iBAAiB;AAAA,QACtCK,UAAUJ;AAAAA,QACVK,OAAOA,MAAM,oBAAA,UAAA,EAAE,UAAA,YAAA,CAAE;AAAA,MAAA;AAAA,IAGrB,KAAK;AACH,aAAO;AAAA,QACLF,OAAOL,cAAc;AAAA,QACrBM,UAAUP;AAAAA,QACVQ,OAAOA,MAAM,oBAAA,UAAA,EAAE,UAAA,YAAA,CAAE;AAAA,MAAA;AAAA,IAErB,KAAK;AACH,aAAO;AAAA,QACLF,OAAOL,cAAc;AAAA,QACrBM,UAAUR;AAAAA,QACVS,OAAOA,MAAM,oBAAA,UAAA,EAAE,UAAA,YAAA,CAAE;AAAA,MAAA;AAAA,IAErB,KAAK;AACH,aAAO;AAAA,QACLF,OAAOL,cAAc;AAAA,QACrBM,UAAUT;AAAAA,QACVU,OAAOA,MAAM,oBAAA,UAAA,EAAE,UAAA,eAAA,CAAE;AAAA,MAAA;AAAA,IAErB,KAAK;AACH,aAAO;AAAA,QACLF,OAAOL,cAAc;AAAA,QACrBM,UAAUH;AAAAA,QACVI,OAAOA,MAAM,oBAAA,UAAA,EAAE,UAAA,YAAA,CAAE;AAAA,MAAA;AAAA,IAErB;AACE,aAAO;AAAA,QACLF,OAAOL,cAAc;AAAA,QACrBO,OAAOA,MAAM,oBAAA,UAAA,EAAE,UAAA,8BAAA,CAAK;AAAA,MAAA;AAAA,EACtB;AAEN;AC5EO,MAAMC,WAAW,aAGXC,WAAW;AAAA,EACtBC,UAAU;AAAA,EACVC,UAAU;AAAA,EACVb,OAAO;AAAA,EACPD,OAAO;AAAA,EACPe,MAAM;AACR,GCNMC,gBAAgBC,UAAU,MAAM,GAChCC,iBAAiBC,WAAW,MAAM,GAGlCC,4BAA4BC,aAA4BC,CAAAA,YACrD;AAAA,EACL1D,MAAM;AAAA,EACN2D,QAAQ;AAAA,IACNC,OAAO,CACLC,WAAW;AAAA,MACT7D,MAAM+C;AAAAA,MACNe,MAAM;AAAA,MACNlB,OAAO;AAAA,MACPmB,aAAa;AAAA,MACbC,MAAMA,MAAM,oBAAA,UAAA,EAAE,UAAA,YAAA,CAAE;AAAA,MAChBC,YAAY;AAAA,QACVC,OAAQ9E,CAAAA,UAAU,oBAAC,YAAA,EAAW,GAAIA,MAAAA,CAAM;AAAA,MAAA;AAAA,MAE1C+E,QAAQ,CACNC,YAAY;AAAA,QACVpE,MAAM;AAAA,QACN8D,MAAM;AAAA,QACNlB,OAAO;AAAA,QACPmB,aAAa;AAAA,QACbtE,SAAS;AAAA,UACP4E,QAAQ;AAAA,UACRC,MAAM,CACJ;AAAA,YAAE1B,OAAO;AAAA,YAAepC,OAAOwC,SAASC;AAAAA,UAAAA,GACxC;AAAA,YAAEL,OAAO;AAAA,YAAepC,OAAOwC,SAASE;AAAAA,UAAAA,GACxC;AAAA,YAAEN,OAAO;AAAA,YAAYpC,OAAOwC,SAASX;AAAAA,UAAAA,GACrC;AAAA,YAAEO,OAAO;AAAA,YAAYpC,OAAOwC,SAASZ;AAAAA,UAAAA,GACrC;AAAA,YAAEQ,OAAO;AAAA,YAAWpC,OAAOwC,SAASG;AAAAA,UAAAA,CAAM;AAAA,QAAA;AAAA,MAE9C,CACD,GACDiB,YAAY;AAAA,QACVpE,MAAMgD,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,QACVpE,MAAMgD,SAASX;AAAAA,QACfyB,MAAM;AAAA,QACNlB,OAAO;AAAA,QACP,GAAGQ,cAAcJ,SAASX,KAAK;AAAA,QAC/B,GAAGiB,eAAeN,SAASX,KAAK;AAAA,MAAA,CACjC,GACD+B,YAAY;AAAA,QACVpE,MAAMgD,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,QACVpE,MAAMgD,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,QACVpE,MAAMgD,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,QACvD9E,SAAS;AAAA,UAAE+E,WAAW;AAAA,UAAOC,aAAa;AAAA,QAAA;AAAA,QAC1CR,YAAY;AAAA,UACVjC,OAAQ5C,CAAAA,UAAU,oBAAA,UAAA,EAAGA,UAAAA,MAAMsF,UAAS;AAAA,UACpCR,OAAQ9E,CAAAA,UAAU,oBAAC,mBAAA,EAAkB,GAAIA,MAAAA,CAAM;AAAA,QAAA;AAAA,QAEjD+E,QAAQ,CACNC,YAAY;AAAA,UACVN,MAAM;AAAA,UACN9D,MAAM;AAAA,UACN4C,OAAO;AAAA,UACPmB,aAAa;AAAA,UACbY,MAAMjB,OAAOkB,kBAAkB;AAAA,UAC/BC,IAAInB,OAAOoB;AAAAA,UACXrF,SAAS;AAAA,YACPsF,YAAY;AAAA,UAAA;AAAA,QACd,CACD,GACDX,YAAY;AAAA,UACVN,MAAM;AAAA,UACN9D,MAAM;AAAA,UACN4C,OAAO;AAAA,QAAA,CACR,CAAC;AAAA,MAAA,CAEL,GACDwB,YAAY;AAAA,QACVpE,MAAM;AAAA,QACN8D,MAAM;AAAA,QACNlB,OAAO;AAAA,QACPmB,aAAa;AAAA,MAAA,CACd,GACDK,YAAY;AAAA,QACVpE,MAAM;AAAA,QACN8D,MAAM;AAAA,QACNlB,OAAO;AAAA,QACPoC,cAAc;AAAA,QACdjB,aAAa;AAAA,QACbtE,SAAS;AAAA,UAAE4E,QAAQ;AAAA,QAAA;AAAA,QACnB,GAAGjB,cAAcJ,SAASG,IAAI;AAAA,MAAA,CAC/B,GACDiB,YAAY;AAAA,QACVpE,MAAM;AAAA,QACN8D,MAAM;AAAA,QACNlB,OAAO;AAAA,QACPmB,aAAa;AAAA,QACbiB,cAAc;AAAA,QACdvF,SAAS;AAAA,UAAE4E,QAAQ;AAAA,QAAA;AAAA,QACnB,GAAGjB,cAAc,CAACJ,SAASE,UAAUF,SAASC,QAAQ,CAAC;AAAA,MAAA,CACxD,CAAC;AAAA,MAEJgC,SAAS;AAAA,QACPC,QAAQhD,kBAAAA;AAAAA,QACRiD,SAAU/F,CAAAA,UAAUuD,mBAAmBvD,KAAK;AAAA,MAAA;AAAA,IAC9C,CACD,CAAC;AAAA,EAAA;AAGR,EACD;AAGD,SAASgG,YAAYC,GAAe;AAClC,SAAOA,EAAEC,OAAQC,CAAAA,QAAUA,KAAKzF,OAAiE,KAA1D;AAAA,IAAE0F,SAAS;AAAA,IAA4BjB,MAAM,CAAC,MAAM;AAAA,EAAA,CAAW;AACxG;"}
|
|
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":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@madebywild/sanity-link-field",
|
|
3
|
-
"version": "0.
|
|
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.
|
|
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
|
-
//
|
|
20
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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(
|
|
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
|
|
18
|
-
|
|
19
|
-
|
|
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?:
|
|
60
|
+
extensions?: LinkExtensions;
|
|
44
61
|
};
|
|
45
62
|
|
|
46
63
|
// Add the custom field definition to Sanity's intrinsic definitions
|