@stonecrop/atable 0.4.11 → 0.4.13

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.
@@ -18,18 +18,19 @@
18
18
  v-if="column.isGantt"
19
19
  :is="column.ganttComponent || 'AGanttCell'"
20
20
  :store="store"
21
- :color="column.color"
21
+ :columnsCount="store.columns.length - pinnedColumnCount"
22
+ :color="row.gantt?.color"
23
+ :start="row.gantt?.startIndex"
24
+ :end="row.gantt?.endIndex"
22
25
  :colspan="column.colspan"
23
26
  :pinned="column.pinned"
24
27
  :rowIndex="rowIndex"
25
- :colIndex="column.originalIndex !== undefined ? column.originalIndex : colIndex"
28
+ :colIndex="column.originalIndex ?? colIndex"
26
29
  :style="{
27
30
  textAlign: column?.align || 'center',
28
31
  minWidth: column?.width || '40ch',
29
32
  width: store.config.fullWidth ? 'auto' : null,
30
- // tabIndex: column.isGantt ? '-1' : '0',
31
- }"
32
- spellcheck="false" />
33
+ }" />
33
34
  <component
34
35
  v-else
35
36
  :is="column.cellComponent || 'ACell'"
@@ -68,13 +69,13 @@
68
69
  <script setup lang="ts">
69
70
  import { vOnClickOutside } from '@vueuse/components'
70
71
  import { useMutationObserver } from '@vueuse/core'
71
- import { nextTick, watch, onMounted, useTemplateRef } from 'vue'
72
+ import { nextTick, watch, onMounted, useTemplateRef, computed } from 'vue'
72
73
 
73
74
  import ARow from './ARow.vue'
74
75
  import ATableHeader from './ATableHeader.vue'
75
76
  import ATableModal from './ATableModal.vue'
76
77
  import { createTableStore } from '../stores/table'
77
- import type { TableColumn, TableConfig, TableRow } from '../types'
78
+ import type { GanttDragEvent, TableColumn, TableConfig, TableRow } from '../types'
78
79
 
