@driftime/sanity-plugin-handbook 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +299 -0
- package/dist/index.cjs +1866 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +143 -0
- package/dist/index.d.ts +143 -0
- package/dist/index.js +1870 -0
- package/dist/index.js.map +1 -0
- package/package.json +92 -0
- package/src/blocks/callout/block.tsx +56 -0
- package/src/blocks/callout/content.tsx +39 -0
- package/src/blocks/callout/preview.tsx +15 -0
- package/src/blocks/code/block.tsx +18 -0
- package/src/blocks/code/content.tsx +18 -0
- package/src/blocks/code/preview.tsx +17 -0
- package/src/blocks/horizontal-rule/block.tsx +5 -0
- package/src/blocks/horizontal-rule/content.tsx +8 -0
- package/src/blocks/horizontal-rule/preview.tsx +5 -0
- package/src/blocks/image/block.tsx +23 -0
- package/src/blocks/image/content.tsx +31 -0
- package/src/blocks/image/preview.tsx +23 -0
- package/src/blocks/video/block.tsx +21 -0
- package/src/blocks/video/content.tsx +37 -0
- package/src/blocks/video/preview.tsx +21 -0
- package/src/components/content-panel.tsx +58 -0
- package/src/components/decorators.tsx +53 -0
- package/src/components/document-types.tsx +49 -0
- package/src/components/field.tsx +528 -0
- package/src/components/guide-content.tsx +117 -0
- package/src/components/guides.tsx +37 -0
- package/src/components/pane-header.tsx +26 -0
- package/src/components/sidebar-group.tsx +30 -0
- package/src/components/sidebar-tab.tsx +78 -0
- package/src/contexts/handbook-context.tsx +84 -0
- package/src/getting-started/document-types.tsx +104 -0
- package/src/getting-started/how-to-use.tsx +93 -0
- package/src/getting-started/overview.tsx +64 -0
- package/src/hooks/use-container-width.ts +41 -0
- package/src/hooks/use-handbook-configuration.ts +74 -0
- package/src/hooks/use-is-handbook-editor.ts +18 -0
- package/src/index.ts +145 -0
- package/src/layouts/content.tsx +13 -0
- package/src/layouts/sidebar.tsx +15 -0
- package/src/layouts/tool.tsx +205 -0
- package/src/lib/handbook-structure.ts +46 -0
- package/src/lib/icons.ts +18 -0
- package/src/lib/utils.ts +69 -0
- package/src/schemas/annotations/link.ts +19 -0
- package/src/schemas/blocks/callout.ts +69 -0
- package/src/schemas/blocks/code.ts +46 -0
- package/src/schemas/blocks/horizontal-rule.ts +31 -0
- package/src/schemas/blocks/image.ts +40 -0
- package/src/schemas/blocks/video.ts +34 -0
- package/src/schemas/guide-content.ts +58 -0
- package/src/schemas/types/guide.ts +48 -0
- package/src/schemas/types/handbook.ts +85 -0
- package/src/types.ts +101 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1870 @@
|
|
|
1
|
+
import { LightbulbIcon, InfoIcon, TriangleAlertIcon, BookOpenTextIcon, BookTextIcon, LayoutGridIcon, MessageCircleQuestionMarkIcon, CodeIcon, MinusIcon, ImageIcon, VideoIcon, ListPlusIcon } from "lucide-react";
|
|
2
|
+
import { useClient, useSchema, defineArrayMember, defineField, defineType, useCurrentUser, definePlugin } from "sanity";
|
|
3
|
+
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
4
|
+
import { forwardRef, useState, useEffect, createContext, useContext, useRef, createElement, useCallback, useMemo } from "react";
|
|
5
|
+
import { Card, Flex, Text, Box, Stack, Heading, useClickOutsideEvent, Popover, Code, Button } from "@sanity/ui";
|
|
6
|
+
const ArrowLeftIcon = /* @__PURE__ */ forwardRef(function(props, ref) {
|
|
7
|
+
return /* @__PURE__ */ jsxs(
|
|
8
|
+
"svg",
|
|
9
|
+
{
|
|
10
|
+
"data-sanity-icon": "arrow-left",
|
|
11
|
+
width: "1em",
|
|
12
|
+
height: "1em",
|
|
13
|
+
viewBox: "0 0 25 25",
|
|
14
|
+
fill: "none",
|
|
15
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
16
|
+
...props,
|
|
17
|
+
ref,
|
|
18
|
+
children: [
|
|
19
|
+
/* @__PURE__ */ jsx("path", { d: "M5.5 12.5H20", stroke: "currentColor", strokeWidth: 1.2, strokeLinejoin: "round" }),
|
|
20
|
+
/* @__PURE__ */ jsx(
|
|
21
|
+
"path",
|
|
22
|
+
{
|
|
23
|
+
d: "M11 18L5.5 12.5L11 7",
|
|
24
|
+
stroke: "currentColor",
|
|
25
|
+
strokeWidth: 1.2,
|
|
26
|
+
strokeLinejoin: "round"
|
|
27
|
+
}
|
|
28
|
+
)
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
);
|
|
32
|
+
}), ChevronDownIcon = /* @__PURE__ */ forwardRef(function(props, ref) {
|
|
33
|
+
return /* @__PURE__ */ jsx(
|
|
34
|
+
"svg",
|
|
35
|
+
{
|
|
36
|
+
"data-sanity-icon": "chevron-down",
|
|
37
|
+
width: "1em",
|
|
38
|
+
height: "1em",
|
|
39
|
+
viewBox: "0 0 25 25",
|
|
40
|
+
fill: "none",
|
|
41
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
42
|
+
...props,
|
|
43
|
+
ref,
|
|
44
|
+
children: /* @__PURE__ */ jsx(
|
|
45
|
+
"path",
|
|
46
|
+
{
|
|
47
|
+
d: "M17 10L12.5 14.5L8 10",
|
|
48
|
+
stroke: "currentColor",
|
|
49
|
+
strokeWidth: 1.2,
|
|
50
|
+
strokeLinejoin: "round"
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
}), ChevronRightIcon = /* @__PURE__ */ forwardRef(function(props, ref) {
|
|
56
|
+
return /* @__PURE__ */ jsx(
|
|
57
|
+
"svg",
|
|
58
|
+
{
|
|
59
|
+
"data-sanity-icon": "chevron-right",
|
|
60
|
+
width: "1em",
|
|
61
|
+
height: "1em",
|
|
62
|
+
viewBox: "0 0 25 25",
|
|
63
|
+
fill: "none",
|
|
64
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
65
|
+
...props,
|
|
66
|
+
ref,
|
|
67
|
+
children: /* @__PURE__ */ jsx(
|
|
68
|
+
"path",
|
|
69
|
+
{
|
|
70
|
+
d: "M10 8L14.5 12.5L10 17",
|
|
71
|
+
stroke: "currentColor",
|
|
72
|
+
strokeWidth: 1.2,
|
|
73
|
+
strokeLinejoin: "round"
|
|
74
|
+
}
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
});
|
|
79
|
+
function PaneHeader({ title }) {
|
|
80
|
+
return /* @__PURE__ */ jsx("div", { style: { position: "sticky", top: 0, zIndex: 1, lineHeight: 0 }, children: /* @__PURE__ */ jsx(Card, { tone: "inherit", children: /* @__PURE__ */ jsx(Flex, { direction: "column", padding: 3, style: { boxSizing: "border-box" }, children: /* @__PURE__ */ jsx(Flex, { align: "flex-start", children: /* @__PURE__ */ jsx(Card, { flex: 1, paddingLeft: 2, padding: 2, children: /* @__PURE__ */ jsx(Flex, { align: "center", gap: 1, children: /* @__PURE__ */ jsx(Text, { size: 1, textOverflow: "ellipsis", weight: "semibold", style: { cursor: "default", outline: "none" }, children: title }) }) }) }) }) }) });
|
|
81
|
+
}
|
|
82
|
+
const query = `*[_type == "handbook.handbook"][0]{
|
|
83
|
+
groups[]{
|
|
84
|
+
_key,
|
|
85
|
+
title,
|
|
86
|
+
guides[]{
|
|
87
|
+
_key,
|
|
88
|
+
...@->{
|
|
89
|
+
_id,
|
|
90
|
+
_type,
|
|
91
|
+
title,
|
|
92
|
+
description,
|
|
93
|
+
content[]{
|
|
94
|
+
...,
|
|
95
|
+
_type == "handbook.image" => {
|
|
96
|
+
...,
|
|
97
|
+
"asset": { "url": asset.asset->url }
|
|
98
|
+
},
|
|
99
|
+
_type == "handbook.video" => {
|
|
100
|
+
...,
|
|
101
|
+
"asset": { "url": asset.asset->url }
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}`, listenQuery = '*[_type == "handbook.handbook" || _type == "handbook.guide"]';
|
|
108
|
+
function useHandbookConfiguration() {
|
|
109
|
+
const client = useClient({ apiVersion: "2026-01-01" }), [configuration, setConfiguration] = useState(null), [loading, setLoading] = useState(!0);
|
|
110
|
+
return useEffect(() => {
|
|
111
|
+
async function fetchConfiguration() {
|
|
112
|
+
const result = await client.fetch(query);
|
|
113
|
+
setConfiguration(result), setLoading(!1);
|
|
114
|
+
}
|
|
115
|
+
fetchConfiguration();
|
|
116
|
+
const subscription = client.listen(listenQuery).subscribe(() => {
|
|
117
|
+
fetchConfiguration();
|
|
118
|
+
});
|
|
119
|
+
return () => {
|
|
120
|
+
subscription.unsubscribe();
|
|
121
|
+
};
|
|
122
|
+
}, [client]), { configuration, loading };
|
|
123
|
+
}
|
|
124
|
+
function isDefined(value) {
|
|
125
|
+
return value == null || value === !1 ? !1 : typeof value == "string" ? value.trim() !== "" : Array.isArray(value) ? value.some((element) => isDefined(element)) : typeof value == "object" ? Object.keys(value).length > 0 : !0;
|
|
126
|
+
}
|
|
127
|
+
function isPermittedEditor(editors, email) {
|
|
128
|
+
return isDefined(editors) ? isDefined(email) ? editors.includes(email) : !1 : !0;
|
|
129
|
+
}
|
|
130
|
+
function convertCase(value, format) {
|
|
131
|
+
const words = value.replaceAll(/([a-z0-9])([A-Z])/g, "$1 $2").replaceAll(/[-_\s]+/g, " ").trim().toLowerCase();
|
|
132
|
+
switch (format) {
|
|
133
|
+
case "kebab":
|
|
134
|
+
return words.replaceAll(/\s+/g, "-");
|
|
135
|
+
case "snake":
|
|
136
|
+
return words.replaceAll(/\s+/g, "_");
|
|
137
|
+
case "camel":
|
|
138
|
+
return words.replaceAll(/\s+(.)/g, (_match, character) => character.toUpperCase());
|
|
139
|
+
case "pascal":
|
|
140
|
+
return words.replaceAll(/(?:^|\s+)(.)/g, (_match, character) => character.toUpperCase());
|
|
141
|
+
case "title":
|
|
142
|
+
return words.replaceAll(/\b\w/g, (character) => character.toUpperCase());
|
|
143
|
+
case "sentence":
|
|
144
|
+
return words.replaceAll(/^(.)/g, (character) => character.toUpperCase());
|
|
145
|
+
default:
|
|
146
|
+
return value;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const HandbookContext = createContext(void 0);
|
|
150
|
+
function HandbookProvider({
|
|
151
|
+
sidebarTitle,
|
|
152
|
+
groups,
|
|
153
|
+
blocks,
|
|
154
|
+
undocumentedFieldMessage,
|
|
155
|
+
children
|
|
156
|
+
}) {
|
|
157
|
+
const [activeTab, setActiveTab] = useState(null), [sidebarExpanded, setSidebarExpanded] = useState(!0), { configuration, loading } = useHandbookConfiguration();
|
|
158
|
+
return /* @__PURE__ */ jsx(
|
|
159
|
+
HandbookContext,
|
|
160
|
+
{
|
|
161
|
+
value: {
|
|
162
|
+
sidebarTitle,
|
|
163
|
+
activeTab,
|
|
164
|
+
sidebarExpanded,
|
|
165
|
+
loading,
|
|
166
|
+
groups,
|
|
167
|
+
configuration,
|
|
168
|
+
blocks,
|
|
169
|
+
undocumentedFieldMessage,
|
|
170
|
+
setActiveTab,
|
|
171
|
+
setSidebarExpanded
|
|
172
|
+
},
|
|
173
|
+
children
|
|
174
|
+
}
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
function useHandbookContext() {
|
|
178
|
+
const handbookContext = useContext(HandbookContext);
|
|
179
|
+
if (!isDefined(handbookContext)) throw new Error("useHandbookContext can only be used within <HandbookProvider>");
|
|
180
|
+
return handbookContext;
|
|
181
|
+
}
|
|
182
|
+
function useContainerWidth() {
|
|
183
|
+
const reference = useRef(null), [width, setWidth] = useState(void 0);
|
|
184
|
+
return useEffect(() => {
|
|
185
|
+
const element = reference.current;
|
|
186
|
+
if (!isDefined(element)) return;
|
|
187
|
+
const observer = new ResizeObserver((entries2) => {
|
|
188
|
+
for (const entry of entries2)
|
|
189
|
+
setWidth(entry.contentRect.width);
|
|
190
|
+
});
|
|
191
|
+
return observer.observe(element), () => {
|
|
192
|
+
observer.disconnect();
|
|
193
|
+
};
|
|
194
|
+
}, []), { reference, width };
|
|
195
|
+
}
|
|
196
|
+
function ContentPanel({ id, title, description, children }) {
|
|
197
|
+
const { activeTab } = useHandbookContext();
|
|
198
|
+
return activeTab !== id ? null : /* @__PURE__ */ jsxs(
|
|
199
|
+
"div",
|
|
200
|
+
{
|
|
201
|
+
id: `panel-${id}`,
|
|
202
|
+
"aria-labelledby": id,
|
|
203
|
+
role: "tabpanel",
|
|
204
|
+
style: { display: "flex", flexDirection: "column", height: "100%" },
|
|
205
|
+
children: [
|
|
206
|
+
/* @__PURE__ */ jsx(PaneHeader, { title }),
|
|
207
|
+
/* @__PURE__ */ jsx("div", { style: { flex: 1, overflowY: "auto" }, children: /* @__PURE__ */ jsxs(
|
|
208
|
+
Box,
|
|
209
|
+
{
|
|
210
|
+
style: {
|
|
211
|
+
maxWidth: "640px",
|
|
212
|
+
boxSizing: "border-box",
|
|
213
|
+
marginInline: "auto",
|
|
214
|
+
paddingInline: "20px",
|
|
215
|
+
paddingBlockStart: "32px",
|
|
216
|
+
paddingBlockEnd: "220px"
|
|
217
|
+
},
|
|
218
|
+
children: [
|
|
219
|
+
/* @__PURE__ */ jsxs(Stack, { marginBottom: 6, space: 5, children: [
|
|
220
|
+
/* @__PURE__ */ jsx(Heading, { as: "h2", size: 4, children: title }),
|
|
221
|
+
isDefined(description) && /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: description })
|
|
222
|
+
] }),
|
|
223
|
+
children
|
|
224
|
+
]
|
|
225
|
+
}
|
|
226
|
+
) })
|
|
227
|
+
]
|
|
228
|
+
}
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
const defaultIconProps = { width: "1em", height: "1em", strokeWidth: 1.5 };
|
|
232
|
+
function createSanityIcon(icon, props) {
|
|
233
|
+
return () => createElement(icon, { ...defaultIconProps, ...props });
|
|
234
|
+
}
|
|
235
|
+
const opaqueTypes = /* @__PURE__ */ new Set([
|
|
236
|
+
"block",
|
|
237
|
+
"boolean",
|
|
238
|
+
"crossDatasetReference",
|
|
239
|
+
"date",
|
|
240
|
+
"datetime",
|
|
241
|
+
"file",
|
|
242
|
+
"geopoint",
|
|
243
|
+
"image",
|
|
244
|
+
"number",
|
|
245
|
+
"reference",
|
|
246
|
+
"slug",
|
|
247
|
+
"string",
|
|
248
|
+
"text",
|
|
249
|
+
"url"
|
|
250
|
+
]), inheritedFieldNames = {
|
|
251
|
+
block: /* @__PURE__ */ new Set(["children", "level", "listItem", "markDefs", "style"]),
|
|
252
|
+
file: /* @__PURE__ */ new Set(["asset", "media"]),
|
|
253
|
+
geopoint: /* @__PURE__ */ new Set(["alt", "lat", "lng"]),
|
|
254
|
+
image: /* @__PURE__ */ new Set(["asset", "crop", "hotspot", "media"]),
|
|
255
|
+
slug: /* @__PURE__ */ new Set(["current", "source"])
|
|
256
|
+
};
|
|
257
|
+
function isCompiledField(value) {
|
|
258
|
+
return typeof value != "object" || value === null || !("name" in value) || !("type" in value) ? !1 : typeof value.type == "object" && value.type !== null;
|
|
259
|
+
}
|
|
260
|
+
function hasFieldsArray(value) {
|
|
261
|
+
return typeof value != "object" || value === null || !("fields" in value) ? !1 : Array.isArray(value.fields);
|
|
262
|
+
}
|
|
263
|
+
function hasOfArray(value) {
|
|
264
|
+
return typeof value != "object" || value === null || !("of" in value) ? !1 : Array.isArray(value.of);
|
|
265
|
+
}
|
|
266
|
+
function getTypeName(value) {
|
|
267
|
+
if (!(typeof value != "object" || value === null || !("type" in value)))
|
|
268
|
+
return typeof value.type == "string" ? value.type : void 0;
|
|
269
|
+
}
|
|
270
|
+
function isOpaqueType(typeName) {
|
|
271
|
+
return opaqueTypes.has(typeName);
|
|
272
|
+
}
|
|
273
|
+
function getParentTypeName(resolvedType) {
|
|
274
|
+
if (!(typeof resolvedType != "object" || resolvedType === null || !("type" in resolvedType)) && !(typeof resolvedType.type != "object" || resolvedType.type === null || !("name" in resolvedType.type)))
|
|
275
|
+
return String(resolvedType.type.name);
|
|
276
|
+
}
|
|
277
|
+
function normaliseField(field, visited = /* @__PURE__ */ new Set()) {
|
|
278
|
+
if (isCompiledField(field)) {
|
|
279
|
+
const typeName = field.type.name, result = {
|
|
280
|
+
name: field.name,
|
|
281
|
+
type: typeName,
|
|
282
|
+
title: field.title ?? field.type.title,
|
|
283
|
+
description: field.description ?? field.type.description,
|
|
284
|
+
handbook: field.handbook
|
|
285
|
+
};
|
|
286
|
+
if (!visited.has(typeName)) {
|
|
287
|
+
const next = !opaqueTypes.has(typeName) && typeName !== "object" && typeName !== "array" ? /* @__PURE__ */ new Set([...visited, typeName]) : visited, fieldSource = hasFieldsArray(field) ? field : field.type, ofSource = hasOfArray(field) ? field : field.type;
|
|
288
|
+
hasFieldsArray(fieldSource) && (result.fields = fieldSource.fields.map((subfield) => normaliseField(subfield, next))), hasOfArray(ofSource) && (result.of = ofSource.of.map((member) => normaliseField(member, next)));
|
|
289
|
+
}
|
|
290
|
+
return result;
|
|
291
|
+
}
|
|
292
|
+
return field;
|
|
293
|
+
}
|
|
294
|
+
function resolveCustomFields(resolvedType, parentName) {
|
|
295
|
+
if (!hasFieldsArray(resolvedType)) return;
|
|
296
|
+
const inherited = inheritedFieldNames[parentName], customFields = resolvedType.fields.map((field) => normaliseField(field)).filter((field) => inherited === void 0 || !inherited.has(field.name));
|
|
297
|
+
return isDefined(customFields) ? customFields : void 0;
|
|
298
|
+
}
|
|
299
|
+
function resolveNormalisedFields(items) {
|
|
300
|
+
const normalised = items.map((item) => normaliseField(item));
|
|
301
|
+
return isDefined(normalised) ? normalised : void 0;
|
|
302
|
+
}
|
|
303
|
+
function resolveNormalisedMembers(members) {
|
|
304
|
+
const normalised = members.map((member) => normaliseField(member)).filter((member) => !isOpaqueType(member.type));
|
|
305
|
+
return isDefined(normalised) ? normalised : void 0;
|
|
306
|
+
}
|
|
307
|
+
function resolveParentName(value, schema) {
|
|
308
|
+
const direct = getParentTypeName(value);
|
|
309
|
+
if (isDefined(direct)) return direct;
|
|
310
|
+
const typeName = getTypeName(value);
|
|
311
|
+
if (!isDefined(typeName)) return;
|
|
312
|
+
const resolvedType = schema.get(typeName);
|
|
313
|
+
return isDefined(resolvedType) ? getParentTypeName(resolvedType) : void 0;
|
|
314
|
+
}
|
|
315
|
+
function resolveFields(value, schema) {
|
|
316
|
+
if (hasFieldsArray(value)) {
|
|
317
|
+
const parentName = resolveParentName(value, schema);
|
|
318
|
+
return isDefined(parentName) && isOpaqueType(parentName) ? resolveCustomFields(value, parentName) : resolveNormalisedFields(value.fields);
|
|
319
|
+
}
|
|
320
|
+
if (hasOfArray(value)) {
|
|
321
|
+
const members = value.of;
|
|
322
|
+
if (members.length === 1) {
|
|
323
|
+
const [member] = members, normalised = normaliseField(member);
|
|
324
|
+
if (isOpaqueType(normalised.type)) return;
|
|
325
|
+
if (hasFieldsArray(normalised)) {
|
|
326
|
+
const parentName = resolveParentName(normalised, schema);
|
|
327
|
+
return isDefined(parentName) && isOpaqueType(parentName) ? resolveCustomFields(normalised, parentName) : resolveNormalisedFields(normalised.fields);
|
|
328
|
+
}
|
|
329
|
+
const memberType = schema.get(normalised.type);
|
|
330
|
+
if (isDefined(memberType)) return resolveFields(memberType, schema);
|
|
331
|
+
}
|
|
332
|
+
return resolveNormalisedMembers(members);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
function getSubfields(field, schema) {
|
|
336
|
+
if (isOpaqueType(field.type)) return;
|
|
337
|
+
const directFields = resolveFields(field, schema);
|
|
338
|
+
if (directFields !== void 0) return directFields;
|
|
339
|
+
const resolvedType = schema.get(field.type);
|
|
340
|
+
if (!isDefined(resolvedType)) return;
|
|
341
|
+
const parentName = getParentTypeName(resolvedType);
|
|
342
|
+
return isDefined(parentName) && isOpaqueType(parentName) ? resolveCustomFields(resolvedType, parentName) : resolveFields(resolvedType, schema);
|
|
343
|
+
}
|
|
344
|
+
function FieldHint({ label, icon, color, open, children, onToggle, onClose }) {
|
|
345
|
+
const buttonReference = useRef(null), popoverReference = useRef(null);
|
|
346
|
+
return useClickOutsideEvent(open ? onClose : void 0, () => [buttonReference.current, popoverReference.current]), /* @__PURE__ */ jsx(
|
|
347
|
+
Popover,
|
|
348
|
+
{
|
|
349
|
+
open,
|
|
350
|
+
placement: "top",
|
|
351
|
+
portal: !0,
|
|
352
|
+
radius: 2,
|
|
353
|
+
shadow: 2,
|
|
354
|
+
content: /* @__PURE__ */ jsx("div", { ref: popoverReference, children: /* @__PURE__ */ jsx(Box, { padding: 3, style: { maxWidth: "280px" }, children: /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
355
|
+
/* @__PURE__ */ jsx(Text, { size: 1, weight: "semibold", children: label }),
|
|
356
|
+
/* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children })
|
|
357
|
+
] }) }) }),
|
|
358
|
+
children: /* @__PURE__ */ jsx(
|
|
359
|
+
"button",
|
|
360
|
+
{
|
|
361
|
+
ref: buttonReference,
|
|
362
|
+
"aria-label": label,
|
|
363
|
+
type: "button",
|
|
364
|
+
onClick: onToggle,
|
|
365
|
+
onMouseEnter: onToggle,
|
|
366
|
+
onMouseLeave: onClose,
|
|
367
|
+
style: {
|
|
368
|
+
display: "flex",
|
|
369
|
+
alignItems: "center",
|
|
370
|
+
padding: "2px",
|
|
371
|
+
fontSize: "16px",
|
|
372
|
+
color,
|
|
373
|
+
background: "none",
|
|
374
|
+
border: "none",
|
|
375
|
+
cursor: "pointer"
|
|
376
|
+
},
|
|
377
|
+
children: icon
|
|
378
|
+
}
|
|
379
|
+
)
|
|
380
|
+
}
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
function FieldHints({ tip, info, caution }) {
|
|
384
|
+
const [openHint, setOpenHint] = useState(null), closeHint = useCallback(() => {
|
|
385
|
+
setOpenHint(null);
|
|
386
|
+
}, []);
|
|
387
|
+
return /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
|
|
388
|
+
isDefined(tip) && /* @__PURE__ */ jsx(
|
|
389
|
+
FieldHint,
|
|
390
|
+
{
|
|
391
|
+
label: "Tip",
|
|
392
|
+
icon: /* @__PURE__ */ jsx(LightbulbIcon, { ...defaultIconProps }),
|
|
393
|
+
color: "var(--card-icon-color)",
|
|
394
|
+
open: openHint === "tip",
|
|
395
|
+
onToggle: () => {
|
|
396
|
+
setOpenHint(openHint === "tip" ? null : "tip");
|
|
397
|
+
},
|
|
398
|
+
onClose: closeHint,
|
|
399
|
+
children: tip
|
|
400
|
+
}
|
|
401
|
+
),
|
|
402
|
+
isDefined(info) && /* @__PURE__ */ jsx(
|
|
403
|
+
FieldHint,
|
|
404
|
+
{
|
|
405
|
+
label: "Information",
|
|
406
|
+
icon: /* @__PURE__ */ jsx(InfoIcon, { ...defaultIconProps }),
|
|
407
|
+
color: "var(--card-icon-color)",
|
|
408
|
+
open: openHint === "info",
|
|
409
|
+
onToggle: () => {
|
|
410
|
+
setOpenHint(openHint === "info" ? null : "info");
|
|
411
|
+
},
|
|
412
|
+
onClose: closeHint,
|
|
413
|
+
children: info
|
|
414
|
+
}
|
|
415
|
+
),
|
|
416
|
+
isDefined(caution) && /* @__PURE__ */ jsx(
|
|
417
|
+
FieldHint,
|
|
418
|
+
{
|
|
419
|
+
label: "Caution",
|
|
420
|
+
icon: /* @__PURE__ */ jsx(TriangleAlertIcon, { ...defaultIconProps }),
|
|
421
|
+
color: "var(--card-icon-color)",
|
|
422
|
+
open: openHint === "caution",
|
|
423
|
+
onToggle: () => {
|
|
424
|
+
setOpenHint(openHint === "caution" ? null : "caution");
|
|
425
|
+
},
|
|
426
|
+
onClose: closeHint,
|
|
427
|
+
children: caution
|
|
428
|
+
}
|
|
429
|
+
)
|
|
430
|
+
] });
|
|
431
|
+
}
|
|
432
|
+
function Field({ field, depth = 0, ancestors = /* @__PURE__ */ new Set() }) {
|
|
433
|
+
const schema = useSchema(), { undocumentedFieldMessage } = useHandbookContext(), [expanded, setExpanded] = useState(!1), effectiveTitle = field.handbook?.title ?? field.title ?? (field.name && convertCase(field.name, "title")), effectiveDescription = field.handbook?.description ?? field.description ?? undocumentedFieldMessage, { example, tip, info, caution } = field.handbook ?? {}, hasHints = isDefined([tip, info, caution]), isCycle = ancestors.has(field.type), subfields = isCycle ? void 0 : getSubfields(field, schema), hasSubfields = isDefined(subfields);
|
|
434
|
+
return /* @__PURE__ */ jsxs(Stack, { space: 2, children: [
|
|
435
|
+
/* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
436
|
+
/* @__PURE__ */ jsxs(Flex, { align: "center", paddingY: 1, gap: 2, children: [
|
|
437
|
+
/* @__PURE__ */ jsx(Box, { flex: 1, children: /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
|
|
438
|
+
/* @__PURE__ */ jsx(Text, { size: 1, weight: "medium", children: effectiveTitle }),
|
|
439
|
+
/* @__PURE__ */ jsx(Text, { size: 0, muted: !0, style: { marginTop: "2px" }, children: field.type })
|
|
440
|
+
] }) }),
|
|
441
|
+
hasHints && /* @__PURE__ */ jsx(FieldHints, { tip, info, caution })
|
|
442
|
+
] }),
|
|
443
|
+
isDefined(effectiveDescription) && /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: effectiveDescription }),
|
|
444
|
+
isDefined(example) && /* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsxs(Text, { size: 1, muted: !0, style: { fontStyle: "italic" }, children: [
|
|
445
|
+
"e.g. ",
|
|
446
|
+
example
|
|
447
|
+
] }) })
|
|
448
|
+
] }),
|
|
449
|
+
isCycle && /* @__PURE__ */ jsx(Box, { marginTop: 2, children: /* @__PURE__ */ jsxs(Text, { size: 1, muted: !0, style: { fontStyle: "italic" }, children: [
|
|
450
|
+
"See ",
|
|
451
|
+
convertCase(field.type, "title"),
|
|
452
|
+
" above."
|
|
453
|
+
] }) }),
|
|
454
|
+
hasSubfields && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
455
|
+
/* @__PURE__ */ jsx(
|
|
456
|
+
Card,
|
|
457
|
+
{
|
|
458
|
+
as: "button",
|
|
459
|
+
padding: 3,
|
|
460
|
+
radius: 2,
|
|
461
|
+
tone: "transparent",
|
|
462
|
+
onClick: () => {
|
|
463
|
+
setExpanded(!expanded);
|
|
464
|
+
},
|
|
465
|
+
style: { width: "100%", marginTop: "1rem", textAlign: "left", border: "none", cursor: "pointer" },
|
|
466
|
+
children: /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, children: [
|
|
467
|
+
/* @__PURE__ */ jsx(Text, { size: 0, children: expanded ? /* @__PURE__ */ jsx(ChevronDownIcon, {}) : /* @__PURE__ */ jsx(ChevronRightIcon, {}) }),
|
|
468
|
+
/* @__PURE__ */ jsxs(Text, { size: 1, muted: !0, weight: "medium", children: [
|
|
469
|
+
subfields.length,
|
|
470
|
+
" subfield",
|
|
471
|
+
subfields.length === 1 ? "" : "s"
|
|
472
|
+
] })
|
|
473
|
+
] })
|
|
474
|
+
}
|
|
475
|
+
),
|
|
476
|
+
expanded && /* @__PURE__ */ jsx(
|
|
477
|
+
"div",
|
|
478
|
+
{
|
|
479
|
+
style: {
|
|
480
|
+
marginTop: "16px",
|
|
481
|
+
marginBottom: "8px",
|
|
482
|
+
paddingLeft: "16px",
|
|
483
|
+
borderLeft: "1px solid var(--card-border-color)"
|
|
484
|
+
},
|
|
485
|
+
children: /* @__PURE__ */ jsx(Stack, { space: 5, children: subfields.map((subfield) => /* @__PURE__ */ jsx(
|
|
486
|
+
Field,
|
|
487
|
+
{
|
|
488
|
+
field: subfield,
|
|
489
|
+
depth: depth + 1,
|
|
490
|
+
ancestors: /* @__PURE__ */ new Set([...ancestors, field.type])
|
|
491
|
+
},
|
|
492
|
+
subfield.name
|
|
493
|
+
)) })
|
|
494
|
+
}
|
|
495
|
+
)
|
|
496
|
+
] })
|
|
497
|
+
] });
|
|
498
|
+
}
|
|
499
|
+
function SidebarGroup({ heading, children }) {
|
|
500
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
501
|
+
/* @__PURE__ */ jsx(Box, { paddingY: 2, children: /* @__PURE__ */ jsx(Card, { paddingLeft: 2, padding: 2, children: /* @__PURE__ */ jsx(Flex, { align: "center", gap: 1, children: /* @__PURE__ */ jsx(
|
|
502
|
+
Text,
|
|
503
|
+
{
|
|
504
|
+
size: 1,
|
|
505
|
+
muted: !0,
|
|
506
|
+
textOverflow: "ellipsis",
|
|
507
|
+
weight: "semibold",
|
|
508
|
+
style: { cursor: "default", outline: "none" },
|
|
509
|
+
children: heading
|
|
510
|
+
}
|
|
511
|
+
) }) }) }),
|
|
512
|
+
children
|
|
513
|
+
] });
|
|
514
|
+
}
|
|
515
|
+
function SidebarTab({ id, label, icon: Icon }) {
|
|
516
|
+
const { activeTab, setActiveTab, setSidebarExpanded } = useHandbookContext(), selected = activeTab === id;
|
|
517
|
+
return /* @__PURE__ */ jsx(
|
|
518
|
+
Card,
|
|
519
|
+
{
|
|
520
|
+
id,
|
|
521
|
+
"aria-controls": `panel-${id}`,
|
|
522
|
+
"aria-selected": selected,
|
|
523
|
+
role: "option",
|
|
524
|
+
as: "button",
|
|
525
|
+
selected,
|
|
526
|
+
marginBottom: 1,
|
|
527
|
+
radius: 2,
|
|
528
|
+
sizing: "border",
|
|
529
|
+
tabIndex: -1,
|
|
530
|
+
tone: "inherit",
|
|
531
|
+
onClick: () => {
|
|
532
|
+
setActiveTab(id), setSidebarExpanded(!1);
|
|
533
|
+
},
|
|
534
|
+
style: { width: "100%", textAlign: "left", border: "none", cursor: "pointer" },
|
|
535
|
+
children: /* @__PURE__ */ jsx(
|
|
536
|
+
Flex,
|
|
537
|
+
{
|
|
538
|
+
align: "center",
|
|
539
|
+
paddingLeft: isDefined(Icon) ? 1 : 2,
|
|
540
|
+
paddingRight: 2,
|
|
541
|
+
paddingY: 1,
|
|
542
|
+
style: { height: "1.5625rem", boxSizing: "content-box" },
|
|
543
|
+
children: /* @__PURE__ */ jsxs(Flex, { align: "center", flex: 1, gap: 2, children: [
|
|
544
|
+
isDefined(Icon) && /* @__PURE__ */ jsx(
|
|
545
|
+
"div",
|
|
546
|
+
{
|
|
547
|
+
style: {
|
|
548
|
+
display: "flex",
|
|
549
|
+
alignItems: "center",
|
|
550
|
+
justifyContent: "center",
|
|
551
|
+
width: "25px",
|
|
552
|
+
height: "25px",
|
|
553
|
+
color: "var(--card-icon-color)"
|
|
554
|
+
},
|
|
555
|
+
children: /* @__PURE__ */ jsx(Icon, {})
|
|
556
|
+
}
|
|
557
|
+
),
|
|
558
|
+
/* @__PURE__ */ jsx(Stack, { flex: 1, space: 2, children: /* @__PURE__ */ jsx(Text, { size: 1, textOverflow: "ellipsis", weight: "medium", style: { color: "inherit" }, children: label }) }),
|
|
559
|
+
/* @__PURE__ */ jsx(Box, { paddingLeft: 4, paddingRight: 1, children: /* @__PURE__ */ jsx(Box, { style: { opacity: 0.5 }, children: /* @__PURE__ */ jsx(Text, { muted: !0, size: 1, children: /* @__PURE__ */ jsx(ChevronRightIcon, {}) }) }) })
|
|
560
|
+
] })
|
|
561
|
+
}
|
|
562
|
+
)
|
|
563
|
+
}
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
function DocumentTypesTabs() {
|
|
567
|
+
const { groups } = useHandbookContext();
|
|
568
|
+
return groups.map((group) => {
|
|
569
|
+
const { title, documents } = group;
|
|
570
|
+
return /* @__PURE__ */ jsx(SidebarGroup, { heading: title, children: documents.map((document) => {
|
|
571
|
+
const effectiveTitle = document.handbook?.title ?? document.title ?? convertCase(document.name, "title"), icon = typeof document.icon == "function" ? document.icon : void 0;
|
|
572
|
+
return /* @__PURE__ */ jsx(SidebarTab, { id: document.name, label: effectiveTitle, icon }, document.name);
|
|
573
|
+
}) }, title);
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
function DocumentTypesPanels() {
|
|
577
|
+
const { groups } = useHandbookContext();
|
|
578
|
+
return groups.map((group) => {
|
|
579
|
+
const { documents } = group;
|
|
580
|
+
return documents.map((document) => {
|
|
581
|
+
const effectiveTitle = document.handbook?.title ?? document.title ?? convertCase(document.name, "title"), effectiveDescription = document.handbook?.description ?? (typeof document.description == "string" ? document.description : void 0);
|
|
582
|
+
return /* @__PURE__ */ jsx(ContentPanel, { id: document.name, title: effectiveTitle, description: effectiveDescription, children: /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: "3.25rem" }, children: document.fields.map((field) => /* @__PURE__ */ jsx(Field, { field }, field.name)) }) }, document.name);
|
|
583
|
+
});
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
function isPortableTextSpan(node) {
|
|
587
|
+
return node._type === "span" && "text" in node && typeof node.text == "string" && (node.marks === void 0 || Array.isArray(node.marks) && node.marks.every((mark) => typeof mark == "string"));
|
|
588
|
+
}
|
|
589
|
+
function isPortableTextBlock(node) {
|
|
590
|
+
return typeof node._type == "string" && node._type[0] !== "@" && (!("markDefs" in node) || !node.markDefs || Array.isArray(node.markDefs) && node.markDefs.every((def) => typeof def._key == "string")) && "children" in node && Array.isArray(node.children) && node.children.every((child) => typeof child == "object" && "_type" in child);
|
|
591
|
+
}
|
|
592
|
+
function isPortableTextListItemBlock(block) {
|
|
593
|
+
return isPortableTextBlock(block) && "listItem" in block && typeof block.listItem == "string" && (block.level === void 0 || typeof block.level == "number");
|
|
594
|
+
}
|
|
595
|
+
function isPortableTextToolkitList(block) {
|
|
596
|
+
return block._type === "@list";
|
|
597
|
+
}
|
|
598
|
+
function isPortableTextToolkitSpan(span) {
|
|
599
|
+
return span._type === "@span";
|
|
600
|
+
}
|
|
601
|
+
function isPortableTextToolkitTextNode(node) {
|
|
602
|
+
return node._type === "@text";
|
|
603
|
+
}
|
|
604
|
+
const knownDecorators = [
|
|
605
|
+
"strong",
|
|
606
|
+
"em",
|
|
607
|
+
"code",
|
|
608
|
+
"underline",
|
|
609
|
+
"strike-through"
|
|
610
|
+
];
|
|
611
|
+
function sortMarksByOccurences(span, index, blockChildren) {
|
|
612
|
+
if (!isPortableTextSpan(span) || !span.marks || !span.marks.length) return [];
|
|
613
|
+
let marks = span.marks.slice(), occurences = {};
|
|
614
|
+
return marks.forEach((mark) => {
|
|
615
|
+
occurences[mark] = 1;
|
|
616
|
+
for (let siblingIndex = index + 1; siblingIndex < blockChildren.length; siblingIndex++) {
|
|
617
|
+
let sibling = blockChildren[siblingIndex];
|
|
618
|
+
if (sibling && isPortableTextSpan(sibling) && Array.isArray(sibling.marks) && sibling.marks.indexOf(mark) !== -1) occurences[mark]++;
|
|
619
|
+
else break;
|
|
620
|
+
}
|
|
621
|
+
}), marks.sort((markA, markB) => sortMarks(occurences, markA, markB));
|
|
622
|
+
}
|
|
623
|
+
function sortMarks(occurences, markA, markB) {
|
|
624
|
+
let aOccurences = occurences[markA], bOccurences = occurences[markB];
|
|
625
|
+
if (aOccurences !== bOccurences) return bOccurences - aOccurences;
|
|
626
|
+
let aKnownPos = knownDecorators.indexOf(markA), bKnownPos = knownDecorators.indexOf(markB);
|
|
627
|
+
return aKnownPos === bKnownPos ? markA.localeCompare(markB) : aKnownPos - bKnownPos;
|
|
628
|
+
}
|
|
629
|
+
function buildMarksTree(block) {
|
|
630
|
+
let { children } = block, markDefs = block.markDefs ?? [];
|
|
631
|
+
if (!children || !children.length) return [];
|
|
632
|
+
let sortedMarks = children.map(sortMarksByOccurences), rootNode = {
|
|
633
|
+
_type: "@span",
|
|
634
|
+
children: [],
|
|
635
|
+
markType: "<unknown>"
|
|
636
|
+
}, nodeStack = [rootNode];
|
|
637
|
+
for (let i = 0; i < children.length; i++) {
|
|
638
|
+
let span = children[i];
|
|
639
|
+
if (!span) continue;
|
|
640
|
+
let marksNeeded = sortedMarks[i] || [], pos = 1;
|
|
641
|
+
if (nodeStack.length > 1) for (; pos < nodeStack.length; pos++) {
|
|
642
|
+
let mark = nodeStack[pos]?.markKey || "", index = marksNeeded.indexOf(mark);
|
|
643
|
+
if (index === -1) break;
|
|
644
|
+
marksNeeded.splice(index, 1);
|
|
645
|
+
}
|
|
646
|
+
nodeStack = nodeStack.slice(0, pos);
|
|
647
|
+
let currentNode = nodeStack[nodeStack.length - 1];
|
|
648
|
+
if (currentNode) {
|
|
649
|
+
for (let markKey of marksNeeded) {
|
|
650
|
+
let markDef = markDefs?.find((def) => def._key === markKey), node = {
|
|
651
|
+
_type: "@span",
|
|
652
|
+
_key: span._key,
|
|
653
|
+
children: [],
|
|
654
|
+
markDef,
|
|
655
|
+
markType: markDef ? markDef._type : markKey,
|
|
656
|
+
markKey
|
|
657
|
+
};
|
|
658
|
+
currentNode.children.push(node), nodeStack.push(node), currentNode = node;
|
|
659
|
+
}
|
|
660
|
+
if (isPortableTextSpan(span)) {
|
|
661
|
+
let lines = span.text.split(`
|
|
662
|
+
`);
|
|
663
|
+
for (let line = lines.length; line-- > 1; ) lines.splice(line, 0, `
|
|
664
|
+
`);
|
|
665
|
+
currentNode.children = currentNode.children.concat(lines.map((text) => ({
|
|
666
|
+
_type: "@text",
|
|
667
|
+
text
|
|
668
|
+
})));
|
|
669
|
+
} else currentNode.children = currentNode.children.concat(span);
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
return rootNode.children;
|
|
673
|
+
}
|
|
674
|
+
function nestLists(blocks, mode) {
|
|
675
|
+
let tree = [], currentList;
|
|
676
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
677
|
+
let block = blocks[i];
|
|
678
|
+
if (block) {
|
|
679
|
+
if (!isPortableTextListItemBlock(block)) {
|
|
680
|
+
tree.push(block), currentList = void 0;
|
|
681
|
+
continue;
|
|
682
|
+
}
|
|
683
|
+
if (!currentList) {
|
|
684
|
+
currentList = listFromBlock(block, i, mode), tree.push(currentList);
|
|
685
|
+
continue;
|
|
686
|
+
}
|
|
687
|
+
if (blockMatchesList(block, currentList)) {
|
|
688
|
+
currentList.children.push(block);
|
|
689
|
+
continue;
|
|
690
|
+
}
|
|
691
|
+
if ((block.level || 1) > currentList.level) {
|
|
692
|
+
let newList = listFromBlock(block, i, mode);
|
|
693
|
+
if (mode === "html") {
|
|
694
|
+
let lastListItem = currentList.children[currentList.children.length - 1], newLastChild = {
|
|
695
|
+
...lastListItem,
|
|
696
|
+
children: [...lastListItem.children, newList]
|
|
697
|
+
};
|
|
698
|
+
currentList.children[currentList.children.length - 1] = newLastChild;
|
|
699
|
+
} else currentList.children.push(newList);
|
|
700
|
+
currentList = newList;
|
|
701
|
+
continue;
|
|
702
|
+
}
|
|
703
|
+
if ((block.level || 1) < currentList.level) {
|
|
704
|
+
let matchingBranch = tree[tree.length - 1], match = matchingBranch && findListMatching(matchingBranch, block);
|
|
705
|
+
if (match) {
|
|
706
|
+
currentList = match, currentList.children.push(block);
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
709
|
+
currentList = listFromBlock(block, i, mode), tree.push(currentList);
|
|
710
|
+
continue;
|
|
711
|
+
}
|
|
712
|
+
if (block.listItem !== currentList.listItem) {
|
|
713
|
+
let matchingBranch = tree[tree.length - 1], match = matchingBranch && findListMatching(matchingBranch, { level: block.level || 1 });
|
|
714
|
+
if (match && match.listItem === block.listItem) {
|
|
715
|
+
currentList = match, currentList.children.push(block);
|
|
716
|
+
continue;
|
|
717
|
+
} else {
|
|
718
|
+
currentList = listFromBlock(block, i, mode), tree.push(currentList);
|
|
719
|
+
continue;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
console.warn("Unknown state encountered for block", block), tree.push(block);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
return tree;
|
|
726
|
+
}
|
|
727
|
+
function blockMatchesList(block, list) {
|
|
728
|
+
return (block.level || 1) === list.level && block.listItem === list.listItem;
|
|
729
|
+
}
|
|
730
|
+
function listFromBlock(block, index, mode) {
|
|
731
|
+
return {
|
|
732
|
+
_type: "@list",
|
|
733
|
+
_key: `${block._key || `${index}`}-parent`,
|
|
734
|
+
mode,
|
|
735
|
+
level: block.level || 1,
|
|
736
|
+
listItem: block.listItem,
|
|
737
|
+
children: [block]
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
function findListMatching(rootNode, matching) {
|
|
741
|
+
let level = matching.level || 1, style = matching.listItem || "normal", filterOnType = typeof matching.listItem == "string";
|
|
742
|
+
if (isPortableTextToolkitList(rootNode) && (rootNode.level || 1) === level && filterOnType && (rootNode.listItem || "normal") === style) return rootNode;
|
|
743
|
+
if (!("children" in rootNode)) return;
|
|
744
|
+
let node = rootNode.children[rootNode.children.length - 1];
|
|
745
|
+
return node && !isPortableTextSpan(node) ? findListMatching(node, matching) : void 0;
|
|
746
|
+
}
|
|
747
|
+
function spanToPlainText(span) {
|
|
748
|
+
let text = "";
|
|
749
|
+
return span.children.forEach((current) => {
|
|
750
|
+
isPortableTextToolkitTextNode(current) ? text += current.text : isPortableTextToolkitSpan(current) && (text += spanToPlainText(current));
|
|
751
|
+
}), text;
|
|
752
|
+
}
|
|
753
|
+
const LIST_NEST_MODE_HTML = "html", defaultLists = {
|
|
754
|
+
number: ({ children }) => /* @__PURE__ */ jsx("ol", { children }),
|
|
755
|
+
bullet: ({ children }) => /* @__PURE__ */ jsx("ul", { children })
|
|
756
|
+
}, DefaultListItem = ({ children }) => /* @__PURE__ */ jsx("li", { children }), link = ({ children, value }) => /* @__PURE__ */ jsx("a", {
|
|
757
|
+
href: value?.href,
|
|
758
|
+
children
|
|
759
|
+
}), underlineStyle = { textDecoration: "underline" }, defaultMarks = {
|
|
760
|
+
em: ({ children }) => /* @__PURE__ */ jsx("em", { children }),
|
|
761
|
+
strong: ({ children }) => /* @__PURE__ */ jsx("strong", { children }),
|
|
762
|
+
code: ({ children }) => /* @__PURE__ */ jsx("code", { children }),
|
|
763
|
+
underline: ({ children }) => /* @__PURE__ */ jsx("span", {
|
|
764
|
+
style: underlineStyle,
|
|
765
|
+
children
|
|
766
|
+
}),
|
|
767
|
+
"strike-through": ({ children }) => /* @__PURE__ */ jsx("del", { children }),
|
|
768
|
+
link
|
|
769
|
+
}, getTemplate = (type, prop) => `[@portabletext/react] Unknown ${type}, specify a component for it in the \`components.${prop}\` prop`, unknownTypeWarning = (typeName) => getTemplate(`block type "${typeName}"`, "types"), unknownMarkWarning = (markType) => getTemplate(`mark type "${markType}"`, "marks"), unknownBlockStyleWarning = (blockStyle) => getTemplate(`block style "${blockStyle}"`, "block"), unknownListStyleWarning = (listStyle) => getTemplate(`list style "${listStyle}"`, "list"), unknownListItemStyleWarning = (listStyle) => getTemplate(`list item style "${listStyle}"`, "listItem");
|
|
770
|
+
function printWarning(message) {
|
|
771
|
+
console.warn(message);
|
|
772
|
+
}
|
|
773
|
+
const hidden = { display: "none" }, defaultComponents = {
|
|
774
|
+
types: {},
|
|
775
|
+
block: {
|
|
776
|
+
normal: ({ children }) => /* @__PURE__ */ jsx("p", { children }),
|
|
777
|
+
blockquote: ({ children }) => /* @__PURE__ */ jsx("blockquote", { children }),
|
|
778
|
+
h1: ({ children }) => /* @__PURE__ */ jsx("h1", { children }),
|
|
779
|
+
h2: ({ children }) => /* @__PURE__ */ jsx("h2", { children }),
|
|
780
|
+
h3: ({ children }) => /* @__PURE__ */ jsx("h3", { children }),
|
|
781
|
+
h4: ({ children }) => /* @__PURE__ */ jsx("h4", { children }),
|
|
782
|
+
h5: ({ children }) => /* @__PURE__ */ jsx("h5", { children }),
|
|
783
|
+
h6: ({ children }) => /* @__PURE__ */ jsx("h6", { children })
|
|
784
|
+
},
|
|
785
|
+
marks: defaultMarks,
|
|
786
|
+
list: defaultLists,
|
|
787
|
+
listItem: DefaultListItem,
|
|
788
|
+
hardBreak: () => /* @__PURE__ */ jsx("br", {}),
|
|
789
|
+
unknownType: ({ value, isInline }) => {
|
|
790
|
+
let warning = unknownTypeWarning(value._type);
|
|
791
|
+
return jsx(isInline ? "span" : "div", {
|
|
792
|
+
style: hidden,
|
|
793
|
+
children: warning
|
|
794
|
+
});
|
|
795
|
+
},
|
|
796
|
+
unknownMark: ({ markType, children }) => /* @__PURE__ */ jsx("span", {
|
|
797
|
+
className: `unknown__pt__mark__${markType}`,
|
|
798
|
+
children
|
|
799
|
+
}),
|
|
800
|
+
unknownList: ({ children }) => /* @__PURE__ */ jsx("ul", { children }),
|
|
801
|
+
unknownListItem: ({ children }) => /* @__PURE__ */ jsx("li", { children }),
|
|
802
|
+
unknownBlockStyle: ({ children }) => /* @__PURE__ */ jsx("p", { children })
|
|
803
|
+
};
|
|
804
|
+
function mergeComponents(parent, overrides) {
|
|
805
|
+
let { block: _block, list: _list, listItem: _listItem, marks: _marks, types: _types, ...rest } = overrides;
|
|
806
|
+
return {
|
|
807
|
+
...parent,
|
|
808
|
+
block: mergeDeeply(parent, overrides, "block"),
|
|
809
|
+
list: mergeDeeply(parent, overrides, "list"),
|
|
810
|
+
listItem: mergeDeeply(parent, overrides, "listItem"),
|
|
811
|
+
marks: mergeDeeply(parent, overrides, "marks"),
|
|
812
|
+
types: mergeDeeply(parent, overrides, "types"),
|
|
813
|
+
...rest
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
function mergeDeeply(parent, overrides, key) {
|
|
817
|
+
let override = overrides[key], parentVal = parent[key];
|
|
818
|
+
return typeof override == "function" || override && typeof parentVal == "function" ? override : override ? {
|
|
819
|
+
...parentVal,
|
|
820
|
+
...override
|
|
821
|
+
} : parentVal;
|
|
822
|
+
}
|
|
823
|
+
function PortableText({ value: input, components: componentOverrides, listNestingMode, onMissingComponent: missingComponentHandler = printWarning }) {
|
|
824
|
+
let handleMissingComponent = missingComponentHandler || noop, nested = nestLists(Array.isArray(input) ? input : [input], listNestingMode || LIST_NEST_MODE_HTML), components = useMemo(() => componentOverrides ? mergeComponents(defaultComponents, componentOverrides) : defaultComponents, [componentOverrides]), renderNode = useMemo(() => getNodeRenderer(components, handleMissingComponent), [components, handleMissingComponent]);
|
|
825
|
+
return /* @__PURE__ */ jsx(Fragment, { children: nested.map((node, index) => renderNode({
|
|
826
|
+
node,
|
|
827
|
+
index,
|
|
828
|
+
isInline: !1,
|
|
829
|
+
renderNode
|
|
830
|
+
})) });
|
|
831
|
+
}
|
|
832
|
+
const getNodeRenderer = (components, handleMissingComponent) => {
|
|
833
|
+
function renderNode(options) {
|
|
834
|
+
let { node, index, isInline } = options, key = node._key || `node-${index}`;
|
|
835
|
+
return isPortableTextToolkitList(node) ? renderList(node, index, key) : isPortableTextListItemBlock(node) ? renderListItem(node, index, key) : isPortableTextToolkitSpan(node) ? renderSpan(node, index, key) : hasCustomComponentForNode(node) ? renderCustomBlock(node, index, key, isInline) : isPortableTextBlock(node) ? renderBlock(node, index, key, isInline) : isPortableTextToolkitTextNode(node) ? renderText(node, key) : renderUnknownType(node, index, key, isInline);
|
|
836
|
+
}
|
|
837
|
+
function hasCustomComponentForNode(node) {
|
|
838
|
+
return node._type in components.types;
|
|
839
|
+
}
|
|
840
|
+
function renderListItem(node, index, key) {
|
|
841
|
+
let tree = serializeBlock({
|
|
842
|
+
node,
|
|
843
|
+
index,
|
|
844
|
+
isInline: !1,
|
|
845
|
+
renderNode
|
|
846
|
+
}), renderer = components.listItem, Li = (typeof renderer == "function" ? renderer : renderer[node.listItem]) || components.unknownListItem;
|
|
847
|
+
if (Li === components.unknownListItem) {
|
|
848
|
+
let style = node.listItem || "bullet";
|
|
849
|
+
handleMissingComponent(unknownListItemStyleWarning(style), {
|
|
850
|
+
type: style,
|
|
851
|
+
nodeType: "listItemStyle"
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
let children = tree.children;
|
|
855
|
+
if (node.style && node.style !== "normal") {
|
|
856
|
+
let { listItem: _listItem, ...blockNode } = node;
|
|
857
|
+
children = renderNode({
|
|
858
|
+
node: blockNode,
|
|
859
|
+
index,
|
|
860
|
+
isInline: !1
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
return /* @__PURE__ */ jsx(Li, {
|
|
864
|
+
value: node,
|
|
865
|
+
index,
|
|
866
|
+
isInline: !1,
|
|
867
|
+
renderNode,
|
|
868
|
+
children
|
|
869
|
+
}, key);
|
|
870
|
+
}
|
|
871
|
+
function renderList(node, index, key) {
|
|
872
|
+
let children = node.children.map((child, childIndex) => renderNode({
|
|
873
|
+
node: child._key ? child : {
|
|
874
|
+
...child,
|
|
875
|
+
_key: `li-${index}-${childIndex}`
|
|
876
|
+
},
|
|
877
|
+
index: childIndex,
|
|
878
|
+
isInline: !1
|
|
879
|
+
})), component = components.list, List = (typeof component == "function" ? component : component[node.listItem]) || components.unknownList;
|
|
880
|
+
if (List === components.unknownList) {
|
|
881
|
+
let style = node.listItem || "bullet";
|
|
882
|
+
handleMissingComponent(unknownListStyleWarning(style), {
|
|
883
|
+
nodeType: "listStyle",
|
|
884
|
+
type: style
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
return /* @__PURE__ */ jsx(List, {
|
|
888
|
+
value: node,
|
|
889
|
+
index,
|
|
890
|
+
isInline: !1,
|
|
891
|
+
renderNode,
|
|
892
|
+
children
|
|
893
|
+
}, key);
|
|
894
|
+
}
|
|
895
|
+
function renderSpan(node, _index, key) {
|
|
896
|
+
let { markDef, markType, markKey } = node, Span = components.marks[markType] || components.unknownMark, children = node.children.map((child, childIndex) => renderNode({
|
|
897
|
+
node: child,
|
|
898
|
+
index: childIndex,
|
|
899
|
+
isInline: !0
|
|
900
|
+
}));
|
|
901
|
+
return Span === components.unknownMark && handleMissingComponent(unknownMarkWarning(markType), {
|
|
902
|
+
nodeType: "mark",
|
|
903
|
+
type: markType
|
|
904
|
+
}), /* @__PURE__ */ jsx(Span, {
|
|
905
|
+
text: spanToPlainText(node),
|
|
906
|
+
value: markDef,
|
|
907
|
+
markType,
|
|
908
|
+
markKey,
|
|
909
|
+
renderNode,
|
|
910
|
+
children
|
|
911
|
+
}, key);
|
|
912
|
+
}
|
|
913
|
+
function renderBlock(node, index, key, isInline) {
|
|
914
|
+
let { _key, ...props } = serializeBlock({
|
|
915
|
+
node,
|
|
916
|
+
index,
|
|
917
|
+
isInline,
|
|
918
|
+
renderNode
|
|
919
|
+
}), style = props.node.style || "normal", Block = (typeof components.block == "function" ? components.block : components.block[style]) || components.unknownBlockStyle;
|
|
920
|
+
return Block === components.unknownBlockStyle && handleMissingComponent(unknownBlockStyleWarning(style), {
|
|
921
|
+
nodeType: "blockStyle",
|
|
922
|
+
type: style
|
|
923
|
+
}), /* @__PURE__ */ jsx(Block, {
|
|
924
|
+
...props,
|
|
925
|
+
value: props.node,
|
|
926
|
+
renderNode
|
|
927
|
+
}, key);
|
|
928
|
+
}
|
|
929
|
+
function renderText(node, key) {
|
|
930
|
+
if (node.text === `
|
|
931
|
+
`) {
|
|
932
|
+
let HardBreak = components.hardBreak;
|
|
933
|
+
return HardBreak ? /* @__PURE__ */ jsx(HardBreak, {}, key) : `
|
|
934
|
+
`;
|
|
935
|
+
}
|
|
936
|
+
return node.text;
|
|
937
|
+
}
|
|
938
|
+
function renderUnknownType(node, index, key, isInline) {
|
|
939
|
+
let nodeOptions = {
|
|
940
|
+
value: node,
|
|
941
|
+
isInline,
|
|
942
|
+
index,
|
|
943
|
+
renderNode
|
|
944
|
+
};
|
|
945
|
+
handleMissingComponent(unknownTypeWarning(node._type), {
|
|
946
|
+
nodeType: "block",
|
|
947
|
+
type: node._type
|
|
948
|
+
});
|
|
949
|
+
let UnknownType = components.unknownType;
|
|
950
|
+
return /* @__PURE__ */ jsx(UnknownType, { ...nodeOptions }, key);
|
|
951
|
+
}
|
|
952
|
+
function renderCustomBlock(node, index, key, isInline) {
|
|
953
|
+
let nodeOptions = {
|
|
954
|
+
value: node,
|
|
955
|
+
isInline,
|
|
956
|
+
index,
|
|
957
|
+
renderNode
|
|
958
|
+
}, Node = components.types[node._type];
|
|
959
|
+
return Node ? /* @__PURE__ */ jsx(Node, { ...nodeOptions }, key) : null;
|
|
960
|
+
}
|
|
961
|
+
return renderNode;
|
|
962
|
+
};
|
|
963
|
+
function serializeBlock(options) {
|
|
964
|
+
let { node, index, isInline, renderNode } = options, children = buildMarksTree(node).map((child, i) => renderNode({
|
|
965
|
+
node: child,
|
|
966
|
+
isInline: !0,
|
|
967
|
+
index: i,
|
|
968
|
+
renderNode
|
|
969
|
+
}));
|
|
970
|
+
return {
|
|
971
|
+
_key: node._key || `block-${index}`,
|
|
972
|
+
children,
|
|
973
|
+
index,
|
|
974
|
+
isInline,
|
|
975
|
+
node
|
|
976
|
+
};
|
|
977
|
+
}
|
|
978
|
+
function noop() {
|
|
979
|
+
}
|
|
980
|
+
const variantConfig = {
|
|
981
|
+
tip: { icon: LightbulbIcon, tone: "positive" },
|
|
982
|
+
info: { icon: InfoIcon, tone: "primary" },
|
|
983
|
+
warning: { icon: TriangleAlertIcon, tone: "caution" }
|
|
984
|
+
};
|
|
985
|
+
function CalloutContent({ variant, children }) {
|
|
986
|
+
const config = variantConfig[variant], Icon = config.icon;
|
|
987
|
+
return /* @__PURE__ */ jsx(Card, { padding: 4, radius: 2, tone: config.tone, children: /* @__PURE__ */ jsxs(Flex, { align: "flex-start", gap: 3, children: [
|
|
988
|
+
/* @__PURE__ */ jsx("div", { style: { flexShrink: 0, marginTop: "-2px", fontSize: "16px", color: "var(--card-icon-color)" }, children: /* @__PURE__ */ jsx(Icon, { ...defaultIconProps }) }),
|
|
989
|
+
/* @__PURE__ */ jsx(Text, { size: 1, style: { color: "var(--card-icon-color)" }, children })
|
|
990
|
+
] }) });
|
|
991
|
+
}
|
|
992
|
+
function CalloutBlock({ value }) {
|
|
993
|
+
return /* @__PURE__ */ jsx("div", { style: { marginBlock: "2rem" }, children: /* @__PURE__ */ jsx(CalloutContent, { variant: value.variant, children: /* @__PURE__ */ jsx(
|
|
994
|
+
PortableText,
|
|
995
|
+
{
|
|
996
|
+
value: value.body,
|
|
997
|
+
components: {
|
|
998
|
+
block: { normal: ({ children }) => /* @__PURE__ */ jsx(Fragment, { children }) },
|
|
999
|
+
marks: {
|
|
1000
|
+
strong: ({ children }) => /* @__PURE__ */ jsx("strong", { children }),
|
|
1001
|
+
em: ({ children }) => /* @__PURE__ */ jsx("em", { children }),
|
|
1002
|
+
code: ({ children }) => /* @__PURE__ */ jsx(
|
|
1003
|
+
"code",
|
|
1004
|
+
{
|
|
1005
|
+
style: {
|
|
1006
|
+
padding: "0.125rem 0.25rem",
|
|
1007
|
+
fontSize: "0.875em",
|
|
1008
|
+
backgroundColor: "var(--card-code-bg-color)",
|
|
1009
|
+
borderRadius: "0.125rem"
|
|
1010
|
+
},
|
|
1011
|
+
children
|
|
1012
|
+
}
|
|
1013
|
+
),
|
|
1014
|
+
link: ({ children, value: mark }) => /* @__PURE__ */ jsx(
|
|
1015
|
+
"a",
|
|
1016
|
+
{
|
|
1017
|
+
href: mark?.href,
|
|
1018
|
+
target: "_blank",
|
|
1019
|
+
rel: "noopener noreferrer",
|
|
1020
|
+
style: { color: "var(--card-fg-color)" },
|
|
1021
|
+
children
|
|
1022
|
+
}
|
|
1023
|
+
)
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
) }) });
|
|
1028
|
+
}
|
|
1029
|
+
function CodeContent({ code, language }) {
|
|
1030
|
+
return /* @__PURE__ */ jsx(Card, { padding: 4, radius: 2, tone: "transparent", style: { overflowX: "auto" }, children: /* @__PURE__ */ jsx(Code, { size: 1, language, children: code }) });
|
|
1031
|
+
}
|
|
1032
|
+
function CodeBlock({ value }) {
|
|
1033
|
+
return /* @__PURE__ */ jsx("div", { style: { marginBlock: "1.5rem" }, children: /* @__PURE__ */ jsx(CodeContent, { code: value.code, language: value.language }) });
|
|
1034
|
+
}
|
|
1035
|
+
function HorizontalRuleContent({ marginBlock = "0" }) {
|
|
1036
|
+
return /* @__PURE__ */ jsx("hr", { style: { marginBlock, border: "none", borderTop: "1px solid var(--card-border-color)" } });
|
|
1037
|
+
}
|
|
1038
|
+
function HorizontalRuleBlock() {
|
|
1039
|
+
return /* @__PURE__ */ jsx(HorizontalRuleContent, { marginBlock: "2.5rem" });
|
|
1040
|
+
}
|
|
1041
|
+
function ImageContent({ url, alt, caption }) {
|
|
1042
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1043
|
+
/* @__PURE__ */ jsx(
|
|
1044
|
+
"img",
|
|
1045
|
+
{
|
|
1046
|
+
src: url,
|
|
1047
|
+
alt: alt ?? "",
|
|
1048
|
+
style: { display: "block", maxWidth: "100%", borderRadius: "0.25rem", overflow: "hidden" }
|
|
1049
|
+
}
|
|
1050
|
+
),
|
|
1051
|
+
isDefined(caption) && /* @__PURE__ */ jsx("div", { style: { marginBlockStart: "0.75rem" }, children: /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, style: { fontStyle: "italic" }, children: caption }) })
|
|
1052
|
+
] });
|
|
1053
|
+
}
|
|
1054
|
+
function ImageBlock({ value }) {
|
|
1055
|
+
return isDefined(value.asset?.url) ? /* @__PURE__ */ jsx("div", { style: { marginBlock: "1.5rem" }, children: /* @__PURE__ */ jsx(ImageContent, { url: value.asset.url, alt: value.alt, caption: value.caption }) }) : null;
|
|
1056
|
+
}
|
|
1057
|
+
function VideoContent({ url, caption }) {
|
|
1058
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1059
|
+
/* @__PURE__ */ jsx(
|
|
1060
|
+
"video",
|
|
1061
|
+
{
|
|
1062
|
+
src: url,
|
|
1063
|
+
controls: !0,
|
|
1064
|
+
style: {
|
|
1065
|
+
display: "block",
|
|
1066
|
+
maxWidth: "100%",
|
|
1067
|
+
borderRadius: "0.25rem",
|
|
1068
|
+
overflow: "hidden",
|
|
1069
|
+
WebkitTransform: "translateZ(0)"
|
|
1070
|
+
},
|
|
1071
|
+
children: /* @__PURE__ */ jsx("track", { kind: "captions", label: caption ?? "Video" })
|
|
1072
|
+
}
|
|
1073
|
+
),
|
|
1074
|
+
isDefined(caption) && /* @__PURE__ */ jsx("div", { style: { marginBlockStart: "0.75rem" }, children: /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, style: { fontStyle: "italic" }, children: caption }) })
|
|
1075
|
+
] });
|
|
1076
|
+
}
|
|
1077
|
+
function VideoBlock({ value }) {
|
|
1078
|
+
return isDefined(value.asset?.url) ? /* @__PURE__ */ jsx("div", { style: { marginBlock: "1.5rem" }, children: /* @__PURE__ */ jsx(VideoContent, { url: value.asset.url, caption: value.caption }) }) : null;
|
|
1079
|
+
}
|
|
1080
|
+
const portableTextComponents = {
|
|
1081
|
+
block: {
|
|
1082
|
+
h2: ({ children }) => /* @__PURE__ */ jsx("div", { style: { marginBlockStart: "3rem", marginBlockEnd: "0.25rem" }, children: /* @__PURE__ */ jsx(Text, { as: "h2", size: 1, weight: "medium", children }) }),
|
|
1083
|
+
normal: ({ children }) => /* @__PURE__ */ jsx("div", { style: { marginBlock: "1rem" }, children: /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children }) }),
|
|
1084
|
+
blockquote: ({ children }) => /* @__PURE__ */ jsx("div", { style: { marginBlock: "2rem" }, children: /* @__PURE__ */ jsx(Box, { paddingLeft: 3, paddingY: 1, style: { borderLeft: "2px solid var(--card-border-color)" }, children: /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, style: { fontStyle: "italic" }, children }) }) })
|
|
1085
|
+
},
|
|
1086
|
+
list: {
|
|
1087
|
+
bullet: ({ children, value }) => /* @__PURE__ */ jsx("div", { style: { marginBlock: value.level > 1 ? "0.75rem" : "2rem" }, children: /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: /* @__PURE__ */ jsx("ul", { style: { display: "flex", flexDirection: "column", gap: "0.375rem", margin: 0, paddingLeft: "1.25em" }, children }) }) }),
|
|
1088
|
+
number: ({ children, value }) => /* @__PURE__ */ jsx("div", { style: { marginBlock: value.level > 1 ? "0.75rem" : "2rem" }, children: /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: /* @__PURE__ */ jsx("ol", { style: { display: "flex", flexDirection: "column", gap: "0.375rem", margin: 0, paddingLeft: "1.25em" }, children }) }) })
|
|
1089
|
+
},
|
|
1090
|
+
listItem: {
|
|
1091
|
+
bullet: ({ children }) => /* @__PURE__ */ jsx("li", { children }),
|
|
1092
|
+
number: ({ children }) => /* @__PURE__ */ jsx("li", { children })
|
|
1093
|
+
},
|
|
1094
|
+
marks: {
|
|
1095
|
+
strong: ({ children }) => /* @__PURE__ */ jsx("strong", { children }),
|
|
1096
|
+
em: ({ children }) => /* @__PURE__ */ jsx("em", { children }),
|
|
1097
|
+
code: ({ children }) => /* @__PURE__ */ jsx(
|
|
1098
|
+
"code",
|
|
1099
|
+
{
|
|
1100
|
+
style: {
|
|
1101
|
+
padding: "0.125rem 0.25rem",
|
|
1102
|
+
fontSize: "0.875em",
|
|
1103
|
+
backgroundColor: "var(--card-code-bg-color)",
|
|
1104
|
+
borderRadius: "0.125rem"
|
|
1105
|
+
},
|
|
1106
|
+
children
|
|
1107
|
+
}
|
|
1108
|
+
),
|
|
1109
|
+
link: ({ children, value }) => /* @__PURE__ */ jsx("a", { href: value?.href, target: "_blank", rel: "noopener noreferrer", style: { color: "var(--card-link-fg-color)" }, children }),
|
|
1110
|
+
"strike-through": ({ children }) => /* @__PURE__ */ jsx("del", { children })
|
|
1111
|
+
}
|
|
1112
|
+
};
|
|
1113
|
+
function GuideContent({ content }) {
|
|
1114
|
+
const { blocks: customBlocks } = useHandbookContext(), components = useMemo(() => {
|
|
1115
|
+
const customTypes = {};
|
|
1116
|
+
for (const block of customBlocks)
|
|
1117
|
+
customTypes[block.schema.name] = block.component;
|
|
1118
|
+
return {
|
|
1119
|
+
...portableTextComponents,
|
|
1120
|
+
types: {
|
|
1121
|
+
"handbook.image": ImageBlock,
|
|
1122
|
+
"handbook.video": VideoBlock,
|
|
1123
|
+
"handbook.code": CodeBlock,
|
|
1124
|
+
"handbook.callout": CalloutBlock,
|
|
1125
|
+
"handbook.horizontalRule": HorizontalRuleBlock,
|
|
1126
|
+
...customTypes
|
|
1127
|
+
}
|
|
1128
|
+
};
|
|
1129
|
+
}, [customBlocks]);
|
|
1130
|
+
return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(PortableText, { value: content, components }) });
|
|
1131
|
+
}
|
|
1132
|
+
function GuidesTabs() {
|
|
1133
|
+
const { configuration, loading } = useHandbookContext();
|
|
1134
|
+
return loading || !isDefined(configuration) ? null : configuration.groups.map((group) => /* @__PURE__ */ jsx(SidebarGroup, { heading: group.title, children: group.guides.map((guide) => /* @__PURE__ */ jsx(SidebarTab, { id: guide._key, label: guide.title, icon: createSanityIcon(BookOpenTextIcon) }, guide._key)) }, group._key));
|
|
1135
|
+
}
|
|
1136
|
+
function GuidesPanels() {
|
|
1137
|
+
const { configuration, loading } = useHandbookContext();
|
|
1138
|
+
return loading || !isDefined(configuration) ? null : configuration.groups.flatMap(
|
|
1139
|
+
(group) => group.guides.map((guide) => /* @__PURE__ */ jsx(ContentPanel, { id: guide._key, title: guide.title, description: guide.description, children: /* @__PURE__ */ jsx(GuideContent, { content: guide.content }) }, guide._key))
|
|
1140
|
+
);
|
|
1141
|
+
}
|
|
1142
|
+
function DocumentTypesOverview() {
|
|
1143
|
+
const { groups, setActiveTab, setSidebarExpanded } = useHandbookContext();
|
|
1144
|
+
return /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: "2.5rem" }, children: groups.map((group) => {
|
|
1145
|
+
const { title, description, documents } = group;
|
|
1146
|
+
return /* @__PURE__ */ jsxs(Stack, { space: 2, children: [
|
|
1147
|
+
/* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
1148
|
+
/* @__PURE__ */ jsx(Flex, { align: "center", paddingY: 1, children: /* @__PURE__ */ jsx(Text, { size: 1, weight: "medium", children: title }) }),
|
|
1149
|
+
isDefined(description) && /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: description })
|
|
1150
|
+
] }),
|
|
1151
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Stack, { space: 2, children: documents.map((document) => {
|
|
1152
|
+
const Icon = typeof document.icon == "function" ? document.icon : void 0, documentDescription = document.handbook?.description ?? (typeof document.description == "string" ? document.description : void 0);
|
|
1153
|
+
return /* @__PURE__ */ jsx(
|
|
1154
|
+
Card,
|
|
1155
|
+
{
|
|
1156
|
+
as: "button",
|
|
1157
|
+
padding: 3,
|
|
1158
|
+
radius: 2,
|
|
1159
|
+
sizing: "border",
|
|
1160
|
+
tone: "inherit",
|
|
1161
|
+
onClick: () => {
|
|
1162
|
+
setActiveTab(document.name), setSidebarExpanded(!1);
|
|
1163
|
+
},
|
|
1164
|
+
style: {
|
|
1165
|
+
width: "calc(100% + 24px)",
|
|
1166
|
+
marginLeft: "-12px",
|
|
1167
|
+
textAlign: "left",
|
|
1168
|
+
border: "none",
|
|
1169
|
+
cursor: "pointer"
|
|
1170
|
+
},
|
|
1171
|
+
children: /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 3, children: [
|
|
1172
|
+
/* @__PURE__ */ jsx(Box, { flex: 1, children: /* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
1173
|
+
/* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, style: { marginLeft: "-4px" }, children: [
|
|
1174
|
+
isDefined(Icon) && /* @__PURE__ */ jsx(
|
|
1175
|
+
"div",
|
|
1176
|
+
{
|
|
1177
|
+
style: {
|
|
1178
|
+
display: "flex",
|
|
1179
|
+
alignItems: "center",
|
|
1180
|
+
justifyContent: "center",
|
|
1181
|
+
width: "25px",
|
|
1182
|
+
height: "25px",
|
|
1183
|
+
color: "var(--card-icon-color)"
|
|
1184
|
+
},
|
|
1185
|
+
children: /* @__PURE__ */ jsx(Icon, {})
|
|
1186
|
+
}
|
|
1187
|
+
),
|
|
1188
|
+
/* @__PURE__ */ jsx(Text, { size: 1, weight: "medium", children: document.title ?? document.name })
|
|
1189
|
+
] }),
|
|
1190
|
+
isDefined(documentDescription) && /* @__PURE__ */ jsx(Box, { paddingBottom: 1, children: /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: documentDescription }) })
|
|
1191
|
+
] }) }),
|
|
1192
|
+
/* @__PURE__ */ jsx(Box, { flex: "none", style: { opacity: 0.5 }, children: /* @__PURE__ */ jsx(Text, { muted: !0, size: 1, children: /* @__PURE__ */ jsx(ChevronRightIcon, {}) }) })
|
|
1193
|
+
] })
|
|
1194
|
+
},
|
|
1195
|
+
document.name
|
|
1196
|
+
);
|
|
1197
|
+
}) }) })
|
|
1198
|
+
] }, title);
|
|
1199
|
+
}) });
|
|
1200
|
+
}
|
|
1201
|
+
function HintRow({ icon, children }) {
|
|
1202
|
+
return /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 3, children: [
|
|
1203
|
+
/* @__PURE__ */ jsx(
|
|
1204
|
+
"div",
|
|
1205
|
+
{
|
|
1206
|
+
style: {
|
|
1207
|
+
display: "flex",
|
|
1208
|
+
alignItems: "center",
|
|
1209
|
+
flexShrink: 0,
|
|
1210
|
+
fontSize: "16px",
|
|
1211
|
+
color: "var(--card-icon-color)"
|
|
1212
|
+
},
|
|
1213
|
+
children: icon
|
|
1214
|
+
}
|
|
1215
|
+
),
|
|
1216
|
+
/* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children })
|
|
1217
|
+
] });
|
|
1218
|
+
}
|
|
1219
|
+
function HowToUse() {
|
|
1220
|
+
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "3rem" }, children: [
|
|
1221
|
+
/* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
1222
|
+
/* @__PURE__ */ jsx(Flex, { align: "center", paddingY: 1, children: /* @__PURE__ */ jsx(Text, { size: 1, weight: "medium", children: "Navigating the Handbook" }) }),
|
|
1223
|
+
/* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: "Use the sidebar to browse document types and guides. Each entry opens a detailed view in the main panel showing field descriptions, examples, and tips." })
|
|
1224
|
+
] }),
|
|
1225
|
+
/* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
1226
|
+
/* @__PURE__ */ jsx(Flex, { align: "center", paddingY: 1, children: /* @__PURE__ */ jsx(Text, { size: 1, weight: "medium", children: "Understanding Fields" }) }),
|
|
1227
|
+
/* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: "Each document type lists its fields with a name and type label. Below the name you will find a description explaining what the field is for and how to use it. Some fields include an example value shown in italics." }),
|
|
1228
|
+
/* @__PURE__ */ jsx(Box, { marginTop: 1, children: /* @__PURE__ */ jsx(Text, { size: 1, muted: !0, style: { fontStyle: "italic" }, children: "e.g. Acme Inc." }) })
|
|
1229
|
+
] }),
|
|
1230
|
+
/* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
1231
|
+
/* @__PURE__ */ jsx(Flex, { align: "center", paddingY: 1, children: /* @__PURE__ */ jsx(Text, { size: 1, weight: "medium", children: "Hints and Warnings" }) }),
|
|
1232
|
+
/* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: "Look for these icons next to field names. Hover or click them to reveal additional guidance." }),
|
|
1233
|
+
/* @__PURE__ */ jsxs(Stack, { space: 3, marginTop: 2, children: [
|
|
1234
|
+
/* @__PURE__ */ jsx(HintRow, { icon: /* @__PURE__ */ jsx(LightbulbIcon, { ...defaultIconProps }), children: "Helpful tips and best practices for content editors." }),
|
|
1235
|
+
/* @__PURE__ */ jsx(HintRow, { icon: /* @__PURE__ */ jsx(InfoIcon, { ...defaultIconProps }), children: "Additional context about how the field is used." }),
|
|
1236
|
+
/* @__PURE__ */ jsx(HintRow, { icon: /* @__PURE__ */ jsx(TriangleAlertIcon, { ...defaultIconProps }), children: "Important warnings about constraints or potential issues." })
|
|
1237
|
+
] })
|
|
1238
|
+
] }),
|
|
1239
|
+
/* @__PURE__ */ jsxs(Stack, { space: 3, children: [
|
|
1240
|
+
/* @__PURE__ */ jsx(Flex, { align: "center", paddingY: 1, children: /* @__PURE__ */ jsx(Text, { size: 1, weight: "medium", children: "Nested Fields" }) }),
|
|
1241
|
+
/* @__PURE__ */ jsx(Text, { size: 1, muted: !0, children: "Some fields contain subfields \u2014 for example, a content block might include a heading and body text. These are shown as collapsible sections you can expand to explore the full structure." })
|
|
1242
|
+
] })
|
|
1243
|
+
] });
|
|
1244
|
+
}
|
|
1245
|
+
const entries = [
|
|
1246
|
+
{
|
|
1247
|
+
name: "how-to-use-this-handbook",
|
|
1248
|
+
title: "How to Use This Handbook",
|
|
1249
|
+
icon: createSanityIcon(BookTextIcon),
|
|
1250
|
+
description: "A quick guide to navigating the Handbook, understanding the information presented for each field, and making the most of tips, hints, and warnings.",
|
|
1251
|
+
component: HowToUse
|
|
1252
|
+
},
|
|
1253
|
+
{
|
|
1254
|
+
name: "document-types",
|
|
1255
|
+
title: "Document Types",
|
|
1256
|
+
icon: createSanityIcon(LayoutGridIcon),
|
|
1257
|
+
description: "An overview of all document types, organised by group. Select a document type to view its fields, descriptions, and examples.",
|
|
1258
|
+
component: DocumentTypesOverview
|
|
1259
|
+
}
|
|
1260
|
+
];
|
|
1261
|
+
function GettingStartedTabs() {
|
|
1262
|
+
return /* @__PURE__ */ jsx(SidebarGroup, { heading: "Getting Started", children: entries.map((entry) => /* @__PURE__ */ jsx(SidebarTab, { id: entry.name, label: entry.title, icon: entry.icon }, entry.name)) });
|
|
1263
|
+
}
|
|
1264
|
+
function GettingStartedPanels() {
|
|
1265
|
+
return entries.map((entry) => {
|
|
1266
|
+
const { name, title, description, component: Component } = entry;
|
|
1267
|
+
return /* @__PURE__ */ jsx(ContentPanel, { id: name, title, description, children: /* @__PURE__ */ jsx(Component, {}) }, name);
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1270
|
+
function Content() {
|
|
1271
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1272
|
+
/* @__PURE__ */ jsx(GettingStartedPanels, {}),
|
|
1273
|
+
/* @__PURE__ */ jsx(DocumentTypesPanels, {}),
|
|
1274
|
+
/* @__PURE__ */ jsx(GuidesPanels, {})
|
|
1275
|
+
] });
|
|
1276
|
+
}
|
|
1277
|
+
function Sidebar() {
|
|
1278
|
+
return /* @__PURE__ */ jsxs(Box, { paddingX: 3, paddingBottom: 1, children: [
|
|
1279
|
+
/* @__PURE__ */ jsx(GettingStartedTabs, {}),
|
|
1280
|
+
/* @__PURE__ */ jsx(DocumentTypesTabs, {}),
|
|
1281
|
+
/* @__PURE__ */ jsx(GuidesTabs, {})
|
|
1282
|
+
] });
|
|
1283
|
+
}
|
|
1284
|
+
const defaultUndocumentedFieldMessage = "This field has not been documented yet. Contact your development team for guidance.", wideBreakpoint = 920, narrowBreakpoint = 480, sidebarWidth = 320, collapsedSidebarWidth = 51;
|
|
1285
|
+
function CollapsedSidebar() {
|
|
1286
|
+
const { sidebarTitle, setSidebarExpanded } = useHandbookContext();
|
|
1287
|
+
return /* @__PURE__ */ jsx(
|
|
1288
|
+
"div",
|
|
1289
|
+
{
|
|
1290
|
+
style: {
|
|
1291
|
+
width: `${collapsedSidebarWidth}px`,
|
|
1292
|
+
minWidth: `${collapsedSidebarWidth}px`,
|
|
1293
|
+
overflow: "hidden",
|
|
1294
|
+
borderRight: "1px solid var(--card-border-color)"
|
|
1295
|
+
},
|
|
1296
|
+
children: /* @__PURE__ */ jsx(Card, { tone: "inherit", "data-collapsed": "", children: /* @__PURE__ */ jsx(
|
|
1297
|
+
Flex,
|
|
1298
|
+
{
|
|
1299
|
+
direction: "column",
|
|
1300
|
+
padding: 3,
|
|
1301
|
+
onClick: () => {
|
|
1302
|
+
setSidebarExpanded(!0);
|
|
1303
|
+
},
|
|
1304
|
+
style: {
|
|
1305
|
+
width: "100vh",
|
|
1306
|
+
boxSizing: "border-box",
|
|
1307
|
+
transform: "rotate(90deg)",
|
|
1308
|
+
transformOrigin: `calc(${collapsedSidebarWidth}px / 2)`,
|
|
1309
|
+
cursor: "pointer"
|
|
1310
|
+
},
|
|
1311
|
+
children: /* @__PURE__ */ jsx(Flex, { align: "flex-start", children: /* @__PURE__ */ jsx(Card, { flex: 1, paddingLeft: 2, padding: 2, children: /* @__PURE__ */ jsx(Flex, { align: "center", gap: 1, children: /* @__PURE__ */ jsx(Text, { size: 1, textOverflow: "ellipsis", weight: "semibold", style: { cursor: "default", outline: "none" }, children: sidebarTitle }) }) }) })
|
|
1312
|
+
}
|
|
1313
|
+
) })
|
|
1314
|
+
}
|
|
1315
|
+
);
|
|
1316
|
+
}
|
|
1317
|
+
function NarrowContentHeader() {
|
|
1318
|
+
const { sidebarTitle, setSidebarExpanded } = useHandbookContext();
|
|
1319
|
+
return /* @__PURE__ */ jsxs(Flex, { align: "center", gap: 2, padding: 3, children: [
|
|
1320
|
+
/* @__PURE__ */ jsx(
|
|
1321
|
+
Button,
|
|
1322
|
+
{
|
|
1323
|
+
icon: ArrowLeftIcon,
|
|
1324
|
+
mode: "bleed",
|
|
1325
|
+
onClick: () => {
|
|
1326
|
+
setSidebarExpanded(!0);
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
),
|
|
1330
|
+
/* @__PURE__ */ jsx(Text, { size: 1, weight: "semibold", children: sidebarTitle })
|
|
1331
|
+
] });
|
|
1332
|
+
}
|
|
1333
|
+
function ExpandedSidebar() {
|
|
1334
|
+
const { sidebarTitle } = useHandbookContext();
|
|
1335
|
+
return /* @__PURE__ */ jsxs(
|
|
1336
|
+
Card,
|
|
1337
|
+
{
|
|
1338
|
+
borderRight: !0,
|
|
1339
|
+
style: { display: "flex", flexDirection: "column", width: `${sidebarWidth}px`, minWidth: `${sidebarWidth}px` },
|
|
1340
|
+
children: [
|
|
1341
|
+
/* @__PURE__ */ jsx(PaneHeader, { title: sidebarTitle }),
|
|
1342
|
+
/* @__PURE__ */ jsx("div", { style: { flex: 1, paddingBottom: "4px", overflowY: "auto" }, children: /* @__PURE__ */ jsx(Sidebar, {}) })
|
|
1343
|
+
]
|
|
1344
|
+
}
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
function NarrowLayout() {
|
|
1348
|
+
const { sidebarTitle, sidebarExpanded } = useHandbookContext();
|
|
1349
|
+
return sidebarExpanded ? /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", flex: 1 }, children: [
|
|
1350
|
+
/* @__PURE__ */ jsx(PaneHeader, { title: sidebarTitle }),
|
|
1351
|
+
/* @__PURE__ */ jsx("div", { style: { flex: 1, paddingBottom: "4px", overflowY: "auto" }, children: /* @__PURE__ */ jsx(Sidebar, {}) })
|
|
1352
|
+
] }) : /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", flex: 1 }, children: [
|
|
1353
|
+
/* @__PURE__ */ jsx(NarrowContentHeader, {}),
|
|
1354
|
+
/* @__PURE__ */ jsx(Content, {})
|
|
1355
|
+
] });
|
|
1356
|
+
}
|
|
1357
|
+
function MediumLayout() {
|
|
1358
|
+
const { sidebarExpanded, setSidebarExpanded } = useHandbookContext();
|
|
1359
|
+
return sidebarExpanded ? /* @__PURE__ */ jsxs(Flex, { style: { flex: 1 }, children: [
|
|
1360
|
+
/* @__PURE__ */ jsx(ExpandedSidebar, {}),
|
|
1361
|
+
/* @__PURE__ */ jsx(
|
|
1362
|
+
Card,
|
|
1363
|
+
{
|
|
1364
|
+
flex: 1,
|
|
1365
|
+
padding: 3,
|
|
1366
|
+
onClick: () => {
|
|
1367
|
+
setSidebarExpanded(!1);
|
|
1368
|
+
},
|
|
1369
|
+
style: { opacity: 0.4, overflowY: "auto", cursor: "pointer" }
|
|
1370
|
+
}
|
|
1371
|
+
)
|
|
1372
|
+
] }) : /* @__PURE__ */ jsxs(Flex, { style: { flex: 1 }, children: [
|
|
1373
|
+
/* @__PURE__ */ jsx(CollapsedSidebar, {}),
|
|
1374
|
+
/* @__PURE__ */ jsx(Card, { flex: 1, style: { display: "flex", flexDirection: "column" }, children: /* @__PURE__ */ jsx(Content, {}) })
|
|
1375
|
+
] });
|
|
1376
|
+
}
|
|
1377
|
+
function WideLayout() {
|
|
1378
|
+
return /* @__PURE__ */ jsxs(Flex, { style: { flex: 1 }, children: [
|
|
1379
|
+
/* @__PURE__ */ jsx(ExpandedSidebar, {}),
|
|
1380
|
+
/* @__PURE__ */ jsx(Card, { flex: 1, style: { display: "flex", flexDirection: "column" }, children: /* @__PURE__ */ jsx(Content, {}) })
|
|
1381
|
+
] });
|
|
1382
|
+
}
|
|
1383
|
+
function HandbookLayout() {
|
|
1384
|
+
const { reference: containerReference, width: containerWidth } = useContainerWidth(), isNarrow = isDefined(containerWidth) && containerWidth < narrowBreakpoint, isMedium = isDefined(containerWidth) && containerWidth >= narrowBreakpoint && containerWidth < wideBreakpoint;
|
|
1385
|
+
function renderLayout() {
|
|
1386
|
+
return isNarrow ? /* @__PURE__ */ jsx(NarrowLayout, {}) : isMedium ? /* @__PURE__ */ jsx(MediumLayout, {}) : /* @__PURE__ */ jsx(WideLayout, {});
|
|
1387
|
+
}
|
|
1388
|
+
return /* @__PURE__ */ jsx("div", { ref: containerReference, style: { display: "flex", flexDirection: "column", height: "100%" }, children: renderLayout() });
|
|
1389
|
+
}
|
|
1390
|
+
function createHandbookTool(config) {
|
|
1391
|
+
return function() {
|
|
1392
|
+
return /* @__PURE__ */ jsx(
|
|
1393
|
+
HandbookProvider,
|
|
1394
|
+
{
|
|
1395
|
+
sidebarTitle: config.sidebarTitle ?? "Handbook",
|
|
1396
|
+
groups: config.groups ?? [],
|
|
1397
|
+
blocks: config.blocks ?? [],
|
|
1398
|
+
undocumentedFieldMessage: config.undocumentedFieldMessage ?? defaultUndocumentedFieldMessage,
|
|
1399
|
+
children: /* @__PURE__ */ jsx(HandbookLayout, {})
|
|
1400
|
+
}
|
|
1401
|
+
);
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
let registeredEditors;
|
|
1405
|
+
function setHandbookEditors(editors) {
|
|
1406
|
+
registeredEditors = editors;
|
|
1407
|
+
}
|
|
1408
|
+
function handbookStructure(structureBuilder, context) {
|
|
1409
|
+
if (!isPermittedEditor(registeredEditors, context.currentUser?.email)) return [];
|
|
1410
|
+
const { listItem, document, documentTypeList } = structureBuilder;
|
|
1411
|
+
return [
|
|
1412
|
+
listItem().title("Handbook").icon(createSanityIcon(BookTextIcon)).child(document().id("handbook.handbook").schemaType("handbook.handbook").title("Handbook")),
|
|
1413
|
+
listItem().title("Handbook Guides").icon(createSanityIcon(BookOpenTextIcon)).child(documentTypeList("handbook.guide").title("Handbook Guides"))
|
|
1414
|
+
];
|
|
1415
|
+
}
|
|
1416
|
+
function CalloutPreview({ variant, text }) {
|
|
1417
|
+
return /* @__PURE__ */ jsx(CalloutContent, { variant: variant ?? "tip", children: text });
|
|
1418
|
+
}
|
|
1419
|
+
function ParagraphDecorator({ children }) {
|
|
1420
|
+
return /* @__PURE__ */ jsx("span", { style: { fontSize: "0.8125rem" }, children });
|
|
1421
|
+
}
|
|
1422
|
+
function HeadingDecorator({ children }) {
|
|
1423
|
+
return /* @__PURE__ */ jsx("span", { style: { fontSize: "0.8125rem", fontWeight: "500" }, children });
|
|
1424
|
+
}
|
|
1425
|
+
function BlockquoteDecorator({ children }) {
|
|
1426
|
+
return /* @__PURE__ */ jsx(
|
|
1427
|
+
"div",
|
|
1428
|
+
{
|
|
1429
|
+
style: {
|
|
1430
|
+
paddingLeft: "0.75em",
|
|
1431
|
+
fontSize: "0.8125rem",
|
|
1432
|
+
fontStyle: "italic",
|
|
1433
|
+
borderLeft: "2px solid var(--card-border-color)"
|
|
1434
|
+
},
|
|
1435
|
+
children
|
|
1436
|
+
}
|
|
1437
|
+
);
|
|
1438
|
+
}
|
|
1439
|
+
function BulletListDecorator({ children }) {
|
|
1440
|
+
return /* @__PURE__ */ jsx("div", { style: { marginTop: "-0.25em", fontSize: "0.8125rem" }, children });
|
|
1441
|
+
}
|
|
1442
|
+
function NumberListDecorator({ children }) {
|
|
1443
|
+
return /* @__PURE__ */ jsx("div", { style: { marginTop: "-0.25em", fontSize: "0.8125rem" }, children });
|
|
1444
|
+
}
|
|
1445
|
+
function InlineCodeDecorator({ children }) {
|
|
1446
|
+
return /* @__PURE__ */ jsx(
|
|
1447
|
+
"code",
|
|
1448
|
+
{
|
|
1449
|
+
style: {
|
|
1450
|
+
padding: "0.125rem 0.25rem",
|
|
1451
|
+
fontSize: "0.8125rem",
|
|
1452
|
+
color: "var(--card-muted-fg-color)",
|
|
1453
|
+
backgroundColor: "var(--card-code-bg-color)",
|
|
1454
|
+
borderRadius: "0.125rem"
|
|
1455
|
+
},
|
|
1456
|
+
children
|
|
1457
|
+
}
|
|
1458
|
+
);
|
|
1459
|
+
}
|
|
1460
|
+
const linkAnnotation = defineArrayMember({
|
|
1461
|
+
name: "link",
|
|
1462
|
+
type: "object",
|
|
1463
|
+
fields: [
|
|
1464
|
+
defineField({
|
|
1465
|
+
name: "href",
|
|
1466
|
+
title: "URL",
|
|
1467
|
+
type: "url",
|
|
1468
|
+
description: "Web address for this link. Supports http, https, mailto, and tel schemes.",
|
|
1469
|
+
validation: (rule) => rule.uri({ scheme: ["http", "https", "mailto", "tel"] })
|
|
1470
|
+
})
|
|
1471
|
+
]
|
|
1472
|
+
}), handbookCalloutType = defineType({
|
|
1473
|
+
name: "handbook.callout",
|
|
1474
|
+
title: "Callout",
|
|
1475
|
+
type: "object",
|
|
1476
|
+
icon: createSanityIcon(MessageCircleQuestionMarkIcon),
|
|
1477
|
+
description: "A highlighted message block used to draw attention to tips, supplementary information, or warnings.",
|
|
1478
|
+
preview: {
|
|
1479
|
+
select: { variant: "variant", body: "body" },
|
|
1480
|
+
prepare({ variant, body }) {
|
|
1481
|
+
const text = Array.isArray(body) ? body.flatMap((block) => (block.children ?? []).map((child) => child.text ?? "")).join("") : "";
|
|
1482
|
+
return { variant, text };
|
|
1483
|
+
}
|
|
1484
|
+
},
|
|
1485
|
+
components: {
|
|
1486
|
+
preview: CalloutPreview
|
|
1487
|
+
},
|
|
1488
|
+
fields: [
|
|
1489
|
+
defineField({
|
|
1490
|
+
name: "variant",
|
|
1491
|
+
title: "Variant",
|
|
1492
|
+
type: "string",
|
|
1493
|
+
description: "Visual style and intent of the callout.",
|
|
1494
|
+
initialValue: "tip",
|
|
1495
|
+
validation: (rule) => rule.required(),
|
|
1496
|
+
options: {
|
|
1497
|
+
list: [
|
|
1498
|
+
{ title: "Tip", value: "tip" },
|
|
1499
|
+
{ title: "Information", value: "info" },
|
|
1500
|
+
{ title: "Warning", value: "warning" }
|
|
1501
|
+
],
|
|
1502
|
+
layout: "radio",
|
|
1503
|
+
direction: "horizontal"
|
|
1504
|
+
}
|
|
1505
|
+
}),
|
|
1506
|
+
defineField({
|
|
1507
|
+
name: "body",
|
|
1508
|
+
title: "Body",
|
|
1509
|
+
type: "array",
|
|
1510
|
+
description: "Content displayed inside the callout. Supports inline formatting and links.",
|
|
1511
|
+
of: [
|
|
1512
|
+
defineArrayMember({
|
|
1513
|
+
type: "block",
|
|
1514
|
+
styles: [{ title: "Normal", value: "normal", component: ParagraphDecorator }],
|
|
1515
|
+
marks: {
|
|
1516
|
+
decorators: [
|
|
1517
|
+
{ title: "Bold", value: "strong" },
|
|
1518
|
+
{ title: "Italic", value: "em" },
|
|
1519
|
+
{ title: "Strikethrough", value: "strike-through" },
|
|
1520
|
+
{ title: "Code", value: "code", component: InlineCodeDecorator }
|
|
1521
|
+
],
|
|
1522
|
+
annotations: [linkAnnotation]
|
|
1523
|
+
},
|
|
1524
|
+
lists: []
|
|
1525
|
+
})
|
|
1526
|
+
]
|
|
1527
|
+
})
|
|
1528
|
+
]
|
|
1529
|
+
});
|
|
1530
|
+
function CodePreview({ code, language }) {
|
|
1531
|
+
return isDefined(code) ? /* @__PURE__ */ jsx(CodeContent, { code, language }) : null;
|
|
1532
|
+
}
|
|
1533
|
+
const handbookCodeType = defineType({
|
|
1534
|
+
name: "handbook.code",
|
|
1535
|
+
title: "Code",
|
|
1536
|
+
type: "object",
|
|
1537
|
+
icon: createSanityIcon(CodeIcon),
|
|
1538
|
+
description: "A syntax-highlighted code block with language selection.",
|
|
1539
|
+
preview: {
|
|
1540
|
+
select: { code: "code", language: "language" }
|
|
1541
|
+
},
|
|
1542
|
+
components: {
|
|
1543
|
+
preview: CodePreview
|
|
1544
|
+
},
|
|
1545
|
+
fields: [
|
|
1546
|
+
defineField({
|
|
1547
|
+
name: "code",
|
|
1548
|
+
title: "Code",
|
|
1549
|
+
type: "text",
|
|
1550
|
+
description: "The source code to display."
|
|
1551
|
+
}),
|
|
1552
|
+
defineField({
|
|
1553
|
+
name: "language",
|
|
1554
|
+
title: "Language",
|
|
1555
|
+
type: "string",
|
|
1556
|
+
description: "Programming language used for syntax highlighting.",
|
|
1557
|
+
options: {
|
|
1558
|
+
list: [
|
|
1559
|
+
{ title: "CSS", value: "css" },
|
|
1560
|
+
{ title: "GROQ", value: "groq" },
|
|
1561
|
+
{ title: "HTML", value: "html" },
|
|
1562
|
+
{ title: "JavaScript", value: "javascript" },
|
|
1563
|
+
{ title: "JSON", value: "json" },
|
|
1564
|
+
{ title: "Plain Text", value: "text" },
|
|
1565
|
+
{ title: "React", value: "tsx" },
|
|
1566
|
+
{ title: "Shell", value: "shell" },
|
|
1567
|
+
{ title: "TypeScript", value: "typescript" }
|
|
1568
|
+
]
|
|
1569
|
+
}
|
|
1570
|
+
})
|
|
1571
|
+
]
|
|
1572
|
+
});
|
|
1573
|
+
function HorizontalRulePreview() {
|
|
1574
|
+
return /* @__PURE__ */ jsx(HorizontalRuleContent, { marginBlock: "0.5rem" });
|
|
1575
|
+
}
|
|
1576
|
+
const handbookHorizontalRuleType = defineType({
|
|
1577
|
+
name: "handbook.horizontalRule",
|
|
1578
|
+
title: "Horizontal Rule",
|
|
1579
|
+
type: "object",
|
|
1580
|
+
icon: createSanityIcon(MinusIcon),
|
|
1581
|
+
description: "A visual divider between sections of content.",
|
|
1582
|
+
preview: {
|
|
1583
|
+
prepare() {
|
|
1584
|
+
return { title: "Horizontal Rule" };
|
|
1585
|
+
}
|
|
1586
|
+
},
|
|
1587
|
+
components: {
|
|
1588
|
+
preview: HorizontalRulePreview
|
|
1589
|
+
},
|
|
1590
|
+
fields: [
|
|
1591
|
+
defineField({
|
|
1592
|
+
name: "style",
|
|
1593
|
+
title: "Style",
|
|
1594
|
+
type: "string",
|
|
1595
|
+
description: "Presentation style for the divider.",
|
|
1596
|
+
hidden: !0,
|
|
1597
|
+
initialValue: "default"
|
|
1598
|
+
})
|
|
1599
|
+
]
|
|
1600
|
+
});
|
|
1601
|
+
function ImagePreview({ url, alt, caption }) {
|
|
1602
|
+
return isDefined(url) ? /* @__PURE__ */ jsx("div", { style: { padding: "0.5rem" }, children: /* @__PURE__ */ jsx(ImageContent, { url, alt, caption }) }) : null;
|
|
1603
|
+
}
|
|
1604
|
+
const handbookImageType = defineType({
|
|
1605
|
+
name: "handbook.image",
|
|
1606
|
+
title: "Image",
|
|
1607
|
+
type: "object",
|
|
1608
|
+
icon: createSanityIcon(ImageIcon),
|
|
1609
|
+
description: "An image with optional caption and alternative text for accessibility.",
|
|
1610
|
+
preview: {
|
|
1611
|
+
select: { caption: "caption", alt: "alt", url: "asset.asset.url" }
|
|
1612
|
+
},
|
|
1613
|
+
components: {
|
|
1614
|
+
preview: ImagePreview
|
|
1615
|
+
},
|
|
1616
|
+
fields: [
|
|
1617
|
+
defineField({
|
|
1618
|
+
name: "asset",
|
|
1619
|
+
title: "Image",
|
|
1620
|
+
type: "image",
|
|
1621
|
+
description: "Upload or select an image from the media library.",
|
|
1622
|
+
options: { hotspot: !0 }
|
|
1623
|
+
}),
|
|
1624
|
+
defineField({
|
|
1625
|
+
name: "caption",
|
|
1626
|
+
title: "Caption",
|
|
1627
|
+
type: "string",
|
|
1628
|
+
description: "Text displayed beneath the image in the handbook."
|
|
1629
|
+
}),
|
|
1630
|
+
defineField({
|
|
1631
|
+
name: "alt",
|
|
1632
|
+
title: "Alternative Text",
|
|
1633
|
+
type: "string",
|
|
1634
|
+
description: "Describes the image for screen readers and accessibility tools."
|
|
1635
|
+
})
|
|
1636
|
+
]
|
|
1637
|
+
});
|
|
1638
|
+
function VideoPreview({ url, caption }) {
|
|
1639
|
+
return isDefined(url) ? /* @__PURE__ */ jsx("div", { style: { padding: "0.5rem" }, children: /* @__PURE__ */ jsx(VideoContent, { url, caption }) }) : null;
|
|
1640
|
+
}
|
|
1641
|
+
const handbookVideoType = defineType({
|
|
1642
|
+
name: "handbook.video",
|
|
1643
|
+
title: "Video",
|
|
1644
|
+
type: "object",
|
|
1645
|
+
icon: createSanityIcon(VideoIcon),
|
|
1646
|
+
description: "An uploaded video with optional caption.",
|
|
1647
|
+
preview: {
|
|
1648
|
+
select: { caption: "caption", url: "asset.asset.url" }
|
|
1649
|
+
},
|
|
1650
|
+
components: {
|
|
1651
|
+
preview: VideoPreview
|
|
1652
|
+
},
|
|
1653
|
+
fields: [
|
|
1654
|
+
defineField({
|
|
1655
|
+
name: "asset",
|
|
1656
|
+
title: "Video",
|
|
1657
|
+
type: "file",
|
|
1658
|
+
description: "Upload or select a video from the media library.",
|
|
1659
|
+
options: { accept: "video/*" }
|
|
1660
|
+
}),
|
|
1661
|
+
defineField({
|
|
1662
|
+
name: "caption",
|
|
1663
|
+
title: "Caption",
|
|
1664
|
+
type: "string",
|
|
1665
|
+
description: "Text displayed beneath the video in the handbook."
|
|
1666
|
+
})
|
|
1667
|
+
]
|
|
1668
|
+
});
|
|
1669
|
+
function createGuideContentField(customBlocks = []) {
|
|
1670
|
+
return defineField({
|
|
1671
|
+
name: "content",
|
|
1672
|
+
title: "Content",
|
|
1673
|
+
type: "array",
|
|
1674
|
+
description: "Rich text content for this guide. Supports headings, lists, images, code blocks, and custom blocks.",
|
|
1675
|
+
of: [
|
|
1676
|
+
defineArrayMember({
|
|
1677
|
+
name: "block",
|
|
1678
|
+
type: "block",
|
|
1679
|
+
styles: [
|
|
1680
|
+
{ title: "Paragraph", value: "normal", component: ParagraphDecorator },
|
|
1681
|
+
{ title: "Heading", value: "h2", component: HeadingDecorator },
|
|
1682
|
+
{ title: "Blockquote", value: "blockquote", component: BlockquoteDecorator }
|
|
1683
|
+
],
|
|
1684
|
+
marks: {
|
|
1685
|
+
decorators: [
|
|
1686
|
+
{ title: "Bold", value: "strong" },
|
|
1687
|
+
{ title: "Italic", value: "em" },
|
|
1688
|
+
{ title: "Strikethrough", value: "strike-through" },
|
|
1689
|
+
{ title: "Code", value: "code", component: InlineCodeDecorator }
|
|
1690
|
+
],
|
|
1691
|
+
annotations: [linkAnnotation]
|
|
1692
|
+
},
|
|
1693
|
+
lists: [
|
|
1694
|
+
{ title: "Bullet", value: "bullet", component: BulletListDecorator },
|
|
1695
|
+
{ title: "Number", value: "number", component: NumberListDecorator }
|
|
1696
|
+
]
|
|
1697
|
+
}),
|
|
1698
|
+
defineArrayMember({ type: "handbook.image" }),
|
|
1699
|
+
defineArrayMember({ type: "handbook.video" }),
|
|
1700
|
+
defineArrayMember({ type: "handbook.code" }),
|
|
1701
|
+
defineArrayMember({ type: "handbook.callout" }),
|
|
1702
|
+
defineArrayMember({ type: "handbook.horizontalRule" }),
|
|
1703
|
+
...customBlocks.map((block) => defineArrayMember({ type: block.schema.name }))
|
|
1704
|
+
]
|
|
1705
|
+
});
|
|
1706
|
+
}
|
|
1707
|
+
function createHandbookGuideType(customBlocks = []) {
|
|
1708
|
+
return defineType({
|
|
1709
|
+
name: "handbook.guide",
|
|
1710
|
+
title: "Handbook Guide",
|
|
1711
|
+
type: "document",
|
|
1712
|
+
icon: createSanityIcon(BookOpenTextIcon),
|
|
1713
|
+
description: "A handbook page authored with rich text, images, and custom blocks.",
|
|
1714
|
+
preview: {
|
|
1715
|
+
select: { title: "title", description: "description" },
|
|
1716
|
+
prepare({ title, description }) {
|
|
1717
|
+
return {
|
|
1718
|
+
title: title ?? "Untitled Guide",
|
|
1719
|
+
subtitle: description
|
|
1720
|
+
};
|
|
1721
|
+
}
|
|
1722
|
+
},
|
|
1723
|
+
fields: [
|
|
1724
|
+
defineField({
|
|
1725
|
+
name: "title",
|
|
1726
|
+
title: "Title",
|
|
1727
|
+
type: "string",
|
|
1728
|
+
description: "Display title shown in the handbook sidebar and as the page heading.",
|
|
1729
|
+
validation: (rule) => rule.required()
|
|
1730
|
+
}),
|
|
1731
|
+
defineField({
|
|
1732
|
+
name: "description",
|
|
1733
|
+
title: "Description",
|
|
1734
|
+
type: "string",
|
|
1735
|
+
description: "Brief summary displayed beneath the guide heading."
|
|
1736
|
+
}),
|
|
1737
|
+
createGuideContentField(customBlocks)
|
|
1738
|
+
]
|
|
1739
|
+
});
|
|
1740
|
+
}
|
|
1741
|
+
const handbookType = defineType({
|
|
1742
|
+
name: "handbook.handbook",
|
|
1743
|
+
title: "Handbook",
|
|
1744
|
+
type: "document",
|
|
1745
|
+
icon: createSanityIcon(BookTextIcon),
|
|
1746
|
+
description: "Defines the structure and ordering of the handbook, including groups and their guides.",
|
|
1747
|
+
preview: {
|
|
1748
|
+
prepare() {
|
|
1749
|
+
return { title: "Handbook" };
|
|
1750
|
+
}
|
|
1751
|
+
},
|
|
1752
|
+
fields: [
|
|
1753
|
+
defineField({
|
|
1754
|
+
name: "groups",
|
|
1755
|
+
title: "Groups",
|
|
1756
|
+
type: "array",
|
|
1757
|
+
description: "Ordered list of groups displayed in the handbook sidebar. Each group contains a title and an ordered list of guide references.",
|
|
1758
|
+
of: [
|
|
1759
|
+
defineArrayMember({
|
|
1760
|
+
name: "group",
|
|
1761
|
+
type: "object",
|
|
1762
|
+
icon: createSanityIcon(ListPlusIcon),
|
|
1763
|
+
fields: [
|
|
1764
|
+
defineField({
|
|
1765
|
+
name: "title",
|
|
1766
|
+
title: "Title",
|
|
1767
|
+
type: "string",
|
|
1768
|
+
description: "Heading displayed above this group of guides in the sidebar.",
|
|
1769
|
+
validation: (rule) => rule.required()
|
|
1770
|
+
}),
|
|
1771
|
+
defineField({
|
|
1772
|
+
name: "guides",
|
|
1773
|
+
title: "Guides",
|
|
1774
|
+
type: "array",
|
|
1775
|
+
description: "Ordered list of guides within this group. Drag to reorder.",
|
|
1776
|
+
of: [
|
|
1777
|
+
defineArrayMember({
|
|
1778
|
+
type: "reference",
|
|
1779
|
+
to: [{ type: "handbook.guide" }]
|
|
1780
|
+
})
|
|
1781
|
+
]
|
|
1782
|
+
})
|
|
1783
|
+
],
|
|
1784
|
+
preview: {
|
|
1785
|
+
select: { title: "title", guide0: "guides.0.title", guide1: "guides.1.title", guide2: "guides.2.title" },
|
|
1786
|
+
prepare({
|
|
1787
|
+
title,
|
|
1788
|
+
guide0,
|
|
1789
|
+
guide1,
|
|
1790
|
+
guide2
|
|
1791
|
+
}) {
|
|
1792
|
+
const shown = [guide0, guide1, guide2].filter((guide) => isDefined(guide)).slice(0, 2), hasMore = isDefined(guide2);
|
|
1793
|
+
function formatSubtitle() {
|
|
1794
|
+
return isDefined(shown) ? hasMore ? `${shown.join(", ")}, and others` : shown.join(" and ") : "No guides selected";
|
|
1795
|
+
}
|
|
1796
|
+
return {
|
|
1797
|
+
title: title ?? "Untitled Group",
|
|
1798
|
+
subtitle: formatSubtitle()
|
|
1799
|
+
};
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
})
|
|
1803
|
+
]
|
|
1804
|
+
})
|
|
1805
|
+
]
|
|
1806
|
+
});
|
|
1807
|
+
function useIsHandbookEditor(editors) {
|
|
1808
|
+
const user = useCurrentUser();
|
|
1809
|
+
return isPermittedEditor(editors, user?.email);
|
|
1810
|
+
}
|
|
1811
|
+
const handbookSingletonTypes = /* @__PURE__ */ new Set(["handbook.handbook"]), handbookDocumentTypes = /* @__PURE__ */ new Set(["handbook.handbook", "handbook.guide"]);
|
|
1812
|
+
function isHandbookTemplate(templateId, typeSet) {
|
|
1813
|
+
return typeSet.has(templateId) || [...typeSet].some((type) => templateId.startsWith(`${type}-`));
|
|
1814
|
+
}
|
|
1815
|
+
function validateGroup(index, group) {
|
|
1816
|
+
const prefix = `sanity-handbook: groups[${index}]`;
|
|
1817
|
+
if (!isDefined(group.title)) throw new Error(`${prefix} is missing a required "title" property.`);
|
|
1818
|
+
if (!Array.isArray(group.documents) || group.documents.length === 0)
|
|
1819
|
+
throw new Error(`${prefix} must have a non-empty "documents" array.`);
|
|
1820
|
+
for (const document of group.documents) {
|
|
1821
|
+
if (!isDefined(document.name))
|
|
1822
|
+
throw new Error(`${prefix}.documents contains a document without a "name" property.`);
|
|
1823
|
+
if (!Array.isArray(document.fields))
|
|
1824
|
+
throw new TypeError(`${prefix}.documents.${document.name} is missing a "fields" array.`);
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
function validateConfig(config) {
|
|
1828
|
+
if (config.groups)
|
|
1829
|
+
for (const [index, group] of config.groups.entries())
|
|
1830
|
+
validateGroup(index, group);
|
|
1831
|
+
}
|
|
1832
|
+
function handbookPlugin(config) {
|
|
1833
|
+
config && validateConfig(config);
|
|
1834
|
+
const customBlocks = config?.blocks ?? [], editors = config?.editors;
|
|
1835
|
+
setHandbookEditors(editors);
|
|
1836
|
+
const guideType = createHandbookGuideType(customBlocks);
|
|
1837
|
+
return definePlugin(() => ({
|
|
1838
|
+
name: "@driftime/sanity-plugin-handbook",
|
|
1839
|
+
schema: {
|
|
1840
|
+
types: [
|
|
1841
|
+
handbookType,
|
|
1842
|
+
guideType,
|
|
1843
|
+
handbookImageType,
|
|
1844
|
+
handbookVideoType,
|
|
1845
|
+
handbookCodeType,
|
|
1846
|
+
handbookCalloutType,
|
|
1847
|
+
handbookHorizontalRuleType,
|
|
1848
|
+
...customBlocks.map((block) => block.schema)
|
|
1849
|
+
]
|
|
1850
|
+
},
|
|
1851
|
+
document: {
|
|
1852
|
+
newDocumentOptions: (templateItems, { creationContext, currentUser }) => creationContext.type === "global" || creationContext.type === "structure" ? isPermittedEditor(editors, currentUser?.email) ? templateItems.filter((item) => !isHandbookTemplate(item.templateId, handbookSingletonTypes)) : templateItems.filter((item) => !isHandbookTemplate(item.templateId, handbookDocumentTypes)) : templateItems,
|
|
1853
|
+
actions: (actionComponents, { schemaType }) => handbookSingletonTypes.has(schemaType) ? actionComponents.filter(({ action }) => action !== "duplicate") : actionComponents
|
|
1854
|
+
},
|
|
1855
|
+
tools: [
|
|
1856
|
+
{
|
|
1857
|
+
name: "handbook",
|
|
1858
|
+
title: config?.title ?? "Handbook",
|
|
1859
|
+
icon: createSanityIcon(BookTextIcon),
|
|
1860
|
+
component: createHandbookTool({ ...config, blocks: customBlocks })
|
|
1861
|
+
}
|
|
1862
|
+
]
|
|
1863
|
+
}))();
|
|
1864
|
+
}
|
|
1865
|
+
export {
|
|
1866
|
+
handbookPlugin,
|
|
1867
|
+
handbookStructure,
|
|
1868
|
+
useIsHandbookEditor
|
|
1869
|
+
};
|
|
1870
|
+
//# sourceMappingURL=index.js.map
|