@stonecrop/atable 0.2.38 → 0.2.40
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/dist/atable.d.ts +2 -2
- package/dist/atable.js +489 -476
- package/dist/atable.js.map +1 -1
- package/dist/atable.umd.cjs +1 -1
- package/dist/atable.umd.cjs.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/types/index.d.ts +5 -4
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/style.css +1 -1
- package/package.json +3 -3
- package/src/components/ACell.vue +44 -35
- package/src/components/AExpansionRow.vue +0 -3
- package/src/components/ARow.vue +8 -4
- package/src/components/ATable.vue +44 -3
- package/src/components/ATableHeader.vue +10 -3
- package/src/index.ts +1 -1
- package/src/types/index.ts +5 -4
package/dist/src/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import ATable from '@/components/ATable.vue';
|
|
|
6
6
|
import ATableHeader from '@/components/ATableHeader.vue';
|
|
7
7
|
import ATableModal from '@/components/ATableModal.vue';
|
|
8
8
|
import TableDataStore from './components';
|
|
9
|
-
export type {
|
|
9
|
+
export type { CellContext, TableColumn, TableConfig, TableDisplay, TableRow, TableModal } from '@/types';
|
|
10
10
|
/**
|
|
11
11
|
* Install all ATable components
|
|
12
12
|
* @param app - Vue app instance
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB,OAAO,KAAK,MAAM,wBAAwB,CAAA;AAC1C,OAAO,aAAa,MAAM,gCAAgC,CAAA;AAC1D,OAAO,IAAI,MAAM,uBAAuB,CAAA;AACxC,OAAO,MAAM,MAAM,yBAAyB,CAAA;AAC5C,OAAO,YAAY,MAAM,+BAA+B,CAAA;AACxD,OAAO,WAAW,MAAM,8BAA8B,CAAA;AACtD,OAAO,cAAc,MAAM,cAAc,CAAA;AACzC,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAA;AAEzB,OAAO,KAAK,MAAM,wBAAwB,CAAA;AAC1C,OAAO,aAAa,MAAM,gCAAgC,CAAA;AAC1D,OAAO,IAAI,MAAM,uBAAuB,CAAA;AACxC,OAAO,MAAM,MAAM,yBAAyB,CAAA;AAC5C,OAAO,YAAY,MAAM,+BAA+B,CAAA;AACxD,OAAO,WAAW,MAAM,8BAA8B,CAAA;AACtD,OAAO,cAAc,MAAM,cAAc,CAAA;AACzC,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAExG;;;;GAIG;AACH,iBAAS,OAAO,CAAC,GAAG,EAAE,GAAG,QAOxB;AAED,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,cAAc,EAAE,CAAA"}
|
|
@@ -6,14 +6,15 @@ export type TableColumn = {
|
|
|
6
6
|
label?: string;
|
|
7
7
|
type?: string;
|
|
8
8
|
width?: string;
|
|
9
|
+
pinned?: boolean;
|
|
9
10
|
cellComponent?: string;
|
|
10
11
|
cellComponentProps?: Record<string, any>;
|
|
11
|
-
modalComponent?: string;
|
|
12
|
-
|
|
13
|
-
format?: string | ((value: any, context?:
|
|
12
|
+
modalComponent?: string | ((context?: CellContext) => string);
|
|
13
|
+
modalComponentExtraProps?: Record<string, any>;
|
|
14
|
+
format?: string | ((value: any, context?: CellContext) => string);
|
|
14
15
|
mask?: (value: any) => any;
|
|
15
16
|
};
|
|
16
|
-
export type
|
|
17
|
+
export type CellContext = {
|
|
17
18
|
row: TableRow;
|
|
18
19
|
column: TableColumn;
|
|
19
20
|
table: TableDataStore['table'];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,MAAM,cAAc,CAAA;AAEzC,MAAM,MAAM,WAAW,GAAG;IACzB,IAAI,EAAE,MAAM,CAAA;IAEZ,KAAK,CAAC,EAAE,eAAe,CAAA;IACvB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,MAAM,cAAc,CAAA;AAEzC,MAAM,MAAM,WAAW,GAAG;IACzB,IAAI,EAAE,MAAM,CAAA;IAEZ,KAAK,CAAC,EAAE,eAAe,CAAA;IACvB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;IAEhB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IACxC,cAAc,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,WAAW,KAAK,MAAM,CAAC,CAAA;IAC7D,wBAAwB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;IAE9C,MAAM,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,WAAW,KAAK,MAAM,CAAC,CAAA;IACjE,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAA;CAC1B,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACzB,GAAG,EAAE,QAAQ,CAAA;IACb,MAAM,EAAE,WAAW,CAAA;IACnB,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,CAAA;CAC9B,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACzB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,gBAAgB,CAAA;IACzC,SAAS,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACtB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IAEd,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CACpC,CAAA"}
|
package/dist/style.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
@import"https://fonts.googleapis.com/css2?family=Arimo:ital,wght@0,400..700;1,400..700&display=swap"
|
|
1
|
+
@import"https://fonts.googleapis.com/css2?family=Arimo:ital,wght@0,400..700;1,400..700&display=swap";:root{--primary-color: #0098c9;--primary-text-color: #ffffff;--brand-color: #202a44;--gray-5: #f2f2f2;--gray-10: #e6e6e6;--gray-20: #cccccc;--gray-50: #808080;--gray-60: #666666;--gray-80: #333333;--brand-danger: #e63c28;--brand-success: #155724;--row-color-zebra-light: #eeeeee;--row-color-zebra-dark: #dddddd;--focus-cell-background: #ffffff;--focus-cell-outline: #000000;--cell-border-color: #ffffff;--cell-text-color: #3a3c41;--active-cell-background: #ffffff;--active-cell-outline: #e6a92d;--row-border-color: var(--gray-20);--header-border-color: #ffffff;--header-text-color: var(--gray-20);--row-number-background-color: #ffffff;--input-border-color: var(--gray-20);--input-label-color: var(--gray-60);--input-active-border-color: #000000;--input-active-label-color: #000000;--required-border: #e63c28;--font-size: 10px;--font-family: Arimo, Arial, sans-serif;--table-font-size: 16px;--atable-font-family: "Arimo", sans-serif;--atable-row-padding: 0px;--atable-row-height: 1.5em;--btn-color: white;--btn-border: #cccccc;--btn-hover: #f2f2f2;--btn-label-color: black}.aform{display:flex;flex-wrap:wrap;gap:1rem;padding:1rem;border:1px solid var(--gray-5);border-left:4px solid var(--gray-5);margin-bottom:1rem;max-width:100%}@media screen and (max-width: 400px){.aform{flex-direction:column}}.aform__form-element{border:1px solid transparent;padding:0;margin:0;position:relative;box-sizing:border-box;flex-grow:1;min-width:100px}.aform__input-field{outline:1px solid transparent;border:1px solid var(--input-border-color);font-size:1rem;padding:.5rem .25rem .25rem .5rem;margin:0;border-radius:0;box-sizing:border-box;width:100%;position:relative;color:var(--cell-text-color)}.aform__field-label{color:var(--input-label-color);display:inline-block;position:absolute;padding:0 .25rem;margin:0rem;z-index:2;font-size:.7rem;font-weight:300;letter-spacing:.05rem;width:auto;box-sizing:border-box;background:#fff;margin:0;border:1px solid var(--input-border-color);grid-row:1;top:0;left:10px;border:none;transform:translateY(-50%)}p.error{display:block;display:inline-block;display:none;padding:0rem 0rem 0rem .5rem;margin:.5rem 0 .25rem 0rem;border:1px solid transparent;width:100%;width:auto;color:var(--brand-danger);font-size:.8rem;position:absolute;right:0;top:0;background:#fff;padding:.25rem;transform:translate(-1rem,-50%);margin:0}.aform__input-field:focus{border:1px solid var(--input-active-border-color)}.aform__input-field:focus+.aform__field-label{color:var(--input-active-label-color)}.aform__checkbox{cursor:pointer;width:auto;margin-top:0;display:block}.aform__checkbox:checked{accent-color:var(--primary-color);border:1px solid black}.aform__checkbox-container{width:100%;display:inline-block;text-align:left}.aform__checkbox-container input{width:auto}.aform__checkbox-container:hover+.aform__field-label{color:var(--input-active-label-color)}.aform-primary-action{font-size:100%;text-align:center;min-height:2em;padding:.25rem 1rem;border:1px solid var(--primary-color);color:var(--primary-text-color);background-color:var(--primary-color);outline:2px solid var(--primary-text-color);transition:outline-offset .2s ease;font-size:var(--font-size);margin:.5ch}.aform-primary-action:hover,.aform-primary-action:active{outline:2px solid var(--primary-text-color);outline-offset:-4px;transition:outline-offset .2s ease}tr:focus{background-color:#add8e6;outline:auto}.aform__form-btn{padding:.5rem 2rem;width:auto;border:1px solid var(--input-border-color);color:var(--input-label-color);cursor:pointer;background-color:#fff}.aform__form-btn:disabled{background-color:var(--gray-5)}.aform__file-attach{padding:1rem;display:flex;flex-wrap:wrap;gap:1rem;flex-direction:row;justify-content:center;align-items:center;border:1px dashed var(--input-border-color);width:100%}@media screen and (max-width: 400px){.aform__file-attach>.aform__form-btn{width:100%}}.aform__file-attach-feedback{color:var(--input-label-color);width:100%;padding:.5rem;text-align:center;align-self:center}.aform__file-attach-feedback>li{list-style:none;font-style:italic}.aform__file-attach-feedback>p{margin-top:0}.atable{font-family:var(--atable-font-family);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:var(--table-font-size);border-collapse:collapse}.row-index{color:var(--header-text-color);font-weight:700;text-align:center;-webkit-user-select:none;user-select:none;width:2ch}.expandable-row{border-top:1px solid var(--row-border-color);height:var(--atable-row-height);border-left:4px solid var(--row-border-color)}.expanded-row{border-bottom:1px solid var(--row-border-color);border-top:1px solid var(--row-border-color)}.expanded-row-content{border-bottom:1px solid var(--row-border-color);border-top:1px solid var(--row-border-color);padding:1.5rem}.atable__cell{border-radius:0;box-sizing:border-box;margin:0;outline:none;box-shadow:none;color:var(--cell-text-color);padding-left:.5ch!important;padding-right:.5ch;padding-top:var(--atable-row-padding);padding-bottom:var(--atable-row-padding);border-spacing:0px;border-collapse:collapse;overflow:hidden;text-overflow:ellipsis;order:1}.atable__cell span{overflow:hidden;text-overflow:ellipsis}.atable__cell:focus,.atable__cell:focus-within{background-color:var(--focus-cell-background);outline-width:2px;outline-style:solid;outline-offset:-1px;outline-color:var(--focus-cell-outline);box-shadow:none;min-height:1.15em;max-height:1.15em;overflow:hidden;box-sizing:border-box}.table-row{border-top:1px solid var(--row-border-color);height:var(--atable-row-height);display:flex;background-color:#fff}.list-index{color:var(--header-text-color);font-weight:700;padding-left:var(--atable-row-padding);padding-right:1em;text-align:center;-webkit-user-select:none;user-select:none;width:30px;text-overflow:ellipsis;overflow:hidden}.tree-index{color:var(--header-text-color);font-weight:700;text-align:center;-webkit-user-select:none;user-select:none;width:2ch}.atable #header-index{width:30px;padding-right:1em;padding-left:0;box-sizing:border-box}.atable-header-row{display:flex}.atable th{border-width:0px;border-style:solid;border-radius:0;padding-left:.5ch;padding-right:.5ch;padding-top:var(--atable-row-padding);padding-bottom:var(--atable-row-padding);color:var(--gray-60);height:var(--atable-row-height);font-weight:300;letter-spacing:.05rem;order:1;box-sizing:border-box}#header-index{box-sizing:content-box}.atable th:focus{outline:none}.amodal{z-index:100;position:absolute;background-color:var(--row-color-zebra-dark)}.sticky-index{position:sticky;left:0;z-index:1;order:0}.sticky-column,th.sticky-column,td.sticky-column,th.sticky-index,td.sticky-index{position:sticky;z-index:1;order:0;background:#fff}.sticky-column-edge,.atable th.sticky-column-edge{border-right:1px solid var(--row-border-color);border-right-width:1px}.login-container{width:100%;position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;font-family:var(--font-family)}.account-container{width:100%;margin-left:auto;margin-top:.5rem;margin-right:auto;display:flex;flex-direction:column;justify-content:center}.account-header{display:flex;flex-direction:column;text-align:center;margin-top:.5rem}#account-title{font-size:1.5rem;line-height:2rem;font-weight:600;letter-spacing:-.025em;margin:0}#account-subtitle{font-size:.875rem;line-height:1.25rem;margin:1rem}.login-form-container{display:grid;gap:.5rem}.login-form-element{display:grid;margin:.5rem 0;position:relative}.login-field{padding:.5rem .25rem .25rem .5rem;outline:1px solid transparent;border:1px solid var(--input-border-color);border-radius:.25rem}.login-field:focus{border:1px solid black}.btn{background-color:var(--btn-color);color:var(--btn-label-color);border:1px solid var(--btn-border);margin:.5rem 0;padding:.25rem;position:relative;cursor:pointer}.btn:hover{background-color:var(--btn-hover)}.btn:disabled{background-color:light-dark(rgba(239,239,239,.3),rgba(59,59,59,.3));color:light-dark(rgb(84,84,84),rgb(170,170,170))}.disabled{opacity:.5}.loading-icon{animation:spin 1s linear infinite forwards;display:inline-block;margin-right:.2rem;line-height:0;font-size:1rem;position:relative;top:.2rem}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stonecrop/atable",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.40",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": {
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"@vueuse/core": "^11.1.0",
|
|
36
36
|
"uuid": "^10.0.0",
|
|
37
37
|
"vue": "^3.5.6",
|
|
38
|
-
"@stonecrop/
|
|
39
|
-
"@stonecrop/
|
|
38
|
+
"@stonecrop/utilities": "0.2.40",
|
|
39
|
+
"@stonecrop/themes": "0.2.40"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@microsoft/api-documenter": "^7.25.3",
|
package/src/components/ACell.vue
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
ref="cell"
|
|
4
4
|
:data-colindex="colIndex"
|
|
5
5
|
:data-rowindex="rowIndex"
|
|
6
|
-
:data-editable="
|
|
7
|
-
:contenteditable="
|
|
6
|
+
:data-editable="column.edit"
|
|
7
|
+
:contenteditable="column.edit"
|
|
8
8
|
:tabindex="tabIndex"
|
|
9
9
|
:spellcheck="false"
|
|
10
10
|
:style="cellStyle"
|
|
@@ -14,12 +14,13 @@
|
|
|
14
14
|
@input="onChange"
|
|
15
15
|
@click="handleInput"
|
|
16
16
|
@mousedown="handleInput"
|
|
17
|
-
class="atable__cell"
|
|
17
|
+
class="atable__cell"
|
|
18
|
+
:class="pinned ? 'sticky-column' : ''">
|
|
18
19
|
<component
|
|
19
|
-
v-if="
|
|
20
|
-
:is="
|
|
20
|
+
v-if="column.cellComponent"
|
|
21
|
+
:is="column.cellComponent"
|
|
21
22
|
:value="displayValue"
|
|
22
|
-
v-bind="
|
|
23
|
+
v-bind="column.cellComponentProps">
|
|
23
24
|
</component>
|
|
24
25
|
<span v-else>{{ displayValue }}</span>
|
|
25
26
|
</td>
|
|
@@ -30,7 +31,7 @@ import { KeypressHandlers, defaultKeypressHandlers, useKeyboardNav } from '@ston
|
|
|
30
31
|
import { computed, CSSProperties, inject, ref, useTemplateRef } from 'vue'
|
|
31
32
|
|
|
32
33
|
import TableDataStore from '.'
|
|
33
|
-
import type {
|
|
34
|
+
import type { CellContext } from '@/types'
|
|
34
35
|
|
|
35
36
|
const {
|
|
36
37
|
colIndex,
|
|
@@ -44,6 +45,7 @@ const {
|
|
|
44
45
|
tableid: string
|
|
45
46
|
addNavigation?: boolean | KeypressHandlers
|
|
46
47
|
tabIndex?: number
|
|
48
|
+
pinned?: boolean
|
|
47
49
|
}>()
|
|
48
50
|
|
|
49
51
|
const tableData = inject<TableDataStore>(tableid)
|
|
@@ -51,36 +53,37 @@ const cellRef = useTemplateRef<HTMLTableCellElement>('cell')
|
|
|
51
53
|
const currentData = ref('')
|
|
52
54
|
const cellModified = ref(false)
|
|
53
55
|
|
|
56
|
+
const table = tableData.table
|
|
57
|
+
const column = tableData.columns[colIndex]
|
|
58
|
+
const row = tableData.rows[rowIndex]
|
|
59
|
+
|
|
54
60
|
const displayValue = computed(() => {
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
} else {
|
|
70
|
-
return data
|
|
71
|
-
}
|
|
72
|
-
} else {
|
|
73
|
-
return data
|
|
61
|
+
const cellData = tableData.cellData<any>(colIndex, rowIndex)
|
|
62
|
+
const format = column.format
|
|
63
|
+
|
|
64
|
+
if (!format) {
|
|
65
|
+
return cellData
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (typeof format === 'function') {
|
|
69
|
+
return format(cellData, { table, row, column })
|
|
70
|
+
} else if (typeof format === 'string') {
|
|
71
|
+
// parse format function from string
|
|
72
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
73
|
+
const formatFn: (value: any, context?: CellContext) => string = Function(`"use strict";return (${format})`)()
|
|
74
|
+
return formatFn(cellData, { table, row, column })
|
|
74
75
|
}
|
|
76
|
+
|
|
77
|
+
return cellData
|
|
75
78
|
})
|
|
76
79
|
|
|
77
80
|
const handleInput = () => {
|
|
78
|
-
if (
|
|
81
|
+
if (column.mask) {
|
|
79
82
|
// TODO: add masking to cell values
|
|
80
|
-
//
|
|
83
|
+
// column.mask(event)
|
|
81
84
|
}
|
|
82
85
|
|
|
83
|
-
if (
|
|
86
|
+
if (column.modalComponent) {
|
|
84
87
|
const domRect = cellRef.value.getBoundingClientRect()
|
|
85
88
|
tableData.modal.visible = true
|
|
86
89
|
tableData.modal.colIndex = colIndex
|
|
@@ -89,8 +92,14 @@ const handleInput = () => {
|
|
|
89
92
|
tableData.modal.top = domRect.top + domRect.height
|
|
90
93
|
tableData.modal.left = domRect.left
|
|
91
94
|
tableData.modal.width = cellWidth.value
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
|
|
96
|
+
if (typeof column.modalComponent === 'function') {
|
|
97
|
+
tableData.modal.component = column.modalComponent({ table, row, column })
|
|
98
|
+
} else {
|
|
99
|
+
tableData.modal.component = column.modalComponent
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
tableData.modal.componentProps = column.modalComponentExtraProps
|
|
94
103
|
}
|
|
95
104
|
}
|
|
96
105
|
|
|
@@ -124,7 +133,7 @@ if (addNavigation) {
|
|
|
124
133
|
// const updateData = (event: Event) => {
|
|
125
134
|
// if (event) {
|
|
126
135
|
// // custom components need to handle their own updateData, this is the default
|
|
127
|
-
// if (!
|
|
136
|
+
// if (!column.component) {
|
|
128
137
|
// tableData.setCellData(rowIndex, colIndex, cell.value.innerHTML)
|
|
129
138
|
// }
|
|
130
139
|
// cellModified.value = true
|
|
@@ -132,11 +141,11 @@ if (addNavigation) {
|
|
|
132
141
|
// }
|
|
133
142
|
|
|
134
143
|
const textAlign = computed(() => {
|
|
135
|
-
return
|
|
144
|
+
return column.align || 'center'
|
|
136
145
|
})
|
|
137
146
|
|
|
138
147
|
const cellWidth = computed(() => {
|
|
139
|
-
return
|
|
148
|
+
return column.width || '40ch'
|
|
140
149
|
})
|
|
141
150
|
|
|
142
151
|
const onFocus = () => {
|
|
@@ -151,7 +160,7 @@ const onChange = () => {
|
|
|
151
160
|
currentData.value = cellRef.value.textContent
|
|
152
161
|
cellRef.value.dispatchEvent(new Event('change'))
|
|
153
162
|
cellModified.value = true // set display instead
|
|
154
|
-
if (!
|
|
163
|
+
if (!column.format) {
|
|
155
164
|
// TODO: need to setup reverse format function
|
|
156
165
|
tableData.setCellData(rowIndex, colIndex, currentData.value)
|
|
157
166
|
}
|
|
@@ -17,16 +17,13 @@ import { type KeypressHandlers, useKeyboardNav } from '@stonecrop/utilities'
|
|
|
17
17
|
import { computed, inject, useTemplateRef } from 'vue'
|
|
18
18
|
|
|
19
19
|
import TableDataStore from '.'
|
|
20
|
-
import type { TableRow } from '@/types'
|
|
21
20
|
|
|
22
21
|
const {
|
|
23
|
-
row,
|
|
24
22
|
rowIndex,
|
|
25
23
|
tableid,
|
|
26
24
|
tabIndex = -1,
|
|
27
25
|
addNavigation,
|
|
28
26
|
} = defineProps<{
|
|
29
|
-
row: TableRow
|
|
30
27
|
rowIndex: number
|
|
31
28
|
tableid: string
|
|
32
29
|
tabIndex?: number
|
package/src/components/ARow.vue
CHANGED
|
@@ -2,13 +2,18 @@
|
|
|
2
2
|
<tr ref="rowEl" :tabindex="tabIndex" v-show="isRowVisible" class="table-row">
|
|
3
3
|
<!-- render numbered/tree view index -->
|
|
4
4
|
<slot name="index">
|
|
5
|
-
<td
|
|
5
|
+
<td
|
|
6
|
+
v-if="tableData.config.view === 'list'"
|
|
7
|
+
:tabIndex="-1"
|
|
8
|
+
class="list-index"
|
|
9
|
+
:class="hasPinnedColumns ? 'sticky-index' : ''">
|
|
6
10
|
{{ rowIndex + 1 }}
|
|
7
11
|
</td>
|
|
8
12
|
<td
|
|
9
13
|
v-else-if="tableData.config.view === 'tree'"
|
|
10
14
|
:tabIndex="-1"
|
|
11
15
|
class="tree-index"
|
|
16
|
+
:class="hasPinnedColumns ? 'sticky-index' : ''"
|
|
12
17
|
@click="toggleRowExpand(rowIndex)">
|
|
13
18
|
{{ rowExpandSymbol }}
|
|
14
19
|
</td>
|
|
@@ -24,16 +29,13 @@ import { type KeypressHandlers, useKeyboardNav, defaultKeypressHandlers } from '
|
|
|
24
29
|
import { computed, inject, useTemplateRef } from 'vue'
|
|
25
30
|
|
|
26
31
|
import TableDataStore from '.'
|
|
27
|
-
import type { TableRow } from '@/types'
|
|
28
32
|
|
|
29
33
|
const {
|
|
30
|
-
row,
|
|
31
34
|
rowIndex,
|
|
32
35
|
tableid,
|
|
33
36
|
tabIndex = -1,
|
|
34
37
|
addNavigation = false, // default to allowing cell navigation
|
|
35
38
|
} = defineProps<{
|
|
36
|
-
row: TableRow
|
|
37
39
|
rowIndex: number
|
|
38
40
|
tableid: string
|
|
39
41
|
tabIndex?: number
|
|
@@ -43,6 +45,8 @@ const {
|
|
|
43
45
|
const tableData = inject<TableDataStore>(tableid)
|
|
44
46
|
const rowRef = useTemplateRef<HTMLTableRowElement>('rowEl')
|
|
45
47
|
|
|
48
|
+
const hasPinnedColumns = computed(() => tableData.columns.some(col => col.pinned))
|
|
49
|
+
|
|
46
50
|
const isRowVisible = computed(() => {
|
|
47
51
|
return tableData.config.view !== 'tree' || tableData.display[rowIndex].isRoot || tableData.display[rowIndex].open
|
|
48
52
|
})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<table
|
|
3
|
+
ref="table"
|
|
3
4
|
class="atable"
|
|
4
5
|
:style="{ width: tableData.config.fullWidth ? '100%' : 'auto' }"
|
|
5
6
|
v-on-click-outside="closeModal">
|
|
@@ -21,6 +22,7 @@
|
|
|
21
22
|
:tableid="tableData.id"
|
|
22
23
|
:col="col"
|
|
23
24
|
spellcheck="false"
|
|
25
|
+
:pinned="col.pinned"
|
|
24
26
|
:rowIndex="rowIndex"
|
|
25
27
|
:colIndex="colIndex + (tableData.zeroColumn ? 0 : -1)"
|
|
26
28
|
:component="col.cellComponent"
|
|
@@ -61,7 +63,7 @@
|
|
|
61
63
|
|
|
62
64
|
<script setup lang="ts">
|
|
63
65
|
import { vOnClickOutside } from '@vueuse/components'
|
|
64
|
-
import { nextTick, provide, watch } from 'vue'
|
|
66
|
+
import { nextTick, provide, watch, onMounted, useTemplateRef } from 'vue'
|
|
65
67
|
|
|
66
68
|
import TableDataStore from '.'
|
|
67
69
|
import ACell from '@/components/ACell.vue'
|
|
@@ -76,18 +78,17 @@ const {
|
|
|
76
78
|
columns,
|
|
77
79
|
rows = [],
|
|
78
80
|
config = new Object(),
|
|
79
|
-
tableid,
|
|
80
81
|
} = defineProps<{
|
|
81
82
|
id?: string
|
|
82
83
|
modelValue: TableRow[]
|
|
83
84
|
columns: TableColumn[]
|
|
84
85
|
rows?: TableRow[]
|
|
85
86
|
config?: TableConfig
|
|
86
|
-
tableid?: string
|
|
87
87
|
}>()
|
|
88
88
|
|
|
89
89
|
const emit = defineEmits(['update:modelValue'])
|
|
90
90
|
|
|
91
|
+
const tableRef = useTemplateRef<HTMLTableElement>('table')
|
|
91
92
|
const rowsValue = modelValue ? modelValue : rows
|
|
92
93
|
const tableData = new TableDataStore(id, columns, rowsValue, config)
|
|
93
94
|
provide(tableData.id, tableData)
|
|
@@ -100,6 +101,46 @@ watch(
|
|
|
100
101
|
{ deep: true }
|
|
101
102
|
)
|
|
102
103
|
|
|
104
|
+
onMounted(() => {
|
|
105
|
+
assignStickyCellWidths()
|
|
106
|
+
|
|
107
|
+
// in tree view, a mutation observer is needed to capture and adjust expanded rows
|
|
108
|
+
if (tableData.config.view === 'tree') {
|
|
109
|
+
const observer = new MutationObserver(() => assignStickyCellWidths())
|
|
110
|
+
observer.observe(tableRef.value, { childList: true, subtree: true })
|
|
111
|
+
}
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
const assignStickyCellWidths = () => {
|
|
115
|
+
const table = tableRef.value
|
|
116
|
+
|
|
117
|
+
// set header cell width to match sticky cells' width
|
|
118
|
+
const headerCells = Array.from(table.rows[0].cells)
|
|
119
|
+
for (const [index, headerCell] of headerCells.entries()) {
|
|
120
|
+
const rowCell = table.rows[1].cells[index]
|
|
121
|
+
headerCell.style.width = `${rowCell.offsetWidth}px`
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// pin cells in row that are sticky
|
|
125
|
+
for (const row of table.rows) {
|
|
126
|
+
let totalWidth = 0
|
|
127
|
+
const columns: HTMLTableCellElement[] = []
|
|
128
|
+
|
|
129
|
+
for (const column of row.cells) {
|
|
130
|
+
if (column.classList.contains('sticky-column') || column.classList.contains('sticky-index')) {
|
|
131
|
+
column.style.left = `${totalWidth}px`
|
|
132
|
+
totalWidth += column.offsetWidth
|
|
133
|
+
columns.push(column)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (columns.length > 0) {
|
|
138
|
+
const lastColumn = columns[columns.length - 1]
|
|
139
|
+
lastColumn.classList.add('sticky-column-edge')
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
103
144
|
// const formatCell = (event?: KeyboardEvent, column?: TableColumn, cellData?: any) => {
|
|
104
145
|
// let colIndex: number
|
|
105
146
|
// const target = event?.target as HTMLTableCellElement
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<thead id="resizable" v-if="columns.length">
|
|
3
3
|
<tr class="atable-header-row" tabindex="-1">
|
|
4
|
-
<th v-if="tableData.zeroColumn" id="header-index" />
|
|
5
|
-
<th
|
|
4
|
+
<th v-if="tableData.zeroColumn" id="header-index" :class="hasPinnedColumns ? 'sticky-index' : ''" />
|
|
5
|
+
<th
|
|
6
|
+
v-for="(column, colKey) in columns"
|
|
7
|
+
:key="column.name"
|
|
8
|
+
tabindex="-1"
|
|
9
|
+
:style="getHeaderCellStyle(column)"
|
|
10
|
+
:class="column.pinned ? 'sticky-column' : ''">
|
|
6
11
|
<slot>{{ column.label || String.fromCharCode(colKey + 97).toUpperCase() }}</slot>
|
|
7
12
|
</th>
|
|
8
13
|
</tr>
|
|
@@ -10,7 +15,7 @@
|
|
|
10
15
|
</template>
|
|
11
16
|
|
|
12
17
|
<script setup lang="ts">
|
|
13
|
-
import { CSSProperties, inject } from 'vue'
|
|
18
|
+
import { CSSProperties, inject, computed } from 'vue'
|
|
14
19
|
|
|
15
20
|
import TableDataStore from '.'
|
|
16
21
|
import type { TableColumn } from '@/types'
|
|
@@ -19,6 +24,8 @@ const { columns, tableid } = defineProps<{ columns: TableColumn[]; tableid?: str
|
|
|
19
24
|
|
|
20
25
|
const tableData = inject<TableDataStore>(tableid)
|
|
21
26
|
|
|
27
|
+
const hasPinnedColumns = computed(() => tableData.columns.some(col => col.pinned))
|
|
28
|
+
|
|
22
29
|
const getHeaderCellStyle = (column: TableColumn): CSSProperties => ({
|
|
23
30
|
minWidth: column.width || '40ch',
|
|
24
31
|
textAlign: column.align || 'center',
|
package/src/index.ts
CHANGED
|
@@ -7,7 +7,7 @@ import ATable from '@/components/ATable.vue'
|
|
|
7
7
|
import ATableHeader from '@/components/ATableHeader.vue'
|
|
8
8
|
import ATableModal from '@/components/ATableModal.vue'
|
|
9
9
|
import TableDataStore from './components'
|
|
10
|
-
export type {
|
|
10
|
+
export type { CellContext, TableColumn, TableConfig, TableDisplay, TableRow, TableModal } from '@/types'
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Install all ATable components
|
package/src/types/index.ts
CHANGED
|
@@ -8,17 +8,18 @@ export type TableColumn = {
|
|
|
8
8
|
label?: string
|
|
9
9
|
type?: string
|
|
10
10
|
width?: string
|
|
11
|
+
pinned?: boolean
|
|
11
12
|
|
|
12
13
|
cellComponent?: string
|
|
13
14
|
cellComponentProps?: Record<string, any>
|
|
14
|
-
modalComponent?: string
|
|
15
|
-
|
|
15
|
+
modalComponent?: string | ((context?: CellContext) => string)
|
|
16
|
+
modalComponentExtraProps?: Record<string, any>
|
|
16
17
|
|
|
17
|
-
format?: string | ((value: any, context?:
|
|
18
|
+
format?: string | ((value: any, context?: CellContext) => string)
|
|
18
19
|
mask?: (value: any) => any
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
export type
|
|
22
|
+
export type CellContext = {
|
|
22
23
|
row: TableRow
|
|
23
24
|
column: TableColumn
|
|
24
25
|
table: TableDataStore['table']
|