@stonecrop/atable 0.2.63 → 0.2.65

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/src/index.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  import { App } from 'vue'
2
2
 
3
- import ACell from '@/components/ACell.vue'
4
- import AExpansionRow from '@/components/AExpansionRow.vue'
5
- import ARow from '@/components/ARow.vue'
6
- import ATable from '@/components/ATable.vue'
7
- import ATableHeader from '@/components/ATableHeader.vue'
8
- import ATableModal from '@/components/ATableModal.vue'
9
- import TableDataStore from './components'
10
- export type { CellContext, TableColumn, TableConfig, TableDisplay, TableRow, TableModal } from '@/types'
3
+ import ACell from './components/ACell.vue'
4
+ import AExpansionRow from './components/AExpansionRow.vue'
5
+ import ARow from './components/ARow.vue'
6
+ import ATable from './components/ATable.vue'
7
+ import ATableHeader from './components/ATableHeader.vue'
8
+ import ATableModal from './components/ATableModal.vue'
9
+ export { createTableStore } from './stores/table'
10
+ export type { CellContext, TableColumn, TableConfig, TableDisplay, TableRow, TableModal } from './types'
11
11
 
12
12
  /**
13
13
  * Install all ATable components
@@ -23,4 +23,4 @@ function install(app: App /* options */) {
23
23
  app.component('ATableModal', ATableModal)
24
24
  }
25
25
 
