@stonecrop/atable 0.2.39 → 0.2.41
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.js +704 -547
- 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/types/index.d.ts +1 -0
- 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 +20 -17
- package/src/components/AExpansionRow.vue +0 -3
- package/src/components/ARow.vue +8 -4
- package/src/components/ATable.vue +50 -3
- package/src/components/ATableHeader.vue +10 -3
- package/src/types/index.ts +1 -0
|
@@ -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.41",
|
|
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/utilities": "0.2.
|
|
39
|
-
"@stonecrop/themes": "0.2.
|
|
38
|
+
"@stonecrop/utilities": "0.2.41",
|
|
39
|
+
"@stonecrop/themes": "0.2.41"
|
|
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>
|
|
@@ -27,6 +28,7 @@
|
|
|
27
28
|
|
|
28
29
|
<script setup lang="ts">
|
|
29
30
|
import { KeypressHandlers, defaultKeypressHandlers, useKeyboardNav } from '@stonecrop/utilities'
|
|
31
|
+
import { useElementBounding } from '@vueuse/core'
|
|
30
32
|
import { computed, CSSProperties, inject, ref, useTemplateRef } from 'vue'
|
|
31
33
|
|
|
32
34
|
import TableDataStore from '.'
|
|
@@ -44,6 +46,7 @@ const {
|
|
|
44
46
|
tableid: string
|
|
45
47
|
addNavigation?: boolean | KeypressHandlers
|
|
46
48
|
tabIndex?: number
|
|
49
|
+
pinned?: boolean
|
|
47
50
|
}>()
|
|
48
51
|
|
|
49
52
|
const tableData = inject<TableDataStore>(tableid)
|
|
@@ -55,6 +58,14 @@ const table = tableData.table
|
|
|
55
58
|
const column = tableData.columns[colIndex]
|
|
56
59
|
const row = tableData.rows[rowIndex]
|
|
57
60
|
|
|
61
|
+
const textAlign = computed(() => {
|
|
62
|
+
return column.align || 'center'
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const cellWidth = computed(() => {
|
|
66
|
+
return column.width || '40ch'
|
|
67
|
+
})
|
|
68
|
+
|
|
58
69
|
const displayValue = computed(() => {
|
|
59
70
|
const cellData = tableData.cellData<any>(colIndex, rowIndex)
|
|
60
71
|
const format = column.format
|
|
@@ -82,13 +93,13 @@ const handleInput = () => {
|
|
|
82
93
|
}
|
|
83
94
|
|
|
84
95
|
if (column.modalComponent) {
|
|
85
|
-
const
|
|
96
|
+
const { top, left, height } = useElementBounding(cellRef)
|
|
86
97
|
tableData.modal.visible = true
|
|
87
98
|
tableData.modal.colIndex = colIndex
|
|
88
99
|
tableData.modal.rowIndex = rowIndex
|
|
89
100
|
tableData.modal.parent = cellRef.value
|
|
90
|
-
tableData.modal.top =
|
|
91
|
-
tableData.modal.left =
|
|
101
|
+
tableData.modal.top = top.value + height.value
|
|
102
|
+
tableData.modal.left = left.value
|
|
92
103
|
tableData.modal.width = cellWidth.value
|
|
93
104
|
|
|
94
105
|
if (typeof column.modalComponent === 'function') {
|
|
@@ -138,14 +149,6 @@ if (addNavigation) {
|
|
|
138
149
|
// }
|
|
139
150
|
// }
|
|
140
151
|
|
|
141
|
-
const textAlign = computed(() => {
|
|
142
|
-
return column.align || 'center'
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
const cellWidth = computed(() => {
|
|
146
|
-
return column.width || '40ch'
|
|
147
|
-
})
|
|
148
|
-
|
|
149
152
|
const onFocus = () => {
|
|
150
153
|
if (cellRef.value) {
|
|
151
154
|
currentData.value = cellRef.value.textContent
|
|
@@ -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,8 @@
|
|
|
61
63
|
|
|
62
64
|
<script setup lang="ts">
|
|
63
65
|
import { vOnClickOutside } from '@vueuse/components'
|
|
64
|
-
import {
|
|
66
|
+
import { useMutationObserver } from '@vueuse/core'
|
|
67
|
+
import { nextTick, provide, watch, onMounted, useTemplateRef } from 'vue'
|
|
65
68
|
|
|
66
69
|
import TableDataStore from '.'
|
|
67
70
|
import ACell from '@/components/ACell.vue'
|
|
@@ -76,18 +79,17 @@ const {
|
|
|
76
79
|
columns,
|
|
77
80
|
rows = [],
|
|
78
81
|
config = new Object(),
|
|
79
|
-
tableid,
|
|
80
82
|
} = defineProps<{
|
|
81
83
|
id?: string
|
|
82
84
|
modelValue: TableRow[]
|
|
83
85
|
columns: TableColumn[]
|
|
84
86
|
rows?: TableRow[]
|
|
85
87
|
config?: TableConfig
|
|
86
|
-
tableid?: string
|
|
87
88
|
}>()
|
|
88
89
|
|
|
89
90
|
const emit = defineEmits(['update:modelValue'])
|
|
90
91
|
|
|
92
|
+
const tableRef = useTemplateRef<HTMLTableElement>('table')
|
|
91
93
|
const rowsValue = modelValue ? modelValue : rows
|
|
92
94
|
const tableData = new TableDataStore(id, columns, rowsValue, config)
|
|
93
95
|
provide(tableData.id, tableData)
|
|
@@ -100,6 +102,51 @@ watch(
|
|
|
100
102
|
{ deep: true }
|
|
101
103
|
)
|
|
102
104
|
|
|
105
|
+
onMounted(() => {
|
|
106
|
+
if (columns.some(col => col.pinned)) {
|
|
107
|
+
assignStickyCellWidths()
|
|
108
|
+
|
|
109
|
+
// in tree view, also add a mutation observer to capture and adjust expanded rows
|
|
110
|
+
if (tableData.config.view === 'tree') {
|
|
111
|
+
useMutationObserver(tableRef, assignStickyCellWidths, { childList: true, subtree: true })
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
const assignStickyCellWidths = () => {
|
|
117
|
+
const table = tableRef.value
|
|
118
|
+
|
|
119
|
+
// set header cell width to match sticky cells' width
|
|
120
|
+
const headerRow = table.rows[0]
|
|
121
|
+
const firstDataRow = table.rows[1]
|
|
122
|
+
const headerCells = headerRow ? Array.from(headerRow.cells) : []
|
|
123
|
+
for (const [index, headerCell] of headerCells.entries()) {
|
|
124
|
+
const rowCell = firstDataRow.cells[index]
|
|
125
|
+
if (rowCell) {
|
|
126
|
+
headerCell.style.width = `${rowCell.offsetWidth}px`
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// pin cells in row that are sticky
|
|
131
|
+
for (const row of table.rows) {
|
|
132
|
+
let totalWidth = 0
|
|
133
|
+
const columns: HTMLTableCellElement[] = []
|
|
134
|
+
|
|
135
|
+
for (const column of row.cells) {
|
|
136
|
+
if (column.classList.contains('sticky-column') || column.classList.contains('sticky-index')) {
|
|
137
|
+
column.style.left = `${totalWidth}px`
|
|
138
|
+
totalWidth += column.offsetWidth
|
|
139
|
+
columns.push(column)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (columns.length > 0) {
|
|
144
|
+
const lastColumn = columns[columns.length - 1]
|
|
145
|
+
lastColumn.classList.add('sticky-column-edge')
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
103
150
|
// const formatCell = (event?: KeyboardEvent, column?: TableColumn, cellData?: any) => {
|
|
104
151
|
// let colIndex: number
|
|
105
152
|
// 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',
|