@stonecrop/atable 0.4.22 → 0.4.24

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.
@@ -3,6 +3,9 @@ import { type CSSProperties, computed, ref } from 'vue'
3
3
 
4
4
  import type {
5
5
  CellContext,
6
+ ConnectionHandle,
7
+ ConnectionPath,
8
+ GanttBarInfo,
6
9
  GanttDragEvent,
7
10
  TableColumn,
8
11
  TableConfig,
@@ -56,12 +59,15 @@ export const createTableStore = (initData: {
56
59
 
57
60
  // TODO: (typing) is this type correct for the parent set?
58
61
  const parents = new Set<string | number>()
59
- for (let rowIndex = rows.value.length - 1; rowIndex >= 0; rowIndex--) {
62
+ for (let rowIndex = 0; rowIndex < rows.value.length; rowIndex++) {
60
63
  const row = rows.value[rowIndex]
61
- if (row.parent) {
64
+ if (row.parent !== null && row.parent !== undefined) {
62
65
  parents.add(row.parent)
63
66
  }
67
+ }
64
68
 
69
+ for (let rowIndex = 0; rowIndex < rows.value.length; rowIndex++) {
70
+ const row = rows.value[rowIndex]
65
71
  defaultDisplay[rowIndex] = {
66
72
  childrenOpen: false,
67
73
  expanded: false,
@@ -85,9 +91,14 @@ export const createTableStore = (initData: {
85
91
  const display = ref(createDisplayObject(initData.display))
86
92
  const modal = ref<TableModal>(initData.modal || { visible: false })
87
93
  const updates = ref<Record<string, string>>({})
94
+ const ganttBars = ref<GanttBarInfo[]>([])
95
+ const connectionHandles = ref<ConnectionHandle[]>([])
96
+ const connectionPaths = ref<ConnectionPath[]>([])
88
97
 
89
98
  // getters
90
99
  const hasPinnedColumns = computed(() => columns.value.some(col => col.pinned))
100
+ const isGanttView = computed(() => config.value.view === 'gantt' || config.value.view === 'tree-gantt')
101
+ const isTreeView = computed(() => config.value.view === 'tree' || config.value.view === 'tree-gantt')
91
102
 
92
103
  const numberedRowWidth = computed(() => {
93
104
  const indent = Math.ceil(rows.value.length / 100 + 1)
@@ -95,7 +106,7 @@ export const createTableStore = (initData: {
95
106
  })
96
107
 
97
108
  const zeroColumn = computed(() =>
98
- config.value.view ? ['list', 'tree', 'list-expansion'].includes(config.value.view) : false
109
+ config.value.view ? ['list', 'tree', 'tree-gantt', 'list-expansion'].includes(config.value.view) : false
99
110
  )
100
111
 
101
112
  // actions
@@ -109,7 +120,11 @@ export const createTableStore = (initData: {
109
120
  }
110
121
 
111
122
  table.value[index] = value
112
- rows.value[rowIndex][col.name] = value
123
+ // Create a new row object to ensure reactivity
124
+ rows.value[rowIndex] = {
125
+ ...rows.value[rowIndex],
126
+ [col.name]: value,
127
+ }
113
128
  }
114
129
 
115
130
  const setCellText = (colIndex: number, rowIndex: number, value: string) => {
@@ -154,32 +169,40 @@ export const createTableStore = (initData: {
154
169
 
155
170
  const isRowGantt = (rowIndex: number) => {
156
171
  const row = rows.value[rowIndex]
157
- return config.value.view === 'gantt' && row.indent === 0
172
+ return isGanttView.value && row.gantt !== undefined
158
173
  }
159
174
 
160
175
  const isRowVisible = (rowIndex: number) => {
161
- return config.value.view !== 'tree' || display.value[rowIndex].isRoot || display.value[rowIndex].open
176
+ return !isTreeView.value || display.value[rowIndex].isRoot || display.value[rowIndex].open
162
177
  }
163
178
 
164
179
  const getRowExpandSymbol = (rowIndex: number) => {
165
- if (config.value.view !== 'tree') {
180
+ if (!isTreeView.value && config.value.view !== 'list-expansion') {
166
181
  return ''
167
182
  }
168
183
 
169
- if (display.value[rowIndex].isRoot || display.value[rowIndex].isParent) {
170
- return display.value[rowIndex].childrenOpen ? '-' : '+'
184
+ if (isTreeView.value && (display.value[rowIndex].isRoot || display.value[rowIndex].isParent)) {
185
+ return display.value[rowIndex].childrenOpen ? '' : ''
186
+ }
187
+
188
+ if (config.value.view === 'list-expansion') {
189
+ return display.value[rowIndex].expanded ? '▼' : '►'
171
190
  }
172
191
 
173
192
  return ''
174
193
  }
175
194
 
176
195
  const toggleRowExpand = (rowIndex: number) => {
177
- if (config.value.view === 'tree') {
196
+ if (isTreeView.value) {
178
197
  display.value[rowIndex].childrenOpen = !display.value[rowIndex].childrenOpen
179
- for (let index = rows.value.length - 1; index >= 0; index--) {
198
+ const isOpen = display.value[rowIndex].childrenOpen
199
+
200
+ for (let index = 0; index < rows.value.length; index++) {
180
201
  if (display.value[index].parent === rowIndex) {
181
- display.value[index].open = !display.value[index].open
182
- if (display.value[index].childrenOpen) {
202
+ display.value[index].open = isOpen
203
+ if (!isOpen) {
204
+ // If we're closing, also close any children recursively
205
+ display.value[index].childrenOpen = false
183
206
  toggleRowExpand(index)
184
207
  }
185
208
  }
@@ -255,11 +278,98 @@ export const createTableStore = (initData: {
255
278
  }
256
279
  }
257
280
 
281
+ const registerGanttBar = (barInfo: GanttBarInfo) => {
282
+ const existingIndex = ganttBars.value.findIndex(bar => bar.id === barInfo.id)
283
+ if (existingIndex >= 0) {
284
+ // @ts-expect-error TODO: for some reason, the IDE is expecting an unref'd value
285
+ ganttBars.value[existingIndex] = barInfo
286
+ } else {
287
+ // @ts-expect-error TODO: for some reason, the IDE is expecting an unref'd value
288
+ ganttBars.value.push(barInfo)
289
+ }
290
+ }
291
+
292
+ const unregisterGanttBar = (barId: string) => {
293
+ const index = ganttBars.value.findIndex(bar => bar.id === barId)
294
+ if (index >= 0) {
295
+ ganttBars.value.splice(index, 1)
296
+ }
297
+ }
298
+
299
+ const registerConnectionHandle = (handleInfo: ConnectionHandle) => {
300
+ const existingIndex = connectionHandles.value.findIndex(handle => handle.id === handleInfo.id)
301
+ if (existingIndex >= 0) {
302
+ // @ts-expect-error TODO: for some reason, the IDE is expecting an unref'd value
303
+ connectionHandles.value[existingIndex] = handleInfo
304
+ } else {
305
+ // @ts-expect-error TODO: for some reason, the IDE is expecting an unref'd value
306
+ connectionHandles.value.push(handleInfo)
307
+ }
308
+ }
309
+
310
+ const unregisterConnectionHandle = (handleId: string) => {
311
+ const index = connectionHandles.value.findIndex(handle => handle.id === handleId)
312
+ if (index >= 0) {
313
+ connectionHandles.value.splice(index, 1)
314
+ }
315
+ }
316
+
317
+ const createConnection = (
318
+ fromHandleId: string,
319
+ toHandleId: string,
320
+ options?: { style?: ConnectionPath['style']; label?: string }
321
+ ) => {
322
+ const fromHandle = connectionHandles.value.find(h => h.id === fromHandleId)
323
+ const toHandle = connectionHandles.value.find(h => h.id === toHandleId)
324
+
325
+ if (!fromHandle || !toHandle) {
326
+ console.warn('Cannot create connection: handle not found')
327
+ return null
328
+ }
329
+
330
+ const connection: ConnectionPath = {
331
+ id: `connection-${fromHandleId}-${toHandleId}`,
332
+ from: {
333
+ barId: fromHandle.barId,
334
+ side: fromHandle.side,
335
+ },
336
+ to: {
337
+ barId: toHandle.barId,
338
+ side: toHandle.side,
339
+ },
340
+ style: options?.style,
341
+ label: options?.label,
342
+ }
343
+
344
+ connectionPaths.value.push(connection)
345
+ return connection
346
+ }
347
+
348
+ const deleteConnection = (connectionId: string) => {
349
+ const index = connectionPaths.value.findIndex(conn => conn.id === connectionId)
350
+ if (index >= 0) {
351
+ connectionPaths.value.splice(index, 1)
352
+ return true
353
+ }
354
+ return false
355
+ }
356
+
357
+ const getConnectionsForBar = (barId: string) => {
358
+ return connectionPaths.value.filter(conn => conn.from.barId === barId || conn.to.barId === barId)
359
+ }
360
+
361
+ const getHandlesForBar = (barId: string) => {
362
+ return connectionHandles.value.filter(handle => handle.barId === barId)
363
+ }
364
+
258
365
  return {
259
366
  // state
260
367
  columns,
261
368
  config,
369
+ connectionHandles,
370
+ connectionPaths,
262
371
  display,
372
+ ganttBars,
263
373
  modal,
264
374
  rows,
265
375
  table,
@@ -267,23 +377,33 @@ export const createTableStore = (initData: {
267
377
 
268
378
  // getters
269
379
  hasPinnedColumns,
380
+ isGanttView,
381
+ isTreeView,
270
382
  numberedRowWidth,
271
383
  zeroColumn,
272
384
 
273
385
  // actions
274
386
  closeModal,
387
+ createConnection,
388
+ deleteConnection,
275
389
  getCellData,
276
390
  getCellDisplayValue,
391
+ getConnectionsForBar,
277
392
  getFormattedValue,
393
+ getHandlesForBar,
278
394
  getHeaderCellStyle,
279
- resizeColumn,
280
395
  getIndent,
281
396
  getRowExpandSymbol,
282
397
  isRowGantt,
283
398
  isRowVisible,
399
+ registerConnectionHandle,
400
+ registerGanttBar,
401
+ resizeColumn,
284
402
  setCellData,
285
403
  setCellText,
286
404
  toggleRowExpand,
405
+ unregisterConnectionHandle,
406
+ unregisterGanttBar,
287
407
  updateGanttBar,
288
408
  }
289
409
  })
@@ -1,4 +1,5 @@
1
1
  import { useElementBounding } from '@vueuse/core'
2
+ import type { Ref, ShallowRef } from 'vue'
2
3
 
3
4
  import { createTableStore } from '../stores/table'
4
5
 
@@ -133,8 +134,8 @@ export interface TableColumn {
133
134
  ganttComponent?: string
134
135
 
135
136
  /**
136
- * The colspan of the Gantt-bar for the column. This is used to determine how many columns
137
- * the Gantt-bar should span.
137
+ * The colspan of the Gantt bar for the column. This determines how many columns
138
+ * the Gantt bar should span across.
138
139
  *
139
140
  * Only applicable for Gantt tables.
140
141
  *
@@ -143,8 +144,8 @@ export interface TableColumn {
143
144
  colspan?: number
144
145
 
145
146
  /**
146
- * The starting column index for the Gantt-bar, excluding any pinned columns. This is
147
- * evaluated automatically while rendering the table.
147
+ * The original column index for the Gantt bar, excluding any pinned columns.
148
+ * This is evaluated automatically while rendering the table.
148
149
  *
149
150
  * Only applicable for Gantt tables.
150
151
  *
@@ -185,8 +186,10 @@ export interface TableConfig {
185
186
  * - `list` - row numbers are displayed in the table
186
187
  * - `list-expansion` - carets are displayed in the number column that expand/collapse the row inline
187
188
  * - `tree` - carets are displayed in the number column that expand/collapse grouped rows
189
+ * - `gantt` - view that allows specific rows to be displayed with Gantt functionality
190
+ * - `tree-gantt` - similar to `gantt`, but allows for tree functionality as well
188
191
  */
189
- view?: 'uncounted' | 'list' | 'list-expansion' | 'tree' | 'gantt'
192
+ view?: 'uncounted' | 'list' | 'list-expansion' | 'tree' | 'gantt' | 'tree-gantt'
190
193
 
191
194
  /**
192
195
  * Control whether the table should be allowed to use the full width of its container.
@@ -303,29 +306,33 @@ export interface TableRow {
303
306
  }
304
307
 
305
308
  /**
306
- * This interface defines the options for a row when it is being viewed as a Gantt chart.
309
+ * Gantt chart options for table rows.
307
310
  * @public
308
311
  */
309
312
  export interface GanttOptions {
310
313
  /**
311
- * The colour to be applied to the row's gantt bar.
314
+ * The color to be applied to the row's gantt bar.
315
+ *
316
+ * @defaultValue '#cccccc'
312
317
  */
313
318
  color?: string
314
319
 
315
320
  /**
316
321
  * The starting column index for the gantt bar.
322
+ *
323
+ * @defaultValue 0
317
324
  */
318
325
  startIndex?: number
319
326
 
320
327
  /**
321
- * The ending column index for the gantt bar. If the endIndex and colspan are not provided,
328
+ * The ending column index for the gantt bar. If endIndex and colspan are not provided,
322
329
  * the bar will stretch to the end of the table.
323
330
  */
324
331
  endIndex?: number
325
332
 
326
333
  /**
327
- * The length of the gantt bar. Useful when only the start index is provided. If the
328
- * colspan and endIndex are not provided, the bar will stretch to the end of the table.
334
+ * The length of the gantt bar in columns. Useful when only the start index is provided.
335
+ * If colspan and endIndex are not provided, the bar will stretch to the end of the table.
329
336
  */
330
337
  colspan?: number
331
338
  }
@@ -468,3 +475,145 @@ export interface TableModalProps {
468
475
  */
469
476
  store: ReturnType<typeof createTableStore>
470
477
  }
478
+
479
+ /**
480
+ * Gantt bar information for VueFlow integration.
481
+ * @public
482
+ */
483
+ export interface GanttBarInfo {
484
+ /**
485
+ * Unique identifier for the gantt bar.
486
+ */
487
+ id: string
488
+
489
+ /**
490
+ * The row index of the gantt bar.
491
+ */
492
+ rowIndex: number
493
+
494
+ /**
495
+ * The primary column index of the gantt bar (typically the start index).
496
+ */
497
+ colIndex: number
498
+
499
+ /**
500
+ * Starting column index of the gantt bar.
501
+ */
502
+ startIndex: Ref<number>
503
+
504
+ /**
505
+ * Ending column index of the gantt bar.
506
+ */
507
+ endIndex: Ref<number>
508
+
509
+ /**
510
+ * Color of the gantt bar.
511
+ */
512
+ color: Ref<string>
513
+
514
+ /**
515
+ * The position of the gantt bar in the ATable component.
516
+ */
517
+ position: {
518
+ x: ShallowRef<number>
519
+ y: ShallowRef<number>
520
+ }
521
+
522
+ /**
523
+ * Display label for the gantt bar.
524
+ */
525
+ label?: string
526
+ }
527
+
528
+ /**
529
+ * Connection handle information for gantt bar connections.
530
+ * @public
531
+ */
532
+ export interface ConnectionHandle {
533
+ /**
534
+ * Unique identifier for the connection handle.
535
+ */
536
+ id: string
537
+
538
+ /**
539
+ * The row index of the gantt bar this handle belongs to.
540
+ */
541
+ rowIndex: number
542
+
543
+ /**
544
+ * The column index of the gantt bar this handle belongs to.
545
+ */
546
+ colIndex: number
547
+
548
+ /**
549
+ * The side of the gantt bar where this handle is located.
550
+ */
551
+ side: 'left' | 'right'
552
+
553
+ /**
554
+ * The position of the connection handle.
555
+ */
556
+ position: {
557
+ x: ShallowRef<number>
558
+ y: ShallowRef<number>
559
+ }
560
+
561
+ /**
562
+ * Whether the handle is currently visible (on hover).
563
+ */
564
+ visible: Ref<boolean>
565
+
566
+ /**
567
+ * Reference to the gantt bar this handle belongs to.
568
+ */
569
+ barId: string
570
+ }
571
+
572
+ /**
573
+ * Connection path between two gantt bars.
574
+ * @public
575
+ */
576
+ export interface ConnectionPath {
577
+ /**
578
+ * Unique identifier for the connection path.
579
+ */
580
+ id: string
581
+
582
+ /**
583
+ * The source connection handle.
584
+ */
585
+ from: {
586
+ barId: string
587
+ side: 'left' | 'right'
588
+ }
589
+
590
+ /**
591
+ * The target connection handle.
592
+ */
593
+ to: {
594
+ barId: string
595
+ side: 'left' | 'right'
596
+ }
597
+
598
+ /**
599
+ * Optional styling for the connection path.
600
+ */
601
+ style?: {
602
+ color?: string
603
+ width?: number
604
+ }
605
+
606
+ /**
607
+ * Optional label for the connection.
608
+ */
609
+ label?: string
610
+ }
611
+
612
+ /**
613
+ * Connection event for handling connection creation/deletion.
614
+ * @public
615
+ */
616
+ export type ConnectionEvent = {
617
+ type: 'create' | 'delete'
618
+ connection: ConnectionPath
619
+ }