@driftime/sanity-plugin-handbook 0.2.1 → 0.3.1

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