@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.js
CHANGED
|
@@ -8305,6 +8305,529 @@ function PublishDrawer({
|
|
|
8305
8305
|
) });
|
|
8306
8306
|
}
|
|
8307
8307
|
|
|
8308
|
+
// src/components/organisms/roadmap/contract.ts
|
|
8309
|
+
import { z } from "zod";
|
|
8310
|
+
var ROADMAP_COLUMNS = ["left", "center", "right"];
|
|
8311
|
+
var ROADMAP_TONES = [
|
|
8312
|
+
"primary",
|
|
8313
|
+
"secondary",
|
|
8314
|
+
"accent",
|
|
8315
|
+
"muted",
|
|
8316
|
+
"success",
|
|
8317
|
+
"info",
|
|
8318
|
+
"danger",
|
|
8319
|
+
"neutral"
|
|
8320
|
+
];
|
|
8321
|
+
var ROADMAP_NODE_KINDS = [
|
|
8322
|
+
"topic",
|
|
8323
|
+
"subtopic",
|
|
8324
|
+
"label",
|
|
8325
|
+
"title",
|
|
8326
|
+
"paragraph",
|
|
8327
|
+
"button",
|
|
8328
|
+
"legend"
|
|
8329
|
+
];
|
|
8330
|
+
var ROADMAP_NODE_STATES = ["todo", "done", "learning", "skipped", "locked"];
|
|
8331
|
+
var ROADMAP_EDGE_STYLES = ["solid", "dashed", "dotted"];
|
|
8332
|
+
var ROADMAP_EDGE_ROUTES = ["auto", "straight", "elbow", "curve"];
|
|
8333
|
+
var ROADMAP_ARROWS = ["none", "end", "both"];
|
|
8334
|
+
var ROADMAP_LEGEND_PLACEMENTS = ["top", "bottom", "inline"];
|
|
8335
|
+
var DEFAULT_NODE_SPAN = 1;
|
|
8336
|
+
var DEFAULT_EDGE_STYLE = "dashed";
|
|
8337
|
+
var DEFAULT_EDGE_ROUTE = "auto";
|
|
8338
|
+
var DEFAULT_EDGE_ARROW = "none";
|
|
8339
|
+
var DEFAULT_GROUP_TONE = "neutral";
|
|
8340
|
+
var DEFAULT_LEGEND_PLACEMENT = "top";
|
|
8341
|
+
var DEFAULT_NODE_STATE = "todo";
|
|
8342
|
+
var NODE_KIND_DEFAULT_TONE = {
|
|
8343
|
+
topic: "primary",
|
|
8344
|
+
subtopic: "secondary",
|
|
8345
|
+
button: "primary",
|
|
8346
|
+
label: "neutral",
|
|
8347
|
+
title: "neutral",
|
|
8348
|
+
paragraph: "neutral",
|
|
8349
|
+
legend: "neutral"
|
|
8350
|
+
};
|
|
8351
|
+
var resolveNodeSpan = (node) => node.span ?? DEFAULT_NODE_SPAN;
|
|
8352
|
+
var resolveNodeTone = (node) => node.tone ?? NODE_KIND_DEFAULT_TONE[node.kind];
|
|
8353
|
+
var resolveEdgeStyle = (edge) => edge.style ?? DEFAULT_EDGE_STYLE;
|
|
8354
|
+
var resolveEdgeRoute = (edge) => edge.route ?? DEFAULT_EDGE_ROUTE;
|
|
8355
|
+
var resolveEdgeArrow = (edge) => edge.arrow ?? DEFAULT_EDGE_ARROW;
|
|
8356
|
+
var resolveGroupTone = (group) => group.tone ?? DEFAULT_GROUP_TONE;
|
|
8357
|
+
var resolveGroupColumns = (group) => group.columns && group.columns.length > 0 ? group.columns : [...ROADMAP_COLUMNS];
|
|
8358
|
+
var resolveLegendPlacement = (legend) => legend.placement ?? DEFAULT_LEGEND_PLACEMENT;
|
|
8359
|
+
var resolveNodeState = (node, overlay) => overlay?.nodes[node.id] ?? node.state ?? DEFAULT_NODE_STATE;
|
|
8360
|
+
var ROADMAP_NODE_ID_PATTERN = /^[A-Za-z0-9_-]{1,64}$/;
|
|
8361
|
+
var ROADMAP_EDGE_LABEL_MAX = 40;
|
|
8362
|
+
var ROADMAP_LABEL_MAX = 200;
|
|
8363
|
+
var columnSchema = z.enum(ROADMAP_COLUMNS);
|
|
8364
|
+
var toneSchema = z.enum(ROADMAP_TONES);
|
|
8365
|
+
var idSchema = z.string().regex(ROADMAP_NODE_ID_PATTERN, "must match /^[A-Za-z0-9_-]{1,64}$/");
|
|
8366
|
+
var metaSchema = z.record(z.unknown());
|
|
8367
|
+
var roadmapIconSchema = z.object({
|
|
8368
|
+
name: z.string().min(1).max(64),
|
|
8369
|
+
side: z.enum(["left", "right"]),
|
|
8370
|
+
tone: toneSchema.optional()
|
|
8371
|
+
}).strict();
|
|
8372
|
+
var roadmapActionSchema = z.object({
|
|
8373
|
+
label: z.string().min(1).max(80),
|
|
8374
|
+
href: z.string().min(1).optional(),
|
|
8375
|
+
actionId: z.string().min(1).optional(),
|
|
8376
|
+
tone: toneSchema.optional()
|
|
8377
|
+
}).strict().superRefine((action, ctx) => {
|
|
8378
|
+
const given = [action.href, action.actionId].filter((v) => v !== void 0).length;
|
|
8379
|
+
if (given !== 1) {
|
|
8380
|
+
ctx.addIssue({
|
|
8381
|
+
code: z.ZodIssueCode.custom,
|
|
8382
|
+
path: ["href"],
|
|
8383
|
+
message: "an action needs exactly one of `href` or `actionId`"
|
|
8384
|
+
});
|
|
8385
|
+
}
|
|
8386
|
+
});
|
|
8387
|
+
var roadmapLinkSchema = z.object({ label: z.string().min(1).max(120), href: z.string().min(1) }).strict();
|
|
8388
|
+
var roadmapNodeStateSchema = z.enum(ROADMAP_NODE_STATES);
|
|
8389
|
+
var roadmapNodeSchema = z.object({
|
|
8390
|
+
id: idSchema,
|
|
8391
|
+
column: columnSchema,
|
|
8392
|
+
order: z.number().int().min(0),
|
|
8393
|
+
span: z.union([z.literal(1), z.literal(2), z.literal(3)]).optional(),
|
|
8394
|
+
kind: z.enum(ROADMAP_NODE_KINDS),
|
|
8395
|
+
label: z.string().min(1).max(ROADMAP_LABEL_MAX),
|
|
8396
|
+
description: z.string().max(2e3).optional(),
|
|
8397
|
+
tone: toneSchema.optional(),
|
|
8398
|
+
icon: roadmapIconSchema.optional(),
|
|
8399
|
+
badge: z.string().min(1).optional(),
|
|
8400
|
+
state: roadmapNodeStateSchema.optional(),
|
|
8401
|
+
action: roadmapActionSchema.optional(),
|
|
8402
|
+
href: z.string().min(1).optional(),
|
|
8403
|
+
links: z.array(roadmapLinkSchema).optional(),
|
|
8404
|
+
meta: metaSchema.optional()
|
|
8405
|
+
}).strict();
|
|
8406
|
+
var roadmapEdgeSchema = z.object({
|
|
8407
|
+
id: idSchema,
|
|
8408
|
+
source: z.string().min(1),
|
|
8409
|
+
target: z.string().min(1),
|
|
8410
|
+
style: z.enum(ROADMAP_EDGE_STYLES).optional(),
|
|
8411
|
+
label: z.string().min(1).max(ROADMAP_EDGE_LABEL_MAX).optional(),
|
|
8412
|
+
route: z.enum(ROADMAP_EDGE_ROUTES).optional(),
|
|
8413
|
+
arrow: z.enum(ROADMAP_ARROWS).optional(),
|
|
8414
|
+
tone: toneSchema.optional()
|
|
8415
|
+
}).strict();
|
|
8416
|
+
var roadmapGroupSchema = z.object({
|
|
8417
|
+
id: idSchema,
|
|
8418
|
+
title: z.string().min(1).max(ROADMAP_LABEL_MAX).optional(),
|
|
8419
|
+
tone: toneSchema.optional(),
|
|
8420
|
+
from: z.number().int().min(0),
|
|
8421
|
+
to: z.number().int().min(0),
|
|
8422
|
+
columns: z.array(columnSchema).min(1).max(3).optional(),
|
|
8423
|
+
description: z.string().max(2e3).optional()
|
|
8424
|
+
}).strict();
|
|
8425
|
+
var roadmapLegendEntrySchema = z.object({
|
|
8426
|
+
id: idSchema,
|
|
8427
|
+
label: z.string().min(1).max(ROADMAP_LABEL_MAX),
|
|
8428
|
+
tone: toneSchema,
|
|
8429
|
+
icon: z.string().min(1).max(64).optional()
|
|
8430
|
+
}).strict();
|
|
8431
|
+
var roadmapLegendSchema = z.object({
|
|
8432
|
+
entries: z.array(roadmapLegendEntrySchema).min(1),
|
|
8433
|
+
placement: z.enum(ROADMAP_LEGEND_PLACEMENTS).optional()
|
|
8434
|
+
}).strict();
|
|
8435
|
+
function columnsCovered(node) {
|
|
8436
|
+
const span = node.span ?? DEFAULT_NODE_SPAN;
|
|
8437
|
+
if (span === 3) return [...ROADMAP_COLUMNS];
|
|
8438
|
+
const start = ROADMAP_COLUMNS.indexOf(node.column);
|
|
8439
|
+
return ROADMAP_COLUMNS.slice(start, start + span);
|
|
8440
|
+
}
|
|
8441
|
+
var roadmapDocumentBaseSchema = z.object({
|
|
8442
|
+
version: z.literal("roadmap/v1"),
|
|
8443
|
+
title: z.string().min(1).max(ROADMAP_LABEL_MAX).optional(),
|
|
8444
|
+
direction: z.literal("down"),
|
|
8445
|
+
columns: z.literal(3),
|
|
8446
|
+
nodes: z.array(roadmapNodeSchema),
|
|
8447
|
+
edges: z.array(roadmapEdgeSchema),
|
|
8448
|
+
groups: z.array(roadmapGroupSchema),
|
|
8449
|
+
legend: roadmapLegendSchema.optional(),
|
|
8450
|
+
meta: metaSchema.optional()
|
|
8451
|
+
}).strict();
|
|
8452
|
+
var roadmapDocumentSchema = roadmapDocumentBaseSchema.superRefine((doc, ctx) => {
|
|
8453
|
+
const nodeIndexById = /* @__PURE__ */ new Map();
|
|
8454
|
+
doc.nodes.forEach((node, i) => {
|
|
8455
|
+
if (nodeIndexById.has(node.id)) {
|
|
8456
|
+
ctx.addIssue({
|
|
8457
|
+
code: z.ZodIssueCode.custom,
|
|
8458
|
+
path: ["nodes", i, "id"],
|
|
8459
|
+
message: `duplicate node id "${node.id}" (first seen at nodes[${nodeIndexById.get(node.id)}])`
|
|
8460
|
+
});
|
|
8461
|
+
return;
|
|
8462
|
+
}
|
|
8463
|
+
nodeIndexById.set(node.id, i);
|
|
8464
|
+
});
|
|
8465
|
+
const cellOwner = /* @__PURE__ */ new Map();
|
|
8466
|
+
doc.nodes.forEach((node, i) => {
|
|
8467
|
+
const span = node.span ?? DEFAULT_NODE_SPAN;
|
|
8468
|
+
if (span === 2 && node.column === "right") {
|
|
8469
|
+
ctx.addIssue({
|
|
8470
|
+
code: z.ZodIssueCode.custom,
|
|
8471
|
+
path: ["nodes", i, "span"],
|
|
8472
|
+
message: 'a `span: 2` node at column "right" runs past the last track'
|
|
8473
|
+
});
|
|
8474
|
+
return;
|
|
8475
|
+
}
|
|
8476
|
+
for (const column of columnsCovered(node)) {
|
|
8477
|
+
const key = `${column}:${node.order}`;
|
|
8478
|
+
const owner = cellOwner.get(key);
|
|
8479
|
+
if (owner !== void 0) {
|
|
8480
|
+
ctx.addIssue({
|
|
8481
|
+
code: z.ZodIssueCode.custom,
|
|
8482
|
+
path: ["nodes", i, "order"],
|
|
8483
|
+
message: `cell (${column}, ${node.order}) is already taken by nodes[${owner}] ("${doc.nodes[owner].id}")`
|
|
8484
|
+
});
|
|
8485
|
+
continue;
|
|
8486
|
+
}
|
|
8487
|
+
cellOwner.set(key, i);
|
|
8488
|
+
}
|
|
8489
|
+
});
|
|
8490
|
+
const edgeIds = /* @__PURE__ */ new Set();
|
|
8491
|
+
doc.edges.forEach((edge, i) => {
|
|
8492
|
+
if (edgeIds.has(edge.id)) {
|
|
8493
|
+
ctx.addIssue({
|
|
8494
|
+
code: z.ZodIssueCode.custom,
|
|
8495
|
+
path: ["edges", i, "id"],
|
|
8496
|
+
message: `duplicate edge id "${edge.id}"`
|
|
8497
|
+
});
|
|
8498
|
+
}
|
|
8499
|
+
edgeIds.add(edge.id);
|
|
8500
|
+
if (!nodeIndexById.has(edge.source)) {
|
|
8501
|
+
ctx.addIssue({
|
|
8502
|
+
code: z.ZodIssueCode.custom,
|
|
8503
|
+
path: ["edges", i, "source"],
|
|
8504
|
+
message: `edge source "${edge.source}" is not a node in this document`
|
|
8505
|
+
});
|
|
8506
|
+
}
|
|
8507
|
+
if (!nodeIndexById.has(edge.target)) {
|
|
8508
|
+
ctx.addIssue({
|
|
8509
|
+
code: z.ZodIssueCode.custom,
|
|
8510
|
+
path: ["edges", i, "target"],
|
|
8511
|
+
message: `edge target "${edge.target}" is not a node in this document`
|
|
8512
|
+
});
|
|
8513
|
+
}
|
|
8514
|
+
if (edge.source === edge.target) {
|
|
8515
|
+
ctx.addIssue({
|
|
8516
|
+
code: z.ZodIssueCode.custom,
|
|
8517
|
+
path: ["edges", i, "target"],
|
|
8518
|
+
message: `edge "${edge.id}" is a self-loop`
|
|
8519
|
+
});
|
|
8520
|
+
}
|
|
8521
|
+
});
|
|
8522
|
+
const groupIds = /* @__PURE__ */ new Set();
|
|
8523
|
+
doc.groups.forEach((group, i) => {
|
|
8524
|
+
if (groupIds.has(group.id)) {
|
|
8525
|
+
ctx.addIssue({
|
|
8526
|
+
code: z.ZodIssueCode.custom,
|
|
8527
|
+
path: ["groups", i, "id"],
|
|
8528
|
+
message: `duplicate group id "${group.id}"`
|
|
8529
|
+
});
|
|
8530
|
+
}
|
|
8531
|
+
groupIds.add(group.id);
|
|
8532
|
+
if (group.from > group.to) {
|
|
8533
|
+
ctx.addIssue({
|
|
8534
|
+
code: z.ZodIssueCode.custom,
|
|
8535
|
+
path: ["groups", i, "to"],
|
|
8536
|
+
message: `group "${group.id}" has from (${group.from}) greater than to (${group.to})`
|
|
8537
|
+
});
|
|
8538
|
+
}
|
|
8539
|
+
});
|
|
8540
|
+
for (let i = 0; i < doc.groups.length; i += 1) {
|
|
8541
|
+
const a = doc.groups[i];
|
|
8542
|
+
if (a.from > a.to) continue;
|
|
8543
|
+
const aColumns = new Set(resolveGroupColumns(a));
|
|
8544
|
+
for (let j = i + 1; j < doc.groups.length; j += 1) {
|
|
8545
|
+
const b = doc.groups[j];
|
|
8546
|
+
if (b.from > b.to) continue;
|
|
8547
|
+
const rowsOverlap = a.from <= b.to && b.from <= a.to;
|
|
8548
|
+
if (!rowsOverlap) continue;
|
|
8549
|
+
const shared = resolveGroupColumns(b).filter((c) => aColumns.has(c));
|
|
8550
|
+
if (shared.length === 0) continue;
|
|
8551
|
+
ctx.addIssue({
|
|
8552
|
+
code: z.ZodIssueCode.custom,
|
|
8553
|
+
path: ["groups", j, "from"],
|
|
8554
|
+
message: `group "${b.id}" overlaps rows ${a.from}..${a.to} of group "${a.id}" on column(s) ${shared.join(", ")}`
|
|
8555
|
+
});
|
|
8556
|
+
}
|
|
8557
|
+
}
|
|
8558
|
+
const legendIds = /* @__PURE__ */ new Set();
|
|
8559
|
+
doc.legend?.entries.forEach((entry, i) => {
|
|
8560
|
+
if (legendIds.has(entry.id)) {
|
|
8561
|
+
ctx.addIssue({
|
|
8562
|
+
code: z.ZodIssueCode.custom,
|
|
8563
|
+
path: ["legend", "entries", i, "id"],
|
|
8564
|
+
message: `duplicate legend entry id "${entry.id}"`
|
|
8565
|
+
});
|
|
8566
|
+
}
|
|
8567
|
+
legendIds.add(entry.id);
|
|
8568
|
+
});
|
|
8569
|
+
doc.nodes.forEach((node, i) => {
|
|
8570
|
+
if (node.badge === void 0) return;
|
|
8571
|
+
if (!legendIds.has(node.badge)) {
|
|
8572
|
+
ctx.addIssue({
|
|
8573
|
+
code: z.ZodIssueCode.custom,
|
|
8574
|
+
path: ["nodes", i, "badge"],
|
|
8575
|
+
message: `badge "${node.badge}" is not a legend entry of this document`
|
|
8576
|
+
});
|
|
8577
|
+
}
|
|
8578
|
+
});
|
|
8579
|
+
});
|
|
8580
|
+
var roadmapStateOverlaySchema = z.object({
|
|
8581
|
+
version: z.literal("roadmap-state/v1"),
|
|
8582
|
+
nodes: z.record(roadmapNodeStateSchema)
|
|
8583
|
+
}).strict();
|
|
8584
|
+
var roadmapStateSchema = roadmapStateOverlaySchema;
|
|
8585
|
+
function parseRoadmapDocument(input) {
|
|
8586
|
+
return roadmapDocumentSchema.parse(input);
|
|
8587
|
+
}
|
|
8588
|
+
function safeParseRoadmapDocument(input) {
|
|
8589
|
+
return roadmapDocumentSchema.safeParse(input);
|
|
8590
|
+
}
|
|
8591
|
+
function parseRoadmapStateOverlay(input) {
|
|
8592
|
+
return roadmapStateOverlaySchema.parse(input);
|
|
8593
|
+
}
|
|
8594
|
+
function safeParseRoadmapStateOverlay(input) {
|
|
8595
|
+
return roadmapStateOverlaySchema.safeParse(input);
|
|
8596
|
+
}
|
|
8597
|
+
|
|
8598
|
+
// src/components/organisms/roadmap/layout.ts
|
|
8599
|
+
function columnIndex(column) {
|
|
8600
|
+
return ROADMAP_COLUMNS.indexOf(column);
|
|
8601
|
+
}
|
|
8602
|
+
function gridColumnFor(node) {
|
|
8603
|
+
const span = resolveNodeSpan(node);
|
|
8604
|
+
if (span === 3) return { start: 1, span: 3 };
|
|
8605
|
+
return { start: columnIndex(node.column) + 1, span };
|
|
8606
|
+
}
|
|
8607
|
+
function rowsOf(doc) {
|
|
8608
|
+
const rows = /* @__PURE__ */ new Set();
|
|
8609
|
+
for (const node of doc.nodes) rows.add(node.order);
|
|
8610
|
+
return [...rows].sort((a, b) => a - b);
|
|
8611
|
+
}
|
|
8612
|
+
function maxRowOf(doc) {
|
|
8613
|
+
return doc.nodes.reduce((max, node) => Math.max(max, node.order), -1);
|
|
8614
|
+
}
|
|
8615
|
+
function nodesByRow(doc) {
|
|
8616
|
+
const byRow = /* @__PURE__ */ new Map();
|
|
8617
|
+
for (const node of doc.nodes) {
|
|
8618
|
+
const bucket = byRow.get(node.order);
|
|
8619
|
+
if (bucket) bucket.push(node);
|
|
8620
|
+
else byRow.set(node.order, [node]);
|
|
8621
|
+
}
|
|
8622
|
+
for (const bucket of byRow.values()) {
|
|
8623
|
+
bucket.sort((a, b) => {
|
|
8624
|
+
const byColumn = columnIndex(a.column) - columnIndex(b.column);
|
|
8625
|
+
return byColumn !== 0 ? byColumn : a.id.localeCompare(b.id);
|
|
8626
|
+
});
|
|
8627
|
+
}
|
|
8628
|
+
return byRow;
|
|
8629
|
+
}
|
|
8630
|
+
function columnsUsed(doc) {
|
|
8631
|
+
const used = /* @__PURE__ */ new Set();
|
|
8632
|
+
for (const node of doc.nodes) for (const column of columnsCovered(node)) used.add(column);
|
|
8633
|
+
return ROADMAP_COLUMNS.filter((column) => used.has(column));
|
|
8634
|
+
}
|
|
8635
|
+
function groupRanges(doc) {
|
|
8636
|
+
return doc.groups.map((group) => {
|
|
8637
|
+
const columns = resolveGroupColumns(group);
|
|
8638
|
+
const indices = columns.map(columnIndex).sort((a, b) => a - b);
|
|
8639
|
+
const start = indices[0];
|
|
8640
|
+
const end = indices[indices.length - 1];
|
|
8641
|
+
return {
|
|
8642
|
+
id: group.id,
|
|
8643
|
+
from: group.from,
|
|
8644
|
+
to: group.to,
|
|
8645
|
+
columns: ROADMAP_COLUMNS.filter((c) => columns.includes(c)),
|
|
8646
|
+
row: { start: group.from + 1, span: group.to - group.from + 1 },
|
|
8647
|
+
column: { start: start + 1, span: end - start + 1 },
|
|
8648
|
+
fragmented: end - start + 1 !== indices.length
|
|
8649
|
+
};
|
|
8650
|
+
});
|
|
8651
|
+
}
|
|
8652
|
+
function groupColumnRuns(group) {
|
|
8653
|
+
const indices = resolveGroupColumns(group).map(columnIndex).sort((a, b) => a - b);
|
|
8654
|
+
const runs = [];
|
|
8655
|
+
for (const index of indices) {
|
|
8656
|
+
const last = runs[runs.length - 1];
|
|
8657
|
+
if (last && last.start + last.span === index + 1) last.span += 1;
|
|
8658
|
+
else runs.push({ start: index + 1, span: 1 });
|
|
8659
|
+
}
|
|
8660
|
+
return runs;
|
|
8661
|
+
}
|
|
8662
|
+
function validateNoOverlap(doc) {
|
|
8663
|
+
const overlaps = [];
|
|
8664
|
+
const nodeCell = /* @__PURE__ */ new Map();
|
|
8665
|
+
for (const node of doc.nodes) {
|
|
8666
|
+
for (const column of columnsCovered(node)) {
|
|
8667
|
+
const key = `${column}:${node.order}`;
|
|
8668
|
+
const owner = nodeCell.get(key);
|
|
8669
|
+
if (owner !== void 0) {
|
|
8670
|
+
overlaps.push({ kind: "node", column, row: node.order, ids: [owner, node.id] });
|
|
8671
|
+
continue;
|
|
8672
|
+
}
|
|
8673
|
+
nodeCell.set(key, node.id);
|
|
8674
|
+
}
|
|
8675
|
+
}
|
|
8676
|
+
const groupCell = /* @__PURE__ */ new Map();
|
|
8677
|
+
for (const group of doc.groups) {
|
|
8678
|
+
if (group.from > group.to) continue;
|
|
8679
|
+
for (const column of resolveGroupColumns(group)) {
|
|
8680
|
+
for (let row = group.from; row <= group.to; row += 1) {
|
|
8681
|
+
const key = `${column}:${row}`;
|
|
8682
|
+
const owner = groupCell.get(key);
|
|
8683
|
+
if (owner !== void 0) {
|
|
8684
|
+
if (owner !== group.id) {
|
|
8685
|
+
overlaps.push({ kind: "group", column, row, ids: [owner, group.id] });
|
|
8686
|
+
}
|
|
8687
|
+
continue;
|
|
8688
|
+
}
|
|
8689
|
+
groupCell.set(key, group.id);
|
|
8690
|
+
}
|
|
8691
|
+
}
|
|
8692
|
+
}
|
|
8693
|
+
return overlaps;
|
|
8694
|
+
}
|
|
8695
|
+
function placeNodes(doc) {
|
|
8696
|
+
const byRow = nodesByRow(doc);
|
|
8697
|
+
const rows = rowsOf(doc);
|
|
8698
|
+
const placements = [];
|
|
8699
|
+
for (const row of rows) {
|
|
8700
|
+
for (const node of byRow.get(row) ?? []) {
|
|
8701
|
+
placements.push({
|
|
8702
|
+
node,
|
|
8703
|
+
row: { start: node.order + 1, span: 1 },
|
|
8704
|
+
column: gridColumnFor(node)
|
|
8705
|
+
});
|
|
8706
|
+
}
|
|
8707
|
+
}
|
|
8708
|
+
return { placements, rows, rowCount: maxRowOf(doc) + 1 };
|
|
8709
|
+
}
|
|
8710
|
+
function firstFreeRow(taken, columns, from) {
|
|
8711
|
+
let row = from;
|
|
8712
|
+
for (let guard = 0; guard < 1e5; guard += 1) {
|
|
8713
|
+
if (columns.every((column) => !taken.has(`${column}:${row}`))) return row;
|
|
8714
|
+
row += 1;
|
|
8715
|
+
}
|
|
8716
|
+
return row;
|
|
8717
|
+
}
|
|
8718
|
+
|
|
8719
|
+
// src/components/organisms/roadmap/Roadmap.tsx
|
|
8720
|
+
import { useMemo as useMemo5 } from "react";
|
|
8721
|
+
|
|
8722
|
+
// src/components/organisms/roadmap/RoadmapNode.tsx
|
|
8723
|
+
import { cva as cva14 } from "class-variance-authority";
|
|
8724
|
+
|
|
8725
|
+
// src/components/organisms/roadmap/RoadmapLegend.tsx
|
|
8726
|
+
import { Check as Check4, Globe, Lock, Star, BookOpen, Play, Circle as Circle2, CircleHelp, Clock, Link as Link2, ExternalLink } from "lucide-react";
|
|
8727
|
+
|
|
8728
|
+
// src/components/organisms/roadmap/appearance.ts
|
|
8729
|
+
function toneStyle(tone) {
|
|
8730
|
+
return {
|
|
8731
|
+
"--c-roadmap-node-bg": `var(--c-roadmap-${tone}-bg)`,
|
|
8732
|
+
"--c-roadmap-node-fg": `var(--c-roadmap-${tone}-fg)`
|
|
8733
|
+
};
|
|
8734
|
+
}
|
|
8735
|
+
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)]";
|
|
8736
|
+
|
|
8737
|
+
// src/components/organisms/roadmap/RoadmapLegend.tsx
|
|
8738
|
+
import { jsx as jsx65, jsxs as jsxs40 } from "react/jsx-runtime";
|
|
8739
|
+
var glyphs = { check: Check4, globe: Globe, lock: Lock, star: Star, "book-open": BookOpen, play: Play, circle: Circle2, clock: Clock, link: Link2, "external-link": ExternalLink };
|
|
8740
|
+
function RoadmapBadge({ name = "check", tone = "neutral", label, className = "" }) {
|
|
8741
|
+
const Glyph = glyphs[name] ?? CircleHelp;
|
|
8742
|
+
return /* @__PURE__ */ jsx65("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__ */ jsx65(Glyph, { size: 12, "aria-hidden": "true" }) });
|
|
8743
|
+
}
|
|
8744
|
+
function RoadmapLegendView({ legend, testIdPrefix = "roadmap" }) {
|
|
8745
|
+
return /* @__PURE__ */ jsx65("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__ */ jsx65("ul", { className: "m-0 flex list-none flex-wrap gap-3 p-0", children: legend.entries.map((entry) => /* @__PURE__ */ jsxs40("li", { className: "flex min-w-0 items-center gap-2 text-xs leading-normal", children: [
|
|
8746
|
+
/* @__PURE__ */ jsx65(RoadmapBadge, { name: entry.icon, tone: entry.tone }),
|
|
8747
|
+
/* @__PURE__ */ jsx65("span", { className: "[overflow-wrap:anywhere]", children: entry.label })
|
|
8748
|
+
] }, entry.id)) }) });
|
|
8749
|
+
}
|
|
8750
|
+
|
|
8751
|
+
// src/components/organisms/roadmap/RoadmapNode.tsx
|
|
8752
|
+
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-2 py-3 text-center text-xs leading-normal md:px-4 md:text-base text-[var(--c-roadmap-node-fg)]", {
|
|
8754
|
+
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 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)]" },
|
|
8756
|
+
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
|
+
}
|
|
8758
|
+
});
|
|
8759
|
+
function RoadmapNodeView({ node, state, badge, testIdPrefix, onNodeClick, onAction }) {
|
|
8760
|
+
const locked = state === "locked";
|
|
8761
|
+
const nodeTone = resolveNodeTone(node);
|
|
8762
|
+
const label = /* @__PURE__ */ jsx66("span", { "data-testid": `${testIdPrefix}-node-label`, className: `[overflow-wrap:anywhere] ${state === "done" ? "line-through" : state === "learning" ? "underline" : ""}`, children: node.label });
|
|
8763
|
+
const interactiveClass = `block w-full min-w-0 text-inherit ${focusClass}`;
|
|
8764
|
+
const wholeBox = !locked && !node.action && !node.links?.length;
|
|
8765
|
+
const Box = wholeBox && node.href ? "a" : wholeBox && onNodeClick ? "button" : "div";
|
|
8766
|
+
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
|
+
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: [
|
|
8769
|
+
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
|
+
primary,
|
|
8771
|
+
node.description && node.kind === "paragraph" && /* @__PURE__ */ jsx66("p", { className: "mt-2 [overflow-wrap:anywhere]", children: node.description }),
|
|
8772
|
+
node.links && /* @__PURE__ */ jsx66("ul", { className: "mt-2 list-none space-y-2 p-0", children: node.links.map((link, index) => /* @__PURE__ */ jsx66("li", { children: locked ? /* @__PURE__ */ jsx66("span", { children: link.label }) : /* @__PURE__ */ jsx66("a", { href: link.href, className: `underline [overflow-wrap:anywhere] ${focusClass}`, onClick: (e) => e.stopPropagation(), children: link.label }) }, index)) }),
|
|
8773
|
+
node.action && /* @__PURE__ */ jsx66("div", { style: toneStyle(node.action.tone ?? "primary"), className: "mt-3 min-w-0", children: node.action.href && !locked ? /* @__PURE__ */ jsx66("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__ */ jsx66("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) => {
|
|
8774
|
+
e.stopPropagation();
|
|
8775
|
+
if (node.action?.actionId) onAction?.(node.action.actionId, node);
|
|
8776
|
+
}, children: node.action.label }) })
|
|
8777
|
+
] }) });
|
|
8778
|
+
}
|
|
8779
|
+
|
|
8780
|
+
// src/components/organisms/roadmap/RoadmapGroup.tsx
|
|
8781
|
+
import { Fragment as Fragment10, jsx as jsx67, jsxs as jsxs42 } from "react/jsx-runtime";
|
|
8782
|
+
function RoadmapGroupView({ group, testIdPrefix }) {
|
|
8783
|
+
return /* @__PURE__ */ jsx67(Fragment10, { children: groupColumnRuns(group).map((run, index) => /* @__PURE__ */ jsxs42(
|
|
8784
|
+
"div",
|
|
8785
|
+
{
|
|
8786
|
+
"data-testid": `${testIdPrefix}-group`,
|
|
8787
|
+
"data-group-id": group.id,
|
|
8788
|
+
style: { ...toneStyle(resolveGroupTone(group)), gridColumn: `${run.start} / span ${run.span}`, gridRow: `${group.from + 1} / span ${group.to - group.from + 1}` },
|
|
8789
|
+
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
|
+
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 [overflow-wrap:anywhere]", children: group.title }) }),
|
|
8792
|
+
index === 0 && group.description && /* @__PURE__ */ jsx67("span", { className: "sr-only", children: group.description })
|
|
8793
|
+
]
|
|
8794
|
+
},
|
|
8795
|
+
run.start
|
|
8796
|
+
)) });
|
|
8797
|
+
}
|
|
8798
|
+
|
|
8799
|
+
// src/components/organisms/roadmap/Roadmap.tsx
|
|
8800
|
+
import { jsx as jsx68, jsxs as jsxs43 } from "react/jsx-runtime";
|
|
8801
|
+
function Roadmap({ document: document2, state, renderNode, onNodeClick, onAction, testIdPrefix = "roadmap", className = "", ariaLabel }) {
|
|
8802
|
+
const { placements } = useMemo5(() => placeNodes(document2), [document2]);
|
|
8803
|
+
const badges = useMemo5(() => new Map(document2.legend?.entries.map((entry) => [entry.id, entry])), [document2.legend]);
|
|
8804
|
+
const legendPlacement = document2.legend ? resolveLegendPlacement(document2.legend) : void 0;
|
|
8805
|
+
const legend = document2.legend && /* @__PURE__ */ jsx68(RoadmapLegendView, { legend: document2.legend, testIdPrefix });
|
|
8806
|
+
return /* @__PURE__ */ jsxs43("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: [
|
|
8807
|
+
legendPlacement === "top" && /* @__PURE__ */ jsx68("div", { className: "mb-6", children: legend }),
|
|
8808
|
+
/* @__PURE__ */ jsxs43("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: [
|
|
8809
|
+
document2.groups.map((group) => /* @__PURE__ */ jsx68(RoadmapGroupView, { group, testIdPrefix }, group.id)),
|
|
8810
|
+
placements.map(({ node, row, column }) => {
|
|
8811
|
+
const resolvedState = resolveNodeState(node, state);
|
|
8812
|
+
return /* @__PURE__ */ jsx68(
|
|
8813
|
+
"div",
|
|
8814
|
+
{
|
|
8815
|
+
role: "listitem",
|
|
8816
|
+
"data-testid": `${testIdPrefix}-node`,
|
|
8817
|
+
"data-node-id": node.id,
|
|
8818
|
+
"data-state": resolvedState,
|
|
8819
|
+
style: { gridColumn: `${column.start} / span ${column.span}`, gridRow: `${row.start} / span 1` },
|
|
8820
|
+
className: "relative z-10 min-w-0 px-2 py-4",
|
|
8821
|
+
children: node.kind === "legend" ? legendPlacement === "inline" ? legend : null : renderNode ? renderNode(node, resolvedState) : /* @__PURE__ */ jsx68(RoadmapNodeView, { node, state: resolvedState, badge: node.badge ? badges.get(node.badge) : void 0, onNodeClick, onAction, testIdPrefix })
|
|
8822
|
+
},
|
|
8823
|
+
node.id
|
|
8824
|
+
);
|
|
8825
|
+
})
|
|
8826
|
+
] }),
|
|
8827
|
+
legendPlacement === "bottom" && /* @__PURE__ */ jsx68("div", { className: "mt-6", children: legend })
|
|
8828
|
+
] });
|
|
8829
|
+
}
|
|
8830
|
+
|
|
8308
8831
|
// src/hooks/use-iframe-auth.ts
|
|
8309
8832
|
import { useContext as useContext9 } from "react";
|
|
8310
8833
|
function useIframeAuth() {
|
|
@@ -8328,9 +8851,9 @@ function useIframeNavigate() {
|
|
|
8328
8851
|
import {
|
|
8329
8852
|
createContext as createContext10,
|
|
8330
8853
|
useContext as useContext10,
|
|
8331
|
-
useMemo as
|
|
8854
|
+
useMemo as useMemo6
|
|
8332
8855
|
} from "react";
|
|
8333
|
-
import { jsx as
|
|
8856
|
+
import { jsx as jsx69 } from "react/jsx-runtime";
|
|
8334
8857
|
var FeatureFlagContext = createContext10({
|
|
8335
8858
|
flags: {},
|
|
8336
8859
|
isEnabled: () => false
|
|
@@ -8339,14 +8862,14 @@ var FeatureFlagProvider = ({
|
|
|
8339
8862
|
children,
|
|
8340
8863
|
flags
|
|
8341
8864
|
}) => {
|
|
8342
|
-
const value =
|
|
8865
|
+
const value = useMemo6(
|
|
8343
8866
|
() => ({
|
|
8344
8867
|
flags,
|
|
8345
8868
|
isEnabled: (flag) => Boolean(flags[flag])
|
|
8346
8869
|
}),
|
|
8347
8870
|
[flags]
|
|
8348
8871
|
);
|
|
8349
|
-
return /* @__PURE__ */
|
|
8872
|
+
return /* @__PURE__ */ jsx69(FeatureFlagContext.Provider, { value, children });
|
|
8350
8873
|
};
|
|
8351
8874
|
var useFeatureFlag = (flag) => {
|
|
8352
8875
|
const { isEnabled } = useContext10(FeatureFlagContext);
|
|
@@ -8445,7 +8968,14 @@ export {
|
|
|
8445
8968
|
ContextMenuSubContent,
|
|
8446
8969
|
ContextMenuSubTrigger,
|
|
8447
8970
|
ContextMenuTrigger,
|
|
8971
|
+
DEFAULT_EDGE_ARROW,
|
|
8972
|
+
DEFAULT_EDGE_ROUTE,
|
|
8973
|
+
DEFAULT_EDGE_STYLE,
|
|
8974
|
+
DEFAULT_GROUP_TONE,
|
|
8975
|
+
DEFAULT_LEGEND_PLACEMENT,
|
|
8448
8976
|
DEFAULT_NAME_COL_WIDTH,
|
|
8977
|
+
DEFAULT_NODE_SPAN,
|
|
8978
|
+
DEFAULT_NODE_STATE,
|
|
8449
8979
|
DflRemote,
|
|
8450
8980
|
Dialog,
|
|
8451
8981
|
DialogClose,
|
|
@@ -8523,6 +9053,7 @@ export {
|
|
|
8523
9053
|
MenubarSubContent,
|
|
8524
9054
|
MenubarSubTrigger,
|
|
8525
9055
|
MenubarTrigger,
|
|
9056
|
+
NODE_KIND_DEFAULT_TONE,
|
|
8526
9057
|
NavigationMenu,
|
|
8527
9058
|
NavigationMenuContent,
|
|
8528
9059
|
NavigationMenuIndicator,
|
|
@@ -8546,12 +9077,24 @@ export {
|
|
|
8546
9077
|
Progress,
|
|
8547
9078
|
ProtectedRoute,
|
|
8548
9079
|
PublishDrawer,
|
|
9080
|
+
ROADMAP_ARROWS,
|
|
9081
|
+
ROADMAP_COLUMNS,
|
|
9082
|
+
ROADMAP_EDGE_LABEL_MAX,
|
|
9083
|
+
ROADMAP_EDGE_ROUTES,
|
|
9084
|
+
ROADMAP_EDGE_STYLES,
|
|
9085
|
+
ROADMAP_LABEL_MAX,
|
|
9086
|
+
ROADMAP_LEGEND_PLACEMENTS,
|
|
9087
|
+
ROADMAP_NODE_ID_PATTERN,
|
|
9088
|
+
ROADMAP_NODE_KINDS,
|
|
9089
|
+
ROADMAP_NODE_STATES,
|
|
9090
|
+
ROADMAP_TONES,
|
|
8549
9091
|
RadioGroup4 as RadioGroup,
|
|
8550
9092
|
RadioGroupItem,
|
|
8551
9093
|
RadioGroupRow,
|
|
8552
9094
|
ResizableHandle,
|
|
8553
9095
|
ResizablePanel,
|
|
8554
9096
|
ResizablePanelGroup,
|
|
9097
|
+
Roadmap,
|
|
8555
9098
|
ScrollArea,
|
|
8556
9099
|
ScrollBar,
|
|
8557
9100
|
Select,
|
|
@@ -8639,19 +9182,55 @@ export {
|
|
|
8639
9182
|
buttonVariants,
|
|
8640
9183
|
clampPct,
|
|
8641
9184
|
cn,
|
|
9185
|
+
columnIndex,
|
|
9186
|
+
columnsCovered,
|
|
9187
|
+
columnsUsed,
|
|
8642
9188
|
filterPublishableAccounts,
|
|
9189
|
+
firstFreeRow,
|
|
8643
9190
|
getInitials,
|
|
9191
|
+
gridColumnFor,
|
|
9192
|
+
groupColumnRuns,
|
|
9193
|
+
groupRanges,
|
|
8644
9194
|
iconButtonVariants,
|
|
8645
9195
|
isAllowedOrigin,
|
|
8646
9196
|
kbdVariants,
|
|
8647
9197
|
labelVariants,
|
|
9198
|
+
maxRowOf,
|
|
8648
9199
|
memberHueIndex,
|
|
8649
9200
|
memberHueVar,
|
|
8650
9201
|
navigationMenuTriggerStyle,
|
|
9202
|
+
nodesByRow,
|
|
9203
|
+
parseRoadmapDocument,
|
|
9204
|
+
parseRoadmapStateOverlay,
|
|
8651
9205
|
parseTags,
|
|
9206
|
+
placeNodes,
|
|
9207
|
+
resolveEdgeArrow,
|
|
9208
|
+
resolveEdgeRoute,
|
|
9209
|
+
resolveEdgeStyle,
|
|
9210
|
+
resolveGroupColumns,
|
|
9211
|
+
resolveGroupTone,
|
|
9212
|
+
resolveLegendPlacement,
|
|
8652
9213
|
resolveNameColWidth,
|
|
9214
|
+
resolveNodeSpan,
|
|
9215
|
+
resolveNodeState,
|
|
9216
|
+
resolveNodeTone,
|
|
8653
9217
|
resolveWeekCount,
|
|
8654
9218
|
resolveWeekLabels,
|
|
9219
|
+
roadmapActionSchema,
|
|
9220
|
+
roadmapDocumentSchema,
|
|
9221
|
+
roadmapEdgeSchema,
|
|
9222
|
+
roadmapGroupSchema,
|
|
9223
|
+
roadmapIconSchema,
|
|
9224
|
+
roadmapLegendEntrySchema,
|
|
9225
|
+
roadmapLegendSchema,
|
|
9226
|
+
roadmapLinkSchema,
|
|
9227
|
+
roadmapNodeSchema,
|
|
9228
|
+
roadmapNodeStateSchema,
|
|
9229
|
+
roadmapStateOverlaySchema,
|
|
9230
|
+
roadmapStateSchema,
|
|
9231
|
+
rowsOf,
|
|
9232
|
+
safeParseRoadmapDocument,
|
|
9233
|
+
safeParseRoadmapStateOverlay,
|
|
8655
9234
|
stageProgress,
|
|
8656
9235
|
toast,
|
|
8657
9236
|
toggleVariants,
|
|
@@ -8665,5 +9244,6 @@ export {
|
|
|
8665
9244
|
useSession,
|
|
8666
9245
|
useSidebar,
|
|
8667
9246
|
useToast,
|
|
9247
|
+
validateNoOverlap,
|
|
8668
9248
|
validatePublishForm
|
|
8669
9249
|
};
|