nuxt-ui-elements-pro 0.1.5 → 0.1.7

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.
Files changed (67) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/module.mjs +153 -18
  3. package/dist/runtime/components/EventCalendar.d.vue.ts +109 -22
  4. package/dist/runtime/components/EventCalendar.vue +225 -602
  5. package/dist/runtime/components/EventCalendar.vue.d.ts +109 -22
  6. package/dist/runtime/components/OrgChart.d.vue.ts +191 -0
  7. package/dist/runtime/components/OrgChart.vue +290 -0
  8. package/dist/runtime/components/OrgChart.vue.d.ts +191 -0
  9. package/dist/runtime/components/event-calendar/EventCalendarHeader.d.vue.ts +45 -0
  10. package/dist/runtime/components/event-calendar/EventCalendarHeader.vue +55 -0
  11. package/dist/runtime/components/event-calendar/EventCalendarHeader.vue.d.ts +45 -0
  12. package/dist/runtime/components/event-calendar/EventCalendarListView.d.vue.ts +25 -0
  13. package/dist/runtime/components/event-calendar/EventCalendarListView.vue +95 -0
  14. package/dist/runtime/components/event-calendar/EventCalendarListView.vue.d.ts +25 -0
  15. package/dist/runtime/components/event-calendar/EventCalendarMonthView.d.vue.ts +34 -0
  16. package/dist/runtime/components/event-calendar/EventCalendarMonthView.vue +336 -0
  17. package/dist/runtime/components/event-calendar/EventCalendarMonthView.vue.d.ts +34 -0
  18. package/dist/runtime/components/event-calendar/EventCalendarTimeGrid.d.vue.ts +31 -0
  19. package/dist/runtime/components/event-calendar/EventCalendarTimeGrid.vue +306 -0
  20. package/dist/runtime/components/event-calendar/EventCalendarTimeGrid.vue.d.ts +31 -0
  21. package/dist/runtime/components/org-chart/OrgChartConnectors.d.vue.ts +15 -0
  22. package/dist/runtime/components/org-chart/OrgChartConnectors.vue +29 -0
  23. package/dist/runtime/components/org-chart/OrgChartConnectors.vue.d.ts +15 -0
  24. package/dist/runtime/components/org-chart/OrgChartControls.d.vue.ts +19 -0
  25. package/dist/runtime/components/org-chart/OrgChartControls.vue +25 -0
  26. package/dist/runtime/components/org-chart/OrgChartControls.vue.d.ts +19 -0
  27. package/dist/runtime/components/org-chart/OrgChartNode.d.vue.ts +30 -0
  28. package/dist/runtime/components/org-chart/OrgChartNode.vue +129 -0
  29. package/dist/runtime/components/org-chart/OrgChartNode.vue.d.ts +30 -0
  30. package/dist/runtime/composables/useEventCalendar.d.ts +52 -0
  31. package/dist/runtime/composables/useEventCalendar.js +362 -0
  32. package/dist/runtime/composables/useEventCalendarContext.d.ts +8 -0
  33. package/dist/runtime/composables/useEventCalendarContext.js +11 -0
  34. package/dist/runtime/composables/useEventCalendarDragDrop.d.ts +1 -1
  35. package/dist/runtime/composables/useEventCalendarDragDrop.js +11 -9
  36. package/dist/runtime/composables/useEventCalendarKeyboard.d.ts +20 -0
  37. package/dist/runtime/composables/useEventCalendarKeyboard.js +128 -0
  38. package/dist/runtime/composables/useEventCalendarResize.d.ts +31 -0
  39. package/dist/runtime/composables/useEventCalendarResize.js +87 -0
  40. package/dist/runtime/composables/useEventCalendarSelect.d.ts +21 -0
  41. package/dist/runtime/composables/useEventCalendarSelect.js +119 -0
  42. package/dist/runtime/composables/useOrgChart.d.ts +42 -0
  43. package/dist/runtime/composables/useOrgChart.js +154 -0
  44. package/dist/runtime/composables/useOrgChartContext.d.ts +8 -0
  45. package/dist/runtime/composables/useOrgChartContext.js +11 -0
  46. package/dist/runtime/composables/useOrgChartDragDrop.d.ts +20 -0
  47. package/dist/runtime/composables/useOrgChartDragDrop.js +67 -0
  48. package/dist/runtime/composables/useOrgChartKeyboard.d.ts +16 -0
  49. package/dist/runtime/composables/useOrgChartKeyboard.js +101 -0
  50. package/dist/runtime/composables/useOrgChartSearch.d.ts +16 -0
  51. package/dist/runtime/composables/useOrgChartSearch.js +62 -0
  52. package/dist/runtime/composables/useOrgChartZoom.d.ts +26 -0
  53. package/dist/runtime/composables/useOrgChartZoom.js +101 -0
  54. package/dist/runtime/index.d.ts +3 -0
  55. package/dist/runtime/index.js +2 -0
  56. package/dist/runtime/types/event-calendar.d.ts +170 -0
  57. package/dist/runtime/types/index.d.ts +2 -0
  58. package/dist/runtime/types/index.js +2 -0
  59. package/dist/runtime/types/org-chart.d.ts +181 -0
  60. package/dist/runtime/types/org-chart.js +0 -0
  61. package/dist/runtime/utils/event-calendar.d.ts +22 -1
  62. package/dist/runtime/utils/event-calendar.js +199 -1
  63. package/dist/runtime/utils/org-chart.d.ts +55 -0
  64. package/dist/runtime/utils/org-chart.js +367 -0
  65. package/dist/runtime/utils/recurrence.d.ts +30 -0
  66. package/dist/runtime/utils/recurrence.js +150 -0
  67. package/package.json +15 -6
