bsmnt 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/application/add-integration/index.d.ts.map +1 -1
- package/dist/application/add-integration/index.js +23 -0
- package/dist/application/add-integration/index.js.map +1 -1
- package/dist/domain/consent.d.ts +17 -0
- package/dist/domain/consent.d.ts.map +1 -0
- package/dist/domain/consent.js +13 -0
- package/dist/domain/consent.js.map +1 -0
- package/dist/domain/integration.d.ts +19 -2
- package/dist/domain/integration.d.ts.map +1 -1
- package/dist/domain/integration.js +10 -1
- package/dist/domain/integration.js.map +1 -1
- package/dist/domain/jobs.d.ts +17 -0
- package/dist/domain/jobs.d.ts.map +1 -0
- package/dist/domain/jobs.js +13 -0
- package/dist/domain/jobs.js.map +1 -0
- package/dist/domain/skills.d.ts +23 -2
- package/dist/domain/skills.d.ts.map +1 -1
- package/dist/domain/skills.js +23 -2
- package/dist/domain/skills.js.map +1 -1
- package/dist/domain/templates.d.ts +7 -1
- package/dist/domain/templates.d.ts.map +1 -1
- package/dist/domain/templates.js +2 -0
- package/dist/domain/templates.js.map +1 -1
- package/dist/index.js +34 -9
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/create/setup-agent.d.ts.map +1 -1
- package/dist/infrastructure/create/setup-agent.js +11 -4
- package/dist/infrastructure/create/setup-agent.js.map +1 -1
- package/dist/infrastructure/integrations/inject-integration.d.ts.map +1 -1
- package/dist/infrastructure/integrations/inject-integration.js +36 -2
- package/dist/infrastructure/integrations/inject-integration.js.map +1 -1
- package/dist/modules/features/consent/ketch/config.d.ts +18 -0
- package/dist/modules/features/consent/ketch/config.d.ts.map +1 -0
- package/dist/modules/features/consent/ketch/config.js +24 -0
- package/dist/modules/features/consent/ketch/config.js.map +1 -0
- package/dist/modules/features/consent/ketch/mergers/biome.d.ts +16 -0
- package/dist/modules/features/consent/ketch/mergers/biome.d.ts.map +1 -0
- package/dist/modules/features/consent/ketch/mergers/biome.js +31 -0
- package/dist/modules/features/consent/ketch/mergers/biome.js.map +1 -0
- package/dist/modules/features/consent/ketch/mergers/check-integration.d.ts +13 -0
- package/dist/modules/features/consent/ketch/mergers/check-integration.d.ts.map +1 -0
- package/dist/modules/features/consent/ketch/mergers/check-integration.js +64 -0
- package/dist/modules/features/consent/ketch/mergers/check-integration.js.map +1 -0
- package/dist/modules/features/consent/ketch/mergers/env-example.d.ts +12 -0
- package/dist/modules/features/consent/ketch/mergers/env-example.d.ts.map +1 -0
- package/dist/modules/features/consent/ketch/mergers/env-example.js +28 -0
- package/dist/modules/features/consent/ketch/mergers/env-example.js.map +1 -0
- package/dist/modules/features/consent/ketch/mergers/footer.d.ts +17 -0
- package/dist/modules/features/consent/ketch/mergers/footer.d.ts.map +1 -0
- package/dist/modules/features/consent/ketch/mergers/footer.js +40 -0
- package/dist/modules/features/consent/ketch/mergers/footer.js.map +1 -0
- package/dist/modules/features/consent/ketch/mergers/knip.d.ts +18 -0
- package/dist/modules/features/consent/ketch/mergers/knip.d.ts.map +1 -0
- package/dist/modules/features/consent/ketch/mergers/knip.js +37 -0
- package/dist/modules/features/consent/ketch/mergers/knip.js.map +1 -0
- package/dist/modules/features/consent/ketch/mergers/layout.d.ts +25 -0
- package/dist/modules/features/consent/ketch/mergers/layout.d.ts.map +1 -0
- package/dist/modules/features/consent/ketch/mergers/layout.js +55 -0
- package/dist/modules/features/consent/ketch/mergers/layout.js.map +1 -0
- package/dist/modules/features/jobs/ashby/config.d.ts +19 -0
- package/dist/modules/features/jobs/ashby/config.d.ts.map +1 -0
- package/dist/modules/features/jobs/ashby/config.js +29 -0
- package/dist/modules/features/jobs/ashby/config.js.map +1 -0
- package/dist/modules/features/jobs/ashby/mergers/check-integration.d.ts +14 -0
- package/dist/modules/features/jobs/ashby/mergers/check-integration.d.ts.map +1 -0
- package/dist/modules/features/jobs/ashby/mergers/check-integration.js +78 -0
- package/dist/modules/features/jobs/ashby/mergers/check-integration.js.map +1 -0
- package/dist/modules/features/jobs/ashby/mergers/env-example.d.ts +12 -0
- package/dist/modules/features/jobs/ashby/mergers/env-example.d.ts.map +1 -0
- package/dist/modules/features/jobs/ashby/mergers/env-example.js +27 -0
- package/dist/modules/features/jobs/ashby/mergers/env-example.js.map +1 -0
- package/dist/modules/features/jobs/ashby/mergers/knip.d.ts +18 -0
- package/dist/modules/features/jobs/ashby/mergers/knip.d.ts.map +1 -0
- package/dist/modules/features/jobs/ashby/mergers/knip.js +41 -0
- package/dist/modules/features/jobs/ashby/mergers/knip.js.map +1 -0
- package/dist/paths.d.ts +12 -0
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +12 -0
- package/dist/paths.js.map +1 -1
- package/package.json +3 -1
- package/src/agent-skills/ashby-pagebuilder-block/SKILL.md +277 -0
- package/src/agent-skills/hubspot-pagebuilder-block/SKILL.md +58 -25
- package/src/agent-skills/ketch-consent-check/SKILL.md +197 -0
- package/src/agent-skills/ketch-consent-setup/SKILL.md +326 -0
- package/src/agent-skills/ketch-consent-setup/references/ui-customization.md +329 -0
- package/src/agent-skills/page-builder-block/SKILL.md +150 -0
- package/src/modules/features/cms/sanity/files/components/ui/sanity-image/index.tsx +36 -4
- package/src/modules/features/cms/sanity/files/lib/integrations/sanity/components/rich-text.tsx +7 -1
- package/src/modules/features/cms/sanity-pagebuilder/files/components/sanity/rich-text.tsx +7 -1
- package/src/modules/features/cms/sanity-pagebuilder/files/components/ui/sanity-image/index.tsx +36 -4
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/anchor-id-input.tsx +17 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/page-builder-input.tsx +26 -7
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/page-builder-insert-menu.tsx +242 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/presentation-input.tsx +146 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/spacing-steps-input.tsx +84 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/components/use-studio-preview-urls.ts +62 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/fetchers/layout.ts +6 -3
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/icons.ts +8 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/page-builder-config.ts +72 -34
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/queries.ts +12 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/index.ts +1 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/reusable/blog-content.ts +3 -3
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/reusable/description.ts +3 -3
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/reusable/gated-content.ts +3 -3
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/reusable/hero.ts +3 -3
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/reusable/rich-text-block.ts +3 -3
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/singleton/content-collection.ts +3 -3
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/components/studio-preview.ts +37 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/index.ts +11 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/layout/spacing-settings.ts +120 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/anchor.ts +75 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/block.ts +46 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/presentation.ts +44 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/schemas/shared/spacing.ts +164 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/singletons.ts +9 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/structure.ts +5 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/components/sanity/rich-text.tsx +7 -1
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/components/ui/sanity-image/index.tsx +36 -4
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/anchor-id-input.tsx +17 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/page-builder-input.tsx +26 -7
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/page-builder-insert-menu.tsx +242 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/presentation-input.tsx +146 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/spacing-steps-input.tsx +84 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/components/use-studio-preview-urls.ts +62 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/fetchers/layout.ts +4 -2
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/icons.ts +8 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/page-builder-config.ts +72 -34
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/queries.ts +12 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/index.ts +1 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/blog-content.ts +3 -3
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/description.ts +3 -3
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/gated-content.ts +3 -3
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/hero.ts +3 -3
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/reusable/rich-text-block.ts +3 -3
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/singleton/content-collection.ts +3 -3
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/components/studio-preview.ts +37 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/index.ts +11 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/layout/spacing-settings.ts +120 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/anchor.ts +75 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/block.ts +46 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/presentation.ts +44 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/schemas/shared/spacing.ts +164 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/singletons.ts +9 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/structure.ts +5 -0
- package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/tags.ts +2 -0
- package/src/modules/features/consent/ketch/files/lib/integrations/check-integration.ts +25 -0
- package/src/modules/features/consent/ketch/files/lib/integrations/ketch/README.md +70 -0
- package/src/modules/features/consent/ketch/files/lib/integrations/ketch/categories.ts +20 -0
- package/src/modules/features/consent/ketch/files/lib/integrations/ketch/consent-store.ts +107 -0
- package/src/modules/features/consent/ketch/files/lib/integrations/ketch/env.ts +20 -0
- package/src/modules/features/consent/ketch/files/lib/integrations/ketch/ketch-experience.css +136 -0
- package/src/modules/features/consent/ketch/files/lib/integrations/ketch/preferences-button.tsx +51 -0
- package/src/modules/features/consent/ketch/files/lib/integrations/ketch/script.tsx +8 -0
- package/src/modules/features/consent/ketch/files/lib/integrations/ketch/types.ts +15 -0
- package/src/modules/features/consent/ketch/files/lib/integrations/ketch/use-consent.ts +26 -0
- package/src/modules/features/jobs/ashby/files/_sanity-adapter/job-board-block.ts +53 -0
- package/src/modules/features/jobs/ashby/files/lib/integrations/ashby/README.md +134 -0
- package/src/modules/features/jobs/ashby/files/lib/integrations/ashby/env.ts +3 -0
- package/src/modules/features/jobs/ashby/files/lib/integrations/ashby/index.ts +40 -0
- package/src/modules/features/jobs/ashby/files/lib/integrations/ashby/normalize.ts +124 -0
- package/src/modules/features/jobs/ashby/files/lib/integrations/ashby/types.ts +20 -0
- package/src/modules/features/jobs/ashby/files/lib/integrations/check-integration.ts +25 -0
- package/src/templates/next-default/.biome/plugins/no-fill-without-sizes.grit +11 -0
- package/src/templates/next-default/biome.json +2 -1
- package/src/templates/next-default/components/ui/image/index.tsx +20 -12
- package/src/templates/next-default/next.config.ts +10 -4
- package/src/templates/next-experiments/.biome/plugins/no-fill-without-sizes.grit +11 -0
- package/src/templates/next-experiments/biome.json +2 -1
- package/src/templates/next-experiments/components/ui/image/index.tsx +20 -12
- package/src/templates/next-experiments/next.config.ts +10 -4
- package/src/templates/next-pagebuilder/.biome/plugins/no-fill-without-sizes.grit +11 -0
- package/src/templates/next-pagebuilder/AGENTS.md +129 -0
- package/src/templates/next-pagebuilder/CLAUDE.md +3 -0
- package/src/templates/next-pagebuilder/README.md +70 -0
- package/src/templates/next-pagebuilder/app/(content)/layout.tsx +2 -0
- package/src/templates/next-pagebuilder/biome.json +2 -1
- package/src/templates/next-pagebuilder/components/layout/spacing-scale/index.tsx +18 -0
- package/src/templates/next-pagebuilder/components/page-builder/components/article-content/index.tsx +6 -1
- package/src/templates/next-pagebuilder/components/page-builder/components/article-content/related-post-item.tsx +1 -0
- package/src/templates/next-pagebuilder/components/page-builder/components/content-collection/index.tsx +2 -2
- package/src/templates/next-pagebuilder/components/page-builder/components/description.tsx +3 -7
- package/src/templates/next-pagebuilder/components/page-builder/components/gated-content/index.tsx +2 -2
- package/src/templates/next-pagebuilder/components/page-builder/components/hero.tsx +3 -7
- package/src/templates/next-pagebuilder/components/page-builder/components/rich-text-block.tsx +3 -5
- package/src/templates/next-pagebuilder/components/page-builder/registry.tsx +70 -0
- package/src/templates/next-pagebuilder/components/page-builder/renderer.tsx +33 -49
- package/src/templates/next-pagebuilder/components/page-builder/section.tsx +35 -0
- package/src/templates/next-pagebuilder/components/page-document/index.tsx +1 -1
- package/src/templates/next-pagebuilder/components/ui/image/index.tsx +20 -12
- package/src/templates/next-pagebuilder/lib/integrations/sanity/sanity.types.ts +92 -4
- package/src/templates/next-pagebuilder/next.config.ts +4 -3
- package/src/templates/next-pagebuilder-cache/.biome/plugins/no-fill-without-sizes.grit +11 -0
- package/src/templates/next-pagebuilder-cache/AGENTS.md +136 -0
- package/src/templates/next-pagebuilder-cache/CLAUDE.md +3 -0
- package/src/templates/next-pagebuilder-cache/README.md +75 -0
- package/src/templates/next-pagebuilder-cache/app/(content)/layout.tsx +2 -0
- package/src/templates/next-pagebuilder-cache/biome.json +2 -1
- package/src/templates/next-pagebuilder-cache/components/layout/spacing-scale/index.tsx +46 -0
- package/src/templates/next-pagebuilder-cache/components/page-builder/components/article-content/index.tsx +6 -1
- package/src/templates/next-pagebuilder-cache/components/page-builder/components/article-content/related-post-item.tsx +1 -0
- package/src/templates/next-pagebuilder-cache/components/page-builder/components/content-collection/index.tsx +2 -2
- package/src/templates/next-pagebuilder-cache/components/page-builder/components/description.tsx +3 -7
- package/src/templates/next-pagebuilder-cache/components/page-builder/components/gated-content/index.tsx +2 -2
- package/src/templates/next-pagebuilder-cache/components/page-builder/components/hero.tsx +3 -7
- package/src/templates/next-pagebuilder-cache/components/page-builder/components/rich-text-block.tsx +3 -5
- package/src/templates/next-pagebuilder-cache/components/page-builder/registry.tsx +70 -0
- package/src/templates/next-pagebuilder-cache/components/page-builder/renderer.tsx +33 -49
- package/src/templates/next-pagebuilder-cache/components/page-builder/section.tsx +35 -0
- package/src/templates/next-pagebuilder-cache/components/page-document/index.tsx +1 -1
- package/src/templates/next-pagebuilder-cache/components/ui/image/index.tsx +21 -12
- package/src/templates/next-pagebuilder-cache/lib/integrations/sanity/sanity.types.ts +92 -4
- package/src/templates/next-pagebuilder-cache/lib/utils/image-sizes.ts +72 -0
- package/src/templates/next-pagebuilder-cache/next.config.ts +4 -3
- package/src/templates/next-webgl/.biome/plugins/no-fill-without-sizes.grit +11 -0
- package/src/templates/next-webgl/biome.json +2 -1
- package/src/templates/next-webgl/components/ui/image/index.tsx +20 -12
- package/src/templates/next-webgl/next.config.ts +10 -4
|
@@ -9,15 +9,35 @@ export type PageBuilderDocumentType =
|
|
|
9
9
|
| "blogContent"
|
|
10
10
|
| (typeof singletonComponents)[number]["schemaType"];
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* A block declares two orthogonal axes. `pageTypes` is which page documents may
|
|
14
|
+
* host it and drives validation; `category` is what the block structurally is
|
|
15
|
+
* and drives the insert menu. Never collapse them back into one field — that is
|
|
16
|
+
* what made the menu a single flat list.
|
|
17
|
+
*/
|
|
18
|
+
export type PageBuilderGroupName = "generic" | "blogEntry";
|
|
19
|
+
|
|
20
|
+
export const componentCategories = [
|
|
21
|
+
{ name: "hero", title: "Hero" },
|
|
22
|
+
{ name: "text", title: "Text" },
|
|
23
|
+
{ name: "list", title: "List" },
|
|
24
|
+
{ name: "content", title: "Content" },
|
|
25
|
+
{ name: "misc", title: "Miscellaneous" },
|
|
26
|
+
] as const satisfies ReadonlyArray<{ name: string; title: string }>;
|
|
27
|
+
|
|
28
|
+
export type ComponentCategory = (typeof componentCategories)[number]["name"];
|
|
29
|
+
|
|
12
30
|
type PageBuilderReferenceMember = {
|
|
13
31
|
documentType: PageBuilderDocumentType;
|
|
14
32
|
name: string;
|
|
15
33
|
title: string;
|
|
16
34
|
documentId?: string;
|
|
17
|
-
|
|
35
|
+
pageTypes: readonly PageBuilderGroupName[];
|
|
36
|
+
category: ComponentCategory;
|
|
18
37
|
};
|
|
19
38
|
|
|
20
|
-
export
|
|
39
|
+
export const blockPreviewKey = (schemaTypeName: string) =>
|
|
40
|
+
`block.${schemaTypeName}`;
|
|
21
41
|
|
|
22
42
|
export const pageBuilderPageTypes: Array<{
|
|
23
43
|
title: string;
|
|
@@ -41,31 +61,36 @@ const reusablePageBuilderReferenceMembers: PageBuilderReferenceMember[] = [
|
|
|
41
61
|
documentType: "hero",
|
|
42
62
|
name: "heroReference",
|
|
43
63
|
title: "Heading",
|
|
44
|
-
|
|
64
|
+
pageTypes: ["generic"],
|
|
65
|
+
category: "hero",
|
|
45
66
|
},
|
|
46
67
|
{
|
|
47
68
|
documentType: "description",
|
|
48
69
|
name: "descriptionReference",
|
|
49
70
|
title: "Description",
|
|
50
|
-
|
|
71
|
+
pageTypes: ["generic"],
|
|
72
|
+
category: "text",
|
|
51
73
|
},
|
|
52
74
|
{
|
|
53
75
|
documentType: "richTextBlock",
|
|
54
76
|
name: "richTextBlockReference",
|
|
55
77
|
title: "Rich Text",
|
|
56
|
-
|
|
78
|
+
pageTypes: ["generic"],
|
|
79
|
+
category: "text",
|
|
57
80
|
},
|
|
58
81
|
{
|
|
59
82
|
documentType: "gatedContent",
|
|
60
83
|
name: "gatedContentReference",
|
|
61
84
|
title: "Gated Content",
|
|
62
|
-
|
|
85
|
+
pageTypes: ["generic"],
|
|
86
|
+
category: "misc",
|
|
63
87
|
},
|
|
64
88
|
{
|
|
65
89
|
documentType: "blogContent",
|
|
66
90
|
name: "blogContentReference",
|
|
67
91
|
title: "Blog Content",
|
|
68
|
-
|
|
92
|
+
pageTypes: ["blogEntry"],
|
|
93
|
+
category: "content",
|
|
69
94
|
},
|
|
70
95
|
];
|
|
71
96
|
|
|
@@ -75,7 +100,8 @@ const singletonPageBuilderReferenceMembers: PageBuilderReferenceMember[] =
|
|
|
75
100
|
name: `${component.schemaType}Reference`,
|
|
76
101
|
title: component.title,
|
|
77
102
|
documentId: component.documentId,
|
|
78
|
-
|
|
103
|
+
pageTypes: ["generic"],
|
|
104
|
+
category: component.category,
|
|
79
105
|
}));
|
|
80
106
|
|
|
81
107
|
export const pageBuilderReferenceMembers: PageBuilderReferenceMember[] = [
|
|
@@ -93,25 +119,37 @@ export const getPageBuilderReferenceMemberByDocumentType = (
|
|
|
93
119
|
(member) => member.documentType === documentType,
|
|
94
120
|
);
|
|
95
121
|
|
|
96
|
-
const
|
|
122
|
+
const isMemberVisibleForPageType = (
|
|
123
|
+
member: PageBuilderReferenceMember,
|
|
97
124
|
pageType: PageBuilderGroupName | undefined,
|
|
98
|
-
|
|
99
|
-
) => (pageType ?? "generic") === group;
|
|
125
|
+
) => member.pageTypes.includes(pageType ?? "generic");
|
|
100
126
|
|
|
101
127
|
export const getVisiblePageBuilderReferenceMembers = (
|
|
102
128
|
pageType: PageBuilderGroupName | undefined,
|
|
103
129
|
) =>
|
|
104
130
|
pageBuilderReferenceMembers.filter((member) =>
|
|
105
|
-
member
|
|
131
|
+
isMemberVisibleForPageType(member, pageType),
|
|
106
132
|
);
|
|
107
133
|
|
|
108
|
-
export const
|
|
134
|
+
export const getVisiblePageBuilderMemberNames = (
|
|
109
135
|
pageType: PageBuilderGroupName | undefined,
|
|
110
136
|
) =>
|
|
111
|
-
|
|
112
|
-
(
|
|
137
|
+
new Set(
|
|
138
|
+
getVisiblePageBuilderReferenceMembers(pageType).map((member) => member.name),
|
|
113
139
|
);
|
|
114
140
|
|
|
141
|
+
export const getVisiblePageBuilderInsertMenuGroups = (
|
|
142
|
+
pageType: PageBuilderGroupName | undefined,
|
|
143
|
+
) => {
|
|
144
|
+
const visible = getVisiblePageBuilderMemberNames(pageType);
|
|
145
|
+
return pageBuilderInsertMenuGroups
|
|
146
|
+
.map((group) => ({
|
|
147
|
+
...group,
|
|
148
|
+
of: group.of.filter((name) => visible.has(name)),
|
|
149
|
+
}))
|
|
150
|
+
.filter((group) => group.of.length > 0);
|
|
151
|
+
};
|
|
152
|
+
|
|
115
153
|
export const getInvalidPageBuilderDocumentTypes = (
|
|
116
154
|
pageType: PageBuilderGroupName | undefined,
|
|
117
155
|
documentTypes: PageBuilderDocumentType[],
|
|
@@ -121,24 +159,24 @@ export const getInvalidPageBuilderDocumentTypes = (
|
|
|
121
159
|
|
|
122
160
|
if (!member) return false;
|
|
123
161
|
|
|
124
|
-
return !member
|
|
125
|
-
isPageBuilderGroupVisible(pageType, group),
|
|
126
|
-
);
|
|
162
|
+
return !isMemberVisibleForPageType(member, pageType);
|
|
127
163
|
});
|
|
128
164
|
|
|
129
|
-
export const pageBuilderInsertMenuGroups
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
name
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
165
|
+
export const pageBuilderInsertMenuGroups: Array<{
|
|
166
|
+
name: ComponentCategory;
|
|
167
|
+
title: string;
|
|
168
|
+
of: string[];
|
|
169
|
+
}> = componentCategories.map(({ name, title }) => ({
|
|
170
|
+
name,
|
|
171
|
+
title,
|
|
172
|
+
of: pageBuilderReferenceMembers
|
|
173
|
+
.filter((member) => member.category === name)
|
|
174
|
+
.map((member) => member.name),
|
|
175
|
+
}));
|
|
176
|
+
|
|
177
|
+
export const pageBuilderBlockPreviewOptions = pageBuilderReferenceMembers.map(
|
|
178
|
+
(member) => ({
|
|
179
|
+
title: `Block — ${member.title} · ${member.name}`,
|
|
180
|
+
value: blockPreviewKey(member.name),
|
|
181
|
+
}),
|
|
182
|
+
);
|
package/src/modules/features/cms/sanity-pagebuilder-cache/files/lib/integrations/sanity/queries.ts
CHANGED
|
@@ -96,6 +96,8 @@ const pageBuilderProjection = `
|
|
|
96
96
|
_type == "gatedContent" => {
|
|
97
97
|
_id,
|
|
98
98
|
_type,
|
|
99
|
+
anchorId,
|
|
100
|
+
presentation,
|
|
99
101
|
internalTitle,
|
|
100
102
|
teaserHeading,
|
|
101
103
|
teaserBody[]{
|
|
@@ -533,6 +535,16 @@ export const LAYOUT_QUERY = defineQuery(`
|
|
|
533
535
|
},
|
|
534
536
|
"structuredData": *[_type == "structuredData"][0]{
|
|
535
537
|
${structuredDataProjection}
|
|
538
|
+
},
|
|
539
|
+
"spacingSettings": *[_type == "spacingSettings"][0]{
|
|
540
|
+
steps[]{
|
|
541
|
+
_key,
|
|
542
|
+
name,
|
|
543
|
+
mobile,
|
|
544
|
+
tablet,
|
|
545
|
+
desktop,
|
|
546
|
+
isDefault
|
|
547
|
+
}
|
|
536
548
|
}
|
|
537
549
|
}
|
|
538
550
|
`);
|
|
@@ -4,3 +4,4 @@ export { gatedContent } from "./reusable/gated-content";
|
|
|
4
4
|
export { hero } from "./reusable/hero";
|
|
5
5
|
export { richTextBlock } from "./reusable/rich-text-block";
|
|
6
6
|
export { blogCollection } from "./singleton/content-collection";
|
|
7
|
+
export { studioPreview } from "./studio-preview";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { defineField
|
|
1
|
+
import { defineField } from "sanity";
|
|
2
2
|
import { contentIcon } from "@/lib/integrations/sanity/icons";
|
|
3
|
+
import { defineBlock } from "@/lib/integrations/sanity/schemas/shared/block";
|
|
3
4
|
|
|
4
|
-
export const blogContent =
|
|
5
|
+
export const blogContent = defineBlock({
|
|
5
6
|
name: "blogContent",
|
|
6
7
|
title: "Blog Content",
|
|
7
|
-
type: "document",
|
|
8
8
|
icon: contentIcon,
|
|
9
9
|
fields: [
|
|
10
10
|
defineField({
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { defineField
|
|
1
|
+
import { defineField } from "sanity";
|
|
2
2
|
import { descriptionIcon } from "@/lib/integrations/sanity/icons";
|
|
3
|
+
import { defineBlock } from "@/lib/integrations/sanity/schemas/shared/block";
|
|
3
4
|
|
|
4
|
-
export const description =
|
|
5
|
+
export const description = defineBlock({
|
|
5
6
|
name: "description",
|
|
6
7
|
title: "Description",
|
|
7
|
-
type: "document",
|
|
8
8
|
icon: descriptionIcon,
|
|
9
9
|
fields: [
|
|
10
10
|
defineField({
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { defineField
|
|
1
|
+
import { defineField } from "sanity";
|
|
2
2
|
import { gatedContentIcon } from "@/lib/integrations/sanity/icons";
|
|
3
|
+
import { defineBlock } from "@/lib/integrations/sanity/schemas/shared/block";
|
|
3
4
|
|
|
4
5
|
type GatedContentParent = { kind?: string };
|
|
5
6
|
|
|
@@ -19,10 +20,9 @@ const requiredForKind =
|
|
|
19
20
|
return isEmpty ? message : true;
|
|
20
21
|
};
|
|
21
22
|
|
|
22
|
-
export const gatedContent =
|
|
23
|
+
export const gatedContent = defineBlock({
|
|
23
24
|
name: "gatedContent",
|
|
24
25
|
title: "Gated Content",
|
|
25
|
-
type: "document",
|
|
26
26
|
icon: gatedContentIcon,
|
|
27
27
|
description:
|
|
28
28
|
"Content hidden behind a lead-capture gate. Everyone sees the teaser; the payload is withheld from the page until the visitor submits the form.",
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { defineField
|
|
1
|
+
import { defineField } from "sanity";
|
|
2
2
|
import { heroIcon } from "@/lib/integrations/sanity/icons";
|
|
3
|
+
import { defineBlock } from "@/lib/integrations/sanity/schemas/shared/block";
|
|
3
4
|
|
|
4
|
-
export const hero =
|
|
5
|
+
export const hero = defineBlock({
|
|
5
6
|
name: "hero",
|
|
6
7
|
title: "Heading",
|
|
7
|
-
type: "document",
|
|
8
8
|
icon: heroIcon,
|
|
9
9
|
fields: [
|
|
10
10
|
defineField({
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { defineField
|
|
1
|
+
import { defineField } from "sanity";
|
|
2
2
|
import { richTextIcon } from "@/lib/integrations/sanity/icons";
|
|
3
|
+
import { defineBlock } from "@/lib/integrations/sanity/schemas/shared/block";
|
|
3
4
|
|
|
4
|
-
export const richTextBlock =
|
|
5
|
+
export const richTextBlock = defineBlock({
|
|
5
6
|
name: "richTextBlock",
|
|
6
7
|
title: "Rich Text",
|
|
7
|
-
type: "document",
|
|
8
8
|
icon: richTextIcon,
|
|
9
9
|
fields: [
|
|
10
10
|
defineField({
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { defineField
|
|
1
|
+
import { defineField } from "sanity";
|
|
2
2
|
import type { SanityStudioIcon } from "@/lib/integrations/sanity/icons";
|
|
3
3
|
import { pageListIcon } from "@/lib/integrations/sanity/icons";
|
|
4
|
+
import { defineBlock } from "@/lib/integrations/sanity/schemas/shared/block";
|
|
4
5
|
|
|
5
6
|
function createContentCollection(config: {
|
|
6
7
|
name: string;
|
|
@@ -8,10 +9,9 @@ function createContentCollection(config: {
|
|
|
8
9
|
icon: SanityStudioIcon;
|
|
9
10
|
label: string;
|
|
10
11
|
}) {
|
|
11
|
-
return
|
|
12
|
+
return defineBlock({
|
|
12
13
|
name: config.name,
|
|
13
14
|
title: config.title,
|
|
14
|
-
type: "document",
|
|
15
15
|
icon: config.icon,
|
|
16
16
|
description: `This component contains all the ${config.label}`,
|
|
17
17
|
fields: [
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { defineField, defineType } from "sanity";
|
|
2
|
+
import { pageBuilderCardIcon } from "@/lib/integrations/sanity/icons";
|
|
3
|
+
import { pageBuilderBlockPreviewOptions } from "@/lib/integrations/sanity/page-builder-config";
|
|
4
|
+
|
|
5
|
+
export const studioPreview = defineType({
|
|
6
|
+
name: "studioPreview",
|
|
7
|
+
title: "Component Preview",
|
|
8
|
+
type: "document",
|
|
9
|
+
icon: pageBuilderCardIcon,
|
|
10
|
+
fields: [
|
|
11
|
+
defineField({
|
|
12
|
+
name: "key",
|
|
13
|
+
title: "Component",
|
|
14
|
+
type: "string",
|
|
15
|
+
options: { list: pageBuilderBlockPreviewOptions },
|
|
16
|
+
validation: (Rule) => Rule.required(),
|
|
17
|
+
}),
|
|
18
|
+
defineField({
|
|
19
|
+
name: "preview",
|
|
20
|
+
title: "Preview image",
|
|
21
|
+
type: "image",
|
|
22
|
+
options: { hotspot: true },
|
|
23
|
+
validation: (Rule) => Rule.required(),
|
|
24
|
+
}),
|
|
25
|
+
],
|
|
26
|
+
preview: {
|
|
27
|
+
select: { key: "key", media: "preview" },
|
|
28
|
+
prepare({ key, media }) {
|
|
29
|
+
return {
|
|
30
|
+
title:
|
|
31
|
+
pageBuilderBlockPreviewOptions.find((option) => option.value === key)
|
|
32
|
+
?.title ?? "Component Preview",
|
|
33
|
+
media,
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
});
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
gatedContent,
|
|
16
16
|
hero,
|
|
17
17
|
richTextBlock,
|
|
18
|
+
studioPreview,
|
|
18
19
|
} from "./components";
|
|
19
20
|
import { author } from "./content/author";
|
|
20
21
|
import { blogCategory } from "./content/blog-category";
|
|
@@ -22,6 +23,7 @@ import { companyData } from "./layout/company-data";
|
|
|
22
23
|
import { footer } from "./layout/footer";
|
|
23
24
|
import { navbar } from "./layout/navbar";
|
|
24
25
|
import { redirect } from "./layout/redirect";
|
|
26
|
+
import { spacingSettings, spacingStep } from "./layout/spacing-settings";
|
|
25
27
|
import { structuredData } from "./layout/structured-data";
|
|
26
28
|
import { externalLink, link, pageReference } from "./shared/link";
|
|
27
29
|
import { metadata } from "./shared/metadata";
|
|
@@ -34,6 +36,7 @@ import {
|
|
|
34
36
|
import { page } from "./shared/page";
|
|
35
37
|
import { pageBuilder } from "./shared/page-builder";
|
|
36
38
|
import { pageFolder } from "./shared/page-folder";
|
|
39
|
+
import { presentation } from "./shared/presentation";
|
|
37
40
|
import { richText } from "./shared/richText";
|
|
38
41
|
import {
|
|
39
42
|
customJsonLd,
|
|
@@ -73,10 +76,14 @@ export {
|
|
|
73
76
|
pageBuilder,
|
|
74
77
|
pageFolder,
|
|
75
78
|
pageReference,
|
|
79
|
+
presentation,
|
|
76
80
|
redirect,
|
|
77
81
|
richText,
|
|
78
82
|
softwareApplicationJsonLd,
|
|
83
|
+
spacingSettings,
|
|
84
|
+
spacingStep,
|
|
79
85
|
structuredData,
|
|
86
|
+
studioPreview,
|
|
80
87
|
videoJsonLd,
|
|
81
88
|
};
|
|
82
89
|
|
|
@@ -89,6 +96,7 @@ export const schema: { types: SchemaTypeDefinition[] } = {
|
|
|
89
96
|
link,
|
|
90
97
|
metadata,
|
|
91
98
|
pageBuilder,
|
|
99
|
+
presentation,
|
|
92
100
|
richText,
|
|
93
101
|
|
|
94
102
|
// Page-level JSON-LD blocks (selectable in metadata)
|
|
@@ -113,12 +121,15 @@ export const schema: { types: SchemaTypeDefinition[] } = {
|
|
|
113
121
|
description,
|
|
114
122
|
richTextBlock,
|
|
115
123
|
gatedContent,
|
|
124
|
+
studioPreview,
|
|
116
125
|
|
|
117
126
|
// Layout types (singletons for site-wide layout)
|
|
118
127
|
companyData,
|
|
119
128
|
navbar,
|
|
120
129
|
footer,
|
|
121
130
|
structuredData,
|
|
131
|
+
spacingStep,
|
|
132
|
+
spacingSettings,
|
|
122
133
|
|
|
123
134
|
// Layout collections (managed lists, not singletons)
|
|
124
135
|
redirect,
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { defineArrayMember, defineField, defineType } from "sanity";
|
|
2
|
+
import { SpacingStepsInput } from "@/lib/integrations/sanity/components/spacing-steps-input";
|
|
3
|
+
import { spacingSettingsIcon } from "@/lib/integrations/sanity/icons";
|
|
4
|
+
import { defaultSpacingSteps } from "@/lib/integrations/sanity/schemas/shared/spacing";
|
|
5
|
+
|
|
6
|
+
const pxField = (
|
|
7
|
+
name: "mobile" | "tablet" | "desktop",
|
|
8
|
+
title: string,
|
|
9
|
+
description: string,
|
|
10
|
+
isRequired = false,
|
|
11
|
+
) =>
|
|
12
|
+
defineField({
|
|
13
|
+
name,
|
|
14
|
+
title,
|
|
15
|
+
type: "number",
|
|
16
|
+
description,
|
|
17
|
+
validation: (Rule) => {
|
|
18
|
+
const range = Rule.min(0).max(400).integer();
|
|
19
|
+
return isRequired ? range.required() : range;
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export const spacingStep = defineType({
|
|
24
|
+
name: "spacingStep",
|
|
25
|
+
title: "Spacing step",
|
|
26
|
+
type: "object",
|
|
27
|
+
fields: [
|
|
28
|
+
defineField({
|
|
29
|
+
name: "name",
|
|
30
|
+
title: "Name",
|
|
31
|
+
type: "string",
|
|
32
|
+
description: "Shown in the Presentation Mode dropdown on every block.",
|
|
33
|
+
validation: (Rule) => Rule.required().max(24),
|
|
34
|
+
}),
|
|
35
|
+
pxField("desktop", "Desktop", "From 1440px up. Required.", true),
|
|
36
|
+
pxField("tablet", "Tablet", "From 768px up. Empty reuses Desktop."),
|
|
37
|
+
pxField("mobile", "Mobile", "Below 768px. Empty reuses Desktop."),
|
|
38
|
+
defineField({
|
|
39
|
+
name: "isDefault",
|
|
40
|
+
title: "Use as default",
|
|
41
|
+
type: "boolean",
|
|
42
|
+
description:
|
|
43
|
+
"Blocks that never touch Presentation Mode use this step. Exactly one step can be the default.",
|
|
44
|
+
initialValue: false,
|
|
45
|
+
}),
|
|
46
|
+
],
|
|
47
|
+
preview: {
|
|
48
|
+
select: {
|
|
49
|
+
name: "name",
|
|
50
|
+
mobile: "mobile",
|
|
51
|
+
tablet: "tablet",
|
|
52
|
+
desktop: "desktop",
|
|
53
|
+
isDefault: "isDefault",
|
|
54
|
+
},
|
|
55
|
+
prepare({ name, mobile, tablet, desktop, isDefault }) {
|
|
56
|
+
const px = [mobile ?? desktop, tablet ?? desktop, desktop]
|
|
57
|
+
.map((value) => (typeof value === "number" ? `${value}px` : "—"))
|
|
58
|
+
.join(" / ");
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
title: `${name || "Untitled"}${isDefault ? " · default" : ""}`,
|
|
62
|
+
subtitle: `${px} (mobile / tablet / desktop)`,
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
export const spacingSettings = defineType({
|
|
69
|
+
name: "spacingSettings",
|
|
70
|
+
title: "Spacing Scale",
|
|
71
|
+
type: "document",
|
|
72
|
+
icon: spacingSettingsIcon,
|
|
73
|
+
fields: [
|
|
74
|
+
defineField({
|
|
75
|
+
name: "steps",
|
|
76
|
+
title: "Spacing steps",
|
|
77
|
+
description:
|
|
78
|
+
"The vertical spacing every block can pick from. Add as many as you need and mark one as the default. Left empty, the built-in scale is used.",
|
|
79
|
+
type: "array",
|
|
80
|
+
components: { input: SpacingStepsInput },
|
|
81
|
+
of: [defineArrayMember({ type: "spacingStep" })],
|
|
82
|
+
initialValue: defaultSpacingSteps.map((step) => ({
|
|
83
|
+
_key: step.key,
|
|
84
|
+
_type: "spacingStep",
|
|
85
|
+
name: step.name,
|
|
86
|
+
mobile: step.mobile,
|
|
87
|
+
tablet: step.tablet,
|
|
88
|
+
desktop: step.desktop,
|
|
89
|
+
isDefault: step.isDefault,
|
|
90
|
+
})),
|
|
91
|
+
validation: (Rule) =>
|
|
92
|
+
Rule.custom((steps: unknown) => {
|
|
93
|
+
if (!Array.isArray(steps) || steps.length === 0) return true;
|
|
94
|
+
|
|
95
|
+
const defaults = steps.filter(
|
|
96
|
+
(step) => (step as { isDefault?: boolean })?.isDefault === true,
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
if (defaults.length === 0) return "Mark one step as the default";
|
|
100
|
+
if (defaults.length > 1) return "Only one step can be the default";
|
|
101
|
+
|
|
102
|
+
const names = steps.map((step) =>
|
|
103
|
+
(step as { name?: string })?.name?.trim().toLowerCase(),
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
return new Set(names).size === names.length
|
|
107
|
+
? true
|
|
108
|
+
: "Two steps share the same name";
|
|
109
|
+
}),
|
|
110
|
+
}),
|
|
111
|
+
],
|
|
112
|
+
preview: {
|
|
113
|
+
prepare() {
|
|
114
|
+
return {
|
|
115
|
+
title: "Spacing Scale",
|
|
116
|
+
subtitle: "Section padding steps per breakpoint",
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const anchorSourceFields = [
|
|
2
|
+
"headline",
|
|
3
|
+
"heading",
|
|
4
|
+
"teaserHeading",
|
|
5
|
+
"title",
|
|
6
|
+
"label",
|
|
7
|
+
] as const;
|
|
8
|
+
|
|
9
|
+
const ANCHOR_MAX_LENGTH = 60;
|
|
10
|
+
|
|
11
|
+
export const slugifyAnchor = (raw: string) =>
|
|
12
|
+
raw
|
|
13
|
+
.normalize("NFKD")
|
|
14
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
15
|
+
.toLowerCase()
|
|
16
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
17
|
+
.replace(/^-+|-+$/g, "")
|
|
18
|
+
.slice(0, ANCHOR_MAX_LENGTH)
|
|
19
|
+
.replace(/-+$/g, "");
|
|
20
|
+
|
|
21
|
+
export const deriveAnchorId = (block: Record<string, unknown>) => {
|
|
22
|
+
for (const field of anchorSourceFields) {
|
|
23
|
+
const value = block[field];
|
|
24
|
+
if (typeof value === "string" && value.trim()) {
|
|
25
|
+
const slug = slugifyAnchor(value);
|
|
26
|
+
if (slug) return slug;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return "";
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const explicitAnchorId = (block: Record<string, unknown>) =>
|
|
34
|
+
typeof block.anchorId === "string" ? slugifyAnchor(block.anchorId) : "";
|
|
35
|
+
|
|
36
|
+
const NO_RESERVED_IDS: ReadonlySet<string> = new Set();
|
|
37
|
+
|
|
38
|
+
export const reserveAnchorIds = (
|
|
39
|
+
blocks: readonly Record<string, unknown>[],
|
|
40
|
+
): ReadonlySet<string> => {
|
|
41
|
+
const reserved = new Set<string>();
|
|
42
|
+
|
|
43
|
+
for (const block of blocks) {
|
|
44
|
+
const explicit = explicitAnchorId(block);
|
|
45
|
+
if (explicit) reserved.add(explicit);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return reserved;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const resolveAnchorId = (
|
|
52
|
+
block: Record<string, unknown>,
|
|
53
|
+
taken: Set<string>,
|
|
54
|
+
reserved: ReadonlySet<string> = NO_RESERVED_IDS,
|
|
55
|
+
) => {
|
|
56
|
+
const explicit = explicitAnchorId(block);
|
|
57
|
+
const base = explicit || deriveAnchorId(block);
|
|
58
|
+
|
|
59
|
+
if (!base) return "";
|
|
60
|
+
|
|
61
|
+
const isBlocked = (candidate: string) =>
|
|
62
|
+
taken.has(candidate) || (!explicit && reserved.has(candidate));
|
|
63
|
+
|
|
64
|
+
let candidate = base;
|
|
65
|
+
let suffix = 2;
|
|
66
|
+
|
|
67
|
+
while (isBlocked(candidate)) {
|
|
68
|
+
candidate = `${base}-${suffix}`;
|
|
69
|
+
suffix += 1;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
taken.add(candidate);
|
|
73
|
+
|
|
74
|
+
return candidate;
|
|
75
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type DocumentDefinition,
|
|
3
|
+
defineType,
|
|
4
|
+
type FieldDefinition,
|
|
5
|
+
type FieldGroupDefinition,
|
|
6
|
+
} from "sanity";
|
|
7
|
+
import { presentationFields } from "./presentation";
|
|
8
|
+
|
|
9
|
+
const presentationGroup: FieldGroupDefinition = {
|
|
10
|
+
name: "presentation",
|
|
11
|
+
title: "Presentation",
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type BlockDefinition = Omit<DocumentDefinition, "type" | "fields" | "groups"> & {
|
|
15
|
+
fields: FieldDefinition[];
|
|
16
|
+
groups?: FieldGroupDefinition[];
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const withPresentationGroup = (groups: FieldGroupDefinition[] | undefined) => {
|
|
20
|
+
if (!groups?.length) return undefined;
|
|
21
|
+
if (groups.some((group) => group.name === presentationGroup.name)) {
|
|
22
|
+
return groups;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return [...groups, presentationGroup];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Defines a page-builder block — `type: "document"` plus Anchor ID and
|
|
30
|
+
* Presentation Mode. Use instead of `defineType`.
|
|
31
|
+
*/
|
|
32
|
+
export const defineBlock = ({ fields, groups, ...rest }: BlockDefinition) => {
|
|
33
|
+
const blockGroups = withPresentationGroup(groups);
|
|
34
|
+
|
|
35
|
+
return defineType({
|
|
36
|
+
...rest,
|
|
37
|
+
type: "document",
|
|
38
|
+
...(blockGroups ? { groups: blockGroups } : {}),
|
|
39
|
+
fields: [
|
|
40
|
+
...fields,
|
|
41
|
+
...presentationFields(
|
|
42
|
+
blockGroups ? { group: presentationGroup.name } : undefined,
|
|
43
|
+
),
|
|
44
|
+
],
|
|
45
|
+
});
|
|
46
|
+
};
|