26
- export { install, ACell, AExpansionRow, ARow, ATable, ATableHeader, ATableModal, TableDataStore }
26
+ export { install, ACell, AExpansionRow, ARow, ATable, ATableHeader, ATableModal }
@@ -0,0 +1,214 @@
1
+ import { defineStore } from 'pinia'
2
+ import { type CSSProperties, computed, ref } from 'vue'
3
+
4
+ import type { CellContext, TableColumn, TableConfig, TableDisplay, TableModal, TableRow } from '@/types'
5
+
6
+ /**
7
+ * Create a table store
8
+ * @param initData - Initial data for the table store
9
+ * @returns table store instance
10
+ * @public
11
+ */
12
+ export const createTableStore = (initData: {
13
+ columns: TableColumn[]
14
+ rows: TableRow[]
15
+ id?: string
16
+ config?: TableConfig
17
+ table?: { [key: string]: any }
18
+ display?: TableDisplay[]
19
+ modal?: TableModal
20
+ }) => {
21
+ const id = initData.id || crypto.randomUUID()
22
+ const createStore = defineStore(`table-${id}`, () => {
23
+ // util functions
24
+ const createTableObject = () => {
25
+ const table = {}
26
+ for (const [colIndex, column] of columns.value.entries()) {
27
+ for (const [rowIndex, row] of rows.value.entries()) {
28
+ table[`${colIndex}:${rowIndex}`] = row[column.name]
29
+ }
30
+ }
31
+ return table
32
+ }
33
+
34
+ const createDisplayObject = (display?: TableDisplay[]) => {
35
+ const defaultDisplay: TableDisplay[] = [Object.assign({}, { rowModified: false })]
36
+
37
+ // TODO: (typing) what is the type of `display` here?
38
+ if (display) {
39
+ if ('0:0' in display) {
40
+ return display
41
+ }
42
+ // else if ('default' in display) {
43
+ // // TODO: (typing) what is the possible input here for 'default'?
44
+ // defaultDisplay = display.default
45
+ // }
46
+ }
47
+
48
+ // TODO: (typing) is this type correct for the parent set?
49
+ const parents = new Set<string | number>()
50
+ for (let rowIndex = rows.value.length - 1; rowIndex >= 0; rowIndex--) {
51
+ const row = rows.value[rowIndex]
52
+ if (row.parent) {
53
+ parents.add(row.parent)
54
+ }
55
+
56
+ defaultDisplay[rowIndex] = {
57
+ childrenOpen: false,
58
+ expanded: false,
59
+ indent: row.indent || null,
60
+ isParent: parents.has(rowIndex),
61
+ isRoot: row.parent === null || row.parent === undefined,
62
+ rowModified: false,
63
+ open: row.parent === null || row.parent === undefined,
64
+ parent: row.parent,
65
+ }
66
+ }
67
+
68
+ return defaultDisplay
69
+ }
70
+
71
+ // state
72
+ const columns = ref(initData.columns)
73
+ const rows = ref(initData.rows)
74
+ const config = ref(initData.config || {})
75
+ const table = ref(initData.table || createTableObject())
76
+ const display = ref(createDisplayObject(initData.display))
77
+ const modal = ref(initData.modal || { visible: false })
78
+
79
+ // getters
80
+ const hasPinnedColumns = computed(() => columns.value.some(col => col.pinned))
81
+
82
+ const numberedRowWidth = computed(() => {
83
+ const indent = Math.ceil(rows.value.length / 100 + 1)
84
+ return `${indent}ch`
85
+ })
86
+
87
+ const zeroColumn = computed(() => ['list', 'tree', 'list-expansion'].includes(config.value.view))
88
+
89
+ // actions
90
+ const getCellData = <T = any>(colIndex: number, rowIndex: number): T => table.value[`${colIndex}:${rowIndex}`]
91
+ const setCellData = (colIndex: number, rowIndex: number, value: any) => {
92
+ const index = `${colIndex}:${rowIndex}`
93
+ const col = columns.value[colIndex]
94
+
95
+ if (table.value[index] !== value) {
96
+ display.value[rowIndex].rowModified = true
97
+ }
98
+
99
+ table.value[index] = value
100
+ rows.value[rowIndex][col.name] = value
101
+ }
102
+
103
+ const getHeaderCellStyle = (column: TableColumn): CSSProperties => ({
104
+ minWidth: column.width || '40ch',
105
+ textAlign: column.align || 'center',
106
+ width: config.value.fullWidth ? 'auto' : null,
107
+ })
108
+
109
+ const isRowVisible = (rowIndex: number) => {
110
+ return config.value.view !== 'tree' || display.value[rowIndex].isRoot || display.value[rowIndex].open
111
+ }
112
+
113
+ const getRowExpandSymbol = (rowIndex: number) => {
114
+ if (config.value.view !== 'tree') {
115
+ return ''
116
+ }
117
+
118
+ if (display.value[rowIndex].isRoot || display.value[rowIndex].isParent) {
119
+ return display.value[rowIndex].childrenOpen ? '-' : '+'
120
+ }
121
+
122
+ return ''
123
+ }
124
+
125
+ const toggleRowExpand = (rowIndex: number) => {
126
+ if (config.value.view === 'tree') {
127
+ display.value[rowIndex].childrenOpen = !display.value[rowIndex].childrenOpen
128
+ for (let index = rows.value.length - 1; index >= 0; index--) {
129
+ if (display.value[index].parent === rowIndex) {
130
+ display.value[index].open = !display.value[index].open
131
+ if (display.value[index].childrenOpen) {
132
+ toggleRowExpand(index)
133
+ }
134
+ }
135
+ }
136
+ } else if (config.value.view === 'list-expansion') {
137
+ display.value[rowIndex].expanded = !display.value[rowIndex].expanded
138
+ }
139
+ }
140
+
141
+ const getCellDisplayValue = (colIndex: number, rowIndex: number) => {
142
+ const cellData = getCellData(colIndex, rowIndex)
143
+ return getFormattedValue(colIndex, rowIndex, cellData)
144
+ }
145
+
146
+ const getFormattedValue = (colIndex: number, rowIndex: number, value: any) => {
147
+ const column = columns.value[colIndex]
148
+ const row = rows.value[rowIndex]
149
+ const format = column.format
150
+
151
+ if (!format) {
152
+ return value
153
+ }
154
+
155
+ if (typeof format === 'function') {
156
+ return format(value, { table: table.value, row, column })
157
+ } else if (typeof format === 'string') {
158
+ // parse format function from string
159
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
160
+ const formatFn: (value: any, context?: CellContext) => string = Function(`"use strict";return (${format})`)()
161
+ return formatFn(value, { table: table.value, row, column })
162
+ }
163
+
164
+ return value
165
+ }
166
+
167
+ const closeModal = (event: MouseEvent) => {
168
+ if (!(event.target instanceof Node)) {
169
+ // if the target is not a node, it's probably a custom click event to Document or Window
170
+ // err on the side of closing the modal in that case
171
+ if (modal.value.visible) modal.value.visible = false
172
+ } else if (!modal.value.parent?.contains(event.target)) {
173
+ if (modal.value.visible) modal.value.visible = false
174
+ }
175
+ }
176
+
177
+ const getIndent = (colIndex: number, indentLevel?: number) => {
178
+ if (indentLevel && colIndex === 0 && indentLevel > 0) {
179
+ return `${indentLevel}ch`
180
+ } else {
181
+ return 'inherit'
182
+ }
183
+ }
184
+
185
+ return {
186
+ // state
187
+ columns,
188
+ rows,
189
+ config,
190
+ table,
191
+ display,
192
+ modal,
193
+
194
+ // getters
195
+ hasPinnedColumns,
196
+ numberedRowWidth,
197
+ zeroColumn,
198
+
199
+ // actions
200
+ closeModal,
201
+ getCellData,
202
+ getCellDisplayValue,
203
+ getFormattedValue,
204
+ getHeaderCellStyle,
205
+ getIndent,
206
+ getRowExpandSymbol,
207
+ isRowVisible,
208
+ setCellData,
209
+ toggleRowExpand,
210
+ }
211
+ })
212
+
213
+ return createStore()
214
+ }
@@ -1,5 +1,7 @@
1
- import TableDataStore from '@/components'
2
-
1
+ /**
2
+ * Table column definition.
3
+ * @public
4
+ */
3
5
  export type TableColumn = {
4
6
  name: string
5
7
 
@@ -19,12 +21,20 @@ export type TableColumn = {
19
21
  mask?: (value: any) => any
20
22
  }
21
23
 
24
+ /**
25
+ * Table cell context definition.
26
+ * @public
27
+ */
22
28
  export type CellContext = {
23
29
  row: TableRow
24
30
  column: TableColumn
25
- table: TableDataStore['table']
31
+ table: { [key: string]: any }
26
32
  }
27
33
 
34
+ /**
35
+ * Table configuration definition.
36
+ * @public
37
+ */
28
38
  export type TableConfig = {
29
39
  /**
30
40
  * The type of view to display the table in. Possible values:
@@ -37,6 +47,10 @@ export type TableConfig = {
37
47
  fullWidth?: boolean
38
48
  }
39
49
 
50
+ /**
51
+ * Table display definition.
52
+ * @public
53
+ */
40
54
  export type TableDisplay = {
41
55
  childrenOpen?: boolean
42
56
  expanded?: boolean
@@ -48,12 +62,20 @@ export type TableDisplay = {
48
62
  rowModified?: boolean
49
63
  }
50
64
 
65
+ /**
66
+ * Table row definition.
67
+ * @public
68
+ */
51
69
  export type TableRow = {
52
70
  [key: string]: any
53
71
  indent?: number
54
72
  parent?: number
55
73
  }
56
74
 
75
+ /**
76
+ * Table modal definition.
77
+ * @public
78
+ */
57
79
  export type TableModal = {
58
80
  colIndex?: number
59
81
  event?: string
@@ -1,97 +0,0 @@
1
- import { computed, reactive } from 'vue';
2
- export default class TableDataStore {
3
- id;
4
- rows;
5
- columns;
6
- config;
7
- table;
8
- display;
9
- modal;
10
- constructor(id, columns, rows, config, table, display) {
11
- this.id = id || crypto.randomUUID();
12
- this.rows = rows;
13
- this.columns = reactive(columns);
14
- this.config = reactive(config);
15
- this.table = table || reactive(this.createTableObject());
16
- this.display = this.createDisplayObject(display);
17
- this.modal = reactive({ visible: false });
18
- }
19
- createTableObject() {
20
- const table = {};
21
- for (const [colIndex, column] of this.columns.entries()) {
22
- for (const [rowIndex, row] of this.rows.entries()) {
23
- table[`${colIndex}:${rowIndex}`] = row[column.name];
24
- }
25
- }
26
- return table;
27
- }
28
- createDisplayObject(display) {
29
- const defaultDisplay = [Object.assign({}, { rowModified: false })];
30
- // TODO: (typing) what is the type of `display` here?
31
- if (display) {
32
- if ('0:0' in display) {
33
- return display;
34
- }
35
- // else if ('default' in display) {
36
- // // TODO: (typing) what is the possible input here for 'default'?
37
- // defaultDisplay = display.default
38
- // }
39
- }
40
- // TODO: (typing) is this type correct for the parent set?
41
- const parents = new Set();
42
- for (let rowIndex = this.rows.length - 1; rowIndex >= 0; rowIndex--) {
43
- const row = this.rows[rowIndex];
44
- if (row.parent) {
45
- parents.add(row.parent);
46
- }
47
- defaultDisplay[rowIndex] = {
48
- childrenOpen: false,
49
- expanded: false,
50
- indent: row.indent || null,
51
- isParent: parents.has(rowIndex),
52
- isRoot: row.parent === null || row.parent === undefined,
53
- rowModified: false,
54
- open: row.parent === null || row.parent === undefined,
55
- parent: row.parent,
56
- };
57
- }
58
- return reactive(defaultDisplay);
59
- }
60
- get zeroColumn() {
61
- return ['list', 'tree', 'list-expansion'].includes(this.config.view);
62
- }
63
- get numberedRowWidth() {
64
- return computed(() => {
65
- return String(Math.ceil(this.rows.length / 100) + 1) + 'ch';
66
- });
67
- }
68
- cellData(colIndex, rowIndex) {
69
- return this.table[`${colIndex}:${rowIndex}`];
70
- }
71
- setCellData(rowIndex, colIndex, value) {
72
- const index = `${colIndex}:${rowIndex}`;
73
- const col = this.columns[colIndex];
74
- if (this.table[index] !== value) {
75
- this.display[rowIndex].rowModified = true;
76
- }
77
- this.table[index] = value;
78
- this.rows[rowIndex][col.name] = value;
79
- return this.table[index];
80
- }
81
- toggleRowExpand(rowIndex) {
82
- if (this.config.view === 'tree') {
83
- this.display[rowIndex].childrenOpen = !this.display[rowIndex].childrenOpen;
84
- for (let index = this.rows.length - 1; index >= 0; index--) {
85
- if (this.display[index].parent === rowIndex) {
86
- this.display[index].open = !this.display[index].open;
87
- if (this.display[index].childrenOpen) {
88
- this.toggleRowExpand(index);
89
- }
90
- }
91
- }
92
- }
93
- else if (this.config.view === 'list-expansion') {
94
- this.display[rowIndex].expanded = !this.display[rowIndex].expanded;
95
- }
96
- }
97
- }
@@ -1,23 +0,0 @@
1
- import type { TableDisplay, TableRow, TableColumn, TableConfig, TableModal } from '@/types';
2
- export default class TableDataStore {
3
- id: string;
4
- rows: TableRow[];
5
- columns: TableColumn[];
6
- config: TableConfig;
7
- table: {
8
- [key: string]: any;
9
- };
10
- display: TableDisplay[];
11
- modal: TableModal;
12
- constructor(id?: string, columns?: TableColumn[], rows?: TableRow[], config?: TableConfig, table?: {
13
- [key: string]: any;
14
- }, display?: TableDisplay[]);
15
- createTableObject(): {};
16
- createDisplayObject(display?: TableDisplay[]): (TableDisplay[] & Record<"0:0", unknown>) | import("vue").Reactive<TableDisplay[]>;
17
- get zeroColumn(): boolean;
18
- get numberedRowWidth(): import("vue").ComputedRef<string>;
19
- cellData<T>(colIndex: number, rowIndex: number): T;
20
- setCellData(rowIndex: number, colIndex: number, value: any): any;
21
- toggleRowExpand(rowIndex: number): void;
22
- }
23
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAE3F,MAAM,CAAC,OAAO,OAAO,cAAc;IAClC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,QAAQ,EAAE,CAAA;IAChB,OAAO,EAAE,WAAW,EAAE,CAAA;IACtB,MAAM,EAAE,WAAW,CAAA;IACnB,KAAK,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,CAAA;IAC7B,OAAO,EAAE,YAAY,EAAE,CAAA;IACvB,KAAK,EAAE,UAAU,CAAA;gBAGhB,EAAE,CAAC,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,WAAW,EAAE,EACvB,IAAI,CAAC,EAAE,QAAQ,EAAE,EACjB,MAAM,CAAC,EAAE,WAAW,EACpB,KAAK,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,EAC9B,OAAO,CAAC,EAAE,YAAY,EAAE;IAWzB,iBAAiB;IAUjB,mBAAmB,CAAC,OAAO,CAAC,EAAE,YAAY,EAAE;IAqC5C,IAAI,UAAU,YAEb;IAED,IAAI,gBAAgB,sCAInB;IAED,QAAQ,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC;IAIlD,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG;IAa1D,eAAe,CAAC,QAAQ,EAAE,MAAM;CAehC"}
@@ -1,120 +0,0 @@
1
- import { computed, reactive } from 'vue'
2
-
3
- import type { TableDisplay, TableRow, TableColumn, TableConfig, TableModal } from '@/types'
4
-
5
- export default class TableDataStore {
6
- id: string
7
- rows: TableRow[]
8
- columns: TableColumn[]
9
- config: TableConfig
10
- table: { [key: string]: any }
11
- display: TableDisplay[]
12
- modal: TableModal
13
-
14
- constructor(
15
- id?: string,
16
- columns?: TableColumn[],
17
- rows?: TableRow[],
18
- config?: TableConfig,
19
- table?: { [key: string]: any },
20
- display?: TableDisplay[]
21
- ) {
22
- this.id = id || crypto.randomUUID()
23
- this.rows = rows
24
- this.columns = reactive(columns)
25
- this.config = reactive(config)
26
- this.table = table || reactive(this.createTableObject())
27
- this.display = this.createDisplayObject(display)
28
- this.modal = reactive({ visible: false })
29
- }
30
-
31
- createTableObject() {
32
- const table = {}
33
- for (const [colIndex, column] of this.columns.entries()) {
34
- for (const [rowIndex, row] of this.rows.entries()) {
35
- table[`${colIndex}:${rowIndex}`] = row[column.name]
36
- }
37
- }
38
- return table
39
- }
40
-
41
- createDisplayObject(display?: TableDisplay[]) {
42
- const defaultDisplay: TableDisplay[] = [Object.assign({}, { rowModified: false })]
43
-
44
- // TODO: (typing) what is the type of `display` here?
45
- if (display) {
46
- if ('0:0' in display) {
47
- return display
48
- }
49
- // else if ('default' in display) {
50
- // // TODO: (typing) what is the possible input here for 'default'?
51
- // defaultDisplay = display.default
52
- // }
53
- }
54
-
55
- // TODO: (typing) is this type correct for the parent set?
56
- const parents = new Set<string | number>()
57
- for (let rowIndex = this.rows.length - 1; rowIndex >= 0; rowIndex--) {
58
- const row = this.rows[rowIndex]
59
- if (row.parent) {
60
- parents.add(row.parent)
61
- }
62
-
63
- defaultDisplay[rowIndex] = {
64
- childrenOpen: false,
65
- expanded: false,
66
- indent: row.indent || null,
67
- isParent: parents.has(rowIndex),
68
- isRoot: row.parent === null || row.parent === undefined,
69
- rowModified: false,
70
- open: row.parent === null || row.parent === undefined,
71
- parent: row.parent,
72
- }
73
- }
74
-
75
- return reactive(defaultDisplay)
76
- }
77
-
78
- get zeroColumn() {
79
- return ['list', 'tree', 'list-expansion'].includes(this.config.view)
80
- }
81
-
82
- get numberedRowWidth() {
83
- return computed(() => {
84
- return String(Math.ceil(this.rows.length / 100) + 1) + 'ch'
85
- })
86
- }
87
-
88
- cellData<T>(colIndex: number, rowIndex: number): T {
89
- return this.table[`${colIndex}:${rowIndex}`]
90
- }
91
-
92
- setCellData(rowIndex: number, colIndex: number, value: any) {
93
- const index = `${colIndex}:${rowIndex}`
94
- const col = this.columns[colIndex]
95
-
96
- if (this.table[index] !== value) {
97
- this.display[rowIndex].rowModified = true
98
- }
99
-
100
- this.table[index] = value
101
- this.rows[rowIndex][col.name] = value
102
- return this.table[index]
103
- }
104
-
105
- toggleRowExpand(rowIndex: number) {
106
- if (this.config.view === 'tree') {
107
- this.display[rowIndex].childrenOpen = !this.display[rowIndex].childrenOpen
108
- for (let index = this.rows.length - 1; index >= 0; index--) {
109
- if (this.display[index].parent === rowIndex) {
110
- this.display[index].open = !this.display[index].open
111
- if (this.display[index].childrenOpen) {
112
- this.toggleRowExpand(index)
113
- }
114
- }
115
- }
116
- } else if (this.config.view === 'list-expansion') {
117
- this.display[rowIndex].expanded = !this.display[rowIndex].expanded
118
- }
119
- }
120
- }