@echothink-ui/templates 0.2.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/index.cjs +429 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +395 -0
- package/dist/index.js.map +1 -0
- package/dist/templates/bright-events.d.ts +2 -0
- package/dist/templates/glass-flowchart.d.ts +2 -0
- package/dist/templates/soft-card-campaign.d.ts +2 -0
- package/dist/templates/studio-dark-check-box.d.ts +2 -0
- package/dist/types.d.ts +36 -0
- package/package.json +43 -0
- package/src/__tests__/templates.test.tsx +74 -0
- package/src/index.ts +38 -0
- package/src/templates/bright-events.ts +64 -0
- package/src/templates/glass-flowchart.ts +84 -0
- package/src/templates/soft-card-campaign.ts +135 -0
- package/src/templates/studio-dark-check-box.ts +68 -0
- package/src/types.ts +84 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
// src/templates/soft-card-campaign.ts
|
|
2
|
+
import { layout, componentSlot, layoutSlot, fragmentSlot } from "@echothink-ui/layout";
|
|
3
|
+
var campaignColumns = [
|
|
4
|
+
{ key: "name", header: "Campaign" },
|
|
5
|
+
{ key: "channel", header: "Channel" },
|
|
6
|
+
{ key: "statusLabel", header: "Status" },
|
|
7
|
+
{ key: "audience", header: "Audience", align: "end" },
|
|
8
|
+
{ key: "updated", header: "Updated" }
|
|
9
|
+
];
|
|
10
|
+
var campaignRows = [
|
|
11
|
+
{ id: "c1", name: "Taste the Future", channel: "Instagram + Ads", statusLabel: "Running", audience: "10,000", updated: "24 Sep" },
|
|
12
|
+
{ id: "c2", name: "Cybersecurity Week", channel: "Facebook", statusLabel: "In Progress", audience: "6,420", updated: "22 Sep" },
|
|
13
|
+
{ id: "c3", name: "E-learning Essentials", channel: "Email", statusLabel: "Approval", audience: "3,890", updated: "21 Sep" }
|
|
14
|
+
];
|
|
15
|
+
var channelChart = [
|
|
16
|
+
{ label: "Mon", sent: 32, opened: 19 },
|
|
17
|
+
{ label: "Tue", sent: 46, opened: 22 },
|
|
18
|
+
{ label: "Wed", sent: 38, opened: 27 },
|
|
19
|
+
{ label: "Thu", sent: 54, opened: 31 },
|
|
20
|
+
{ label: "Fri", sent: 49, opened: 36 }
|
|
21
|
+
];
|
|
22
|
+
var softCardCampaignTemplate = {
|
|
23
|
+
id: "soft-card-campaign",
|
|
24
|
+
name: "Soft card campaign",
|
|
25
|
+
description: "Campaign dashboard with a master/detail configuration panel.",
|
|
26
|
+
stylePreset: "soft-card",
|
|
27
|
+
tags: ["dashboard", "marketing", "crud", "soft-card"],
|
|
28
|
+
layout: layout({
|
|
29
|
+
id: "soft-card-campaign",
|
|
30
|
+
type: "PageLayout.AdminShell",
|
|
31
|
+
styleIntent: { preset: "soft-card", taskMode: "crud" },
|
|
32
|
+
slots: {
|
|
33
|
+
topbar: fragmentSlot(
|
|
34
|
+
[
|
|
35
|
+
componentSlot("CarbonSectionHeader", { title: "Metricmap" }),
|
|
36
|
+
componentSlot("CarbonSearchInput", { placeholder: "Search campaigns" }),
|
|
37
|
+
componentSlot("CarbonButton", { label: "New campaign", intent: "primary" })
|
|
38
|
+
],
|
|
39
|
+
{ op: "stack", direction: "horizontal", slots: [] }
|
|
40
|
+
),
|
|
41
|
+
navigation: componentSlot("CarbonSideNav", {
|
|
42
|
+
items: [
|
|
43
|
+
{ label: "Summary of Key Metrics", href: "#summary" },
|
|
44
|
+
{ label: "Recent Campaigns", href: "#recent", current: true },
|
|
45
|
+
{ label: "Performance Snapshot", href: "#performance" },
|
|
46
|
+
{ label: "Integrations", href: "#integrations" },
|
|
47
|
+
{ label: "Social Media Metrics", href: "#social" }
|
|
48
|
+
]
|
|
49
|
+
}),
|
|
50
|
+
content: layoutSlot(
|
|
51
|
+
layout({
|
|
52
|
+
id: "campaign-split",
|
|
53
|
+
type: "Primitive.SplitPane",
|
|
54
|
+
variant: "horizontal",
|
|
55
|
+
composition: {
|
|
56
|
+
op: "parallel",
|
|
57
|
+
axis: "x",
|
|
58
|
+
slots: ["primary", "secondary"],
|
|
59
|
+
sizing: { primary: { basis: "1.7fr" }, secondary: { basis: "1fr" } }
|
|
60
|
+
},
|
|
61
|
+
slots: {
|
|
62
|
+
primary: fragmentSlot(
|
|
63
|
+
[
|
|
64
|
+
componentSlot("CarbonPageHeader", {
|
|
65
|
+
title: "Recent Campaign",
|
|
66
|
+
description: "Campaign configuration, audience rules, and channel performance in one workspace."
|
|
67
|
+
}),
|
|
68
|
+
fragmentSlot(
|
|
69
|
+
[
|
|
70
|
+
componentSlot("CarbonKPIBlock", { title: "Sent", value: "203 Mail", description: "+6% from last week" }),
|
|
71
|
+
componentSlot("CarbonKPIBlock", { title: "Opened", value: "18%", description: "+3% from last week" }),
|
|
72
|
+
componentSlot("CarbonKPIBlock", { title: "Conversions", value: "42", description: "12 awaiting review" })
|
|
73
|
+
],
|
|
74
|
+
{ op: "stack", direction: "horizontal", slots: [] }
|
|
75
|
+
),
|
|
76
|
+
componentSlot("CarbonChartBlock", {
|
|
77
|
+
title: "Channel performance",
|
|
78
|
+
subtitle: "Chosen period versus last period",
|
|
79
|
+
chartType: "bar",
|
|
80
|
+
data: channelChart,
|
|
81
|
+
series: [
|
|
82
|
+
{ key: "sent", label: "Chosen period" },
|
|
83
|
+
{ key: "opened", label: "Last period" }
|
|
84
|
+
]
|
|
85
|
+
}),
|
|
86
|
+
componentSlot("CarbonDataTable", { rows: campaignRows, columns: campaignColumns, rowKey: "id" })
|
|
87
|
+
],
|
|
88
|
+
{ op: "stack", direction: "vertical", slots: [] }
|
|
89
|
+
),
|
|
90
|
+
secondary: layoutSlot(
|
|
91
|
+
layout({
|
|
92
|
+
id: "campaign-form",
|
|
93
|
+
type: "Primitive.Panel",
|
|
94
|
+
styleIntent: { surface: "card" },
|
|
95
|
+
slots: {
|
|
96
|
+
header: componentSlot("CarbonSectionHeader", { title: "Campaign Info" }),
|
|
97
|
+
body: fragmentSlot(
|
|
98
|
+
[
|
|
99
|
+
componentSlot("CarbonTextInput", { labelText: "Campaign name", defaultValue: "Taste the Future" }),
|
|
100
|
+
componentSlot("CarbonTextInput", { labelText: "Brand", defaultValue: "Damory Food Indonesia" }),
|
|
101
|
+
componentSlot("CarbonSelect", {
|
|
102
|
+
labelText: "Channel",
|
|
103
|
+
defaultValue: "instagram",
|
|
104
|
+
options: [
|
|
105
|
+
{ value: "instagram", label: "Instagram + Google Ads" },
|
|
106
|
+
{ value: "email", label: "Email only" },
|
|
107
|
+
{ value: "social", label: "Social media" }
|
|
108
|
+
]
|
|
109
|
+
}),
|
|
110
|
+
componentSlot("CarbonTextarea", {
|
|
111
|
+
labelText: "Description",
|
|
112
|
+
rows: 4,
|
|
113
|
+
defaultValue: "A social media and Google Ads campaign showcasing convenience and taste."
|
|
114
|
+
})
|
|
115
|
+
],
|
|
116
|
+
{ op: "stack", direction: "vertical", slots: [] }
|
|
117
|
+
)
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// src/templates/bright-events.ts
|
|
129
|
+
import { layout as layout2, componentSlot as componentSlot2, layoutSlot as layoutSlot2, fragmentSlot as fragmentSlot2 } from "@echothink-ui/layout";
|
|
130
|
+
var taskColumns = [
|
|
131
|
+
{ key: "task", header: "Task" },
|
|
132
|
+
{ key: "statusLabel", header: "Status" },
|
|
133
|
+
{ key: "due", header: "Due" },
|
|
134
|
+
{ key: "comments", header: "Comments", align: "end" }
|
|
135
|
+
];
|
|
136
|
+
var taskRows = [
|
|
137
|
+
{ id: "t1", task: "Schedule endocrinologist appointment", statusLabel: "At risk", due: "15d", comments: 6 },
|
|
138
|
+
{ id: "t2", task: "Help DStudio get more customers", statusLabel: "In progress", due: "15d", comments: 23 },
|
|
139
|
+
{ id: "t3", task: "Plan an event", statusLabel: "Running", due: "15d", comments: 7 },
|
|
140
|
+
{ id: "t4", task: "Return a package", statusLabel: "Completed", due: "15d", comments: 34 }
|
|
141
|
+
];
|
|
142
|
+
var brightEventsTemplate = {
|
|
143
|
+
id: "bright-events",
|
|
144
|
+
name: "Bright event workspace",
|
|
145
|
+
description: "Task list with toolbar filters and a task-detail inspector.",
|
|
146
|
+
stylePreset: "bright",
|
|
147
|
+
tags: ["tasks", "events", "list-detail", "bright"],
|
|
148
|
+
layout: layout2({
|
|
149
|
+
id: "bright-events",
|
|
150
|
+
type: "PageLayout.DataGridInspector",
|
|
151
|
+
styleIntent: { preset: "bright", taskMode: "crud" },
|
|
152
|
+
slots: {
|
|
153
|
+
toolbar: fragmentSlot2(
|
|
154
|
+
[
|
|
155
|
+
componentSlot2("CarbonSearchInput", { placeholder: "Search tasks" }),
|
|
156
|
+
componentSlot2("CarbonButton", { label: "Filter", intent: "tertiary" }),
|
|
157
|
+
componentSlot2("CarbonButton", { label: "New task", intent: "primary" })
|
|
158
|
+
],
|
|
159
|
+
{ op: "stack", direction: "horizontal", slots: [] }
|
|
160
|
+
),
|
|
161
|
+
grid: componentSlot2("CarbonDataTable", { rows: taskRows, columns: taskColumns, rowKey: "id" }),
|
|
162
|
+
inspector: layoutSlot2(
|
|
163
|
+
layout2({
|
|
164
|
+
id: "task-detail",
|
|
165
|
+
type: "Primitive.Panel",
|
|
166
|
+
styleIntent: { surface: "panel" },
|
|
167
|
+
slots: {
|
|
168
|
+
header: componentSlot2("CarbonSectionHeader", { title: "Task detail" }),
|
|
169
|
+
body: fragmentSlot2(
|
|
170
|
+
[
|
|
171
|
+
componentSlot2("CarbonStatusDot", { status: "in-progress", label: "15d left" }),
|
|
172
|
+
componentSlot2("CarbonBadge", { severity: "info", children: "Medium priority" }),
|
|
173
|
+
componentSlot2("CarbonTextInput", { labelText: "Owner", defaultValue: "Alex Rivera" }),
|
|
174
|
+
componentSlot2("CarbonTextarea", { labelText: "Notes", rows: 5, defaultValue: "" }),
|
|
175
|
+
componentSlot2("CarbonButton", { label: "Mark complete", intent: "primary" })
|
|
176
|
+
],
|
|
177
|
+
{ op: "stack", direction: "vertical", slots: [] }
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
})
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
})
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
// src/templates/glass-flowchart.ts
|
|
187
|
+
import { layout as layout3, componentSlot as componentSlot3, layoutSlot as layoutSlot3, fragmentSlot as fragmentSlot3 } from "@echothink-ui/layout";
|
|
188
|
+
var throughput = [
|
|
189
|
+
{ label: "00:00", value: 120 },
|
|
190
|
+
{ label: "04:00", value: 98 },
|
|
191
|
+
{ label: "08:00", value: 187 },
|
|
192
|
+
{ label: "12:00", value: 240 },
|
|
193
|
+
{ label: "16:00", value: 210 },
|
|
194
|
+
{ label: "20:00", value: 168 }
|
|
195
|
+
];
|
|
196
|
+
var glassFlowchartTemplate = {
|
|
197
|
+
id: "glass-flowchart",
|
|
198
|
+
name: "Liquid glass flowchart",
|
|
199
|
+
description: "Visualization canvas with KPI overlays and a node-config inspector.",
|
|
200
|
+
stylePreset: "glass",
|
|
201
|
+
tags: ["canvas", "flow", "analytics", "glass"],
|
|
202
|
+
layout: layout3({
|
|
203
|
+
id: "glass-flowchart",
|
|
204
|
+
type: "PageLayout.CanvasInspector",
|
|
205
|
+
styleIntent: { preset: "glass", taskMode: "authoring", visualEmphasis: "immersive" },
|
|
206
|
+
slots: {
|
|
207
|
+
toolbar: fragmentSlot3(
|
|
208
|
+
[
|
|
209
|
+
componentSlot3("CarbonSectionHeader", { title: "Flow studio" }),
|
|
210
|
+
componentSlot3("CarbonButton", { label: "Run", intent: "primary" }),
|
|
211
|
+
componentSlot3("CarbonButton", { label: "Snapshot", intent: "tertiary" })
|
|
212
|
+
],
|
|
213
|
+
{ op: "stack", direction: "horizontal", slots: [] }
|
|
214
|
+
),
|
|
215
|
+
canvas: fragmentSlot3(
|
|
216
|
+
[
|
|
217
|
+
fragmentSlot3(
|
|
218
|
+
[
|
|
219
|
+
componentSlot3("CarbonKPIBlock", { title: "Throughput", value: "240/min", description: "peak 12:00" }),
|
|
220
|
+
componentSlot3("CarbonKPIBlock", { title: "Errors", value: "0.4%", description: "-0.1% today" }),
|
|
221
|
+
componentSlot3("CarbonKPIBlock", { title: "Latency p95", value: "182ms", description: "stable" })
|
|
222
|
+
],
|
|
223
|
+
{ op: "stack", direction: "horizontal", slots: [] }
|
|
224
|
+
),
|
|
225
|
+
componentSlot3("CarbonAreaChart", {
|
|
226
|
+
title: "Pipeline throughput",
|
|
227
|
+
data: throughput,
|
|
228
|
+
valueKey: "value"
|
|
229
|
+
})
|
|
230
|
+
],
|
|
231
|
+
{ op: "stack", direction: "vertical", slots: [] }
|
|
232
|
+
),
|
|
233
|
+
inspector: layoutSlot3(
|
|
234
|
+
layout3({
|
|
235
|
+
id: "node-config",
|
|
236
|
+
type: "Primitive.Panel",
|
|
237
|
+
styleIntent: { surface: "panel" },
|
|
238
|
+
slots: {
|
|
239
|
+
header: componentSlot3("CarbonSectionHeader", { title: "Node config" }),
|
|
240
|
+
body: fragmentSlot3(
|
|
241
|
+
[
|
|
242
|
+
componentSlot3("CarbonTextInput", { labelText: "Node name", defaultValue: "Transform" }),
|
|
243
|
+
componentSlot3("CarbonSelect", {
|
|
244
|
+
labelText: "Kind",
|
|
245
|
+
defaultValue: "map",
|
|
246
|
+
options: [
|
|
247
|
+
{ value: "map", label: "Map" },
|
|
248
|
+
{ value: "filter", label: "Filter" },
|
|
249
|
+
{ value: "aggregate", label: "Aggregate" }
|
|
250
|
+
]
|
|
251
|
+
}),
|
|
252
|
+
componentSlot3("CarbonToggle", { labelText: "Enabled", defaultToggled: true }),
|
|
253
|
+
componentSlot3("CarbonTextarea", { labelText: "Expression", rows: 4, defaultValue: "" })
|
|
254
|
+
],
|
|
255
|
+
{ op: "stack", direction: "vertical", slots: [] }
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
})
|
|
259
|
+
)
|
|
260
|
+
}
|
|
261
|
+
})
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
// src/templates/studio-dark-check-box.ts
|
|
265
|
+
import { layout as layout4, componentSlot as componentSlot4, fragmentSlot as fragmentSlot4 } from "@echothink-ui/layout";
|
|
266
|
+
var monitorColumns = [
|
|
267
|
+
{ key: "check", header: "Check" },
|
|
268
|
+
{ key: "statusLabel", header: "Status" },
|
|
269
|
+
{ key: "latency", header: "Latency", align: "end" },
|
|
270
|
+
{ key: "updated", header: "Updated" }
|
|
271
|
+
];
|
|
272
|
+
var monitorRows = [
|
|
273
|
+
{ id: "m1", check: "Gateway /execute", statusLabel: "Healthy", latency: "42ms", updated: "now" },
|
|
274
|
+
{ id: "m2", check: "Worker queue lease", statusLabel: "Healthy", latency: "11ms", updated: "now" },
|
|
275
|
+
{ id: "m3", check: "Outcome committer", statusLabel: "Degraded", latency: "318ms", updated: "1m" },
|
|
276
|
+
{ id: "m4", check: "Artifact store", statusLabel: "Healthy", latency: "73ms", updated: "now" }
|
|
277
|
+
];
|
|
278
|
+
var trend = [
|
|
279
|
+
{ label: "T-5", value: 99.9 },
|
|
280
|
+
{ label: "T-4", value: 99.8 },
|
|
281
|
+
{ label: "T-3", value: 99.95 },
|
|
282
|
+
{ label: "T-2", value: 99.7 },
|
|
283
|
+
{ label: "T-1", value: 99.85 }
|
|
284
|
+
];
|
|
285
|
+
var studioDarkCheckBoxTemplate = {
|
|
286
|
+
id: "studio-dark-check-box",
|
|
287
|
+
name: "Studio dark monitoring",
|
|
288
|
+
description: "Realtime ops board with metric strip, health table, and activity console.",
|
|
289
|
+
stylePreset: "studio-dark",
|
|
290
|
+
tags: ["monitoring", "ops", "realtime", "studio-dark"],
|
|
291
|
+
layout: layout4({
|
|
292
|
+
id: "studio-dark-check-box",
|
|
293
|
+
type: "PageLayout.MonitoringOps",
|
|
294
|
+
styleIntent: { preset: "studio-dark", taskMode: "monitoring", dataIntensity: "realtime" },
|
|
295
|
+
slots: {
|
|
296
|
+
toolbar: fragmentSlot4(
|
|
297
|
+
[
|
|
298
|
+
componentSlot4("CarbonSectionHeader", { title: "Runtime health" }),
|
|
299
|
+
componentSlot4("CarbonButton", { label: "Acknowledge", intent: "tertiary" }),
|
|
300
|
+
componentSlot4("CarbonButton", { label: "Snapshot", intent: "primary" })
|
|
301
|
+
],
|
|
302
|
+
{ op: "stack", direction: "horizontal", slots: [] }
|
|
303
|
+
),
|
|
304
|
+
metrics: fragmentSlot4(
|
|
305
|
+
[
|
|
306
|
+
componentSlot4("CarbonMetricTrend", { title: "Uptime", value: "99.85%", data: trend, valueKey: "value" }),
|
|
307
|
+
componentSlot4("CarbonKPIBlock", { title: "Active jobs", value: "37", description: "4 retrying" }),
|
|
308
|
+
componentSlot4("CarbonKPIBlock", { title: "Error rate", value: "0.6%", description: "+0.2%" })
|
|
309
|
+
],
|
|
310
|
+
{ op: "stack", direction: "horizontal", slots: [] }
|
|
311
|
+
),
|
|
312
|
+
primary: componentSlot4("CarbonDataTable", { rows: monitorRows, columns: monitorColumns, rowKey: "id" }),
|
|
313
|
+
console: componentSlot4("CarbonActivityList", {
|
|
314
|
+
items: [
|
|
315
|
+
{ id: "a1", title: "Outcome committer latency spike", time: "1m ago", severity: "warning" },
|
|
316
|
+
{ id: "a2", title: "Worker pool scaled to 8", time: "3m ago", severity: "info" },
|
|
317
|
+
{ id: "a3", title: "Snapshot snap-29 committed", time: "5m ago", severity: "success" }
|
|
318
|
+
]
|
|
319
|
+
})
|
|
320
|
+
}
|
|
321
|
+
})
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
// src/types.ts
|
|
325
|
+
function collectComponentNames(node) {
|
|
326
|
+
const names = /* @__PURE__ */ new Set();
|
|
327
|
+
const visitContent = (content) => {
|
|
328
|
+
switch (content.kind) {
|
|
329
|
+
case "component":
|
|
330
|
+
names.add(content.component);
|
|
331
|
+
break;
|
|
332
|
+
case "layout":
|
|
333
|
+
visitNode(content.layout);
|
|
334
|
+
break;
|
|
335
|
+
case "fragment":
|
|
336
|
+
content.items.forEach(visitContent);
|
|
337
|
+
break;
|
|
338
|
+
default:
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
const visitNode = (n) => {
|
|
343
|
+
for (const content of Object.values(n.slots)) visitContent(content);
|
|
344
|
+
const comp = n.composition;
|
|
345
|
+
if (comp?.op === "switch") comp.items.forEach((i) => visitContent(i.content));
|
|
346
|
+
if (comp?.op === "stepper") comp.steps.forEach((s) => visitContent(s.content));
|
|
347
|
+
};
|
|
348
|
+
visitNode(node);
|
|
349
|
+
return [...names].sort();
|
|
350
|
+
}
|
|
351
|
+
var TemplateRegistry = class {
|
|
352
|
+
items = /* @__PURE__ */ new Map();
|
|
353
|
+
register(def) {
|
|
354
|
+
this.items.set(def.id, def);
|
|
355
|
+
return this;
|
|
356
|
+
}
|
|
357
|
+
registerAll(defs) {
|
|
358
|
+
for (const def of defs) this.register(def);
|
|
359
|
+
return this;
|
|
360
|
+
}
|
|
361
|
+
get(id) {
|
|
362
|
+
return this.items.get(id);
|
|
363
|
+
}
|
|
364
|
+
has(id) {
|
|
365
|
+
return this.items.has(id);
|
|
366
|
+
}
|
|
367
|
+
list() {
|
|
368
|
+
return [...this.items.values()];
|
|
369
|
+
}
|
|
370
|
+
byPreset(preset) {
|
|
371
|
+
return this.list().filter((t) => t.stylePreset === preset);
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
// src/index.ts
|
|
376
|
+
var templateCatalog = [
|
|
377
|
+
softCardCampaignTemplate,
|
|
378
|
+
brightEventsTemplate,
|
|
379
|
+
glassFlowchartTemplate,
|
|
380
|
+
studioDarkCheckBoxTemplate
|
|
381
|
+
];
|
|
382
|
+
function createTemplateRegistry() {
|
|
383
|
+
return new TemplateRegistry().registerAll(templateCatalog);
|
|
384
|
+
}
|
|
385
|
+
export {
|
|
386
|
+
TemplateRegistry,
|
|
387
|
+
brightEventsTemplate,
|
|
388
|
+
collectComponentNames,
|
|
389
|
+
createTemplateRegistry,
|
|
390
|
+
glassFlowchartTemplate,
|
|
391
|
+
softCardCampaignTemplate,
|
|
392
|
+
studioDarkCheckBoxTemplate,
|
|
393
|
+
templateCatalog
|
|
394
|
+
};
|
|
395
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/templates/soft-card-campaign.ts","../src/templates/bright-events.ts","../src/templates/glass-flowchart.ts","../src/templates/studio-dark-check-box.ts","../src/types.ts","../src/index.ts"],"sourcesContent":["import { layout, componentSlot, layoutSlot, fragmentSlot } from \"@echothink-ui/layout\";\nimport type { TemplateDefinition } from \"../types.js\";\n\n/**\n * Soft-card campaign workspace — an `AdminShell` whose content is a master/detail\n * split: a campaign dashboard (KPIs + chart + table) beside a campaign-info form\n * panel. Replaces the former hard-coded `SoftCardCampaignTemplate`.\n */\nconst campaignColumns = [\n { key: \"name\", header: \"Campaign\" },\n { key: \"channel\", header: \"Channel\" },\n { key: \"statusLabel\", header: \"Status\" },\n { key: \"audience\", header: \"Audience\", align: \"end\" },\n { key: \"updated\", header: \"Updated\" },\n];\n\nconst campaignRows = [\n { id: \"c1\", name: \"Taste the Future\", channel: \"Instagram + Ads\", statusLabel: \"Running\", audience: \"10,000\", updated: \"24 Sep\" },\n { id: \"c2\", name: \"Cybersecurity Week\", channel: \"Facebook\", statusLabel: \"In Progress\", audience: \"6,420\", updated: \"22 Sep\" },\n { id: \"c3\", name: \"E-learning Essentials\", channel: \"Email\", statusLabel: \"Approval\", audience: \"3,890\", updated: \"21 Sep\" },\n];\n\nconst channelChart = [\n { label: \"Mon\", sent: 32, opened: 19 },\n { label: \"Tue\", sent: 46, opened: 22 },\n { label: \"Wed\", sent: 38, opened: 27 },\n { label: \"Thu\", sent: 54, opened: 31 },\n { label: \"Fri\", sent: 49, opened: 36 },\n];\n\nexport const softCardCampaignTemplate: TemplateDefinition = {\n id: \"soft-card-campaign\",\n name: \"Soft card campaign\",\n description: \"Campaign dashboard with a master/detail configuration panel.\",\n stylePreset: \"soft-card\",\n tags: [\"dashboard\", \"marketing\", \"crud\", \"soft-card\"],\n layout: layout({\n id: \"soft-card-campaign\",\n type: \"PageLayout.AdminShell\",\n styleIntent: { preset: \"soft-card\", taskMode: \"crud\" },\n slots: {\n topbar: fragmentSlot(\n [\n componentSlot(\"CarbonSectionHeader\", { title: \"Metricmap\" }),\n componentSlot(\"CarbonSearchInput\", { placeholder: \"Search campaigns\" }),\n componentSlot(\"CarbonButton\", { label: \"New campaign\", intent: \"primary\" }),\n ],\n { op: \"stack\", direction: \"horizontal\", slots: [] },\n ),\n navigation: componentSlot(\"CarbonSideNav\", {\n items: [\n { label: \"Summary of Key Metrics\", href: \"#summary\" },\n { label: \"Recent Campaigns\", href: \"#recent\", current: true },\n { label: \"Performance Snapshot\", href: \"#performance\" },\n { label: \"Integrations\", href: \"#integrations\" },\n { label: \"Social Media Metrics\", href: \"#social\" },\n ],\n }),\n content: layoutSlot(\n layout({\n id: \"campaign-split\",\n type: \"Primitive.SplitPane\",\n variant: \"horizontal\",\n composition: {\n op: \"parallel\",\n axis: \"x\",\n slots: [\"primary\", \"secondary\"],\n sizing: { primary: { basis: \"1.7fr\" }, secondary: { basis: \"1fr\" } },\n },\n slots: {\n primary: fragmentSlot(\n [\n componentSlot(\"CarbonPageHeader\", {\n title: \"Recent Campaign\",\n description: \"Campaign configuration, audience rules, and channel performance in one workspace.\",\n }),\n fragmentSlot(\n [\n componentSlot(\"CarbonKPIBlock\", { title: \"Sent\", value: \"203 Mail\", description: \"+6% from last week\" }),\n componentSlot(\"CarbonKPIBlock\", { title: \"Opened\", value: \"18%\", description: \"+3% from last week\" }),\n componentSlot(\"CarbonKPIBlock\", { title: \"Conversions\", value: \"42\", description: \"12 awaiting review\" }),\n ],\n { op: \"stack\", direction: \"horizontal\", slots: [] },\n ),\n componentSlot(\"CarbonChartBlock\", {\n title: \"Channel performance\",\n subtitle: \"Chosen period versus last period\",\n chartType: \"bar\",\n data: channelChart,\n series: [\n { key: \"sent\", label: \"Chosen period\" },\n { key: \"opened\", label: \"Last period\" },\n ],\n }),\n componentSlot(\"CarbonDataTable\", { rows: campaignRows, columns: campaignColumns, rowKey: \"id\" }),\n ],\n { op: \"stack\", direction: \"vertical\", slots: [] },\n ),\n secondary: layoutSlot(\n layout({\n id: \"campaign-form\",\n type: \"Primitive.Panel\",\n styleIntent: { surface: \"card\" },\n slots: {\n header: componentSlot(\"CarbonSectionHeader\", { title: \"Campaign Info\" }),\n body: fragmentSlot(\n [\n componentSlot(\"CarbonTextInput\", { labelText: \"Campaign name\", defaultValue: \"Taste the Future\" }),\n componentSlot(\"CarbonTextInput\", { labelText: \"Brand\", defaultValue: \"Damory Food Indonesia\" }),\n componentSlot(\"CarbonSelect\", {\n labelText: \"Channel\",\n defaultValue: \"instagram\",\n options: [\n { value: \"instagram\", label: \"Instagram + Google Ads\" },\n { value: \"email\", label: \"Email only\" },\n { value: \"social\", label: \"Social media\" },\n ],\n }),\n componentSlot(\"CarbonTextarea\", {\n labelText: \"Description\",\n rows: 4,\n defaultValue: \"A social media and Google Ads campaign showcasing convenience and taste.\",\n }),\n ],\n { op: \"stack\", direction: \"vertical\", slots: [] },\n ),\n },\n }),\n ),\n },\n }),\n ),\n },\n }),\n};\n","import { layout, componentSlot, layoutSlot, fragmentSlot } from \"@echothink-ui/layout\";\nimport type { TemplateDefinition } from \"../types.js\";\n\n/**\n * Bright event workspace — a `DataGridInspector`: a task list grid with a toolbar\n * and a task-detail inspector. Replaces the former `BrightEventWorkspaceTemplate`.\n */\nconst taskColumns = [\n { key: \"task\", header: \"Task\" },\n { key: \"statusLabel\", header: \"Status\" },\n { key: \"due\", header: \"Due\" },\n { key: \"comments\", header: \"Comments\", align: \"end\" },\n];\n\nconst taskRows = [\n { id: \"t1\", task: \"Schedule endocrinologist appointment\", statusLabel: \"At risk\", due: \"15d\", comments: 6 },\n { id: \"t2\", task: \"Help DStudio get more customers\", statusLabel: \"In progress\", due: \"15d\", comments: 23 },\n { id: \"t3\", task: \"Plan an event\", statusLabel: \"Running\", due: \"15d\", comments: 7 },\n { id: \"t4\", task: \"Return a package\", statusLabel: \"Completed\", due: \"15d\", comments: 34 },\n];\n\nexport const brightEventsTemplate: TemplateDefinition = {\n id: \"bright-events\",\n name: \"Bright event workspace\",\n description: \"Task list with toolbar filters and a task-detail inspector.\",\n stylePreset: \"bright\",\n tags: [\"tasks\", \"events\", \"list-detail\", \"bright\"],\n layout: layout({\n id: \"bright-events\",\n type: \"PageLayout.DataGridInspector\",\n styleIntent: { preset: \"bright\", taskMode: \"crud\" },\n slots: {\n toolbar: fragmentSlot(\n [\n componentSlot(\"CarbonSearchInput\", { placeholder: \"Search tasks\" }),\n componentSlot(\"CarbonButton\", { label: \"Filter\", intent: \"tertiary\" }),\n componentSlot(\"CarbonButton\", { label: \"New task\", intent: \"primary\" }),\n ],\n { op: \"stack\", direction: \"horizontal\", slots: [] },\n ),\n grid: componentSlot(\"CarbonDataTable\", { rows: taskRows, columns: taskColumns, rowKey: \"id\" }),\n inspector: layoutSlot(\n layout({\n id: \"task-detail\",\n type: \"Primitive.Panel\",\n styleIntent: { surface: \"panel\" },\n slots: {\n header: componentSlot(\"CarbonSectionHeader\", { title: \"Task detail\" }),\n body: fragmentSlot(\n [\n componentSlot(\"CarbonStatusDot\", { status: \"in-progress\", label: \"15d left\" }),\n componentSlot(\"CarbonBadge\", { severity: \"info\", children: \"Medium priority\" }),\n componentSlot(\"CarbonTextInput\", { labelText: \"Owner\", defaultValue: \"Alex Rivera\" }),\n componentSlot(\"CarbonTextarea\", { labelText: \"Notes\", rows: 5, defaultValue: \"\" }),\n componentSlot(\"CarbonButton\", { label: \"Mark complete\", intent: \"primary\" }),\n ],\n { op: \"stack\", direction: \"vertical\", slots: [] },\n ),\n },\n }),\n ),\n },\n }),\n};\n","import { layout, componentSlot, layoutSlot, fragmentSlot } from \"@echothink-ui/layout\";\nimport type { TemplateDefinition } from \"../types.js\";\n\n/**\n * Liquid-glass flowchart studio — a `CanvasInspector`: a full-bleed visualization\n * canvas with a floating toolbar and a node-config inspector. Replaces the former\n * `LiquidGlassFlowchartTemplate`.\n */\nconst throughput = [\n { label: \"00:00\", value: 120 },\n { label: \"04:00\", value: 98 },\n { label: \"08:00\", value: 187 },\n { label: \"12:00\", value: 240 },\n { label: \"16:00\", value: 210 },\n { label: \"20:00\", value: 168 },\n];\n\nexport const glassFlowchartTemplate: TemplateDefinition = {\n id: \"glass-flowchart\",\n name: \"Liquid glass flowchart\",\n description: \"Visualization canvas with KPI overlays and a node-config inspector.\",\n stylePreset: \"glass\",\n tags: [\"canvas\", \"flow\", \"analytics\", \"glass\"],\n layout: layout({\n id: \"glass-flowchart\",\n type: \"PageLayout.CanvasInspector\",\n styleIntent: { preset: \"glass\", taskMode: \"authoring\", visualEmphasis: \"immersive\" },\n slots: {\n toolbar: fragmentSlot(\n [\n componentSlot(\"CarbonSectionHeader\", { title: \"Flow studio\" }),\n componentSlot(\"CarbonButton\", { label: \"Run\", intent: \"primary\" }),\n componentSlot(\"CarbonButton\", { label: \"Snapshot\", intent: \"tertiary\" }),\n ],\n { op: \"stack\", direction: \"horizontal\", slots: [] },\n ),\n canvas: fragmentSlot(\n [\n fragmentSlot(\n [\n componentSlot(\"CarbonKPIBlock\", { title: \"Throughput\", value: \"240/min\", description: \"peak 12:00\" }),\n componentSlot(\"CarbonKPIBlock\", { title: \"Errors\", value: \"0.4%\", description: \"-0.1% today\" }),\n componentSlot(\"CarbonKPIBlock\", { title: \"Latency p95\", value: \"182ms\", description: \"stable\" }),\n ],\n { op: \"stack\", direction: \"horizontal\", slots: [] },\n ),\n componentSlot(\"CarbonAreaChart\", {\n title: \"Pipeline throughput\",\n data: throughput,\n valueKey: \"value\",\n }),\n ],\n { op: \"stack\", direction: \"vertical\", slots: [] },\n ),\n inspector: layoutSlot(\n layout({\n id: \"node-config\",\n type: \"Primitive.Panel\",\n styleIntent: { surface: \"panel\" },\n slots: {\n header: componentSlot(\"CarbonSectionHeader\", { title: \"Node config\" }),\n body: fragmentSlot(\n [\n componentSlot(\"CarbonTextInput\", { labelText: \"Node name\", defaultValue: \"Transform\" }),\n componentSlot(\"CarbonSelect\", {\n labelText: \"Kind\",\n defaultValue: \"map\",\n options: [\n { value: \"map\", label: \"Map\" },\n { value: \"filter\", label: \"Filter\" },\n { value: \"aggregate\", label: \"Aggregate\" },\n ],\n }),\n componentSlot(\"CarbonToggle\", { labelText: \"Enabled\", defaultToggled: true }),\n componentSlot(\"CarbonTextarea\", { labelText: \"Expression\", rows: 4, defaultValue: \"\" }),\n ],\n { op: \"stack\", direction: \"vertical\", slots: [] },\n ),\n },\n }),\n ),\n },\n }),\n};\n","import { layout, componentSlot, fragmentSlot } from \"@echothink-ui/layout\";\nimport type { TemplateDefinition } from \"../types.js\";\n\n/**\n * Studio-dark monitoring board — a `MonitoringOps` layout: toolbar + metric strip\n * + primary table + console activity feed. Replaces the former\n * `StudioDarkCheckBoxTemplate`.\n */\nconst monitorColumns = [\n { key: \"check\", header: \"Check\" },\n { key: \"statusLabel\", header: \"Status\" },\n { key: \"latency\", header: \"Latency\", align: \"end\" },\n { key: \"updated\", header: \"Updated\" },\n];\n\nconst monitorRows = [\n { id: \"m1\", check: \"Gateway /execute\", statusLabel: \"Healthy\", latency: \"42ms\", updated: \"now\" },\n { id: \"m2\", check: \"Worker queue lease\", statusLabel: \"Healthy\", latency: \"11ms\", updated: \"now\" },\n { id: \"m3\", check: \"Outcome committer\", statusLabel: \"Degraded\", latency: \"318ms\", updated: \"1m\" },\n { id: \"m4\", check: \"Artifact store\", statusLabel: \"Healthy\", latency: \"73ms\", updated: \"now\" },\n];\n\nconst trend = [\n { label: \"T-5\", value: 99.9 },\n { label: \"T-4\", value: 99.8 },\n { label: \"T-3\", value: 99.95 },\n { label: \"T-2\", value: 99.7 },\n { label: \"T-1\", value: 99.85 },\n];\n\nexport const studioDarkCheckBoxTemplate: TemplateDefinition = {\n id: \"studio-dark-check-box\",\n name: \"Studio dark monitoring\",\n description: \"Realtime ops board with metric strip, health table, and activity console.\",\n stylePreset: \"studio-dark\",\n tags: [\"monitoring\", \"ops\", \"realtime\", \"studio-dark\"],\n layout: layout({\n id: \"studio-dark-check-box\",\n type: \"PageLayout.MonitoringOps\",\n styleIntent: { preset: \"studio-dark\", taskMode: \"monitoring\", dataIntensity: \"realtime\" },\n slots: {\n toolbar: fragmentSlot(\n [\n componentSlot(\"CarbonSectionHeader\", { title: \"Runtime health\" }),\n componentSlot(\"CarbonButton\", { label: \"Acknowledge\", intent: \"tertiary\" }),\n componentSlot(\"CarbonButton\", { label: \"Snapshot\", intent: \"primary\" }),\n ],\n { op: \"stack\", direction: \"horizontal\", slots: [] },\n ),\n metrics: fragmentSlot(\n [\n componentSlot(\"CarbonMetricTrend\", { title: \"Uptime\", value: \"99.85%\", data: trend, valueKey: \"value\" }),\n componentSlot(\"CarbonKPIBlock\", { title: \"Active jobs\", value: \"37\", description: \"4 retrying\" }),\n componentSlot(\"CarbonKPIBlock\", { title: \"Error rate\", value: \"0.6%\", description: \"+0.2%\" }),\n ],\n { op: \"stack\", direction: \"horizontal\", slots: [] },\n ),\n primary: componentSlot(\"CarbonDataTable\", { rows: monitorRows, columns: monitorColumns, rowKey: \"id\" }),\n console: componentSlot(\"CarbonActivityList\", {\n items: [\n { id: \"a1\", title: \"Outcome committer latency spike\", time: \"1m ago\", severity: \"warning\" },\n { id: \"a2\", title: \"Worker pool scaled to 8\", time: \"3m ago\", severity: \"info\" },\n { id: \"a3\", title: \"Snapshot snap-29 committed\", time: \"5m ago\", severity: \"success\" },\n ],\n }),\n },\n }),\n};\n","/**\n * `@echothink-ui/templates` — template contract.\n *\n * A template is now a GENUINE template: a declarative `LayoutNode` AST built from\n * `@echothink-ui/layout` page-layouts and populated with `@echothink-ui`\n * components (referenced by name). It is rendered through the layout engine\n * (`LayoutRoot`) and can be flattened into Puck data for editing in the builder.\n */\nimport type { EthStylePreset } from \"@echothink-ui/style\";\nimport type { LayoutNode, SlotContent } from \"@echothink-ui/layout\";\n\nexport interface TemplateDefinition {\n /** Stable id. */\n id: string;\n /** Human label. */\n name: string;\n /** One-line description. */\n description: string;\n /** Preset this template was designed around (the engine still infers per-slot). */\n stylePreset: EthStylePreset;\n /** Discovery tags. */\n tags: string[];\n /** The root layout AST. */\n layout: LayoutNode;\n}\n\n/** Walk a layout AST and collect every referenced component name (deduped). */\nexport function collectComponentNames(node: LayoutNode): string[] {\n const names = new Set<string>();\n const visitContent = (content: SlotContent): void => {\n switch (content.kind) {\n case \"component\":\n names.add(content.component);\n break;\n case \"layout\":\n visitNode(content.layout);\n break;\n case \"fragment\":\n content.items.forEach(visitContent);\n break;\n default:\n break;\n }\n };\n const visitNode = (n: LayoutNode): void => {\n for (const content of Object.values(n.slots)) visitContent(content);\n const comp = n.composition;\n if (comp?.op === \"switch\") comp.items.forEach((i) => visitContent(i.content));\n if (comp?.op === \"stepper\") comp.steps.forEach((s) => visitContent(s.content));\n };\n visitNode(node);\n return [...names].sort();\n}\n\n/** A tiny registry over template definitions. */\nexport class TemplateRegistry {\n private readonly items = new Map<string, TemplateDefinition>();\n\n register(def: TemplateDefinition): this {\n this.items.set(def.id, def);\n return this;\n }\n\n registerAll(defs: readonly TemplateDefinition[]): this {\n for (const def of defs) this.register(def);\n return this;\n }\n\n get(id: string): TemplateDefinition | undefined {\n return this.items.get(id);\n }\n\n has(id: string): boolean {\n return this.items.has(id);\n }\n\n list(): TemplateDefinition[] {\n return [...this.items.values()];\n }\n\n byPreset(preset: EthStylePreset): TemplateDefinition[] {\n return this.list().filter((t) => t.stylePreset === preset);\n }\n}\n","/**\n * `@echothink-ui/templates` — genuine templates built from the layout system.\n *\n * Each template is a declarative `LayoutNode` AST composed from\n * `@echothink-ui/layout` page-layouts and populated with `@echothink-ui`\n * components by name. Render via `LayoutRoot` (with a component resolver) or\n * flatten into Puck data for editing in the builder.\n */\nimport { softCardCampaignTemplate } from \"./templates/soft-card-campaign.js\";\nimport { brightEventsTemplate } from \"./templates/bright-events.js\";\nimport { glassFlowchartTemplate } from \"./templates/glass-flowchart.js\";\nimport { studioDarkCheckBoxTemplate } from \"./templates/studio-dark-check-box.js\";\nimport { TemplateRegistry, type TemplateDefinition } from \"./types.js\";\n\nexport type { TemplateDefinition } from \"./types.js\";\nexport { TemplateRegistry, collectComponentNames } from \"./types.js\";\n\nexport {\n softCardCampaignTemplate,\n brightEventsTemplate,\n glassFlowchartTemplate,\n studioDarkCheckBoxTemplate,\n};\n\n/** All builtin templates, in catalog order. */\nexport const templateCatalog: TemplateDefinition[] = [\n softCardCampaignTemplate,\n brightEventsTemplate,\n glassFlowchartTemplate,\n studioDarkCheckBoxTemplate,\n];\n\nexport type TemplateId = (typeof templateCatalog)[number][\"id\"];\n\n/** A registry preloaded with the builtin templates. */\nexport function createTemplateRegistry(): TemplateRegistry {\n return new TemplateRegistry().registerAll(templateCatalog);\n}\n"],"mappings":"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","names":["layout","componentSlot","layoutSlot","fragmentSlot","layout","componentSlot","layoutSlot","fragmentSlot","layout","componentSlot","fragmentSlot"]}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@echothink-ui/templates` — template contract.
|
|
3
|
+
*
|
|
4
|
+
* A template is now a GENUINE template: a declarative `LayoutNode` AST built from
|
|
5
|
+
* `@echothink-ui/layout` page-layouts and populated with `@echothink-ui`
|
|
6
|
+
* components (referenced by name). It is rendered through the layout engine
|
|
7
|
+
* (`LayoutRoot`) and can be flattened into Puck data for editing in the builder.
|
|
8
|
+
*/
|
|
9
|
+
import type { EthStylePreset } from "@echothink-ui/style";
|
|
10
|
+
import type { LayoutNode } from "@echothink-ui/layout";
|
|
11
|
+
export interface TemplateDefinition {
|
|
12
|
+
/** Stable id. */
|
|
13
|
+
id: string;
|
|
14
|
+
/** Human label. */
|
|
15
|
+
name: string;
|
|
16
|
+
/** One-line description. */
|
|
17
|
+
description: string;
|
|
18
|
+
/** Preset this template was designed around (the engine still infers per-slot). */
|
|
19
|
+
stylePreset: EthStylePreset;
|
|
20
|
+
/** Discovery tags. */
|
|
21
|
+
tags: string[];
|
|
22
|
+
/** The root layout AST. */
|
|
23
|
+
layout: LayoutNode;
|
|
24
|
+
}
|
|
25
|
+
/** Walk a layout AST and collect every referenced component name (deduped). */
|
|
26
|
+
export declare function collectComponentNames(node: LayoutNode): string[];
|
|
27
|
+
/** A tiny registry over template definitions. */
|
|
28
|
+
export declare class TemplateRegistry {
|
|
29
|
+
private readonly items;
|
|
30
|
+
register(def: TemplateDefinition): this;
|
|
31
|
+
registerAll(defs: readonly TemplateDefinition[]): this;
|
|
32
|
+
get(id: string): TemplateDefinition | undefined;
|
|
33
|
+
has(id: string): boolean;
|
|
34
|
+
list(): TemplateDefinition[];
|
|
35
|
+
byPreset(preset: EthStylePreset): TemplateDefinition[];
|
|
36
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@echothink-ui/templates",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"src",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"peerDependencies": {
|
|
22
|
+
"react": ">=18.3.0",
|
|
23
|
+
"react-dom": ">=18.3.0"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@echothink-ui/layout": "0.2.0",
|
|
27
|
+
"@echothink-ui/style": "0.2.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^24.9.1",
|
|
31
|
+
"@types/react": "^19.2.2",
|
|
32
|
+
"@types/react-dom": "^19.2.2"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsup src/index.ts --format esm,cjs --sourcemap --clean --external react --external react-dom && tsc -p tsconfig.json --declaration --emitDeclarationOnly --noEmit false --outDir dist",
|
|
39
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
40
|
+
"test": "vitest run --root . --config ../echothink-UI-components/vitest.config.ts --passWithNoTests",
|
|
41
|
+
"lint": "eslint src"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { renderToStaticMarkup } from "react-dom/server";
|
|
4
|
+
import {
|
|
5
|
+
LayoutRoot,
|
|
6
|
+
createDefaultLayoutRegistry,
|
|
7
|
+
validateLayout,
|
|
8
|
+
type ComponentResolver,
|
|
9
|
+
} from "@echothink-ui/layout";
|
|
10
|
+
import {
|
|
11
|
+
templateCatalog,
|
|
12
|
+
createTemplateRegistry,
|
|
13
|
+
collectComponentNames,
|
|
14
|
+
} from "../index.js";
|
|
15
|
+
|
|
16
|
+
const registry = createDefaultLayoutRegistry();
|
|
17
|
+
|
|
18
|
+
// Resolve any component name to a marker div so render is library-agnostic.
|
|
19
|
+
const resolver: ComponentResolver = (name) => {
|
|
20
|
+
const C = (props: Record<string, unknown>) =>
|
|
21
|
+
React.createElement(
|
|
22
|
+
"div",
|
|
23
|
+
{ "data-c": name },
|
|
24
|
+
typeof props.title === "string" ? props.title : name,
|
|
25
|
+
);
|
|
26
|
+
return C;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
describe("templateCatalog", () => {
|
|
30
|
+
it("has the 4 migrated templates with distinct presets", () => {
|
|
31
|
+
expect(templateCatalog.map((t) => t.id).sort()).toEqual([
|
|
32
|
+
"bright-events",
|
|
33
|
+
"glass-flowchart",
|
|
34
|
+
"soft-card-campaign",
|
|
35
|
+
"studio-dark-check-box",
|
|
36
|
+
]);
|
|
37
|
+
const presets = new Set(templateCatalog.map((t) => t.stylePreset));
|
|
38
|
+
expect(presets.size).toBe(4);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("every template validates against the layout registry", () => {
|
|
42
|
+
for (const t of templateCatalog) {
|
|
43
|
+
const diags = validateLayout(t.layout, registry).filter((d) => d.level === "error");
|
|
44
|
+
expect(diags, `${t.id} should have no layout errors`).toHaveLength(0);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("collectComponentNames returns the referenced components", () => {
|
|
49
|
+
const names = collectComponentNames(templateCatalog[0].layout);
|
|
50
|
+
expect(names).toContain("CarbonDataTable");
|
|
51
|
+
expect(names).toContain("CarbonKPIBlock");
|
|
52
|
+
expect(names.length).toBeGreaterThan(3);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("registry round-trips by id", () => {
|
|
56
|
+
const reg = createTemplateRegistry();
|
|
57
|
+
expect(reg.get("glass-flowchart")?.stylePreset).toBe("glass");
|
|
58
|
+
expect(reg.byPreset("studio-dark")).toHaveLength(1);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("renders every template through LayoutRoot without throwing", () => {
|
|
62
|
+
for (const t of templateCatalog) {
|
|
63
|
+
const html = renderToStaticMarkup(
|
|
64
|
+
React.createElement(LayoutRoot, {
|
|
65
|
+
node: t.layout,
|
|
66
|
+
componentResolver: resolver,
|
|
67
|
+
observeViewport: false,
|
|
68
|
+
}),
|
|
69
|
+
);
|
|
70
|
+
expect(html).toContain("data-c=");
|
|
71
|
+
expect(html).not.toContain("Unknown layout type");
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
});
|