@driftime/sanity-plugin-handbook 0.2.1 → 0.3.1
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/README.md +130 -77
- package/dist/index.d.ts +203 -110
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2790 -2064
- package/dist/index.js.map +1 -1
- package/package.json +36 -34
- package/dist/index.cjs +0 -2094
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -143
- package/src/blocks/callout/block.tsx +0 -56
- package/src/blocks/callout/content.tsx +0 -41
- package/src/blocks/callout/preview.tsx +0 -15
- package/src/blocks/code/block.tsx +0 -18
- package/src/blocks/code/content.tsx +0 -18
- package/src/blocks/code/preview.tsx +0 -17
- package/src/blocks/horizontal-rule/block.tsx +0 -5
- package/src/blocks/horizontal-rule/content.tsx +0 -8
- package/src/blocks/horizontal-rule/preview.tsx +0 -5
- package/src/blocks/image/block.tsx +0 -23
- package/src/blocks/image/content.tsx +0 -31
- package/src/blocks/image/preview.tsx +0 -23
- package/src/blocks/video/block.tsx +0 -21
- package/src/blocks/video/content.tsx +0 -37
- package/src/blocks/video/preview.tsx +0 -21
- package/src/components/content-panel.tsx +0 -58
- package/src/components/decorators.tsx +0 -53
- package/src/components/document-types.tsx +0 -49
- package/src/components/field.tsx +0 -530
- package/src/components/guide-content.tsx +0 -117
- package/src/components/guides.tsx +0 -36
- package/src/components/pane-header.tsx +0 -26
- package/src/components/sidebar-group.tsx +0 -30
- package/src/components/sidebar-tab.tsx +0 -78
- package/src/contexts/handbook-context.tsx +0 -84
- package/src/getting-started/document-types.tsx +0 -104
- package/src/getting-started/how-to-use.tsx +0 -95
- package/src/getting-started/overview.tsx +0 -65
- package/src/hooks/use-container-width.ts +0 -41
- package/src/hooks/use-handbook-configuration.ts +0 -74
- package/src/hooks/use-is-handbook-editor.ts +0 -18
- package/src/icons/book-open-text.tsx +0 -22
- package/src/icons/book-text.tsx +0 -19
- package/src/icons/code.tsx +0 -18
- package/src/icons/image.tsx +0 -19
- package/src/icons/info.tsx +0 -19
- package/src/icons/layout-grid.tsx +0 -20
- package/src/icons/lightbulb.tsx +0 -19
- package/src/icons/list-plus.tsx +0 -21
- package/src/icons/message-circle-question-mark.tsx +0 -19
- package/src/icons/minus.tsx +0 -17
- package/src/icons/triangle-alert.tsx +0 -19
- package/src/icons/video.tsx +0 -18
- package/src/index.ts +0 -145
- package/src/layouts/content.tsx +0 -13
- package/src/layouts/sidebar.tsx +0 -15
- package/src/layouts/tool.tsx +0 -205
- package/src/lib/handbook-structure.ts +0 -47
- package/src/lib/icons.ts +0 -16
- package/src/lib/utils.ts +0 -69
- package/src/schemas/annotations/link.ts +0 -19
- package/src/schemas/blocks/callout.ts +0 -69
- package/src/schemas/blocks/code.ts +0 -46
- package/src/schemas/blocks/horizontal-rule.ts +0 -31
- package/src/schemas/blocks/image.ts +0 -40
- package/src/schemas/blocks/video.ts +0 -34
- package/src/schemas/guide-content.ts +0 -58
- package/src/schemas/types/guide.ts +0 -48
- package/src/schemas/types/handbook.ts +0 -86
- package/src/types.ts +0 -101
package/src/lib/utils.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Determines whether a value is defined and non-empty. Treats `false`, empty strings,
|
|
3
|
-
* empty arrays, and empty objects as not defined.
|
|
4
|
-
*
|
|
5
|
-
* @param value - The value to check.
|
|
6
|
-
* @returns True if the value is defined and not empty.
|
|
7
|
-
*/
|
|
8
|
-
export function isDefined<T>(value: T | null | undefined | false): value is T {
|
|
9
|
-
if (value === undefined || value === null || value === false) return false;
|
|
10
|
-
if (typeof value === "string") return value.trim() !== "";
|
|
11
|
-
if (Array.isArray(value)) return value.some((element) => isDefined(element));
|
|
12
|
-
if (typeof value === "object") return Object.keys(value).length > 0;
|
|
13
|
-
|
|
14
|
-
return true;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Checks whether an email address is in the permitted editors list.
|
|
19
|
-
* Returns true if no editors list is provided or the list is empty (unrestricted access).
|
|
20
|
-
*
|
|
21
|
-
* @param editors - Optional list of permitted email addresses.
|
|
22
|
-
* @param email - The email address to check.
|
|
23
|
-
* @returns Whether the email is permitted to edit.
|
|
24
|
-
*/
|
|
25
|
-
export function isPermittedEditor(editors: string[] | undefined, email: string | undefined) {
|
|
26
|
-
if (!isDefined(editors)) return true;
|
|
27
|
-
if (!isDefined(email)) return false;
|
|
28
|
-
|
|
29
|
-
return editors.includes(email);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Converts a string between different casing formats.
|
|
34
|
-
*
|
|
35
|
-
* @param value - The string to convert.
|
|
36
|
-
* @param format - The target casing format.
|
|
37
|
-
* @returns The converted string.
|
|
38
|
-
*/
|
|
39
|
-
export function convertCase(value: string, format: "kebab" | "snake" | "camel" | "pascal" | "title" | "sentence") {
|
|
40
|
-
const words = value
|
|
41
|
-
.replaceAll(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
42
|
-
.replaceAll(/[-_\s]+/g, " ")
|
|
43
|
-
.trim()
|
|
44
|
-
.toLowerCase();
|
|
45
|
-
|
|
46
|
-
switch (format) {
|
|
47
|
-
case "kebab": {
|
|
48
|
-
return words.replaceAll(/\s+/g, "-");
|
|
49
|
-
}
|
|
50
|
-
case "snake": {
|
|
51
|
-
return words.replaceAll(/\s+/g, "_");
|
|
52
|
-
}
|
|
53
|
-
case "camel": {
|
|
54
|
-
return words.replaceAll(/\s+(.)/g, (_match, character: string) => character.toUpperCase());
|
|
55
|
-
}
|
|
56
|
-
case "pascal": {
|
|
57
|
-
return words.replaceAll(/(?:^|\s+)(.)/g, (_match, character: string) => character.toUpperCase());
|
|
58
|
-
}
|
|
59
|
-
case "title": {
|
|
60
|
-
return words.replaceAll(/\b\w/g, (character) => character.toUpperCase());
|
|
61
|
-
}
|
|
62
|
-
case "sentence": {
|
|
63
|
-
return words.replaceAll(/^(.)/g, (character) => character.toUpperCase());
|
|
64
|
-
}
|
|
65
|
-
default: {
|
|
66
|
-
return value;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { defineArrayMember, defineField } from "sanity";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Inline link annotation for Portable Text fields.
|
|
5
|
-
* Supports http, https, mailto, and tel URI schemes.
|
|
6
|
-
*/
|
|
7
|
-
export const linkAnnotation = defineArrayMember({
|
|
8
|
-
name: "link",
|
|
9
|
-
type: "object",
|
|
10
|
-
fields: [
|
|
11
|
-
defineField({
|
|
12
|
-
name: "href",
|
|
13
|
-
title: "URL",
|
|
14
|
-
type: "url",
|
|
15
|
-
description: "Web address for this link. Supports http, https, mailto, and tel schemes.",
|
|
16
|
-
validation: (rule) => rule.uri({ scheme: ["http", "https", "mailto", "tel"] }),
|
|
17
|
-
}),
|
|
18
|
-
],
|
|
19
|
-
});
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { defineArrayMember, defineField, defineType } from "sanity";
|
|
2
|
-
|
|
3
|
-
import { CalloutPreview } from "../../blocks/callout/preview";
|
|
4
|
-
import { InlineCodeDecorator, ParagraphDecorator } from "../../components/decorators";
|
|
5
|
-
import { MessageCircleQuestionMarkIcon } from "../../icons/message-circle-question-mark";
|
|
6
|
-
import { createSanityIcon } from "../../lib/icons";
|
|
7
|
-
import { linkAnnotation } from "../annotations/link";
|
|
8
|
-
|
|
9
|
-
export const handbookCalloutType = defineType({
|
|
10
|
-
name: "handbook.callout",
|
|
11
|
-
title: "Callout",
|
|
12
|
-
type: "object",
|
|
13
|
-
icon: createSanityIcon(MessageCircleQuestionMarkIcon),
|
|
14
|
-
description: "A highlighted message block used to draw attention to tips, supplementary information, or warnings.",
|
|
15
|
-
preview: {
|
|
16
|
-
select: { variant: "variant", body: "body" },
|
|
17
|
-
prepare({ variant, body }: { variant?: string; body?: { children?: { text?: string }[] }[] }) {
|
|
18
|
-
const text = Array.isArray(body)
|
|
19
|
-
? body.flatMap((block) => (block.children ?? []).map((child) => child.text ?? "")).join("")
|
|
20
|
-
: "";
|
|
21
|
-
|
|
22
|
-
return { variant, text };
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
components: {
|
|
26
|
-
preview: CalloutPreview,
|
|
27
|
-
},
|
|
28
|
-
fields: [
|
|
29
|
-
defineField({
|
|
30
|
-
name: "variant",
|
|
31
|
-
title: "Variant",
|
|
32
|
-
type: "string",
|
|
33
|
-
description: "Visual style and intent of the callout.",
|
|
34
|
-
initialValue: "tip",
|
|
35
|
-
validation: (rule) => rule.required(),
|
|
36
|
-
options: {
|
|
37
|
-
list: [
|
|
38
|
-
{ title: "Tip", value: "tip" },
|
|
39
|
-
{ title: "Information", value: "info" },
|
|
40
|
-
{ title: "Warning", value: "warning" },
|
|
41
|
-
],
|
|
42
|
-
layout: "radio",
|
|
43
|
-
direction: "horizontal",
|
|
44
|
-
},
|
|
45
|
-
}),
|
|
46
|
-
defineField({
|
|
47
|
-
name: "body",
|
|
48
|
-
title: "Body",
|
|
49
|
-
type: "array",
|
|
50
|
-
description: "Content displayed inside the callout. Supports inline formatting and links.",
|
|
51
|
-
of: [
|
|
52
|
-
defineArrayMember({
|
|
53
|
-
type: "block",
|
|
54
|
-
styles: [{ title: "Normal", value: "normal", component: ParagraphDecorator }],
|
|
55
|
-
marks: {
|
|
56
|
-
decorators: [
|
|
57
|
-
{ title: "Bold", value: "strong" },
|
|
58
|
-
{ title: "Italic", value: "em" },
|
|
59
|
-
{ title: "Strikethrough", value: "strike-through" },
|
|
60
|
-
{ title: "Code", value: "code", component: InlineCodeDecorator },
|
|
61
|
-
],
|
|
62
|
-
annotations: [linkAnnotation],
|
|
63
|
-
},
|
|
64
|
-
lists: [],
|
|
65
|
-
}),
|
|
66
|
-
],
|
|
67
|
-
}),
|
|
68
|
-
],
|
|
69
|
-
});
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { defineField, defineType } from "sanity";
|
|
2
|
-
|
|
3
|
-
import { CodePreview } from "../../blocks/code/preview";
|
|
4
|
-
import { CodeIcon } from "../../icons/code";
|
|
5
|
-
import { createSanityIcon } from "../../lib/icons";
|
|
6
|
-
|
|
7
|
-
export const handbookCodeType = defineType({
|
|
8
|
-
name: "handbook.code",
|
|
9
|
-
title: "Code",
|
|
10
|
-
type: "object",
|
|
11
|
-
icon: createSanityIcon(CodeIcon),
|
|
12
|
-
description: "A syntax-highlighted code block with language selection.",
|
|
13
|
-
preview: {
|
|
14
|
-
select: { code: "code", language: "language" },
|
|
15
|
-
},
|
|
16
|
-
components: {
|
|
17
|
-
preview: CodePreview,
|
|
18
|
-
},
|
|
19
|
-
fields: [
|
|
20
|
-
defineField({
|
|
21
|
-
name: "code",
|
|
22
|
-
title: "Code",
|
|
23
|
-
type: "text",
|
|
24
|
-
description: "The source code to display.",
|
|
25
|
-
}),
|
|
26
|
-
defineField({
|
|
27
|
-
name: "language",
|
|
28
|
-
title: "Language",
|
|
29
|
-
type: "string",
|
|
30
|
-
description: "Programming language used for syntax highlighting.",
|
|
31
|
-
options: {
|
|
32
|
-
list: [
|
|
33
|
-
{ title: "CSS", value: "css" },
|
|
34
|
-
{ title: "GROQ", value: "groq" },
|
|
35
|
-
{ title: "HTML", value: "html" },
|
|
36
|
-
{ title: "JavaScript", value: "javascript" },
|
|
37
|
-
{ title: "JSON", value: "json" },
|
|
38
|
-
{ title: "Plain Text", value: "text" },
|
|
39
|
-
{ title: "React", value: "tsx" },
|
|
40
|
-
{ title: "Shell", value: "shell" },
|
|
41
|
-
{ title: "TypeScript", value: "typescript" },
|
|
42
|
-
],
|
|
43
|
-
},
|
|
44
|
-
}),
|
|
45
|
-
],
|
|
46
|
-
});
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { defineField, defineType } from "sanity";
|
|
2
|
-
|
|
3
|
-
import { HorizontalRulePreview } from "../../blocks/horizontal-rule/preview";
|
|
4
|
-
import { MinusIcon } from "../../icons/minus";
|
|
5
|
-
import { createSanityIcon } from "../../lib/icons";
|
|
6
|
-
|
|
7
|
-
export const handbookHorizontalRuleType = defineType({
|
|
8
|
-
name: "handbook.horizontalRule",
|
|
9
|
-
title: "Horizontal Rule",
|
|
10
|
-
type: "object",
|
|
11
|
-
icon: createSanityIcon(MinusIcon),
|
|
12
|
-
description: "A visual divider between sections of content.",
|
|
13
|
-
preview: {
|
|
14
|
-
prepare() {
|
|
15
|
-
return { title: "Horizontal Rule" };
|
|
16
|
-
},
|
|
17
|
-
},
|
|
18
|
-
components: {
|
|
19
|
-
preview: HorizontalRulePreview,
|
|
20
|
-
},
|
|
21
|
-
fields: [
|
|
22
|
-
defineField({
|
|
23
|
-
name: "style",
|
|
24
|
-
title: "Style",
|
|
25
|
-
type: "string",
|
|
26
|
-
description: "Presentation style for the divider.",
|
|
27
|
-
hidden: true,
|
|
28
|
-
initialValue: "default",
|
|
29
|
-
}),
|
|
30
|
-
],
|
|
31
|
-
});
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { defineField, defineType } from "sanity";
|
|
2
|
-
|
|
3
|
-
import { ImagePreview } from "../../blocks/image/preview";
|
|
4
|
-
import { ImageIcon } from "../../icons/image";
|
|
5
|
-
import { createSanityIcon } from "../../lib/icons";
|
|
6
|
-
|
|
7
|
-
export const handbookImageType = defineType({
|
|
8
|
-
name: "handbook.image",
|
|
9
|
-
title: "Image",
|
|
10
|
-
type: "object",
|
|
11
|
-
icon: createSanityIcon(ImageIcon),
|
|
12
|
-
description: "An image with optional caption and alternative text for accessibility.",
|
|
13
|
-
preview: {
|
|
14
|
-
select: { caption: "caption", alt: "alt", url: "asset.asset.url" },
|
|
15
|
-
},
|
|
16
|
-
components: {
|
|
17
|
-
preview: ImagePreview,
|
|
18
|
-
},
|
|
19
|
-
fields: [
|
|
20
|
-
defineField({
|
|
21
|
-
name: "asset",
|
|
22
|
-
title: "Image",
|
|
23
|
-
type: "image",
|
|
24
|
-
description: "Upload or select an image from the media library.",
|
|
25
|
-
options: { hotspot: true },
|
|
26
|
-
}),
|
|
27
|
-
defineField({
|
|
28
|
-
name: "caption",
|
|
29
|
-
title: "Caption",
|
|
30
|
-
type: "string",
|
|
31
|
-
description: "Text displayed beneath the image in the handbook.",
|
|
32
|
-
}),
|
|
33
|
-
defineField({
|
|
34
|
-
name: "alt",
|
|
35
|
-
title: "Alternative Text",
|
|
36
|
-
type: "string",
|
|
37
|
-
description: "Describes the image for screen readers and accessibility tools.",
|
|
38
|
-
}),
|
|
39
|
-
],
|
|
40
|
-
});
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { defineField, defineType } from "sanity";
|
|
2
|
-
|
|
3
|
-
import { VideoPreview } from "../../blocks/video/preview";
|
|
4
|
-
import { VideoIcon } from "../../icons/video";
|
|
5
|
-
import { createSanityIcon } from "../../lib/icons";
|
|
6
|
-
|
|
7
|
-
export const handbookVideoType = defineType({
|
|
8
|
-
name: "handbook.video",
|
|
9
|
-
title: "Video",
|
|
10
|
-
type: "object",
|
|
11
|
-
icon: createSanityIcon(VideoIcon),
|
|
12
|
-
description: "An uploaded video with optional caption.",
|
|
13
|
-
preview: {
|
|
14
|
-
select: { caption: "caption", url: "asset.asset.url" },
|
|
15
|
-
},
|
|
16
|
-
components: {
|
|
17
|
-
preview: VideoPreview,
|
|
18
|
-
},
|
|
19
|
-
fields: [
|
|
20
|
-
defineField({
|
|
21
|
-
name: "asset",
|
|
22
|
-
title: "Video",
|
|
23
|
-
type: "file",
|
|
24
|
-
description: "Upload or select a video from the media library.",
|
|
25
|
-
options: { accept: "video/*" },
|
|
26
|
-
}),
|
|
27
|
-
defineField({
|
|
28
|
-
name: "caption",
|
|
29
|
-
title: "Caption",
|
|
30
|
-
type: "string",
|
|
31
|
-
description: "Text displayed beneath the video in the handbook.",
|
|
32
|
-
}),
|
|
33
|
-
],
|
|
34
|
-
});
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { defineArrayMember, defineField } from "sanity";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
BlockquoteDecorator,
|
|
5
|
-
BulletListDecorator,
|
|
6
|
-
HeadingDecorator,
|
|
7
|
-
InlineCodeDecorator,
|
|
8
|
-
NumberListDecorator,
|
|
9
|
-
ParagraphDecorator,
|
|
10
|
-
} from "../components/decorators";
|
|
11
|
-
import type { HandbookBlockDefinition } from "../types";
|
|
12
|
-
import { linkAnnotation } from "./annotations/link";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Creates the Portable Text content field for handbook guides, merging
|
|
16
|
-
* default blocks with any custom blocks from the plugin configuration.
|
|
17
|
-
*
|
|
18
|
-
* @param customBlocks - Custom block definitions registered by the consumer.
|
|
19
|
-
* @returns A field definition for the guide content array.
|
|
20
|
-
*/
|
|
21
|
-
export function createGuideContentField(customBlocks: HandbookBlockDefinition[] = []) {
|
|
22
|
-
return defineField({
|
|
23
|
-
name: "content",
|
|
24
|
-
title: "Content",
|
|
25
|
-
type: "array",
|
|
26
|
-
description: "Rich text content for this guide. Supports headings, lists, images, code blocks, and custom blocks.",
|
|
27
|
-
of: [
|
|
28
|
-
defineArrayMember({
|
|
29
|
-
name: "block",
|
|
30
|
-
type: "block",
|
|
31
|
-
styles: [
|
|
32
|
-
{ title: "Paragraph", value: "normal", component: ParagraphDecorator },
|
|
33
|
-
{ title: "Heading", value: "h2", component: HeadingDecorator },
|
|
34
|
-
{ title: "Blockquote", value: "blockquote", component: BlockquoteDecorator },
|
|
35
|
-
],
|
|
36
|
-
marks: {
|
|
37
|
-
decorators: [
|
|
38
|
-
{ title: "Bold", value: "strong" },
|
|
39
|
-
{ title: "Italic", value: "em" },
|
|
40
|
-
{ title: "Strikethrough", value: "strike-through" },
|
|
41
|
-
{ title: "Code", value: "code", component: InlineCodeDecorator },
|
|
42
|
-
],
|
|
43
|
-
annotations: [linkAnnotation],
|
|
44
|
-
},
|
|
45
|
-
lists: [
|
|
46
|
-
{ title: "Bullet", value: "bullet", component: BulletListDecorator },
|
|
47
|
-
{ title: "Number", value: "number", component: NumberListDecorator },
|
|
48
|
-
],
|
|
49
|
-
}),
|
|
50
|
-
defineArrayMember({ type: "handbook.image" }),
|
|
51
|
-
defineArrayMember({ type: "handbook.video" }),
|
|
52
|
-
defineArrayMember({ type: "handbook.code" }),
|
|
53
|
-
defineArrayMember({ type: "handbook.callout" }),
|
|
54
|
-
defineArrayMember({ type: "handbook.horizontalRule" }),
|
|
55
|
-
...customBlocks.map((block) => defineArrayMember({ type: block.schema.name })),
|
|
56
|
-
],
|
|
57
|
-
});
|
|
58
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { defineField, defineType } from "sanity";
|
|
2
|
-
|
|
3
|
-
import { BookOpenTextIcon } from "../../icons/book-open-text";
|
|
4
|
-
import { createSanityIcon } from "../../lib/icons";
|
|
5
|
-
import type { HandbookBlockDefinition, SanityHandbookGuide } from "../../types";
|
|
6
|
-
import { createGuideContentField } from "../guide-content";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Creates the handbook.guide document type with the content field
|
|
10
|
-
* composed from default and custom blocks.
|
|
11
|
-
*
|
|
12
|
-
* @param customBlocks - Custom block definitions registered by the consumer.
|
|
13
|
-
* @returns A document type definition for handbook guides.
|
|
14
|
-
*/
|
|
15
|
-
export function createHandbookGuideType(customBlocks: HandbookBlockDefinition[] = []) {
|
|
16
|
-
return defineType({
|
|
17
|
-
name: "handbook.guide" satisfies SanityHandbookGuide["_type"],
|
|
18
|
-
title: "Handbook Guide",
|
|
19
|
-
type: "document",
|
|
20
|
-
icon: createSanityIcon(BookOpenTextIcon),
|
|
21
|
-
description: "A handbook page authored with rich text, images, and custom blocks.",
|
|
22
|
-
preview: {
|
|
23
|
-
select: { title: "title", description: "description" },
|
|
24
|
-
prepare({ title, description }: { title?: string; description?: string }) {
|
|
25
|
-
return {
|
|
26
|
-
title: title ?? "Untitled Guide",
|
|
27
|
-
subtitle: description,
|
|
28
|
-
};
|
|
29
|
-
},
|
|
30
|
-
},
|
|
31
|
-
fields: [
|
|
32
|
-
defineField({
|
|
33
|
-
name: "title" satisfies keyof SanityHandbookGuide,
|
|
34
|
-
title: "Title",
|
|
35
|
-
type: "string",
|
|
36
|
-
description: "Display title shown in the handbook sidebar and as the page heading.",
|
|
37
|
-
validation: (rule) => rule.required(),
|
|
38
|
-
}),
|
|
39
|
-
defineField({
|
|
40
|
-
name: "description" satisfies keyof SanityHandbookGuide,
|
|
41
|
-
title: "Description",
|
|
42
|
-
type: "string",
|
|
43
|
-
description: "Brief summary displayed beneath the guide heading.",
|
|
44
|
-
}),
|
|
45
|
-
createGuideContentField(customBlocks),
|
|
46
|
-
],
|
|
47
|
-
});
|
|
48
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import { defineArrayMember, defineField, defineType } from "sanity";
|
|
2
|
-
|
|
3
|
-
import { BookTextIcon } from "../../icons/book-text";
|
|
4
|
-
import { ListPlusIcon } from "../../icons/list-plus";
|
|
5
|
-
import { createSanityIcon } from "../../lib/icons";
|
|
6
|
-
import { isDefined } from "../../lib/utils";
|
|
7
|
-
import type { SanityHandbook } from "../../types";
|
|
8
|
-
|
|
9
|
-
export const handbookType = defineType({
|
|
10
|
-
name: "handbook.handbook" satisfies SanityHandbook["_type"],
|
|
11
|
-
title: "Handbook",
|
|
12
|
-
type: "document",
|
|
13
|
-
icon: createSanityIcon(BookTextIcon),
|
|
14
|
-
description: "Defines the structure and ordering of the handbook, including groups and their guides.",
|
|
15
|
-
preview: {
|
|
16
|
-
prepare() {
|
|
17
|
-
return { title: "Handbook" };
|
|
18
|
-
},
|
|
19
|
-
},
|
|
20
|
-
fields: [
|
|
21
|
-
defineField({
|
|
22
|
-
name: "groups" satisfies keyof SanityHandbook,
|
|
23
|
-
title: "Groups",
|
|
24
|
-
type: "array",
|
|
25
|
-
description:
|
|
26
|
-
"Ordered list of groups displayed in the handbook sidebar. Each group contains a title and an ordered list of guide references.",
|
|
27
|
-
of: [
|
|
28
|
-
defineArrayMember({
|
|
29
|
-
name: "group",
|
|
30
|
-
type: "object",
|
|
31
|
-
icon: createSanityIcon(ListPlusIcon),
|
|
32
|
-
fields: [
|
|
33
|
-
defineField({
|
|
34
|
-
name: "title",
|
|
35
|
-
title: "Title",
|
|
36
|
-
type: "string",
|
|
37
|
-
description: "Heading displayed above this group of guides in the sidebar.",
|
|
38
|
-
validation: (rule) => rule.required(),
|
|
39
|
-
}),
|
|
40
|
-
defineField({
|
|
41
|
-
name: "guides",
|
|
42
|
-
title: "Guides",
|
|
43
|
-
type: "array",
|
|
44
|
-
description: "Ordered list of guides within this group. Drag to reorder.",
|
|
45
|
-
of: [
|
|
46
|
-
defineArrayMember({
|
|
47
|
-
type: "reference",
|
|
48
|
-
to: [{ type: "handbook.guide" }],
|
|
49
|
-
}),
|
|
50
|
-
],
|
|
51
|
-
}),
|
|
52
|
-
],
|
|
53
|
-
preview: {
|
|
54
|
-
select: { title: "title", guide0: "guides.0.title", guide1: "guides.1.title", guide2: "guides.2.title" },
|
|
55
|
-
prepare({
|
|
56
|
-
title,
|
|
57
|
-
guide0,
|
|
58
|
-
guide1,
|
|
59
|
-
guide2,
|
|
60
|
-
}: {
|
|
61
|
-
title?: string;
|
|
62
|
-
guide0?: string;
|
|
63
|
-
guide1?: string;
|
|
64
|
-
guide2?: string;
|
|
65
|
-
}) {
|
|
66
|
-
const all = [guide0, guide1, guide2].filter((guide): guide is string => isDefined(guide));
|
|
67
|
-
const shown = all.slice(0, 2);
|
|
68
|
-
const hasMore = isDefined(guide2);
|
|
69
|
-
|
|
70
|
-
function formatSubtitle() {
|
|
71
|
-
if (!isDefined(shown)) return "No guides selected";
|
|
72
|
-
if (hasMore) return `${shown.join(", ")}, and others`;
|
|
73
|
-
return shown.join(" and ");
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return {
|
|
77
|
-
title: title ?? "Untitled Group",
|
|
78
|
-
subtitle: formatSubtitle(),
|
|
79
|
-
};
|
|
80
|
-
},
|
|
81
|
-
},
|
|
82
|
-
}),
|
|
83
|
-
],
|
|
84
|
-
}),
|
|
85
|
-
],
|
|
86
|
-
});
|
package/src/types.ts
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import type { ComponentType } from "react";
|
|
2
|
-
import type {
|
|
3
|
-
DocumentDefinition,
|
|
4
|
-
PortableTextBlock,
|
|
5
|
-
PortableTextObject,
|
|
6
|
-
SanityDocument,
|
|
7
|
-
SchemaTypeDefinition,
|
|
8
|
-
} from "sanity";
|
|
9
|
-
|
|
10
|
-
declare module "@sanity/types" {
|
|
11
|
-
interface FieldDefinitionBase {
|
|
12
|
-
handbook?: HandbookMetadata;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// oxlint-disable-next-line no-shadow -- Module augmentation intentionally redeclares the imported type.
|
|
16
|
-
interface DocumentDefinition {
|
|
17
|
-
handbook?: HandbookMetadata;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** @public */
|
|
22
|
-
export interface HandbookMetadata {
|
|
23
|
-
/** Display title override for the field or document. */
|
|
24
|
-
title?: string;
|
|
25
|
-
/** Description shown beneath the field or document heading. */
|
|
26
|
-
description?: string;
|
|
27
|
-
/** Illustrative example value for the field. */
|
|
28
|
-
example?: string;
|
|
29
|
-
/** Helpful tip displayed as a popover hint. */
|
|
30
|
-
tip?: string;
|
|
31
|
-
/** Informational note displayed as a popover hint. */
|
|
32
|
-
info?: string;
|
|
33
|
-
/** Warning displayed as a popover hint. */
|
|
34
|
-
caution?: string;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** @public */
|
|
38
|
-
export interface HandbookStructureGroup {
|
|
39
|
-
/** Display title for the document type group. */
|
|
40
|
-
title: string;
|
|
41
|
-
/** Brief description of the group's purpose. */
|
|
42
|
-
description?: string;
|
|
43
|
-
/** Document definitions belonging to this group. */
|
|
44
|
-
documents: DocumentDefinition[];
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/** @public */
|
|
48
|
-
export interface HandbookBlockDefinition {
|
|
49
|
-
/** Sanity schema type definition for the custom block. */
|
|
50
|
-
schema: SchemaTypeDefinition;
|
|
51
|
-
/** React component for rendering the block in the Handbook viewer. */
|
|
52
|
-
component: ComponentType<{ value: PortableTextObject }>;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/** @public */
|
|
56
|
-
export interface HandbookConfig {
|
|
57
|
-
/** Title shown in the Studio tool navigation. */
|
|
58
|
-
title?: string;
|
|
59
|
-
/** Heading displayed at the top of the sidebar. */
|
|
60
|
-
sidebarTitle?: string;
|
|
61
|
-
/** Document type groups defining the hierarchy. */
|
|
62
|
-
groups?: HandbookStructureGroup[];
|
|
63
|
-
/** Custom Portable Text block definitions for guide content. */
|
|
64
|
-
blocks?: HandbookBlockDefinition[];
|
|
65
|
-
/** Email addresses permitted to edit handbook documents. */
|
|
66
|
-
editors?: string[];
|
|
67
|
-
/** Fallback message shown when a field has no description. */
|
|
68
|
-
undocumentedFieldMessage?: string;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/** @public */
|
|
72
|
-
export interface HandbookGuideGroup {
|
|
73
|
-
/** Unique key assigned by Sanity for this array item. */
|
|
74
|
-
_key: string;
|
|
75
|
-
/** Display title for this group in the sidebar. */
|
|
76
|
-
title: string;
|
|
77
|
-
/** Ordered array of guide references. */
|
|
78
|
-
guides: SanityHandbookGuide[];
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/** @public */
|
|
82
|
-
export interface SanityHandbook extends SanityDocument {
|
|
83
|
-
/** Document type identifier. */
|
|
84
|
-
_type: "handbook.handbook";
|
|
85
|
-
/** Ordered array of groups, each containing a title and guide references. */
|
|
86
|
-
groups: HandbookGuideGroup[];
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** @public */
|
|
90
|
-
export interface SanityHandbookGuide extends SanityDocument {
|
|
91
|
-
/** Document type identifier. */
|
|
92
|
-
_type: "handbook.guide";
|
|
93
|
-
/** Unique key assigned by Sanity for this array item. */
|
|
94
|
-
_key: string;
|
|
95
|
-
/** Display title for this guide. */
|
|
96
|
-
title: string;
|
|
97
|
-
/** Brief description shown beneath the guide heading. */
|
|
98
|
-
description?: string;
|
|
99
|
-
/** Portable Text content of the guide. */
|
|
100
|
-
content: PortableTextBlock[];
|
|
101
|
-
}
|