@asheeui/puck 0.0.0-stage → 2.3.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.
Files changed (154) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +10 -0
  3. package/README.md +126 -3
  4. package/dist/index.d.ts +22 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +23 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/index.native.d.ts +22 -0
  9. package/dist/index.native.d.ts.map +1 -0
  10. package/dist/index.native.js +22 -0
  11. package/dist/index.native.js.map +1 -0
  12. package/dist/native/blocks/content.d.ts +19 -0
  13. package/dist/native/blocks/content.d.ts.map +1 -0
  14. package/dist/native/blocks/content.js +29 -0
  15. package/dist/native/blocks/content.js.map +1 -0
  16. package/dist/native/blocks/layout.d.ts +42 -0
  17. package/dist/native/blocks/layout.d.ts.map +1 -0
  18. package/dist/native/blocks/layout.js +33 -0
  19. package/dist/native/blocks/layout.js.map +1 -0
  20. package/dist/native/blocks/navigation.d.ts +19 -0
  21. package/dist/native/blocks/navigation.d.ts.map +1 -0
  22. package/dist/native/blocks/navigation.js +38 -0
  23. package/dist/native/blocks/navigation.js.map +1 -0
  24. package/dist/native/blocks/sections.d.ts +30 -0
  25. package/dist/native/blocks/sections.d.ts.map +1 -0
  26. package/dist/native/blocks/sections.js +60 -0
  27. package/dist/native/blocks/sections.js.map +1 -0
  28. package/dist/native/blocks/utility.d.ts +13 -0
  29. package/dist/native/blocks/utility.d.ts.map +1 -0
  30. package/dist/native/blocks/utility.js +18 -0
  31. package/dist/native/blocks/utility.js.map +1 -0
  32. package/dist/native/config.d.ts +19 -0
  33. package/dist/native/config.d.ts.map +1 -0
  34. package/dist/native/config.js +45 -0
  35. package/dist/native/config.js.map +1 -0
  36. package/dist/native/index.d.ts +18 -0
  37. package/dist/native/index.d.ts.map +1 -0
  38. package/dist/native/index.js +18 -0
  39. package/dist/native/index.js.map +1 -0
  40. package/dist/native/render.d.ts +39 -0
  41. package/dist/native/render.d.ts.map +1 -0
  42. package/dist/native/render.js +71 -0
  43. package/dist/native/render.js.map +1 -0
  44. package/dist/native/root.d.ts +21 -0
  45. package/dist/native/root.d.ts.map +1 -0
  46. package/dist/native/root.js +28 -0
  47. package/dist/native/root.js.map +1 -0
  48. package/dist/shared/adapters.d.ts +52 -0
  49. package/dist/shared/adapters.d.ts.map +1 -0
  50. package/dist/shared/adapters.js +57 -0
  51. package/dist/shared/adapters.js.map +1 -0
  52. package/dist/shared/blocks/content.d.ts +42 -0
  53. package/dist/shared/blocks/content.d.ts.map +1 -0
  54. package/dist/shared/blocks/content.js +44 -0
  55. package/dist/shared/blocks/content.js.map +1 -0
  56. package/dist/shared/blocks/index.d.ts +42 -0
  57. package/dist/shared/blocks/index.d.ts.map +1 -0
  58. package/dist/shared/blocks/index.js +81 -0
  59. package/dist/shared/blocks/index.js.map +1 -0
  60. package/dist/shared/blocks/layout.d.ts +52 -0
  61. package/dist/shared/blocks/layout.d.ts.map +1 -0
  62. package/dist/shared/blocks/layout.js +51 -0
  63. package/dist/shared/blocks/layout.js.map +1 -0
  64. package/dist/shared/blocks/navigation.d.ts +74 -0
  65. package/dist/shared/blocks/navigation.d.ts.map +1 -0
  66. package/dist/shared/blocks/navigation.js +86 -0
  67. package/dist/shared/blocks/navigation.js.map +1 -0
  68. package/dist/shared/blocks/sections.d.ts +142 -0
  69. package/dist/shared/blocks/sections.d.ts.map +1 -0
  70. package/dist/shared/blocks/sections.js +166 -0
  71. package/dist/shared/blocks/sections.js.map +1 -0
  72. package/dist/shared/blocks/utility.d.ts +31 -0
  73. package/dist/shared/blocks/utility.d.ts.map +1 -0
  74. package/dist/shared/blocks/utility.js +36 -0
  75. package/dist/shared/blocks/utility.js.map +1 -0
  76. package/dist/shared/fields.d.ts +103 -0
  77. package/dist/shared/fields.d.ts.map +1 -0
  78. package/dist/shared/fields.js +162 -0
  79. package/dist/shared/fields.js.map +1 -0
  80. package/dist/shared/index.d.ts +14 -0
  81. package/dist/shared/index.d.ts.map +1 -0
  82. package/dist/shared/index.js +14 -0
  83. package/dist/shared/index.js.map +1 -0
  84. package/dist/shared/types.d.ts +235 -0
  85. package/dist/shared/types.d.ts.map +1 -0
  86. package/dist/shared/types.js +20 -0
  87. package/dist/shared/types.js.map +1 -0
  88. package/dist/web/blocks/content.d.ts +10 -0
  89. package/dist/web/blocks/content.d.ts.map +1 -0
  90. package/dist/web/blocks/content.js +28 -0
  91. package/dist/web/blocks/content.js.map +1 -0
  92. package/dist/web/blocks/layout.d.ts +27 -0
  93. package/dist/web/blocks/layout.d.ts.map +1 -0
  94. package/dist/web/blocks/layout.js +28 -0
  95. package/dist/web/blocks/layout.js.map +1 -0
  96. package/dist/web/blocks/navigation.d.ts +10 -0
  97. package/dist/web/blocks/navigation.d.ts.map +1 -0
  98. package/dist/web/blocks/navigation.js +38 -0
  99. package/dist/web/blocks/navigation.js.map +1 -0
  100. package/dist/web/blocks/sections.d.ts +22 -0
  101. package/dist/web/blocks/sections.d.ts.map +1 -0
  102. package/dist/web/blocks/sections.js +60 -0
  103. package/dist/web/blocks/sections.js.map +1 -0
  104. package/dist/web/blocks/utility.d.ts +6 -0
  105. package/dist/web/blocks/utility.d.ts.map +1 -0
  106. package/dist/web/blocks/utility.js +19 -0
  107. package/dist/web/blocks/utility.js.map +1 -0
  108. package/dist/web/config.d.ts +65 -0
  109. package/dist/web/config.d.ts.map +1 -0
  110. package/dist/web/config.js +35 -0
  111. package/dist/web/config.js.map +1 -0
  112. package/dist/web/index.d.ts +19 -0
  113. package/dist/web/index.d.ts.map +1 -0
  114. package/dist/web/index.js +20 -0
  115. package/dist/web/index.js.map +1 -0
  116. package/dist/web/page.d.ts +24 -0
  117. package/dist/web/page.d.ts.map +1 -0
  118. package/dist/web/page.js +27 -0
  119. package/dist/web/page.js.map +1 -0
  120. package/dist/web/root.d.ts +9 -0
  121. package/dist/web/root.d.ts.map +1 -0
  122. package/dist/web/root.js +26 -0
  123. package/dist/web/root.js.map +1 -0
  124. package/package.json +119 -3
  125. package/src/index.native.ts +22 -0
  126. package/src/index.ts +24 -0
  127. package/src/native/blocks/content.tsx +55 -0
  128. package/src/native/blocks/layout.tsx +72 -0
  129. package/src/native/blocks/navigation.tsx +71 -0
  130. package/src/native/blocks/sections.tsx +174 -0
  131. package/src/native/blocks/utility.tsx +27 -0
  132. package/src/native/config.tsx +66 -0
  133. package/src/native/index.ts +18 -0
  134. package/src/native/render.tsx +113 -0
  135. package/src/native/root.tsx +35 -0
  136. package/src/shared/adapters.ts +82 -0
  137. package/src/shared/blocks/content.ts +80 -0
  138. package/src/shared/blocks/index.ts +100 -0
  139. package/src/shared/blocks/layout.ts +82 -0
  140. package/src/shared/blocks/navigation.ts +168 -0
  141. package/src/shared/blocks/sections.ts +322 -0
  142. package/src/shared/blocks/utility.ts +65 -0
  143. package/src/shared/fields.ts +199 -0
  144. package/src/shared/index.ts +14 -0
  145. package/src/shared/types.ts +278 -0
  146. package/src/web/blocks/content.tsx +55 -0
  147. package/src/web/blocks/layout.tsx +73 -0
  148. package/src/web/blocks/navigation.tsx +72 -0
  149. package/src/web/blocks/sections.tsx +177 -0
  150. package/src/web/blocks/utility.tsx +31 -0
  151. package/src/web/config.tsx +141 -0
  152. package/src/web/index.ts +21 -0
  153. package/src/web/page.tsx +40 -0
  154. package/src/web/root.tsx +34 -0
