@glyphjs/components 0.1.0 → 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 +2347 -351
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +184 -11
- package/dist/index.d.ts +184 -11
- package/dist/index.js +2333 -353
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -46,7 +46,7 @@ var CALLOUT_LABELS = {
|
|
|
46
46
|
};
|
|
47
47
|
function Callout({ data }) {
|
|
48
48
|
const { type, title, content } = data;
|
|
49
|
-
const
|
|
49
|
+
const containerStyle11 = {
|
|
50
50
|
backgroundColor: `var(--glyph-callout-${type}-bg)`,
|
|
51
51
|
borderLeft: `4px solid var(--glyph-callout-${type}-border)`,
|
|
52
52
|
borderRadius: "var(--glyph-radius-md, 0.1875rem)",
|
|
@@ -63,7 +63,7 @@ function Callout({ data }) {
|
|
|
63
63
|
fontSize: "1.25em",
|
|
64
64
|
lineHeight: 1
|
|
65
65
|
};
|
|
66
|
-
const
|
|
66
|
+
const bodyStyle3 = {
|
|
67
67
|
flex: 1,
|
|
68
68
|
minWidth: 0
|
|
69
69
|
};
|
|
@@ -71,9 +71,9 @@ function Callout({ data }) {
|
|
|
71
71
|
fontWeight: 700,
|
|
72
72
|
marginBottom: "var(--glyph-spacing-xs, 0.25rem)"
|
|
73
73
|
};
|
|
74
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "note", "aria-label": CALLOUT_LABELS[type], style:
|
|
74
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "note", "aria-label": CALLOUT_LABELS[type], style: containerStyle11, children: [
|
|
75
75
|
/* @__PURE__ */ jsxRuntime.jsx("span", { style: iconStyle, "aria-hidden": "true", children: CALLOUT_ICONS[type] }),
|
|
76
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style:
|
|
76
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: bodyStyle3, children: [
|
|
77
77
|
title && /* @__PURE__ */ jsxRuntime.jsx("div", { style: titleStyle2, children: title }),
|
|
78
78
|
/* @__PURE__ */ jsxRuntime.jsx("div", { children: content })
|
|
79
79
|
] })
|
|
@@ -278,9 +278,104 @@ function computeScales(width, height, type, series, xKey, yKey, margin) {
|
|
|
278
278
|
const yScale = d32__namespace.scaleLinear().domain([yMin, yMax]).nice().range([innerHeight, 0]);
|
|
279
279
|
return { xScale, xScalePoint, yScale, innerWidth, innerHeight };
|
|
280
280
|
}
|
|
281
|
+
function renderAllSeries(g, type, series, scales, xKey, yKey, showTooltip, hideTooltip) {
|
|
282
|
+
const { xScale, xScalePoint, yScale, innerHeight } = scales;
|
|
283
|
+
series.forEach((s, i) => {
|
|
284
|
+
const color3 = COLOR_SCHEME[i % COLOR_SCHEME.length] ?? "#333";
|
|
285
|
+
switch (type) {
|
|
286
|
+
case "line":
|
|
287
|
+
renderLineSeries(
|
|
288
|
+
g,
|
|
289
|
+
s.data,
|
|
290
|
+
xScalePoint,
|
|
291
|
+
yScale,
|
|
292
|
+
yKey,
|
|
293
|
+
xKey,
|
|
294
|
+
color3,
|
|
295
|
+
i,
|
|
296
|
+
s.name,
|
|
297
|
+
showTooltip,
|
|
298
|
+
hideTooltip
|
|
299
|
+
);
|
|
300
|
+
break;
|
|
301
|
+
case "area":
|
|
302
|
+
renderAreaSeries(
|
|
303
|
+
g,
|
|
304
|
+
s.data,
|
|
305
|
+
xScalePoint,
|
|
306
|
+
yScale,
|
|
307
|
+
yKey,
|
|
308
|
+
xKey,
|
|
309
|
+
innerHeight,
|
|
310
|
+
color3,
|
|
311
|
+
i,
|
|
312
|
+
s.name,
|
|
313
|
+
showTooltip,
|
|
314
|
+
hideTooltip
|
|
315
|
+
);
|
|
316
|
+
break;
|
|
317
|
+
case "bar":
|
|
318
|
+
renderBarSeries(
|
|
319
|
+
g,
|
|
320
|
+
s.data,
|
|
321
|
+
xScale,
|
|
322
|
+
yScale,
|
|
323
|
+
yKey,
|
|
324
|
+
xKey,
|
|
325
|
+
color3,
|
|
326
|
+
i,
|
|
327
|
+
series.length,
|
|
328
|
+
innerHeight,
|
|
329
|
+
s.name,
|
|
330
|
+
showTooltip,
|
|
331
|
+
hideTooltip
|
|
332
|
+
);
|
|
333
|
+
break;
|
|
334
|
+
case "ohlc":
|
|
335
|
+
renderOHLCSeries(
|
|
336
|
+
g,
|
|
337
|
+
s.data,
|
|
338
|
+
xScale,
|
|
339
|
+
xScalePoint,
|
|
340
|
+
yScale,
|
|
341
|
+
scales.innerWidth,
|
|
342
|
+
s.name,
|
|
343
|
+
showTooltip,
|
|
344
|
+
hideTooltip
|
|
345
|
+
);
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
function attachChartInteraction(g, type, series, xKey, yKey, block, onInteraction) {
|
|
351
|
+
if (!onInteraction) return;
|
|
352
|
+
series.forEach((s, seriesIdx) => {
|
|
353
|
+
const className = type === "bar" ? `bar-${String(seriesIdx)}` : `dot-${String(seriesIdx)}`;
|
|
354
|
+
g.selectAll(`.${className}`).on(
|
|
355
|
+
"click",
|
|
356
|
+
(_event, d) => {
|
|
357
|
+
const dataIdx = s.data.indexOf(d);
|
|
358
|
+
onInteraction({
|
|
359
|
+
kind: "chart-select",
|
|
360
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
361
|
+
blockId: block.id,
|
|
362
|
+
blockType: block.type,
|
|
363
|
+
payload: {
|
|
364
|
+
seriesIndex: seriesIdx,
|
|
365
|
+
dataIndex: dataIdx >= 0 ? dataIdx : 0,
|
|
366
|
+
label: String(d[xKey] ?? ""),
|
|
367
|
+
value: getNumericValue(d, yKey)
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
);
|
|
372
|
+
});
|
|
373
|
+
}
|
|
281
374
|
function Chart({
|
|
282
375
|
data,
|
|
283
|
-
|
|
376
|
+
block,
|
|
377
|
+
container: containerCtx,
|
|
378
|
+
onInteraction
|
|
284
379
|
}) {
|
|
285
380
|
const containerRef = react.useRef(null);
|
|
286
381
|
const svgRef = react.useRef(null);
|
|
@@ -333,76 +428,19 @@ function Chart({
|
|
|
333
428
|
if (!svg || series.length === 0) return;
|
|
334
429
|
const sel = d32__namespace.select(svg);
|
|
335
430
|
sel.selectAll("*").remove();
|
|
336
|
-
const { xScale, xScalePoint, yScale, innerWidth, innerHeight } = scales;
|
|
337
431
|
const g = sel.append("g").attr("transform", `translate(${String(margin.left)},${String(margin.top)})`);
|
|
338
|
-
renderAxes(
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
xKey,
|
|
351
|
-
color3,
|
|
352
|
-
i,
|
|
353
|
-
s.name,
|
|
354
|
-
showTooltip,
|
|
355
|
-
hideTooltip
|
|
356
|
-
);
|
|
357
|
-
break;
|
|
358
|
-
case "area":
|
|
359
|
-
renderAreaSeries(
|
|
360
|
-
g,
|
|
361
|
-
s.data,
|
|
362
|
-
xScalePoint,
|
|
363
|
-
yScale,
|
|
364
|
-
yKey,
|
|
365
|
-
xKey,
|
|
366
|
-
innerHeight,
|
|
367
|
-
color3,
|
|
368
|
-
i,
|
|
369
|
-
s.name,
|
|
370
|
-
showTooltip,
|
|
371
|
-
hideTooltip
|
|
372
|
-
);
|
|
373
|
-
break;
|
|
374
|
-
case "bar":
|
|
375
|
-
renderBarSeries(
|
|
376
|
-
g,
|
|
377
|
-
s.data,
|
|
378
|
-
xScale,
|
|
379
|
-
yScale,
|
|
380
|
-
yKey,
|
|
381
|
-
xKey,
|
|
382
|
-
color3,
|
|
383
|
-
i,
|
|
384
|
-
series.length,
|
|
385
|
-
innerHeight,
|
|
386
|
-
s.name,
|
|
387
|
-
showTooltip,
|
|
388
|
-
hideTooltip
|
|
389
|
-
);
|
|
390
|
-
break;
|
|
391
|
-
case "ohlc":
|
|
392
|
-
renderOHLCSeries(
|
|
393
|
-
g,
|
|
394
|
-
s.data,
|
|
395
|
-
xScale,
|
|
396
|
-
xScalePoint,
|
|
397
|
-
yScale,
|
|
398
|
-
innerWidth,
|
|
399
|
-
s.name,
|
|
400
|
-
showTooltip,
|
|
401
|
-
hideTooltip
|
|
402
|
-
);
|
|
403
|
-
break;
|
|
404
|
-
}
|
|
405
|
-
});
|
|
432
|
+
renderAxes(
|
|
433
|
+
g,
|
|
434
|
+
scales.xScale,
|
|
435
|
+
scales.yScale,
|
|
436
|
+
xAxis,
|
|
437
|
+
yAxis,
|
|
438
|
+
scales.innerWidth,
|
|
439
|
+
scales.innerHeight
|
|
440
|
+
);
|
|
441
|
+
renderGridLines(g, scales.yScale, scales.innerWidth);
|
|
442
|
+
renderAllSeries(g, type, series, scales, xKey, yKey, showTooltip, hideTooltip);
|
|
443
|
+
attachChartInteraction(g, type, series, xKey, yKey, block, onInteraction);
|
|
406
444
|
if (legend) {
|
|
407
445
|
renderLegend(sel, series, margin.left, margin.top, isCompact ? "10px" : void 0);
|
|
408
446
|
}
|
|
@@ -418,7 +456,9 @@ function Chart({
|
|
|
418
456
|
margin,
|
|
419
457
|
isCompact,
|
|
420
458
|
showTooltip,
|
|
421
|
-
hideTooltip
|
|
459
|
+
hideTooltip,
|
|
460
|
+
onInteraction,
|
|
461
|
+
block
|
|
422
462
|
]);
|
|
423
463
|
const ariaLabel = `${type} chart with ${String(series.length)} series: ${series.map((s) => s.name).join(", ")}`;
|
|
424
464
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -492,14 +532,14 @@ var STATUS_LABELS = {
|
|
|
492
532
|
};
|
|
493
533
|
function Steps({ data }) {
|
|
494
534
|
const { steps } = data;
|
|
495
|
-
const
|
|
535
|
+
const listStyle2 = {
|
|
496
536
|
listStyle: "none",
|
|
497
537
|
padding: 0,
|
|
498
538
|
margin: "var(--glyph-spacing-sm, 0.5rem) 0",
|
|
499
539
|
fontFamily: "var(--glyph-font-body, system-ui, sans-serif)",
|
|
500
540
|
color: "var(--glyph-text, #1a2035)"
|
|
501
541
|
};
|
|
502
|
-
return /* @__PURE__ */ jsxRuntime.jsx("ol", { role: "list", style:
|
|
542
|
+
return /* @__PURE__ */ jsxRuntime.jsx("ol", { role: "list", style: listStyle2, children: steps.map((step, index) => {
|
|
503
543
|
const status = step.status ?? "pending";
|
|
504
544
|
const isLast = index === steps.length - 1;
|
|
505
545
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -646,7 +686,95 @@ function TableAggregationFooter({
|
|
|
646
686
|
);
|
|
647
687
|
}) }) });
|
|
648
688
|
}
|
|
649
|
-
function
|
|
689
|
+
function TableHead({
|
|
690
|
+
columns,
|
|
691
|
+
sort,
|
|
692
|
+
hasFilters,
|
|
693
|
+
filters,
|
|
694
|
+
onSort,
|
|
695
|
+
onHeaderKeyDown,
|
|
696
|
+
onFilterChange
|
|
697
|
+
}) {
|
|
698
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("thead", { children: [
|
|
699
|
+
/* @__PURE__ */ jsxRuntime.jsx("tr", { children: columns.map((col) => {
|
|
700
|
+
const isSorted = sort.column === col.key;
|
|
701
|
+
const direction = isSorted ? sort.direction : "none";
|
|
702
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
703
|
+
"th",
|
|
704
|
+
{
|
|
705
|
+
scope: "col",
|
|
706
|
+
"aria-sort": col.sortable ? direction : void 0,
|
|
707
|
+
tabIndex: col.sortable ? 0 : void 0,
|
|
708
|
+
role: col.sortable ? "columnheader" : void 0,
|
|
709
|
+
onClick: col.sortable ? () => onSort(col.key) : void 0,
|
|
710
|
+
onKeyDown: col.sortable ? (e) => onHeaderKeyDown(e, col.key) : void 0,
|
|
711
|
+
style: {
|
|
712
|
+
padding: "var(--glyph-table-cell-padding, 8px 12px)",
|
|
713
|
+
textAlign: "left",
|
|
714
|
+
borderBottom: "2px solid var(--glyph-table-border, #d0d8e4)",
|
|
715
|
+
background: "var(--glyph-table-header-bg, #e8ecf3)",
|
|
716
|
+
color: "var(--glyph-table-header-color, inherit)",
|
|
717
|
+
cursor: col.sortable ? "pointer" : "default",
|
|
718
|
+
userSelect: col.sortable ? "none" : void 0,
|
|
719
|
+
whiteSpace: "nowrap"
|
|
720
|
+
},
|
|
721
|
+
children: [
|
|
722
|
+
col.label,
|
|
723
|
+
col.sortable ? sortIndicator(direction) : ""
|
|
724
|
+
]
|
|
725
|
+
},
|
|
726
|
+
col.key
|
|
727
|
+
);
|
|
728
|
+
}) }),
|
|
729
|
+
hasFilters && /* @__PURE__ */ jsxRuntime.jsx("tr", { children: columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
730
|
+
"th",
|
|
731
|
+
{
|
|
732
|
+
scope: "col",
|
|
733
|
+
style: { padding: "4px 8px", fontWeight: "normal" },
|
|
734
|
+
children: col.filterable ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
735
|
+
"input",
|
|
736
|
+
{
|
|
737
|
+
type: "text",
|
|
738
|
+
"aria-label": `Filter ${col.label}`,
|
|
739
|
+
placeholder: `Filter ${col.label}...`,
|
|
740
|
+
value: filters[col.key] ?? "",
|
|
741
|
+
onChange: (e) => onFilterChange(col.key, e.target.value),
|
|
742
|
+
style: {
|
|
743
|
+
width: "100%",
|
|
744
|
+
padding: "4px 6px",
|
|
745
|
+
border: "1px solid var(--glyph-table-border, #d0d8e4)",
|
|
746
|
+
borderRadius: "3px",
|
|
747
|
+
fontSize: "inherit",
|
|
748
|
+
boxSizing: "border-box",
|
|
749
|
+
background: "var(--glyph-surface, #e8ecf3)",
|
|
750
|
+
color: "var(--glyph-text, inherit)"
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
754
|
+
"span",
|
|
755
|
+
{
|
|
756
|
+
style: {
|
|
757
|
+
position: "absolute",
|
|
758
|
+
width: "1px",
|
|
759
|
+
height: "1px",
|
|
760
|
+
overflow: "hidden",
|
|
761
|
+
clip: "rect(0,0,0,0)",
|
|
762
|
+
whiteSpace: "nowrap"
|
|
763
|
+
},
|
|
764
|
+
children: `No filter for ${col.label}`
|
|
765
|
+
}
|
|
766
|
+
)
|
|
767
|
+
},
|
|
768
|
+
`filter-${col.key}`
|
|
769
|
+
)) })
|
|
770
|
+
] });
|
|
771
|
+
}
|
|
772
|
+
function Table({
|
|
773
|
+
data,
|
|
774
|
+
block,
|
|
775
|
+
container,
|
|
776
|
+
onInteraction
|
|
777
|
+
}) {
|
|
650
778
|
const { columns, rows, aggregation } = data;
|
|
651
779
|
const [sort, setSort] = react.useState({ column: "", direction: "none" });
|
|
652
780
|
const [filters, setFilters] = react.useState({});
|
|
@@ -679,12 +807,27 @@ function Table({ data, container }) {
|
|
|
679
807
|
});
|
|
680
808
|
}, [filteredRows, sort]);
|
|
681
809
|
const handleSort = (columnKey) => {
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
810
|
+
const newDirection = sort.column === columnKey ? nextDirection(sort.direction) : "ascending";
|
|
811
|
+
setSort({ column: columnKey, direction: newDirection });
|
|
812
|
+
if (onInteraction) {
|
|
813
|
+
const eventDir = newDirection === "ascending" ? "asc" : newDirection === "descending" ? "desc" : "none";
|
|
814
|
+
onInteraction({
|
|
815
|
+
kind: "table-sort",
|
|
816
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
817
|
+
blockId: block.id,
|
|
818
|
+
blockType: block.type,
|
|
819
|
+
payload: {
|
|
820
|
+
column: columnKey,
|
|
821
|
+
direction: eventDir,
|
|
822
|
+
state: {
|
|
823
|
+
sort: newDirection === "none" ? null : { column: columnKey, direction: eventDir },
|
|
824
|
+
filters,
|
|
825
|
+
visibleRowCount: filteredRows.length,
|
|
826
|
+
totalRowCount: rows.length
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
});
|
|
830
|
+
}
|
|
688
831
|
};
|
|
689
832
|
const handleHeaderKeyDown = (e, columnKey) => {
|
|
690
833
|
if (e.key === "Enter" || e.key === " ") {
|
|
@@ -693,7 +836,38 @@ function Table({ data, container }) {
|
|
|
693
836
|
}
|
|
694
837
|
};
|
|
695
838
|
const handleFilterChange = (columnKey, value) => {
|
|
696
|
-
|
|
839
|
+
const newFilters = { ...filters, [columnKey]: value };
|
|
840
|
+
setFilters(newFilters);
|
|
841
|
+
if (onInteraction) {
|
|
842
|
+
const newVisibleCount = rows.filter(
|
|
843
|
+
(row) => columns.every((col) => {
|
|
844
|
+
if (!col.filterable) return true;
|
|
845
|
+
const fv = newFilters[col.key];
|
|
846
|
+
if (!fv) return true;
|
|
847
|
+
return String(row[col.key] ?? "").toLowerCase().includes(fv.toLowerCase());
|
|
848
|
+
})
|
|
849
|
+
).length;
|
|
850
|
+
const eventSort = sort.direction === "none" || !sort.column ? null : {
|
|
851
|
+
column: sort.column,
|
|
852
|
+
direction: sort.direction === "ascending" ? "asc" : "desc"
|
|
853
|
+
};
|
|
854
|
+
onInteraction({
|
|
855
|
+
kind: "table-filter",
|
|
856
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
857
|
+
blockId: block.id,
|
|
858
|
+
blockType: block.type,
|
|
859
|
+
payload: {
|
|
860
|
+
column: columnKey,
|
|
861
|
+
value,
|
|
862
|
+
state: {
|
|
863
|
+
sort: eventSort,
|
|
864
|
+
filters: newFilters,
|
|
865
|
+
visibleRowCount: newVisibleCount,
|
|
866
|
+
totalRowCount: rows.length
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
|
+
}
|
|
697
871
|
};
|
|
698
872
|
const hasFilters = columns.some((c) => c.filterable);
|
|
699
873
|
const aggMap = react.useMemo(() => {
|
|
@@ -717,79 +891,18 @@ function Table({ data, container }) {
|
|
|
717
891
|
fontSize: isCompact ? "0.8125rem" : "var(--glyph-table-font-size, 0.9rem)"
|
|
718
892
|
},
|
|
719
893
|
children: [
|
|
720
|
-
/* @__PURE__ */ jsxRuntime.
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
onKeyDown: col.sortable ? (e) => handleHeaderKeyDown(e, col.key) : void 0,
|
|
733
|
-
style: {
|
|
734
|
-
padding: "var(--glyph-table-cell-padding, 8px 12px)",
|
|
735
|
-
textAlign: "left",
|
|
736
|
-
borderBottom: "2px solid var(--glyph-table-border, #d0d8e4)",
|
|
737
|
-
background: "var(--glyph-table-header-bg, #e8ecf3)",
|
|
738
|
-
color: "var(--glyph-table-header-color, inherit)",
|
|
739
|
-
cursor: col.sortable ? "pointer" : "default",
|
|
740
|
-
userSelect: col.sortable ? "none" : void 0,
|
|
741
|
-
whiteSpace: "nowrap"
|
|
742
|
-
},
|
|
743
|
-
children: [
|
|
744
|
-
col.label,
|
|
745
|
-
col.sortable ? sortIndicator(direction) : ""
|
|
746
|
-
]
|
|
747
|
-
},
|
|
748
|
-
col.key
|
|
749
|
-
);
|
|
750
|
-
}) }),
|
|
751
|
-
hasFilters && /* @__PURE__ */ jsxRuntime.jsx("tr", { children: columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
752
|
-
"th",
|
|
753
|
-
{
|
|
754
|
-
scope: "col",
|
|
755
|
-
style: { padding: "4px 8px", fontWeight: "normal" },
|
|
756
|
-
children: col.filterable ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
757
|
-
"input",
|
|
758
|
-
{
|
|
759
|
-
type: "text",
|
|
760
|
-
"aria-label": `Filter ${col.label}`,
|
|
761
|
-
placeholder: `Filter ${col.label}...`,
|
|
762
|
-
value: filters[col.key] ?? "",
|
|
763
|
-
onChange: (e) => handleFilterChange(col.key, e.target.value),
|
|
764
|
-
style: {
|
|
765
|
-
width: "100%",
|
|
766
|
-
padding: "4px 6px",
|
|
767
|
-
border: "1px solid var(--glyph-table-border, #d0d8e4)",
|
|
768
|
-
borderRadius: "3px",
|
|
769
|
-
fontSize: "inherit",
|
|
770
|
-
boxSizing: "border-box",
|
|
771
|
-
background: "var(--glyph-surface, #e8ecf3)",
|
|
772
|
-
color: "var(--glyph-text, inherit)"
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
776
|
-
"span",
|
|
777
|
-
{
|
|
778
|
-
style: {
|
|
779
|
-
position: "absolute",
|
|
780
|
-
width: "1px",
|
|
781
|
-
height: "1px",
|
|
782
|
-
overflow: "hidden",
|
|
783
|
-
clip: "rect(0,0,0,0)",
|
|
784
|
-
whiteSpace: "nowrap"
|
|
785
|
-
},
|
|
786
|
-
children: `No filter for ${col.label}`
|
|
787
|
-
}
|
|
788
|
-
)
|
|
789
|
-
},
|
|
790
|
-
`filter-${col.key}`
|
|
791
|
-
)) })
|
|
792
|
-
] }),
|
|
894
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
895
|
+
TableHead,
|
|
896
|
+
{
|
|
897
|
+
columns,
|
|
898
|
+
sort,
|
|
899
|
+
hasFilters,
|
|
900
|
+
filters,
|
|
901
|
+
onSort: handleSort,
|
|
902
|
+
onHeaderKeyDown: handleHeaderKeyDown,
|
|
903
|
+
onFilterChange: handleFilterChange
|
|
904
|
+
}
|
|
905
|
+
),
|
|
793
906
|
/* @__PURE__ */ jsxRuntime.jsx("tbody", { children: sortedRows.map((row, rowIdx) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
794
907
|
"tr",
|
|
795
908
|
{
|
|
@@ -827,18 +940,28 @@ var tableDefinition = {
|
|
|
827
940
|
schema: schemas.tableSchema,
|
|
828
941
|
render: Table
|
|
829
942
|
};
|
|
830
|
-
function Tabs({ data, block }) {
|
|
943
|
+
function Tabs({ data, block, onInteraction }) {
|
|
831
944
|
const [activeIndex, setActiveIndex] = react.useState(0);
|
|
832
945
|
const tabRefs = react.useRef([]);
|
|
833
946
|
const tabs = data.tabs;
|
|
834
947
|
const baseId = `glyph-tabs-${block.id}`;
|
|
835
|
-
const
|
|
948
|
+
const selectTab = react.useCallback(
|
|
836
949
|
(index) => {
|
|
837
950
|
const clampedIndex = Math.max(0, Math.min(index, tabs.length - 1));
|
|
838
951
|
setActiveIndex(clampedIndex);
|
|
839
952
|
tabRefs.current[clampedIndex]?.focus();
|
|
953
|
+
const tab = tabs[clampedIndex];
|
|
954
|
+
if (tab) {
|
|
955
|
+
onInteraction?.({
|
|
956
|
+
kind: "tab-select",
|
|
957
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
958
|
+
blockId: block.id,
|
|
959
|
+
blockType: block.type,
|
|
960
|
+
payload: { tabIndex: clampedIndex, tabLabel: tab.label }
|
|
961
|
+
});
|
|
962
|
+
}
|
|
840
963
|
},
|
|
841
|
-
[tabs.
|
|
964
|
+
[tabs, block.id, block.type, onInteraction]
|
|
842
965
|
);
|
|
843
966
|
const handleKeyDown = react.useCallback(
|
|
844
967
|
(e) => {
|
|
@@ -846,28 +969,28 @@ function Tabs({ data, block }) {
|
|
|
846
969
|
case "ArrowRight": {
|
|
847
970
|
e.preventDefault();
|
|
848
971
|
const next = (activeIndex + 1) % tabs.length;
|
|
849
|
-
|
|
972
|
+
selectTab(next);
|
|
850
973
|
break;
|
|
851
974
|
}
|
|
852
975
|
case "ArrowLeft": {
|
|
853
976
|
e.preventDefault();
|
|
854
977
|
const prev = (activeIndex - 1 + tabs.length) % tabs.length;
|
|
855
|
-
|
|
978
|
+
selectTab(prev);
|
|
856
979
|
break;
|
|
857
980
|
}
|
|
858
981
|
case "Home": {
|
|
859
982
|
e.preventDefault();
|
|
860
|
-
|
|
983
|
+
selectTab(0);
|
|
861
984
|
break;
|
|
862
985
|
}
|
|
863
986
|
case "End": {
|
|
864
987
|
e.preventDefault();
|
|
865
|
-
|
|
988
|
+
selectTab(tabs.length - 1);
|
|
866
989
|
break;
|
|
867
990
|
}
|
|
868
991
|
}
|
|
869
992
|
},
|
|
870
|
-
[activeIndex,
|
|
993
|
+
[activeIndex, selectTab, tabs.length]
|
|
871
994
|
);
|
|
872
995
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
873
996
|
"div",
|
|
@@ -906,7 +1029,7 @@ function Tabs({ data, block }) {
|
|
|
906
1029
|
"aria-selected": isActive,
|
|
907
1030
|
"aria-controls": panelId,
|
|
908
1031
|
tabIndex: isActive ? 0 : -1,
|
|
909
|
-
onClick: () =>
|
|
1032
|
+
onClick: () => selectTab(index),
|
|
910
1033
|
onKeyDown: handleKeyDown,
|
|
911
1034
|
style: {
|
|
912
1035
|
padding: "10px 18px",
|
|
@@ -1018,7 +1141,7 @@ function Timeline({ data }) {
|
|
|
1018
1141
|
position: dates.length === 1 ? totalLength / 2 : timeScale(e._parsed),
|
|
1019
1142
|
side: isVertical ? i % 2 === 0 ? "left" : "right" : i % 2 === 0 ? "top" : "bottom"
|
|
1020
1143
|
}));
|
|
1021
|
-
const
|
|
1144
|
+
const containerStyle11 = {
|
|
1022
1145
|
position: "relative",
|
|
1023
1146
|
fontFamily: "var(--glyph-font-body, system-ui, sans-serif)",
|
|
1024
1147
|
color: "var(--glyph-text, #1a2035)",
|
|
@@ -1045,7 +1168,7 @@ function Timeline({ data }) {
|
|
|
1045
1168
|
"div",
|
|
1046
1169
|
{
|
|
1047
1170
|
ref: containerRef,
|
|
1048
|
-
style:
|
|
1171
|
+
style: containerStyle11,
|
|
1049
1172
|
role: "img",
|
|
1050
1173
|
"aria-label": `Timeline with ${events.length} events`,
|
|
1051
1174
|
children: [
|
|
@@ -1373,7 +1496,7 @@ function getThemeVar(container, varName, fallback) {
|
|
|
1373
1496
|
return getComputedStyle(container).getPropertyValue(varName).trim() || fallback;
|
|
1374
1497
|
}
|
|
1375
1498
|
var ARROW_MARKER_ID = "glyph-graph-arrowhead";
|
|
1376
|
-
function renderGraph(svgElement, layout, groupIndex, outgoingRefs, onNavigate) {
|
|
1499
|
+
function renderGraph(svgElement, layout, groupIndex, outgoingRefs, onNavigate, onNodeClick) {
|
|
1377
1500
|
const svg = d32__namespace.select(svgElement);
|
|
1378
1501
|
svg.selectAll("*").remove();
|
|
1379
1502
|
const width = Math.max(layout.width, 200);
|
|
@@ -1424,19 +1547,24 @@ function renderGraph(svgElement, layout, groupIndex, outgoingRefs, onNavigate) {
|
|
|
1424
1547
|
nodeG.append("rect").attr("x", nodeX).attr("y", nodeY).attr("width", node.width).attr("height", node.height).attr("rx", nodeRadius).attr("ry", nodeRadius).attr("fill", node.style?.["fill"] ?? color3).attr("stroke", node.style?.["stroke"] ?? defaultStroke).attr("stroke-width", node.style?.["stroke-width"] ?? nodeStrokeWidth).attr("opacity", nodeFillOpacity);
|
|
1425
1548
|
}
|
|
1426
1549
|
nodeG.append("text").attr("x", node.x).attr("y", node.y).attr("dy", "0.35em").attr("text-anchor", "middle").attr("font-size", "13px").attr("font-family", "Inter, system-ui, sans-serif").attr("fill", "var(--glyph-node-label-color, #fff)").attr("pointer-events", "none").text(node.label);
|
|
1427
|
-
if (isNavigable) {
|
|
1550
|
+
if (isNavigable || onNodeClick) {
|
|
1428
1551
|
nodeG.attr("cursor", "pointer");
|
|
1429
1552
|
nodeG.on("click", () => {
|
|
1430
|
-
|
|
1431
|
-
|
|
1553
|
+
if (isNavigable) {
|
|
1554
|
+
const ref = refByAnchor.get(node.id);
|
|
1555
|
+
if (ref) onNavigate(ref);
|
|
1556
|
+
}
|
|
1557
|
+
onNodeClick?.(node.id, node.label);
|
|
1432
1558
|
});
|
|
1433
1559
|
}
|
|
1434
1560
|
}
|
|
1435
1561
|
}
|
|
1436
1562
|
function Graph({
|
|
1437
1563
|
data,
|
|
1564
|
+
block,
|
|
1438
1565
|
outgoingRefs,
|
|
1439
1566
|
onNavigate,
|
|
1567
|
+
onInteraction,
|
|
1440
1568
|
container
|
|
1441
1569
|
}) {
|
|
1442
1570
|
const svgRef = react.useRef(null);
|
|
@@ -1448,10 +1576,29 @@ function Graph({
|
|
|
1448
1576
|
}
|
|
1449
1577
|
return computeDagreLayout(data.nodes, data.edges, direction);
|
|
1450
1578
|
}, [data]);
|
|
1579
|
+
const handleNodeClick = react.useMemo(() => {
|
|
1580
|
+
if (!onInteraction) return void 0;
|
|
1581
|
+
return (nodeId, nodeLabel) => {
|
|
1582
|
+
onInteraction({
|
|
1583
|
+
kind: "graph-node-click",
|
|
1584
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1585
|
+
blockId: block.id,
|
|
1586
|
+
blockType: block.type,
|
|
1587
|
+
payload: { nodeId, nodeLabel }
|
|
1588
|
+
});
|
|
1589
|
+
};
|
|
1590
|
+
}, [onInteraction, block.id, block.type]);
|
|
1451
1591
|
react.useEffect(() => {
|
|
1452
1592
|
if (!svgRef.current) return;
|
|
1453
|
-
renderGraph(
|
|
1454
|
-
|
|
1593
|
+
renderGraph(
|
|
1594
|
+
svgRef.current,
|
|
1595
|
+
layoutResult,
|
|
1596
|
+
groupIndex.current,
|
|
1597
|
+
outgoingRefs,
|
|
1598
|
+
onNavigate,
|
|
1599
|
+
handleNodeClick
|
|
1600
|
+
);
|
|
1601
|
+
}, [layoutResult, outgoingRefs, onNavigate, handleNodeClick]);
|
|
1455
1602
|
const ariaLabel = `${data.type} graph with ${data.nodes.length} nodes and ${data.edges.length} edges`;
|
|
1456
1603
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "glyph-graph-container", children: [
|
|
1457
1604
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1795,7 +1942,7 @@ function Kpi({ data, block, container }) {
|
|
|
1795
1942
|
default:
|
|
1796
1943
|
colCount = authorCols;
|
|
1797
1944
|
}
|
|
1798
|
-
const
|
|
1945
|
+
const containerStyle11 = {
|
|
1799
1946
|
fontFamily: "var(--glyph-font-body, system-ui, sans-serif)",
|
|
1800
1947
|
color: "var(--glyph-text, #1a2035)"
|
|
1801
1948
|
};
|
|
@@ -1805,13 +1952,13 @@ function Kpi({ data, block, container }) {
|
|
|
1805
1952
|
gridTemplateColumns: `repeat(auto-fill, minmax(max(120px, calc((100% - ${String(gapCount)}rem) / ${String(colCount)})), 1fr))`,
|
|
1806
1953
|
gap: "var(--glyph-spacing-md, 1rem)"
|
|
1807
1954
|
};
|
|
1808
|
-
const
|
|
1955
|
+
const cardStyle2 = {
|
|
1809
1956
|
background: "var(--glyph-surface-raised, #f4f6fa)",
|
|
1810
1957
|
border: "1px solid var(--glyph-border, #d0d8e4)",
|
|
1811
1958
|
borderRadius: "var(--glyph-radius-md, 0.5rem)",
|
|
1812
1959
|
padding: "var(--glyph-spacing-md, 1rem)"
|
|
1813
1960
|
};
|
|
1814
|
-
const
|
|
1961
|
+
const labelStyle4 = {
|
|
1815
1962
|
fontSize: "0.8125rem",
|
|
1816
1963
|
color: "var(--glyph-text-muted, #6b7a94)",
|
|
1817
1964
|
marginBottom: "var(--glyph-spacing-xs, 0.25rem)"
|
|
@@ -1822,7 +1969,7 @@ function Kpi({ data, block, container }) {
|
|
|
1822
1969
|
color: "var(--glyph-heading, #0a0e1a)",
|
|
1823
1970
|
lineHeight: 1.2
|
|
1824
1971
|
};
|
|
1825
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { id: baseId, role: "region", "aria-label": title ?? "Key metrics", style:
|
|
1972
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { id: baseId, role: "region", "aria-label": title ?? "Key metrics", style: containerStyle11, children: [
|
|
1826
1973
|
title && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1827
1974
|
"div",
|
|
1828
1975
|
{
|
|
@@ -1842,8 +1989,8 @@ function Kpi({ data, block, container }) {
|
|
|
1842
1989
|
marginTop: "var(--glyph-spacing-xs, 0.25rem)",
|
|
1843
1990
|
color: `var(--glyph-kpi-${sentiment}, inherit)`
|
|
1844
1991
|
};
|
|
1845
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "group", "aria-label": buildAriaLabel(metric), style:
|
|
1846
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style:
|
|
1992
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "group", "aria-label": buildAriaLabel(metric), style: cardStyle2, children: [
|
|
1993
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: labelStyle4, children: metric.label }),
|
|
1847
1994
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: valueStyle, children: [
|
|
1848
1995
|
metric.value,
|
|
1849
1996
|
metric.unit && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.875rem", fontWeight: 400, marginLeft: "0.25rem" }, children: metric.unit })
|
|
@@ -1863,25 +2010,41 @@ var kpiDefinition = {
|
|
|
1863
2010
|
schema: schemas.kpiSchema,
|
|
1864
2011
|
render: Kpi
|
|
1865
2012
|
};
|
|
1866
|
-
function Accordion({
|
|
2013
|
+
function Accordion({
|
|
2014
|
+
data,
|
|
2015
|
+
block,
|
|
2016
|
+
onInteraction
|
|
2017
|
+
}) {
|
|
1867
2018
|
const { title, sections, defaultOpen = [], multiple = true } = data;
|
|
1868
2019
|
const baseId = `glyph-accordion-${block.id}`;
|
|
1869
2020
|
const containerRef = react.useRef(null);
|
|
1870
2021
|
const handleToggle = react.useCallback(
|
|
1871
|
-
(e) => {
|
|
1872
|
-
if (multiple) return;
|
|
2022
|
+
(e, sectionIndex) => {
|
|
1873
2023
|
const target = e.currentTarget;
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
details.open
|
|
2024
|
+
const expanded = target.open;
|
|
2025
|
+
if (!multiple && expanded && containerRef.current) {
|
|
2026
|
+
const allDetails = containerRef.current.querySelectorAll("details");
|
|
2027
|
+
for (const details of allDetails) {
|
|
2028
|
+
if (details !== target && details.open) {
|
|
2029
|
+
details.open = false;
|
|
2030
|
+
}
|
|
1879
2031
|
}
|
|
1880
2032
|
}
|
|
2033
|
+
onInteraction?.({
|
|
2034
|
+
kind: "accordion-toggle",
|
|
2035
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2036
|
+
blockId: block.id,
|
|
2037
|
+
blockType: block.type,
|
|
2038
|
+
payload: {
|
|
2039
|
+
sectionIndex,
|
|
2040
|
+
sectionTitle: sections[sectionIndex]?.title ?? "",
|
|
2041
|
+
expanded
|
|
2042
|
+
}
|
|
2043
|
+
});
|
|
1881
2044
|
},
|
|
1882
|
-
[multiple]
|
|
2045
|
+
[multiple, sections, block.id, block.type, onInteraction]
|
|
1883
2046
|
);
|
|
1884
|
-
const
|
|
2047
|
+
const containerStyle11 = {
|
|
1885
2048
|
fontFamily: "var(--glyph-font-body, system-ui, sans-serif)",
|
|
1886
2049
|
color: "var(--glyph-text, #1a2035)",
|
|
1887
2050
|
border: "1px solid var(--glyph-border, #d0d8e4)",
|
|
@@ -1915,7 +2078,7 @@ function Accordion({ data, block }) {
|
|
|
1915
2078
|
ref: containerRef,
|
|
1916
2079
|
role: "region",
|
|
1917
2080
|
"aria-label": title ?? "Accordion",
|
|
1918
|
-
style:
|
|
2081
|
+
style: containerStyle11,
|
|
1919
2082
|
children: [
|
|
1920
2083
|
title && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1921
2084
|
"div",
|
|
@@ -1934,7 +2097,7 @@ function Accordion({ data, block }) {
|
|
|
1934
2097
|
"details",
|
|
1935
2098
|
{
|
|
1936
2099
|
open: defaultOpen.includes(i),
|
|
1937
|
-
onToggle: handleToggle,
|
|
2100
|
+
onToggle: (e) => handleToggle(e, i),
|
|
1938
2101
|
style: sectionStyle(i === sections.length - 1),
|
|
1939
2102
|
children: [
|
|
1940
2103
|
/* @__PURE__ */ jsxRuntime.jsxs("summary", { style: summaryStyle, children: [
|
|
@@ -2003,17 +2166,18 @@ function renderValue(value) {
|
|
|
2003
2166
|
function Comparison({
|
|
2004
2167
|
data,
|
|
2005
2168
|
block,
|
|
2006
|
-
container
|
|
2169
|
+
container,
|
|
2170
|
+
onInteraction
|
|
2007
2171
|
}) {
|
|
2008
2172
|
const { title, options, features } = data;
|
|
2009
2173
|
const baseId = `glyph-comparison-${block.id}`;
|
|
2010
2174
|
const isCompact = container.tier === "compact";
|
|
2011
2175
|
const cellPadding = isCompact ? "var(--glyph-spacing-xs, 0.25rem) var(--glyph-spacing-sm, 0.5rem)" : "var(--glyph-spacing-sm, 0.5rem) var(--glyph-spacing-md, 1rem)";
|
|
2012
|
-
const
|
|
2176
|
+
const containerStyle11 = {
|
|
2013
2177
|
fontFamily: "var(--glyph-font-body, system-ui, sans-serif)",
|
|
2014
2178
|
color: "var(--glyph-text, #1a2035)"
|
|
2015
2179
|
};
|
|
2016
|
-
const
|
|
2180
|
+
const tableStyle2 = {
|
|
2017
2181
|
width: "100%",
|
|
2018
2182
|
borderCollapse: "collapse",
|
|
2019
2183
|
border: "1px solid var(--glyph-table-border, #d0d8e4)",
|
|
@@ -2021,7 +2185,7 @@ function Comparison({
|
|
|
2021
2185
|
overflow: "hidden",
|
|
2022
2186
|
fontSize: isCompact ? "0.8125rem" : "0.875rem"
|
|
2023
2187
|
};
|
|
2024
|
-
const
|
|
2188
|
+
const thStyle2 = {
|
|
2025
2189
|
padding: cellPadding,
|
|
2026
2190
|
textAlign: "center",
|
|
2027
2191
|
fontWeight: 600,
|
|
@@ -2030,7 +2194,7 @@ function Comparison({
|
|
|
2030
2194
|
color: "var(--glyph-heading, #0a0e1a)"
|
|
2031
2195
|
};
|
|
2032
2196
|
const featureThStyle = {
|
|
2033
|
-
...
|
|
2197
|
+
...thStyle2,
|
|
2034
2198
|
textAlign: "left"
|
|
2035
2199
|
};
|
|
2036
2200
|
const rowThStyle = {
|
|
@@ -2040,13 +2204,13 @@ function Comparison({
|
|
|
2040
2204
|
borderBottom: "1px solid var(--glyph-table-border, #d0d8e4)",
|
|
2041
2205
|
fontSize: "0.8125rem"
|
|
2042
2206
|
};
|
|
2043
|
-
const
|
|
2207
|
+
const cellStyle2 = (rowIndex) => ({
|
|
2044
2208
|
padding: cellPadding,
|
|
2045
2209
|
textAlign: "center",
|
|
2046
2210
|
borderBottom: "1px solid var(--glyph-table-border, #d0d8e4)",
|
|
2047
2211
|
background: rowIndex % 2 === 1 ? "var(--glyph-table-row-alt-bg, transparent)" : "transparent"
|
|
2048
2212
|
});
|
|
2049
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { id: baseId, role: "region", "aria-label": title ?? "Comparison", style:
|
|
2213
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { id: baseId, role: "region", "aria-label": title ?? "Comparison", style: containerStyle11, children: [
|
|
2050
2214
|
title && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2051
2215
|
"div",
|
|
2052
2216
|
{
|
|
@@ -2059,23 +2223,46 @@ function Comparison({
|
|
|
2059
2223
|
children: title
|
|
2060
2224
|
}
|
|
2061
2225
|
),
|
|
2062
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { overflowX: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsxs("table", { role: "grid", style:
|
|
2226
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { overflowX: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsxs("table", { role: "grid", style: tableStyle2, children: [
|
|
2063
2227
|
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
2064
2228
|
/* @__PURE__ */ jsxRuntime.jsx("th", { style: featureThStyle, scope: "col", children: "Feature" }),
|
|
2065
|
-
options.map((option, i) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2229
|
+
options.map((option, i) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2230
|
+
"th",
|
|
2231
|
+
{
|
|
2232
|
+
style: {
|
|
2233
|
+
...thStyle2,
|
|
2234
|
+
...onInteraction ? { cursor: "pointer" } : {}
|
|
2235
|
+
},
|
|
2236
|
+
scope: "col",
|
|
2237
|
+
onClick: onInteraction ? () => {
|
|
2238
|
+
onInteraction({
|
|
2239
|
+
kind: "comparison-select",
|
|
2240
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2241
|
+
blockId: block.id,
|
|
2242
|
+
blockType: block.type,
|
|
2243
|
+
payload: {
|
|
2244
|
+
optionIndex: i,
|
|
2245
|
+
optionName: option.name
|
|
2246
|
+
}
|
|
2247
|
+
});
|
|
2248
|
+
} : void 0,
|
|
2249
|
+
children: [
|
|
2250
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: option.name }),
|
|
2251
|
+
option.description && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2252
|
+
"div",
|
|
2253
|
+
{
|
|
2254
|
+
style: {
|
|
2255
|
+
fontWeight: 400,
|
|
2256
|
+
fontSize: "0.75rem",
|
|
2257
|
+
color: "var(--glyph-text-muted, #6b7a94)"
|
|
2258
|
+
},
|
|
2259
|
+
children: option.description
|
|
2260
|
+
}
|
|
2261
|
+
)
|
|
2262
|
+
]
|
|
2263
|
+
},
|
|
2264
|
+
i
|
|
2265
|
+
))
|
|
2079
2266
|
] }) }),
|
|
2080
2267
|
/* @__PURE__ */ jsxRuntime.jsx("tbody", { children: features.map((feature, rowIndex) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
2081
2268
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2091,7 +2278,7 @@ function Comparison({
|
|
|
2091
2278
|
),
|
|
2092
2279
|
options.map((_, colIndex) => {
|
|
2093
2280
|
const value = feature.values[colIndex] ?? "";
|
|
2094
|
-
return /* @__PURE__ */ jsxRuntime.jsx("td", { style:
|
|
2281
|
+
return /* @__PURE__ */ jsxRuntime.jsx("td", { style: cellStyle2(rowIndex), children: value ? renderValue(value) : null }, colIndex);
|
|
2095
2282
|
})
|
|
2096
2283
|
] }, rowIndex)) })
|
|
2097
2284
|
] }) })
|
|
@@ -2169,7 +2356,7 @@ function CodeDiff({ data, block }) {
|
|
|
2169
2356
|
const baseId = `glyph-codediff-${block.id}`;
|
|
2170
2357
|
const diffLines = react.useMemo(() => computeDiff(before, after), [before, after]);
|
|
2171
2358
|
const summary = react.useMemo(() => summarizeDiff(diffLines), [diffLines]);
|
|
2172
|
-
const
|
|
2359
|
+
const containerStyle11 = {
|
|
2173
2360
|
fontFamily: 'var(--glyph-font-mono, ui-monospace, "Cascadia Code", "Fira Code", monospace)',
|
|
2174
2361
|
fontSize: "0.8125rem",
|
|
2175
2362
|
lineHeight: 1.5,
|
|
@@ -2188,7 +2375,7 @@ function CodeDiff({ data, block }) {
|
|
|
2188
2375
|
fontWeight: 600,
|
|
2189
2376
|
color: "var(--glyph-text-muted, #6b7a94)"
|
|
2190
2377
|
};
|
|
2191
|
-
const
|
|
2378
|
+
const tableStyle2 = {
|
|
2192
2379
|
width: "100%",
|
|
2193
2380
|
borderCollapse: "collapse",
|
|
2194
2381
|
tableLayout: "fixed"
|
|
@@ -2227,13 +2414,13 @@ function CodeDiff({ data, block }) {
|
|
|
2227
2414
|
if (kind === "del") return "var(--glyph-codediff-del-color, inherit)";
|
|
2228
2415
|
return void 0;
|
|
2229
2416
|
}
|
|
2230
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { id: baseId, role: "region", "aria-label": summary, style:
|
|
2417
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { id: baseId, role: "region", "aria-label": summary, style: containerStyle11, children: [
|
|
2231
2418
|
(beforeLabel || afterLabel) && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: labelBarStyle, children: [
|
|
2232
2419
|
beforeLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { children: beforeLabel }),
|
|
2233
2420
|
beforeLabel && afterLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { children: "\u2192" }),
|
|
2234
2421
|
afterLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { children: afterLabel })
|
|
2235
2422
|
] }),
|
|
2236
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { overflowX: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsx("table", { role: "grid", style:
|
|
2423
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { overflowX: "auto" }, children: /* @__PURE__ */ jsxRuntime.jsx("table", { role: "grid", style: tableStyle2, children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: diffLines.map((line6, i) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2237
2424
|
"tr",
|
|
2238
2425
|
{
|
|
2239
2426
|
"aria-label": ARIA_LABELS[line6.kind],
|
|
@@ -2499,15 +2686,25 @@ function TreeItem({
|
|
|
2499
2686
|
setSize,
|
|
2500
2687
|
posInSet,
|
|
2501
2688
|
onFocusChange,
|
|
2502
|
-
flatItems
|
|
2689
|
+
flatItems,
|
|
2690
|
+
parentPath,
|
|
2691
|
+
onSelect
|
|
2503
2692
|
}) {
|
|
2504
2693
|
const [expanded, setExpanded] = react.useState(defaultExpanded);
|
|
2505
2694
|
const itemRef = react.useRef(null);
|
|
2506
2695
|
const isDir = Array.isArray(node.children) && node.children.length > 0;
|
|
2507
2696
|
const isFocused = flatIndex === focusedIndex;
|
|
2697
|
+
const path = parentPath ? `${parentPath}/${node.name}` : node.name;
|
|
2508
2698
|
const handleToggle = react.useCallback(() => {
|
|
2509
|
-
if (isDir)
|
|
2510
|
-
|
|
2699
|
+
if (isDir) {
|
|
2700
|
+
setExpanded((prev) => {
|
|
2701
|
+
onSelect?.(path, "directory", !prev);
|
|
2702
|
+
return !prev;
|
|
2703
|
+
});
|
|
2704
|
+
} else {
|
|
2705
|
+
onSelect?.(path, "file");
|
|
2706
|
+
}
|
|
2707
|
+
}, [isDir, path, onSelect]);
|
|
2511
2708
|
const handleKeyDown = react.useCallback(
|
|
2512
2709
|
(e) => {
|
|
2513
2710
|
let handled = true;
|
|
@@ -2639,7 +2836,9 @@ function TreeItem({
|
|
|
2639
2836
|
setSize: node.children?.length ?? 0,
|
|
2640
2837
|
posInSet: childIdx + 1,
|
|
2641
2838
|
onFocusChange,
|
|
2642
|
-
flatItems
|
|
2839
|
+
flatItems,
|
|
2840
|
+
parentPath: path,
|
|
2841
|
+
onSelect
|
|
2643
2842
|
},
|
|
2644
2843
|
child.name
|
|
2645
2844
|
);
|
|
@@ -2672,7 +2871,11 @@ function getChildFlatIndex(flatItems, parentFlatIndex, childIdx, children) {
|
|
|
2672
2871
|
}
|
|
2673
2872
|
return parentFlatIndex + childIdx + 1;
|
|
2674
2873
|
}
|
|
2675
|
-
function FileTree({
|
|
2874
|
+
function FileTree({
|
|
2875
|
+
data,
|
|
2876
|
+
block,
|
|
2877
|
+
onInteraction
|
|
2878
|
+
}) {
|
|
2676
2879
|
const [focusedIndex, setFocusedIndex] = react.useState(0);
|
|
2677
2880
|
const containerRef = react.useRef(null);
|
|
2678
2881
|
const flatItems = flattenTree(data.tree, data.root ? 1 : 0);
|
|
@@ -2683,6 +2886,18 @@ function FileTree({ data }) {
|
|
|
2683
2886
|
const el = container.querySelector(`[data-flat-index="${String(index)}"]`);
|
|
2684
2887
|
el?.focus();
|
|
2685
2888
|
}, []);
|
|
2889
|
+
const handleSelect = react.useCallback(
|
|
2890
|
+
(path, type, expanded) => {
|
|
2891
|
+
onInteraction?.({
|
|
2892
|
+
kind: "filetree-select",
|
|
2893
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2894
|
+
blockId: block.id,
|
|
2895
|
+
blockType: block.type,
|
|
2896
|
+
payload: { path, type, expanded }
|
|
2897
|
+
});
|
|
2898
|
+
},
|
|
2899
|
+
[onInteraction, block.id, block.type]
|
|
2900
|
+
);
|
|
2686
2901
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2687
2902
|
"div",
|
|
2688
2903
|
{
|
|
@@ -2739,7 +2954,9 @@ function FileTree({ data }) {
|
|
|
2739
2954
|
setSize: data.tree.length,
|
|
2740
2955
|
posInSet: idx + 1,
|
|
2741
2956
|
onFocusChange: handleFocusChange,
|
|
2742
|
-
flatItems
|
|
2957
|
+
flatItems,
|
|
2958
|
+
parentPath: data.root ?? "",
|
|
2959
|
+
onSelect: handleSelect
|
|
2743
2960
|
},
|
|
2744
2961
|
node.name
|
|
2745
2962
|
);
|
|
@@ -2760,7 +2977,9 @@ function FileTree({ data }) {
|
|
|
2760
2977
|
setSize: data.tree.length,
|
|
2761
2978
|
posInSet: idx + 1,
|
|
2762
2979
|
onFocusChange: handleFocusChange,
|
|
2763
|
-
flatItems
|
|
2980
|
+
flatItems,
|
|
2981
|
+
parentPath: "",
|
|
2982
|
+
onSelect: handleSelect
|
|
2764
2983
|
},
|
|
2765
2984
|
node.name
|
|
2766
2985
|
);
|
|
@@ -4008,7 +4227,115 @@ function isCorrect(question, selected) {
|
|
|
4008
4227
|
}
|
|
4009
4228
|
}
|
|
4010
4229
|
}
|
|
4011
|
-
function
|
|
4230
|
+
function renderMultipleChoice(question, qIndex, state, updateState, baseId) {
|
|
4231
|
+
const selected = typeof state.selected === "number" ? state.selected : null;
|
|
4232
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "radiogroup", "aria-label": question.question, children: question.options.map((option, oIndex) => {
|
|
4233
|
+
const isSelected = selected === oIndex;
|
|
4234
|
+
const isCorrectOption = state.submitted && oIndex === question.answer;
|
|
4235
|
+
const isIncorrectSelection = state.submitted && isSelected && oIndex !== question.answer;
|
|
4236
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4237
|
+
"label",
|
|
4238
|
+
{
|
|
4239
|
+
style: optionLabelStyle(
|
|
4240
|
+
isSelected,
|
|
4241
|
+
state.submitted,
|
|
4242
|
+
isCorrectOption,
|
|
4243
|
+
isIncorrectSelection
|
|
4244
|
+
),
|
|
4245
|
+
children: [
|
|
4246
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4247
|
+
"input",
|
|
4248
|
+
{
|
|
4249
|
+
type: "radio",
|
|
4250
|
+
role: "radio",
|
|
4251
|
+
name: `${baseId}-q${String(qIndex)}`,
|
|
4252
|
+
checked: isSelected,
|
|
4253
|
+
disabled: state.submitted,
|
|
4254
|
+
onChange: () => updateState(qIndex, { selected: oIndex }),
|
|
4255
|
+
"aria-checked": isSelected
|
|
4256
|
+
}
|
|
4257
|
+
),
|
|
4258
|
+
option
|
|
4259
|
+
]
|
|
4260
|
+
},
|
|
4261
|
+
oIndex
|
|
4262
|
+
);
|
|
4263
|
+
}) });
|
|
4264
|
+
}
|
|
4265
|
+
function renderTrueFalse(question, qIndex, state, updateState, baseId) {
|
|
4266
|
+
const selected = typeof state.selected === "boolean" ? state.selected : null;
|
|
4267
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "radiogroup", "aria-label": question.question, children: [true, false].map((value) => {
|
|
4268
|
+
const isSelected = selected === value;
|
|
4269
|
+
const isCorrectOption = state.submitted && value === question.answer;
|
|
4270
|
+
const isIncorrectSelection = state.submitted && isSelected && value !== question.answer;
|
|
4271
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4272
|
+
"label",
|
|
4273
|
+
{
|
|
4274
|
+
style: optionLabelStyle(
|
|
4275
|
+
isSelected,
|
|
4276
|
+
state.submitted,
|
|
4277
|
+
isCorrectOption,
|
|
4278
|
+
isIncorrectSelection
|
|
4279
|
+
),
|
|
4280
|
+
children: [
|
|
4281
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4282
|
+
"input",
|
|
4283
|
+
{
|
|
4284
|
+
type: "radio",
|
|
4285
|
+
role: "radio",
|
|
4286
|
+
name: `${baseId}-q${String(qIndex)}`,
|
|
4287
|
+
checked: isSelected,
|
|
4288
|
+
disabled: state.submitted,
|
|
4289
|
+
onChange: () => updateState(qIndex, { selected: value }),
|
|
4290
|
+
"aria-checked": isSelected
|
|
4291
|
+
}
|
|
4292
|
+
),
|
|
4293
|
+
value ? "True" : "False"
|
|
4294
|
+
]
|
|
4295
|
+
},
|
|
4296
|
+
String(value)
|
|
4297
|
+
);
|
|
4298
|
+
}) });
|
|
4299
|
+
}
|
|
4300
|
+
function renderMultiSelect(question, qIndex, state, updateState) {
|
|
4301
|
+
const selected = Array.isArray(state.selected) ? state.selected : [];
|
|
4302
|
+
const toggleOption = (oIndex) => {
|
|
4303
|
+
const next = selected.includes(oIndex) ? selected.filter((v) => v !== oIndex) : [...selected, oIndex];
|
|
4304
|
+
updateState(qIndex, { selected: next });
|
|
4305
|
+
};
|
|
4306
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { children: question.options.map((option, oIndex) => {
|
|
4307
|
+
const isSelected = selected.includes(oIndex);
|
|
4308
|
+
const isCorrectOption = state.submitted && question.answer.includes(oIndex);
|
|
4309
|
+
const isIncorrectSelection = state.submitted && isSelected && !question.answer.includes(oIndex);
|
|
4310
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4311
|
+
"label",
|
|
4312
|
+
{
|
|
4313
|
+
style: optionLabelStyle(
|
|
4314
|
+
isSelected,
|
|
4315
|
+
state.submitted,
|
|
4316
|
+
isCorrectOption,
|
|
4317
|
+
isIncorrectSelection
|
|
4318
|
+
),
|
|
4319
|
+
children: [
|
|
4320
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4321
|
+
"input",
|
|
4322
|
+
{
|
|
4323
|
+
type: "checkbox",
|
|
4324
|
+
role: "checkbox",
|
|
4325
|
+
checked: isSelected,
|
|
4326
|
+
disabled: state.submitted,
|
|
4327
|
+
onChange: () => toggleOption(oIndex),
|
|
4328
|
+
"aria-checked": isSelected
|
|
4329
|
+
}
|
|
4330
|
+
),
|
|
4331
|
+
option
|
|
4332
|
+
]
|
|
4333
|
+
},
|
|
4334
|
+
oIndex
|
|
4335
|
+
);
|
|
4336
|
+
}) });
|
|
4337
|
+
}
|
|
4338
|
+
function Quiz({ data, block, onInteraction }) {
|
|
4012
4339
|
const { questions, showScore = true, title } = data;
|
|
4013
4340
|
const baseId = `glyph-quiz-${block.id}`;
|
|
4014
4341
|
const [states, setStates] = react.useState(
|
|
@@ -4026,114 +4353,6 @@ function Quiz({ data, block }) {
|
|
|
4026
4353
|
return acc + (isCorrect(q, s.selected) ? 1 : 0);
|
|
4027
4354
|
}, 0);
|
|
4028
4355
|
const submittedCount = states.filter((s) => s.submitted).length;
|
|
4029
|
-
function renderMultipleChoice(question, qIndex, state) {
|
|
4030
|
-
const selected = typeof state.selected === "number" ? state.selected : null;
|
|
4031
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "radiogroup", "aria-label": question.question, children: question.options.map((option, oIndex) => {
|
|
4032
|
-
const isSelected = selected === oIndex;
|
|
4033
|
-
const isCorrectOption = state.submitted && oIndex === question.answer;
|
|
4034
|
-
const isIncorrectSelection = state.submitted && isSelected && oIndex !== question.answer;
|
|
4035
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4036
|
-
"label",
|
|
4037
|
-
{
|
|
4038
|
-
style: optionLabelStyle(
|
|
4039
|
-
isSelected,
|
|
4040
|
-
state.submitted,
|
|
4041
|
-
isCorrectOption,
|
|
4042
|
-
isIncorrectSelection
|
|
4043
|
-
),
|
|
4044
|
-
children: [
|
|
4045
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4046
|
-
"input",
|
|
4047
|
-
{
|
|
4048
|
-
type: "radio",
|
|
4049
|
-
role: "radio",
|
|
4050
|
-
name: `${baseId}-q${String(qIndex)}`,
|
|
4051
|
-
checked: isSelected,
|
|
4052
|
-
disabled: state.submitted,
|
|
4053
|
-
onChange: () => updateState(qIndex, { selected: oIndex }),
|
|
4054
|
-
"aria-checked": isSelected
|
|
4055
|
-
}
|
|
4056
|
-
),
|
|
4057
|
-
option
|
|
4058
|
-
]
|
|
4059
|
-
},
|
|
4060
|
-
oIndex
|
|
4061
|
-
);
|
|
4062
|
-
}) });
|
|
4063
|
-
}
|
|
4064
|
-
function renderTrueFalse(question, qIndex, state) {
|
|
4065
|
-
const selected = typeof state.selected === "boolean" ? state.selected : null;
|
|
4066
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "radiogroup", "aria-label": question.question, children: [true, false].map((value) => {
|
|
4067
|
-
const isSelected = selected === value;
|
|
4068
|
-
const isCorrectOption = state.submitted && value === question.answer;
|
|
4069
|
-
const isIncorrectSelection = state.submitted && isSelected && value !== question.answer;
|
|
4070
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4071
|
-
"label",
|
|
4072
|
-
{
|
|
4073
|
-
style: optionLabelStyle(
|
|
4074
|
-
isSelected,
|
|
4075
|
-
state.submitted,
|
|
4076
|
-
isCorrectOption,
|
|
4077
|
-
isIncorrectSelection
|
|
4078
|
-
),
|
|
4079
|
-
children: [
|
|
4080
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4081
|
-
"input",
|
|
4082
|
-
{
|
|
4083
|
-
type: "radio",
|
|
4084
|
-
role: "radio",
|
|
4085
|
-
name: `${baseId}-q${String(qIndex)}`,
|
|
4086
|
-
checked: isSelected,
|
|
4087
|
-
disabled: state.submitted,
|
|
4088
|
-
onChange: () => updateState(qIndex, { selected: value }),
|
|
4089
|
-
"aria-checked": isSelected
|
|
4090
|
-
}
|
|
4091
|
-
),
|
|
4092
|
-
value ? "True" : "False"
|
|
4093
|
-
]
|
|
4094
|
-
},
|
|
4095
|
-
String(value)
|
|
4096
|
-
);
|
|
4097
|
-
}) });
|
|
4098
|
-
}
|
|
4099
|
-
function renderMultiSelect(question, qIndex, state) {
|
|
4100
|
-
const selected = Array.isArray(state.selected) ? state.selected : [];
|
|
4101
|
-
const toggleOption = (oIndex) => {
|
|
4102
|
-
const next = selected.includes(oIndex) ? selected.filter((v) => v !== oIndex) : [...selected, oIndex];
|
|
4103
|
-
updateState(qIndex, { selected: next });
|
|
4104
|
-
};
|
|
4105
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { children: question.options.map((option, oIndex) => {
|
|
4106
|
-
const isSelected = selected.includes(oIndex);
|
|
4107
|
-
const isCorrectOption = state.submitted && question.answer.includes(oIndex);
|
|
4108
|
-
const isIncorrectSelection = state.submitted && isSelected && !question.answer.includes(oIndex);
|
|
4109
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4110
|
-
"label",
|
|
4111
|
-
{
|
|
4112
|
-
style: optionLabelStyle(
|
|
4113
|
-
isSelected,
|
|
4114
|
-
state.submitted,
|
|
4115
|
-
isCorrectOption,
|
|
4116
|
-
isIncorrectSelection
|
|
4117
|
-
),
|
|
4118
|
-
children: [
|
|
4119
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4120
|
-
"input",
|
|
4121
|
-
{
|
|
4122
|
-
type: "checkbox",
|
|
4123
|
-
role: "checkbox",
|
|
4124
|
-
checked: isSelected,
|
|
4125
|
-
disabled: state.submitted,
|
|
4126
|
-
onChange: () => toggleOption(oIndex),
|
|
4127
|
-
"aria-checked": isSelected
|
|
4128
|
-
}
|
|
4129
|
-
),
|
|
4130
|
-
option
|
|
4131
|
-
]
|
|
4132
|
-
},
|
|
4133
|
-
oIndex
|
|
4134
|
-
);
|
|
4135
|
-
}) });
|
|
4136
|
-
}
|
|
4137
4356
|
function renderQuestion(question, qIndex) {
|
|
4138
4357
|
const state = states[qIndex] ?? { selected: null, submitted: false };
|
|
4139
4358
|
const isLast = qIndex === questions.length - 1;
|
|
@@ -4150,16 +4369,52 @@ function Quiz({ data, block }) {
|
|
|
4150
4369
|
questions.length > 1 ? `${String(qIndex + 1)}. ` : "",
|
|
4151
4370
|
question.question
|
|
4152
4371
|
] }),
|
|
4153
|
-
question.type === "multiple-choice" && renderMultipleChoice(question, qIndex, state),
|
|
4154
|
-
question.type === "true-false" && renderTrueFalse(question, qIndex, state),
|
|
4155
|
-
question.type === "multi-select" && renderMultiSelect(question, qIndex, state),
|
|
4372
|
+
question.type === "multiple-choice" && renderMultipleChoice(question, qIndex, state, updateState, baseId),
|
|
4373
|
+
question.type === "true-false" && renderTrueFalse(question, qIndex, state, updateState, baseId),
|
|
4374
|
+
question.type === "multi-select" && renderMultiSelect(question, qIndex, state, updateState),
|
|
4156
4375
|
!state.submitted && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4157
4376
|
"button",
|
|
4158
4377
|
{
|
|
4159
4378
|
type: "button",
|
|
4160
4379
|
disabled: !hasSelection,
|
|
4161
4380
|
style: buttonStyle(!hasSelection),
|
|
4162
|
-
onClick: () =>
|
|
4381
|
+
onClick: () => {
|
|
4382
|
+
updateState(qIndex, { submitted: true });
|
|
4383
|
+
if (onInteraction) {
|
|
4384
|
+
const correct2 = isCorrect(question, state.selected);
|
|
4385
|
+
const newScore = states.reduce((acc, s, i) => {
|
|
4386
|
+
if (i === qIndex) return acc + (correct2 ? 1 : 0);
|
|
4387
|
+
const q = questions[i];
|
|
4388
|
+
if (!q || !s.submitted) return acc;
|
|
4389
|
+
return acc + (isCorrect(q, s.selected) ? 1 : 0);
|
|
4390
|
+
}, 0);
|
|
4391
|
+
let selected;
|
|
4392
|
+
switch (question.type) {
|
|
4393
|
+
case "multiple-choice":
|
|
4394
|
+
selected = typeof state.selected === "number" ? [question.options[state.selected] ?? String(state.selected)] : [];
|
|
4395
|
+
break;
|
|
4396
|
+
case "true-false":
|
|
4397
|
+
selected = typeof state.selected === "boolean" ? [state.selected ? "True" : "False"] : [];
|
|
4398
|
+
break;
|
|
4399
|
+
case "multi-select":
|
|
4400
|
+
selected = Array.isArray(state.selected) ? state.selected.map((idx) => question.options[idx] ?? String(idx)) : [];
|
|
4401
|
+
break;
|
|
4402
|
+
}
|
|
4403
|
+
onInteraction({
|
|
4404
|
+
kind: "quiz-submit",
|
|
4405
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4406
|
+
blockId: block.id,
|
|
4407
|
+
blockType: block.type,
|
|
4408
|
+
payload: {
|
|
4409
|
+
questionIndex: qIndex,
|
|
4410
|
+
question: question.question,
|
|
4411
|
+
selected,
|
|
4412
|
+
correct: correct2,
|
|
4413
|
+
score: { correct: newScore, total: questions.length }
|
|
4414
|
+
}
|
|
4415
|
+
});
|
|
4416
|
+
}
|
|
4417
|
+
},
|
|
4163
4418
|
children: "Submit"
|
|
4164
4419
|
}
|
|
4165
4420
|
),
|
|
@@ -4222,7 +4477,7 @@ function Card({ data, block, container }) {
|
|
|
4222
4477
|
default:
|
|
4223
4478
|
colCount = authorCols;
|
|
4224
4479
|
}
|
|
4225
|
-
const
|
|
4480
|
+
const containerStyle11 = {
|
|
4226
4481
|
fontFamily: "var(--glyph-font-body, system-ui, sans-serif)",
|
|
4227
4482
|
color: "var(--glyph-text, #1a2035)"
|
|
4228
4483
|
};
|
|
@@ -4267,7 +4522,7 @@ function Card({ data, block, container }) {
|
|
|
4267
4522
|
color: "var(--glyph-text-muted, #6b7a94)",
|
|
4268
4523
|
marginTop: "var(--glyph-spacing-xs, 0.25rem)"
|
|
4269
4524
|
};
|
|
4270
|
-
const
|
|
4525
|
+
const bodyStyle3 = {
|
|
4271
4526
|
fontSize: "0.875rem",
|
|
4272
4527
|
lineHeight: 1.6,
|
|
4273
4528
|
marginTop: "var(--glyph-spacing-sm, 0.5rem)",
|
|
@@ -4285,7 +4540,7 @@ function Card({ data, block, container }) {
|
|
|
4285
4540
|
color: "var(--glyph-link, #0a9d7c)",
|
|
4286
4541
|
textDecoration: "none"
|
|
4287
4542
|
};
|
|
4288
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { id: baseId, role: "region", "aria-label": title ?? "Cards", style:
|
|
4543
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { id: baseId, role: "region", "aria-label": title ?? "Cards", style: containerStyle11, children: [
|
|
4289
4544
|
title && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4290
4545
|
"div",
|
|
4291
4546
|
{
|
|
@@ -4304,7 +4559,7 @@ function Card({ data, block, container }) {
|
|
|
4304
4559
|
card.icon && /* @__PURE__ */ jsxRuntime.jsx("div", { style: iconStyle, children: card.icon }),
|
|
4305
4560
|
/* @__PURE__ */ jsxRuntime.jsx("h3", { style: titleStyle2, children: card.title }),
|
|
4306
4561
|
card.subtitle && /* @__PURE__ */ jsxRuntime.jsx("div", { style: subtitleStyle, children: card.subtitle }),
|
|
4307
|
-
card.body && /* @__PURE__ */ jsxRuntime.jsx("div", { style:
|
|
4562
|
+
card.body && /* @__PURE__ */ jsxRuntime.jsx("div", { style: bodyStyle3, children: card.body }),
|
|
4308
4563
|
card.actions && card.actions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: actionsStyle, children: card.actions.map((action, j) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4309
4564
|
"a",
|
|
4310
4565
|
{
|
|
@@ -4394,7 +4649,7 @@ function renderStatGroup(items, keyPrefix) {
|
|
|
4394
4649
|
color: "var(--glyph-infographic-value-color, #1d4ed8)",
|
|
4395
4650
|
lineHeight: 1.2
|
|
4396
4651
|
};
|
|
4397
|
-
const
|
|
4652
|
+
const labelStyle4 = {
|
|
4398
4653
|
fontSize: "0.8125rem",
|
|
4399
4654
|
color: "var(--glyph-infographic-label-color, #475569)",
|
|
4400
4655
|
marginTop: "var(--glyph-spacing-xs, 0.25rem)",
|
|
@@ -4410,7 +4665,7 @@ function renderStatGroup(items, keyPrefix) {
|
|
|
4410
4665
|
};
|
|
4411
4666
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: rowStyle, "data-group": "stat", children: items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: statStyle, children: [
|
|
4412
4667
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: valueStyle, children: item.value }),
|
|
4413
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style:
|
|
4668
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: labelStyle4, children: item.label }),
|
|
4414
4669
|
item.description && /* @__PURE__ */ jsxRuntime.jsx("div", { style: descStyle, children: item.description })
|
|
4415
4670
|
] }, `${keyPrefix}-${String(i)}`)) }, keyPrefix);
|
|
4416
4671
|
}
|
|
@@ -4481,18 +4736,18 @@ function renderProgressGroup(items, keyPrefix, colorOffset) {
|
|
|
4481
4736
|
}) }, keyPrefix);
|
|
4482
4737
|
}
|
|
4483
4738
|
function renderFactGroup(items, keyPrefix) {
|
|
4484
|
-
const
|
|
4739
|
+
const listStyle2 = {
|
|
4485
4740
|
listStyle: "none",
|
|
4486
4741
|
margin: 0,
|
|
4487
4742
|
padding: 0
|
|
4488
4743
|
};
|
|
4489
|
-
const
|
|
4744
|
+
const itemStyle4 = {
|
|
4490
4745
|
padding: "var(--glyph-spacing-xs, 0.25rem) 0",
|
|
4491
4746
|
fontSize: "0.875rem",
|
|
4492
4747
|
color: "var(--glyph-text, #1a2035)",
|
|
4493
4748
|
fontWeight: 500
|
|
4494
4749
|
};
|
|
4495
|
-
return /* @__PURE__ */ jsxRuntime.jsx("ul", { style:
|
|
4750
|
+
return /* @__PURE__ */ jsxRuntime.jsx("ul", { style: listStyle2, "data-group": "fact", children: items.map((item, i) => /* @__PURE__ */ jsxRuntime.jsxs("li", { style: itemStyle4, children: [
|
|
4496
4751
|
item.icon && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4497
4752
|
"span",
|
|
4498
4753
|
{
|
|
@@ -4769,7 +5024,7 @@ function Infographic({
|
|
|
4769
5024
|
const baseId = `glyph-infographic-${block.id}`;
|
|
4770
5025
|
const useGrid = sections.length >= 2 && container.tier !== "compact";
|
|
4771
5026
|
const sectionLayouts = react.useMemo(() => computeSectionLayout(sections), [sections]);
|
|
4772
|
-
const
|
|
5027
|
+
const containerStyle11 = {
|
|
4773
5028
|
fontFamily: "var(--glyph-font-body, system-ui, sans-serif)",
|
|
4774
5029
|
color: "var(--glyph-text, #1a2035)",
|
|
4775
5030
|
background: "var(--glyph-surface, #e8ecf3)",
|
|
@@ -4814,7 +5069,7 @@ function Infographic({
|
|
|
4814
5069
|
};
|
|
4815
5070
|
const printCss = useGrid ? `@media print { #${CSS.escape(baseId)} [data-layout="grid"] { display: grid !important; grid-template-columns: repeat(2, 1fr) !important; gap: 0.5rem !important; } #${CSS.escape(baseId)} [data-layout="grid"] > div { break-inside: avoid; } }` : "";
|
|
4816
5071
|
let progressColorOffset = 0;
|
|
4817
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { id: baseId, role: "region", "aria-label": title ?? "Infographic", style:
|
|
5072
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { id: baseId, role: "region", "aria-label": title ?? "Infographic", style: containerStyle11, children: [
|
|
4818
5073
|
printCss && /* @__PURE__ */ jsxRuntime.jsx("style", { children: printCss }),
|
|
4819
5074
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { "data-layout": useGrid ? "grid" : "stack", style: useGrid ? sectionsGridStyle : void 0, children: [
|
|
4820
5075
|
title && /* @__PURE__ */ jsxRuntime.jsx("div", { style: titleStyle2, children: title }),
|
|
@@ -4875,7 +5130,1733 @@ var infographicDefinition = {
|
|
|
4875
5130
|
render: Infographic
|
|
4876
5131
|
};
|
|
4877
5132
|
|
|
5133
|
+
// src/poll/styles.ts
|
|
5134
|
+
var containerStyle3 = {
|
|
5135
|
+
fontFamily: "var(--glyph-font-body, system-ui, sans-serif)",
|
|
5136
|
+
color: "var(--glyph-text, #1a2035)",
|
|
5137
|
+
border: "1px solid var(--glyph-border, #d0d8e4)",
|
|
5138
|
+
borderRadius: "var(--glyph-radius-md, 0.5rem)",
|
|
5139
|
+
overflow: "hidden"
|
|
5140
|
+
};
|
|
5141
|
+
var headerStyle2 = {
|
|
5142
|
+
fontWeight: 700,
|
|
5143
|
+
fontSize: "1.125rem",
|
|
5144
|
+
padding: "var(--glyph-spacing-md, 1rem)",
|
|
5145
|
+
borderBottom: "1px solid var(--glyph-border, #d0d8e4)",
|
|
5146
|
+
color: "var(--glyph-heading, #0a0e1a)"
|
|
5147
|
+
};
|
|
5148
|
+
var questionStyle = {
|
|
5149
|
+
fontWeight: 600,
|
|
5150
|
+
fontSize: "0.9375rem",
|
|
5151
|
+
padding: "var(--glyph-spacing-md, 1rem)",
|
|
5152
|
+
paddingBottom: "0.5rem"
|
|
5153
|
+
};
|
|
5154
|
+
var optionsStyle = {
|
|
5155
|
+
padding: "0 var(--glyph-spacing-md, 1rem)",
|
|
5156
|
+
paddingBottom: "var(--glyph-spacing-md, 1rem)"
|
|
5157
|
+
};
|
|
5158
|
+
function optionLabelStyle2(selected) {
|
|
5159
|
+
return {
|
|
5160
|
+
display: "flex",
|
|
5161
|
+
alignItems: "center",
|
|
5162
|
+
gap: "0.5rem",
|
|
5163
|
+
padding: "0.5rem 0.75rem",
|
|
5164
|
+
marginBottom: "0.375rem",
|
|
5165
|
+
borderRadius: "var(--glyph-radius-md, 0.5rem)",
|
|
5166
|
+
cursor: "pointer",
|
|
5167
|
+
background: selected ? "var(--glyph-surface, #e8ecf3)" : "transparent",
|
|
5168
|
+
border: "1px solid",
|
|
5169
|
+
borderColor: selected ? "var(--glyph-border, #d0d8e4)" : "transparent",
|
|
5170
|
+
fontSize: "0.875rem",
|
|
5171
|
+
lineHeight: 1.6
|
|
5172
|
+
};
|
|
5173
|
+
}
|
|
5174
|
+
var voteButtonStyle = {
|
|
5175
|
+
margin: "0 var(--glyph-spacing-md, 1rem) var(--glyph-spacing-md, 1rem)",
|
|
5176
|
+
padding: "0.5rem 1.25rem",
|
|
5177
|
+
borderRadius: "var(--glyph-radius-md, 0.5rem)",
|
|
5178
|
+
border: "1px solid var(--glyph-border, #d0d8e4)",
|
|
5179
|
+
background: "var(--glyph-surface, #e8ecf3)",
|
|
5180
|
+
color: "var(--glyph-text, #1a2035)",
|
|
5181
|
+
cursor: "pointer",
|
|
5182
|
+
fontWeight: 600,
|
|
5183
|
+
fontSize: "0.875rem"
|
|
5184
|
+
};
|
|
5185
|
+
var resultsStyle = {
|
|
5186
|
+
padding: "var(--glyph-spacing-md, 1rem)",
|
|
5187
|
+
borderTop: "1px solid var(--glyph-border, #d0d8e4)"
|
|
5188
|
+
};
|
|
5189
|
+
var resultRowStyle = {
|
|
5190
|
+
marginBottom: "0.5rem"
|
|
5191
|
+
};
|
|
5192
|
+
var resultLabelStyle = {
|
|
5193
|
+
display: "flex",
|
|
5194
|
+
justifyContent: "space-between",
|
|
5195
|
+
fontSize: "0.8125rem",
|
|
5196
|
+
marginBottom: "0.25rem"
|
|
5197
|
+
};
|
|
5198
|
+
var barTrackStyle = {
|
|
5199
|
+
height: "0.5rem",
|
|
5200
|
+
borderRadius: "0.25rem",
|
|
5201
|
+
background: "var(--glyph-poll-bar-bg, var(--glyph-surface, #e8ecf3))",
|
|
5202
|
+
overflow: "hidden"
|
|
5203
|
+
};
|
|
5204
|
+
function barFillStyle(percentage) {
|
|
5205
|
+
return {
|
|
5206
|
+
height: "100%",
|
|
5207
|
+
width: `${String(percentage)}%`,
|
|
5208
|
+
borderRadius: "0.25rem",
|
|
5209
|
+
background: "var(--glyph-poll-bar-fill, var(--glyph-accent, #0a9d7c))",
|
|
5210
|
+
transition: "width 0.3s ease"
|
|
5211
|
+
};
|
|
5212
|
+
}
|
|
5213
|
+
function Poll({ data, block, onInteraction }) {
|
|
5214
|
+
const { question, options, multiple = false, showResults = true, title } = data;
|
|
5215
|
+
const baseId = `glyph-poll-${block.id}`;
|
|
5216
|
+
const [selected, setSelected] = react.useState([]);
|
|
5217
|
+
const [votes, setVotes] = react.useState(() => options.map(() => 0));
|
|
5218
|
+
const [hasVoted, setHasVoted] = react.useState(false);
|
|
5219
|
+
const toggleOption = (index) => {
|
|
5220
|
+
if (hasVoted) return;
|
|
5221
|
+
if (multiple) {
|
|
5222
|
+
setSelected(
|
|
5223
|
+
(prev) => prev.includes(index) ? prev.filter((i) => i !== index) : [...prev, index]
|
|
5224
|
+
);
|
|
5225
|
+
} else {
|
|
5226
|
+
setSelected([index]);
|
|
5227
|
+
}
|
|
5228
|
+
};
|
|
5229
|
+
const handleVote = () => {
|
|
5230
|
+
if (selected.length === 0 || hasVoted) return;
|
|
5231
|
+
const newVotes = [...votes];
|
|
5232
|
+
for (const idx of selected) {
|
|
5233
|
+
newVotes[idx] = (newVotes[idx] ?? 0) + 1;
|
|
5234
|
+
}
|
|
5235
|
+
setVotes(newVotes);
|
|
5236
|
+
setHasVoted(true);
|
|
5237
|
+
if (onInteraction) {
|
|
5238
|
+
onInteraction({
|
|
5239
|
+
kind: "poll-vote",
|
|
5240
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5241
|
+
blockId: block.id,
|
|
5242
|
+
blockType: block.type,
|
|
5243
|
+
payload: {
|
|
5244
|
+
selectedOptions: selected.map((i) => options[i] ?? String(i)),
|
|
5245
|
+
selectedIndices: [...selected]
|
|
5246
|
+
}
|
|
5247
|
+
});
|
|
5248
|
+
}
|
|
5249
|
+
};
|
|
5250
|
+
const totalVotes = votes.reduce((a, b) => a + b, 0);
|
|
5251
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { id: baseId, role: "region", "aria-label": title ?? "Poll", style: containerStyle3, children: [
|
|
5252
|
+
title && /* @__PURE__ */ jsxRuntime.jsx("div", { style: headerStyle2, children: title }),
|
|
5253
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: questionStyle, children: question }),
|
|
5254
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { role: "group", "aria-label": question, style: optionsStyle, children: options.map((option, index) => /* @__PURE__ */ jsxRuntime.jsxs("label", { style: optionLabelStyle2(selected.includes(index)), children: [
|
|
5255
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5256
|
+
"input",
|
|
5257
|
+
{
|
|
5258
|
+
type: multiple ? "checkbox" : "radio",
|
|
5259
|
+
name: `${baseId}-option`,
|
|
5260
|
+
checked: selected.includes(index),
|
|
5261
|
+
disabled: hasVoted,
|
|
5262
|
+
onChange: () => toggleOption(index),
|
|
5263
|
+
"aria-checked": selected.includes(index)
|
|
5264
|
+
}
|
|
5265
|
+
),
|
|
5266
|
+
option
|
|
5267
|
+
] }, index)) }),
|
|
5268
|
+
!hasVoted && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5269
|
+
"button",
|
|
5270
|
+
{
|
|
5271
|
+
type: "button",
|
|
5272
|
+
disabled: selected.length === 0,
|
|
5273
|
+
style: {
|
|
5274
|
+
...voteButtonStyle,
|
|
5275
|
+
opacity: selected.length === 0 ? 0.5 : 1,
|
|
5276
|
+
cursor: selected.length === 0 ? "not-allowed" : "pointer"
|
|
5277
|
+
},
|
|
5278
|
+
onClick: handleVote,
|
|
5279
|
+
children: "Vote"
|
|
5280
|
+
}
|
|
5281
|
+
),
|
|
5282
|
+
showResults && hasVoted && /* @__PURE__ */ jsxRuntime.jsx("div", { role: "status", "aria-live": "polite", style: resultsStyle, children: options.map((option, index) => {
|
|
5283
|
+
const count = votes[index] ?? 0;
|
|
5284
|
+
const percentage = totalVotes > 0 ? count / totalVotes * 100 : 0;
|
|
5285
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: resultRowStyle, children: [
|
|
5286
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: resultLabelStyle, children: [
|
|
5287
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: option }),
|
|
5288
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
5289
|
+
String(count),
|
|
5290
|
+
" vote",
|
|
5291
|
+
count !== 1 ? "s" : "",
|
|
5292
|
+
" (",
|
|
5293
|
+
String(Math.round(percentage)),
|
|
5294
|
+
"%)"
|
|
5295
|
+
] })
|
|
5296
|
+
] }),
|
|
5297
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5298
|
+
"div",
|
|
5299
|
+
{
|
|
5300
|
+
style: barTrackStyle,
|
|
5301
|
+
role: "progressbar",
|
|
5302
|
+
"aria-valuenow": percentage,
|
|
5303
|
+
"aria-valuemin": 0,
|
|
5304
|
+
"aria-valuemax": 100,
|
|
5305
|
+
"aria-label": `${option}: ${String(Math.round(percentage))}%`,
|
|
5306
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: barFillStyle(percentage) })
|
|
5307
|
+
}
|
|
5308
|
+
)
|
|
5309
|
+
] }, index);
|
|
5310
|
+
}) })
|
|
5311
|
+
] });
|
|
5312
|
+
}
|
|
5313
|
+
|
|
5314
|
+
// src/poll/index.ts
|
|
5315
|
+
var pollDefinition = {
|
|
5316
|
+
type: "ui:poll",
|
|
5317
|
+
schema: schemas.pollSchema,
|
|
5318
|
+
render: Poll
|
|
5319
|
+
};
|
|
5320
|
+
|
|
5321
|
+
// src/rating/styles.ts
|
|
5322
|
+
var containerStyle4 = {
|
|
5323
|
+
fontFamily: "var(--glyph-font-body, system-ui, sans-serif)",
|
|
5324
|
+
color: "var(--glyph-text, #1a2035)",
|
|
5325
|
+
border: "1px solid var(--glyph-border, #d0d8e4)",
|
|
5326
|
+
borderRadius: "var(--glyph-radius-md, 0.5rem)",
|
|
5327
|
+
overflow: "hidden"
|
|
5328
|
+
};
|
|
5329
|
+
var headerStyle3 = {
|
|
5330
|
+
fontWeight: 700,
|
|
5331
|
+
fontSize: "1.125rem",
|
|
5332
|
+
padding: "var(--glyph-spacing-md, 1rem)",
|
|
5333
|
+
borderBottom: "1px solid var(--glyph-border, #d0d8e4)",
|
|
5334
|
+
color: "var(--glyph-heading, #0a0e1a)"
|
|
5335
|
+
};
|
|
5336
|
+
function itemStyle2(isLast) {
|
|
5337
|
+
return {
|
|
5338
|
+
padding: "var(--glyph-spacing-md, 1rem)",
|
|
5339
|
+
borderBottom: isLast ? "none" : "1px solid var(--glyph-border, #d0d8e4)"
|
|
5340
|
+
};
|
|
5341
|
+
}
|
|
5342
|
+
var itemLabelStyle = {
|
|
5343
|
+
fontWeight: 600,
|
|
5344
|
+
fontSize: "0.9375rem",
|
|
5345
|
+
marginBottom: "0.25rem"
|
|
5346
|
+
};
|
|
5347
|
+
var itemDescriptionStyle = {
|
|
5348
|
+
fontSize: "0.8125rem",
|
|
5349
|
+
color: "var(--glyph-text-muted, #6b7a94)",
|
|
5350
|
+
marginBottom: "0.5rem"
|
|
5351
|
+
};
|
|
5352
|
+
var starsContainerStyle = {
|
|
5353
|
+
display: "flex",
|
|
5354
|
+
gap: "0.25rem",
|
|
5355
|
+
alignItems: "center"
|
|
5356
|
+
};
|
|
5357
|
+
function starButtonStyle(filled, hovered) {
|
|
5358
|
+
return {
|
|
5359
|
+
background: "none",
|
|
5360
|
+
border: "none",
|
|
5361
|
+
padding: "0.125rem",
|
|
5362
|
+
cursor: "pointer",
|
|
5363
|
+
fontSize: "1.25rem",
|
|
5364
|
+
lineHeight: 1,
|
|
5365
|
+
color: filled || hovered ? "var(--glyph-rating-star-fill, #f59e0b)" : "var(--glyph-rating-star-empty, var(--glyph-border, #d0d8e4))",
|
|
5366
|
+
transition: "color 0.15s ease"
|
|
5367
|
+
};
|
|
5368
|
+
}
|
|
5369
|
+
function numberButtonStyle(selected) {
|
|
5370
|
+
return {
|
|
5371
|
+
minWidth: "2rem",
|
|
5372
|
+
height: "2rem",
|
|
5373
|
+
display: "flex",
|
|
5374
|
+
alignItems: "center",
|
|
5375
|
+
justifyContent: "center",
|
|
5376
|
+
borderRadius: "var(--glyph-radius-sm, 0.375rem)",
|
|
5377
|
+
border: "1px solid var(--glyph-border, #d0d8e4)",
|
|
5378
|
+
background: selected ? "var(--glyph-accent, #0a9d7c)" : "transparent",
|
|
5379
|
+
color: selected ? "#fff" : "var(--glyph-text, #1a2035)",
|
|
5380
|
+
cursor: "pointer",
|
|
5381
|
+
fontWeight: 600,
|
|
5382
|
+
fontSize: "0.875rem",
|
|
5383
|
+
transition: "background 0.15s ease, color 0.15s ease"
|
|
5384
|
+
};
|
|
5385
|
+
}
|
|
5386
|
+
var scaleLabelsStyle = {
|
|
5387
|
+
display: "flex",
|
|
5388
|
+
justifyContent: "space-between",
|
|
5389
|
+
fontSize: "0.75rem",
|
|
5390
|
+
color: "var(--glyph-text-muted, #6b7a94)",
|
|
5391
|
+
marginTop: "0.375rem"
|
|
5392
|
+
};
|
|
5393
|
+
function Rating({
|
|
5394
|
+
data,
|
|
5395
|
+
block,
|
|
5396
|
+
onInteraction
|
|
5397
|
+
}) {
|
|
5398
|
+
const { title, scale = 5, mode = "star", labels, items } = data;
|
|
5399
|
+
const baseId = `glyph-rating-${block.id}`;
|
|
5400
|
+
const [ratings, setRatings] = react.useState(() => items.map(() => null));
|
|
5401
|
+
const [hoveredStar, setHoveredStar] = react.useState(null);
|
|
5402
|
+
const handleRate = (itemIndex, value) => {
|
|
5403
|
+
const newRatings = [...ratings];
|
|
5404
|
+
newRatings[itemIndex] = value;
|
|
5405
|
+
setRatings(newRatings);
|
|
5406
|
+
if (onInteraction) {
|
|
5407
|
+
onInteraction({
|
|
5408
|
+
kind: "rating-change",
|
|
5409
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5410
|
+
blockId: block.id,
|
|
5411
|
+
blockType: block.type,
|
|
5412
|
+
payload: {
|
|
5413
|
+
itemIndex,
|
|
5414
|
+
itemLabel: items[itemIndex]?.label ?? "",
|
|
5415
|
+
value,
|
|
5416
|
+
allRatings: items.map((item, i) => ({
|
|
5417
|
+
label: item.label,
|
|
5418
|
+
value: i === itemIndex ? value : newRatings[i] ?? null
|
|
5419
|
+
}))
|
|
5420
|
+
}
|
|
5421
|
+
});
|
|
5422
|
+
}
|
|
5423
|
+
};
|
|
5424
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { id: baseId, role: "region", "aria-label": title ?? "Rating", style: containerStyle4, children: [
|
|
5425
|
+
title && /* @__PURE__ */ jsxRuntime.jsx("div", { style: headerStyle3, children: title }),
|
|
5426
|
+
items.map((item, itemIndex) => {
|
|
5427
|
+
const currentRating = ratings[itemIndex] ?? null;
|
|
5428
|
+
const isLast = itemIndex === items.length - 1;
|
|
5429
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: itemStyle2(isLast), children: [
|
|
5430
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: itemLabelStyle, children: item.label }),
|
|
5431
|
+
item.description && /* @__PURE__ */ jsxRuntime.jsx("div", { style: itemDescriptionStyle, children: item.description }),
|
|
5432
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { role: "radiogroup", "aria-label": `Rate ${item.label}`, style: starsContainerStyle, children: Array.from({ length: scale }, (_, starIndex) => {
|
|
5433
|
+
const value = starIndex + 1;
|
|
5434
|
+
const isHovered = hoveredStar !== null && hoveredStar.itemIndex === itemIndex && value <= hoveredStar.value;
|
|
5435
|
+
const isFilled = currentRating !== null && value <= currentRating;
|
|
5436
|
+
if (mode === "number") {
|
|
5437
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5438
|
+
"button",
|
|
5439
|
+
{
|
|
5440
|
+
type: "button",
|
|
5441
|
+
role: "radio",
|
|
5442
|
+
"aria-checked": currentRating === value,
|
|
5443
|
+
"aria-label": `${String(value)} out of ${String(scale)}`,
|
|
5444
|
+
style: numberButtonStyle(currentRating === value),
|
|
5445
|
+
onClick: () => handleRate(itemIndex, value),
|
|
5446
|
+
children: String(value)
|
|
5447
|
+
},
|
|
5448
|
+
starIndex
|
|
5449
|
+
);
|
|
5450
|
+
}
|
|
5451
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5452
|
+
"button",
|
|
5453
|
+
{
|
|
5454
|
+
type: "button",
|
|
5455
|
+
role: "radio",
|
|
5456
|
+
"aria-checked": currentRating === value,
|
|
5457
|
+
"aria-label": `${String(value)} out of ${String(scale)} stars`,
|
|
5458
|
+
style: starButtonStyle(isFilled, isHovered),
|
|
5459
|
+
onClick: () => handleRate(itemIndex, value),
|
|
5460
|
+
onMouseEnter: () => setHoveredStar({ itemIndex, value }),
|
|
5461
|
+
onMouseLeave: () => setHoveredStar(null),
|
|
5462
|
+
children: "\u2605"
|
|
5463
|
+
},
|
|
5464
|
+
starIndex
|
|
5465
|
+
);
|
|
5466
|
+
}) }),
|
|
5467
|
+
labels && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: scaleLabelsStyle, children: [
|
|
5468
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: labels.low }),
|
|
5469
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: labels.high })
|
|
5470
|
+
] }),
|
|
5471
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5472
|
+
"div",
|
|
5473
|
+
{
|
|
5474
|
+
"aria-live": "polite",
|
|
5475
|
+
style: {
|
|
5476
|
+
position: "absolute",
|
|
5477
|
+
width: "1px",
|
|
5478
|
+
height: "1px",
|
|
5479
|
+
padding: 0,
|
|
5480
|
+
margin: "-1px",
|
|
5481
|
+
overflow: "hidden",
|
|
5482
|
+
clip: "rect(0,0,0,0)",
|
|
5483
|
+
whiteSpace: "nowrap",
|
|
5484
|
+
border: 0
|
|
5485
|
+
},
|
|
5486
|
+
children: currentRating !== null && `${item.label} rated ${String(currentRating)} out of ${String(scale)}`
|
|
5487
|
+
}
|
|
5488
|
+
)
|
|
5489
|
+
] }, itemIndex);
|
|
5490
|
+
})
|
|
5491
|
+
] });
|
|
5492
|
+
}
|
|
5493
|
+
|
|
5494
|
+
// src/rating/index.ts
|
|
5495
|
+
var ratingDefinition = {
|
|
5496
|
+
type: "ui:rating",
|
|
5497
|
+
schema: schemas.ratingSchema,
|
|
5498
|
+
render: Rating
|
|
5499
|
+
};
|
|
5500
|
+
|
|
5501
|
+
// src/ranker/styles.ts
|
|
5502
|
+
var containerStyle5 = {
|
|
5503
|
+
fontFamily: "var(--glyph-font-body, system-ui, sans-serif)",
|
|
5504
|
+
color: "var(--glyph-text, #1a2035)",
|
|
5505
|
+
border: "1px solid var(--glyph-border, #d0d8e4)",
|
|
5506
|
+
borderRadius: "var(--glyph-radius-md, 0.5rem)",
|
|
5507
|
+
overflow: "hidden"
|
|
5508
|
+
};
|
|
5509
|
+
var headerStyle4 = {
|
|
5510
|
+
fontWeight: 700,
|
|
5511
|
+
fontSize: "1.125rem",
|
|
5512
|
+
padding: "var(--glyph-spacing-md, 1rem)",
|
|
5513
|
+
borderBottom: "1px solid var(--glyph-border, #d0d8e4)",
|
|
5514
|
+
color: "var(--glyph-heading, #0a0e1a)"
|
|
5515
|
+
};
|
|
5516
|
+
var listStyle = {
|
|
5517
|
+
listStyle: "none",
|
|
5518
|
+
margin: 0,
|
|
5519
|
+
padding: 0
|
|
5520
|
+
};
|
|
5521
|
+
function itemStyle3(isDragging, isGrabbed) {
|
|
5522
|
+
return {
|
|
5523
|
+
display: "flex",
|
|
5524
|
+
alignItems: "center",
|
|
5525
|
+
gap: "0.75rem",
|
|
5526
|
+
padding: "0.75rem var(--glyph-spacing-md, 1rem)",
|
|
5527
|
+
borderBottom: "1px solid var(--glyph-border, #d0d8e4)",
|
|
5528
|
+
background: isDragging ? "var(--glyph-accent-subtle, #e6f6f2)" : "transparent",
|
|
5529
|
+
cursor: isGrabbed ? "grabbing" : "grab",
|
|
5530
|
+
userSelect: "none",
|
|
5531
|
+
transition: "background 0.15s ease",
|
|
5532
|
+
outline: isGrabbed ? "2px solid var(--glyph-accent, #0a9d7c)" : "none",
|
|
5533
|
+
outlineOffset: "-2px"
|
|
5534
|
+
};
|
|
5535
|
+
}
|
|
5536
|
+
var rankBadgeStyle = {
|
|
5537
|
+
minWidth: "1.75rem",
|
|
5538
|
+
height: "1.75rem",
|
|
5539
|
+
display: "flex",
|
|
5540
|
+
alignItems: "center",
|
|
5541
|
+
justifyContent: "center",
|
|
5542
|
+
borderRadius: "50%",
|
|
5543
|
+
background: "var(--glyph-surface, #e8ecf3)",
|
|
5544
|
+
fontWeight: 700,
|
|
5545
|
+
fontSize: "0.8125rem",
|
|
5546
|
+
color: "var(--glyph-text-muted, #6b7a94)",
|
|
5547
|
+
flexShrink: 0
|
|
5548
|
+
};
|
|
5549
|
+
var itemContentStyle = {
|
|
5550
|
+
flex: 1,
|
|
5551
|
+
minWidth: 0
|
|
5552
|
+
};
|
|
5553
|
+
var itemLabelStyle2 = {
|
|
5554
|
+
fontWeight: 600,
|
|
5555
|
+
fontSize: "0.9375rem"
|
|
5556
|
+
};
|
|
5557
|
+
var itemDescriptionStyle2 = {
|
|
5558
|
+
fontSize: "0.8125rem",
|
|
5559
|
+
color: "var(--glyph-text-muted, #6b7a94)",
|
|
5560
|
+
marginTop: "0.125rem"
|
|
5561
|
+
};
|
|
5562
|
+
var gripStyle = {
|
|
5563
|
+
color: "var(--glyph-text-muted, #6b7a94)",
|
|
5564
|
+
fontSize: "1rem",
|
|
5565
|
+
flexShrink: 0
|
|
5566
|
+
};
|
|
5567
|
+
function Ranker({
|
|
5568
|
+
data,
|
|
5569
|
+
block,
|
|
5570
|
+
onInteraction
|
|
5571
|
+
}) {
|
|
5572
|
+
const { title, items: initialItems } = data;
|
|
5573
|
+
const baseId = `glyph-ranker-${block.id}`;
|
|
5574
|
+
const [items, setItems] = react.useState(initialItems);
|
|
5575
|
+
const [grabbedIndex, setGrabbedIndex] = react.useState(null);
|
|
5576
|
+
const moveItem = react.useCallback(
|
|
5577
|
+
(fromIndex, toIndex) => {
|
|
5578
|
+
if (fromIndex === toIndex) return;
|
|
5579
|
+
setItems((prevItems) => {
|
|
5580
|
+
const newItems = [...prevItems];
|
|
5581
|
+
const [moved] = newItems.splice(fromIndex, 1);
|
|
5582
|
+
if (!moved) return prevItems;
|
|
5583
|
+
newItems.splice(toIndex, 0, moved);
|
|
5584
|
+
if (onInteraction) {
|
|
5585
|
+
onInteraction({
|
|
5586
|
+
kind: "ranker-reorder",
|
|
5587
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5588
|
+
blockId: block.id,
|
|
5589
|
+
blockType: block.type,
|
|
5590
|
+
payload: {
|
|
5591
|
+
orderedItems: newItems.map((item, i) => ({
|
|
5592
|
+
id: item.id,
|
|
5593
|
+
label: item.label,
|
|
5594
|
+
rank: i + 1
|
|
5595
|
+
})),
|
|
5596
|
+
movedItem: {
|
|
5597
|
+
id: moved.id,
|
|
5598
|
+
label: moved.label,
|
|
5599
|
+
fromRank: fromIndex + 1,
|
|
5600
|
+
toRank: toIndex + 1
|
|
5601
|
+
}
|
|
5602
|
+
}
|
|
5603
|
+
});
|
|
5604
|
+
}
|
|
5605
|
+
return newItems;
|
|
5606
|
+
});
|
|
5607
|
+
},
|
|
5608
|
+
[block.id, block.type, onInteraction]
|
|
5609
|
+
);
|
|
5610
|
+
const handleKeyDown = (e, index) => {
|
|
5611
|
+
if (e.key === " " || e.key === "Enter") {
|
|
5612
|
+
e.preventDefault();
|
|
5613
|
+
if (grabbedIndex === null) {
|
|
5614
|
+
setGrabbedIndex(index);
|
|
5615
|
+
} else {
|
|
5616
|
+
setGrabbedIndex(null);
|
|
5617
|
+
}
|
|
5618
|
+
} else if (e.key === "Escape") {
|
|
5619
|
+
setGrabbedIndex(null);
|
|
5620
|
+
} else if (e.key === "ArrowUp" && grabbedIndex !== null) {
|
|
5621
|
+
e.preventDefault();
|
|
5622
|
+
if (grabbedIndex > 0) {
|
|
5623
|
+
moveItem(grabbedIndex, grabbedIndex - 1);
|
|
5624
|
+
setGrabbedIndex(grabbedIndex - 1);
|
|
5625
|
+
}
|
|
5626
|
+
} else if (e.key === "ArrowDown" && grabbedIndex !== null) {
|
|
5627
|
+
e.preventDefault();
|
|
5628
|
+
if (grabbedIndex < items.length - 1) {
|
|
5629
|
+
moveItem(grabbedIndex, grabbedIndex + 1);
|
|
5630
|
+
setGrabbedIndex(grabbedIndex + 1);
|
|
5631
|
+
}
|
|
5632
|
+
}
|
|
5633
|
+
};
|
|
5634
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { id: baseId, role: "region", "aria-label": title ?? "Ranker", style: containerStyle5, children: [
|
|
5635
|
+
title && /* @__PURE__ */ jsxRuntime.jsx("div", { style: headerStyle4, children: title }),
|
|
5636
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { role: "list", "aria-label": title ?? "Rank items", style: listStyle, children: items.map((item, index) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5637
|
+
"li",
|
|
5638
|
+
{
|
|
5639
|
+
role: "listitem",
|
|
5640
|
+
"aria-grabbed": grabbedIndex === index,
|
|
5641
|
+
"aria-label": `${item.label}, rank ${String(index + 1)}`,
|
|
5642
|
+
tabIndex: 0,
|
|
5643
|
+
style: itemStyle3(false, grabbedIndex === index),
|
|
5644
|
+
onKeyDown: (e) => handleKeyDown(e, index),
|
|
5645
|
+
children: [
|
|
5646
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: gripStyle, "aria-hidden": "true", children: "\u283F" }),
|
|
5647
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: rankBadgeStyle, children: String(index + 1) }),
|
|
5648
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: itemContentStyle, children: [
|
|
5649
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: itemLabelStyle2, children: item.label }),
|
|
5650
|
+
item.description && /* @__PURE__ */ jsxRuntime.jsx("div", { style: itemDescriptionStyle2, children: item.description })
|
|
5651
|
+
] })
|
|
5652
|
+
]
|
|
5653
|
+
},
|
|
5654
|
+
item.id
|
|
5655
|
+
)) }),
|
|
5656
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5657
|
+
"div",
|
|
5658
|
+
{
|
|
5659
|
+
"aria-live": "assertive",
|
|
5660
|
+
style: {
|
|
5661
|
+
position: "absolute",
|
|
5662
|
+
width: "1px",
|
|
5663
|
+
height: "1px",
|
|
5664
|
+
padding: 0,
|
|
5665
|
+
margin: "-1px",
|
|
5666
|
+
overflow: "hidden",
|
|
5667
|
+
clip: "rect(0,0,0,0)",
|
|
5668
|
+
whiteSpace: "nowrap",
|
|
5669
|
+
border: 0
|
|
5670
|
+
},
|
|
5671
|
+
children: grabbedIndex !== null && `${items[grabbedIndex]?.label ?? ""} grabbed, rank ${String(grabbedIndex + 1)} of ${String(items.length)}. Use arrow keys to move.`
|
|
5672
|
+
}
|
|
5673
|
+
)
|
|
5674
|
+
] });
|
|
5675
|
+
}
|
|
5676
|
+
|
|
5677
|
+
// src/ranker/index.ts
|
|
5678
|
+
var rankerDefinition = {
|
|
5679
|
+
type: "ui:ranker",
|
|
5680
|
+
schema: schemas.rankerSchema,
|
|
5681
|
+
render: Ranker
|
|
5682
|
+
};
|
|
5683
|
+
|
|
5684
|
+
// src/slider/styles.ts
|
|
5685
|
+
var containerStyle6 = {
|
|
5686
|
+
fontFamily: "var(--glyph-font-body, system-ui, sans-serif)",
|
|
5687
|
+
color: "var(--glyph-text, #1a2035)",
|
|
5688
|
+
border: "1px solid var(--glyph-border, #d0d8e4)",
|
|
5689
|
+
borderRadius: "var(--glyph-radius-md, 0.5rem)",
|
|
5690
|
+
overflow: "hidden"
|
|
5691
|
+
};
|
|
5692
|
+
var headerStyle5 = {
|
|
5693
|
+
fontWeight: 700,
|
|
5694
|
+
fontSize: "1.125rem",
|
|
5695
|
+
padding: "var(--glyph-spacing-md, 1rem)",
|
|
5696
|
+
borderBottom: "1px solid var(--glyph-border, #d0d8e4)",
|
|
5697
|
+
color: "var(--glyph-heading, #0a0e1a)"
|
|
5698
|
+
};
|
|
5699
|
+
function parameterStyle(isLast) {
|
|
5700
|
+
return {
|
|
5701
|
+
padding: "var(--glyph-spacing-md, 1rem)",
|
|
5702
|
+
borderBottom: isLast ? "none" : "1px solid var(--glyph-border, #d0d8e4)"
|
|
5703
|
+
};
|
|
5704
|
+
}
|
|
5705
|
+
var parameterHeaderStyle = {
|
|
5706
|
+
display: "flex",
|
|
5707
|
+
justifyContent: "space-between",
|
|
5708
|
+
alignItems: "center",
|
|
5709
|
+
marginBottom: "0.5rem"
|
|
5710
|
+
};
|
|
5711
|
+
var parameterLabelStyle = {
|
|
5712
|
+
fontWeight: 600,
|
|
5713
|
+
fontSize: "0.9375rem"
|
|
5714
|
+
};
|
|
5715
|
+
var parameterValueStyle = {
|
|
5716
|
+
fontSize: "0.9375rem",
|
|
5717
|
+
fontWeight: 600,
|
|
5718
|
+
color: "var(--glyph-accent, #0a9d7c)",
|
|
5719
|
+
fontVariantNumeric: "tabular-nums"
|
|
5720
|
+
};
|
|
5721
|
+
var rangeInputStyle = {
|
|
5722
|
+
width: "100%",
|
|
5723
|
+
margin: 0,
|
|
5724
|
+
accentColor: "var(--glyph-slider-fill, var(--glyph-accent, #0a9d7c))"
|
|
5725
|
+
};
|
|
5726
|
+
var rangeLabelsStyle = {
|
|
5727
|
+
display: "flex",
|
|
5728
|
+
justifyContent: "space-between",
|
|
5729
|
+
fontSize: "0.75rem",
|
|
5730
|
+
color: "var(--glyph-text-muted, #6b7a94)",
|
|
5731
|
+
marginTop: "0.25rem"
|
|
5732
|
+
};
|
|
5733
|
+
function Slider({
|
|
5734
|
+
data,
|
|
5735
|
+
block,
|
|
5736
|
+
onInteraction
|
|
5737
|
+
}) {
|
|
5738
|
+
const { title, parameters } = data;
|
|
5739
|
+
const baseId = `glyph-slider-${block.id}`;
|
|
5740
|
+
const [values, setValues] = react.useState(
|
|
5741
|
+
() => parameters.map((p) => p.value ?? p.min ?? 0)
|
|
5742
|
+
);
|
|
5743
|
+
const handleChange = (paramIndex, newValue) => {
|
|
5744
|
+
const newValues = [...values];
|
|
5745
|
+
newValues[paramIndex] = newValue;
|
|
5746
|
+
setValues(newValues);
|
|
5747
|
+
const param = parameters[paramIndex];
|
|
5748
|
+
if (!param) return;
|
|
5749
|
+
if (onInteraction) {
|
|
5750
|
+
onInteraction({
|
|
5751
|
+
kind: "slider-change",
|
|
5752
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5753
|
+
blockId: block.id,
|
|
5754
|
+
blockType: block.type,
|
|
5755
|
+
payload: {
|
|
5756
|
+
parameterId: param.id,
|
|
5757
|
+
parameterLabel: param.label,
|
|
5758
|
+
value: newValue,
|
|
5759
|
+
allValues: parameters.map((p, i) => ({
|
|
5760
|
+
id: p.id,
|
|
5761
|
+
label: p.label,
|
|
5762
|
+
value: i === paramIndex ? newValue : newValues[i] ?? 0
|
|
5763
|
+
}))
|
|
5764
|
+
}
|
|
5765
|
+
});
|
|
5766
|
+
}
|
|
5767
|
+
};
|
|
5768
|
+
const formatValue = (value, unit) => {
|
|
5769
|
+
return unit ? `${String(value)}${unit}` : String(value);
|
|
5770
|
+
};
|
|
5771
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { id: baseId, role: "region", "aria-label": title ?? "Slider", style: containerStyle6, children: [
|
|
5772
|
+
title && /* @__PURE__ */ jsxRuntime.jsx("div", { style: headerStyle5, children: title }),
|
|
5773
|
+
parameters.map((param, index) => {
|
|
5774
|
+
const min2 = param.min ?? 0;
|
|
5775
|
+
const max2 = param.max ?? 100;
|
|
5776
|
+
const step = param.step ?? 1;
|
|
5777
|
+
const currentValue = values[index] ?? min2;
|
|
5778
|
+
const isLast = index === parameters.length - 1;
|
|
5779
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: parameterStyle(isLast), children: [
|
|
5780
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: parameterHeaderStyle, children: [
|
|
5781
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: `${baseId}-${param.id}`, style: parameterLabelStyle, children: param.label }),
|
|
5782
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: parameterValueStyle, "aria-live": "polite", children: formatValue(currentValue, param.unit) })
|
|
5783
|
+
] }),
|
|
5784
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5785
|
+
"input",
|
|
5786
|
+
{
|
|
5787
|
+
id: `${baseId}-${param.id}`,
|
|
5788
|
+
type: "range",
|
|
5789
|
+
min: min2,
|
|
5790
|
+
max: max2,
|
|
5791
|
+
step,
|
|
5792
|
+
value: currentValue,
|
|
5793
|
+
onChange: (e) => handleChange(index, Number(e.target.value)),
|
|
5794
|
+
"aria-valuemin": min2,
|
|
5795
|
+
"aria-valuemax": max2,
|
|
5796
|
+
"aria-valuenow": currentValue,
|
|
5797
|
+
"aria-valuetext": formatValue(currentValue, param.unit),
|
|
5798
|
+
style: rangeInputStyle
|
|
5799
|
+
}
|
|
5800
|
+
),
|
|
5801
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: rangeLabelsStyle, children: [
|
|
5802
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: formatValue(min2, param.unit) }),
|
|
5803
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: formatValue(max2, param.unit) })
|
|
5804
|
+
] })
|
|
5805
|
+
] }, param.id);
|
|
5806
|
+
})
|
|
5807
|
+
] });
|
|
5808
|
+
}
|
|
5809
|
+
|
|
5810
|
+
// src/slider/index.ts
|
|
5811
|
+
var sliderDefinition = {
|
|
5812
|
+
type: "ui:slider",
|
|
5813
|
+
schema: schemas.sliderSchema,
|
|
5814
|
+
render: Slider
|
|
5815
|
+
};
|
|
5816
|
+
|
|
5817
|
+
// src/matrix/styles.ts
|
|
5818
|
+
var containerStyle7 = {
|
|
5819
|
+
fontFamily: "var(--glyph-font-body, system-ui, sans-serif)",
|
|
5820
|
+
color: "var(--glyph-text, #1a2035)",
|
|
5821
|
+
border: "1px solid var(--glyph-border, #d0d8e4)",
|
|
5822
|
+
borderRadius: "var(--glyph-radius-md, 0.5rem)",
|
|
5823
|
+
overflow: "auto"
|
|
5824
|
+
};
|
|
5825
|
+
var headerStyle6 = {
|
|
5826
|
+
fontWeight: 700,
|
|
5827
|
+
fontSize: "1.125rem",
|
|
5828
|
+
padding: "var(--glyph-spacing-md, 1rem)",
|
|
5829
|
+
borderBottom: "1px solid var(--glyph-border, #d0d8e4)",
|
|
5830
|
+
color: "var(--glyph-heading, #0a0e1a)"
|
|
5831
|
+
};
|
|
5832
|
+
var tableStyle = {
|
|
5833
|
+
width: "100%",
|
|
5834
|
+
borderCollapse: "collapse",
|
|
5835
|
+
fontSize: "0.875rem"
|
|
5836
|
+
};
|
|
5837
|
+
var thStyle = {
|
|
5838
|
+
padding: "0.625rem 0.75rem",
|
|
5839
|
+
textAlign: "center",
|
|
5840
|
+
fontWeight: 600,
|
|
5841
|
+
borderBottom: "2px solid var(--glyph-border, #d0d8e4)",
|
|
5842
|
+
background: "var(--glyph-table-header-bg, var(--glyph-surface, #e8ecf3))",
|
|
5843
|
+
whiteSpace: "nowrap"
|
|
5844
|
+
};
|
|
5845
|
+
var rowHeaderStyle = {
|
|
5846
|
+
padding: "0.625rem 0.75rem",
|
|
5847
|
+
textAlign: "left",
|
|
5848
|
+
fontWeight: 600,
|
|
5849
|
+
borderBottom: "1px solid var(--glyph-border, #d0d8e4)",
|
|
5850
|
+
borderRight: "1px solid var(--glyph-border, #d0d8e4)",
|
|
5851
|
+
whiteSpace: "nowrap"
|
|
5852
|
+
};
|
|
5853
|
+
var cellStyle = {
|
|
5854
|
+
padding: "0.375rem 0.5rem",
|
|
5855
|
+
textAlign: "center",
|
|
5856
|
+
borderBottom: "1px solid var(--glyph-border, #d0d8e4)"
|
|
5857
|
+
};
|
|
5858
|
+
var inputStyle = {
|
|
5859
|
+
width: "3.5rem",
|
|
5860
|
+
padding: "0.25rem 0.375rem",
|
|
5861
|
+
textAlign: "center",
|
|
5862
|
+
border: "1px solid var(--glyph-border, #d0d8e4)",
|
|
5863
|
+
borderRadius: "var(--glyph-radius-sm, 0.375rem)",
|
|
5864
|
+
background: "transparent",
|
|
5865
|
+
color: "var(--glyph-text, #1a2035)",
|
|
5866
|
+
fontSize: "0.875rem",
|
|
5867
|
+
fontVariantNumeric: "tabular-nums"
|
|
5868
|
+
};
|
|
5869
|
+
var weightStyle = {
|
|
5870
|
+
fontSize: "0.6875rem",
|
|
5871
|
+
color: "var(--glyph-text-muted, #6b7a94)",
|
|
5872
|
+
fontWeight: 400
|
|
5873
|
+
};
|
|
5874
|
+
var totalCellStyle = {
|
|
5875
|
+
padding: "0.625rem 0.75rem",
|
|
5876
|
+
textAlign: "center",
|
|
5877
|
+
fontWeight: 700,
|
|
5878
|
+
borderBottom: "1px solid var(--glyph-border, #d0d8e4)",
|
|
5879
|
+
background: "var(--glyph-surface, #e8ecf3)",
|
|
5880
|
+
fontVariantNumeric: "tabular-nums"
|
|
5881
|
+
};
|
|
5882
|
+
var totalHeaderStyle = {
|
|
5883
|
+
...thStyle,
|
|
5884
|
+
borderLeft: "2px solid var(--glyph-border, #d0d8e4)"
|
|
5885
|
+
};
|
|
5886
|
+
function computeWeightedTotals(rows, columns, values) {
|
|
5887
|
+
return rows.map((row) => {
|
|
5888
|
+
let total = 0;
|
|
5889
|
+
for (const col of columns) {
|
|
5890
|
+
const score = values[row.id]?.[col.id] ?? 0;
|
|
5891
|
+
const weight = col.weight ?? 1;
|
|
5892
|
+
total += score * weight;
|
|
5893
|
+
}
|
|
5894
|
+
return { rowId: row.id, rowLabel: row.label, total: Math.round(total * 100) / 100 };
|
|
5895
|
+
});
|
|
5896
|
+
}
|
|
5897
|
+
function Matrix({
|
|
5898
|
+
data,
|
|
5899
|
+
block,
|
|
5900
|
+
onInteraction
|
|
5901
|
+
}) {
|
|
5902
|
+
const { title, scale = 5, showTotals = true, columns, rows } = data;
|
|
5903
|
+
const baseId = `glyph-matrix-${block.id}`;
|
|
5904
|
+
const [values, setValues] = react.useState(() => {
|
|
5905
|
+
const init = {};
|
|
5906
|
+
for (const row of rows) {
|
|
5907
|
+
const rowMap = {};
|
|
5908
|
+
for (const col of columns) {
|
|
5909
|
+
rowMap[col.id] = 0;
|
|
5910
|
+
}
|
|
5911
|
+
init[row.id] = rowMap;
|
|
5912
|
+
}
|
|
5913
|
+
return init;
|
|
5914
|
+
});
|
|
5915
|
+
const handleChange = react.useCallback(
|
|
5916
|
+
(rowId, columnId, value) => {
|
|
5917
|
+
const clamped = Math.max(0, Math.min(scale, value));
|
|
5918
|
+
setValues((prevValues) => {
|
|
5919
|
+
const newValues = Object.fromEntries(
|
|
5920
|
+
Object.entries(prevValues).map(([k, v]) => [k, { ...v }])
|
|
5921
|
+
);
|
|
5922
|
+
if (!newValues[rowId]) newValues[rowId] = {};
|
|
5923
|
+
newValues[rowId] = { ...newValues[rowId], [columnId]: clamped };
|
|
5924
|
+
const row = rows.find((r) => r.id === rowId);
|
|
5925
|
+
const col = columns.find((c) => c.id === columnId);
|
|
5926
|
+
if (onInteraction && row && col) {
|
|
5927
|
+
const payloadValues = Object.fromEntries(
|
|
5928
|
+
Object.entries(newValues).map(([k, v]) => [k, { ...v }])
|
|
5929
|
+
);
|
|
5930
|
+
onInteraction({
|
|
5931
|
+
kind: "matrix-change",
|
|
5932
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5933
|
+
blockId: block.id,
|
|
5934
|
+
blockType: block.type,
|
|
5935
|
+
payload: {
|
|
5936
|
+
rowId,
|
|
5937
|
+
rowLabel: row.label,
|
|
5938
|
+
columnId,
|
|
5939
|
+
columnLabel: col.label,
|
|
5940
|
+
value: clamped,
|
|
5941
|
+
allValues: payloadValues,
|
|
5942
|
+
weightedTotals: computeWeightedTotals(rows, columns, newValues)
|
|
5943
|
+
}
|
|
5944
|
+
});
|
|
5945
|
+
}
|
|
5946
|
+
return newValues;
|
|
5947
|
+
});
|
|
5948
|
+
},
|
|
5949
|
+
[scale, rows, columns, block.id, block.type, onInteraction]
|
|
5950
|
+
);
|
|
5951
|
+
const totals = computeWeightedTotals(rows, columns, values);
|
|
5952
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { id: baseId, role: "region", "aria-label": title ?? "Decision Matrix", style: containerStyle7, children: [
|
|
5953
|
+
title && /* @__PURE__ */ jsxRuntime.jsx("div", { style: headerStyle6, children: title }),
|
|
5954
|
+
/* @__PURE__ */ jsxRuntime.jsxs("table", { role: "grid", style: tableStyle, children: [
|
|
5955
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
5956
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { style: thStyle }),
|
|
5957
|
+
columns.map((col) => /* @__PURE__ */ jsxRuntime.jsxs("th", { style: thStyle, children: [
|
|
5958
|
+
col.label,
|
|
5959
|
+
(col.weight ?? 1) !== 1 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: weightStyle, children: [
|
|
5960
|
+
"\xD7",
|
|
5961
|
+
String(col.weight)
|
|
5962
|
+
] })
|
|
5963
|
+
] }, col.id)),
|
|
5964
|
+
showTotals && /* @__PURE__ */ jsxRuntime.jsx("th", { style: totalHeaderStyle, children: "Total" })
|
|
5965
|
+
] }) }),
|
|
5966
|
+
/* @__PURE__ */ jsxRuntime.jsx("tbody", { children: rows.map((row) => {
|
|
5967
|
+
const rowTotal = totals.find((t) => t.rowId === row.id)?.total ?? 0;
|
|
5968
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
5969
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { scope: "row", style: rowHeaderStyle, children: row.label }),
|
|
5970
|
+
columns.map((col) => {
|
|
5971
|
+
const cellValue = values[row.id]?.[col.id] ?? 0;
|
|
5972
|
+
return /* @__PURE__ */ jsxRuntime.jsx("td", { style: cellStyle, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5973
|
+
"input",
|
|
5974
|
+
{
|
|
5975
|
+
type: "number",
|
|
5976
|
+
min: 0,
|
|
5977
|
+
max: scale,
|
|
5978
|
+
value: cellValue,
|
|
5979
|
+
onChange: (e) => handleChange(row.id, col.id, Number(e.target.value)),
|
|
5980
|
+
"aria-label": `Score for ${row.label} on ${col.label}`,
|
|
5981
|
+
style: inputStyle
|
|
5982
|
+
}
|
|
5983
|
+
) }, col.id);
|
|
5984
|
+
}),
|
|
5985
|
+
showTotals && /* @__PURE__ */ jsxRuntime.jsx("td", { style: totalCellStyle, children: String(rowTotal) })
|
|
5986
|
+
] }, row.id);
|
|
5987
|
+
}) })
|
|
5988
|
+
] })
|
|
5989
|
+
] });
|
|
5990
|
+
}
|
|
5991
|
+
|
|
5992
|
+
// src/matrix/index.ts
|
|
5993
|
+
var matrixDefinition = {
|
|
5994
|
+
type: "ui:matrix",
|
|
5995
|
+
schema: schemas.matrixSchema,
|
|
5996
|
+
render: Matrix
|
|
5997
|
+
};
|
|
5998
|
+
|
|
5999
|
+
// src/form/styles.ts
|
|
6000
|
+
var containerStyle8 = {
|
|
6001
|
+
fontFamily: "var(--glyph-font-body, system-ui, sans-serif)",
|
|
6002
|
+
color: "var(--glyph-text, #1a2035)",
|
|
6003
|
+
border: "1px solid var(--glyph-border, #d0d8e4)",
|
|
6004
|
+
borderRadius: "var(--glyph-radius-md, 0.5rem)",
|
|
6005
|
+
overflow: "hidden"
|
|
6006
|
+
};
|
|
6007
|
+
var headerStyle7 = {
|
|
6008
|
+
fontWeight: 700,
|
|
6009
|
+
fontSize: "1.125rem",
|
|
6010
|
+
padding: "var(--glyph-spacing-md, 1rem)",
|
|
6011
|
+
paddingBottom: "0.25rem",
|
|
6012
|
+
color: "var(--glyph-heading, #0a0e1a)"
|
|
6013
|
+
};
|
|
6014
|
+
var descriptionStyle = {
|
|
6015
|
+
fontSize: "0.875rem",
|
|
6016
|
+
color: "var(--glyph-text-muted, #6b7a94)",
|
|
6017
|
+
padding: "0 var(--glyph-spacing-md, 1rem)",
|
|
6018
|
+
paddingBottom: "var(--glyph-spacing-sm, 0.5rem)"
|
|
6019
|
+
};
|
|
6020
|
+
var formStyle = {
|
|
6021
|
+
padding: "var(--glyph-spacing-md, 1rem)"
|
|
6022
|
+
};
|
|
6023
|
+
var fieldStyle = {
|
|
6024
|
+
marginBottom: "var(--glyph-spacing-md, 1rem)"
|
|
6025
|
+
};
|
|
6026
|
+
var labelStyle3 = {
|
|
6027
|
+
display: "block",
|
|
6028
|
+
fontWeight: 600,
|
|
6029
|
+
fontSize: "0.875rem",
|
|
6030
|
+
marginBottom: "0.375rem"
|
|
6031
|
+
};
|
|
6032
|
+
var requiredStyle = {
|
|
6033
|
+
color: "var(--glyph-form-error, #dc2626)",
|
|
6034
|
+
marginLeft: "0.25rem"
|
|
6035
|
+
};
|
|
6036
|
+
var textInputStyle = {
|
|
6037
|
+
width: "100%",
|
|
6038
|
+
padding: "0.5rem 0.75rem",
|
|
6039
|
+
border: "1px solid var(--glyph-border, #d0d8e4)",
|
|
6040
|
+
borderRadius: "var(--glyph-radius-sm, 0.375rem)",
|
|
6041
|
+
background: "transparent",
|
|
6042
|
+
color: "var(--glyph-text, #1a2035)",
|
|
6043
|
+
fontSize: "0.875rem",
|
|
6044
|
+
fontFamily: "inherit",
|
|
6045
|
+
boxSizing: "border-box"
|
|
6046
|
+
};
|
|
6047
|
+
var selectInputStyle = {
|
|
6048
|
+
...textInputStyle,
|
|
6049
|
+
appearance: "auto"
|
|
6050
|
+
};
|
|
6051
|
+
var checkboxLabelStyle = {
|
|
6052
|
+
display: "flex",
|
|
6053
|
+
alignItems: "center",
|
|
6054
|
+
gap: "0.5rem",
|
|
6055
|
+
fontSize: "0.875rem",
|
|
6056
|
+
cursor: "pointer"
|
|
6057
|
+
};
|
|
6058
|
+
var rangeValueStyle = {
|
|
6059
|
+
fontSize: "0.875rem",
|
|
6060
|
+
fontWeight: 600,
|
|
6061
|
+
color: "var(--glyph-accent, #0a9d7c)",
|
|
6062
|
+
marginLeft: "0.5rem",
|
|
6063
|
+
fontVariantNumeric: "tabular-nums"
|
|
6064
|
+
};
|
|
6065
|
+
var submitButtonStyle = {
|
|
6066
|
+
padding: "0.625rem 1.5rem",
|
|
6067
|
+
borderRadius: "var(--glyph-radius-md, 0.5rem)",
|
|
6068
|
+
border: "1px solid var(--glyph-accent, #0a9d7c)",
|
|
6069
|
+
background: "var(--glyph-accent, #0a9d7c)",
|
|
6070
|
+
color: "#fff",
|
|
6071
|
+
cursor: "pointer",
|
|
6072
|
+
fontWeight: 600,
|
|
6073
|
+
fontSize: "0.875rem",
|
|
6074
|
+
marginTop: "var(--glyph-spacing-sm, 0.5rem)"
|
|
6075
|
+
};
|
|
6076
|
+
function invalidStyle(isInvalid) {
|
|
6077
|
+
if (!isInvalid) return {};
|
|
6078
|
+
return {
|
|
6079
|
+
borderColor: "var(--glyph-form-error, #dc2626)"
|
|
6080
|
+
};
|
|
6081
|
+
}
|
|
6082
|
+
function renderField({
|
|
6083
|
+
field,
|
|
6084
|
+
baseId,
|
|
6085
|
+
values,
|
|
6086
|
+
validation,
|
|
6087
|
+
submitted,
|
|
6088
|
+
updateValue
|
|
6089
|
+
}) {
|
|
6090
|
+
const isInvalid = validation[field.id] === true;
|
|
6091
|
+
const fieldId = `${baseId}-${field.id}`;
|
|
6092
|
+
switch (field.type) {
|
|
6093
|
+
case "text":
|
|
6094
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: fieldStyle, children: [
|
|
6095
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { htmlFor: fieldId, style: labelStyle3, children: [
|
|
6096
|
+
field.label,
|
|
6097
|
+
field.required && /* @__PURE__ */ jsxRuntime.jsx("span", { style: requiredStyle, "aria-hidden": "true", children: "*" })
|
|
6098
|
+
] }),
|
|
6099
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6100
|
+
"input",
|
|
6101
|
+
{
|
|
6102
|
+
id: fieldId,
|
|
6103
|
+
type: "text",
|
|
6104
|
+
value: values[field.id] ?? "",
|
|
6105
|
+
onChange: (e) => updateValue(field.id, e.target.value),
|
|
6106
|
+
placeholder: field.placeholder,
|
|
6107
|
+
disabled: submitted,
|
|
6108
|
+
"aria-required": field.required,
|
|
6109
|
+
"aria-invalid": isInvalid,
|
|
6110
|
+
style: { ...textInputStyle, ...invalidStyle(isInvalid) }
|
|
6111
|
+
}
|
|
6112
|
+
)
|
|
6113
|
+
] }, field.id);
|
|
6114
|
+
case "textarea":
|
|
6115
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: fieldStyle, children: [
|
|
6116
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { htmlFor: fieldId, style: labelStyle3, children: [
|
|
6117
|
+
field.label,
|
|
6118
|
+
field.required && /* @__PURE__ */ jsxRuntime.jsx("span", { style: requiredStyle, "aria-hidden": "true", children: "*" })
|
|
6119
|
+
] }),
|
|
6120
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6121
|
+
"textarea",
|
|
6122
|
+
{
|
|
6123
|
+
id: fieldId,
|
|
6124
|
+
value: values[field.id] ?? "",
|
|
6125
|
+
onChange: (e) => updateValue(field.id, e.target.value),
|
|
6126
|
+
placeholder: field.placeholder,
|
|
6127
|
+
rows: field.rows ?? 4,
|
|
6128
|
+
disabled: submitted,
|
|
6129
|
+
"aria-required": field.required,
|
|
6130
|
+
"aria-invalid": isInvalid,
|
|
6131
|
+
style: {
|
|
6132
|
+
...textInputStyle,
|
|
6133
|
+
...invalidStyle(isInvalid),
|
|
6134
|
+
resize: "vertical",
|
|
6135
|
+
fontFamily: "inherit"
|
|
6136
|
+
}
|
|
6137
|
+
}
|
|
6138
|
+
)
|
|
6139
|
+
] }, field.id);
|
|
6140
|
+
case "select":
|
|
6141
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: fieldStyle, children: [
|
|
6142
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { htmlFor: fieldId, style: labelStyle3, children: [
|
|
6143
|
+
field.label,
|
|
6144
|
+
field.required && /* @__PURE__ */ jsxRuntime.jsx("span", { style: requiredStyle, "aria-hidden": "true", children: "*" })
|
|
6145
|
+
] }),
|
|
6146
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6147
|
+
"select",
|
|
6148
|
+
{
|
|
6149
|
+
id: fieldId,
|
|
6150
|
+
value: values[field.id] ?? "",
|
|
6151
|
+
onChange: (e) => updateValue(field.id, e.target.value),
|
|
6152
|
+
disabled: submitted,
|
|
6153
|
+
"aria-required": field.required,
|
|
6154
|
+
style: selectInputStyle,
|
|
6155
|
+
children: field.options.map((opt) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: opt, children: opt }, opt))
|
|
6156
|
+
}
|
|
6157
|
+
)
|
|
6158
|
+
] }, field.id);
|
|
6159
|
+
case "checkbox":
|
|
6160
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: fieldStyle, children: /* @__PURE__ */ jsxRuntime.jsxs("label", { style: checkboxLabelStyle, children: [
|
|
6161
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6162
|
+
"input",
|
|
6163
|
+
{
|
|
6164
|
+
id: fieldId,
|
|
6165
|
+
type: "checkbox",
|
|
6166
|
+
checked: values[field.id] ?? false,
|
|
6167
|
+
onChange: (e) => updateValue(field.id, e.target.checked),
|
|
6168
|
+
disabled: submitted
|
|
6169
|
+
}
|
|
6170
|
+
),
|
|
6171
|
+
field.label
|
|
6172
|
+
] }) }, field.id);
|
|
6173
|
+
case "range": {
|
|
6174
|
+
const min2 = field.min ?? 0;
|
|
6175
|
+
const max2 = field.max ?? 100;
|
|
6176
|
+
const step = field.step ?? 1;
|
|
6177
|
+
const currentValue = values[field.id] ?? min2;
|
|
6178
|
+
const displayValue = field.unit ? `${String(currentValue)}${field.unit}` : String(currentValue);
|
|
6179
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: fieldStyle, children: [
|
|
6180
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { htmlFor: fieldId, style: labelStyle3, children: [
|
|
6181
|
+
field.label,
|
|
6182
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: rangeValueStyle, children: displayValue })
|
|
6183
|
+
] }),
|
|
6184
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6185
|
+
"input",
|
|
6186
|
+
{
|
|
6187
|
+
id: fieldId,
|
|
6188
|
+
type: "range",
|
|
6189
|
+
min: min2,
|
|
6190
|
+
max: max2,
|
|
6191
|
+
step,
|
|
6192
|
+
value: currentValue,
|
|
6193
|
+
onChange: (e) => updateValue(field.id, Number(e.target.value)),
|
|
6194
|
+
disabled: submitted,
|
|
6195
|
+
"aria-valuemin": min2,
|
|
6196
|
+
"aria-valuemax": max2,
|
|
6197
|
+
"aria-valuenow": currentValue,
|
|
6198
|
+
"aria-valuetext": displayValue,
|
|
6199
|
+
style: { width: "100%", accentColor: "var(--glyph-accent, #0a9d7c)" }
|
|
6200
|
+
}
|
|
6201
|
+
)
|
|
6202
|
+
] }, field.id);
|
|
6203
|
+
}
|
|
6204
|
+
}
|
|
6205
|
+
}
|
|
6206
|
+
function Form({ data, block, onInteraction }) {
|
|
6207
|
+
const { title, description, submitLabel = "Submit", fields } = data;
|
|
6208
|
+
const baseId = `glyph-form-${block.id}`;
|
|
6209
|
+
const [values, setValues] = react.useState(() => {
|
|
6210
|
+
const init = {};
|
|
6211
|
+
for (const field of fields) {
|
|
6212
|
+
switch (field.type) {
|
|
6213
|
+
case "text":
|
|
6214
|
+
case "textarea":
|
|
6215
|
+
init[field.id] = field.default ?? "";
|
|
6216
|
+
break;
|
|
6217
|
+
case "select":
|
|
6218
|
+
init[field.id] = field.default ?? field.options[0] ?? "";
|
|
6219
|
+
break;
|
|
6220
|
+
case "checkbox":
|
|
6221
|
+
init[field.id] = field.default ?? false;
|
|
6222
|
+
break;
|
|
6223
|
+
case "range":
|
|
6224
|
+
init[field.id] = field.default ?? field.min ?? 0;
|
|
6225
|
+
break;
|
|
6226
|
+
}
|
|
6227
|
+
}
|
|
6228
|
+
return init;
|
|
6229
|
+
});
|
|
6230
|
+
const [submitted, setSubmitted] = react.useState(false);
|
|
6231
|
+
const [validation, setValidation] = react.useState({});
|
|
6232
|
+
const updateValue = (fieldId, value) => {
|
|
6233
|
+
setValues((prev) => ({ ...prev, [fieldId]: value }));
|
|
6234
|
+
if (validation[fieldId]) {
|
|
6235
|
+
setValidation((prev) => ({ ...prev, [fieldId]: false }));
|
|
6236
|
+
}
|
|
6237
|
+
};
|
|
6238
|
+
const handleSubmit = (e) => {
|
|
6239
|
+
e.preventDefault();
|
|
6240
|
+
const errors = {};
|
|
6241
|
+
for (const field of fields) {
|
|
6242
|
+
if ("required" in field && field.required) {
|
|
6243
|
+
const val = values[field.id];
|
|
6244
|
+
if (val === "" || val === void 0) {
|
|
6245
|
+
errors[field.id] = true;
|
|
6246
|
+
}
|
|
6247
|
+
}
|
|
6248
|
+
}
|
|
6249
|
+
if (Object.keys(errors).length > 0) {
|
|
6250
|
+
setValidation(errors);
|
|
6251
|
+
return;
|
|
6252
|
+
}
|
|
6253
|
+
setSubmitted(true);
|
|
6254
|
+
if (onInteraction) {
|
|
6255
|
+
onInteraction({
|
|
6256
|
+
kind: "form-submit",
|
|
6257
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6258
|
+
blockId: block.id,
|
|
6259
|
+
blockType: block.type,
|
|
6260
|
+
payload: {
|
|
6261
|
+
values: { ...values },
|
|
6262
|
+
fields: fields.map((f) => ({
|
|
6263
|
+
id: f.id,
|
|
6264
|
+
label: f.label,
|
|
6265
|
+
type: f.type,
|
|
6266
|
+
value: values[f.id] !== void 0 ? values[f.id] : ""
|
|
6267
|
+
}))
|
|
6268
|
+
}
|
|
6269
|
+
});
|
|
6270
|
+
}
|
|
6271
|
+
};
|
|
6272
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { id: baseId, role: "region", "aria-label": title ?? "Form", style: containerStyle8, children: [
|
|
6273
|
+
title && /* @__PURE__ */ jsxRuntime.jsx("div", { style: headerStyle7, children: title }),
|
|
6274
|
+
description && /* @__PURE__ */ jsxRuntime.jsx("div", { style: descriptionStyle, children: description }),
|
|
6275
|
+
/* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, style: formStyle, noValidate: true, children: [
|
|
6276
|
+
fields.map(
|
|
6277
|
+
(field) => renderField({ field, baseId, values, validation, submitted, updateValue })
|
|
6278
|
+
),
|
|
6279
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6280
|
+
"button",
|
|
6281
|
+
{
|
|
6282
|
+
type: "submit",
|
|
6283
|
+
disabled: submitted,
|
|
6284
|
+
style: {
|
|
6285
|
+
...submitButtonStyle,
|
|
6286
|
+
opacity: submitted ? 0.5 : 1,
|
|
6287
|
+
cursor: submitted ? "default" : "pointer"
|
|
6288
|
+
},
|
|
6289
|
+
children: submitted ? "Submitted" : submitLabel
|
|
6290
|
+
}
|
|
6291
|
+
)
|
|
6292
|
+
] })
|
|
6293
|
+
] });
|
|
6294
|
+
}
|
|
6295
|
+
|
|
6296
|
+
// src/form/index.ts
|
|
6297
|
+
var formDefinition = {
|
|
6298
|
+
type: "ui:form",
|
|
6299
|
+
schema: schemas.formSchema,
|
|
6300
|
+
render: Form
|
|
6301
|
+
};
|
|
6302
|
+
|
|
6303
|
+
// src/kanban/styles.ts
|
|
6304
|
+
var containerStyle9 = {
|
|
6305
|
+
fontFamily: "var(--glyph-font-body, system-ui, sans-serif)",
|
|
6306
|
+
color: "var(--glyph-text, #1a2035)",
|
|
6307
|
+
border: "1px solid var(--glyph-border, #d0d8e4)",
|
|
6308
|
+
borderRadius: "var(--glyph-radius-md, 0.5rem)",
|
|
6309
|
+
overflow: "hidden"
|
|
6310
|
+
};
|
|
6311
|
+
var headerStyle8 = {
|
|
6312
|
+
fontWeight: 700,
|
|
6313
|
+
fontSize: "1.125rem",
|
|
6314
|
+
padding: "var(--glyph-spacing-md, 1rem)",
|
|
6315
|
+
borderBottom: "1px solid var(--glyph-border, #d0d8e4)",
|
|
6316
|
+
color: "var(--glyph-heading, #0a0e1a)"
|
|
6317
|
+
};
|
|
6318
|
+
var boardStyle = {
|
|
6319
|
+
display: "flex",
|
|
6320
|
+
gap: "var(--glyph-spacing-sm, 0.5rem)",
|
|
6321
|
+
padding: "var(--glyph-spacing-md, 1rem)",
|
|
6322
|
+
overflowX: "auto",
|
|
6323
|
+
minHeight: "200px"
|
|
6324
|
+
};
|
|
6325
|
+
function columnStyle(isOver) {
|
|
6326
|
+
return {
|
|
6327
|
+
flex: "1 1 0",
|
|
6328
|
+
minWidth: "180px",
|
|
6329
|
+
background: isOver ? "var(--glyph-accent-subtle, #e6f6f2)" : "var(--glyph-kanban-column-bg, var(--glyph-surface, #e8ecf3))",
|
|
6330
|
+
borderRadius: "var(--glyph-radius-md, 0.5rem)",
|
|
6331
|
+
padding: "var(--glyph-spacing-sm, 0.5rem)",
|
|
6332
|
+
display: "flex",
|
|
6333
|
+
flexDirection: "column",
|
|
6334
|
+
transition: "background 0.15s ease"
|
|
6335
|
+
};
|
|
6336
|
+
}
|
|
6337
|
+
var columnHeaderStyle = {
|
|
6338
|
+
fontWeight: 700,
|
|
6339
|
+
fontSize: "0.8125rem",
|
|
6340
|
+
textTransform: "uppercase",
|
|
6341
|
+
letterSpacing: "0.5px",
|
|
6342
|
+
padding: "0.375rem 0.5rem",
|
|
6343
|
+
marginBottom: "0.375rem",
|
|
6344
|
+
display: "flex",
|
|
6345
|
+
justifyContent: "space-between",
|
|
6346
|
+
alignItems: "center"
|
|
6347
|
+
};
|
|
6348
|
+
var columnCountStyle = {
|
|
6349
|
+
fontSize: "0.6875rem",
|
|
6350
|
+
fontWeight: 400,
|
|
6351
|
+
color: "var(--glyph-text-muted, #6b7a94)"
|
|
6352
|
+
};
|
|
6353
|
+
function cardStyle(isGrabbed, priority) {
|
|
6354
|
+
const priorityColors = {
|
|
6355
|
+
high: "var(--glyph-kanban-priority-high, #dc2626)",
|
|
6356
|
+
medium: "var(--glyph-kanban-priority-medium, #f59e0b)",
|
|
6357
|
+
low: "var(--glyph-kanban-priority-low, #22c55e)"
|
|
6358
|
+
};
|
|
6359
|
+
return {
|
|
6360
|
+
background: "var(--glyph-kanban-card-bg, var(--glyph-surface-raised, #f4f6fa))",
|
|
6361
|
+
border: `1px solid var(--glyph-kanban-card-border, var(--glyph-border, #d0d8e4))`,
|
|
6362
|
+
borderRadius: "var(--glyph-radius-sm, 0.375rem)",
|
|
6363
|
+
padding: "0.625rem 0.75rem",
|
|
6364
|
+
marginBottom: "0.375rem",
|
|
6365
|
+
cursor: isGrabbed ? "grabbing" : "grab",
|
|
6366
|
+
userSelect: "none",
|
|
6367
|
+
boxShadow: isGrabbed ? "var(--glyph-kanban-drag-shadow, var(--glyph-shadow-md, 0 4px 12px rgba(0,0,0,0.15)))" : "none",
|
|
6368
|
+
borderLeft: priority && priorityColors[priority] ? `3px solid ${priorityColors[priority]}` : void 0,
|
|
6369
|
+
outline: isGrabbed ? "2px solid var(--glyph-accent, #0a9d7c)" : "none",
|
|
6370
|
+
outlineOffset: "-2px"
|
|
6371
|
+
};
|
|
6372
|
+
}
|
|
6373
|
+
var cardTitleStyle = {
|
|
6374
|
+
fontWeight: 600,
|
|
6375
|
+
fontSize: "0.875rem",
|
|
6376
|
+
marginBottom: "0.25rem"
|
|
6377
|
+
};
|
|
6378
|
+
var cardDescStyle = {
|
|
6379
|
+
fontSize: "0.75rem",
|
|
6380
|
+
color: "var(--glyph-text-muted, #6b7a94)",
|
|
6381
|
+
lineHeight: 1.4
|
|
6382
|
+
};
|
|
6383
|
+
var tagContainerStyle = {
|
|
6384
|
+
display: "flex",
|
|
6385
|
+
flexWrap: "wrap",
|
|
6386
|
+
gap: "0.25rem",
|
|
6387
|
+
marginTop: "0.375rem"
|
|
6388
|
+
};
|
|
6389
|
+
var tagStyle = {
|
|
6390
|
+
fontSize: "0.625rem",
|
|
6391
|
+
fontWeight: 600,
|
|
6392
|
+
padding: "0.125rem 0.375rem",
|
|
6393
|
+
borderRadius: "9999px",
|
|
6394
|
+
background: "var(--glyph-accent-subtle, #e6f6f2)",
|
|
6395
|
+
color: "var(--glyph-accent, #0a9d7c)"
|
|
6396
|
+
};
|
|
6397
|
+
var limitStyle = {
|
|
6398
|
+
fontSize: "0.625rem",
|
|
6399
|
+
color: "var(--glyph-text-muted, #6b7a94)"
|
|
6400
|
+
};
|
|
6401
|
+
function Kanban({
|
|
6402
|
+
data,
|
|
6403
|
+
block,
|
|
6404
|
+
onInteraction
|
|
6405
|
+
}) {
|
|
6406
|
+
const { title } = data;
|
|
6407
|
+
const baseId = `glyph-kanban-${block.id}`;
|
|
6408
|
+
const [columns, setColumns] = react.useState(
|
|
6409
|
+
() => data.columns.map((col) => ({ ...col, cards: [...col.cards] }))
|
|
6410
|
+
);
|
|
6411
|
+
const [grabbed, setGrabbed] = react.useState(null);
|
|
6412
|
+
const moveCard = (cardId, sourceColId, destColId, destIndex) => {
|
|
6413
|
+
const newColumns = columns.map((col) => ({
|
|
6414
|
+
...col,
|
|
6415
|
+
cards: [...col.cards]
|
|
6416
|
+
}));
|
|
6417
|
+
const sourceCol = newColumns.find((c) => c.id === sourceColId);
|
|
6418
|
+
const destCol = newColumns.find((c) => c.id === destColId);
|
|
6419
|
+
if (!sourceCol || !destCol) return;
|
|
6420
|
+
const cardIndex = sourceCol.cards.findIndex((c) => c.id === cardId);
|
|
6421
|
+
if (cardIndex === -1) return;
|
|
6422
|
+
const [card] = sourceCol.cards.splice(cardIndex, 1);
|
|
6423
|
+
if (!card) return;
|
|
6424
|
+
destCol.cards.splice(destIndex, 0, card);
|
|
6425
|
+
setColumns(newColumns);
|
|
6426
|
+
if (onInteraction) {
|
|
6427
|
+
onInteraction({
|
|
6428
|
+
kind: "kanban-move",
|
|
6429
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6430
|
+
blockId: block.id,
|
|
6431
|
+
blockType: block.type,
|
|
6432
|
+
payload: {
|
|
6433
|
+
cardId: card.id,
|
|
6434
|
+
cardTitle: card.title,
|
|
6435
|
+
sourceColumnId: sourceColId,
|
|
6436
|
+
sourceColumnTitle: sourceCol.title,
|
|
6437
|
+
destinationColumnId: destColId,
|
|
6438
|
+
destinationColumnTitle: destCol.title,
|
|
6439
|
+
position: destIndex,
|
|
6440
|
+
allColumns: newColumns.map((c) => ({
|
|
6441
|
+
id: c.id,
|
|
6442
|
+
title: c.title,
|
|
6443
|
+
cardIds: c.cards.map((card2) => card2.id)
|
|
6444
|
+
}))
|
|
6445
|
+
}
|
|
6446
|
+
});
|
|
6447
|
+
}
|
|
6448
|
+
};
|
|
6449
|
+
const handleCardKeyDown = (e, cardId, columnId, cardIndex) => {
|
|
6450
|
+
if (e.key === " " || e.key === "Enter") {
|
|
6451
|
+
e.preventDefault();
|
|
6452
|
+
if (grabbed === null) {
|
|
6453
|
+
setGrabbed({ cardId, columnId, cardIndex });
|
|
6454
|
+
} else {
|
|
6455
|
+
setGrabbed(null);
|
|
6456
|
+
}
|
|
6457
|
+
} else if (e.key === "Escape") {
|
|
6458
|
+
setGrabbed(null);
|
|
6459
|
+
} else if (grabbed && grabbed.cardId === cardId) {
|
|
6460
|
+
const colIndex = columns.findIndex((c) => c.id === grabbed.columnId);
|
|
6461
|
+
const col = columns[colIndex];
|
|
6462
|
+
if (!col) return;
|
|
6463
|
+
if (e.key === "ArrowUp") {
|
|
6464
|
+
e.preventDefault();
|
|
6465
|
+
if (grabbed.cardIndex > 0) {
|
|
6466
|
+
moveCard(cardId, grabbed.columnId, grabbed.columnId, grabbed.cardIndex - 1);
|
|
6467
|
+
setGrabbed({ ...grabbed, cardIndex: grabbed.cardIndex - 1 });
|
|
6468
|
+
}
|
|
6469
|
+
} else if (e.key === "ArrowDown") {
|
|
6470
|
+
e.preventDefault();
|
|
6471
|
+
if (grabbed.cardIndex < col.cards.length - 1) {
|
|
6472
|
+
moveCard(cardId, grabbed.columnId, grabbed.columnId, grabbed.cardIndex + 1);
|
|
6473
|
+
setGrabbed({ ...grabbed, cardIndex: grabbed.cardIndex + 1 });
|
|
6474
|
+
}
|
|
6475
|
+
} else if (e.key === "ArrowLeft") {
|
|
6476
|
+
e.preventDefault();
|
|
6477
|
+
if (colIndex > 0) {
|
|
6478
|
+
const prevCol = columns[colIndex - 1];
|
|
6479
|
+
if (!prevCol) return;
|
|
6480
|
+
const newIndex = prevCol.cards.length;
|
|
6481
|
+
moveCard(cardId, grabbed.columnId, prevCol.id, newIndex);
|
|
6482
|
+
setGrabbed({ cardId, columnId: prevCol.id, cardIndex: newIndex });
|
|
6483
|
+
}
|
|
6484
|
+
} else if (e.key === "ArrowRight") {
|
|
6485
|
+
e.preventDefault();
|
|
6486
|
+
if (colIndex < columns.length - 1) {
|
|
6487
|
+
const nextCol = columns[colIndex + 1];
|
|
6488
|
+
if (!nextCol) return;
|
|
6489
|
+
const newIndex = nextCol.cards.length;
|
|
6490
|
+
moveCard(cardId, grabbed.columnId, nextCol.id, newIndex);
|
|
6491
|
+
setGrabbed({ cardId, columnId: nextCol.id, cardIndex: newIndex });
|
|
6492
|
+
}
|
|
6493
|
+
}
|
|
6494
|
+
}
|
|
6495
|
+
};
|
|
6496
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { id: baseId, role: "region", "aria-label": title ?? "Kanban Board", style: containerStyle9, children: [
|
|
6497
|
+
title && /* @__PURE__ */ jsxRuntime.jsx("div", { style: headerStyle8, children: title }),
|
|
6498
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: boardStyle, children: columns.map((col) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: columnStyle(false), children: [
|
|
6499
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: columnHeaderStyle, children: [
|
|
6500
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: col.title }),
|
|
6501
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: columnCountStyle, children: [
|
|
6502
|
+
String(col.cards.length),
|
|
6503
|
+
col.limit !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { style: limitStyle, children: [
|
|
6504
|
+
" / ",
|
|
6505
|
+
String(col.limit)
|
|
6506
|
+
] })
|
|
6507
|
+
] })
|
|
6508
|
+
] }),
|
|
6509
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { role: "list", "aria-label": col.title, children: col.cards.map((card, cardIndex) => {
|
|
6510
|
+
const isGrabbed = grabbed !== null && grabbed.cardId === card.id;
|
|
6511
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6512
|
+
"div",
|
|
6513
|
+
{
|
|
6514
|
+
role: "listitem",
|
|
6515
|
+
"aria-grabbed": isGrabbed,
|
|
6516
|
+
"aria-label": `${card.title}${card.priority ? `, ${card.priority} priority` : ""}`,
|
|
6517
|
+
tabIndex: 0,
|
|
6518
|
+
style: cardStyle(isGrabbed, card.priority),
|
|
6519
|
+
onKeyDown: (e) => handleCardKeyDown(e, card.id, col.id, cardIndex),
|
|
6520
|
+
children: [
|
|
6521
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: cardTitleStyle, children: card.title }),
|
|
6522
|
+
card.description && /* @__PURE__ */ jsxRuntime.jsx("div", { style: cardDescStyle, children: card.description }),
|
|
6523
|
+
card.tags && card.tags.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: tagContainerStyle, children: card.tags.map((tag) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: tagStyle, children: tag }, tag)) })
|
|
6524
|
+
]
|
|
6525
|
+
},
|
|
6526
|
+
card.id
|
|
6527
|
+
);
|
|
6528
|
+
}) })
|
|
6529
|
+
] }, col.id)) }),
|
|
6530
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6531
|
+
"div",
|
|
6532
|
+
{
|
|
6533
|
+
"aria-live": "assertive",
|
|
6534
|
+
style: {
|
|
6535
|
+
position: "absolute",
|
|
6536
|
+
width: "1px",
|
|
6537
|
+
height: "1px",
|
|
6538
|
+
padding: 0,
|
|
6539
|
+
margin: "-1px",
|
|
6540
|
+
overflow: "hidden",
|
|
6541
|
+
clip: "rect(0,0,0,0)",
|
|
6542
|
+
whiteSpace: "nowrap",
|
|
6543
|
+
border: 0
|
|
6544
|
+
},
|
|
6545
|
+
children: grabbed !== null && `${columns.find((c) => c.id === grabbed.columnId)?.cards[grabbed.cardIndex]?.title ?? "Card"} grabbed in ${columns.find((c) => c.id === grabbed.columnId)?.title ?? "column"}. Use arrow keys to move.`
|
|
6546
|
+
}
|
|
6547
|
+
)
|
|
6548
|
+
] });
|
|
6549
|
+
}
|
|
6550
|
+
|
|
6551
|
+
// src/kanban/index.ts
|
|
6552
|
+
var kanbanDefinition = {
|
|
6553
|
+
type: "ui:kanban",
|
|
6554
|
+
schema: schemas.kanbanSchema,
|
|
6555
|
+
render: Kanban
|
|
6556
|
+
};
|
|
6557
|
+
|
|
6558
|
+
// src/annotate/styles.ts
|
|
6559
|
+
var containerStyle10 = {
|
|
6560
|
+
fontFamily: "var(--glyph-font-body, system-ui, sans-serif)",
|
|
6561
|
+
color: "var(--glyph-text, #1a2035)",
|
|
6562
|
+
border: "1px solid var(--glyph-border, #d0d8e4)",
|
|
6563
|
+
borderRadius: "var(--glyph-radius-md, 0.5rem)",
|
|
6564
|
+
overflow: "hidden"
|
|
6565
|
+
};
|
|
6566
|
+
var headerStyle9 = {
|
|
6567
|
+
fontWeight: 700,
|
|
6568
|
+
fontSize: "1.125rem",
|
|
6569
|
+
padding: "var(--glyph-spacing-md, 1rem)",
|
|
6570
|
+
borderBottom: "1px solid var(--glyph-border, #d0d8e4)",
|
|
6571
|
+
color: "var(--glyph-heading, #0a0e1a)"
|
|
6572
|
+
};
|
|
6573
|
+
var bodyStyle2 = {
|
|
6574
|
+
display: "flex",
|
|
6575
|
+
minHeight: "200px"
|
|
6576
|
+
};
|
|
6577
|
+
var textPaneStyle = {
|
|
6578
|
+
flex: 1,
|
|
6579
|
+
padding: "var(--glyph-spacing-md, 1rem)",
|
|
6580
|
+
fontFamily: "var(--glyph-font-mono, ui-monospace, monospace)",
|
|
6581
|
+
fontSize: "0.8125rem",
|
|
6582
|
+
lineHeight: 1.8,
|
|
6583
|
+
whiteSpace: "pre-wrap",
|
|
6584
|
+
wordBreak: "break-word",
|
|
6585
|
+
position: "relative",
|
|
6586
|
+
cursor: "text"
|
|
6587
|
+
};
|
|
6588
|
+
var labelPickerStyle = {
|
|
6589
|
+
position: "absolute",
|
|
6590
|
+
zIndex: 10,
|
|
6591
|
+
background: "var(--glyph-surface-raised, #f4f6fa)",
|
|
6592
|
+
border: "1px solid var(--glyph-border, #d0d8e4)",
|
|
6593
|
+
borderRadius: "var(--glyph-radius-sm, 0.375rem)",
|
|
6594
|
+
boxShadow: "var(--glyph-shadow-md, 0 4px 12px rgba(0,0,0,0.15))",
|
|
6595
|
+
padding: "0.25rem 0",
|
|
6596
|
+
minWidth: "120px"
|
|
6597
|
+
};
|
|
6598
|
+
function labelOptionStyle() {
|
|
6599
|
+
return {
|
|
6600
|
+
display: "flex",
|
|
6601
|
+
alignItems: "center",
|
|
6602
|
+
gap: "0.5rem",
|
|
6603
|
+
padding: "0.375rem 0.75rem",
|
|
6604
|
+
cursor: "pointer",
|
|
6605
|
+
fontSize: "0.8125rem",
|
|
6606
|
+
background: "transparent",
|
|
6607
|
+
border: "none",
|
|
6608
|
+
width: "100%",
|
|
6609
|
+
textAlign: "left",
|
|
6610
|
+
color: "var(--glyph-text, #1a2035)"
|
|
6611
|
+
};
|
|
6612
|
+
}
|
|
6613
|
+
function colorDotStyle(color3) {
|
|
6614
|
+
return {
|
|
6615
|
+
width: "0.625rem",
|
|
6616
|
+
height: "0.625rem",
|
|
6617
|
+
borderRadius: "50%",
|
|
6618
|
+
background: color3,
|
|
6619
|
+
flexShrink: 0
|
|
6620
|
+
};
|
|
6621
|
+
}
|
|
6622
|
+
var sidebarStyle = {
|
|
6623
|
+
width: "220px",
|
|
6624
|
+
borderLeft: "1px solid var(--glyph-border, #d0d8e4)",
|
|
6625
|
+
background: "var(--glyph-annotate-sidebar-bg, var(--glyph-surface, #e8ecf3))",
|
|
6626
|
+
overflow: "auto"
|
|
6627
|
+
};
|
|
6628
|
+
var sidebarHeaderStyle = {
|
|
6629
|
+
fontWeight: 700,
|
|
6630
|
+
fontSize: "0.75rem",
|
|
6631
|
+
textTransform: "uppercase",
|
|
6632
|
+
letterSpacing: "0.5px",
|
|
6633
|
+
padding: "0.75rem",
|
|
6634
|
+
borderBottom: "1px solid var(--glyph-border, #d0d8e4)",
|
|
6635
|
+
color: "var(--glyph-text-muted, #6b7a94)"
|
|
6636
|
+
};
|
|
6637
|
+
function annotationItemStyle(color3) {
|
|
6638
|
+
return {
|
|
6639
|
+
padding: "0.5rem 0.75rem",
|
|
6640
|
+
borderBottom: "1px solid var(--glyph-border, #d0d8e4)",
|
|
6641
|
+
borderLeft: `3px solid ${color3}`,
|
|
6642
|
+
fontSize: "0.75rem"
|
|
6643
|
+
};
|
|
6644
|
+
}
|
|
6645
|
+
var annotationTextStyle = {
|
|
6646
|
+
fontFamily: "var(--glyph-font-mono, ui-monospace, monospace)",
|
|
6647
|
+
fontSize: "0.6875rem",
|
|
6648
|
+
color: "var(--glyph-text-muted, #6b7a94)",
|
|
6649
|
+
marginTop: "0.25rem",
|
|
6650
|
+
overflow: "hidden",
|
|
6651
|
+
textOverflow: "ellipsis",
|
|
6652
|
+
whiteSpace: "nowrap"
|
|
6653
|
+
};
|
|
6654
|
+
var annotationNoteStyle = {
|
|
6655
|
+
fontSize: "0.6875rem",
|
|
6656
|
+
fontStyle: "italic",
|
|
6657
|
+
color: "var(--glyph-text-muted, #6b7a94)",
|
|
6658
|
+
marginTop: "0.125rem"
|
|
6659
|
+
};
|
|
6660
|
+
function computeSegments(text, annotations) {
|
|
6661
|
+
if (annotations.length === 0) {
|
|
6662
|
+
return [{ text, start: 0, annotation: null }];
|
|
6663
|
+
}
|
|
6664
|
+
const sorted = [...annotations].sort((a, b) => a.start - b.start);
|
|
6665
|
+
const segments = [];
|
|
6666
|
+
let cursor = 0;
|
|
6667
|
+
for (const ann of sorted) {
|
|
6668
|
+
if (ann.start > cursor) {
|
|
6669
|
+
segments.push({ text: text.slice(cursor, ann.start), start: cursor, annotation: null });
|
|
6670
|
+
}
|
|
6671
|
+
segments.push({
|
|
6672
|
+
text: text.slice(ann.start, ann.end),
|
|
6673
|
+
start: ann.start,
|
|
6674
|
+
annotation: ann
|
|
6675
|
+
});
|
|
6676
|
+
cursor = ann.end;
|
|
6677
|
+
}
|
|
6678
|
+
if (cursor < text.length) {
|
|
6679
|
+
segments.push({ text: text.slice(cursor), start: cursor, annotation: null });
|
|
6680
|
+
}
|
|
6681
|
+
return segments;
|
|
6682
|
+
}
|
|
6683
|
+
function Annotate({
|
|
6684
|
+
data,
|
|
6685
|
+
block,
|
|
6686
|
+
onInteraction
|
|
6687
|
+
}) {
|
|
6688
|
+
const { title, labels, text } = data;
|
|
6689
|
+
const baseId = `glyph-annotate-${block.id}`;
|
|
6690
|
+
const [annotations, setAnnotations] = react.useState(data.annotations ?? []);
|
|
6691
|
+
const [pickerPos, setPickerPos] = react.useState(null);
|
|
6692
|
+
const [pendingSelection, setPendingSelection] = react.useState(null);
|
|
6693
|
+
const textRef = react.useRef(null);
|
|
6694
|
+
const handleMouseUp = react.useCallback(() => {
|
|
6695
|
+
const selection = window.getSelection();
|
|
6696
|
+
if (!selection || selection.isCollapsed || !textRef.current) return;
|
|
6697
|
+
const range = selection.getRangeAt(0);
|
|
6698
|
+
if (!range || !textRef.current.contains(range.commonAncestorContainer)) return;
|
|
6699
|
+
const selectedText = selection.toString();
|
|
6700
|
+
if (!selectedText.trim()) return;
|
|
6701
|
+
const preCaretRange = document.createRange();
|
|
6702
|
+
preCaretRange.selectNodeContents(textRef.current);
|
|
6703
|
+
preCaretRange.setEnd(range.startContainer, range.startOffset);
|
|
6704
|
+
const startOffset = preCaretRange.toString().length;
|
|
6705
|
+
const endOffset = startOffset + selectedText.length;
|
|
6706
|
+
const rect = range.getBoundingClientRect();
|
|
6707
|
+
const containerRect = textRef.current.getBoundingClientRect();
|
|
6708
|
+
setPendingSelection({ start: startOffset, end: endOffset, text: selectedText });
|
|
6709
|
+
setPickerPos({
|
|
6710
|
+
x: rect.left - containerRect.left,
|
|
6711
|
+
y: rect.bottom - containerRect.top + 4
|
|
6712
|
+
});
|
|
6713
|
+
}, []);
|
|
6714
|
+
const selectLabel = (labelName) => {
|
|
6715
|
+
if (!pendingSelection) return;
|
|
6716
|
+
const newAnnotation = {
|
|
6717
|
+
start: pendingSelection.start,
|
|
6718
|
+
end: pendingSelection.end,
|
|
6719
|
+
label: labelName
|
|
6720
|
+
};
|
|
6721
|
+
const newAnnotations = [...annotations, newAnnotation];
|
|
6722
|
+
setAnnotations(newAnnotations);
|
|
6723
|
+
setPickerPos(null);
|
|
6724
|
+
setPendingSelection(null);
|
|
6725
|
+
window.getSelection()?.removeAllRanges();
|
|
6726
|
+
if (onInteraction) {
|
|
6727
|
+
onInteraction({
|
|
6728
|
+
kind: "annotate-create",
|
|
6729
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6730
|
+
blockId: block.id,
|
|
6731
|
+
blockType: block.type,
|
|
6732
|
+
payload: {
|
|
6733
|
+
start: newAnnotation.start,
|
|
6734
|
+
end: newAnnotation.end,
|
|
6735
|
+
selectedText: pendingSelection.text,
|
|
6736
|
+
label: labelName,
|
|
6737
|
+
allAnnotations: newAnnotations.map((a) => ({
|
|
6738
|
+
start: a.start,
|
|
6739
|
+
end: a.end,
|
|
6740
|
+
text: text.slice(a.start, a.end),
|
|
6741
|
+
label: a.label
|
|
6742
|
+
}))
|
|
6743
|
+
}
|
|
6744
|
+
});
|
|
6745
|
+
}
|
|
6746
|
+
};
|
|
6747
|
+
const closePicker = (e) => {
|
|
6748
|
+
if (e.target.closest("[data-label-picker]")) return;
|
|
6749
|
+
setPickerPos(null);
|
|
6750
|
+
setPendingSelection(null);
|
|
6751
|
+
};
|
|
6752
|
+
const segments = computeSegments(text, annotations);
|
|
6753
|
+
const labelColorMap = new Map(labels.map((l) => [l.name, l.color]));
|
|
6754
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6755
|
+
"div",
|
|
6756
|
+
{
|
|
6757
|
+
id: baseId,
|
|
6758
|
+
role: "region",
|
|
6759
|
+
"aria-label": title ?? "Annotate",
|
|
6760
|
+
style: containerStyle10,
|
|
6761
|
+
onClick: closePicker,
|
|
6762
|
+
children: [
|
|
6763
|
+
title && /* @__PURE__ */ jsxRuntime.jsx("div", { style: headerStyle9, children: title }),
|
|
6764
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: bodyStyle2, children: [
|
|
6765
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { ref: textRef, role: "document", style: textPaneStyle, onMouseUp: handleMouseUp, children: [
|
|
6766
|
+
segments.map((seg, i) => {
|
|
6767
|
+
if (seg.annotation) {
|
|
6768
|
+
const color3 = labelColorMap.get(seg.annotation.label) ?? "#888";
|
|
6769
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
6770
|
+
"mark",
|
|
6771
|
+
{
|
|
6772
|
+
style: {
|
|
6773
|
+
backgroundColor: `${color3}33`,
|
|
6774
|
+
borderBottom: `2px solid ${color3}`,
|
|
6775
|
+
padding: "0 1px"
|
|
6776
|
+
},
|
|
6777
|
+
title: `${seg.annotation.label}${seg.annotation.note ? `: ${seg.annotation.note}` : ""}`,
|
|
6778
|
+
children: seg.text
|
|
6779
|
+
},
|
|
6780
|
+
i
|
|
6781
|
+
);
|
|
6782
|
+
}
|
|
6783
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { children: seg.text }, i);
|
|
6784
|
+
}),
|
|
6785
|
+
pickerPos && /* @__PURE__ */ jsxRuntime.jsx(
|
|
6786
|
+
"div",
|
|
6787
|
+
{
|
|
6788
|
+
role: "menu",
|
|
6789
|
+
"data-label-picker": true,
|
|
6790
|
+
style: {
|
|
6791
|
+
...labelPickerStyle,
|
|
6792
|
+
left: `${String(pickerPos.x)}px`,
|
|
6793
|
+
top: `${String(pickerPos.y)}px`
|
|
6794
|
+
},
|
|
6795
|
+
children: labels.map((label) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6796
|
+
"button",
|
|
6797
|
+
{
|
|
6798
|
+
role: "menuitem",
|
|
6799
|
+
style: labelOptionStyle(),
|
|
6800
|
+
onClick: (e) => {
|
|
6801
|
+
e.stopPropagation();
|
|
6802
|
+
selectLabel(label.name);
|
|
6803
|
+
},
|
|
6804
|
+
children: [
|
|
6805
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: colorDotStyle(label.color) }),
|
|
6806
|
+
label.name
|
|
6807
|
+
]
|
|
6808
|
+
},
|
|
6809
|
+
label.name
|
|
6810
|
+
))
|
|
6811
|
+
}
|
|
6812
|
+
)
|
|
6813
|
+
] }),
|
|
6814
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: sidebarStyle, role: "complementary", "aria-label": "Annotations", children: [
|
|
6815
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: sidebarHeaderStyle, children: [
|
|
6816
|
+
"Annotations (",
|
|
6817
|
+
String(annotations.length),
|
|
6818
|
+
")"
|
|
6819
|
+
] }),
|
|
6820
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { role: "list", children: [
|
|
6821
|
+
annotations.map((ann, i) => {
|
|
6822
|
+
const color3 = labelColorMap.get(ann.label) ?? "#888";
|
|
6823
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { role: "listitem", style: annotationItemStyle(color3), children: [
|
|
6824
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.375rem" }, children: [
|
|
6825
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: colorDotStyle(color3) }),
|
|
6826
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { style: { fontSize: "0.75rem" }, children: ann.label })
|
|
6827
|
+
] }),
|
|
6828
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: annotationTextStyle, children: text.slice(ann.start, ann.end) }),
|
|
6829
|
+
ann.note && /* @__PURE__ */ jsxRuntime.jsx("div", { style: annotationNoteStyle, children: ann.note })
|
|
6830
|
+
] }, i);
|
|
6831
|
+
}),
|
|
6832
|
+
annotations.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
6833
|
+
"div",
|
|
6834
|
+
{
|
|
6835
|
+
style: {
|
|
6836
|
+
padding: "0.75rem",
|
|
6837
|
+
fontSize: "0.75rem",
|
|
6838
|
+
color: "var(--glyph-text-muted, #6b7a94)"
|
|
6839
|
+
},
|
|
6840
|
+
children: "Select text to add annotations."
|
|
6841
|
+
}
|
|
6842
|
+
)
|
|
6843
|
+
] })
|
|
6844
|
+
] })
|
|
6845
|
+
] })
|
|
6846
|
+
]
|
|
6847
|
+
}
|
|
6848
|
+
);
|
|
6849
|
+
}
|
|
6850
|
+
|
|
6851
|
+
// src/annotate/index.ts
|
|
6852
|
+
var annotateDefinition = {
|
|
6853
|
+
type: "ui:annotate",
|
|
6854
|
+
schema: schemas.annotateSchema,
|
|
6855
|
+
render: Annotate
|
|
6856
|
+
};
|
|
6857
|
+
|
|
4878
6858
|
exports.Accordion = Accordion;
|
|
6859
|
+
exports.Annotate = Annotate;
|
|
4879
6860
|
exports.Architecture = Architecture;
|
|
4880
6861
|
exports.Callout = Callout;
|
|
4881
6862
|
exports.Card = Card;
|
|
@@ -4885,18 +6866,26 @@ exports.Comparison = Comparison;
|
|
|
4885
6866
|
exports.Equation = Equation;
|
|
4886
6867
|
exports.FileTree = FileTree;
|
|
4887
6868
|
exports.Flowchart = Flowchart;
|
|
6869
|
+
exports.Form = Form;
|
|
4888
6870
|
exports.Graph = Graph;
|
|
4889
6871
|
exports.Infographic = Infographic;
|
|
6872
|
+
exports.Kanban = Kanban;
|
|
4890
6873
|
exports.Kpi = Kpi;
|
|
6874
|
+
exports.Matrix = Matrix;
|
|
4891
6875
|
exports.MindMap = MindMap;
|
|
6876
|
+
exports.Poll = Poll;
|
|
4892
6877
|
exports.Quiz = Quiz;
|
|
6878
|
+
exports.Ranker = Ranker;
|
|
6879
|
+
exports.Rating = Rating;
|
|
4893
6880
|
exports.Relation = Relation;
|
|
4894
6881
|
exports.Sequence = Sequence;
|
|
6882
|
+
exports.Slider = Slider;
|
|
4895
6883
|
exports.Steps = Steps;
|
|
4896
6884
|
exports.Table = Table;
|
|
4897
6885
|
exports.Tabs = Tabs;
|
|
4898
6886
|
exports.Timeline = Timeline;
|
|
4899
6887
|
exports.accordionDefinition = accordionDefinition;
|
|
6888
|
+
exports.annotateDefinition = annotateDefinition;
|
|
4900
6889
|
exports.architectureDefinition = architectureDefinition;
|
|
4901
6890
|
exports.calloutDefinition = calloutDefinition;
|
|
4902
6891
|
exports.cardDefinition = cardDefinition;
|
|
@@ -4910,13 +6899,20 @@ exports.computeForceLayout = computeForceLayout;
|
|
|
4910
6899
|
exports.equationDefinition = equationDefinition;
|
|
4911
6900
|
exports.fileTreeDefinition = fileTreeDefinition;
|
|
4912
6901
|
exports.flowchartDefinition = flowchartDefinition;
|
|
6902
|
+
exports.formDefinition = formDefinition;
|
|
4913
6903
|
exports.graphDefinition = graphDefinition;
|
|
4914
6904
|
exports.infographicDefinition = infographicDefinition;
|
|
6905
|
+
exports.kanbanDefinition = kanbanDefinition;
|
|
4915
6906
|
exports.kpiDefinition = kpiDefinition;
|
|
6907
|
+
exports.matrixDefinition = matrixDefinition;
|
|
4916
6908
|
exports.mindMapDefinition = mindMapDefinition;
|
|
6909
|
+
exports.pollDefinition = pollDefinition;
|
|
4917
6910
|
exports.quizDefinition = quizDefinition;
|
|
6911
|
+
exports.rankerDefinition = rankerDefinition;
|
|
6912
|
+
exports.ratingDefinition = ratingDefinition;
|
|
4918
6913
|
exports.relationDefinition = relationDefinition;
|
|
4919
6914
|
exports.sequenceDefinition = sequenceDefinition;
|
|
6915
|
+
exports.sliderDefinition = sliderDefinition;
|
|
4920
6916
|
exports.stepsDefinition = stepsDefinition;
|
|
4921
6917
|
exports.tableDefinition = tableDefinition;
|
|
4922
6918
|
exports.tabsDefinition = tabsDefinition;
|