Fixes: 1. media.ts: wrap placeholder generation in try-catch 2. toolbar.ts: check r.ok, display error message in popover
219 lines
6.1 KiB
TypeScript
219 lines
6.1 KiB
TypeScript
/**
|
|
* Marketing blocks plugin (inline, template-local).
|
|
*
|
|
* Registers the five marketing block types so editors can insert and edit them
|
|
* in the admin's Portable Text editor. Block Kit `fields` describe the form
|
|
* shown when inserting or editing a block.
|
|
*
|
|
* Constraints worth knowing:
|
|
*
|
|
* - Block Kit has no "object group" element, so nested object shapes (e.g. a
|
|
* CTA's { label, url }) are flattened to sibling fields like ctaLabel and
|
|
* ctaUrl. The site-side renderer reads the flat keys.
|
|
* - Repeater sub-fields are scalar only: text_input, number_input, select,
|
|
* toggle. Nested repeaters are not allowed -- list-of-strings becomes a
|
|
* single multiline text field, split on newline at render time (see
|
|
* Pricing.astro for the pattern).
|
|
* - There is no media picker element in the editor's plugin-block modal yet,
|
|
* so image fields (avatars, hero images) are URL strings entered by hand.
|
|
*
|
|
* Site-side rendering still goes through MarketingBlocks.astro --
|
|
* componentsEntry auto-wiring is a separate cleanup.
|
|
*/
|
|
|
|
import { definePlugin } from "emdash";
|
|
import type { PluginDefinition } from "emdash";
|
|
|
|
const ICON_OPTIONS = [
|
|
{ label: "Lightning", value: "zap" },
|
|
{ label: "Shield", value: "shield" },
|
|
{ label: "Users", value: "users" },
|
|
{ label: "Chart", value: "chart" },
|
|
{ label: "Code", value: "code" },
|
|
{ label: "Globe", value: "globe" },
|
|
{ label: "Heart", value: "heart" },
|
|
{ label: "Star", value: "star" },
|
|
{ label: "Check", value: "check" },
|
|
{ label: "Lock", value: "lock" },
|
|
{ label: "Clock", value: "clock" },
|
|
{ label: "Cloud", value: "cloud" },
|
|
];
|
|
|
|
const definition: PluginDefinition = {
|
|
id: "marketing-blocks",
|
|
version: "0.1.0",
|
|
|
|
admin: {
|
|
portableTextBlocks: [
|
|
{
|
|
type: "marketing.hero",
|
|
label: "Hero",
|
|
category: "Sections",
|
|
description: "Big headline section with optional CTAs",
|
|
fields: [
|
|
{ type: "text_input", action_id: "headline", label: "Headline" },
|
|
{
|
|
type: "text_input",
|
|
action_id: "subheadline",
|
|
label: "Subheadline",
|
|
multiline: true,
|
|
},
|
|
{ type: "text_input", action_id: "primaryCtaLabel", label: "Primary CTA label" },
|
|
{ type: "text_input", action_id: "primaryCtaUrl", label: "Primary CTA URL" },
|
|
{
|
|
type: "text_input",
|
|
action_id: "secondaryCtaLabel",
|
|
label: "Secondary CTA label",
|
|
},
|
|
{ type: "text_input", action_id: "secondaryCtaUrl", label: "Secondary CTA URL" },
|
|
{ type: "toggle", action_id: "centered", label: "Center the layout" },
|
|
],
|
|
},
|
|
|
|
{
|
|
type: "marketing.features",
|
|
label: "Features",
|
|
category: "Sections",
|
|
description: "Grid of feature cards with icons",
|
|
fields: [
|
|
{ type: "text_input", action_id: "headline", label: "Headline" },
|
|
{
|
|
type: "text_input",
|
|
action_id: "subheadline",
|
|
label: "Subheadline",
|
|
multiline: true,
|
|
},
|
|
{
|
|
type: "repeater",
|
|
action_id: "features",
|
|
label: "Features",
|
|
item_label: "Feature",
|
|
min_items: 1,
|
|
max_items: 12,
|
|
fields: [
|
|
{
|
|
type: "select",
|
|
action_id: "icon",
|
|
label: "Icon",
|
|
options: ICON_OPTIONS,
|
|
},
|
|
{ type: "text_input", action_id: "title", label: "Title" },
|
|
{
|
|
type: "text_input",
|
|
action_id: "description",
|
|
label: "Description",
|
|
multiline: true,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
|
|
{
|
|
type: "marketing.testimonials",
|
|
label: "Testimonials",
|
|
category: "Sections",
|
|
description: "Customer testimonial cards",
|
|
fields: [
|
|
{ type: "text_input", action_id: "headline", label: "Headline" },
|
|
{
|
|
type: "repeater",
|
|
action_id: "testimonials",
|
|
label: "Testimonials",
|
|
item_label: "Testimonial",
|
|
min_items: 1,
|
|
fields: [
|
|
{ type: "text_input", action_id: "quote", label: "Quote", multiline: true },
|
|
{ type: "text_input", action_id: "author", label: "Author name" },
|
|
{ type: "text_input", action_id: "role", label: "Role / title" },
|
|
{ type: "text_input", action_id: "company", label: "Company" },
|
|
{ type: "text_input", action_id: "avatar", label: "Avatar URL" },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
|
|
{
|
|
type: "marketing.pricing",
|
|
label: "Pricing",
|
|
category: "Sections",
|
|
description: "Pricing plan comparison cards",
|
|
fields: [
|
|
{ type: "text_input", action_id: "headline", label: "Headline" },
|
|
{
|
|
type: "repeater",
|
|
action_id: "plans",
|
|
label: "Plans",
|
|
item_label: "Plan",
|
|
min_items: 1,
|
|
max_items: 6,
|
|
fields: [
|
|
{ type: "text_input", action_id: "name", label: "Plan name" },
|
|
{
|
|
type: "text_input",
|
|
action_id: "price",
|
|
label: "Price",
|
|
placeholder: "$29 or Custom",
|
|
},
|
|
{
|
|
type: "text_input",
|
|
action_id: "period",
|
|
label: "Period",
|
|
placeholder: "/month",
|
|
},
|
|
{
|
|
type: "text_input",
|
|
action_id: "description",
|
|
label: "Description",
|
|
multiline: true,
|
|
},
|
|
{
|
|
type: "text_input",
|
|
action_id: "features",
|
|
label: "Features (one per line)",
|
|
multiline: true,
|
|
placeholder: "Unlimited projects\nPriority support\nSSO",
|
|
},
|
|
{ type: "text_input", action_id: "ctaLabel", label: "CTA label" },
|
|
{ type: "text_input", action_id: "ctaUrl", label: "CTA URL" },
|
|
{ type: "toggle", action_id: "highlighted", label: "Highlight this plan" },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
|
|
{
|
|
type: "marketing.faq",
|
|
label: "FAQ",
|
|
category: "Sections",
|
|
description: "Frequently asked questions",
|
|
fields: [
|
|
{ type: "text_input", action_id: "headline", label: "Headline" },
|
|
{
|
|
type: "repeater",
|
|
action_id: "items",
|
|
label: "Questions",
|
|
item_label: "Question",
|
|
min_items: 1,
|
|
fields: [
|
|
{ type: "text_input", action_id: "question", label: "Question" },
|
|
{
|
|
type: "text_input",
|
|
action_id: "answer",
|
|
label: "Answer",
|
|
multiline: true,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
};
|
|
|
|
export function createPlugin() {
|
|
return definePlugin(definition);
|
|
}
|
|
|
|
export default createPlugin;
|