@@ -0,0 +1,82 @@
1
+ /**
2
+ * The layout blocks, stated once for both renderers.
3
+ *
4
+ * A band with its own rhythm and background, and a two-column arrangement. These are
5
+ * the two blocks that hold other blocks, and they are also the two whose *props* differ
6
+ * between the platforms while their *fields* do not: the web hands a render function a
7
+ * drop-zone component to place, the platform hands it rendered children. So the fields
8
+ * and the defaults are stated here, once, and the prop type each renderer receives is
9
+ * declared beside it — which keeps a builder from offering two different drop zones for
10
+ * one block.
11
+ */
12
+
13
+ import type { SectionBackground, Space } from "@asheeui/core";
14
+ import { BACKGROUND_OPTIONS, SPACING_OPTIONS, selectField } from "../fields";
15
+ import type { AsheeBlockSpec } from "../types";
16
+
17
+ /**
18
+ * Ratio options a two-column block offers.
19
+ */
20
+ export const COLUMN_RATIO_OPTIONS = ["50/50", "33/67", "67/33"] as const;
21
+
22
+ /**
23
+ * Ratio of a two-column arrangement, from the `lg` breakpoint upwards.
24
+ */
25
+ export type ColumnRatio = (typeof COLUMN_RATIO_OPTIONS)[number];
26
+
27
+ /**
28
+ * The band options a section-level block carries, which is every part of its value that
29
+ * is not a drop zone.
30
+ */
31
+ export type SectionBandProps = {
32
+ /** Vertical padding of the band. */
33
+ spacing?: Space;
34
+
35
+ /** Background treatment of the band. */
36
+ background?: SectionBackground;
37
+ };
38
+
39
+ /**
40
+ * A band with its own vertical rhythm and background.
41
+ */
42
+ export const SECTION_SPEC: AsheeBlockSpec<SectionBandProps> = {
43
+ label: "Section",
44
+ fields: {
45
+ spacing: selectField("Vertical spacing", SPACING_OPTIONS),
46
+ background: selectField("Background", BACKGROUND_OPTIONS),
47
+ content: { type: "slot", label: "Content" },
48
+ },
49
+ defaultProps: { spacing: "lg", background: "none" },
50
+ };
51
+
52
+ /**
53
+ * Everything of the two-column block that is not a drop zone.
54
+ */
55
+ export type ColumnsBandProps = SectionBandProps & {
56
+ /** Ratio of the two columns from the `lg` breakpoint upwards. */
57
+ ratio?: ColumnRatio;
58
+ };
59
+
60
+ /**
61
+ * Two columns that stack on a narrow screen.
62
+ */
63
+ export const COLUMNS_SPEC: AsheeBlockSpec<ColumnsBandProps> = {
64
+ label: "Columns",
65
+ fields: {
66
+ ratio: selectField("Column ratio", COLUMN_RATIO_OPTIONS),
67
+ spacing: selectField("Vertical spacing", SPACING_OPTIONS),
68
+ start: { type: "slot", label: "First column" },
69
+ end: { type: "slot", label: "Second column" },
70
+ },
71
+ defaultProps: { ratio: "50/50", spacing: "lg" },
72
+ };
73
+
74
+ /**
75
+ * The grid columns each ratio resolves to, on the web.
76
+ * The strings are complete literals, so Tailwind emits them.
77
+ */
78
+ export const COLUMN_RATIO_CLASS: Record<ColumnRatio, string> = {
79
+ "50/50": "lg:grid-cols-2",
80
+ "33/67": "lg:grid-cols-[1fr_2fr]",
81
+ "67/33": "lg:grid-cols-[2fr_1fr]",
82
+ };
@@ -0,0 +1,168 @@
1
+ /**
2
+ * The navigation blocks, stated once for both renderers.
3
+ *
4
+ * A builder usually owns the header and the footer of a site once, at the page level,
5
+ * so these blocks expose the framework's bar and footer with the navigation as data:
6
+ * link lists a non-developer can edit, and no application state. The link lists are
7
+ * described here, once, so the two platforms offer the same editor for them.
8
+ */
9
+
10
+ import {
11
+ ACTION_FIELDS,
12
+ LINK_ITEM_FIELDS,
13
+ selectField,
14
+ textareaField,
15
+ textField,
16
+ } from "../fields";
17
+ import type { AsheeArrayField, AsheeBlockSpec } from "../types";
18
+
19
+ /**
20
+ * One navigation link.
21
+ */
22
+ export type LinkItemProps = {
23
+ /** Visible name of the link. */
24
+ label: string;
25
+
26
+ /** Destination of the link. */
27
+ href: string;
28
+ };
29
+
30
+ /**
31
+ * One column of footer navigation.
32
+ */
33
+ export type FooterGroupProps = {
34
+ /** Title of the column. */
35
+ title: string;
36
+
37
+ /** The links in the column. */
38
+ links: LinkItemProps[];
39
+ };
40
+
41
+ /**
42
+ * The links of one column.
43
+ */
44
+ const COLUMN_LINK_ARRAY: AsheeArrayField<LinkItemProps> = {
45
+ type: "array",
46
+ label: "Links",
47
+ arrayFields: LINK_ITEM_FIELDS,
48
+ defaultItemProps: { label: "New link", href: "/" },
49
+ getItemSummary: (item) => item.label ?? "Link",
50
+ };
51
+
52
+ /**
53
+ * The link list of the bar.
54
+ */
55
+ const NAVBAR_LINK_ARRAY: AsheeArrayField<LinkItemProps> = COLUMN_LINK_ARRAY;
56
+
57
+ /**
58
+ * The columns of the footer.
59
+ */
60
+ const FOOTER_GROUP_ARRAY: AsheeArrayField<FooterGroupProps> = {
61
+ type: "array",
62
+ label: "Columns",
63
+ arrayFields: {
64
+ title: textField("Column title"),
65
+ links: COLUMN_LINK_ARRAY,
66
+ },
67
+ defaultItemProps: { title: "New column", links: [] },
68
+ getItemSummary: (item) => item.title ?? "Column",
69
+ };
70
+
71
+ /**
72
+ * The props of the navigation bar block.
73
+ */
74
+ export type NavbarBlockProps = {
75
+ /** Brand content, typically the product name. */
76
+ brand?: string;
77
+
78
+ /** Destination of the brand. */
79
+ brandHref?: string;
80
+
81
+ /** Where the links sit in the bar, from the `md` breakpoint upwards. */
82
+ align?: "start" | "center" | "end";
83
+
84
+ /** The navigation links. */
85
+ links?: LinkItemProps[];
86
+
87
+ /** The emphasised action of the bar. */
88
+ primaryAction?: {
89
+ label: string;
90
+ href: string;
91
+ variant: string;
92
+ };
93
+
94
+ /** The supporting action of the bar. */
95
+ secondaryAction?: {
96
+ label: string;
97
+ href: string;
98
+ variant: string;
99
+ };
100
+ };
101
+
102
+ /**
103
+ * The navigation bar of a page.
104
+ */
105
+ export const NAVBAR_SPEC: AsheeBlockSpec<NavbarBlockProps> = {
106
+ label: "Navbar",
107
+ fields: {
108
+ brand: textField("Brand", "Ashee"),
109
+ brandHref: textField("Brand link", "/"),
110
+ align: selectField("Link alignment", ["start", "center", "end"] as const),
111
+ links: NAVBAR_LINK_ARRAY,
112
+ primaryAction: ACTION_FIELDS,
113
+ secondaryAction: ACTION_FIELDS,
114
+ },
115
+ defaultProps: {
116
+ brand: "Ashee",
117
+ brandHref: "/",
118
+ align: "center",
119
+ links: [
120
+ { label: "Product", href: "/product" },
121
+ { label: "Pricing", href: "/pricing" },
122
+ ],
123
+ },
124
+ };
125
+
126
+ /**
127
+ * The props of the footer block.
128
+ */
129
+ export type FooterBlockProps = {
130
+ /** Brand content. */
131
+ brand?: string;
132
+
133
+ /** Short description under the brand. */
134
+ description?: string;
135
+
136
+ /** The navigation columns. */
137
+ groups?: FooterGroupProps[];
138
+
139
+ /** The copyright line. */
140
+ copyright?: string;
141
+ };
142
+
143
+ /**
144
+ * The footer of a page.
145
+ */
146
+ export const FOOTER_SPEC: AsheeBlockSpec<FooterBlockProps> = {
147
+ label: "Footer",
148
+ fields: {
149
+ brand: textField("Brand", "Ashee"),
150
+ description: textareaField("Description"),
151
+ groups: FOOTER_GROUP_ARRAY,
152
+ copyright: textField("Copyright", "Ashee Softworks"),
153
+ },
154
+ defaultProps: {
155
+ brand: "Ashee",
156
+ description: "Campaign messaging for growing teams.",
157
+ groups: [
158
+ {
159
+ title: "Product",
160
+ links: [
161
+ { label: "Campaigns", href: "/campaigns" },
162
+ { label: "Pricing", href: "/pricing" },
163
+ ],
164
+ },
165
+ ],
166
+ copyright: "Ashee Softworks",
167
+ },
168
+ };
@@ -0,0 +1,322 @@
1
+ /**
2
+ * The marketing section blocks, stated once for both renderers.
3
+ *
4
+ * These are the sections a page is assembled from: a hero, a feature grid, a
5
+ * testimonial wall, a pricing card and a closing call to action. What is stated here is
6
+ * the editor's half of each — its label, its fields and its starting value — so the two
7
+ * platforms offer one editor and one default, and differ only in how each draws the
8
+ * component it names.
9
+ */
10
+
11
+ import type { ContainerSize, CtaPanel, SectionBackground } from "@asheeui/core";
12
+ import type { BuilderAction } from "../adapters";
13
+ import {
14
+ ACTION_FIELDS,
15
+ ALIGNMENT_OPTIONS,
16
+ BACKGROUND_OPTIONS,
17
+ COLUMNS_OPTIONS,
18
+ CONTAINER_SIZE_OPTIONS,
19
+ FEATURE_ITEM_FIELD,
20
+ PRICING_FEATURE_FIELD,
21
+ selectField,
22
+ TESTIMONIAL_ITEM_FIELD,
23
+ textareaField,
24
+ textField,
25
+ } from "../fields";
26
+ import type { AsheeBlockSpec } from "../types";
27
+
28
+ /**
29
+ * The props of the hero block.
30
+ */
31
+ export type HeroBlockProps = {
32
+ /** Short label above the headline. */
33
+ eyebrow?: string;
34
+
35
+ /** The headline. */
36
+ title?: string;
37
+
38
+ /** Supporting sentence under the headline. */
39
+ description?: string;
40
+
41
+ /** The emphasised action. */
42
+ primaryAction?: BuilderAction;
43
+
44
+ /** The supporting action. */
45
+ secondaryAction?: BuilderAction;
46
+
47
+ /** Alignment of the text column. */
48
+ align?: "start" | "center";
49
+
50
+ /** Background treatment of the band. */
51
+ background?: SectionBackground;
52
+
53
+ /** Maximum content width of the band. */
54
+ contentWidth?: ContainerSize;
55
+ };
56
+
57
+ /**
58
+ * The leading statement of a page.
59
+ */
60
+ export const HERO_SPEC: AsheeBlockSpec<HeroBlockProps> = {
61
+ label: "Hero",
62
+ fields: {
63
+ eyebrow: textField("Eyebrow"),
64
+ title: textField("Headline", "Run your campaigns from one place"),
65
+ description: textareaField("Description"),
66
+ primaryAction: ACTION_FIELDS,
67
+ secondaryAction: ACTION_FIELDS,
68
+ align: selectField("Alignment", ALIGNMENT_OPTIONS),
69
+ background: selectField("Background", BACKGROUND_OPTIONS),
70
+ contentWidth: selectField("Content width", CONTAINER_SIZE_OPTIONS),
71
+ },
72
+ defaultProps: {
73
+ eyebrow: "",
74
+ title: "Run your campaigns from one place",
75
+ description: "Messages, templates and results, without switching tools.",
76
+ primaryAction: { label: "Start free", href: "/signup", variant: "solid" },
77
+ secondaryAction: {
78
+ label: "Book a demo",
79
+ href: "/demo",
80
+ variant: "bordered",
81
+ },
82
+ align: "start",
83
+ background: "none",
84
+ contentWidth: "lg",
85
+ },
86
+ };
87
+
88
+ /**
89
+ * The props of the call-to-action block.
90
+ */
91
+ export type CtaBlockProps = {
92
+ /** Short label above the headline. */
93
+ eyebrow?: string;
94
+
95
+ /** The headline of the band. */
96
+ title?: string;
97
+
98
+ /** Supporting sentence under the headline. */
99
+ description?: string;
100
+
101
+ /** The emphasised action. */
102
+ primaryAction?: BuilderAction;
103
+
104
+ /** The supporting action. */
105
+ secondaryAction?: BuilderAction;
106
+
107
+ /** Alignment of the content. */
108
+ align?: "start" | "center";
109
+
110
+ /** Background treatment of the band. */
111
+ background?: SectionBackground;
112
+
113
+ /** Panel treatment of the content. */
114
+ panel?: CtaPanel;
115
+ };
116
+
117
+ /**
118
+ * The closing call to action of a page.
119
+ */
120
+ export const CTA_SPEC: AsheeBlockSpec<CtaBlockProps> = {
121
+ label: "Call to action",
122
+ fields: {
123
+ eyebrow: textField("Eyebrow"),
124
+ title: textField("Headline", "Send your first campaign today"),
125
+ description: textareaField("Description"),
126
+ primaryAction: ACTION_FIELDS,
127
+ secondaryAction: ACTION_FIELDS,
128
+ align: selectField("Alignment", ALIGNMENT_OPTIONS),
129
+ background: selectField("Background", BACKGROUND_OPTIONS),
130
+ panel: selectField("Panel", ["bordered", "muted", "plain"] as const),
131
+ },
132
+ defaultProps: {
133
+ eyebrow: "",
134
+ title: "Send your first campaign today",
135
+ description: "No card required, and no setup call.",
136
+ primaryAction: {
137
+ label: "Create an account",
138
+ href: "/signup",
139
+ variant: "solid",
140
+ },
141
+ align: "center",
142
+ background: "none",
143
+ panel: "bordered",
144
+ },
145
+ };
146
+
147
+ /**
148
+ * The props of the feature grid block.
149
+ */
150
+ export type FeatureGridBlockProps = {
151
+ /** Short label above the heading. */
152
+ eyebrow?: string;
153
+
154
+ /** Heading of the band. */
155
+ title?: string;
156
+
157
+ /** Supporting sentence under the heading. */
158
+ description?: string;
159
+
160
+ /** The features to show. */
161
+ items?: Array<{ title: string; description: string }>;
162
+
163
+ /** Columns from the `md` breakpoint upwards. */
164
+ columnsMd?: string;
165
+
166
+ /** Columns from the `lg` breakpoint upwards. */
167
+ columnsLg?: string;
168
+
169
+ /** Alignment of the heading. */
170
+ align?: "start" | "center";
171
+
172
+ /** Background treatment of the band. */
173
+ background?: SectionBackground;
174
+ };
175
+
176
+ /**
177
+ * A band of features laid out in a responsive grid.
178
+ */
179
+ export const FEATURE_GRID_SPEC: AsheeBlockSpec<FeatureGridBlockProps> = {
180
+ label: "Feature grid",
181
+ fields: {
182
+ eyebrow: textField("Eyebrow"),
183
+ title: textField("Heading", "Everything the campaign needs"),
184
+ description: textareaField("Description"),
185
+ items: FEATURE_ITEM_FIELD,
186
+ columnsMd: selectField("Columns on tablet", COLUMNS_OPTIONS),
187
+ columnsLg: selectField("Columns on desktop", COLUMNS_OPTIONS),
188
+ align: selectField("Heading alignment", ALIGNMENT_OPTIONS),
189
+ background: selectField("Background", BACKGROUND_OPTIONS),
190
+ },
191
+ defaultProps: {
192
+ eyebrow: "",
193
+ title: "Everything the campaign needs",
194
+ description: "",
195
+ items: [
196
+ { title: "Templates", description: "Reusable messages." },
197
+ { title: "Scheduling", description: "Send at the right time." },
198
+ { title: "Reporting", description: "See what landed." },
199
+ ],
200
+ columnsMd: "2",
201
+ columnsLg: "3",
202
+ align: "center",
203
+ background: "none",
204
+ },
205
+ };
206
+
207
+ /**
208
+ * The props of the testimonials block.
209
+ */
210
+ export type TestimonialsBlockProps = {
211
+ /** Short label above the heading. */
212
+ eyebrow?: string;
213
+
214
+ /** Heading of the band. */
215
+ title?: string;
216
+
217
+ /** Supporting sentence under the heading. */
218
+ description?: string;
219
+
220
+ /** The quotes to show. */
221
+ items?: Array<{ quote: string; name: string; role: string }>;
222
+
223
+ /** Alignment of the heading. */
224
+ align?: "start" | "center";
225
+ };
226
+
227
+ /**
228
+ * A band of attributed customer quotes.
229
+ */
230
+ export const TESTIMONIALS_SPEC: AsheeBlockSpec<TestimonialsBlockProps> = {
231
+ label: "Testimonials",
232
+ fields: {
233
+ eyebrow: textField("Eyebrow"),
234
+ title: textField("Heading", "Teams send more with Ashee"),
235
+ description: textareaField("Description"),
236
+ items: TESTIMONIAL_ITEM_FIELD,
237
+ align: selectField("Heading alignment", ALIGNMENT_OPTIONS),
238
+ },
239
+ defaultProps: {
240
+ eyebrow: "",
241
+ title: "Teams send more with Ashee",
242
+ description: "",
243
+ items: [
244
+ {
245
+ quote: "We cut campaign setup from hours to minutes.",
246
+ name: "Ama Boateng",
247
+ role: "Head of Growth, Kora Retail",
248
+ },
249
+ ],
250
+ align: "center",
251
+ },
252
+ };
253
+
254
+ /**
255
+ * The props of the pricing card block.
256
+ */
257
+ export type PricingCardBlockProps = {
258
+ /** Name of the plan. */
259
+ name?: string;
260
+
261
+ /** The amount. */
262
+ price?: string;
263
+
264
+ /** Billing period shown beside the amount. */
265
+ period?: string;
266
+
267
+ /** One sentence about the plan. */
268
+ description?: string;
269
+
270
+ /** Short label above the plan name. */
271
+ badge?: string;
272
+
273
+ /** The plan's feature lines. */
274
+ features?: Array<{ label: string; included: boolean }>;
275
+
276
+ /** Whether the plan is the recommended one. */
277
+ highlighted?: boolean;
278
+
279
+ /** The emphasised action. */
280
+ primaryAction?: BuilderAction;
281
+ };
282
+
283
+ /**
284
+ * One plan in a pricing table.
285
+ */
286
+ export const PRICING_CARD_SPEC: AsheeBlockSpec<PricingCardBlockProps> = {
287
+ label: "Pricing card",
288
+ fields: {
289
+ badge: textField("Badge", "Most popular"),
290
+ name: textField("Plan", "Growth"),
291
+ price: textField("Price", "$29"),
292
+ period: textField("Period", "/month"),
293
+ description: textareaField("Description"),
294
+ features: PRICING_FEATURE_FIELD,
295
+ highlighted: {
296
+ type: "radio",
297
+ label: "Recommended plan",
298
+ options: [
299
+ { label: "Yes", value: true },
300
+ { label: "No", value: false },
301
+ ],
302
+ },
303
+ primaryAction: ACTION_FIELDS,
304
+ },
305
+ defaultProps: {
306
+ badge: "",
307
+ name: "Growth",
308
+ price: "$29",
309
+ period: "/month",
310
+ description: "For teams sending their first campaigns.",
311
+ features: [
312
+ { label: "10,000 messages", included: true },
313
+ { label: "Audit log", included: false },
314
+ ],
315
+ highlighted: false,
316
+ primaryAction: {
317
+ label: "Choose Growth",
318
+ href: "/signup",
319
+ variant: "solid",
320
+ },
321
+ },
322
+ };
@@ -0,0 +1,65 @@
1
+ /**
2
+ * The utility blocks, stated once for both renderers.
3
+ *
4
+ * The empty state is the one utility block a marketing or application page needs from
5
+ * a builder: a region that has nothing in it yet, which a non-developer can word and
6
+ * justify without touching code. Its fields are stated here so the two platforms offer
7
+ * the same editor for it.
8
+ */
9
+
10
+ import type { EmptyStateType } from "@asheeui/core";
11
+ import type { BuilderAction } from "../adapters";
12
+ import {
13
+ ACTION_FIELDS,
14
+ selectField,
15
+ textareaField,
16
+ textField,
17
+ } from "../fields";
18
+ import type { AsheeBlockSpec } from "../types";
19
+
20
+ /**
21
+ * The props of the empty state block.
22
+ */
23
+ export type EmptyStateBlockProps = {
24
+ /** What the region is. */
25
+ title?: string;
26
+
27
+ /** What the reader can do about it. */
28
+ description?: string;
29
+
30
+ /** Tone of the state. */
31
+ type?: EmptyStateType;
32
+
33
+ /** Whether the state is drawn as a panel. */
34
+ panel?: boolean;
35
+
36
+ /** The emphasised action. */
37
+ primaryAction?: BuilderAction;
38
+ };
39
+
40
+ /**
41
+ * A region with nothing in it.
42
+ */
43
+ export const EMPTY_STATE_SPEC: AsheeBlockSpec<EmptyStateBlockProps> = {
44
+ label: "Empty state",
45
+ fields: {
46
+ title: textField("Heading", "Nothing here yet"),
47
+ description: textareaField("Description"),
48
+ type: selectField("Tone", ["info", "success", "warning", "error"] as const),
49
+ panel: {
50
+ type: "radio",
51
+ label: "Panel",
52
+ options: [
53
+ { label: "Yes", value: true },
54
+ { label: "No", value: false },
55
+ ],
56
+ },
57
+ primaryAction: ACTION_FIELDS,
58
+ },
59
+ defaultProps: {
60
+ title: "Nothing here yet",
61
+ description: "Create the first item to see it here.",
62
+ type: "info",
63
+ panel: true,
64
+ },
65
+ };