@@ -1,4 +1,12 @@
1
- import { toCalendarDateTime } from "@internationalized/date";
1
+ import { getDayOfWeek, toCalendarDate, toCalendarDateTime } from "@internationalized/date";
2
+ export function startOfWeekByDay(date, weekStartsOn) {
3
+ const dow = getDayOfWeek(date, "en-US");
4
+ const diff = (dow - weekStartsOn + 7) % 7;
5
+ return toCalendarDate(date.subtract({ days: diff }));
6
+ }
7
+ export function endOfWeekByDay(date, weekStartsOn) {
8
+ return toCalendarDate(startOfWeekByDay(date, weekStartsOn).add({ days: 6 }));
9
+ }
2
10
  export function hasTimeComponent(d) {
3
11
  return "hour" in d;
4
12
  }
@@ -65,3 +73,193 @@ export function layoutTimedEvents(events, config, slotHeight) {
65
73
  }
66
74
  return timed;
67
75
  }
76
+ export function layoutMonthWeekEvents(week, maxEvents) {
77
+ const days = week.days;
78
+ const weekStartDate = days[0].date;
79
+ const weekEndDate = days[6].date;
80
+ const seenSpanning = /* @__PURE__ */ new Set();
81
+ const spanningCandidates = [];
82
+ const singleDayByCol = Array.from({ length: 7 }, () => []);
83
+ for (let col = 0; col < 7; col++) {
84
+ const day = days[col];
85
+ for (const event of day.events) {
86
+ const eventStartDate = toCalendarDate(event.start);
87
+ const eventEndDate = toCalendarDate(event.end);
88
+ const isMultiDay = eventStartDate.compare(eventEndDate) !== 0;
89
+ const isSpanning = event.allDay || isMultiDay;
90
+ if (isSpanning) {
91
+ if (!seenSpanning.has(event.id)) {
92
+ seenSpanning.add(event.id);
93
+ const clampedStart = eventStartDate.compare(weekStartDate) < 0 ? weekStartDate : eventStartDate;
94
+ const clampedEnd = eventEndDate.compare(weekEndDate) > 0 ? weekEndDate : eventEndDate;
95
+ let startCol = 0;
96
+ let endCol = 6;
97
+ for (let i = 0; i < 7; i++) {
98
+ if (days[i].date.compare(clampedStart) === 0) startCol = i;
99
+ if (days[i].date.compare(clampedEnd) === 0) endCol = i;
100
+ }
101
+ spanningCandidates.push({
102
+ event,
103
+ startCol,
104
+ endCol,
105
+ isStart: eventStartDate.compare(weekStartDate) >= 0,
106
+ isEnd: eventEndDate.compare(weekEndDate) <= 0
107
+ });
108
+ }
109
+ } else {
110
+ singleDayByCol[col].push(event);
111
+ }
112
+ }
113
+ }
114
+ spanningCandidates.sort((a, b) => {
115
+ if (a.startCol !== b.startCol) return a.startCol - b.startCol;
116
+ const spanA = a.endCol - a.startCol;
117
+ const spanB = b.endCol - b.startCol;
118
+ if (spanA !== spanB) return spanB - spanA;
119
+ return String(a.event.id).localeCompare(String(b.event.id));
120
+ });
121
+ const laneOccupancy = [];
122
+ const spanning = [];
123
+ for (const candidate of spanningCandidates) {
124
+ const { event, startCol, endCol, isStart, isEnd } = candidate;
125
+ const span = endCol - startCol + 1;
126
+ let assignedLane = -1;
127
+ for (let lane = 0; lane < laneOccupancy.length; lane++) {
128
+ let free = true;
129
+ for (let col = startCol; col <= endCol; col++) {
130
+ if (laneOccupancy[lane][col]) {
131
+ free = false;
132
+ break;
133
+ }
134
+ }
135
+ if (free) {
136
+ assignedLane = lane;
137
+ break;
138
+ }
139
+ }
140
+ if (assignedLane === -1) {
141
+ assignedLane = laneOccupancy.length;
142
+ laneOccupancy.push(new Array(7).fill(false));
143
+ }
144
+ for (let col = startCol; col <= endCol; col++) {
145
+ laneOccupancy[assignedLane][col] = true;
146
+ }
147
+ spanning.push({ event, startCol, span, lane: assignedLane, isStart, isEnd });
148
+ }
149
+ const perColSpanningOffset = new Array(7).fill(0);
150
+ for (let lane = 0; lane < laneOccupancy.length; lane++) {
151
+ for (let col = 0; col < 7; col++) {
152
+ if (laneOccupancy[lane][col]) {
153
+ perColSpanningOffset[col] = lane + 1;
154
+ }
155
+ }
156
+ }
157
+ const perColVisibleOffset = perColSpanningOffset.map((o) => Math.min(o, maxEvents));
158
+ const singleDay = [];
159
+ const overflow = [];
160
+ let totalEventRows = 0;
161
+ for (let col = 0; col < 7; col++) {
162
+ const colSingleDay = singleDayByCol[col];
163
+ const colOffset = perColVisibleOffset[col];
164
+ const colRemainingSlots = Math.max(0, maxEvents - colOffset);
165
+ const visibleSingle = colSingleDay.slice(0, colRemainingSlots);
166
+ for (let row = 0; row < visibleSingle.length; row++) {
167
+ singleDay.push({ event: visibleSingle[row], col, row, gridRow: colOffset + row + 2 });
168
+ }
169
+ totalEventRows = Math.max(totalEventRows, colOffset + visibleSingle.length);
170
+ const hiddenSpanning = [];
171
+ for (const s of spanning) {
172
+ if (s.lane >= maxEvents && s.startCol <= col && s.startCol + s.span - 1 >= col) {
173
+ hiddenSpanning.push(s.event);
174
+ }
175
+ }
176
+ const hiddenSingle = colSingleDay.slice(colRemainingSlots);
177
+ const hiddenCount = hiddenSpanning.length + hiddenSingle.length;
178
+ if (hiddenCount > 0) {
179
+ overflow.push({
180
+ col,
181
+ hiddenCount,
182
+ hiddenEvents: [...hiddenSpanning, ...hiddenSingle],
183
+ gridRow: 0
184
+ // set after loop
185
+ });
186
+ }
187
+ }
188
+ for (const item of overflow) {
189
+ item.gridRow = totalEventRows + 2;
190
+ }
191
+ return {
192
+ week,
193
+ spanning,
194
+ singleDay,
195
+ overflow,
196
+ totalEventRows,
197
+ spanningLaneCount: Math.min(laneOccupancy.length, maxEvents)
198
+ };
199
+ }
200
+ export function layoutAllDayEvents(days) {
201
+ const colCount = days.length;
202
+ if (colCount === 0) return { spanning: [], laneCount: 0 };
203
+ const startDate = days[0].date;
204
+ const endDate = days[colCount - 1].date;
205
+ const seen = /* @__PURE__ */ new Set();
206
+ const candidates = [];
207
+ for (let col = 0; col < colCount; col++) {
208
+ for (const event of days[col].allDayEvents) {
209
+ if (seen.has(event.id)) continue;
210
+ seen.add(event.id);
211
+ const eventStartDate = toCalendarDate(event.start);
212
+ const eventEndDate = toCalendarDate(event.end);
213
+ const clampedStart = eventStartDate.compare(startDate) < 0 ? startDate : eventStartDate;
214
+ const clampedEnd = eventEndDate.compare(endDate) > 0 ? endDate : eventEndDate;
215
+ let sc = 0;
216
+ let ec = colCount - 1;
217
+ for (let i = 0; i < colCount; i++) {
218
+ if (days[i].date.compare(clampedStart) === 0) sc = i;
219
+ if (days[i].date.compare(clampedEnd) === 0) ec = i;
220
+ }
221
+ candidates.push({
222
+ event,
223
+ startCol: sc,
224
+ endCol: ec,
225
+ isStart: eventStartDate.compare(startDate) >= 0,
226
+ isEnd: eventEndDate.compare(endDate) <= 0
227
+ });
228
+ }
229
+ }
230
+ candidates.sort((a, b) => {
231
+ if (a.startCol !== b.startCol) return a.startCol - b.startCol;
232
+ const spanA = a.endCol - a.startCol;
233
+ const spanB = b.endCol - b.startCol;
234
+ if (spanA !== spanB) return spanB - spanA;
235
+ return String(a.event.id).localeCompare(String(b.event.id));
236
+ });
237
+ const laneOccupancy = [];
238
+ const spanning = [];
239
+ for (const { event, startCol, endCol, isStart, isEnd } of candidates) {
240
+ const span = endCol - startCol + 1;
241
+ let assignedLane = -1;
242
+ for (let lane = 0; lane < laneOccupancy.length; lane++) {
243
+ let free = true;
244
+ for (let col = startCol; col <= endCol; col++) {
245
+ if (laneOccupancy[lane][col]) {
246
+ free = false;
247
+ break;
248
+ }
249
+ }
250
+ if (free) {
251
+ assignedLane = lane;
252
+ break;
253
+ }
254
+ }
255
+ if (assignedLane === -1) {
256
+ assignedLane = laneOccupancy.length;
257
+ laneOccupancy.push(new Array(colCount).fill(false));
258
+ }
259
+ for (let col = startCol; col <= endCol; col++) {
260
+ laneOccupancy[assignedLane][col] = true;
261
+ }
262
+ spanning.push({ event, startCol, span, lane: assignedLane, isStart, isEnd });
263
+ }
264
+ return { spanning, laneCount: laneOccupancy.length };
265
+ }
@@ -0,0 +1,55 @@
1
+ import type { OrgChartNode, OrgChartTreeNode, OrgChartConnector, OrgChartConnection, OrgChartConnectionPath, OrgChartLayout, OrgChartDirection, OrgChartConnectorStyle, ResolvedLayoutConfig } from "../types/org-chart.js";
2
+ /**
3
+ * Build a tree from a flat node list.
4
+ * Validates exactly one root (parentId === null). Returns null for empty input.
5
+ */
6
+ export declare function buildTree(nodes: OrgChartNode[]): OrgChartTreeNode | null;
7
+ /**
8
+ * Compute full layout for the tree.
9
+ * Returns positioned nodes, connector paths, and total dimensions.
10
+ */
11
+ export declare function computeLayout(root: OrgChartTreeNode, config: ResolvedLayoutConfig, direction: OrgChartDirection, expandedIds: Set<string | number>): OrgChartLayout;
12
+ /** Overloaded layout that also accepts connector style */
13
+ export declare function computeFullLayout(root: OrgChartTreeNode, config: ResolvedLayoutConfig, direction: OrgChartDirection, connectorStyle: OrgChartConnectorStyle, expandedIds: Set<string | number>, connections?: OrgChartConnection[]): OrgChartLayout;
14
+ /**
15
+ * Compute SVG path data for connectors between parent-child pairs.
16
+ */
17
+ export declare function computeConnectors(nodes: OrgChartTreeNode[], config: ResolvedLayoutConfig, direction: OrgChartDirection, style: OrgChartConnectorStyle): OrgChartConnector[];
18
+ /**
19
+ * Compute SVG paths for non-hierarchical connections between arbitrary nodes.
20
+ * These are rendered as curved bezier paths offset from the straight line to
21
+ * avoid overlapping with hierarchical connectors.
22
+ */
23
+ export declare function computeConnectionPaths(nodes: OrgChartTreeNode[], config: ResolvedLayoutConfig, direction: OrgChartDirection, connections: OrgChartConnection[]): OrgChartConnectionPath[];
24
+ /**
25
+ * Flatten visible tree in DFS order, skipping collapsed subtrees.
26
+ * The root is always included.
27
+ */
28
+ export declare function flattenVisibleTree(root: OrgChartTreeNode, expandedIds: Set<string | number>): OrgChartTreeNode[];
29
+ /**
30
+ * Get ancestors of a node (from immediate parent to root).
31
+ */
32
+ export declare function getAncestors(nodeId: string | number, nodeMap: Map<string | number, OrgChartTreeNode>): OrgChartTreeNode[];
33
+ /**
34
+ * Get all descendants of a node (DFS, not including the node itself).
35
+ */
36
+ export declare function getDescendants(node: OrgChartTreeNode): OrgChartTreeNode[];
37
+ /**
38
+ * Collect all node IDs in the tree.
39
+ */
40
+ export declare function getAllNodeIds(root: OrgChartTreeNode): Set<string | number>;
41
+ /**
42
+ * Build a Map from node ID to OrgChartTreeNode for O(1) lookups.
43
+ */
44
+ export declare function buildNodeMap(root: OrgChartTreeNode): Map<string | number, OrgChartTreeNode>;
45
+ /** Default layout config values */
46
+ export declare const DEFAULT_LAYOUT_CONFIG: ResolvedLayoutConfig;
47
+ /** Default zoom config values */
48
+ export declare const DEFAULT_ZOOM_CONFIG: {
49
+ readonly enabled: false;
50
+ readonly min: 0.25;
51
+ readonly max: 2;
52
+ readonly initial: 1;
53
+ readonly step: 0.1;
54
+ readonly controls: true;
55
+ };
@@ -0,0 +1,367 @@
1
+ export function buildTree(nodes) {
2
+ if (nodes.length === 0) return null;
3
+ const ROOT_KEY = Symbol("root");
4
+ const childrenMap = /* @__PURE__ */ new Map();
5
+ for (const node of nodes) {
6
+ const key = node.parentId === null ? ROOT_KEY : node.parentId;
7
+ const list = childrenMap.get(key);
8
+ if (list) list.push(node);
9
+ else childrenMap.set(key, [node]);
10
+ }
11
+ const roots = childrenMap.get(ROOT_KEY);
12
+ if (!roots || roots.length === 0) {
13
+ throw new Error("[OrgChart] No root node found (parentId === null).");
14
+ }
15
+ if (roots.length > 1) {
16
+ throw new Error(`[OrgChart] Multiple root nodes found: ${roots.map((r) => r.id).join(", ")}. Only one root is supported.`);
17
+ }
18
+ function build(data, parent, depth, indexInSiblings, siblingCount) {
19
+ const node = {
20
+ data,
21
+ parent,
22
+ children: [],
23
+ depth,
24
+ indexInSiblings,
25
+ siblingCount,
26
+ descendantCount: 0,
27
+ x: 0,
28
+ y: 0
29
+ };
30
+ const childData = childrenMap.get(data.id) ?? [];
31
+ node.children = childData.map((child, i) => build(child, node, depth + 1, i, childData.length));
32
+ node.descendantCount = node.children.reduce((sum, c) => sum + 1 + c.descendantCount, 0);
33
+ return node;
34
+ }
35
+ return build(roots[0], null, 0, 0, 1);
36
+ }
37
+ function buildLayoutTree(root, expandedIds) {
38
+ const children = expandedIds.has(root.data.id) ? root.children.map((c) => buildLayoutTree(c, expandedIds)) : [];
39
+ return { treeNode: root, children, prelim: 0, mod: 0, x: 0 };
40
+ }
41
+ function layoutChildren(node, siblingGap, nodeWidth) {
42
+ if (node.children.length === 0) return;
43
+ let nextX = 0;
44
+ for (const child of node.children) {
45
+ child.prelim = nextX;
46
+ layoutChildren(child, siblingGap, nodeWidth);
47
+ nextX = getRightContour(child, child.prelim) + nodeWidth + siblingGap;
48
+ }
49
+ const first = node.children[0];
50
+ const last = node.children[node.children.length - 1];
51
+ const mid = (first.prelim + last.prelim) / 2;
52
+ node.mod = mid;
53
+ }
54
+ function getRightContour(node, offset) {
55
+ let max = offset;
56
+ for (const child of node.children) {
57
+ max = Math.max(max, getRightContour(child, offset + child.prelim));
58
+ }
59
+ return max;
60
+ }
61
+ function isVerticalFlow(direction) {
62
+ return direction === "vertical" || direction === "bottom-up";
63
+ }
64
+ function assignPositions(root, config, direction) {
65
+ const vertical = isVerticalFlow(direction);
66
+ layoutChildren(root, config.siblingGap, vertical ? config.nodeWidth : config.nodeHeight);
67
+ function absolutePositions(node, offsetX, depth) {
68
+ const absX = offsetX + node.prelim;
69
+ if (vertical) {
70
+ node.treeNode.x = absX;
71
+ node.treeNode.y = depth * (config.nodeHeight + config.levelGap);
72
+ } else {
73
+ node.treeNode.x = depth * (config.nodeWidth + config.levelGap);
74
+ node.treeNode.y = absX;
75
+ }
76
+ for (const child of node.children) {
77
+ absolutePositions(child, absX - node.mod, depth + 1);
78
+ }
79
+ }
80
+ absolutePositions(root, 0, 0);
81
+ }
82
+ function normalizePositions(nodes, padding = 20) {
83
+ if (nodes.length === 0) return;
84
+ let minX = Infinity;
85
+ let minY = Infinity;
86
+ for (const n of nodes) {
87
+ if (n.x < minX) minX = n.x;
88
+ if (n.y < minY) minY = n.y;
89
+ }
90
+ const dx = padding - minX;
91
+ const dy = padding - minY;
92
+ for (const n of nodes) {
93
+ n.x += dx;
94
+ n.y += dy;
95
+ }
96
+ }
97
+ export function computeLayout(root, config, direction, expandedIds) {
98
+ const layoutRoot = buildLayoutTree(root, expandedIds);
99
+ assignPositions(layoutRoot, config, direction);
100
+ const nodes = flattenAll(layoutRoot);
101
+ const treeNodes = nodes.map((ln) => ln.treeNode);
102
+ if (direction === "bottom-up") {
103
+ flipVertical(treeNodes, config);
104
+ } else if (direction === "right-to-left") {
105
+ flipHorizontal(treeNodes, config);
106
+ }
107
+ normalizePositions(treeNodes);
108
+ const connectors = computeConnectors(treeNodes, config, direction, "right-angle");
109
+ const w = config.nodeWidth;
110
+ const h = config.nodeHeight;
111
+ let maxX = 0;
112
+ let maxY = 0;
113
+ for (const n of treeNodes) {
114
+ if (n.x + w > maxX) maxX = n.x + w;
115
+ if (n.y + h > maxY) maxY = n.y + h;
116
+ }
117
+ return {
118
+ nodes: treeNodes,
119
+ connectors,
120
+ connectionPaths: [],
121
+ width: maxX + 20,
122
+ height: maxY + 20
123
+ };
124
+ }
125
+ function flipVertical(nodes, config) {
126
+ if (nodes.length === 0) return;
127
+ let maxY = 0;
128
+ for (const n of nodes) {
129
+ const bottom = n.y + config.nodeHeight;
130
+ if (bottom > maxY) maxY = bottom;
131
+ }
132
+ for (const n of nodes) {
133
+ n.y = maxY - n.y - config.nodeHeight;
134
+ }
135
+ }
136
+ function flipHorizontal(nodes, config) {
137
+ if (nodes.length === 0) return;
138
+ let maxX = 0;
139
+ for (const n of nodes) {
140
+ const right = n.x + config.nodeWidth;
141
+ if (right > maxX) maxX = right;
142
+ }
143
+ for (const n of nodes) {
144
+ n.x = maxX - n.x - config.nodeWidth;
145
+ }
146
+ }
147
+ export function computeFullLayout(root, config, direction, connectorStyle, expandedIds, connections) {
148
+ const layoutRoot = buildLayoutTree(root, expandedIds);
149
+ assignPositions(layoutRoot, config, direction);
150
+ const nodes = flattenAll(layoutRoot);
151
+ const treeNodes = nodes.map((ln) => ln.treeNode);
152
+ if (direction === "bottom-up") {
153
+ flipVertical(treeNodes, config);
154
+ } else if (direction === "right-to-left") {
155
+ flipHorizontal(treeNodes, config);
156
+ }
157
+ normalizePositions(treeNodes);
158
+ const connectors = computeConnectors(treeNodes, config, direction, connectorStyle);
159
+ const connectionPaths = connections && connections.length > 0 ? computeConnectionPaths(treeNodes, config, direction, connections) : [];
160
+ let maxX = 0;
161
+ let maxY = 0;
162
+ for (const n of treeNodes) {
163
+ const right = n.x + config.nodeWidth;
164
+ const bottom = n.y + config.nodeHeight;
165
+ if (right > maxX) maxX = right;
166
+ if (bottom > maxY) maxY = bottom;
167
+ }
168
+ return {
169
+ nodes: treeNodes,
170
+ connectors,
171
+ connectionPaths,
172
+ width: maxX + 20,
173
+ height: maxY + 20
174
+ };
175
+ }
176
+ function flattenAll(node) {
177
+ const result = [node];
178
+ for (const child of node.children) {
179
+ result.push(...flattenAll(child));
180
+ }
181
+ return result;
182
+ }
183
+ function getConnectorEndpoints(parent, child, config, direction) {
184
+ switch (direction) {
185
+ case "vertical":
186
+ return {
187
+ px: parent.x + config.nodeWidth / 2,
188
+ py: parent.y + config.nodeHeight,
189
+ cx: child.x + config.nodeWidth / 2,
190
+ cy: child.y
191
+ };
192
+ case "bottom-up":
193
+ return {
194
+ px: parent.x + config.nodeWidth / 2,
195
+ py: parent.y,
196
+ cx: child.x + config.nodeWidth / 2,
197
+ cy: child.y + config.nodeHeight
198
+ };
199
+ case "horizontal":
200
+ return {
201
+ px: parent.x + config.nodeWidth,
202
+ py: parent.y + config.nodeHeight / 2,
203
+ cx: child.x,
204
+ cy: child.y + config.nodeHeight / 2
205
+ };
206
+ case "right-to-left":
207
+ return {
208
+ px: parent.x,
209
+ py: parent.y + config.nodeHeight / 2,
210
+ cx: child.x + config.nodeWidth,
211
+ cy: child.y + config.nodeHeight / 2
212
+ };
213
+ }
214
+ }
215
+ export function computeConnectors(nodes, config, direction, style) {
216
+ const nodeMap = /* @__PURE__ */ new Map();
217
+ for (const n of nodes) nodeMap.set(n.data.id, n);
218
+ const connectors = [];
219
+ for (const node of nodes) {
220
+ if (!node.parent) continue;
221
+ const parent = nodeMap.get(node.parent.data.id);
222
+ if (!parent) continue;
223
+ const { px, py, cx, cy } = getConnectorEndpoints(parent, node, config, direction);
224
+ const path = buildConnectorPath(px, py, cx, cy, style, isVerticalFlow(direction), config.connectorRadius);
225
+ connectors.push({
226
+ fromId: parent.data.id,
227
+ toId: node.data.id,
228
+ path
229
+ });
230
+ }
231
+ return connectors;
232
+ }
233
+ function buildConnectorPath(px, py, cx, cy, style, vertical, radius = 0) {
234
+ if (style === "straight") {
235
+ return `M ${px} ${py} L ${cx} ${cy}`;
236
+ }
237
+ if (style === "curved") {
238
+ if (vertical) {
239
+ const midY = py + (cy - py) / 2;
240
+ return `M ${px} ${py} C ${px} ${midY}, ${cx} ${midY}, ${cx} ${cy}`;
241
+ }
242
+ const midX2 = px + (cx - px) / 2;
243
+ return `M ${px} ${py} C ${midX2} ${py}, ${midX2} ${cy}, ${cx} ${cy}`;
244
+ }
245
+ if (vertical) {
246
+ const midY = py + (cy - py) / 2;
247
+ if (radius <= 0 || px === cx) {
248
+ return `M ${px} ${py} L ${px} ${midY} L ${cx} ${midY} L ${cx} ${cy}`;
249
+ }
250
+ const r2 = Math.min(radius, Math.abs(cy - py) / 4, Math.abs(cx - px) / 2);
251
+ const dx = cx > px ? r2 : -r2;
252
+ const dy1 = midY > py ? r2 : -r2;
253
+ const dy2 = cy > midY ? r2 : -r2;
254
+ return [
255
+ `M ${px} ${py}`,
256
+ `L ${px} ${midY - dy1}`,
257
+ `Q ${px} ${midY}, ${px + dx} ${midY}`,
258
+ `L ${cx - dx} ${midY}`,
259
+ `Q ${cx} ${midY}, ${cx} ${midY + dy2}`,
260
+ `L ${cx} ${cy}`
261
+ ].join(" ");
262
+ }
263
+ const midX = px + (cx - px) / 2;
264
+ if (radius <= 0 || py === cy) {
265
+ return `M ${px} ${py} L ${midX} ${py} L ${midX} ${cy} L ${cx} ${cy}`;
266
+ }
267
+ const r = Math.min(radius, Math.abs(cx - px) / 4, Math.abs(cy - py) / 2);
268
+ const dx1 = midX > px ? r : -r;
269
+ const dx2 = cx > midX ? r : -r;
270
+ const dy = cy > py ? r : -r;
271
+ return [
272
+ `M ${px} ${py}`,
273
+ `L ${midX - dx1} ${py}`,
274
+ `Q ${midX} ${py}, ${midX} ${py + dy}`,
275
+ `L ${midX} ${cy - dy}`,
276
+ `Q ${midX} ${cy}, ${midX + dx2} ${cy}`,
277
+ `L ${cx} ${cy}`
278
+ ].join(" ");
279
+ }
280
+ export function computeConnectionPaths(nodes, config, direction, connections) {
281
+ const nodeMap = /* @__PURE__ */ new Map();
282
+ for (const n of nodes) nodeMap.set(n.data.id, n);
283
+ const result = [];
284
+ for (const conn of connections) {
285
+ const from = nodeMap.get(conn.fromId);
286
+ const to = nodeMap.get(conn.toId);
287
+ if (!from || !to) continue;
288
+ const fromCx = from.x + config.nodeWidth / 2;
289
+ const fromCy = from.y + config.nodeHeight / 2;
290
+ const toCx = to.x + config.nodeWidth / 2;
291
+ const toCy = to.y + config.nodeHeight / 2;
292
+ const dx = toCx - fromCx;
293
+ const dy = toCy - fromCy;
294
+ const dist = Math.sqrt(dx * dx + dy * dy);
295
+ const offset = Math.min(dist * 0.3, 80);
296
+ const nx = dist > 0 ? -dy / dist : 0;
297
+ const ny = dist > 0 ? dx / dist : 1;
298
+ const cpX = (fromCx + toCx) / 2 + nx * offset;
299
+ const cpY = (fromCy + toCy) / 2 + ny * offset;
300
+ const path = `M ${fromCx} ${fromCy} Q ${cpX} ${cpY}, ${toCx} ${toCy}`;
301
+ const labelX = (fromCx + 2 * cpX + toCx) / 4;
302
+ const labelY = (fromCy + 2 * cpY + toCy) / 4;
303
+ result.push({ connection: conn, path, labelX, labelY });
304
+ }
305
+ return result;
306
+ }
307
+ export function flattenVisibleTree(root, expandedIds) {
308
+ const result = [root];
309
+ if (expandedIds.has(root.data.id)) {
310
+ for (const child of root.children) {
311
+ result.push(...flattenVisibleTree(child, expandedIds));
312
+ }
313
+ }
314
+ return result;
315
+ }
316
+ export function getAncestors(nodeId, nodeMap) {
317
+ const result = [];
318
+ let current = nodeMap.get(nodeId);
319
+ if (!current) return result;
320
+ current = current.parent ?? void 0;
321
+ while (current) {
322
+ result.push(current);
323
+ current = current.parent ?? void 0;
324
+ }
325
+ return result;
326
+ }
327
+ export function getDescendants(node) {
328
+ const result = [];
329
+ for (const child of node.children) {
330
+ result.push(child);
331
+ result.push(...getDescendants(child));
332
+ }
333
+ return result;
334
+ }
335
+ export function getAllNodeIds(root) {
336
+ const ids = /* @__PURE__ */ new Set();
337
+ function walk(node) {
338
+ ids.add(node.data.id);
339
+ for (const child of node.children) walk(child);
340
+ }
341
+ walk(root);
342
+ return ids;
343
+ }
344
+ export function buildNodeMap(root) {
345
+ const map = /* @__PURE__ */ new Map();
346
+ function walk(node) {
347
+ map.set(node.data.id, node);
348
+ for (const child of node.children) walk(child);
349
+ }
350
+ walk(root);
351
+ return map;
352
+ }
353
+ export const DEFAULT_LAYOUT_CONFIG = {
354
+ siblingGap: 40,
355
+ levelGap: 60,
356
+ nodeWidth: 200,
357
+ nodeHeight: 80,
358
+ connectorRadius: 8
359
+ };
360
+ export const DEFAULT_ZOOM_CONFIG = {
361
+ enabled: false,
362
+ min: 0.25,
363
+ max: 2,
364
+ initial: 1,
365
+ step: 0.1,
366
+ controls: true
367
+ };
@@ -0,0 +1,30 @@
1
+ import type { CalendarDate } from "@internationalized/date";
2
+ import type { CalendarEvent } from "../types/event-calendar.js";
3
+ /** Visible date range for the calendar grid */
4
+ export interface VisibleRange {
5
+ start: CalendarDate;
6
+ end: CalendarDate;
7
+ }
8
+ /**
9
+ * Compute the date range the calendar grid actually renders.
10
+ * Includes padding for multi-day event bleed.
11
+ */
12
+ export declare function getVisibleRange(displayDate: CalendarDate, view: "month" | "week" | "day" | "list", weekStartsOn: 0 | 1 | 2 | 3 | 4 | 5 | 6, fixedWeeks: boolean): VisibleRange;
13
+ /**
14
+ * Expand recurring events within a visible date range.
15
+ * Synchronous — requires the rrule module to be pre-loaded.
16
+ *
17
+ * @param events - Array of CalendarEvent (recurring and non-recurring mixed)
18
+ * @param range - Visible date range to expand within
19
+ * @param rruleLib - The pre-loaded rrule module (`import('rrule')`)
20
+ * @returns Flat array of CalendarEvent with occurrences expanded
21
+ */
22
+ export declare function expandRecurringEvents(events: CalendarEvent[], range: VisibleRange, rruleLib: typeof import("rrule")): CalendarEvent[];
23
+ /**
24
+ * Async convenience wrapper for standalone use (auto-imports rrule).
25
+ *
26
+ * @param events - Array of CalendarEvent
27
+ * @param range - Visible date range
28
+ * @returns Flat array of CalendarEvent with occurrences expanded
29
+ */
30
+ export declare function expandRecurringEventsAsync(events: CalendarEvent[], range: VisibleRange): Promise<CalendarEvent[]>;