@stonecrop/atable 0.4.12 → 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,7 +18,10 @@
18
18
  v-if="column.isGantt"
19
19
  :is="column.ganttComponent || 'AGanttCell'"
20
20
  :store="store"
21
- :color="row.gantt_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"
@@ -66,13 +69,13 @@
66
69
  <script setup lang="ts">
67
70
  import { vOnClickOutside } from '@vueuse/components'
68
71
  import { useMutationObserver } from '@vueuse/core'
69
- import { nextTick, watch, onMounted, useTemplateRef } from 'vue'
72
+ import { nextTick, watch, onMounted, useTemplateRef, computed } from 'vue'
70
73
 
71
74
  import ARow from './ARow.vue'
72
75
  import ATableHeader from './ATableHeader.vue'
73
76
  import ATableModal from './ATableModal.vue'
74
77
  import { createTableStore } from '../stores/table'
75
- import type { TableColumn, TableConfig, TableRow } from '../types'
78
+ import type { GanttDragEvent, TableColumn, TableConfig, TableRow } from '../types'
76
79
 
77
80
  const {
78
81
  id,
@@ -91,6 +94,7 @@ const {
91
94
  const emit = defineEmits<{
92
95
  'update:modelValue': [value: TableRow[]]
93
96
  cellUpdate: [{ colIndex: number; rowIndex: number; newValue: any; oldValue: any }]
97
+ 'gantt:drag': [event: GanttDragEvent]
94
98
  }>()
95
99
 
96
100
  const tableRef = useTemplateRef<HTMLTableElement>('table')
@@ -101,10 +105,31 @@ store.$onAction(({ name, store, args, after }) => {
101
105
  if (name === 'setCellData' || name === 'setCellText') {
102
106
  const [colIndex, rowIndex, newValue] = args
103
107
  const oldValue = store.getCellData(colIndex, rowIndex)
104
-
105
108
  after(() => {
106
109
  emit('cellUpdate', { colIndex, rowIndex, newValue, oldValue })
107
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
+ }
108
133
  }
109
134
  })
110
135
 
@@ -127,6 +152,10 @@ onMounted(() => {
127
152
  }
128
153
  })
129
154
 
155
+ const pinnedColumnCount = computed(() => {
156
+ return store.columns.filter(column => column.pinned).length
157
+ })
158
+
130
159
  const assignStickyCellWidths = () => {
131
160
  const table = tableRef.value
132
161
 
@@ -180,25 +209,24 @@ window.addEventListener('keydown', (event: KeyboardEvent) => {
180
209
 
181
210
  const getProcessedColumnsForRow = (row: TableRow) => {
182
211
  const isGanttRow = row.indent === 0
183
- const pinnedColumnCount = store.columns.filter(column => column.pinned).length
184
- if (!isGanttRow || pinnedColumnCount === 0) {
212
+ if (!isGanttRow || pinnedColumnCount.value === 0) {
185
213
  return store.columns
186
214
  }
187
215
 
188
216
  // For Gantt views, first add the pinned columns
189
217
  const result: TableColumn[] = []
190
- for (let i = 0; i < pinnedColumnCount; i++) {
218
+ for (let i = 0; i < pinnedColumnCount.value; i++) {
191
219
  const column = { ...store.columns[i] }
192
220
  column.originalIndex = i // Preserve original index
193
221
  result.push(column)
194
222
  }
195
223
 
196
- // Finally, add the Gantt bar column with a full-width colspan
224
+ // Finally, add the Gantt bar column with either a provided colspan or default to full-width colspan
197
225
  result.push({
198
- ...store.columns[pinnedColumnCount],
226
+ ...store.columns[pinnedColumnCount.value],
199
227
  isGantt: true,
200
- colspan: store.columns.length - pinnedColumnCount,
201
- originalIndex: pinnedColumnCount,
228
+ colspan: row.gantt?.colspan || store.columns.length - pinnedColumnCount.value,
229
+ originalIndex: pinnedColumnCount.value,
202
230
  width: 'auto', // TODO: refactor to API that can detect when data exists in a cell. Might have be custom and not generalizable
203
231
  })
204
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
  /**
@@ -121,7 +129,6 @@ export const createTableStore = (initData: {
121
129
 
122
130
  const isRowGantt = (rowIndex: number) => {
123
131
  const row = rows.value[rowIndex]
124
- console.log(config.value.view, row.indent)
125
132
  return config.value.view === 'gantt' && row.indent === 0
126
133
  }
127
134
 
@@ -201,6 +208,23 @@ export const createTableStore = (initData: {
201
208
  }
202
209
  }
203
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
+
204
228
  return {
205
229
  // state
206
230
  columns,
@@ -229,6 +253,7 @@ export const createTableStore = (initData: {
229
253
  setCellData,
230
254
  setCellText,
231
255
  toggleRowExpand,
256
+ updateGanttBar,
232
257
  }
233
258
  })
234
259
 
@@ -6,24 +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
- colspan?: number
21
- ganttComponent?: string
22
- isGantt?: boolean
23
- originalIndex?: number
60
+ /**
61
+ * Control whether the column should be pinned to the table.
62
+ *
63
+ * @defaultValue false
64
+ */
65
+ pinned?: boolean
24
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
+ */
25
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
+ */
26
78
  cellComponentProps?: Record<string, any>
79
+
27
80
  /**
28
81
  * The component to use for the modal. If a function is provided, it will be called with the cell context.
29
82
  * The following properties are available on the cell context:
@@ -38,20 +91,85 @@ export type TableColumn = {
38
91
  * - `rowIndex` - the row index of the current cell
39
92
  * - `store` - the table data store
40
93
  */
94
+
41
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
+ */
42
102
  modalComponentExtraProps?: Record<string, any>
43
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
+ */
44
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
+ */
45
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
46
153
  }
47
154
 
48
155
  /**
49
156
  * Table cell context definition.
50
157
  * @public
51
158
  */
52
- export type CellContext = {
159
+ export interface CellContext {
160
+ /**
161
+ * The row object for the current cell.
162
+ */
53
163
  row: TableRow
164
+
165
+ /**
166
+ * The column object for the current cell.
167
+ */
54
168
  column: TableColumn
169
+
170
+ /**
171
+ * The table object for the current cell.
172
+ */
55
173
  table: { [key: string]: any }
56
174
  }
57
175
 
@@ -59,7 +177,7 @@ export type CellContext = {
59
177
  * Table configuration definition.
60
178
  * @public
61
179
  */
62
- export type TableConfig = {
180
+ export interface TableConfig {
63
181
  /**
64
182
  * The type of view to display the table in. Possible values:
65
183
  * - `uncounted` - row numbers are not displayed in the table
@@ -68,6 +186,12 @@ export type TableConfig = {
68
186
  * - `tree` - carets are displayed in the number column that expand/collapse grouped rows
69
187
  */
70
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
+ */
71
195
  fullWidth?: boolean
72
196
  }
73
197
 
@@ -75,14 +199,72 @@ export type TableConfig = {
75
199
  * Table display definition.
76
200
  * @public
77
201
  */
78
- export type TableDisplay = {
79
- 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
+ */
80
210
  expanded?: boolean
81
- 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
+ */
82
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
+ */
83
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
+ */
84
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
+ */
85
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
+ */
86
268
  rowModified?: boolean
87
269
  }
88
270
 
@@ -90,44 +272,168 @@ export type TableDisplay = {
90
272
  * Table row definition.
91
273
  * @public
92
274
  */
93
- export type TableRow = {
275
+ export interface TableRow {
276
+ /**
277
+ * Additional arbitrary properties that can be passed to the row object.
278
+ */
94
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
+ */
95
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
+ */
96
296
  parent?: number
97
297
 
98
298
  /**
99
- * When a table is being viewed as a Gantt chart, this colour would be applied to the row's gantt bar, if one exists.
299
+ * The options to use when rendering the row as a Gantt table.
300
+ */
301
+ gantt?: GanttOptions
302
+ }
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.
100
328
  */
101
- gantt_color?: string
329
+ colspan?: number
102
330
  }
103
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
+
104
340
  /**
105
341
  * Table modal definition.
106
342
  * @public
107
343
  */
108
- export type TableModal = {
109
- 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
+ */
110
356
  cell?: HTMLTableCellElement | null
111
- colIndex?: number
112
- event?: string
113
- height?: ReturnType<typeof useElementBounding>['height']
114
- 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
+ */
115
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
+ */
116
374
  rowIndex?: number
117
- visible?: boolean
118
- width?: ReturnType<typeof useElementBounding>['width']
119
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
+ */
120
383
  component?: string
384
+
385
+ /**
386
+ * Additional properties to pass to the table's modal component.
387
+ */
121
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']
122
413
  }
123
414
 
124
415
  /**
125
416
  * Table modal component props definition.
126
417
  * @public
127
418
  */
128
- export type TableModalProps = {
419
+ export interface TableModalProps {
420
+ /**
421
+ * Additional arbitrary properties that can be passed to the modal component.
422
+ */
129
423
  [key: string]: any
424
+
425
+ /**
426
+ * The index of the column that the modal is currently being displayed for.
427
+ */
130
428
  colIndex: number
429
+
430
+ /**
431
+ * The index of the row that the modal is currently being displayed for.
432
+ */
131
433
  rowIndex: number
434
+
435
+ /**
436
+ * The store for managing the current table's state.
437
+ */
132
438
  store: ReturnType<typeof createTableStore>
133
439
  }