@devfellowship/components 3.2.4 → 3.4.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 +648 -11
- package/dist/index.d.cts +1074 -1
- package/dist/index.d.ts +1074 -1
- package/dist/index.js +584 -4
- package/dist/styles/theme-mappings.css +27 -0
- package/dist/styles/tokens.css +29 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -118,7 +118,14 @@ __export(src_exports, {
|
|
|
118
118
|
ContextMenuSubContent: () => ContextMenuSubContent,
|
|
119
119
|
ContextMenuSubTrigger: () => ContextMenuSubTrigger,
|
|
120
120
|
ContextMenuTrigger: () => ContextMenuTrigger,
|
|
121
|
+
DEFAULT_EDGE_ARROW: () => DEFAULT_EDGE_ARROW,
|
|
122
|
+
DEFAULT_EDGE_ROUTE: () => DEFAULT_EDGE_ROUTE,
|
|
123
|
+
DEFAULT_EDGE_STYLE: () => DEFAULT_EDGE_STYLE,
|
|
124
|
+
DEFAULT_GROUP_TONE: () => DEFAULT_GROUP_TONE,
|
|
125
|
+
DEFAULT_LEGEND_PLACEMENT: () => DEFAULT_LEGEND_PLACEMENT,
|
|
121
126
|
DEFAULT_NAME_COL_WIDTH: () => DEFAULT_NAME_COL_WIDTH,
|
|
127
|
+
DEFAULT_NODE_SPAN: () => DEFAULT_NODE_SPAN,
|
|
128
|
+
DEFAULT_NODE_STATE: () => DEFAULT_NODE_STATE,
|
|
122
129
|
DflRemote: () => DflRemote,
|
|
123
130
|
Dialog: () => Dialog,
|
|
124
131
|
DialogClose: () => DialogClose,
|
|
@@ -196,6 +203,7 @@ __export(src_exports, {
|
|
|
196
203
|
MenubarSubContent: () => MenubarSubContent,
|
|
197
204
|
MenubarSubTrigger: () => MenubarSubTrigger,
|
|
198
205
|
MenubarTrigger: () => MenubarTrigger,
|
|
206
|
+
NODE_KIND_DEFAULT_TONE: () => NODE_KIND_DEFAULT_TONE,
|
|
199
207
|
NavigationMenu: () => NavigationMenu,
|
|
200
208
|
NavigationMenuContent: () => NavigationMenuContent,
|
|
201
209
|
NavigationMenuIndicator: () => NavigationMenuIndicator,
|
|
@@ -219,12 +227,24 @@ __export(src_exports, {
|
|
|
219
227
|
Progress: () => Progress,
|
|
220
228
|
ProtectedRoute: () => ProtectedRoute,
|
|
221
229
|
PublishDrawer: () => PublishDrawer,
|
|
230
|
+
ROADMAP_ARROWS: () => ROADMAP_ARROWS,
|
|
231
|
+
ROADMAP_COLUMNS: () => ROADMAP_COLUMNS,
|
|
232
|
+
ROADMAP_EDGE_LABEL_MAX: () => ROADMAP_EDGE_LABEL_MAX,
|
|
233
|
+
ROADMAP_EDGE_ROUTES: () => ROADMAP_EDGE_ROUTES,
|
|
234
|
+
ROADMAP_EDGE_STYLES: () => ROADMAP_EDGE_STYLES,
|
|
235
|
+
ROADMAP_LABEL_MAX: () => ROADMAP_LABEL_MAX,
|
|
236
|
+
ROADMAP_LEGEND_PLACEMENTS: () => ROADMAP_LEGEND_PLACEMENTS,
|
|
237
|
+
ROADMAP_NODE_ID_PATTERN: () => ROADMAP_NODE_ID_PATTERN,
|
|
238
|
+
ROADMAP_NODE_KINDS: () => ROADMAP_NODE_KINDS,
|
|
239
|
+
ROADMAP_NODE_STATES: () => ROADMAP_NODE_STATES,
|
|
240
|
+
ROADMAP_TONES: () => ROADMAP_TONES,
|
|
222
241
|
RadioGroup: () => RadioGroup4,
|
|
223
242
|
RadioGroupItem: () => RadioGroupItem,
|
|
224
243
|
RadioGroupRow: () => RadioGroupRow,
|
|
225
244
|
ResizableHandle: () => ResizableHandle,
|
|
226
245
|
ResizablePanel: () => ResizablePanel,
|
|
227
246
|
ResizablePanelGroup: () => ResizablePanelGroup,
|
|
247
|
+
Roadmap: () => Roadmap,
|
|
228
248
|
ScrollArea: () => ScrollArea,
|
|
229
249
|
ScrollBar: () => ScrollBar,
|
|
230
250
|
Select: () => Select,
|
|
@@ -312,19 +332,55 @@ __export(src_exports, {
|
|
|
312
332
|
buttonVariants: () => buttonVariants,
|
|
313
333
|
clampPct: () => clampPct,
|
|
314
334
|
cn: () => cn,
|
|
335
|
+
columnIndex: () => columnIndex,
|
|
336
|
+
columnsCovered: () => columnsCovered,
|
|
337
|
+
columnsUsed: () => columnsUsed,
|
|
315
338
|
filterPublishableAccounts: () => filterPublishableAccounts,
|
|
339
|
+
firstFreeRow: () => firstFreeRow,
|
|
316
340
|
getInitials: () => getInitials,
|
|
341
|
+
gridColumnFor: () => gridColumnFor,
|
|
342
|
+
groupColumnRuns: () => groupColumnRuns,
|
|
343
|
+
groupRanges: () => groupRanges,
|
|
317
344
|
iconButtonVariants: () => iconButtonVariants,
|
|
318
345
|
isAllowedOrigin: () => isAllowedOrigin,
|
|
319
346
|
kbdVariants: () => kbdVariants,
|
|
320
347
|
labelVariants: () => labelVariants,
|
|
348
|
+
maxRowOf: () => maxRowOf,
|
|
321
349
|
memberHueIndex: () => memberHueIndex,
|
|
322
350
|
memberHueVar: () => memberHueVar,
|
|
323
351
|
navigationMenuTriggerStyle: () => navigationMenuTriggerStyle,
|
|
352
|
+
nodesByRow: () => nodesByRow,
|
|
353
|
+
parseRoadmapDocument: () => parseRoadmapDocument,
|
|
354
|
+
parseRoadmapStateOverlay: () => parseRoadmapStateOverlay,
|
|
324
355
|
parseTags: () => parseTags,
|
|
356
|
+
placeNodes: () => placeNodes,
|
|
357
|
+
resolveEdgeArrow: () => resolveEdgeArrow,
|
|
358
|
+
resolveEdgeRoute: () => resolveEdgeRoute,
|
|
359
|
+
resolveEdgeStyle: () => resolveEdgeStyle,
|
|
360
|
+
resolveGroupColumns: () => resolveGroupColumns,
|
|
361
|
+
resolveGroupTone: () => resolveGroupTone,
|
|
362
|
+
resolveLegendPlacement: () => resolveLegendPlacement,
|
|
325
363
|
resolveNameColWidth: () => resolveNameColWidth,
|
|
364
|
+
resolveNodeSpan: () => resolveNodeSpan,
|
|
365
|
+
resolveNodeState: () => resolveNodeState,
|
|
366
|
+
resolveNodeTone: () => resolveNodeTone,
|
|
326
367
|
resolveWeekCount: () => resolveWeekCount,
|
|
327
368
|
resolveWeekLabels: () => resolveWeekLabels,
|
|
369
|
+
roadmapActionSchema: () => roadmapActionSchema,
|
|
370
|
+
roadmapDocumentSchema: () => roadmapDocumentSchema,
|
|
371
|
+
roadmapEdgeSchema: () => roadmapEdgeSchema,
|
|
372
|
+
roadmapGroupSchema: () => roadmapGroupSchema,
|
|
373
|
+
roadmapIconSchema: () => roadmapIconSchema,
|
|
374
|
+
roadmapLegendEntrySchema: () => roadmapLegendEntrySchema,
|
|
375
|
+
roadmapLegendSchema: () => roadmapLegendSchema,
|
|
376
|
+
roadmapLinkSchema: () => roadmapLinkSchema,
|
|
377
|
+
roadmapNodeSchema: () => roadmapNodeSchema,
|
|
378
|
+
roadmapNodeStateSchema: () => roadmapNodeStateSchema,
|
|
379
|
+
roadmapStateOverlaySchema: () => roadmapStateOverlaySchema,
|
|
380
|
+
roadmapStateSchema: () => roadmapStateSchema,
|
|
381
|
+
rowsOf: () => rowsOf,
|
|
382
|
+
safeParseRoadmapDocument: () => safeParseRoadmapDocument,
|
|
383
|
+
safeParseRoadmapStateOverlay: () => safeParseRoadmapStateOverlay,
|
|
328
384
|
stageProgress: () => stageProgress,
|
|
329
385
|
toast: () => toast,
|
|
330
386
|
toggleVariants: () => toggleVariants,
|
|
@@ -338,6 +394,7 @@ __export(src_exports, {
|
|
|
338
394
|
useSession: () => useSession,
|
|
339
395
|
useSidebar: () => useSidebar,
|
|
340
396
|
useToast: () => useToast,
|
|
397
|
+
validateNoOverlap: () => validateNoOverlap,
|
|
341
398
|
validatePublishForm: () => validatePublishForm
|
|
342
399
|
});
|
|
343
400
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -8637,17 +8694,540 @@ function PublishDrawer({
|
|
|
8637
8694
|
) });
|
|
8638
8695
|
}
|
|
8639
8696
|
|
|
8640
|
-
// src/
|
|
8697
|
+
// src/components/organisms/roadmap/contract.ts
|
|
8698
|
+
var import_zod = require("zod");
|
|
8699
|
+
var ROADMAP_COLUMNS = ["left", "center", "right"];
|
|
8700
|
+
var ROADMAP_TONES = [
|
|
8701
|
+
"primary",
|
|
8702
|
+
"secondary",
|
|
8703
|
+
"accent",
|
|
8704
|
+
"muted",
|
|
8705
|
+
"success",
|
|
8706
|
+
"info",
|
|
8707
|
+
"danger",
|
|
8708
|
+
"neutral"
|
|
8709
|
+
];
|
|
8710
|
+
var ROADMAP_NODE_KINDS = [
|
|
8711
|
+
"topic",
|
|
8712
|
+
"subtopic",
|
|
8713
|
+
"label",
|
|
8714
|
+
"title",
|
|
8715
|
+
"paragraph",
|
|
8716
|
+
"button",
|
|
8717
|
+
"legend"
|
|
8718
|
+
];
|
|
8719
|
+
var ROADMAP_NODE_STATES = ["todo", "done", "learning", "skipped", "locked"];
|
|
8720
|
+
var ROADMAP_EDGE_STYLES = ["solid", "dashed", "dotted"];
|
|
8721
|
+
var ROADMAP_EDGE_ROUTES = ["auto", "straight", "elbow", "curve"];
|
|
8722
|
+
var ROADMAP_ARROWS = ["none", "end", "both"];
|
|
8723
|
+
var ROADMAP_LEGEND_PLACEMENTS = ["top", "bottom", "inline"];
|
|
8724
|
+
var DEFAULT_NODE_SPAN = 1;
|
|
8725
|
+
var DEFAULT_EDGE_STYLE = "dashed";
|
|
8726
|
+
var DEFAULT_EDGE_ROUTE = "auto";
|
|
8727
|
+
var DEFAULT_EDGE_ARROW = "none";
|
|
8728
|
+
var DEFAULT_GROUP_TONE = "neutral";
|
|
8729
|
+
var DEFAULT_LEGEND_PLACEMENT = "top";
|
|
8730
|
+
var DEFAULT_NODE_STATE = "todo";
|
|
8731
|
+
var NODE_KIND_DEFAULT_TONE = {
|
|
8732
|
+
topic: "primary",
|
|
8733
|
+
subtopic: "secondary",
|
|
8734
|
+
button: "primary",
|
|
8735
|
+
label: "neutral",
|
|
8736
|
+
title: "neutral",
|
|
8737
|
+
paragraph: "neutral",
|
|
8738
|
+
legend: "neutral"
|
|
8739
|
+
};
|
|
8740
|
+
var resolveNodeSpan = (node) => node.span ?? DEFAULT_NODE_SPAN;
|
|
8741
|
+
var resolveNodeTone = (node) => node.tone ?? NODE_KIND_DEFAULT_TONE[node.kind];
|
|
8742
|
+
var resolveEdgeStyle = (edge) => edge.style ?? DEFAULT_EDGE_STYLE;
|
|
8743
|
+
var resolveEdgeRoute = (edge) => edge.route ?? DEFAULT_EDGE_ROUTE;
|
|
8744
|
+
var resolveEdgeArrow = (edge) => edge.arrow ?? DEFAULT_EDGE_ARROW;
|
|
8745
|
+
var resolveGroupTone = (group) => group.tone ?? DEFAULT_GROUP_TONE;
|
|
8746
|
+
var resolveGroupColumns = (group) => group.columns && group.columns.length > 0 ? group.columns : [...ROADMAP_COLUMNS];
|
|
8747
|
+
var resolveLegendPlacement = (legend) => legend.placement ?? DEFAULT_LEGEND_PLACEMENT;
|
|
8748
|
+
var resolveNodeState = (node, overlay) => overlay?.nodes[node.id] ?? node.state ?? DEFAULT_NODE_STATE;
|
|
8749
|
+
var ROADMAP_NODE_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
|
|
8750
|
+
var ROADMAP_EDGE_LABEL_MAX = 40;
|
|
8751
|
+
var ROADMAP_LABEL_MAX = 200;
|
|
8752
|
+
var columnSchema = import_zod.z.enum(ROADMAP_COLUMNS);
|
|
8753
|
+
var toneSchema = import_zod.z.enum(ROADMAP_TONES);
|
|
8754
|
+
var idSchema = import_zod.z.string().regex(ROADMAP_NODE_ID_PATTERN, "must match /^[A-Za-z0-9_-]{1,64}$/");
|
|
8755
|
+
var metaSchema = import_zod.z.record(import_zod.z.unknown());
|
|
8756
|
+
var roadmapIconSchema = import_zod.z.object({
|
|
8757
|
+
name: import_zod.z.string().min(1).max(64),
|
|
8758
|
+
side: import_zod.z.enum(["left", "right"]),
|
|
8759
|
+
tone: toneSchema.optional()
|
|
8760
|
+
}).strict();
|
|
8761
|
+
var roadmapActionSchema = import_zod.z.object({
|
|
8762
|
+
label: import_zod.z.string().min(1).max(80),
|
|
8763
|
+
href: import_zod.z.string().min(1).optional(),
|
|
8764
|
+
actionId: import_zod.z.string().min(1).optional(),
|
|
8765
|
+
tone: toneSchema.optional()
|
|
8766
|
+
}).strict().superRefine((action, ctx) => {
|
|
8767
|
+
const given = [action.href, action.actionId].filter((v) => v !== void 0).length;
|
|
8768
|
+
if (given !== 1) {
|
|
8769
|
+
ctx.addIssue({
|
|
8770
|
+
code: import_zod.z.ZodIssueCode.custom,
|
|
8771
|
+
path: ["href"],
|
|
8772
|
+
message: "an action needs exactly one of `href` or `actionId`"
|
|
8773
|
+
});
|
|
8774
|
+
}
|
|
8775
|
+
});
|
|
8776
|
+
var roadmapLinkSchema = import_zod.z.object({ label: import_zod.z.string().min(1).max(120), href: import_zod.z.string().min(1) }).strict();
|
|
8777
|
+
var roadmapNodeStateSchema = import_zod.z.enum(ROADMAP_NODE_STATES);
|
|
8778
|
+
var roadmapNodeSchema = import_zod.z.object({
|
|
8779
|
+
id: idSchema,
|
|
8780
|
+
column: columnSchema,
|
|
8781
|
+
order: import_zod.z.number().int().min(0),
|
|
8782
|
+
span: import_zod.z.union([import_zod.z.literal(1), import_zod.z.literal(2), import_zod.z.literal(3)]).optional(),
|
|
8783
|
+
kind: import_zod.z.enum(ROADMAP_NODE_KINDS),
|
|
8784
|
+
label: import_zod.z.string().min(1).max(ROADMAP_LABEL_MAX),
|
|
8785
|
+
description: import_zod.z.string().max(2e3).optional(),
|
|
8786
|
+
tone: toneSchema.optional(),
|
|
8787
|
+
icon: roadmapIconSchema.optional(),
|
|
8788
|
+
badge: import_zod.z.string().min(1).optional(),
|
|
8789
|
+
state: roadmapNodeStateSchema.optional(),
|
|
8790
|
+
action: roadmapActionSchema.optional(),
|
|
8791
|
+
href: import_zod.z.string().min(1).optional(),
|
|
8792
|
+
links: import_zod.z.array(roadmapLinkSchema).optional(),
|
|
8793
|
+
meta: metaSchema.optional()
|
|
8794
|
+
}).strict();
|
|
8795
|
+
var roadmapEdgeSchema = import_zod.z.object({
|
|
8796
|
+
id: idSchema,
|
|
8797
|
+
source: import_zod.z.string().min(1),
|
|
8798
|
+
target: import_zod.z.string().min(1),
|
|
8799
|
+
style: import_zod.z.enum(ROADMAP_EDGE_STYLES).optional(),
|
|
8800
|
+
label: import_zod.z.string().min(1).max(ROADMAP_EDGE_LABEL_MAX).optional(),
|
|
8801
|
+
route: import_zod.z.enum(ROADMAP_EDGE_ROUTES).optional(),
|
|
8802
|
+
arrow: import_zod.z.enum(ROADMAP_ARROWS).optional(),
|
|
8803
|
+
tone: toneSchema.optional()
|
|
8804
|
+
}).strict();
|
|
8805
|
+
var roadmapGroupSchema = import_zod.z.object({
|
|
8806
|
+
id: idSchema,
|
|
8807
|
+
title: import_zod.z.string().min(1).max(ROADMAP_LABEL_MAX).optional(),
|
|
8808
|
+
tone: toneSchema.optional(),
|
|
8809
|
+
from: import_zod.z.number().int().min(0),
|
|
8810
|
+
to: import_zod.z.number().int().min(0),
|
|
8811
|
+
columns: import_zod.z.array(columnSchema).min(1).max(3).optional(),
|
|
8812
|
+
description: import_zod.z.string().max(2e3).optional()
|
|
8813
|
+
}).strict();
|
|
8814
|
+
var roadmapLegendEntrySchema = import_zod.z.object({
|
|
8815
|
+
id: idSchema,
|
|
8816
|
+
label: import_zod.z.string().min(1).max(ROADMAP_LABEL_MAX),
|
|
8817
|
+
tone: toneSchema,
|
|
8818
|
+
icon: import_zod.z.string().min(1).max(64).optional()
|
|
8819
|
+
}).strict();
|
|
8820
|
+
var roadmapLegendSchema = import_zod.z.object({
|
|
8821
|
+
entries: import_zod.z.array(roadmapLegendEntrySchema).min(1),
|
|
8822
|
+
placement: import_zod.z.enum(ROADMAP_LEGEND_PLACEMENTS).optional()
|
|
8823
|
+
}).strict();
|
|
8824
|
+
function columnsCovered(node) {
|
|
8825
|
+
const span = node.span ?? DEFAULT_NODE_SPAN;
|
|
8826
|
+
if (span === 3) return [...ROADMAP_COLUMNS];
|
|
8827
|
+
const start = ROADMAP_COLUMNS.indexOf(node.column);
|
|
8828
|
+
return ROADMAP_COLUMNS.slice(start, start + span);
|
|
8829
|
+
}
|
|
8830
|
+
var roadmapDocumentBaseSchema = import_zod.z.object({
|
|
8831
|
+
version: import_zod.z.literal("roadmap/v1"),
|
|
8832
|
+
title: import_zod.z.string().min(1).max(ROADMAP_LABEL_MAX).optional(),
|
|
8833
|
+
direction: import_zod.z.literal("down"),
|
|
8834
|
+
columns: import_zod.z.literal(3),
|
|
8835
|
+
nodes: import_zod.z.array(roadmapNodeSchema),
|
|
8836
|
+
edges: import_zod.z.array(roadmapEdgeSchema),
|
|
8837
|
+
groups: import_zod.z.array(roadmapGroupSchema),
|
|
8838
|
+
legend: roadmapLegendSchema.optional(),
|
|
8839
|
+
meta: metaSchema.optional()
|
|
8840
|
+
}).strict();
|
|
8841
|
+
var roadmapDocumentSchema = roadmapDocumentBaseSchema.superRefine((doc, ctx) => {
|
|
8842
|
+
const nodeIndexById = /* @__PURE__ */ new Map();
|
|
8843
|
+
doc.nodes.forEach((node, i) => {
|
|
8844
|
+
if (nodeIndexById.has(node.id)) {
|
|
8845
|
+
ctx.addIssue({
|
|
8846
|
+
code: import_zod.z.ZodIssueCode.custom,
|
|
8847
|
+
path: ["nodes", i, "id"],
|
|
8848
|
+
message: `duplicate node id "${node.id}" (first seen at nodes[${nodeIndexById.get(node.id)}])`
|
|
8849
|
+
});
|
|
8850
|
+
return;
|
|
8851
|
+
}
|
|
8852
|
+
nodeIndexById.set(node.id, i);
|
|
8853
|
+
});
|
|
8854
|
+
const cellOwner = /* @__PURE__ */ new Map();
|
|
8855
|
+
doc.nodes.forEach((node, i) => {
|
|
8856
|
+
const span = node.span ?? DEFAULT_NODE_SPAN;
|
|
8857
|
+
if (span === 2 && node.column === "right") {
|
|
8858
|
+
ctx.addIssue({
|
|
8859
|
+
code: import_zod.z.ZodIssueCode.custom,
|
|
8860
|
+
path: ["nodes", i, "span"],
|
|
8861
|
+
message: 'a `span: 2` node at column "right" runs past the last track'
|
|
8862
|
+
});
|
|
8863
|
+
return;
|
|
8864
|
+
}
|
|
8865
|
+
for (const column of columnsCovered(node)) {
|
|
8866
|
+
const key = `${column}:${node.order}`;
|
|
8867
|
+
const owner = cellOwner.get(key);
|
|
8868
|
+
if (owner !== void 0) {
|
|
8869
|
+
ctx.addIssue({
|
|
8870
|
+
code: import_zod.z.ZodIssueCode.custom,
|
|
8871
|
+
path: ["nodes", i, "order"],
|
|
8872
|
+
message: `cell (${column}, ${node.order}) is already taken by nodes[${owner}] ("${doc.nodes[owner].id}")`
|
|
8873
|
+
});
|
|
8874
|
+
continue;
|
|
8875
|
+
}
|
|
8876
|
+
cellOwner.set(key, i);
|
|
8877
|
+
}
|
|
8878
|
+
});
|
|
8879
|
+
const edgeIds = /* @__PURE__ */ new Set();
|
|
8880
|
+
doc.edges.forEach((edge, i) => {
|
|
8881
|
+
if (edgeIds.has(edge.id)) {
|
|
8882
|
+
ctx.addIssue({
|
|
8883
|
+
code: import_zod.z.ZodIssueCode.custom,
|
|
8884
|
+
path: ["edges", i, "id"],
|
|
8885
|
+
message: `duplicate edge id "${edge.id}"`
|
|
8886
|
+
});
|
|
8887
|
+
}
|
|
8888
|
+
edgeIds.add(edge.id);
|
|
8889
|
+
if (!nodeIndexById.has(edge.source)) {
|
|
8890
|
+
ctx.addIssue({
|
|
8891
|
+
code: import_zod.z.ZodIssueCode.custom,
|
|
8892
|
+
path: ["edges", i, "source"],
|
|
8893
|
+
message: `edge source "${edge.source}" is not a node in this document`
|
|
8894
|
+
});
|
|
8895
|
+
}
|
|
8896
|
+
if (!nodeIndexById.has(edge.target)) {
|
|
8897
|
+
ctx.addIssue({
|
|
8898
|
+
code: import_zod.z.ZodIssueCode.custom,
|
|
8899
|
+
path: ["edges", i, "target"],
|
|
8900
|
+
message: `edge target "${edge.target}" is not a node in this document`
|
|
8901
|
+
});
|
|
8902
|
+
}
|
|
8903
|
+
if (edge.source === edge.target) {
|
|
8904
|
+
ctx.addIssue({
|
|
8905
|
+
code: import_zod.z.ZodIssueCode.custom,
|
|
8906
|
+
path: ["edges", i, "target"],
|
|
8907
|
+
message: `edge "${edge.id}" is a self-loop`
|
|
8908
|
+
});
|
|
8909
|
+
}
|
|
8910
|
+
});
|
|
8911
|
+
const groupIds = /* @__PURE__ */ new Set();
|
|
8912
|
+
doc.groups.forEach((group, i) => {
|
|
8913
|
+
if (groupIds.has(group.id)) {
|
|
8914
|
+
ctx.addIssue({
|
|
8915
|
+
code: import_zod.z.ZodIssueCode.custom,
|
|
8916
|
+
path: ["groups", i, "id"],
|
|
8917
|
+
message: `duplicate group id "${group.id}"`
|
|
8918
|
+
});
|
|
8919
|
+
}
|
|
8920
|
+
groupIds.add(group.id);
|
|
8921
|
+
if (group.from > group.to) {
|
|
8922
|
+
ctx.addIssue({
|
|
8923
|
+
code: import_zod.z.ZodIssueCode.custom,
|
|
8924
|
+
path: ["groups", i, "to"],
|
|
8925
|
+
message: `group "${group.id}" has from (${group.from}) greater than to (${group.to})`
|
|
8926
|
+
});
|
|
8927
|
+
}
|
|
8928
|
+
});
|
|
8929
|
+
for (let i = 0; i < doc.groups.length; i += 1) {
|
|
8930
|
+
const a = doc.groups[i];
|
|
8931
|
+
if (a.from > a.to) continue;
|
|
8932
|
+
const aColumns = new Set(resolveGroupColumns(a));
|
|
8933
|
+
for (let j = i + 1; j < doc.groups.length; j += 1) {
|
|
8934
|
+
const b = doc.groups[j];
|
|
8935
|
+
if (b.from > b.to) continue;
|
|
8936
|
+
const rowsOverlap = a.from <= b.to && b.from <= a.to;
|
|
8937
|
+
if (!rowsOverlap) continue;
|
|
8938
|
+
const shared = resolveGroupColumns(b).filter((c) => aColumns.has(c));
|
|
8939
|
+
if (shared.length === 0) continue;
|
|
8940
|
+
ctx.addIssue({
|
|
8941
|
+
code: import_zod.z.ZodIssueCode.custom,
|
|
8942
|
+
path: ["groups", j, "from"],
|
|
8943
|
+
message: `group "${b.id}" overlaps rows ${a.from}..${a.to} of group "${a.id}" on column(s) ${shared.join(", ")}`
|
|
8944
|
+
});
|
|
8945
|
+
}
|
|
8946
|
+
}
|
|
8947
|
+
const legendIds = /* @__PURE__ */ new Set();
|
|
8948
|
+
doc.legend?.entries.forEach((entry, i) => {
|
|
8949
|
+
if (legendIds.has(entry.id)) {
|
|
8950
|
+
ctx.addIssue({
|
|
8951
|
+
code: import_zod.z.ZodIssueCode.custom,
|
|
8952
|
+
path: ["legend", "entries", i, "id"],
|
|
8953
|
+
message: `duplicate legend entry id "${entry.id}"`
|
|
8954
|
+
});
|
|
8955
|
+
}
|
|
8956
|
+
legendIds.add(entry.id);
|
|
8957
|
+
});
|
|
8958
|
+
doc.nodes.forEach((node, i) => {
|
|
8959
|
+
if (node.badge === void 0) return;
|
|
8960
|
+
if (!legendIds.has(node.badge)) {
|
|
8961
|
+
ctx.addIssue({
|
|
8962
|
+
code: import_zod.z.ZodIssueCode.custom,
|
|
8963
|
+
path: ["nodes", i, "badge"],
|
|
8964
|
+
message: `badge "${node.badge}" is not a legend entry of this document`
|
|
8965
|
+
});
|
|
8966
|
+
}
|
|
8967
|
+
});
|
|
8968
|
+
});
|
|
8969
|
+
var roadmapStateOverlaySchema = import_zod.z.object({
|
|
8970
|
+
version: import_zod.z.literal("roadmap-state/v1"),
|
|
8971
|
+
nodes: import_zod.z.record(roadmapNodeStateSchema)
|
|
8972
|
+
}).strict();
|
|
8973
|
+
var roadmapStateSchema = roadmapStateOverlaySchema;
|
|
8974
|
+
function parseRoadmapDocument(input) {
|
|
8975
|
+
return roadmapDocumentSchema.parse(input);
|
|
8976
|
+
}
|
|
8977
|
+
function safeParseRoadmapDocument(input) {
|
|
8978
|
+
return roadmapDocumentSchema.safeParse(input);
|
|
8979
|
+
}
|
|
8980
|
+
function parseRoadmapStateOverlay(input) {
|
|
8981
|
+
return roadmapStateOverlaySchema.parse(input);
|
|
8982
|
+
}
|
|
8983
|
+
function safeParseRoadmapStateOverlay(input) {
|
|
8984
|
+
return roadmapStateOverlaySchema.safeParse(input);
|
|
8985
|
+
}
|
|
8986
|
+
|
|
8987
|
+
// src/components/organisms/roadmap/layout.ts
|
|
8988
|
+
function columnIndex(column) {
|
|
8989
|
+
return ROADMAP_COLUMNS.indexOf(column);
|
|
8990
|
+
}
|
|
8991
|
+
function gridColumnFor(node) {
|
|
8992
|
+
const span = resolveNodeSpan(node);
|
|
8993
|
+
if (span === 3) return { start: 1, span: 3 };
|
|
8994
|
+
return { start: columnIndex(node.column) + 1, span };
|
|
8995
|
+
}
|
|
8996
|
+
function rowsOf(doc) {
|
|
8997
|
+
const rows = /* @__PURE__ */ new Set();
|
|
8998
|
+
for (const node of doc.nodes) rows.add(node.order);
|
|
8999
|
+
return [...rows].sort((a, b) => a - b);
|
|
9000
|
+
}
|
|
9001
|
+
function maxRowOf(doc) {
|
|
9002
|
+
return doc.nodes.reduce((max, node) => Math.max(max, node.order), -1);
|
|
9003
|
+
}
|
|
9004
|
+
function nodesByRow(doc) {
|
|
9005
|
+
const byRow = /* @__PURE__ */ new Map();
|
|
9006
|
+
for (const node of doc.nodes) {
|
|
9007
|
+
const bucket = byRow.get(node.order);
|
|
9008
|
+
if (bucket) bucket.push(node);
|
|
9009
|
+
else byRow.set(node.order, [node]);
|
|
9010
|
+
}
|
|
9011
|
+
for (const bucket of byRow.values()) {
|
|
9012
|
+
bucket.sort((a, b) => {
|
|
9013
|
+
const byColumn = columnIndex(a.column) - columnIndex(b.column);
|
|
9014
|
+
return byColumn !== 0 ? byColumn : a.id.localeCompare(b.id);
|
|
9015
|
+
});
|
|
9016
|
+
}
|
|
9017
|
+
return byRow;
|
|
9018
|
+
}
|
|
9019
|
+
function columnsUsed(doc) {
|
|
9020
|
+
const used = /* @__PURE__ */ new Set();
|
|
9021
|
+
for (const node of doc.nodes) for (const column of columnsCovered(node)) used.add(column);
|
|
9022
|
+
return ROADMAP_COLUMNS.filter((column) => used.has(column));
|
|
9023
|
+
}
|
|
9024
|
+
function groupRanges(doc) {
|
|
9025
|
+
return doc.groups.map((group) => {
|
|
9026
|
+
const columns = resolveGroupColumns(group);
|
|
9027
|
+
const indices = columns.map(columnIndex).sort((a, b) => a - b);
|
|
9028
|
+
const start = indices[0];
|
|
9029
|
+
const end = indices[indices.length - 1];
|
|
9030
|
+
return {
|
|
9031
|
+
id: group.id,
|
|
9032
|
+
from: group.from,
|
|
9033
|
+
to: group.to,
|
|
9034
|
+
columns: ROADMAP_COLUMNS.filter((c) => columns.includes(c)),
|
|
9035
|
+
row: { start: group.from + 1, span: group.to - group.from + 1 },
|
|
9036
|
+
column: { start: start + 1, span: end - start + 1 },
|
|
9037
|
+
fragmented: end - start + 1 !== indices.length
|
|
9038
|
+
};
|
|
9039
|
+
});
|
|
9040
|
+
}
|
|
9041
|
+
function groupColumnRuns(group) {
|
|
9042
|
+
const indices = resolveGroupColumns(group).map(columnIndex).sort((a, b) => a - b);
|
|
9043
|
+
const runs = [];
|
|
9044
|
+
for (const index of indices) {
|
|
9045
|
+
const last = runs[runs.length - 1];
|
|
9046
|
+
if (last && last.start + last.span === index + 1) last.span += 1;
|
|
9047
|
+
else runs.push({ start: index + 1, span: 1 });
|
|
9048
|
+
}
|
|
9049
|
+
return runs;
|
|
9050
|
+
}
|
|
9051
|
+
function validateNoOverlap(doc) {
|
|
9052
|
+
const overlaps = [];
|
|
9053
|
+
const nodeCell = /* @__PURE__ */ new Map();
|
|
9054
|
+
for (const node of doc.nodes) {
|
|
9055
|
+
for (const column of columnsCovered(node)) {
|
|
9056
|
+
const key = `${column}:${node.order}`;
|
|
9057
|
+
const owner = nodeCell.get(key);
|
|
9058
|
+
if (owner !== void 0) {
|
|
9059
|
+
overlaps.push({ kind: "node", column, row: node.order, ids: [owner, node.id] });
|
|
9060
|
+
continue;
|
|
9061
|
+
}
|
|
9062
|
+
nodeCell.set(key, node.id);
|
|
9063
|
+
}
|
|
9064
|
+
}
|
|
9065
|
+
const groupCell = /* @__PURE__ */ new Map();
|
|
9066
|
+
for (const group of doc.groups) {
|
|
9067
|
+
if (group.from > group.to) continue;
|
|
9068
|
+
for (const column of resolveGroupColumns(group)) {
|
|
9069
|
+
for (let row = group.from; row <= group.to; row += 1) {
|
|
9070
|
+
const key = `${column}:${row}`;
|
|
9071
|
+
const owner = groupCell.get(key);
|
|
9072
|
+
if (owner !== void 0) {
|
|
9073
|
+
if (owner !== group.id) {
|
|
9074
|
+
overlaps.push({ kind: "group", column, row, ids: [owner, group.id] });
|
|
9075
|
+
}
|
|
9076
|
+
continue;
|
|
9077
|
+
}
|
|
9078
|
+
groupCell.set(key, group.id);
|
|
9079
|
+
}
|
|
9080
|
+
}
|
|
9081
|
+
}
|
|
9082
|
+
return overlaps;
|
|
9083
|
+
}
|
|
9084
|
+
function placeNodes(doc) {
|
|
9085
|
+
const byRow = nodesByRow(doc);
|
|
9086
|
+
const rows = rowsOf(doc);
|
|
9087
|
+
const placements = [];
|
|
9088
|
+
for (const row of rows) {
|
|
9089
|
+
for (const node of byRow.get(row) ?? []) {
|
|
9090
|
+
placements.push({
|
|
9091
|
+
node,
|
|
9092
|
+
row: { start: node.order + 1, span: 1 },
|
|
9093
|
+
column: gridColumnFor(node)
|
|
9094
|
+
});
|
|
9095
|
+
}
|
|
9096
|
+
}
|
|
9097
|
+
return { placements, rows, rowCount: maxRowOf(doc) + 1 };
|
|
9098
|
+
}
|
|
9099
|
+
function firstFreeRow(taken, columns, from) {
|
|
9100
|
+
let row = from;
|
|
9101
|
+
for (let guard = 0; guard < 1e5; guard += 1) {
|
|
9102
|
+
if (columns.every((column) => !taken.has(`${column}:${row}`))) return row;
|
|
9103
|
+
row += 1;
|
|
9104
|
+
}
|
|
9105
|
+
return row;
|
|
9106
|
+
}
|
|
9107
|
+
|
|
9108
|
+
// src/components/organisms/roadmap/Roadmap.tsx
|
|
8641
9109
|
var import_react7 = require("react");
|
|
9110
|
+
|
|
9111
|
+
// src/components/organisms/roadmap/RoadmapNode.tsx
|
|
9112
|
+
var import_class_variance_authority14 = require("class-variance-authority");
|
|
9113
|
+
|
|
9114
|
+
// src/components/organisms/roadmap/RoadmapLegend.tsx
|
|
9115
|
+
var import_lucide_react22 = require("lucide-react");
|
|
9116
|
+
|
|
9117
|
+
// src/components/organisms/roadmap/appearance.ts
|
|
9118
|
+
function toneStyle(tone) {
|
|
9119
|
+
return {
|
|
9120
|
+
"--c-roadmap-node-bg": `var(--c-roadmap-${tone}-bg)`,
|
|
9121
|
+
"--c-roadmap-node-fg": `var(--c-roadmap-${tone}-fg)`
|
|
9122
|
+
};
|
|
9123
|
+
}
|
|
9124
|
+
var focusClass = "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--c-roadmap-focus)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--c-roadmap-surface)]";
|
|
9125
|
+
|
|
9126
|
+
// src/components/organisms/roadmap/RoadmapLegend.tsx
|
|
9127
|
+
var import_jsx_runtime65 = require("react/jsx-runtime");
|
|
9128
|
+
var glyphs = { check: import_lucide_react22.Check, globe: import_lucide_react22.Globe, lock: import_lucide_react22.Lock, star: import_lucide_react22.Star, "book-open": import_lucide_react22.BookOpen, play: import_lucide_react22.Play, circle: import_lucide_react22.Circle, clock: import_lucide_react22.Clock, link: import_lucide_react22.Link, "external-link": import_lucide_react22.ExternalLink };
|
|
9129
|
+
function RoadmapBadge({ name = "check", tone = "neutral", label, className = "" }) {
|
|
9130
|
+
const Glyph = glyphs[name] ?? import_lucide_react22.CircleHelp;
|
|
9131
|
+
return /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("span", { "aria-label": label, "aria-hidden": label ? void 0 : true, style: toneStyle(tone), className: `inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-full border-2 border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-node-bg)] text-[var(--c-roadmap-node-fg)] ${className}`, children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(Glyph, { size: 12, "aria-hidden": "true" }) });
|
|
9132
|
+
}
|
|
9133
|
+
function RoadmapLegendView({ legend, testIdPrefix = "roadmap" }) {
|
|
9134
|
+
return /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("aside", { "data-testid": `${testIdPrefix}-legend`, "aria-label": "Legend", className: "min-w-0 rounded-[var(--radius)] border-2 border-[var(--c-roadmap-border)] bg-[var(--c-roadmap-neutral-bg)] p-3 text-[var(--c-roadmap-neutral-fg)]", children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)("ul", { className: "m-0 flex list-none flex-wrap gap-3 p-0", children: legend.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)("li", { className: "flex min-w-0 items-center gap-2 text-xs leading-normal", children: [
|
|
9135
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)(RoadmapBadge, { name: entry.icon, tone: entry.tone }),
|
|
9136
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)("span", { className: "[overflow-wrap:anywhere]", children: entry.label })
|
|
9137
|
+
] }, entry.id)) }) });
|
|
9138
|
+
}
|
|
9139
|
+
|
|
9140
|
+
// src/components/organisms/roadmap/RoadmapNode.tsx
|
|
9141
|
+
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-2 py-3 text-center text-xs leading-normal md:px-4 md:text-base text-[var(--c-roadmap-node-fg)]", {
|
|
9143
|
+
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 md:text-[28px]", 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
|
+
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
|
+
}
|
|
9147
|
+
});
|
|
9148
|
+
function RoadmapNodeView({ node, state, badge, testIdPrefix, onNodeClick, onAction }) {
|
|
9149
|
+
const locked = state === "locked";
|
|
9150
|
+
const nodeTone = resolveNodeTone(node);
|
|
9151
|
+
const label = /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { "data-testid": `${testIdPrefix}-node-label`, className: `[overflow-wrap:anywhere] ${state === "done" ? "line-through" : state === "learning" ? "underline" : ""}`, children: node.label });
|
|
9152
|
+
const interactiveClass = `block w-full min-w-0 text-inherit ${focusClass}`;
|
|
9153
|
+
const wholeBox = !locked && !node.action && !node.links?.length;
|
|
9154
|
+
const Box = wholeBox && node.href ? "a" : wholeBox && onNodeClick ? "button" : "div";
|
|
9155
|
+
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
|
+
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: [
|
|
9158
|
+
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
|
+
primary,
|
|
9160
|
+
node.description && node.kind === "paragraph" && /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("p", { className: "mt-2 [overflow-wrap:anywhere]", children: node.description }),
|
|
9161
|
+
node.links && /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("ul", { className: "mt-2 list-none space-y-2 p-0", children: node.links.map((link, index) => /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("li", { children: locked ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("span", { children: link.label }) : /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("a", { href: link.href, className: `underline [overflow-wrap:anywhere] ${focusClass}`, onClick: (e) => e.stopPropagation(), children: link.label }) }, index)) }),
|
|
9162
|
+
node.action && /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("div", { style: toneStyle(node.action.tone ?? "primary"), className: "mt-3 min-w-0", children: node.action.href && !locked ? /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("a", { href: node.action.href, className: `inline-block rounded px-2 py-2 bg-[var(--c-roadmap-node-bg)] text-[var(--c-roadmap-node-fg)] [overflow-wrap:anywhere] ${focusClass}`, children: node.action.label }) : /* @__PURE__ */ (0, import_jsx_runtime66.jsx)("button", { type: "button", disabled: locked, className: `max-w-full rounded px-2 py-2 bg-[var(--c-roadmap-node-bg)] text-[var(--c-roadmap-node-fg)] [overflow-wrap:anywhere] ${focusClass}`, onClick: (e) => {
|
|
9163
|
+
e.stopPropagation();
|
|
9164
|
+
if (node.action?.actionId) onAction?.(node.action.actionId, node);
|
|
9165
|
+
}, children: node.action.label }) })
|
|
9166
|
+
] }) });
|
|
9167
|
+
}
|
|
9168
|
+
|
|
9169
|
+
// src/components/organisms/roadmap/RoadmapGroup.tsx
|
|
9170
|
+
var import_jsx_runtime67 = require("react/jsx-runtime");
|
|
9171
|
+
function RoadmapGroupView({ group, testIdPrefix }) {
|
|
9172
|
+
return /* @__PURE__ */ (0, import_jsx_runtime67.jsx)(import_jsx_runtime67.Fragment, { children: groupColumnRuns(group).map((run, index) => /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)(
|
|
9173
|
+
"div",
|
|
9174
|
+
{
|
|
9175
|
+
"data-testid": `${testIdPrefix}-group`,
|
|
9176
|
+
"data-group-id": group.id,
|
|
9177
|
+
style: { ...toneStyle(resolveGroupTone(group)), gridColumn: `${run.start} / span ${run.span}`, gridRow: `${group.from + 1} / span ${group.to - group.from + 1}` },
|
|
9178
|
+
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
|
+
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 [overflow-wrap:anywhere]", children: group.title }) }),
|
|
9181
|
+
index === 0 && group.description && /* @__PURE__ */ (0, import_jsx_runtime67.jsx)("span", { className: "sr-only", children: group.description })
|
|
9182
|
+
]
|
|
9183
|
+
},
|
|
9184
|
+
run.start
|
|
9185
|
+
)) });
|
|
9186
|
+
}
|
|
9187
|
+
|
|
9188
|
+
// src/components/organisms/roadmap/Roadmap.tsx
|
|
9189
|
+
var import_jsx_runtime68 = require("react/jsx-runtime");
|
|
9190
|
+
function Roadmap({ document: document2, state, renderNode, onNodeClick, onAction, testIdPrefix = "roadmap", className = "", ariaLabel }) {
|
|
9191
|
+
const { placements } = (0, import_react7.useMemo)(() => placeNodes(document2), [document2]);
|
|
9192
|
+
const badges = (0, import_react7.useMemo)(() => new Map(document2.legend?.entries.map((entry) => [entry.id, entry])), [document2.legend]);
|
|
9193
|
+
const legendPlacement = document2.legend ? resolveLegendPlacement(document2.legend) : void 0;
|
|
9194
|
+
const legend = document2.legend && /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(RoadmapLegendView, { legend: document2.legend, testIdPrefix });
|
|
9195
|
+
return /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("section", { "data-testid": testIdPrefix, "aria-label": ariaLabel ?? document2.title ?? "Roadmap", className: `min-w-0 bg-[var(--c-roadmap-surface)] p-3 text-[var(--c-roadmap-neutral-fg)] ${className}`, children: [
|
|
9196
|
+
legendPlacement === "top" && /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("div", { className: "mb-6", children: legend }),
|
|
9197
|
+
/* @__PURE__ */ (0, import_jsx_runtime68.jsxs)("div", { "data-testid": `${testIdPrefix}-grid`, role: "list", "aria-label": "Roadmap nodes", className: "relative isolate grid min-w-0 grid-cols-3 items-center gap-x-3 gap-y-8 md:gap-x-8", children: [
|
|
9198
|
+
document2.groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(RoadmapGroupView, { group, testIdPrefix }, group.id)),
|
|
9199
|
+
placements.map(({ node, row, column }) => {
|
|
9200
|
+
const resolvedState = resolveNodeState(node, state);
|
|
9201
|
+
return /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(
|
|
9202
|
+
"div",
|
|
9203
|
+
{
|
|
9204
|
+
role: "listitem",
|
|
9205
|
+
"data-testid": `${testIdPrefix}-node`,
|
|
9206
|
+
"data-node-id": node.id,
|
|
9207
|
+
"data-state": resolvedState,
|
|
9208
|
+
style: { gridColumn: `${column.start} / span ${column.span}`, gridRow: `${row.start} / span 1` },
|
|
9209
|
+
className: "relative z-10 min-w-0 px-2 py-4",
|
|
9210
|
+
children: node.kind === "legend" ? legendPlacement === "inline" ? legend : null : renderNode ? renderNode(node, resolvedState) : /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(RoadmapNodeView, { node, state: resolvedState, badge: node.badge ? badges.get(node.badge) : void 0, onNodeClick, onAction, testIdPrefix })
|
|
9211
|
+
},
|
|
9212
|
+
node.id
|
|
9213
|
+
);
|
|
9214
|
+
})
|
|
9215
|
+
] }),
|
|
9216
|
+
legendPlacement === "bottom" && /* @__PURE__ */ (0, import_jsx_runtime68.jsx)("div", { className: "mt-6", children: legend })
|
|
9217
|
+
] });
|
|
9218
|
+
}
|
|
9219
|
+
|
|
9220
|
+
// src/hooks/use-iframe-auth.ts
|
|
9221
|
+
var import_react8 = require("react");
|
|
8642
9222
|
function useIframeAuth() {
|
|
8643
|
-
const ctx = (0,
|
|
9223
|
+
const ctx = (0, import_react8.useContext)(IframeContext);
|
|
8644
9224
|
return { token: ctx.token, userId: ctx.userId, ready: ctx.ready };
|
|
8645
9225
|
}
|
|
8646
9226
|
|
|
8647
9227
|
// src/hooks/use-iframe-navigate.ts
|
|
8648
|
-
var
|
|
9228
|
+
var import_react9 = require("react");
|
|
8649
9229
|
function useIframeNavigate() {
|
|
8650
|
-
return (0,
|
|
9230
|
+
return (0, import_react9.useCallback)((path) => {
|
|
8651
9231
|
if (!window.parent || window.parent === window) return;
|
|
8652
9232
|
window.parent.postMessage(
|
|
8653
9233
|
{ type: "DFL_NAVIGATE", path },
|
|
@@ -8657,9 +9237,9 @@ function useIframeNavigate() {
|
|
|
8657
9237
|
}
|
|
8658
9238
|
|
|
8659
9239
|
// src/providers/feature-flag-provider.tsx
|
|
8660
|
-
var
|
|
8661
|
-
var
|
|
8662
|
-
var FeatureFlagContext = (0,
|
|
9240
|
+
var import_react10 = require("react");
|
|
9241
|
+
var import_jsx_runtime69 = require("react/jsx-runtime");
|
|
9242
|
+
var FeatureFlagContext = (0, import_react10.createContext)({
|
|
8663
9243
|
flags: {},
|
|
8664
9244
|
isEnabled: () => false
|
|
8665
9245
|
});
|
|
@@ -8667,21 +9247,21 @@ var FeatureFlagProvider = ({
|
|
|
8667
9247
|
children,
|
|
8668
9248
|
flags
|
|
8669
9249
|
}) => {
|
|
8670
|
-
const value = (0,
|
|
9250
|
+
const value = (0, import_react10.useMemo)(
|
|
8671
9251
|
() => ({
|
|
8672
9252
|
flags,
|
|
8673
9253
|
isEnabled: (flag) => Boolean(flags[flag])
|
|
8674
9254
|
}),
|
|
8675
9255
|
[flags]
|
|
8676
9256
|
);
|
|
8677
|
-
return /* @__PURE__ */ (0,
|
|
9257
|
+
return /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(FeatureFlagContext.Provider, { value, children });
|
|
8678
9258
|
};
|
|
8679
9259
|
var useFeatureFlag = (flag) => {
|
|
8680
|
-
const { isEnabled } = (0,
|
|
9260
|
+
const { isEnabled } = (0, import_react10.useContext)(FeatureFlagContext);
|
|
8681
9261
|
return isEnabled(flag);
|
|
8682
9262
|
};
|
|
8683
9263
|
var useFeatureFlags = () => {
|
|
8684
|
-
const { flags } = (0,
|
|
9264
|
+
const { flags } = (0, import_react10.useContext)(FeatureFlagContext);
|
|
8685
9265
|
return flags;
|
|
8686
9266
|
};
|
|
8687
9267
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -8774,7 +9354,14 @@ var useFeatureFlags = () => {
|
|
|
8774
9354
|
ContextMenuSubContent,
|
|
8775
9355
|
ContextMenuSubTrigger,
|
|
8776
9356
|
ContextMenuTrigger,
|
|
9357
|
+
DEFAULT_EDGE_ARROW,
|
|
9358
|
+
DEFAULT_EDGE_ROUTE,
|
|
9359
|
+
DEFAULT_EDGE_STYLE,
|
|
9360
|
+
DEFAULT_GROUP_TONE,
|
|
9361
|
+
DEFAULT_LEGEND_PLACEMENT,
|
|
8777
9362
|
DEFAULT_NAME_COL_WIDTH,
|
|
9363
|
+
DEFAULT_NODE_SPAN,
|
|
9364
|
+
DEFAULT_NODE_STATE,
|
|
8778
9365
|
DflRemote,
|
|
8779
9366
|
Dialog,
|
|
8780
9367
|
DialogClose,
|
|
@@ -8852,6 +9439,7 @@ var useFeatureFlags = () => {
|
|
|
8852
9439
|
MenubarSubContent,
|
|
8853
9440
|
MenubarSubTrigger,
|
|
8854
9441
|
MenubarTrigger,
|
|
9442
|
+
NODE_KIND_DEFAULT_TONE,
|
|
8855
9443
|
NavigationMenu,
|
|
8856
9444
|
NavigationMenuContent,
|
|
8857
9445
|
NavigationMenuIndicator,
|
|
@@ -8875,12 +9463,24 @@ var useFeatureFlags = () => {
|
|
|
8875
9463
|
Progress,
|
|
8876
9464
|
ProtectedRoute,
|
|
8877
9465
|
PublishDrawer,
|
|
9466
|
+
ROADMAP_ARROWS,
|
|
9467
|
+
ROADMAP_COLUMNS,
|
|
9468
|
+
ROADMAP_EDGE_LABEL_MAX,
|
|
9469
|
+
ROADMAP_EDGE_ROUTES,
|
|
9470
|
+
ROADMAP_EDGE_STYLES,
|
|
9471
|
+
ROADMAP_LABEL_MAX,
|
|
9472
|
+
ROADMAP_LEGEND_PLACEMENTS,
|
|
9473
|
+
ROADMAP_NODE_ID_PATTERN,
|
|
9474
|
+
ROADMAP_NODE_KINDS,
|
|
9475
|
+
ROADMAP_NODE_STATES,
|
|
9476
|
+
ROADMAP_TONES,
|
|
8878
9477
|
RadioGroup,
|
|
8879
9478
|
RadioGroupItem,
|
|
8880
9479
|
RadioGroupRow,
|
|
8881
9480
|
ResizableHandle,
|
|
8882
9481
|
ResizablePanel,
|
|
8883
9482
|
ResizablePanelGroup,
|
|
9483
|
+
Roadmap,
|
|
8884
9484
|
ScrollArea,
|
|
8885
9485
|
ScrollBar,
|
|
8886
9486
|
Select,
|
|
@@ -8968,19 +9568,55 @@ var useFeatureFlags = () => {
|
|
|
8968
9568
|
buttonVariants,
|
|
8969
9569
|
clampPct,
|
|
8970
9570
|
cn,
|
|
9571
|
+
columnIndex,
|
|
9572
|
+
columnsCovered,
|
|
9573
|
+
columnsUsed,
|
|
8971
9574
|
filterPublishableAccounts,
|
|
9575
|
+
firstFreeRow,
|
|
8972
9576
|
getInitials,
|
|
9577
|
+
gridColumnFor,
|
|
9578
|
+
groupColumnRuns,
|
|
9579
|
+
groupRanges,
|
|
8973
9580
|
iconButtonVariants,
|
|
8974
9581
|
isAllowedOrigin,
|
|
8975
9582
|
kbdVariants,
|
|
8976
9583
|
labelVariants,
|
|
9584
|
+
maxRowOf,
|
|
8977
9585
|
memberHueIndex,
|
|
8978
9586
|
memberHueVar,
|
|
8979
9587
|
navigationMenuTriggerStyle,
|
|
9588
|
+
nodesByRow,
|
|
9589
|
+
parseRoadmapDocument,
|
|
9590
|
+
parseRoadmapStateOverlay,
|
|
8980
9591
|
parseTags,
|
|
9592
|
+
placeNodes,
|
|
9593
|
+
resolveEdgeArrow,
|
|
9594
|
+
resolveEdgeRoute,
|
|
9595
|
+
resolveEdgeStyle,
|
|
9596
|
+
resolveGroupColumns,
|
|
9597
|
+
resolveGroupTone,
|
|
9598
|
+
resolveLegendPlacement,
|
|
8981
9599
|
resolveNameColWidth,
|
|
9600
|
+
resolveNodeSpan,
|
|
9601
|
+
resolveNodeState,
|
|
9602
|
+
resolveNodeTone,
|
|
8982
9603
|
resolveWeekCount,
|
|
8983
9604
|
resolveWeekLabels,
|
|
9605
|
+
roadmapActionSchema,
|
|
9606
|
+
roadmapDocumentSchema,
|
|
9607
|
+
roadmapEdgeSchema,
|
|
9608
|
+
roadmapGroupSchema,
|
|
9609
|
+
roadmapIconSchema,
|
|
9610
|
+
roadmapLegendEntrySchema,
|
|
9611
|
+
roadmapLegendSchema,
|
|
9612
|
+
roadmapLinkSchema,
|
|
9613
|
+
roadmapNodeSchema,
|
|
9614
|
+
roadmapNodeStateSchema,
|
|
9615
|
+
roadmapStateOverlaySchema,
|
|
9616
|
+
roadmapStateSchema,
|
|
9617
|
+
rowsOf,
|
|
9618
|
+
safeParseRoadmapDocument,
|
|
9619
|
+
safeParseRoadmapStateOverlay,
|
|
8984
9620
|
stageProgress,
|
|
8985
9621
|
toast,
|
|
8986
9622
|
toggleVariants,
|
|
@@ -8994,5 +9630,6 @@ var useFeatureFlags = () => {
|
|
|
8994
9630
|
useSession,
|
|
8995
9631
|
useSidebar,
|
|
8996
9632
|
useToast,
|
|
9633
|
+
validateNoOverlap,
|
|
8997
9634
|
validatePublishForm
|
|
8998
9635
|
});
|