@devfellowship/components 3.4.0 → 3.5.1
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 +194 -31
- package/dist/index.d.cts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +183 -20
- package/dist/styles/theme.css +29 -0
- package/dist/styles/tokens.css +13 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -9104,9 +9104,49 @@ function firstFreeRow(taken, columns, from) {
|
|
|
9104
9104
|
}
|
|
9105
9105
|
return row;
|
|
9106
9106
|
}
|
|
9107
|
+
var pointText = (p) => `${p.x} ${p.y}`;
|
|
9108
|
+
var center = (r) => ({ x: (r.left + r.right) / 2, y: (r.top + r.bottom) / 2 });
|
|
9109
|
+
function segmentHits(a, b, r) {
|
|
9110
|
+
return a.x === b.x ? a.x > r.left && a.x < r.right && Math.max(a.y, b.y) > r.top && Math.min(a.y, b.y) < r.bottom : a.y > r.top && a.y < r.bottom && Math.max(a.x, b.x) > r.left && Math.min(a.x, b.x) < r.right;
|
|
9111
|
+
}
|
|
9112
|
+
function routeEdge(source, target, edge, obstacles = []) {
|
|
9113
|
+
const a = center(source), b = center(target);
|
|
9114
|
+
const sameColumn = Math.abs(a.x - b.x) < 1;
|
|
9115
|
+
const vertical = sameColumn || source.left < target.right && target.left < source.right;
|
|
9116
|
+
const down = b.y >= a.y, right = b.x >= a.x;
|
|
9117
|
+
const start = vertical ? { x: a.x, y: down ? source.bottom : source.top } : { x: right ? source.right : source.left, y: a.y };
|
|
9118
|
+
const end = vertical ? { x: b.x, y: down ? target.top : target.bottom } : { x: right ? target.left : target.right, y: b.y };
|
|
9119
|
+
const route = edge.route ?? "auto";
|
|
9120
|
+
if (route === "straight" || route === "auto" && sameColumn) return { start, end, d: `M ${pointText(start)} L ${pointText(end)}` };
|
|
9121
|
+
if (route !== "elbow") {
|
|
9122
|
+
const mid = vertical ? (start.y + end.y) / 2 : (start.x + end.x) / 2;
|
|
9123
|
+
const c1 = vertical ? { x: start.x, y: mid } : { x: mid, y: start.y };
|
|
9124
|
+
const c2 = vertical ? { x: end.x, y: mid } : { x: mid, y: end.y };
|
|
9125
|
+
return { start, end, d: `M ${pointText(start)} C ${pointText(c1)} ${pointText(c2)} ${pointText(end)}` };
|
|
9126
|
+
}
|
|
9127
|
+
const boxes = [.../* @__PURE__ */ new Set([source, target, ...obstacles])];
|
|
9128
|
+
const clearance = 4;
|
|
9129
|
+
const ys = [...new Set(boxes.flatMap((r) => [r.top - clearance, r.bottom + clearance]))];
|
|
9130
|
+
let best, bestLength = Infinity;
|
|
9131
|
+
for (const sx of [source.left, source.right]) for (const tx of [target.left, target.right]) {
|
|
9132
|
+
const s = { x: sx, y: a.y }, t = { x: tx, y: b.y };
|
|
9133
|
+
const gx = sx + (sx === source.left ? -clearance : clearance);
|
|
9134
|
+
const hx = tx + (tx === target.left ? -clearance : clearance);
|
|
9135
|
+
for (const y of [a.y, b.y, ...ys]) {
|
|
9136
|
+
const points2 = [s, { x: gx, y: a.y }, { x: gx, y }, { x: hx, y }, { x: hx, y: b.y }, t];
|
|
9137
|
+
const length = points2.slice(1).reduce((sum, p, i) => sum + Math.abs(p.x - points2[i].x) + Math.abs(p.y - points2[i].y), 0);
|
|
9138
|
+
if (length >= bestLength || points2.slice(1).some((p, i) => boxes.some((r) => segmentHits(points2[i], p, r)))) continue;
|
|
9139
|
+
best = points2;
|
|
9140
|
+
bestLength = length;
|
|
9141
|
+
}
|
|
9142
|
+
}
|
|
9143
|
+
if (!best) return { start, end, d: "" };
|
|
9144
|
+
const points = best.filter((p, i) => i === 0 || p.x !== best[i - 1].x || p.y !== best[i - 1].y);
|
|
9145
|
+
return { start: points[0], end: points[points.length - 1], points, d: points.map((p, i) => `${i ? "L" : "M"} ${pointText(p)}`).join(" ") };
|
|
9146
|
+
}
|
|
9107
9147
|
|
|
9108
9148
|
// src/components/organisms/roadmap/Roadmap.tsx
|
|
9109
|
-
var
|
|
9149
|
+
var import_react8 = require("react");
|
|
9110
9150
|
|
|
9111
9151
|
// src/components/organisms/roadmap/RoadmapNode.tsx
|
|
9112
9152
|
var import_class_variance_authority14 = require("class-variance-authority");
|
|
@@ -9139,9 +9179,9 @@ function RoadmapLegendView({ legend, testIdPrefix = "roadmap" }) {
|
|
|
9139
9179
|
|
|
9140
9180
|
// src/components/organisms/roadmap/RoadmapNode.tsx
|
|
9141
9181
|
var import_jsx_runtime66 = require("react/jsx-runtime");
|
|
9142
|
-
var nodeVariants = (0, import_class_variance_authority14.cva)("relative flex min-w-0 flex-col justify-center rounded-[var(--radius)] border-2 px-
|
|
9182
|
+
var nodeVariants = (0, import_class_variance_authority14.cva)("relative flex min-w-0 flex-col justify-center rounded-[var(--radius)] border-2 px-1 py-3 text-center text-[length:var(--c-roadmap-font,12px)] leading-normal text-[var(--c-roadmap-node-fg)]", {
|
|
9143
9183
|
variants: {
|
|
9144
|
-
kind: { topic: "min-h-[49px] border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]", subtopic: "min-h-[49px] border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]", button: "min-h-[49px] border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]", label: "border-transparent bg-transparent", title: "border-transparent bg-transparent font-semibold
|
|
9184
|
+
kind: { topic: "min-h-[49px] border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]", subtopic: "min-h-[49px] border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]", button: "min-h-[49px] border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]", label: "border-transparent bg-transparent", title: "border-transparent bg-transparent font-semibold text-[length:var(--c-roadmap-title-font,18px)]", paragraph: "text-left border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]", legend: "border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]" },
|
|
9145
9185
|
state: { todo: "", done: "[--c-roadmap-node-bg:var(--c-roadmap-done-bg)] [--c-roadmap-node-fg:var(--c-roadmap-done-fg)]", learning: "[--c-roadmap-node-bg:var(--c-roadmap-learning-bg)] [--c-roadmap-node-fg:var(--c-roadmap-learning-fg)]", skipped: "[--c-roadmap-node-bg:var(--c-roadmap-skipped-bg)] [--c-roadmap-node-fg:var(--c-roadmap-skipped-fg)]", locked: "opacity-60" }
|
|
9146
9186
|
}
|
|
9147
9187
|
});
|
|
@@ -9154,7 +9194,7 @@ function RoadmapNodeView({ node, state, badge, testIdPrefix, onNodeClick, onActi
|
|
|
9154
9194
|
const Box = wholeBox && node.href ? "a" : wholeBox && onNodeClick ? "button" : "div";
|
|
9155
9195
|
const primary = Box !== "div" ? label : node.href && !locked ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("a", { href: node.href, className: interactiveClass, onClick: () => onNodeClick?.(node), children: label }) : onNodeClick && !locked ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("button", { type: "button", className: interactiveClass, onClick: () => onNodeClick(node), children: label }) : label;
|
|
9156
9196
|
const icon = node.icon ?? (badge ? { name: badge.icon ?? "check", side: "right", tone: badge.tone } : void 0);
|
|
9157
|
-
return /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("div", { style: toneStyle(nodeTone), className: "min-w-0", children: /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(Box, { "data-roadmap-node-box": node.id, href: Box === "a" ? node.href : void 0, type: Box === "button" ? "button" : void 0, onClick: Box !== "div" ? () => onNodeClick?.(node) : void 0, className: `${nodeVariants({ kind: node.kind, state })} w-full ${Box !== "div" ? focusClass : ""}`, "aria-disabled": locked || void 0, title: node.kind === "topic" || node.kind === "subtopic" ? node.description : void 0, children: [
|
|
9197
|
+
return /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("div", { style: toneStyle(nodeTone), className: "min-w-0", children: /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(Box, { style: { borderColor: node.kind === "title" || node.kind === "label" ? "transparent" : "var(--c-roadmap-border)" }, "data-roadmap-node-box": node.id, href: Box === "a" ? node.href : void 0, type: Box === "button" ? "button" : void 0, onClick: Box !== "div" ? () => onNodeClick?.(node) : void 0, className: `${nodeVariants({ kind: node.kind, state })} w-full ${icon ? icon.side === "left" ? "pl-3" : "pr-3" : ""} ${Box !== "div" ? focusClass : ""}`, "aria-disabled": locked || void 0, title: node.kind === "topic" || node.kind === "subtopic" ? node.description : void 0, children: [
|
|
9158
9198
|
icon && /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(RoadmapBadge, { name: icon.name, tone: icon.tone ?? nodeTone, label: badge?.label ?? icon.name, className: `absolute top-1/2 -translate-y-1/2 ${icon.side === "left" ? "left-0 -translate-x-1/2" : "right-0 translate-x-1/2"}` }),
|
|
9159
9199
|
primary,
|
|
9160
9200
|
node.description && node.kind === "paragraph" && /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("p", { className: "mt-2 [overflow-wrap:anywhere]", children: node.description }),
|
|
@@ -9169,15 +9209,17 @@ function RoadmapNodeView({ node, state, badge, testIdPrefix, onNodeClick, onActi
|
|
|
9169
9209
|
// src/components/organisms/roadmap/RoadmapGroup.tsx
|
|
9170
9210
|
var import_jsx_runtime67 = require("react/jsx-runtime");
|
|
9171
9211
|
function RoadmapGroupView({ group, testIdPrefix }) {
|
|
9212
|
+
const tone = resolveGroupTone(group);
|
|
9213
|
+
const appearance = tone === "accent" ? { "--c-roadmap-node-bg": "var(--c-roadmap-group-accent-bg)", "--c-roadmap-node-fg": "var(--c-roadmap-group-accent-fg)" } : toneStyle(tone);
|
|
9172
9214
|
return /* @__PURE__ */ (0, import_jsx_runtime67.jsx)(import_jsx_runtime67.Fragment, { children: groupColumnRuns(group).map((run, index) => /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)(
|
|
9173
9215
|
"div",
|
|
9174
9216
|
{
|
|
9175
9217
|
"data-testid": `${testIdPrefix}-group`,
|
|
9176
9218
|
"data-group-id": group.id,
|
|
9177
|
-
style: {
|
|
9219
|
+
style: { borderColor: "var(--c-roadmap-border)", ...appearance, gridColumn: `${run.start} / span ${run.span}`, gridRow: `${group.from + 1} / span ${group.to - group.from + 1}` },
|
|
9178
9220
|
className: "pointer-events-none relative z-0 min-w-0 self-stretch rounded-[var(--radius)] border-2 border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)] text-[var(--c-roadmap-node-fg)]",
|
|
9179
9221
|
children: [
|
|
9180
|
-
index === 0 && group.title && /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "absolute left-2 right-2 top-0 -translate-y-1/2 text-xs leading-normal", children: /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "bg-[var(--c-roadmap-surface)] px-1 [
|
|
9222
|
+
index === 0 && group.title && /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { title: group.title, className: "absolute left-2 right-2 top-0 -translate-y-1/2 text-xs leading-normal", children: /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "inline-block max-w-full overflow-hidden text-ellipsis whitespace-nowrap bg-[var(--c-roadmap-surface)] px-1 align-top text-[var(--c-roadmap-neutral-fg)]", children: group.title }) }),
|
|
9181
9223
|
index === 0 && group.description && /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "sr-only", children: group.description })
|
|
9182
9224
|
]
|
|
9183
9225
|
},
|
|
@@ -9185,49 +9227,170 @@ function RoadmapGroupView({ group, testIdPrefix }) {
|
|
|
9185
9227
|
)) });
|
|
9186
9228
|
}
|
|
9187
9229
|
|
|
9188
|
-
// src/components/organisms/roadmap/
|
|
9230
|
+
// src/components/organisms/roadmap/RoadmapEdgeLayer.tsx
|
|
9231
|
+
var import_react7 = require("react");
|
|
9189
9232
|
var import_jsx_runtime68 = require("react/jsx-runtime");
|
|
9190
|
-
function
|
|
9191
|
-
const
|
|
9192
|
-
const
|
|
9233
|
+
function Edge({ edge, path, instance, prefix }) {
|
|
9234
|
+
const pathRef = (0, import_react7.useRef)(null);
|
|
9235
|
+
const [midpoint, setMidpoint] = (0, import_react7.useState)();
|
|
9236
|
+
(0, import_react7.useLayoutEffect)(() => {
|
|
9237
|
+
const element = pathRef.current;
|
|
9238
|
+
if (edge.label && element?.getTotalLength && path.d) {
|
|
9239
|
+
const p = element.getPointAtLength(element.getTotalLength() / 2);
|
|
9240
|
+
setMidpoint({ x: p.x, y: p.y });
|
|
9241
|
+
} else setMidpoint(void 0);
|
|
9242
|
+
}, [path.d, edge.label]);
|
|
9243
|
+
const arrow = resolveEdgeArrow(edge), style = resolveEdgeStyle(edge);
|
|
9244
|
+
const marker = `${instance}-${edge.id}`;
|
|
9245
|
+
const color = edge.tone ? `var(--c-roadmap-${edge.tone}-edge)` : "var(--c-roadmap-edge)";
|
|
9246
|
+
return /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("g", { style: { color }, "data-roadmap-edge": edge.id, children: [
|
|
9247
|
+
arrow !== "none" && /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("marker", { id: marker, viewBox: "0 0 8 8", refX: "8", refY: "4", markerWidth: "8", markerHeight: "8", markerUnits: "userSpaceOnUse", orient: "auto-start-reverse", children: /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("path", { d: "M 0 0 L 8 4 L 0 8 Z", fill: "currentColor" }) }) }),
|
|
9248
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)(
|
|
9249
|
+
"path",
|
|
9250
|
+
{
|
|
9251
|
+
ref: pathRef,
|
|
9252
|
+
"data-testid": `${prefix}-edge`,
|
|
9253
|
+
"data-edge-id": edge.id,
|
|
9254
|
+
"data-source": edge.source,
|
|
9255
|
+
"data-target": edge.target,
|
|
9256
|
+
d: path.d,
|
|
9257
|
+
fill: "none",
|
|
9258
|
+
stroke: "currentColor",
|
|
9259
|
+
strokeLinecap: "round",
|
|
9260
|
+
strokeLinejoin: "round",
|
|
9261
|
+
strokeDasharray: style === "solid" ? void 0 : style === "dotted" ? "0.8 4" : "0.8 8",
|
|
9262
|
+
className: "[stroke-width:var(--c-roadmap-edge-width,2px)]",
|
|
9263
|
+
markerStart: arrow === "both" ? `url(#${marker})` : void 0,
|
|
9264
|
+
markerEnd: arrow !== "none" ? `url(#${marker})` : void 0
|
|
9265
|
+
}
|
|
9266
|
+
),
|
|
9267
|
+
edge.label && midpoint && /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("foreignObject", { "data-testid": `${prefix}-edge-label`, "data-edge-id": edge.id, x: midpoint.x, y: midpoint.y, width: "1", height: "1", overflow: "visible", children: /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(
|
|
9268
|
+
"div",
|
|
9269
|
+
{
|
|
9270
|
+
className: "w-max max-w-[160px] -translate-x-1/2 -translate-y-1/2 rounded-full bg-[var(--c-roadmap-edge-label-bg)] px-2 py-0.5 text-center text-xs leading-normal text-[var(--c-roadmap-edge-label-fg)] [overflow-wrap:anywhere]",
|
|
9271
|
+
style: { boxShadow: "0 0 0 2px var(--c-roadmap-surface)" },
|
|
9272
|
+
children: edge.label
|
|
9273
|
+
}
|
|
9274
|
+
) })
|
|
9275
|
+
] });
|
|
9276
|
+
}
|
|
9277
|
+
function RoadmapEdgeLayer({ containerRef, document: documentModel, testIdPrefix, debugPerf }) {
|
|
9278
|
+
const instance = `roadmap-${(0, import_react7.useId)().replace(/:/g, "")}`;
|
|
9279
|
+
const [measured, setMeasured] = (0, import_react7.useState)([]);
|
|
9280
|
+
(0, import_react7.useEffect)(() => {
|
|
9281
|
+
const container = containerRef.current;
|
|
9282
|
+
if (!container || !documentModel.edges.length) {
|
|
9283
|
+
setMeasured([]);
|
|
9284
|
+
return;
|
|
9285
|
+
}
|
|
9286
|
+
let disposed = false, frame = 0;
|
|
9287
|
+
const observed = /* @__PURE__ */ new Set();
|
|
9288
|
+
const measure = () => {
|
|
9289
|
+
frame = 0;
|
|
9290
|
+
if (disposed) return;
|
|
9291
|
+
const started = performance.now();
|
|
9292
|
+
const root = container.getBoundingClientRect();
|
|
9293
|
+
const boxes = /* @__PURE__ */ new Map();
|
|
9294
|
+
for (const wrapper of container.querySelectorAll("[data-roadmap-node]")) {
|
|
9295
|
+
if (wrapper.closest("[data-roadmap-grid]") !== container) continue;
|
|
9296
|
+
const id = wrapper.dataset.roadmapNode;
|
|
9297
|
+
const border = Array.from(wrapper.querySelectorAll("[data-roadmap-node-box]")).find((el) => el.dataset.roadmapNodeBox === id);
|
|
9298
|
+
const element = border ?? wrapper.firstElementChild ?? wrapper;
|
|
9299
|
+
for (const target of [wrapper, element]) if (!observed.has(target)) {
|
|
9300
|
+
observer?.observe(target);
|
|
9301
|
+
observed.add(target);
|
|
9302
|
+
}
|
|
9303
|
+
const rect = element.getBoundingClientRect();
|
|
9304
|
+
boxes.set(id, { left: rect.left - root.left, right: rect.right - root.left, top: rect.top - root.top, bottom: rect.bottom - root.top });
|
|
9305
|
+
}
|
|
9306
|
+
for (const element of observed) if (!container.contains(element)) {
|
|
9307
|
+
observer?.unobserve(element);
|
|
9308
|
+
observed.delete(element);
|
|
9309
|
+
}
|
|
9310
|
+
const obstacles = [...boxes.values()];
|
|
9311
|
+
const next = documentModel.edges.flatMap((edge) => {
|
|
9312
|
+
const source = boxes.get(edge.source), target = boxes.get(edge.target);
|
|
9313
|
+
return source && target ? [{ edge, path: routeEdge(source, target, edge, obstacles) }] : [];
|
|
9314
|
+
});
|
|
9315
|
+
setMeasured(next);
|
|
9316
|
+
if (debugPerf) performance.measure("roadmap:edges", { start: started, end: performance.now() });
|
|
9317
|
+
};
|
|
9318
|
+
const schedule = () => {
|
|
9319
|
+
if (!disposed && !frame) frame = requestAnimationFrame(measure);
|
|
9320
|
+
};
|
|
9321
|
+
const observer = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(schedule);
|
|
9322
|
+
observer?.observe(container);
|
|
9323
|
+
const mutations = new MutationObserver((records) => {
|
|
9324
|
+
if (records.some((record) => (record.target instanceof Element ? record.target : record.target.parentElement)?.closest("[data-roadmap-node]"))) schedule();
|
|
9325
|
+
});
|
|
9326
|
+
mutations.observe(container, { childList: true, subtree: true, characterData: true, attributes: true });
|
|
9327
|
+
const fonts = container.ownerDocument.fonts;
|
|
9328
|
+
fonts?.ready.then(schedule);
|
|
9329
|
+
fonts?.addEventListener("loadingdone", schedule);
|
|
9330
|
+
schedule();
|
|
9331
|
+
return () => {
|
|
9332
|
+
disposed = true;
|
|
9333
|
+
cancelAnimationFrame(frame);
|
|
9334
|
+
observer?.disconnect();
|
|
9335
|
+
mutations.disconnect();
|
|
9336
|
+
fonts?.removeEventListener("loadingdone", schedule);
|
|
9337
|
+
};
|
|
9338
|
+
}, [containerRef, documentModel, debugPerf]);
|
|
9339
|
+
return /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("svg", { "data-testid": `${testIdPrefix}-edges`, "aria-hidden": "true", className: "pointer-events-none print:hidden absolute inset-0 z-[1] h-full w-full overflow-visible", children: measured.map((item) => /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(Edge, { ...item, instance, prefix: testIdPrefix }, item.edge.id)) });
|
|
9340
|
+
}
|
|
9341
|
+
|
|
9342
|
+
// src/components/organisms/roadmap/Roadmap.tsx
|
|
9343
|
+
var import_jsx_runtime69 = require("react/jsx-runtime");
|
|
9344
|
+
function Roadmap({ document: document2, state, renderNode, onNodeClick, onAction, testIdPrefix = "roadmap", className = "", ariaLabel, debugPerf, collapseEmptyColumns = true }) {
|
|
9345
|
+
const gridRef = (0, import_react8.useRef)(null);
|
|
9346
|
+
const { placements } = (0, import_react8.useMemo)(() => placeNodes(document2), [document2]);
|
|
9347
|
+
const used = (0, import_react8.useMemo)(() => columnsUsed({ nodes: document2.nodes.filter((node) => !((node.span ?? 1) === 3 && (node.kind === "title" || node.kind === "label"))) }), [document2]);
|
|
9348
|
+
const weights = ["left", "center", "right"].map((column, index) => !collapseEmptyColumns || used.length === 0 || used.includes(column) ? index === 1 ? 1.25 : 1 : 0);
|
|
9349
|
+
const gridStyle = {
|
|
9350
|
+
gridTemplateColumns: weights.map((weight) => `minmax(0, ${weight}fr)`).join(" "),
|
|
9351
|
+
...weights.filter(Boolean).length === 1 ? { columnGap: 0 } : {}
|
|
9352
|
+
};
|
|
9353
|
+
const badges = (0, import_react8.useMemo)(() => new Map(document2.legend?.entries.map((entry) => [entry.id, entry])), [document2.legend]);
|
|
9193
9354
|
const legendPlacement = document2.legend ? resolveLegendPlacement(document2.legend) : void 0;
|
|
9194
|
-
const legend = document2.legend && /* @__PURE__ */ (0,
|
|
9195
|
-
return /* @__PURE__ */ (0,
|
|
9196
|
-
legendPlacement === "top" && /* @__PURE__ */ (0,
|
|
9197
|
-
/* @__PURE__ */ (0,
|
|
9198
|
-
|
|
9355
|
+
const legend = document2.legend && /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(RoadmapLegendView, { legend: document2.legend, testIdPrefix });
|
|
9356
|
+
return /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("section", { "data-testid": testIdPrefix, "aria-label": ariaLabel ?? document2.title ?? "Roadmap", style: { containerType: "inline-size", containerName: "roadmap" }, className: `mx-auto w-full max-w-[1120px] min-w-0 bg-[var(--c-roadmap-surface)] text-[var(--c-roadmap-neutral-fg)] ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("div", { className: "dfl-roadmap-body min-w-0 p-[var(--c-roadmap-padding)]", children: [
|
|
9357
|
+
legendPlacement === "top" && /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("div", { className: "mb-6", children: legend }),
|
|
9358
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("div", { ref: gridRef, "data-roadmap-grid": "", "data-testid": `${testIdPrefix}-grid`, role: "list", "aria-label": "Roadmap nodes", style: gridStyle, className: "relative isolate grid min-w-0 grid-cols-3 items-center gap-x-[var(--c-roadmap-column-gap)] gap-y-2", children: [
|
|
9359
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)(RoadmapEdgeLayer, { containerRef: gridRef, document: document2, testIdPrefix, debugPerf }),
|
|
9360
|
+
document2.groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(RoadmapGroupView, { group, testIdPrefix }, group.id)),
|
|
9199
9361
|
placements.map(({ node, row, column }) => {
|
|
9200
9362
|
const resolvedState = resolveNodeState(node, state);
|
|
9201
|
-
return /* @__PURE__ */ (0,
|
|
9363
|
+
return /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(
|
|
9202
9364
|
"div",
|
|
9203
9365
|
{
|
|
9204
9366
|
role: "listitem",
|
|
9205
9367
|
"data-testid": `${testIdPrefix}-node`,
|
|
9206
9368
|
"data-node-id": node.id,
|
|
9369
|
+
"data-roadmap-node": node.id,
|
|
9207
9370
|
"data-state": resolvedState,
|
|
9208
9371
|
style: { gridColumn: `${column.start} / span ${column.span}`, gridRow: `${row.start} / span 1` },
|
|
9209
|
-
className:
|
|
9210
|
-
children: node.kind === "legend" ? legendPlacement === "inline" ? legend : null : renderNode ? renderNode(node, resolvedState) : /* @__PURE__ */ (0,
|
|
9372
|
+
className: `relative z-10 min-w-0 py-2 ${node.icon || node.badge ? node.icon?.side === "left" ? "pl-3 pr-1" : "pl-1 pr-3" : "px-1"}`,
|
|
9373
|
+
children: node.kind === "legend" ? legendPlacement === "inline" ? legend : null : renderNode ? renderNode(node, resolvedState) : /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(RoadmapNodeView, { node, state: resolvedState, badge: node.badge ? badges.get(node.badge) : void 0, onNodeClick, onAction, testIdPrefix })
|
|
9211
9374
|
},
|
|
9212
9375
|
node.id
|
|
9213
9376
|
);
|
|
9214
9377
|
})
|
|
9215
9378
|
] }),
|
|
9216
|
-
legendPlacement === "bottom" && /* @__PURE__ */ (0,
|
|
9217
|
-
] });
|
|
9379
|
+
legendPlacement === "bottom" && /* @__PURE__ */ (0, import_jsx_runtime69.jsx)("div", { className: "mt-6", children: legend })
|
|
9380
|
+
] }) });
|
|
9218
9381
|
}
|
|
9219
9382
|
|
|
9220
9383
|
// src/hooks/use-iframe-auth.ts
|
|
9221
|
-
var
|
|
9384
|
+
var import_react9 = require("react");
|
|
9222
9385
|
function useIframeAuth() {
|
|
9223
|
-
const ctx = (0,
|
|
9386
|
+
const ctx = (0, import_react9.useContext)(IframeContext);
|
|
9224
9387
|
return { token: ctx.token, userId: ctx.userId, ready: ctx.ready };
|
|
9225
9388
|
}
|
|
9226
9389
|
|
|
9227
9390
|
// src/hooks/use-iframe-navigate.ts
|
|
9228
|
-
var
|
|
9391
|
+
var import_react10 = require("react");
|
|
9229
9392
|
function useIframeNavigate() {
|
|
9230
|
-
return (0,
|
|
9393
|
+
return (0, import_react10.useCallback)((path) => {
|
|
9231
9394
|
if (!window.parent || window.parent === window) return;
|
|
9232
9395
|
window.parent.postMessage(
|
|
9233
9396
|
{ type: "DFL_NAVIGATE", path },
|
|
@@ -9237,9 +9400,9 @@ function useIframeNavigate() {
|
|
|
9237
9400
|
}
|
|
9238
9401
|
|
|
9239
9402
|
// src/providers/feature-flag-provider.tsx
|
|
9240
|
-
var
|
|
9241
|
-
var
|
|
9242
|
-
var FeatureFlagContext = (0,
|
|
9403
|
+
var import_react11 = require("react");
|
|
9404
|
+
var import_jsx_runtime70 = require("react/jsx-runtime");
|
|
9405
|
+
var FeatureFlagContext = (0, import_react11.createContext)({
|
|
9243
9406
|
flags: {},
|
|
9244
9407
|
isEnabled: () => false
|
|
9245
9408
|
});
|
|
@@ -9247,21 +9410,21 @@ var FeatureFlagProvider = ({
|
|
|
9247
9410
|
children,
|
|
9248
9411
|
flags
|
|
9249
9412
|
}) => {
|
|
9250
|
-
const value = (0,
|
|
9413
|
+
const value = (0, import_react11.useMemo)(
|
|
9251
9414
|
() => ({
|
|
9252
9415
|
flags,
|
|
9253
9416
|
isEnabled: (flag) => Boolean(flags[flag])
|
|
9254
9417
|
}),
|
|
9255
9418
|
[flags]
|
|
9256
9419
|
);
|
|
9257
|
-
return /* @__PURE__ */ (0,
|
|
9420
|
+
return /* @__PURE__ */ (0, import_jsx_runtime70.jsx)(FeatureFlagContext.Provider, { value, children });
|
|
9258
9421
|
};
|
|
9259
9422
|
var useFeatureFlag = (flag) => {
|
|
9260
|
-
const { isEnabled } = (0,
|
|
9423
|
+
const { isEnabled } = (0, import_react11.useContext)(FeatureFlagContext);
|
|
9261
9424
|
return isEnabled(flag);
|
|
9262
9425
|
};
|
|
9263
9426
|
var useFeatureFlags = () => {
|
|
9264
|
-
const { flags } = (0,
|
|
9427
|
+
const { flags } = (0, import_react11.useContext)(FeatureFlagContext);
|
|
9265
9428
|
return flags;
|
|
9266
9429
|
};
|
|
9267
9430
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.d.cts
CHANGED
|
@@ -3268,11 +3268,14 @@ interface RoadmapProps {
|
|
|
3268
3268
|
renderNode?: (node: RoadmapNode, state: RoadmapNodeState) => ReactNode;
|
|
3269
3269
|
onNodeClick?: (node: RoadmapNode) => void;
|
|
3270
3270
|
onAction?: (actionId: string, node: RoadmapNode) => void;
|
|
3271
|
+
debugPerf?: boolean;
|
|
3272
|
+
/** Collapse unused lanes; full-width headings do not reserve empty side tracks. */
|
|
3273
|
+
collapseEmptyColumns?: boolean;
|
|
3271
3274
|
testIdPrefix?: string;
|
|
3272
3275
|
className?: string;
|
|
3273
3276
|
ariaLabel?: string;
|
|
3274
3277
|
}
|
|
3275
3278
|
/** Dark-only, ordinary document scroll. The JSON owns placement; CSS owns the pixels. */
|
|
3276
|
-
declare function Roadmap({ document, state, renderNode, onNodeClick, onAction, testIdPrefix, className, ariaLabel }: RoadmapProps): React__default.JSX.Element;
|
|
3279
|
+
declare function Roadmap({ document, state, renderNode, onNodeClick, onAction, testIdPrefix, className, ariaLabel, debugPerf, collapseEmptyColumns }: RoadmapProps): React__default.JSX.Element;
|
|
3277
3280
|
|
|
3278
3281
|
export { ALLOWED_ORIGINS, AVATAR_MEMBER_PALETTE_SIZE, Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, type AlertDialogActionProps, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppNavbar, type AppNavbarProps, AppSidebar, type AppSidebarProps, AspectRatio, Avatar, AvatarFallback, AvatarGroup, AvatarImage, type AvatarSize, AvatarStatus, type AvatarStatusValue, type AvatarTone, Badge, type BadgeProps, Breadcrumb, BreadcrumbEllipsis, type BreadcrumbEntry, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, type ButtonProps, Calendar, type CalendarProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, type CarouselApi, CarouselContent, CarouselDots, CarouselItem, CarouselNext, CarouselPrevious, type ChartConfig, ChartContainer, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent, Checkbox, Collapsible, CollapsibleContent, CollapsibleHeader, CollapsibleItem, CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ConfirmDialog, type ConfirmDialogProps, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DEFAULT_EDGE_ARROW, DEFAULT_EDGE_ROUTE, DEFAULT_EDGE_STYLE, DEFAULT_GROUP_TONE, DEFAULT_LEGEND_PLACEMENT, DEFAULT_NAME_COL_WIDTH, DEFAULT_NODE_SPAN, DEFAULT_NODE_STATE, type DflIframeMessage, type DflNavigateMessage, type DflReadyMessage, DflRemote, type DflRemoteProps, type DflResizeMessage, type DflSetTokenMessage, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, Gantt, type GanttDependency, type GanttMilestone, type GanttProps, type GanttStage, HoverCard, HoverCardContent, HoverCardTrigger, IconButton, type IconButtonProps, IframeAware, type IframeAwareProps, IframeContext, type IframeContextValue, type IframeMessageType, Input, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Kbd, type KbdProps, Label, LoginPage, type LoginPageProps, LoginScreen, type LoginScreenProps, MEMBER_PALETTE_SIZE, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, NODE_KIND_DEFAULT_TONE, type NavGroup, type NavItem, type NavbarUserInfo, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, type OTPStatus, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PasswordInput, type PasswordInputProps, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Progress, type ProgressProps, ProtectedRoute, type ProtectedRouteProps, PublishDrawer, type PublishDrawerProps, type PublishDrawerSupabase, type PublishResult, type PublishStatus, type PublisherAccount, ROADMAP_ARROWS, ROADMAP_COLUMNS, ROADMAP_EDGE_LABEL_MAX, ROADMAP_EDGE_ROUTES, ROADMAP_EDGE_STYLES, ROADMAP_LABEL_MAX, ROADMAP_LEGEND_PLACEMENTS, ROADMAP_NODE_ID_PATTERN, ROADMAP_NODE_KINDS, ROADMAP_NODE_STATES, ROADMAP_TONES, RadioGroup, RadioGroupItem, RadioGroupRow, ResizableHandle, ResizablePanel, ResizablePanelGroup, Roadmap, type RoadmapAction, type RoadmapArrow, type RoadmapColumn, type RoadmapDocument, type RoadmapEdge, type RoadmapEdgeRoute, type RoadmapEdgeStyle, type RoadmapGroup, type RoadmapGroupRange, type RoadmapIcon, type RoadmapLegend, type RoadmapLegendEntry, type RoadmapLegendPlacement, type RoadmapLink, type RoadmapNode, type RoadmapNodeKind, type RoadmapNodeState, type RoadmapOverlap, type RoadmapPlacement, type RoadmapProps, type RoadmapStateOverlay, type RoadmapTone, ScrollArea, type ScrollAreaScrollbars, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, SeparatorWithLabel, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, SonnerToaster, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, type TextareaProps, Toast, ToastAction, type ToastActionElement, ToastClose, type ToastComponentProps, ToastDescription, ToastProvider, ToastTitle, ToastViewport, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UserAvatar, type UserAvatarProps, type UserInfo, UserMenu, type UserMenuItem, type UserMenuProps, avatarMemberColor, badgeVariants, barGridColumn, buttonVariants, clampPct, columnIndex, columnsCovered, columnsUsed, filterPublishableAccounts, firstFreeRow, getInitials, gridColumnFor, groupColumnRuns, groupRanges, iconButtonVariants, isAllowedOrigin, kbdVariants, labelVariants, maxRowOf, memberHueIndex, memberHueVar, navigationMenuTriggerStyle, nodesByRow, parseRoadmapDocument, parseRoadmapStateOverlay, parseTags, placeNodes, resolveEdgeArrow, resolveEdgeRoute, resolveEdgeStyle, resolveGroupColumns, resolveGroupTone, resolveLegendPlacement, resolveNameColWidth, resolveNodeSpan, resolveNodeState, resolveNodeTone, resolveWeekCount, resolveWeekLabels, roadmapActionSchema, roadmapDocumentSchema, roadmapEdgeSchema, roadmapGroupSchema, roadmapIconSchema, roadmapLegendEntrySchema, roadmapLegendSchema, roadmapLinkSchema, roadmapNodeSchema, roadmapNodeStateSchema, roadmapStateOverlaySchema, roadmapStateSchema, rowsOf, safeParseRoadmapDocument, safeParseRoadmapStateOverlay, stageProgress, toggleVariants, useFormField, useSidebar, validateNoOverlap, validatePublishForm };
|
package/dist/index.d.ts
CHANGED
|
@@ -3268,11 +3268,14 @@ interface RoadmapProps {
|
|
|
3268
3268
|
renderNode?: (node: RoadmapNode, state: RoadmapNodeState) => ReactNode;
|
|
3269
3269
|
onNodeClick?: (node: RoadmapNode) => void;
|
|
3270
3270
|
onAction?: (actionId: string, node: RoadmapNode) => void;
|
|
3271
|
+
debugPerf?: boolean;
|
|
3272
|
+
/** Collapse unused lanes; full-width headings do not reserve empty side tracks. */
|
|
3273
|
+
collapseEmptyColumns?: boolean;
|
|
3271
3274
|
testIdPrefix?: string;
|
|
3272
3275
|
className?: string;
|
|
3273
3276
|
ariaLabel?: string;
|
|
3274
3277
|
}
|
|
3275
3278
|
/** Dark-only, ordinary document scroll. The JSON owns placement; CSS owns the pixels. */
|
|
3276
|
-
declare function Roadmap({ document, state, renderNode, onNodeClick, onAction, testIdPrefix, className, ariaLabel }: RoadmapProps): React__default.JSX.Element;
|
|
3279
|
+
declare function Roadmap({ document, state, renderNode, onNodeClick, onAction, testIdPrefix, className, ariaLabel, debugPerf, collapseEmptyColumns }: RoadmapProps): React__default.JSX.Element;
|
|
3277
3280
|
|
|
3278
3281
|
export { ALLOWED_ORIGINS, AVATAR_MEMBER_PALETTE_SIZE, Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, type AlertDialogActionProps, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppNavbar, type AppNavbarProps, AppSidebar, type AppSidebarProps, AspectRatio, Avatar, AvatarFallback, AvatarGroup, AvatarImage, type AvatarSize, AvatarStatus, type AvatarStatusValue, type AvatarTone, Badge, type BadgeProps, Breadcrumb, BreadcrumbEllipsis, type BreadcrumbEntry, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, type ButtonProps, Calendar, type CalendarProps, Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, type CarouselApi, CarouselContent, CarouselDots, CarouselItem, CarouselNext, CarouselPrevious, type ChartConfig, ChartContainer, ChartLegend, ChartLegendContent, ChartStyle, ChartTooltip, ChartTooltipContent, Checkbox, Collapsible, CollapsibleContent, CollapsibleHeader, CollapsibleItem, CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, ConfirmDialog, type ConfirmDialogProps, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, DEFAULT_EDGE_ARROW, DEFAULT_EDGE_ROUTE, DEFAULT_EDGE_STYLE, DEFAULT_GROUP_TONE, DEFAULT_LEGEND_PLACEMENT, DEFAULT_NAME_COL_WIDTH, DEFAULT_NODE_SPAN, DEFAULT_NODE_STATE, type DflIframeMessage, type DflNavigateMessage, type DflReadyMessage, DflRemote, type DflRemoteProps, type DflResizeMessage, type DflSetTokenMessage, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, Gantt, type GanttDependency, type GanttMilestone, type GanttProps, type GanttStage, HoverCard, HoverCardContent, HoverCardTrigger, IconButton, type IconButtonProps, IframeAware, type IframeAwareProps, IframeContext, type IframeContextValue, type IframeMessageType, Input, InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot, Kbd, type KbdProps, Label, LoginPage, type LoginPageProps, LoginScreen, type LoginScreenProps, MEMBER_PALETTE_SIZE, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarPortal, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarShortcut, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, NODE_KIND_DEFAULT_TONE, type NavGroup, type NavItem, type NavbarUserInfo, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, type OTPStatus, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PasswordInput, type PasswordInputProps, Popover, PopoverAnchor, PopoverContent, PopoverTrigger, Progress, type ProgressProps, ProtectedRoute, type ProtectedRouteProps, PublishDrawer, type PublishDrawerProps, type PublishDrawerSupabase, type PublishResult, type PublishStatus, type PublisherAccount, ROADMAP_ARROWS, ROADMAP_COLUMNS, ROADMAP_EDGE_LABEL_MAX, ROADMAP_EDGE_ROUTES, ROADMAP_EDGE_STYLES, ROADMAP_LABEL_MAX, ROADMAP_LEGEND_PLACEMENTS, ROADMAP_NODE_ID_PATTERN, ROADMAP_NODE_KINDS, ROADMAP_NODE_STATES, ROADMAP_TONES, RadioGroup, RadioGroupItem, RadioGroupRow, ResizableHandle, ResizablePanel, ResizablePanelGroup, Roadmap, type RoadmapAction, type RoadmapArrow, type RoadmapColumn, type RoadmapDocument, type RoadmapEdge, type RoadmapEdgeRoute, type RoadmapEdgeStyle, type RoadmapGroup, type RoadmapGroupRange, type RoadmapIcon, type RoadmapLegend, type RoadmapLegendEntry, type RoadmapLegendPlacement, type RoadmapLink, type RoadmapNode, type RoadmapNodeKind, type RoadmapNodeState, type RoadmapOverlap, type RoadmapPlacement, type RoadmapProps, type RoadmapStateOverlay, type RoadmapTone, ScrollArea, type ScrollAreaScrollbars, ScrollBar, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, SeparatorWithLabel, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Slider, SonnerToaster, Switch, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, type TextareaProps, Toast, ToastAction, type ToastActionElement, ToastClose, type ToastComponentProps, ToastDescription, ToastProvider, ToastTitle, ToastViewport, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, UserAvatar, type UserAvatarProps, type UserInfo, UserMenu, type UserMenuItem, type UserMenuProps, avatarMemberColor, badgeVariants, barGridColumn, buttonVariants, clampPct, columnIndex, columnsCovered, columnsUsed, filterPublishableAccounts, firstFreeRow, getInitials, gridColumnFor, groupColumnRuns, groupRanges, iconButtonVariants, isAllowedOrigin, kbdVariants, labelVariants, maxRowOf, memberHueIndex, memberHueVar, navigationMenuTriggerStyle, nodesByRow, parseRoadmapDocument, parseRoadmapStateOverlay, parseTags, placeNodes, resolveEdgeArrow, resolveEdgeRoute, resolveEdgeStyle, resolveGroupColumns, resolveGroupTone, resolveLegendPlacement, resolveNameColWidth, resolveNodeSpan, resolveNodeState, resolveNodeTone, resolveWeekCount, resolveWeekLabels, roadmapActionSchema, roadmapDocumentSchema, roadmapEdgeSchema, roadmapGroupSchema, roadmapIconSchema, roadmapLegendEntrySchema, roadmapLegendSchema, roadmapLinkSchema, roadmapNodeSchema, roadmapNodeStateSchema, roadmapStateOverlaySchema, roadmapStateSchema, rowsOf, safeParseRoadmapDocument, safeParseRoadmapStateOverlay, stageProgress, toggleVariants, useFormField, useSidebar, validateNoOverlap, validatePublishForm };
|
package/dist/index.js
CHANGED
|
@@ -8715,9 +8715,49 @@ function firstFreeRow(taken, columns, from) {
|
|
|
8715
8715
|
}
|
|
8716
8716
|
return row;
|
|
8717
8717
|
}
|
|
8718
|
+
var pointText = (p) => `${p.x} ${p.y}`;
|
|
8719
|
+
var center = (r) => ({ x: (r.left + r.right) / 2, y: (r.top + r.bottom) / 2 });
|
|
8720
|
+
function segmentHits(a, b, r) {
|
|
8721
|
+
return a.x === b.x ? a.x > r.left && a.x < r.right && Math.max(a.y, b.y) > r.top && Math.min(a.y, b.y) < r.bottom : a.y > r.top && a.y < r.bottom && Math.max(a.x, b.x) > r.left && Math.min(a.x, b.x) < r.right;
|
|
8722
|
+
}
|
|
8723
|
+
function routeEdge(source, target, edge, obstacles = []) {
|
|
8724
|
+
const a = center(source), b = center(target);
|
|
8725
|
+
const sameColumn = Math.abs(a.x - b.x) < 1;
|
|
8726
|
+
const vertical = sameColumn || source.left < target.right && target.left < source.right;
|
|
8727
|
+
const down = b.y >= a.y, right = b.x >= a.x;
|
|
8728
|
+
const start = vertical ? { x: a.x, y: down ? source.bottom : source.top } : { x: right ? source.right : source.left, y: a.y };
|
|
8729
|
+
const end = vertical ? { x: b.x, y: down ? target.top : target.bottom } : { x: right ? target.left : target.right, y: b.y };
|
|
8730
|
+
const route = edge.route ?? "auto";
|
|
8731
|
+
if (route === "straight" || route === "auto" && sameColumn) return { start, end, d: `M ${pointText(start)} L ${pointText(end)}` };
|
|
8732
|
+
if (route !== "elbow") {
|
|
8733
|
+
const mid = vertical ? (start.y + end.y) / 2 : (start.x + end.x) / 2;
|
|
8734
|
+
const c1 = vertical ? { x: start.x, y: mid } : { x: mid, y: start.y };
|
|
8735
|
+
const c2 = vertical ? { x: end.x, y: mid } : { x: mid, y: end.y };
|
|
8736
|
+
return { start, end, d: `M ${pointText(start)} C ${pointText(c1)} ${pointText(c2)} ${pointText(end)}` };
|
|
8737
|
+
}
|
|
8738
|
+
const boxes = [.../* @__PURE__ */ new Set([source, target, ...obstacles])];
|
|
8739
|
+
const clearance = 4;
|
|
8740
|
+
const ys = [...new Set(boxes.flatMap((r) => [r.top - clearance, r.bottom + clearance]))];
|
|
8741
|
+
let best, bestLength = Infinity;
|
|
8742
|
+
for (const sx of [source.left, source.right]) for (const tx of [target.left, target.right]) {
|
|
8743
|
+
const s = { x: sx, y: a.y }, t = { x: tx, y: b.y };
|
|
8744
|
+
const gx = sx + (sx === source.left ? -clearance : clearance);
|
|
8745
|
+
const hx = tx + (tx === target.left ? -clearance : clearance);
|
|
8746
|
+
for (const y of [a.y, b.y, ...ys]) {
|
|
8747
|
+
const points2 = [s, { x: gx, y: a.y }, { x: gx, y }, { x: hx, y }, { x: hx, y: b.y }, t];
|
|
8748
|
+
const length = points2.slice(1).reduce((sum, p, i) => sum + Math.abs(p.x - points2[i].x) + Math.abs(p.y - points2[i].y), 0);
|
|
8749
|
+
if (length >= bestLength || points2.slice(1).some((p, i) => boxes.some((r) => segmentHits(points2[i], p, r)))) continue;
|
|
8750
|
+
best = points2;
|
|
8751
|
+
bestLength = length;
|
|
8752
|
+
}
|
|
8753
|
+
}
|
|
8754
|
+
if (!best) return { start, end, d: "" };
|
|
8755
|
+
const points = best.filter((p, i) => i === 0 || p.x !== best[i - 1].x || p.y !== best[i - 1].y);
|
|
8756
|
+
return { start: points[0], end: points[points.length - 1], points, d: points.map((p, i) => `${i ? "L" : "M"} ${pointText(p)}`).join(" ") };
|
|
8757
|
+
}
|
|
8718
8758
|
|
|
8719
8759
|
// src/components/organisms/roadmap/Roadmap.tsx
|
|
8720
|
-
import { useMemo as useMemo5 } from "react";
|
|
8760
|
+
import { useMemo as useMemo5, useRef as useRef6 } from "react";
|
|
8721
8761
|
|
|
8722
8762
|
// src/components/organisms/roadmap/RoadmapNode.tsx
|
|
8723
8763
|
import { cva as cva14 } from "class-variance-authority";
|
|
@@ -8750,9 +8790,9 @@ function RoadmapLegendView({ legend, testIdPrefix = "roadmap" }) {
|
|
|
8750
8790
|
|
|
8751
8791
|
// src/components/organisms/roadmap/RoadmapNode.tsx
|
|
8752
8792
|
import { jsx as jsx66, jsxs as jsxs41 } from "react/jsx-runtime";
|
|
8753
|
-
var nodeVariants = cva14("relative flex min-w-0 flex-col justify-center rounded-[var(--radius)] border-2 px-
|
|
8793
|
+
var nodeVariants = cva14("relative flex min-w-0 flex-col justify-center rounded-[var(--radius)] border-2 px-1 py-3 text-center text-[length:var(--c-roadmap-font,12px)] leading-normal text-[var(--c-roadmap-node-fg)]", {
|
|
8754
8794
|
variants: {
|
|
8755
|
-
kind: { topic: "min-h-[49px] border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]", subtopic: "min-h-[49px] border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]", button: "min-h-[49px] border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]", label: "border-transparent bg-transparent", title: "border-transparent bg-transparent font-semibold
|
|
8795
|
+
kind: { topic: "min-h-[49px] border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]", subtopic: "min-h-[49px] border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]", button: "min-h-[49px] border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]", label: "border-transparent bg-transparent", title: "border-transparent bg-transparent font-semibold text-[length:var(--c-roadmap-title-font,18px)]", paragraph: "text-left border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]", legend: "border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)]" },
|
|
8756
8796
|
state: { todo: "", done: "[--c-roadmap-node-bg:var(--c-roadmap-done-bg)] [--c-roadmap-node-fg:var(--c-roadmap-done-fg)]", learning: "[--c-roadmap-node-bg:var(--c-roadmap-learning-bg)] [--c-roadmap-node-fg:var(--c-roadmap-learning-fg)]", skipped: "[--c-roadmap-node-bg:var(--c-roadmap-skipped-bg)] [--c-roadmap-node-fg:var(--c-roadmap-skipped-fg)]", locked: "opacity-60" }
|
|
8757
8797
|
}
|
|
8758
8798
|
});
|
|
@@ -8765,7 +8805,7 @@ function RoadmapNodeView({ node, state, badge, testIdPrefix, onNodeClick, onActi
|
|
|
8765
8805
|
const Box = wholeBox && node.href ? "a" : wholeBox && onNodeClick ? "button" : "div";
|
|
8766
8806
|
const primary = Box !== "div" ? label : node.href && !locked ? /* @__PURE__ */ jsx66("a", { href: node.href, className: interactiveClass, onClick: () => onNodeClick?.(node), children: label }) : onNodeClick && !locked ? /* @__PURE__ */ jsx66("button", { type: "button", className: interactiveClass, onClick: () => onNodeClick(node), children: label }) : label;
|
|
8767
8807
|
const icon = node.icon ?? (badge ? { name: badge.icon ?? "check", side: "right", tone: badge.tone } : void 0);
|
|
8768
|
-
return /* @__PURE__ */ jsx66("div", { style: toneStyle(nodeTone), className: "min-w-0", children: /* @__PURE__ */ jsxs41(Box, { "data-roadmap-node-box": node.id, href: Box === "a" ? node.href : void 0, type: Box === "button" ? "button" : void 0, onClick: Box !== "div" ? () => onNodeClick?.(node) : void 0, className: `${nodeVariants({ kind: node.kind, state })} w-full ${Box !== "div" ? focusClass : ""}`, "aria-disabled": locked || void 0, title: node.kind === "topic" || node.kind === "subtopic" ? node.description : void 0, children: [
|
|
8808
|
+
return /* @__PURE__ */ jsx66("div", { style: toneStyle(nodeTone), className: "min-w-0", children: /* @__PURE__ */ jsxs41(Box, { style: { borderColor: node.kind === "title" || node.kind === "label" ? "transparent" : "var(--c-roadmap-border)" }, "data-roadmap-node-box": node.id, href: Box === "a" ? node.href : void 0, type: Box === "button" ? "button" : void 0, onClick: Box !== "div" ? () => onNodeClick?.(node) : void 0, className: `${nodeVariants({ kind: node.kind, state })} w-full ${icon ? icon.side === "left" ? "pl-3" : "pr-3" : ""} ${Box !== "div" ? focusClass : ""}`, "aria-disabled": locked || void 0, title: node.kind === "topic" || node.kind === "subtopic" ? node.description : void 0, children: [
|
|
8769
8809
|
icon && /* @__PURE__ */ jsx66(RoadmapBadge, { name: icon.name, tone: icon.tone ?? nodeTone, label: badge?.label ?? icon.name, className: `absolute top-1/2 -translate-y-1/2 ${icon.side === "left" ? "left-0 -translate-x-1/2" : "right-0 translate-x-1/2"}` }),
|
|
8770
8810
|
primary,
|
|
8771
8811
|
node.description && node.kind === "paragraph" && /* @__PURE__ */ jsx66("p", { className: "mt-2 [overflow-wrap:anywhere]", children: node.description }),
|
|
@@ -8780,15 +8820,17 @@ function RoadmapNodeView({ node, state, badge, testIdPrefix, onNodeClick, onActi
|
|
|
8780
8820
|
// src/components/organisms/roadmap/RoadmapGroup.tsx
|
|
8781
8821
|
import { Fragment as Fragment10, jsx as jsx67, jsxs as jsxs42 } from "react/jsx-runtime";
|
|
8782
8822
|
function RoadmapGroupView({ group, testIdPrefix }) {
|
|
8823
|
+
const tone = resolveGroupTone(group);
|
|
8824
|
+
const appearance = tone === "accent" ? { "--c-roadmap-node-bg": "var(--c-roadmap-group-accent-bg)", "--c-roadmap-node-fg": "var(--c-roadmap-group-accent-fg)" } : toneStyle(tone);
|
|
8783
8825
|
return /* @__PURE__ */ jsx67(Fragment10, { children: groupColumnRuns(group).map((run, index) => /* @__PURE__ */ jsxs42(
|
|
8784
8826
|
"div",
|
|
8785
8827
|
{
|
|
8786
8828
|
"data-testid": `${testIdPrefix}-group`,
|
|
8787
8829
|
"data-group-id": group.id,
|
|
8788
|
-
style: {
|
|
8830
|
+
style: { borderColor: "var(--c-roadmap-border)", ...appearance, gridColumn: `${run.start} / span ${run.span}`, gridRow: `${group.from + 1} / span ${group.to - group.from + 1}` },
|
|
8789
8831
|
className: "pointer-events-none relative z-0 min-w-0 self-stretch rounded-[var(--radius)] border-2 border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)] text-[var(--c-roadmap-node-fg)]",
|
|
8790
8832
|
children: [
|
|
8791
|
-
index === 0 && group.title && /* @__PURE__ */ jsx67("span", { className: "absolute left-2 right-2 top-0 -translate-y-1/2 text-xs leading-normal", children: /* @__PURE__ */ jsx67("span", { className: "bg-[var(--c-roadmap-surface)] px-1 [
|
|
8833
|
+
index === 0 && group.title && /* @__PURE__ */ jsx67("span", { title: group.title, className: "absolute left-2 right-2 top-0 -translate-y-1/2 text-xs leading-normal", children: /* @__PURE__ */ jsx67("span", { className: "inline-block max-w-full overflow-hidden text-ellipsis whitespace-nowrap bg-[var(--c-roadmap-surface)] px-1 align-top text-[var(--c-roadmap-neutral-fg)]", children: group.title }) }),
|
|
8792
8834
|
index === 0 && group.description && /* @__PURE__ */ jsx67("span", { className: "sr-only", children: group.description })
|
|
8793
8835
|
]
|
|
8794
8836
|
},
|
|
@@ -8796,36 +8838,157 @@ function RoadmapGroupView({ group, testIdPrefix }) {
|
|
|
8796
8838
|
)) });
|
|
8797
8839
|
}
|
|
8798
8840
|
|
|
8799
|
-
// src/components/organisms/roadmap/
|
|
8841
|
+
// src/components/organisms/roadmap/RoadmapEdgeLayer.tsx
|
|
8842
|
+
import { useId as useId4, useEffect as useEffect10, useLayoutEffect as useLayoutEffect2, useRef as useRef5, useState as useState13 } from "react";
|
|
8800
8843
|
import { jsx as jsx68, jsxs as jsxs43 } from "react/jsx-runtime";
|
|
8801
|
-
function
|
|
8844
|
+
function Edge({ edge, path, instance, prefix }) {
|
|
8845
|
+
const pathRef = useRef5(null);
|
|
8846
|
+
const [midpoint, setMidpoint] = useState13();
|
|
8847
|
+
useLayoutEffect2(() => {
|
|
8848
|
+
const element = pathRef.current;
|
|
8849
|
+
if (edge.label && element?.getTotalLength && path.d) {
|
|
8850
|
+
const p = element.getPointAtLength(element.getTotalLength() / 2);
|
|
8851
|
+
setMidpoint({ x: p.x, y: p.y });
|
|
8852
|
+
} else setMidpoint(void 0);
|
|
8853
|
+
}, [path.d, edge.label]);
|
|
8854
|
+
const arrow = resolveEdgeArrow(edge), style = resolveEdgeStyle(edge);
|
|
8855
|
+
const marker = `${instance}-${edge.id}`;
|
|
8856
|
+
const color = edge.tone ? `var(--c-roadmap-${edge.tone}-edge)` : "var(--c-roadmap-edge)";
|
|
8857
|
+
return /* @__PURE__ */ jsxs43("g", { style: { color }, "data-roadmap-edge": edge.id, children: [
|
|
8858
|
+
arrow !== "none" && /* @__PURE__ */ jsx68("defs", { children: /* @__PURE__ */ jsx68("marker", { id: marker, viewBox: "0 0 8 8", refX: "8", refY: "4", markerWidth: "8", markerHeight: "8", markerUnits: "userSpaceOnUse", orient: "auto-start-reverse", children: /* @__PURE__ */ jsx68("path", { d: "M 0 0 L 8 4 L 0 8 Z", fill: "currentColor" }) }) }),
|
|
8859
|
+
/* @__PURE__ */ jsx68(
|
|
8860
|
+
"path",
|
|
8861
|
+
{
|
|
8862
|
+
ref: pathRef,
|
|
8863
|
+
"data-testid": `${prefix}-edge`,
|
|
8864
|
+
"data-edge-id": edge.id,
|
|
8865
|
+
"data-source": edge.source,
|
|
8866
|
+
"data-target": edge.target,
|
|
8867
|
+
d: path.d,
|
|
8868
|
+
fill: "none",
|
|
8869
|
+
stroke: "currentColor",
|
|
8870
|
+
strokeLinecap: "round",
|
|
8871
|
+
strokeLinejoin: "round",
|
|
8872
|
+
strokeDasharray: style === "solid" ? void 0 : style === "dotted" ? "0.8 4" : "0.8 8",
|
|
8873
|
+
className: "[stroke-width:var(--c-roadmap-edge-width,2px)]",
|
|
8874
|
+
markerStart: arrow === "both" ? `url(#${marker})` : void 0,
|
|
8875
|
+
markerEnd: arrow !== "none" ? `url(#${marker})` : void 0
|
|
8876
|
+
}
|
|
8877
|
+
),
|
|
8878
|
+
edge.label && midpoint && /* @__PURE__ */ jsx68("foreignObject", { "data-testid": `${prefix}-edge-label`, "data-edge-id": edge.id, x: midpoint.x, y: midpoint.y, width: "1", height: "1", overflow: "visible", children: /* @__PURE__ */ jsx68(
|
|
8879
|
+
"div",
|
|
8880
|
+
{
|
|
8881
|
+
className: "w-max max-w-[160px] -translate-x-1/2 -translate-y-1/2 rounded-full bg-[var(--c-roadmap-edge-label-bg)] px-2 py-0.5 text-center text-xs leading-normal text-[var(--c-roadmap-edge-label-fg)] [overflow-wrap:anywhere]",
|
|
8882
|
+
style: { boxShadow: "0 0 0 2px var(--c-roadmap-surface)" },
|
|
8883
|
+
children: edge.label
|
|
8884
|
+
}
|
|
8885
|
+
) })
|
|
8886
|
+
] });
|
|
8887
|
+
}
|
|
8888
|
+
function RoadmapEdgeLayer({ containerRef, document: documentModel, testIdPrefix, debugPerf }) {
|
|
8889
|
+
const instance = `roadmap-${useId4().replace(/:/g, "")}`;
|
|
8890
|
+
const [measured, setMeasured] = useState13([]);
|
|
8891
|
+
useEffect10(() => {
|
|
8892
|
+
const container = containerRef.current;
|
|
8893
|
+
if (!container || !documentModel.edges.length) {
|
|
8894
|
+
setMeasured([]);
|
|
8895
|
+
return;
|
|
8896
|
+
}
|
|
8897
|
+
let disposed = false, frame = 0;
|
|
8898
|
+
const observed = /* @__PURE__ */ new Set();
|
|
8899
|
+
const measure = () => {
|
|
8900
|
+
frame = 0;
|
|
8901
|
+
if (disposed) return;
|
|
8902
|
+
const started = performance.now();
|
|
8903
|
+
const root = container.getBoundingClientRect();
|
|
8904
|
+
const boxes = /* @__PURE__ */ new Map();
|
|
8905
|
+
for (const wrapper of container.querySelectorAll("[data-roadmap-node]")) {
|
|
8906
|
+
if (wrapper.closest("[data-roadmap-grid]") !== container) continue;
|
|
8907
|
+
const id = wrapper.dataset.roadmapNode;
|
|
8908
|
+
const border = Array.from(wrapper.querySelectorAll("[data-roadmap-node-box]")).find((el) => el.dataset.roadmapNodeBox === id);
|
|
8909
|
+
const element = border ?? wrapper.firstElementChild ?? wrapper;
|
|
8910
|
+
for (const target of [wrapper, element]) if (!observed.has(target)) {
|
|
8911
|
+
observer?.observe(target);
|
|
8912
|
+
observed.add(target);
|
|
8913
|
+
}
|
|
8914
|
+
const rect = element.getBoundingClientRect();
|
|
8915
|
+
boxes.set(id, { left: rect.left - root.left, right: rect.right - root.left, top: rect.top - root.top, bottom: rect.bottom - root.top });
|
|
8916
|
+
}
|
|
8917
|
+
for (const element of observed) if (!container.contains(element)) {
|
|
8918
|
+
observer?.unobserve(element);
|
|
8919
|
+
observed.delete(element);
|
|
8920
|
+
}
|
|
8921
|
+
const obstacles = [...boxes.values()];
|
|
8922
|
+
const next = documentModel.edges.flatMap((edge) => {
|
|
8923
|
+
const source = boxes.get(edge.source), target = boxes.get(edge.target);
|
|
8924
|
+
return source && target ? [{ edge, path: routeEdge(source, target, edge, obstacles) }] : [];
|
|
8925
|
+
});
|
|
8926
|
+
setMeasured(next);
|
|
8927
|
+
if (debugPerf) performance.measure("roadmap:edges", { start: started, end: performance.now() });
|
|
8928
|
+
};
|
|
8929
|
+
const schedule = () => {
|
|
8930
|
+
if (!disposed && !frame) frame = requestAnimationFrame(measure);
|
|
8931
|
+
};
|
|
8932
|
+
const observer = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(schedule);
|
|
8933
|
+
observer?.observe(container);
|
|
8934
|
+
const mutations = new MutationObserver((records) => {
|
|
8935
|
+
if (records.some((record) => (record.target instanceof Element ? record.target : record.target.parentElement)?.closest("[data-roadmap-node]"))) schedule();
|
|
8936
|
+
});
|
|
8937
|
+
mutations.observe(container, { childList: true, subtree: true, characterData: true, attributes: true });
|
|
8938
|
+
const fonts = container.ownerDocument.fonts;
|
|
8939
|
+
fonts?.ready.then(schedule);
|
|
8940
|
+
fonts?.addEventListener("loadingdone", schedule);
|
|
8941
|
+
schedule();
|
|
8942
|
+
return () => {
|
|
8943
|
+
disposed = true;
|
|
8944
|
+
cancelAnimationFrame(frame);
|
|
8945
|
+
observer?.disconnect();
|
|
8946
|
+
mutations.disconnect();
|
|
8947
|
+
fonts?.removeEventListener("loadingdone", schedule);
|
|
8948
|
+
};
|
|
8949
|
+
}, [containerRef, documentModel, debugPerf]);
|
|
8950
|
+
return /* @__PURE__ */ jsx68("svg", { "data-testid": `${testIdPrefix}-edges`, "aria-hidden": "true", className: "pointer-events-none print:hidden absolute inset-0 z-[1] h-full w-full overflow-visible", children: measured.map((item) => /* @__PURE__ */ jsx68(Edge, { ...item, instance, prefix: testIdPrefix }, item.edge.id)) });
|
|
8951
|
+
}
|
|
8952
|
+
|
|
8953
|
+
// src/components/organisms/roadmap/Roadmap.tsx
|
|
8954
|
+
import { jsx as jsx69, jsxs as jsxs44 } from "react/jsx-runtime";
|
|
8955
|
+
function Roadmap({ document: document2, state, renderNode, onNodeClick, onAction, testIdPrefix = "roadmap", className = "", ariaLabel, debugPerf, collapseEmptyColumns = true }) {
|
|
8956
|
+
const gridRef = useRef6(null);
|
|
8802
8957
|
const { placements } = useMemo5(() => placeNodes(document2), [document2]);
|
|
8958
|
+
const used = useMemo5(() => columnsUsed({ nodes: document2.nodes.filter((node) => !((node.span ?? 1) === 3 && (node.kind === "title" || node.kind === "label"))) }), [document2]);
|
|
8959
|
+
const weights = ["left", "center", "right"].map((column, index) => !collapseEmptyColumns || used.length === 0 || used.includes(column) ? index === 1 ? 1.25 : 1 : 0);
|
|
8960
|
+
const gridStyle = {
|
|
8961
|
+
gridTemplateColumns: weights.map((weight) => `minmax(0, ${weight}fr)`).join(" "),
|
|
8962
|
+
...weights.filter(Boolean).length === 1 ? { columnGap: 0 } : {}
|
|
8963
|
+
};
|
|
8803
8964
|
const badges = useMemo5(() => new Map(document2.legend?.entries.map((entry) => [entry.id, entry])), [document2.legend]);
|
|
8804
8965
|
const legendPlacement = document2.legend ? resolveLegendPlacement(document2.legend) : void 0;
|
|
8805
|
-
const legend = document2.legend && /* @__PURE__ */
|
|
8806
|
-
return /* @__PURE__ */
|
|
8807
|
-
legendPlacement === "top" && /* @__PURE__ */
|
|
8808
|
-
/* @__PURE__ */
|
|
8809
|
-
|
|
8966
|
+
const legend = document2.legend && /* @__PURE__ */ jsx69(RoadmapLegendView, { legend: document2.legend, testIdPrefix });
|
|
8967
|
+
return /* @__PURE__ */ jsx69("section", { "data-testid": testIdPrefix, "aria-label": ariaLabel ?? document2.title ?? "Roadmap", style: { containerType: "inline-size", containerName: "roadmap" }, className: `mx-auto w-full max-w-[1120px] min-w-0 bg-[var(--c-roadmap-surface)] text-[var(--c-roadmap-neutral-fg)] ${className}`, children: /* @__PURE__ */ jsxs44("div", { className: "dfl-roadmap-body min-w-0 p-[var(--c-roadmap-padding)]", children: [
|
|
8968
|
+
legendPlacement === "top" && /* @__PURE__ */ jsx69("div", { className: "mb-6", children: legend }),
|
|
8969
|
+
/* @__PURE__ */ jsxs44("div", { ref: gridRef, "data-roadmap-grid": "", "data-testid": `${testIdPrefix}-grid`, role: "list", "aria-label": "Roadmap nodes", style: gridStyle, className: "relative isolate grid min-w-0 grid-cols-3 items-center gap-x-[var(--c-roadmap-column-gap)] gap-y-2", children: [
|
|
8970
|
+
/* @__PURE__ */ jsx69(RoadmapEdgeLayer, { containerRef: gridRef, document: document2, testIdPrefix, debugPerf }),
|
|
8971
|
+
document2.groups.map((group) => /* @__PURE__ */ jsx69(RoadmapGroupView, { group, testIdPrefix }, group.id)),
|
|
8810
8972
|
placements.map(({ node, row, column }) => {
|
|
8811
8973
|
const resolvedState = resolveNodeState(node, state);
|
|
8812
|
-
return /* @__PURE__ */
|
|
8974
|
+
return /* @__PURE__ */ jsx69(
|
|
8813
8975
|
"div",
|
|
8814
8976
|
{
|
|
8815
8977
|
role: "listitem",
|
|
8816
8978
|
"data-testid": `${testIdPrefix}-node`,
|
|
8817
8979
|
"data-node-id": node.id,
|
|
8980
|
+
"data-roadmap-node": node.id,
|
|
8818
8981
|
"data-state": resolvedState,
|
|
8819
8982
|
style: { gridColumn: `${column.start} / span ${column.span}`, gridRow: `${row.start} / span 1` },
|
|
8820
|
-
className:
|
|
8821
|
-
children: node.kind === "legend" ? legendPlacement === "inline" ? legend : null : renderNode ? renderNode(node, resolvedState) : /* @__PURE__ */
|
|
8983
|
+
className: `relative z-10 min-w-0 py-2 ${node.icon || node.badge ? node.icon?.side === "left" ? "pl-3 pr-1" : "pl-1 pr-3" : "px-1"}`,
|
|
8984
|
+
children: node.kind === "legend" ? legendPlacement === "inline" ? legend : null : renderNode ? renderNode(node, resolvedState) : /* @__PURE__ */ jsx69(RoadmapNodeView, { node, state: resolvedState, badge: node.badge ? badges.get(node.badge) : void 0, onNodeClick, onAction, testIdPrefix })
|
|
8822
8985
|
},
|
|
8823
8986
|
node.id
|
|
8824
8987
|
);
|
|
8825
8988
|
})
|
|
8826
8989
|
] }),
|
|
8827
|
-
legendPlacement === "bottom" && /* @__PURE__ */
|
|
8828
|
-
] });
|
|
8990
|
+
legendPlacement === "bottom" && /* @__PURE__ */ jsx69("div", { className: "mt-6", children: legend })
|
|
8991
|
+
] }) });
|
|
8829
8992
|
}
|
|
8830
8993
|
|
|
8831
8994
|
// src/hooks/use-iframe-auth.ts
|
|
@@ -8853,7 +9016,7 @@ import {
|
|
|
8853
9016
|
useContext as useContext10,
|
|
8854
9017
|
useMemo as useMemo6
|
|
8855
9018
|
} from "react";
|
|
8856
|
-
import { jsx as
|
|
9019
|
+
import { jsx as jsx70 } from "react/jsx-runtime";
|
|
8857
9020
|
var FeatureFlagContext = createContext10({
|
|
8858
9021
|
flags: {},
|
|
8859
9022
|
isEnabled: () => false
|
|
@@ -8869,7 +9032,7 @@ var FeatureFlagProvider = ({
|
|
|
8869
9032
|
}),
|
|
8870
9033
|
[flags]
|
|
8871
9034
|
);
|
|
8872
|
-
return /* @__PURE__ */
|
|
9035
|
+
return /* @__PURE__ */ jsx70(FeatureFlagContext.Provider, { value, children });
|
|
8873
9036
|
};
|
|
8874
9037
|
var useFeatureFlag = (flag) => {
|
|
8875
9038
|
const { isEnabled } = useContext10(FeatureFlagContext);
|
package/dist/styles/theme.css
CHANGED
|
@@ -56,3 +56,32 @@ body {
|
|
|
56
56
|
.dark {
|
|
57
57
|
/* All tokens already render dark at :root. Intentionally empty. */
|
|
58
58
|
}
|
|
59
|
+
|
|
60
|
+
/* Roadmap sizes follow container width, including narrow embedded panels.
|
|
61
|
+
* Plain CSS supports Tailwind 3 and 4. Row spacing remains compact:
|
|
62
|
+
* 8px gap + two 8px node insets = 24px between node borders. */
|
|
63
|
+
.dfl-roadmap-body {
|
|
64
|
+
--c-roadmap-edge-width: 2px;
|
|
65
|
+
--c-roadmap-font: 12px;
|
|
66
|
+
--c-roadmap-title-font: 18px;
|
|
67
|
+
--c-roadmap-column-gap: 8px;
|
|
68
|
+
--c-roadmap-padding: 8px;
|
|
69
|
+
}
|
|
70
|
+
@container roadmap (min-width: 360px) {
|
|
71
|
+
.dfl-roadmap-body { --c-roadmap-padding: 12px; }
|
|
72
|
+
}
|
|
73
|
+
@container roadmap (min-width: 390px) {
|
|
74
|
+
.dfl-roadmap-body { --c-roadmap-font: 13px; --c-roadmap-title-font: 20px; --c-roadmap-column-gap: 12px; }
|
|
75
|
+
}
|
|
76
|
+
@container roadmap (min-width: 768px) {
|
|
77
|
+
.dfl-roadmap-body { --c-roadmap-edge-width: 3px; --c-roadmap-font: 15px; --c-roadmap-title-font: 22px; --c-roadmap-column-gap: 24px; --c-roadmap-padding: 16px; }
|
|
78
|
+
}
|
|
79
|
+
@container roadmap (min-width: 1024px) {
|
|
80
|
+
.dfl-roadmap-body { --c-roadmap-font: 17px; --c-roadmap-title-font: 24px; --c-roadmap-column-gap: 32px; --c-roadmap-padding: 24px; }
|
|
81
|
+
}
|
|
82
|
+
/* The map stops at 1120px; the outer viewport selects the wide rhythm. */
|
|
83
|
+
@media (min-width: 1280px) {
|
|
84
|
+
@container roadmap (min-width: 1120px) {
|
|
85
|
+
.dfl-roadmap-body { --c-roadmap-title-font: 28px; --c-roadmap-column-gap: 48px; }
|
|
86
|
+
}
|
|
87
|
+
}
|
package/dist/styles/tokens.css
CHANGED
|
@@ -1709,6 +1709,19 @@
|
|
|
1709
1709
|
|
|
1710
1710
|
/* Origin: agent — Roadmap. Q1: dark default and dark only. */
|
|
1711
1711
|
:root {
|
|
1712
|
+
--c-roadmap-group-accent-bg: var(--s-surface-elevated);
|
|
1713
|
+
--c-roadmap-group-accent-fg: var(--s-ink-primary);
|
|
1714
|
+
--c-roadmap-edge: var(--s-ink-muted);
|
|
1715
|
+
--c-roadmap-edge-label-bg: var(--s-surface-elevated);
|
|
1716
|
+
--c-roadmap-edge-label-fg: var(--s-ink-secondary);
|
|
1717
|
+
--c-roadmap-primary-edge: var(--s-brand-solid);
|
|
1718
|
+
--c-roadmap-secondary-edge: var(--s-ink-secondary);
|
|
1719
|
+
--c-roadmap-accent-edge: var(--s-ink-primary);
|
|
1720
|
+
--c-roadmap-muted-edge: var(--s-ink-muted);
|
|
1721
|
+
--c-roadmap-neutral-edge: var(--s-ink-secondary);
|
|
1722
|
+
--c-roadmap-success-edge: var(--s-success-fg);
|
|
1723
|
+
--c-roadmap-info-edge: var(--s-info-fg);
|
|
1724
|
+
--c-roadmap-danger-edge: var(--s-danger-fg);
|
|
1712
1725
|
--c-roadmap-surface: var(--s-surface-page);
|
|
1713
1726
|
--c-roadmap-border: var(--s-border-strong);
|
|
1714
1727
|
--c-roadmap-focus: var(--s-border-focus);
|