79
80
  const {
80
81
  id,
@@ -93,6 +94,7 @@ const {
93
94
  const emit = defineEmits<{
94
95
  'update:modelValue': [value: TableRow[]]
95
96
  cellUpdate: [{ colIndex: number; rowIndex: number; newValue: any; oldValue: any }]
97
+ 'gantt:drag': [event: GanttDragEvent]
96
98
  }>()
97
99
 
98
100
  const tableRef = useTemplateRef<HTMLTableElement>('table')
@@ -103,10 +105,31 @@ store.$onAction(({ name, store, args, after }) => {
103
105
  if (name === 'setCellData' || name === 'setCellText') {
104
106
  const [colIndex, rowIndex, newValue] = args
105
107
  const oldValue = store.getCellData(colIndex, rowIndex)
106
-
107
108
  after(() => {
108
109
  emit('cellUpdate', { colIndex, rowIndex, newValue, oldValue })
109
110
  })
111
+ } else if (name === 'updateGanttBar') {
112
+ const [event] = args
113
+ const ganttBar = store.rows[event.rowIndex]?.gantt
114
+ if (ganttBar) {
115
+ // only emit if the bar was actually resized or moved; check before the mutation
116
+ let emitDrag = false
117
+ if (event.type === 'resize') {
118
+ if (event.edge === 'start') {
119
+ emitDrag = ganttBar.startIndex !== event.value
120
+ } else if (event.edge === 'end') {
121
+ emitDrag = ganttBar.endIndex !== event.value
122
+ }
123
+ } else if (event.type === 'bar') {
124
+ emitDrag = ganttBar.startIndex !== event.start || ganttBar.endIndex !== event.end
125
+ }
126
+
127
+ if (emitDrag) {
128
+ after(() => {
129
+ emit('gantt:drag', event)
130
+ })
131
+ }
132
+ }
110
133
  }
111
134
  })
112
135
 
@@ -129,6 +152,10 @@ onMounted(() => {
129
152
  }
130
153
  })
131
154
 
155
+ const pinnedColumnCount = computed(() => {
156
+ return store.columns.filter(column => column.pinned).length
157
+ })
158
+
132
159
  const assignStickyCellWidths = () => {
133
160
  const table = tableRef.value
134
161
 
@@ -182,26 +209,24 @@ window.addEventListener('keydown', (event: KeyboardEvent) => {
182
209
 
183
210
  const getProcessedColumnsForRow = (row: TableRow) => {
184
211
  const isGanttRow = row.indent === 0
185
- const pinnedColumnCount = store.columns.filter(column => column.pinned).length
186
- if (!isGanttRow || pinnedColumnCount === 0) {
212
+ if (!isGanttRow || pinnedColumnCount.value === 0) {
187
213
  return store.columns
188
214
  }
189
215
 
190
- // Add pinned columns
216
+ // For Gantt views, first add the pinned columns
191
217
  const result: TableColumn[] = []
192
- for (let i = 0; i < pinnedColumnCount; i++) {
218
+ for (let i = 0; i < pinnedColumnCount.value; i++) {
193
219
  const column = { ...store.columns[i] }
194
220
  column.originalIndex = i // Preserve original index
195
221
  result.push(column)
196
222
  }
197
- // Add the Gantt column with colspan
198
223
 
224
+ // Finally, add the Gantt bar column with either a provided colspan or default to full-width colspan
199
225
  result.push({
200
- ...store.columns[pinnedColumnCount],
201
- colspan: store.columns.length - pinnedColumnCount,
226
+ ...store.columns[pinnedColumnCount.value],
202
227
  isGantt: true,
203
- originalIndex: pinnedColumnCount,
204
- color: isGanttRow ? (row.resource_name as { color: string }).color : '',
228
+ colspan: row.gantt?.colspan || store.columns.length - pinnedColumnCount.value,
229
+ originalIndex: pinnedColumnCount.value,
205
230
  width: 'auto', // TODO: refactor to API that can detect when data exists in a cell. Might have be custom and not generalizable
206
231
  })
207
232
 
package/src/index.ts CHANGED
@@ -12,6 +12,8 @@ import ATableModal from './components/ATableModal.vue'
12
12
  export { createTableStore } from './stores/table'
13
13
  export type {
14
14
  CellContext,
15
+ GanttDragEvent,
16
+ GanttOptions,
15
17
  TableColumn,
16
18
  TableConfig,
17
19
  TableDisplay,
@@ -1,7 +1,15 @@
1
1
  import { defineStore } from 'pinia'
2
2
  import { type CSSProperties, computed, ref } from 'vue'
3
3
 
4
- import type { CellContext, TableColumn, TableConfig, TableDisplay, TableModal, TableRow } from '../types'
4
+ import type {
5
+ CellContext,
6
+ GanttDragEvent,
7
+ TableColumn,
8
+ TableConfig,
9
+ TableDisplay,
10
+ TableModal,
11
+ TableRow,
12
+ } from '../types'
5
13
  import { generateHash } from '../utils'
6
14
 
7
15
  /**
@@ -119,6 +127,11 @@ export const createTableStore = (initData: {
119
127
  width: config.value.fullWidth ? 'auto' : undefined,
120
128
  })
121
129
 
130
+ const isRowGantt = (rowIndex: number) => {
131
+ const row = rows.value[rowIndex]
132
+ return config.value.view === 'gantt' && row.indent === 0
133
+ }
134
+
122
135
  const isRowVisible = (rowIndex: number) => {
123
136
  return config.value.view !== 'tree' || display.value[rowIndex].isRoot || display.value[rowIndex].open
124
137
  }
@@ -195,6 +208,23 @@ export const createTableStore = (initData: {
195
208
  }
196
209
  }
197
210
 
211
+ const updateGanttBar = (event: GanttDragEvent) => {
212
+ // update the local gantt bar cache
213
+ const ganttBar = rows.value[event.rowIndex]?.gantt
214
+ if (ganttBar) {
215
+ if (event.type === 'resize') {
216
+ if (event.edge === 'start') {
217
+ ganttBar.startIndex = event.value
218
+ } else if (event.edge === 'end') {
219
+ ganttBar.endIndex = event.value
220
+ }
221
+ } else if (event.type === 'bar') {
222
+ ganttBar.startIndex = event.start
223
+ ganttBar.endIndex = event.end
224
+ }
225
+ }
226
+ }
227
+
198
228
  return {
199
229
  // state
200
230
  columns,
@@ -218,10 +248,12 @@ export const createTableStore = (initData: {
218
248
  getHeaderCellStyle,
219
249
  getIndent,
220
250
  getRowExpandSymbol,
251
+ isRowGantt,
221
252
  isRowVisible,
222
253
  setCellData,
223
254
  setCellText,
224
255
  toggleRowExpand,
256
+ updateGanttBar,
225
257
  }
226
258
  })
227
259
 
@@ -6,25 +6,77 @@ import { createTableStore } from '../stores/table'
6
6
  * Table column definition.
7
7
  * @public
8
8
  */
9
- export type TableColumn = {
9
+ export interface TableColumn {
10
+ /**
11
+ * The key of the column. This is used to identify the column in the table.
12
+ */
10
13
  name: string
11
14
 
15
+ /**
16
+ * The alignment of the column. Possible values:
17
+ * - `left` - left aligned
18
+ * - `center` - center aligned
19
+ * - `right` - right aligned
20
+ * - `start` - aligned to the start of the column
21
+ * - `end` - aligned to the end of the column
22
+ *
23
+ * @defaultValue 'center'
24
+ */
12
25
  align?: CanvasTextAlign
26
+
27
+ /**
28
+ * Control whether cells for the column is editable.
29
+ *
30
+ * @defaultValue false
31
+ */
13
32
  edit?: boolean
33
+
34
+ /**
35
+ * The label of the column. This is displayed in the table header.
36
+ *
37
+ * @defaultValue If no label is provided, a character will be assigned alphabetically,
38
+ * starting from 'A' for the first column, 'B' for the second column, and so on.
39
+ */
14
40
  label?: string
41
+
42
+ /**
43
+ * The data-type of the column. Possible values:
44
+ * - `Data` - the column contains text data
45
+ * - `Select` - the column contains a select input
46
+ * - `Date` - the column contains a date input
47
+ * - `component` - the column contains a custom component
48
+ *
49
+ * @beta
50
+ */
15
51
  type?: string
52
+
53
+ /**
54
+ * The width of the column. This can be a number (in pixels) or a string (in CSS units).
55
+ *
56
+ * @defaultValue '40ch'
57
+ */
16
58
  width?: string
17
- pinned?: boolean
18
59
 
19
- // Gantt-specific fields
20
- color?: string
21
- colspan?: number
22
- ganttComponent?: string
23
- isGantt?: boolean
24
- originalIndex?: number
60
+ /**
61
+ * Control whether the column should be pinned to the table.
62
+ *
63
+ * @defaultValue false
64
+ */
65
+ pinned?: boolean
25
66
 
67
+ /**
68
+ * The component to use to render the cell for the column. If not provided, the table will
69
+ * render the default `<td>` element.
70
+ */
26
71
  cellComponent?: string
72
+
73
+ /**
74
+ * Additional properties to pass to the table's cell component.
75
+ *
76
+ * Only applicable if the `cellComponent` property is set for the column.
77
+ */
27
78
  cellComponentProps?: Record<string, any>
79
+
28
80
  /**
29
81
  * The component to use for the modal. If a function is provided, it will be called with the cell context.
30
82
  * The following properties are available on the cell context:
@@ -39,20 +91,85 @@ export type TableColumn = {
39
91
  * - `rowIndex` - the row index of the current cell
40
92
  * - `store` - the table data store
41
93
  */
94
+
42
95
  modalComponent?: string | ((context: CellContext) => string)
96
+
97
+ /**
98
+ * Additional properties to pass to the modal component.
99
+ *
100
+ * Only applicable if the `modalComponent` property is set for the column.
101
+ */
43
102
  modalComponentExtraProps?: Record<string, any>
44
103
 
104
+ /**
105
+ * The format function to use to format the value of the cell. This can either be a normal or stringified
106
+ * function that takes the value and the cell context and returns a string.
107
+ */
45
108
  format?: string | ((value: any, context: CellContext) => string)
109
+
110
+ /**
111
+ * The masking function to use to apply an input mask to the cell. This will accept an input value and
112
+ * return the masked value.
113
+ */
46
114
  mask?: (value: any) => any
115
+
116
+ /**
117
+ * Whether the column is a Gantt column.
118
+ *
119
+ * Only applicable for Gantt tables.
120
+ *
121
+ * @defaultValue false
122
+ */
123
+ isGantt?: boolean
124
+
125
+ /**
126
+ * The component to use to render the Gantt bar for the column.
127
+ *
128
+ * Only applicable for Gantt tables.
129
+ *
130
+ * @defaultValue 'AGanttCell'
131
+ */
132
+ ganttComponent?: string
133
+
134
+ /**
135
+ * The colspan of the Gantt-bar for the column. This is used to determine how many columns
136
+ * the Gantt-bar should span.
137
+ *
138
+ * Only applicable for Gantt tables.
139
+ *
140
+ * @defaultValue The number of columns in the table, excluding any pinned columns.
141
+ */
142
+ colspan?: number
143
+
144
+ /**
145
+ * The starting column index for the Gantt-bar, excluding any pinned columns. This is
146
+ * evaluated automatically while rendering the table.
147
+ *
148
+ * Only applicable for Gantt tables.
149
+ *
150
+ * @defaultValue 0
151
+ */
152
+ originalIndex?: number
47
153
  }
48
154
 
49
155
  /**
50
156
  * Table cell context definition.
51
157
  * @public
52
158
  */
53
- export type CellContext = {
159
+ export interface CellContext {
160
+ /**
161
+ * The row object for the current cell.
162
+ */
54
163
  row: TableRow
164
+
165
+ /**
166
+ * The column object for the current cell.
167
+ */
55
168
  column: TableColumn
169
+
170
+ /**
171
+ * The table object for the current cell.
172
+ */
56
173
  table: { [key: string]: any }
57
174
  }
58
175
 
@@ -60,7 +177,7 @@ export type CellContext = {
60
177
  * Table configuration definition.
61
178
  * @public
62
179
  */
63
- export type TableConfig = {
180
+ export interface TableConfig {
64
181
  /**
65
182
  * The type of view to display the table in. Possible values:
66
183
  * - `uncounted` - row numbers are not displayed in the table
@@ -69,6 +186,12 @@ export type TableConfig = {
69
186
  * - `tree` - carets are displayed in the number column that expand/collapse grouped rows
70
187
  */
71
188
  view?: 'uncounted' | 'list' | 'list-expansion' | 'tree' | 'gantt'
189
+
190
+ /**
191
+ * Control whether the table should be allowed to use the full width of its container.
192
+ *
193
+ * @defaultValue false
194
+ */
72
195
  fullWidth?: boolean
73
196
  }
74
197
 
@@ -76,14 +199,72 @@ export type TableConfig = {
76
199
  * Table display definition.
77
200
  * @public
78
201
  */
79
- export type TableDisplay = {
80
- childrenOpen?: boolean
202
+ export interface TableDisplay {
203
+ /**
204
+ * Indicates whether a row node is expanded or collapsed.
205
+ *
206
+ * Only applicable for list-expansion views.
207
+ *
208
+ * @defaultValue false
209
+ */
81
210
  expanded?: boolean
82
- indent?: number
211
+
212
+ /**
213
+ * Indicates whether a row node's child nodes are open or closed.
214
+ *
215
+ * Only applicable for tree views.
216
+ *
217
+ * @defaultValue false
218
+ */
219
+ childrenOpen?: boolean
220
+
221
+ /**
222
+ * Indicates whether a row node is a parent node. This is evaluated automatically
223
+ * while rendering the table.
224
+ *
225
+ * Only applicable for tree views.
226
+ */
83
227
  isParent?: boolean
228
+
229
+ /**
230
+ * Indicates whether a row node is a root node. This is evaluated automatically
231
+ * while rendering the table.
232
+ *
233
+ * Only applicable for tree views.
234
+ */
84
235
  isRoot?: boolean
236
+
237
+ /**
238
+ * Indicates whether a row node is visible. This is evaluated automatically
239
+ * while rendering the table.
240
+ *
241
+ * Only applicable for tree views.
242
+ */
85
243
  open?: boolean
244
+
245
+ /**
246
+ * The indentation level of the row node.
247
+ *
248
+ * Only applicable for tree and gantt views.
249
+ *
250
+ * @defaultValue 0
251
+ */
252
+ indent?: number
253
+
254
+ /**
255
+ * The HTML parent element for the row node. This is evaluated automatically while rendering
256
+ * the table.
257
+ *
258
+ * Only applicable for tree and gantt views.
259
+ */
86
260
  parent?: number
261
+
262
+ /**
263
+ * Indicates whether a row node has been modified. This is evaluated automatically when a cell
264
+ * is edited.
265
+ *
266
+ * @defaultValue false
267
+ */
87
268
  rowModified?: boolean
88
269
  }
89
270
 
@@ -91,39 +272,168 @@ export type TableDisplay = {
91
272
  * Table row definition.
92
273
  * @public
93
274
  */
94
- export type TableRow = {
275
+ export interface TableRow {
276
+ /**
277
+ * Additional arbitrary properties that can be passed to the row object.
278
+ */
95
279
  [key: string]: any
280
+
281
+ /**
282
+ * The indentation level of the row node.
283
+ *
284
+ * Only applicable for tree and gantt views.
285
+ *
286
+ * @defaultValue 0
287
+ */
96
288
  indent?: number
289
+
290
+ /**
291
+ * The HTML parent element for the row node. This is evaluated automatically while rendering
292
+ * the table.
293
+ *
294
+ * Only applicable for tree and gantt views.
295
+ */
97
296
  parent?: number
297
+
298
+ /**
299
+ * The options to use when rendering the row as a Gantt table.
300
+ */
301
+ gantt?: GanttOptions
98
302
  }
99
303
 
304
+ /**
305
+ * This interface defines the options for a row when it is being viewed as a Gantt chart.
306
+ * @public
307
+ */
308
+ export interface GanttOptions {
309
+ /**
310
+ * The colour to be applied to the row's gantt bar.
311
+ */
312
+ color?: string
313
+
314
+ /**
315
+ * The starting column index for the gantt bar.
316
+ */
317
+ startIndex?: number
318
+
319
+ /**
320
+ * The ending column index for the gantt bar. If the endIndex and colspan are not provided,
321
+ * the bar will stretch to the end of the table.
322
+ */
323
+ endIndex?: number
324
+
325
+ /**
326
+ * The length of the gantt bar. Useful when only the start index is provided. If the
327
+ * colspan and endIndex are not provided, the bar will stretch to the end of the table.
328
+ */
329
+ colspan?: number
330
+ }
331
+
332
+ /**
333
+ * Gantt table drag event definition.
334
+ * @public
335
+ */
336
+ export type GanttDragEvent =
337
+ | { rowIndex: number; colIndex: number; type: 'bar'; start: number; end: number }
338
+ | { rowIndex: number; colIndex: number; type: 'resize'; edge: 'start' | 'end'; value: number }
339
+
100
340
  /**
101
341
  * Table modal definition.
102
342
  * @public
103
343
  */
104
- export type TableModal = {
105
- bottom?: ReturnType<typeof useElementBounding>['bottom']
344
+ export interface TableModal {
345
+ /**
346
+ * Indicates whether the table modal is currently visible.
347
+ *
348
+ * @defaultValue false
349
+ */
350
+ visible?: boolean
351
+
352
+ /**
353
+ * The HTML cell element that the modal is currently being displayed for. The field is unset
354
+ * when the modal is not being displayed.
355
+ */
106
356
  cell?: HTMLTableCellElement | null
107
- colIndex?: number
108
- event?: string
109
- height?: ReturnType<typeof useElementBounding>['height']
110
- left?: ReturnType<typeof useElementBounding>['left']
357
+
358
+ /**
359
+ * The HTML parent element that the modal is currently being displayed for. The field is unset
360
+ * when the modal is not being displayed.
361
+ */
111
362
  parent?: HTMLElement
363
+
364
+ /**
365
+ * The index of the column that the modal is currently being displayed for. The field is
366
+ * unset when the modal is not being displayed.
367
+ */
368
+ colIndex?: number
369
+
370
+ /**
371
+ * The index of the row that the modal is currently being displayed for. The field is
372
+ * unset when the modal is not being displayed.
373
+ */
112
374
  rowIndex?: number
113
- visible?: boolean
114
- width?: ReturnType<typeof useElementBounding>['width']
115
375
 
376
+ /**
377
+ * The component to use to render the modal. If not provided, the table will
378
+ * try to use the column's `modalComponent` property, if set. If that is not set,
379
+ * the table will not display a modal.
380
+ *
381
+ * @see {@link TableColumn.modalComponent}
382
+ */
116
383
  component?: string
384
+
385
+ /**
386
+ * Additional properties to pass to the table's modal component.
387
+ */
117
388
  componentProps?: Record<string, any>
389
+
390
+ /**
391
+ * Reactive bottom value for the modal's bounding box. The field is unset when the modal
392
+ * is not being displayed.
393
+ */
394
+ bottom?: ReturnType<typeof useElementBounding>['bottom']
395
+
396
+ /**
397
+ * Reactive height value for the modal's bounding box. The field is unset when the modal
398
+ * is not being displayed.
399
+ */
400
+ height?: ReturnType<typeof useElementBounding>['height']
401
+
402
+ /**
403
+ * Reactive left value for the modal's bounding box. The field is unset when the modal
404
+ * is not being displayed.
405
+ */
406
+ left?: ReturnType<typeof useElementBounding>['left']
407
+
408
+ /**
409
+ * Reactive width value for the modal's bounding box. The field is unset when the modal
410
+ * is not being displayed.
411
+ */
412
+ width?: ReturnType<typeof useElementBounding>['width']
118
413
  }
119
414
 
120
415
  /**
121
416
  * Table modal component props definition.
122
417
  * @public
123
418
  */
124
- export type TableModalProps = {
419
+ export interface TableModalProps {
420
+ /**
421
+ * Additional arbitrary properties that can be passed to the modal component.
422
+ */
125
423
  [key: string]: any
424
+
425
+ /**
426
+ * The index of the column that the modal is currently being displayed for.
427
+ */
126
428
  colIndex: number
429
+
430
+ /**
431
+ * The index of the row that the modal is currently being displayed for.
432
+ */
127
433
  rowIndex: number
434
+
435
+ /**
436
+ * The store for managing the current table's state.
437
+ */
128
438
  store: ReturnType<typeof createTableStore>
129
439
  }