@ulu/frontend-vue 0.1.0-beta.1
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/LICENSE +21 -0
- package/README.md +9 -0
- package/dist/breakpoints-ClT9bfZm.js +211 -0
- package/dist/frontend-vue.css +1 -0
- package/dist/frontend-vue.js +82 -0
- package/dist/frontend-vue.umd.cjs +561 -0
- package/dist/index-P5Rwl_Dl.js +7263 -0
- package/dist/index.es-HlG3u0J5.js +3134 -0
- package/lib/_index.scss +14 -0
- package/lib/components/_index.scss +6 -0
- package/lib/components/collapsible/UluAccordion.vue +82 -0
- package/lib/components/collapsible/UluCollapsibleRegion.vue +278 -0
- package/lib/components/collapsible/UluDropdown.vue +42 -0
- package/lib/components/collapsible/UluModal.vue +384 -0
- package/lib/components/collapsible/UluOverflowPopover.vue +52 -0
- package/lib/components/collapsible/UluTab.vue +9 -0
- package/lib/components/collapsible/UluTabGroup.vue +31 -0
- package/lib/components/collapsible/UluTabList.vue +9 -0
- package/lib/components/collapsible/UluTabPanel.vue +9 -0
- package/lib/components/collapsible/UluTabPanels.vue +9 -0
- package/lib/components/elements/UluAlert.vue +81 -0
- package/lib/components/elements/UluBadge.vue +58 -0
- package/lib/components/elements/UluBadgeStack.vue +27 -0
- package/lib/components/elements/UluButton.vue +161 -0
- package/lib/components/elements/UluCallout.vue +30 -0
- package/lib/components/elements/UluCard.vue +241 -0
- package/lib/components/elements/UluDefinitionList.vue +40 -0
- package/lib/components/elements/UluExternalLink.vue +47 -0
- package/lib/components/elements/UluIcon.vue +108 -0
- package/lib/components/elements/UluList.vue +87 -0
- package/lib/components/elements/UluMain.vue +5 -0
- package/lib/components/elements/UluSpokeSpinner.vue +25 -0
- package/lib/components/elements/UluTag.vue +53 -0
- package/lib/components/forms/UluCheckboxMenu.vue +36 -0
- package/lib/components/forms/UluFileDisplay.vue +39 -0
- package/lib/components/forms/UluFormDropzone.vue +62 -0
- package/lib/components/forms/UluFormFile.vue +47 -0
- package/lib/components/forms/UluFormMessage.vue +20 -0
- package/lib/components/forms/UluFormSelect.vue +37 -0
- package/lib/components/forms/UluFormText.vue +32 -0
- package/lib/components/forms/UluSearchForm.vue +31 -0
- package/lib/components/index.js +54 -0
- package/lib/components/layout/UluAdaptiveLayout.vue +11 -0
- package/lib/components/layout/UluDataGrid.vue +41 -0
- package/lib/components/layout/UluTitleRail.vue +56 -0
- package/lib/components/layout/UluWhenBreakpoint.vue +86 -0
- package/lib/components/navigation/UluBreadcrumb.vue +72 -0
- package/lib/components/navigation/UluMenu.vue +105 -0
- package/lib/components/navigation/UluMenuStack.vue +49 -0
- package/lib/components/navigation/UluNavStrip.vue +48 -0
- package/lib/components/navigation/UluSkipLink.vue +5 -0
- package/lib/components/systems/facets/UluFacets.vue +380 -0
- package/lib/components/systems/facets/UluFacetsList.vue +39 -0
- package/lib/components/systems/facets/UluFacetsSearch.vue +67 -0
- package/lib/components/systems/facets/_facets.scss +64 -0
- package/lib/components/systems/index.js +17 -0
- package/lib/components/systems/scroll-anchors/UluScrollAnchors.vue +152 -0
- package/lib/components/systems/scroll-anchors/UluScrollAnchorsNav.vue +37 -0
- package/lib/components/systems/scroll-anchors/UluScrollAnchorsNavAnimated.vue +124 -0
- package/lib/components/systems/scroll-anchors/UluScrollAnchorsSection.vue +63 -0
- package/lib/components/systems/scroll-anchors/symbols.js +6 -0
- package/lib/components/systems/skeleton/UluShowSkeleton.vue +13 -0
- package/lib/components/systems/skeleton/UluSkeletonContent.vue +60 -0
- package/lib/components/systems/skeleton/UluSkeletonMedia.vue +11 -0
- package/lib/components/systems/skeleton/UluSkeletonTextInline.vue +9 -0
- package/lib/components/systems/slider/UluImageSlideShow.vue +75 -0
- package/lib/components/systems/slider/UluSlideShow.vue +331 -0
- package/lib/components/systems/slider/UluSlideShowSlide.vue +25 -0
- package/lib/components/systems/table-sticky/UluTableSticky.vue +793 -0
- package/lib/components/systems/table-sticky/UluTableStickyRows.vue +73 -0
- package/lib/components/systems/table-sticky/UluTableStickyTable.vue +237 -0
- package/lib/components/systems/table-sticky/_table-sticky.scss +185 -0
- package/lib/components/utils/UluCondText.vue +28 -0
- package/lib/components/utils/UluEmpty.vue +3 -0
- package/lib/components/utils/UluEmptyView.vue +3 -0
- package/lib/components/utils/UluPlaceholderImage.vue +53 -0
- package/lib/components/utils/UluPlaceholderText.vue +25 -0
- package/lib/components/utils/UluRouteAnnouncer.vue +83 -0
- package/lib/components/visualizations/UluAnimateNumber.vue +32 -0
- package/lib/components/visualizations/UluProgressBar.vue +94 -0
- package/lib/components/visualizations/UluProgressDonut.vue +97 -0
- package/lib/composables/index.js +10 -0
- package/lib/composables/useBreakpointManager.js +68 -0
- package/lib/composables/useIcon.js +62 -0
- package/lib/composables/useModifiers.js +93 -0
- package/lib/composables/useWindowResize.js +64 -0
- package/lib/index.js +10 -0
- package/lib/plugins/_index.scss +7 -0
- package/lib/plugins/breakpoints/index.js +47 -0
- package/lib/plugins/index.js +11 -0
- package/lib/plugins/modals/UluModalsDisplay.vue +59 -0
- package/lib/plugins/modals/api.js +76 -0
- package/lib/plugins/modals/index.js +60 -0
- package/lib/plugins/modals/useModals.js +9 -0
- package/lib/plugins/popovers/UluPopover.vue +189 -0
- package/lib/plugins/popovers/UluTooltipDisplay.vue +15 -0
- package/lib/plugins/popovers/UluTooltipPopover.vue +83 -0
- package/lib/plugins/popovers/defaults.js +108 -0
- package/lib/plugins/popovers/directive.js +95 -0
- package/lib/plugins/popovers/index.js +18 -0
- package/lib/plugins/popovers/manager.js +54 -0
- package/lib/plugins/popovers/useFollow.js +80 -0
- package/lib/plugins/popovers/utils.js +5 -0
- package/lib/plugins/toast/UluToast.vue +87 -0
- package/lib/plugins/toast/UluToastDisplay.vue +35 -0
- package/lib/plugins/toast/_toast.scss +198 -0
- package/lib/plugins/toast/defaults.js +30 -0
- package/lib/plugins/toast/index.js +17 -0
- package/lib/plugins/toast/store.js +71 -0
- package/lib/plugins/toast/useToast.js +18 -0
- package/lib/settings.js +119 -0
- package/lib/utils/dom.js +14 -0
- package/lib/utils/placeholder.js +6 -0
- package/lib/utils/vue-router.js +219 -0
- package/package.json +75 -0
|
@@ -0,0 +1,793 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Version: 2.0.6
|
|
3
|
+
|
|
4
|
+
Updates:
|
|
5
|
+
- 2.0.6 Moved to ulu library and refactored, styles moved to frontend
|
|
6
|
+
- 2.0.5 Updated for SSR in vue
|
|
7
|
+
- 2.0.4 Add sorting, Removed all component options (in table values/titles)
|
|
8
|
+
- Added ResizeObserver incase cells change width (inner component changes, interactive, etc)
|
|
9
|
+
- Only on table actual header for now (performance)
|
|
10
|
+
- Added slotHeader for templating columns
|
|
11
|
+
- Added deep watchers for columns and rows props, so we can update is they are altered
|
|
12
|
+
- Added the ability to use a custom component inside any table cell column.component and column.componentHeader
|
|
13
|
+
- Added ability to pass classes object for adding classes to certain inner elements (table, controls, controlButton)
|
|
14
|
+
- Add ability to pass "controlsComponent" to use custom component for controls
|
|
15
|
+
- Add resolveClasses so that all classes can be functions (useful for things like rows that don't have configuration like column cells)
|
|
16
|
+
Todo:
|
|
17
|
+
- Should probably use global counters for id creation so they are renewed with new id's every time the table re-renders. Not sure
|
|
18
|
+
how this would behave with a screen reader but either way the id's have changed (they won't line up to preexisting id's, guess that
|
|
19
|
+
could be done but would be complicated), I think using an id that never exists will make sure all the connections works ie attr. headers
|
|
20
|
+
- In the future the API for the data should be clearer (ie. row.data is sometimes row to the user), feels weird
|
|
21
|
+
Warning:
|
|
22
|
+
- This version has the vis
|
|
23
|
+
-->
|
|
24
|
+
<template>
|
|
25
|
+
<div class="table-sticky" :class="{
|
|
26
|
+
'table-sticky--overflown-x' : overflownX,
|
|
27
|
+
'table-sticky--can-scroll-right' : canScrollRight,
|
|
28
|
+
'table-sticky--can-scroll-left' : canScrollLeft
|
|
29
|
+
}">
|
|
30
|
+
<div class="table-sticky__sticky-wrap table-sticky__sticky-wrap--header">
|
|
31
|
+
<div class="table-sticky__header-wrap">
|
|
32
|
+
<UluTableStickyTable
|
|
33
|
+
ref="header"
|
|
34
|
+
class="table-sticky__table table-sticky__table--header"
|
|
35
|
+
:classes="classes"
|
|
36
|
+
:caption="caption"
|
|
37
|
+
:resolveClasses="resolveClasses"
|
|
38
|
+
:getColumnTitle="getColumnTitle"
|
|
39
|
+
:idPrefix="idPrefix"
|
|
40
|
+
:headerRows="headerRows"
|
|
41
|
+
:style="{
|
|
42
|
+
opacity: sizesCalculated ? '1' : '0',
|
|
43
|
+
pointerEvents: sizesCalculated ? 'auto' : 'none',
|
|
44
|
+
width: tableWidth
|
|
45
|
+
}"
|
|
46
|
+
@columnSorted="applySort"
|
|
47
|
+
>
|
|
48
|
+
<template v-for="(_, name) in $slots" v-slot:[name]="slotData">
|
|
49
|
+
<slot :name="name" v-bind="slotData" />
|
|
50
|
+
</template>
|
|
51
|
+
</UluTableStickyTable>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
<div class="table-sticky__sticky-wrap table-sticky__sticky-wrap--first-column-header">
|
|
55
|
+
<UluTableStickyTable
|
|
56
|
+
v-if="firstColumnSticky"
|
|
57
|
+
ref="firstColumnHeader"
|
|
58
|
+
class="table-sticky__table table-sticky__table--first-column-header"
|
|
59
|
+
:classes="classes"
|
|
60
|
+
:caption="caption"
|
|
61
|
+
:resolveClasses="resolveClasses"
|
|
62
|
+
:getColumnTitle="getColumnTitle"
|
|
63
|
+
:idPrefix="idPrefix"
|
|
64
|
+
:headerRows="headerRowsFirst"
|
|
65
|
+
:style="{
|
|
66
|
+
opacity: headerOpacityX,
|
|
67
|
+
pointerEvents: headerVisibleX ? 'auto' : 'none'
|
|
68
|
+
}"
|
|
69
|
+
@columnSorted="applySort"
|
|
70
|
+
>
|
|
71
|
+
<template v-for="(_, name) in $slots" v-slot:[name]="slotData">
|
|
72
|
+
<slot :name="name" v-bind="slotData" />
|
|
73
|
+
</template>
|
|
74
|
+
</UluTableStickyTable>
|
|
75
|
+
</div>
|
|
76
|
+
<div class="table-sticky__sticky-wrap table-sticky__sticky-wrap--controls">
|
|
77
|
+
<div
|
|
78
|
+
class="table-sticky__controls"
|
|
79
|
+
:class="resolveClasses(classes.controls)"
|
|
80
|
+
ref="controls"
|
|
81
|
+
v-show="controlsShown"
|
|
82
|
+
>
|
|
83
|
+
<slot
|
|
84
|
+
v-if="$slots.controls"
|
|
85
|
+
name="controls"
|
|
86
|
+
:scrollLeft="scrollLeft"
|
|
87
|
+
:scrollRight="scrollRight"
|
|
88
|
+
:canScrollLeft="canScrollLeft"
|
|
89
|
+
:canScrollRight="canScrollRight"
|
|
90
|
+
/>
|
|
91
|
+
<component
|
|
92
|
+
v-else-if="controlsComponent"
|
|
93
|
+
:is="controlsComponent"
|
|
94
|
+
:scrollLeft="scrollLeft"
|
|
95
|
+
:scrollRight="scrollRight"
|
|
96
|
+
:canScrollLeft="canScrollLeft"
|
|
97
|
+
:canScrollRight="canScrollRight"
|
|
98
|
+
/>
|
|
99
|
+
<div v-else class="table-sticky__controls-inner">
|
|
100
|
+
<button
|
|
101
|
+
class="table-sticky__control table-sticky__control--left"
|
|
102
|
+
aria-label="Scroll Left"
|
|
103
|
+
@click="scrollLeft"
|
|
104
|
+
:class="resolveClasses(classes.controlButton)"
|
|
105
|
+
:disabled="!canScrollLeft"
|
|
106
|
+
>
|
|
107
|
+
<slot name="controlLeft">
|
|
108
|
+
←
|
|
109
|
+
</slot>
|
|
110
|
+
</button>
|
|
111
|
+
<button
|
|
112
|
+
class="table-sticky__control table-sticky__control--right"
|
|
113
|
+
aria-label="Scroll Right"
|
|
114
|
+
@click="scrollRight"
|
|
115
|
+
:class="resolveClasses(classes.controlButton)"
|
|
116
|
+
:disabled="!canScrollRight"
|
|
117
|
+
>
|
|
118
|
+
<slot name="controlRight">
|
|
119
|
+
→
|
|
120
|
+
</slot>
|
|
121
|
+
</button>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
<div ref="display" class="table-sticky__display">
|
|
126
|
+
<UluTableStickyTable
|
|
127
|
+
ref="table"
|
|
128
|
+
class="table-sticky__table table-sticky__table--actual"
|
|
129
|
+
:classes="classes"
|
|
130
|
+
:resolveClasses="resolveClasses"
|
|
131
|
+
isActual
|
|
132
|
+
:headerRows="headerRows"
|
|
133
|
+
:rows="currentRows"
|
|
134
|
+
:footerRows="currentFooterRows"
|
|
135
|
+
:rowColumns="rowColumns"
|
|
136
|
+
:caption="caption"
|
|
137
|
+
:idPrefix="idPrefix"
|
|
138
|
+
:getRowValue="getRowValue"
|
|
139
|
+
:getColumnTitle="getColumnTitle"
|
|
140
|
+
@vue:mounted="tableReady"
|
|
141
|
+
@actualHeaderRemoved="headerRemoved"
|
|
142
|
+
@actualHeaderAdded="headerAdded"
|
|
143
|
+
@columnSorted="applySort"
|
|
144
|
+
>
|
|
145
|
+
<template v-for="(_, name) in $slots" v-slot:[name]="slotData">
|
|
146
|
+
<slot :name="name" v-bind="slotData" />
|
|
147
|
+
</template>
|
|
148
|
+
</UluTableStickyTable>
|
|
149
|
+
<!-- Scroll Controls (optionally allow user templating via slot passed methods) -->
|
|
150
|
+
</div>
|
|
151
|
+
<UluTableStickyTable
|
|
152
|
+
v-if="firstColumnSticky"
|
|
153
|
+
ref="firstColumn"
|
|
154
|
+
class="table-sticky__table table-sticky__table--first-column"
|
|
155
|
+
:classes="classes"
|
|
156
|
+
:resolveClasses="resolveClasses"
|
|
157
|
+
:caption="caption"
|
|
158
|
+
:headerRows="headerRowsFirst"
|
|
159
|
+
:columnWidth="firstColumnSize.width"
|
|
160
|
+
:rows="currentRows"
|
|
161
|
+
:footerRows="currentFooterRows"
|
|
162
|
+
:rowColumns="rowColumnsFirst"
|
|
163
|
+
:idPrefix="idPrefix"
|
|
164
|
+
:getRowValue="getRowValue"
|
|
165
|
+
:getColumnTitle="getColumnTitle"
|
|
166
|
+
:style="{
|
|
167
|
+
opacity: headerOpacityX,
|
|
168
|
+
pointerEvents: headerVisibleX ? 'auto' : 'none'
|
|
169
|
+
}"
|
|
170
|
+
@columnSorted="applySort"
|
|
171
|
+
>
|
|
172
|
+
<template v-for="(_, name) in $slots" v-slot:[name]="slotData">
|
|
173
|
+
<slot :name="name" v-bind="slotData" />
|
|
174
|
+
</template>
|
|
175
|
+
</UluTableStickyTable>
|
|
176
|
+
</div>
|
|
177
|
+
</template>
|
|
178
|
+
|
|
179
|
+
<script>
|
|
180
|
+
import UluTableStickyTable from "./UluTableStickyTable.vue";
|
|
181
|
+
import { debounce } from "@ulu/utils/performance.js";
|
|
182
|
+
import { runAfterFramePaint } from "@ulu/utils/browser/performance.js";
|
|
183
|
+
import cloneDeep from "lodash/cloneDeep";
|
|
184
|
+
|
|
185
|
+
const arrayOfObjects = a => a.every(o => typeof o === "object");
|
|
186
|
+
const required = true;
|
|
187
|
+
const SSR = import.meta.env.SSR;
|
|
188
|
+
const getWindowWidth = () => SSR ? 0 : window.innerWidth;
|
|
189
|
+
// Used to make sure resize events on ios (when address bar collapses)
|
|
190
|
+
// don't trigger recalculations
|
|
191
|
+
let windowWidth = getWindowWidth();
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Sticky table component
|
|
195
|
+
* - Requires separate stylesheet (included in @ulu/frontend)
|
|
196
|
+
* - Requires lodash/cloneDeep
|
|
197
|
+
*/
|
|
198
|
+
export default {
|
|
199
|
+
name: "UluTableSticky",
|
|
200
|
+
components: {
|
|
201
|
+
UluTableStickyTable
|
|
202
|
+
},
|
|
203
|
+
props: {
|
|
204
|
+
/**
|
|
205
|
+
* By default you cannot have interactive items in the cloned sticky header and first column (if set)
|
|
206
|
+
* this disables that feature. It was set up that way for accessibility
|
|
207
|
+
*/
|
|
208
|
+
allowClickClones: Boolean,
|
|
209
|
+
/**
|
|
210
|
+
* Allows user to pass classes object to add custom classes to parts of the component
|
|
211
|
+
*/
|
|
212
|
+
classes: {
|
|
213
|
+
type: Object,
|
|
214
|
+
default: () => ({})
|
|
215
|
+
},
|
|
216
|
+
/**
|
|
217
|
+
* Allow user to pass components
|
|
218
|
+
* - Passed the same values as if using a slot
|
|
219
|
+
* -
|
|
220
|
+
*/
|
|
221
|
+
controlsComponent: Object,
|
|
222
|
+
/**
|
|
223
|
+
* Allows user to pass callback to get the row's value
|
|
224
|
+
*/
|
|
225
|
+
getRowValue: Function,
|
|
226
|
+
getColumnTitle: Function,
|
|
227
|
+
/**
|
|
228
|
+
* Hidden caption for accessibility
|
|
229
|
+
*/
|
|
230
|
+
caption: {
|
|
231
|
+
type: String,
|
|
232
|
+
required
|
|
233
|
+
},
|
|
234
|
+
/**
|
|
235
|
+
* Array of column configurations to convert to list output
|
|
236
|
+
*
|
|
237
|
+
* @property {Object} column A column config
|
|
238
|
+
* @property {String|Boolean} column.title The title to output for the column if set to a falsey value nothing will print
|
|
239
|
+
* @property {Array} column.columns Array of child columns
|
|
240
|
+
* @property {String} column.key The key that should be usec to grab column's value from rows
|
|
241
|
+
* @property {Function} column.value A function that returns the column's value used instead of key passed (row, column)
|
|
242
|
+
* @property {String} column.slot Register custom slot name to use as a template for this column. Passing a slot with this name will link them. The slot are passed the ({row, column}). Note this will disable output of the column's value
|
|
243
|
+
* @property {String} column.component Pass a component to use for this columns values (<td>)
|
|
244
|
+
* @property {String} column.componentHeader Pass a component to use for this columns header (<th>)
|
|
245
|
+
* @property {String} column.slotHeader Register custom slot name to use in the header
|
|
246
|
+
* @property {String} column.classHeader Custom class(s) to be set to column <th>
|
|
247
|
+
* @property {String} column.class Custom class(s) to be set to column's value <td>
|
|
248
|
+
* @property {String} column.html Use v-html output for value
|
|
249
|
+
* @property {String} column.rowHeader When this column is printed in the <tbody> it should be a header for the row. Note supports multiple row headers from left to right only. No rowspan support for rowHeaders.
|
|
250
|
+
*/
|
|
251
|
+
columns: {
|
|
252
|
+
type: Array,
|
|
253
|
+
validator: arrayOfObjects,
|
|
254
|
+
required
|
|
255
|
+
},
|
|
256
|
+
/**
|
|
257
|
+
* Whether the first column of the table should be sticky
|
|
258
|
+
* - Requires that the table's first column header is nested
|
|
259
|
+
*/
|
|
260
|
+
firstColumnSticky: Boolean,
|
|
261
|
+
/**
|
|
262
|
+
* Prefixed used for id generation
|
|
263
|
+
*/
|
|
264
|
+
idPrefix: {
|
|
265
|
+
type: String,
|
|
266
|
+
default: "DT"
|
|
267
|
+
},
|
|
268
|
+
/**
|
|
269
|
+
* Array of tables rows (tbody)
|
|
270
|
+
* - Each row is an object who's value will matched to columns
|
|
271
|
+
*/
|
|
272
|
+
rows: {
|
|
273
|
+
type: Array,
|
|
274
|
+
validator: arrayOfObjects,
|
|
275
|
+
// required
|
|
276
|
+
},
|
|
277
|
+
/**
|
|
278
|
+
* Array of rows for footer (tfoot)
|
|
279
|
+
*/
|
|
280
|
+
footerRows: {
|
|
281
|
+
type: Array,
|
|
282
|
+
validator: arrayOfObjects,
|
|
283
|
+
},
|
|
284
|
+
/**
|
|
285
|
+
* Enables the visibility of the scroll controls
|
|
286
|
+
* - Scroll controls shift the tables x-axis when the table has overflow-x
|
|
287
|
+
* - Can be templated manually using slot named "controlsButtons", slot needs to create layout and call methods
|
|
288
|
+
* + scope = { scrollLeft, scrollRight, canScrollLeft, canScrollRight }
|
|
289
|
+
* - Scroll controls are transformed with the header (move down as the user scrolls)
|
|
290
|
+
*/
|
|
291
|
+
scrollControls: Boolean,
|
|
292
|
+
/**
|
|
293
|
+
* Scrollable context DOM Element, if the sticky element is within another
|
|
294
|
+
* scrolling parent use this to change the scroll activation handler to use a custom
|
|
295
|
+
* scrollable parent element
|
|
296
|
+
*
|
|
297
|
+
*/
|
|
298
|
+
scrollContext: {
|
|
299
|
+
default: () => SSR ? null : window
|
|
300
|
+
},
|
|
301
|
+
/**
|
|
302
|
+
* Amount to scroll when user uses scroll controls (pixels)
|
|
303
|
+
*/
|
|
304
|
+
scrollControlAmount: {
|
|
305
|
+
type: Number,
|
|
306
|
+
default: 100
|
|
307
|
+
}
|
|
308
|
+
},
|
|
309
|
+
data() {
|
|
310
|
+
const currentColumns = this.createColumns();
|
|
311
|
+
return {
|
|
312
|
+
currentColumns,
|
|
313
|
+
currentRows: this.createRows(),
|
|
314
|
+
currentFooterRows: this.createRows(true),
|
|
315
|
+
headerRows: this.createHeaderRows(currentColumns),
|
|
316
|
+
sizesCalculated: false,
|
|
317
|
+
tableWidth: "auto",
|
|
318
|
+
resizeHandler: debounce(this.onResize.bind(this), 500, true),
|
|
319
|
+
resizing: false,
|
|
320
|
+
overflownX: false,
|
|
321
|
+
canScrollLeft: false,
|
|
322
|
+
canScrollRight: false,
|
|
323
|
+
displayY: null,
|
|
324
|
+
sizesPainted: false,
|
|
325
|
+
columnResizeObserver: SSR ? null : new ResizeObserver(() => this.onColumnResize())
|
|
326
|
+
};
|
|
327
|
+
},
|
|
328
|
+
watch: {
|
|
329
|
+
columns: {
|
|
330
|
+
handler() {
|
|
331
|
+
this.currentColumns = this.createColumns();
|
|
332
|
+
this.headerRows = this.createHeaderRows(this.currentColumns);
|
|
333
|
+
this.refresh();
|
|
334
|
+
},
|
|
335
|
+
deep: true
|
|
336
|
+
},
|
|
337
|
+
rows: {
|
|
338
|
+
handler() {
|
|
339
|
+
this.currentRows = this.createRows();
|
|
340
|
+
this.refresh();
|
|
341
|
+
},
|
|
342
|
+
deep: true
|
|
343
|
+
},
|
|
344
|
+
footerRows: {
|
|
345
|
+
handler() {
|
|
346
|
+
this.currentFooterRows = this.createRows(true);
|
|
347
|
+
this.refresh();
|
|
348
|
+
},
|
|
349
|
+
deep: true
|
|
350
|
+
}
|
|
351
|
+
},
|
|
352
|
+
computed: {
|
|
353
|
+
controlsShown() {
|
|
354
|
+
return this.scrollControls && this.overflownX;
|
|
355
|
+
},
|
|
356
|
+
headerVisibleX() {
|
|
357
|
+
return this.sizesCalculated && this.overflownX;
|
|
358
|
+
},
|
|
359
|
+
headerOpacityX() {
|
|
360
|
+
// Only false (0) when translate is 0
|
|
361
|
+
return this.headerVisibleX ? "1" : "0";
|
|
362
|
+
},
|
|
363
|
+
/**
|
|
364
|
+
* Used to output the body rows. This is an array of only the deepest child columns
|
|
365
|
+
* parent column information can be accessed by reference
|
|
366
|
+
*/
|
|
367
|
+
rowColumns() {
|
|
368
|
+
const columns = this.currentColumns;
|
|
369
|
+
const rowColumns = [];
|
|
370
|
+
const add = c => {
|
|
371
|
+
if (c.columns) c.columns.forEach(add);
|
|
372
|
+
else rowColumns.push(c);
|
|
373
|
+
};
|
|
374
|
+
// Create array of actual
|
|
375
|
+
columns.forEach(add);
|
|
376
|
+
// Iterate over all columns checking for rowHeader
|
|
377
|
+
// - If a column has row header create an id function passed current row's index
|
|
378
|
+
// - Store callbacks in an array to call on each rows cells
|
|
379
|
+
let rowHeaders = [];
|
|
380
|
+
rowColumns.forEach((c, columnIndex) => {
|
|
381
|
+
// Creating copy of array here so it doesn't include it's own ID and also
|
|
382
|
+
// so there can be headers of headers going from left to right only
|
|
383
|
+
const thisRowsHeader = rowHeaders.slice();
|
|
384
|
+
c.getRowHeaders = rowIndex => thisRowsHeader.map(cb => cb(rowIndex)).join(" ");
|
|
385
|
+
// Now we add this columns row header function
|
|
386
|
+
// Which will be included in all columns after this iteration
|
|
387
|
+
if (c.rowHeader) {
|
|
388
|
+
c.getRowHeaderId = rowIndex => `${ this.idPrefix }-rh-${ rowIndex }-${ columnIndex }`;
|
|
389
|
+
rowHeaders.push(c.getRowHeaderId);
|
|
390
|
+
}
|
|
391
|
+
});
|
|
392
|
+
return rowColumns;
|
|
393
|
+
},
|
|
394
|
+
headerHeight() {
|
|
395
|
+
// Offset height would be the combination of all the rows height's
|
|
396
|
+
return this.headerRows.reduce((a, r) => a + r.boxHeight, 0);
|
|
397
|
+
},
|
|
398
|
+
/**
|
|
399
|
+
* Reduce the array of column header rows to the first row, first column
|
|
400
|
+
*/
|
|
401
|
+
headerRowsFirst() {
|
|
402
|
+
const firstRow = this.headerRows[0];
|
|
403
|
+
const firstColumn = Object.assign({}, firstRow.columns[0], { rowspan: 1, colspan: 1 });
|
|
404
|
+
const columns = [ firstColumn ];
|
|
405
|
+
return [{
|
|
406
|
+
...firstRow,
|
|
407
|
+
columns,
|
|
408
|
+
boxHeight: this.headerHeight,
|
|
409
|
+
height: `${ this.headerHeight }px`
|
|
410
|
+
}];
|
|
411
|
+
},
|
|
412
|
+
/**
|
|
413
|
+
* Reduce the rowColumn array to only the first column
|
|
414
|
+
*/
|
|
415
|
+
rowColumnsFirst() {
|
|
416
|
+
return [ this.rowColumns[0] ];
|
|
417
|
+
},
|
|
418
|
+
firstColumnSize() {
|
|
419
|
+
const height = this.headerRowsFirst[0].height;
|
|
420
|
+
const width = this.headerRows[0].columns[0].width;
|
|
421
|
+
return { width, height };
|
|
422
|
+
}
|
|
423
|
+
},
|
|
424
|
+
methods: {
|
|
425
|
+
resetSorts(except) {
|
|
426
|
+
const resetSort = cols => {
|
|
427
|
+
cols.forEach(col => {
|
|
428
|
+
if (except.key !== col.key) {
|
|
429
|
+
col.sortApplied = false;
|
|
430
|
+
col.sortAscending = false;
|
|
431
|
+
}
|
|
432
|
+
if (col.columns) {
|
|
433
|
+
resetSort(col.columns);
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
};
|
|
437
|
+
resetSort(this.currentColumns);
|
|
438
|
+
},
|
|
439
|
+
applySort(column) {
|
|
440
|
+
this.resetSorts(column);
|
|
441
|
+
if (column.sortApplied) {
|
|
442
|
+
column.sortAscending = !column.sortAscending;
|
|
443
|
+
} else {
|
|
444
|
+
column.sortApplied = true;
|
|
445
|
+
}
|
|
446
|
+
this.$emit("columnSort", column);
|
|
447
|
+
},
|
|
448
|
+
onColumnResize() {
|
|
449
|
+
if (this.sizesPainted) {
|
|
450
|
+
this.refresh();
|
|
451
|
+
}
|
|
452
|
+
},
|
|
453
|
+
headerAdded(el) {
|
|
454
|
+
if (!SSR) {
|
|
455
|
+
this.columnResizeObserver.observe(el);
|
|
456
|
+
}
|
|
457
|
+
},
|
|
458
|
+
headerRemoved(el) {
|
|
459
|
+
if (!SSR) {
|
|
460
|
+
this.columnResizeObserver.unobserve(el);
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
/**
|
|
464
|
+
* Allow classes options to be strings or functions
|
|
465
|
+
*/
|
|
466
|
+
resolveClasses(passed, args = null) {
|
|
467
|
+
if (typeof passed === "undefined") return;
|
|
468
|
+
if (typeof passed === "function") {
|
|
469
|
+
return passed(args);
|
|
470
|
+
}
|
|
471
|
+
return passed;
|
|
472
|
+
},
|
|
473
|
+
/**
|
|
474
|
+
* Handles horizontal scroll
|
|
475
|
+
* - Shifts the first column as the user scrolls
|
|
476
|
+
*/
|
|
477
|
+
syncScrollLeft() {
|
|
478
|
+
const left = this.$refs.display.scrollLeft;
|
|
479
|
+
this.$refs.header.$el.style.transform = `translateX(-${ left }px)`;
|
|
480
|
+
},
|
|
481
|
+
/**
|
|
482
|
+
* Checks and sets state if the table is overflow horizontally
|
|
483
|
+
*/
|
|
484
|
+
checkOverflowX() {
|
|
485
|
+
this.overflownX = this.$refs.display.scrollWidth > this.$refs.display.clientWidth;
|
|
486
|
+
},
|
|
487
|
+
/**
|
|
488
|
+
* Checks whether if the tables scroll position is at the start or end and updates state
|
|
489
|
+
*/
|
|
490
|
+
checkScrollability() {
|
|
491
|
+
if (!this.overflownX) return;
|
|
492
|
+
const element = this.$refs.display;
|
|
493
|
+
this.canScrollLeft = element.scrollLeft > 0;
|
|
494
|
+
this.canScrollRight = element.clientWidth + element.scrollLeft < element.scrollWidth;
|
|
495
|
+
},
|
|
496
|
+
/**
|
|
497
|
+
* Creates column array for internal use
|
|
498
|
+
* - Avoid mutating user's prop
|
|
499
|
+
* - Current columns being used in the display
|
|
500
|
+
* - This internal copy has internal properties/structural info (like ID)
|
|
501
|
+
* - This is the copy of the users columns to avoid mutating their object
|
|
502
|
+
* - Can be used in the future for adding/removing or enabling/disabling
|
|
503
|
+
*/
|
|
504
|
+
createColumns() {
|
|
505
|
+
const newId = this.idCreator("c");
|
|
506
|
+
const columns = cloneDeep(this.columns);
|
|
507
|
+
const prep = (column, parent) => {
|
|
508
|
+
column.id = newId();
|
|
509
|
+
column.parent = parent;
|
|
510
|
+
column.width = "auto";
|
|
511
|
+
column.boxWidth = null;
|
|
512
|
+
column.sortApplied = false;
|
|
513
|
+
column.sortAscending = false;
|
|
514
|
+
column.sortFocused = false;
|
|
515
|
+
let headers = [];
|
|
516
|
+
// Add the column's headers for output to attribute
|
|
517
|
+
if (parent) {
|
|
518
|
+
if (parent.headers && parent.headers.length) {
|
|
519
|
+
headers = [ ...parent.headers ];
|
|
520
|
+
} else {
|
|
521
|
+
headers.push(parent.id);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
headers.push(column.id);
|
|
525
|
+
column.headers = headers;
|
|
526
|
+
// Call the function on this column's children
|
|
527
|
+
if (column.columns) {
|
|
528
|
+
column.columns.forEach(c => prep(c, column));
|
|
529
|
+
// Make sure column has a required properties
|
|
530
|
+
} else if (!column.key && !column.value && !column.slot) {
|
|
531
|
+
console.warn("UluTableSticky: Missing 'key', 'value' or 'slot' in column configuration for", column);
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
columns.forEach(c => prep(c, null));
|
|
535
|
+
return columns;
|
|
536
|
+
},
|
|
537
|
+
/**
|
|
538
|
+
* Conversion of the columns (which are nested hierarchy) to a flat list of columns
|
|
539
|
+
* sorted by the way they need to be displayed in rows
|
|
540
|
+
* - Used for nested headers
|
|
541
|
+
* - Transform nested data into row arrays
|
|
542
|
+
*/
|
|
543
|
+
createHeaderRows(currentColumns) {
|
|
544
|
+
// Create empty row array, each array will hold it's columns
|
|
545
|
+
const newId = this.idCreator("hr");
|
|
546
|
+
const count = currentColumns.reduce(this.maxColumnChildren, 1);
|
|
547
|
+
const height = "auto";
|
|
548
|
+
const rows = new Array(count).fill(null).map(() => ({
|
|
549
|
+
height,
|
|
550
|
+
boxHeight: null,
|
|
551
|
+
columns: [],
|
|
552
|
+
id: newId()
|
|
553
|
+
}));
|
|
554
|
+
/**
|
|
555
|
+
* Function that adds columns to the rows array's based
|
|
556
|
+
* on their depth, called recursively.
|
|
557
|
+
*/
|
|
558
|
+
function setInRows(depth, column) {
|
|
559
|
+
const columns = column.columns;
|
|
560
|
+
// Go to inward to the deepest child
|
|
561
|
+
if (columns) columns.forEach(c => setInRows(1 + depth, c));
|
|
562
|
+
// Now that the deepest children have been calculated and pushed we have
|
|
563
|
+
// all the information we need to determine the parent's colspan by reducing
|
|
564
|
+
// the parents children's colspans and children would include their children
|
|
565
|
+
column.rowspan = columns ? 1 : count - depth;
|
|
566
|
+
column.colspan = columns ? columns.reduce((a, c) => a + c.colspan, 0) : 1;
|
|
567
|
+
rows[depth].columns.push(column);
|
|
568
|
+
}
|
|
569
|
+
currentColumns.forEach(c => setInRows(0, c));
|
|
570
|
+
return rows;
|
|
571
|
+
},
|
|
572
|
+
/**
|
|
573
|
+
* Creates row array for internal use
|
|
574
|
+
* - Avoid mutating user's prop
|
|
575
|
+
*/
|
|
576
|
+
createRows(forFooter) {
|
|
577
|
+
const newId = this.idCreator(forFooter ? "fr" : "br");
|
|
578
|
+
const rows = forFooter ? this.footerRows : this.rows;
|
|
579
|
+
return rows ? rows.map(row => ({
|
|
580
|
+
height: null,
|
|
581
|
+
boxHeight: null,
|
|
582
|
+
data: row,
|
|
583
|
+
id: newId()
|
|
584
|
+
})) : [];
|
|
585
|
+
},
|
|
586
|
+
onResize() {
|
|
587
|
+
if (SSR) return;
|
|
588
|
+
const newWindowWidth = getWindowWidth();
|
|
589
|
+
if (windowWidth === newWindowWidth) return;
|
|
590
|
+
windowWidth = newWindowWidth;
|
|
591
|
+
|
|
592
|
+
// Called when the resize event is first fired (before change)
|
|
593
|
+
if (!this.resizing) {
|
|
594
|
+
this.resizing = true;
|
|
595
|
+
this.removeTableSizes();
|
|
596
|
+
} else {
|
|
597
|
+
this.resizing = false;
|
|
598
|
+
this.setTableSizes();
|
|
599
|
+
this.checkOverflowX();
|
|
600
|
+
this.syncScrollLeft();
|
|
601
|
+
}
|
|
602
|
+
},
|
|
603
|
+
/**
|
|
604
|
+
* Method to update the table (sizes, etc) when data has changed
|
|
605
|
+
*/
|
|
606
|
+
refresh() {
|
|
607
|
+
if (SSR) return;
|
|
608
|
+
this.removeTableSizes();
|
|
609
|
+
this.$nextTick(() => {
|
|
610
|
+
this.setTableSizes();
|
|
611
|
+
this.checkOverflowX();
|
|
612
|
+
this.checkScrollability();
|
|
613
|
+
this.syncScrollLeft();
|
|
614
|
+
});
|
|
615
|
+
},
|
|
616
|
+
onScrollX() {
|
|
617
|
+
this.checkScrollability();
|
|
618
|
+
this.syncScrollLeft();
|
|
619
|
+
},
|
|
620
|
+
idCreator(type) {
|
|
621
|
+
let id = 0;
|
|
622
|
+
return () => `${ this.idPrefix }-${ type }-${ ++id }`;
|
|
623
|
+
},
|
|
624
|
+
/**
|
|
625
|
+
* Recursive function used as a reducer to return the deepest nested columns
|
|
626
|
+
*/
|
|
627
|
+
maxColumnChildren(d, c) {
|
|
628
|
+
const m = c.columns ? c.columns.reduce(this.maxColumnChildren) + 1 : 1;
|
|
629
|
+
return d > m ? d : m;
|
|
630
|
+
},
|
|
631
|
+
/**
|
|
632
|
+
* Method to attach handlers needed after creation
|
|
633
|
+
*/
|
|
634
|
+
attachHandlers() {
|
|
635
|
+
// this.handlerScrollX = this.throttleScroll(this.onScrollX); // Note: Non-reactive property
|
|
636
|
+
this.handlerScrollX = this.onScrollX; // Note: Non-reactive property
|
|
637
|
+
this.$refs.display.addEventListener("scroll", this.handlerScrollX );
|
|
638
|
+
this.scrollContext.addEventListener("touchmove", this.handlerScrollY);
|
|
639
|
+
window.addEventListener("resize", this.resizeHandler);
|
|
640
|
+
},
|
|
641
|
+
removeHandlers() {
|
|
642
|
+
this.$refs.display.removeEventListener("scroll", this.handlerScrollX);
|
|
643
|
+
this.scrollContext.removeEventListener("scroll", this.handlerScrollY);
|
|
644
|
+
this.scrollContext.addEventListener("touchmove", this.handlerScrollY);
|
|
645
|
+
window.removeEventListener("resize", this.resizeHandler);
|
|
646
|
+
},
|
|
647
|
+
removeTableSizes() {
|
|
648
|
+
this.sizesPainted = false;
|
|
649
|
+
this.sizesCalculated = false;
|
|
650
|
+
const setRowHeight = row => {
|
|
651
|
+
row.boxHeight = null;
|
|
652
|
+
row.height = "auto";
|
|
653
|
+
};
|
|
654
|
+
this.tableWidth = "auto";
|
|
655
|
+
this.headerRows.forEach(row => {
|
|
656
|
+
setRowHeight(row);
|
|
657
|
+
row.columns.forEach(column => {
|
|
658
|
+
column.boxWidth = null;
|
|
659
|
+
column.width = "auto";
|
|
660
|
+
});
|
|
661
|
+
});
|
|
662
|
+
if (this.firstColumnSticky) {
|
|
663
|
+
this.currentRows.forEach(row => setRowHeight(row));
|
|
664
|
+
this.currentFooterRows.forEach(row => setRowHeight(row));
|
|
665
|
+
}
|
|
666
|
+
},
|
|
667
|
+
scrollLeft() {
|
|
668
|
+
const element = this.$refs.display;
|
|
669
|
+
const scrollLeft = element.scrollLeft;
|
|
670
|
+
const amount = this.scrollControlAmount;
|
|
671
|
+
const toScroll = scrollLeft - amount;
|
|
672
|
+
|
|
673
|
+
element.scroll({
|
|
674
|
+
left: toScroll < 0 ? 0 : toScroll,
|
|
675
|
+
behavior: "smooth"
|
|
676
|
+
});
|
|
677
|
+
},
|
|
678
|
+
scrollRight() {
|
|
679
|
+
const element = this.$refs.display;
|
|
680
|
+
const scrollWidth = element.scrollWidth;
|
|
681
|
+
const scrollLeft = element.scrollLeft;
|
|
682
|
+
const amount = this.scrollControlAmount;
|
|
683
|
+
const toScroll = scrollLeft + amount;
|
|
684
|
+
// If amount would be greater than scrollable area
|
|
685
|
+
// scroll to end
|
|
686
|
+
// element.scrollLeft = element.scrollWidth;
|
|
687
|
+
element.scroll({
|
|
688
|
+
left: toScroll > scrollWidth ? element.scrollWidth : toScroll,
|
|
689
|
+
behavior: "smooth"
|
|
690
|
+
});
|
|
691
|
+
},
|
|
692
|
+
/**
|
|
693
|
+
* Cleanup function for when component is not in use
|
|
694
|
+
*/
|
|
695
|
+
setTableSizes() {
|
|
696
|
+
if (SSR) return;
|
|
697
|
+
// Set the table and it's cloned header to the exact same width
|
|
698
|
+
const size = (element, key) => Math.ceil(element.getBoundingClientRect()[key]);
|
|
699
|
+
this.tableWidth = `${ size(this.$refs.table.$el, "width") }px`;
|
|
700
|
+
const getElement = object => document.getElementById(object.id);
|
|
701
|
+
|
|
702
|
+
const setRowHeight = row => {
|
|
703
|
+
const element = getElement(row);
|
|
704
|
+
// Ensure element still exists, sometimes (only seen in storybook the element
|
|
705
|
+
// is removed before the unmounted/beforeUnmounted hook), this prevents the error
|
|
706
|
+
// for missing element. #mounted-no-element
|
|
707
|
+
if (element) {
|
|
708
|
+
row.boxHeight = size(element, "height");
|
|
709
|
+
row.height = `${ row.boxHeight }px`;
|
|
710
|
+
}
|
|
711
|
+
};
|
|
712
|
+
// Set the tables header <tr> and <th> to their rendered sizes
|
|
713
|
+
// By measuring each and updating it's column object data
|
|
714
|
+
// reactively updating all the cloned versions
|
|
715
|
+
this.headerRows.forEach(row => {
|
|
716
|
+
setRowHeight(row);
|
|
717
|
+
row.columns.forEach(column => {
|
|
718
|
+
const element = getElement(column);
|
|
719
|
+
// See #mounted-no-element
|
|
720
|
+
if (element) {
|
|
721
|
+
column.boxWidth = size(element, "width");
|
|
722
|
+
column.width = `${ column.boxWidth }px`;
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
});
|
|
726
|
+
// If first column sticky the plugin needs to set
|
|
727
|
+
// each row's height so the cloned column matches
|
|
728
|
+
if (this.firstColumnSticky) {
|
|
729
|
+
this.currentRows.forEach(row => setRowHeight(row));
|
|
730
|
+
this.currentFooterRows.forEach(row => setRowHeight(row));
|
|
731
|
+
}
|
|
732
|
+
this.$nextTick(() => {
|
|
733
|
+
this.sizesCalculated = true;
|
|
734
|
+
runAfterFramePaint(() => {
|
|
735
|
+
this.sizesPainted = true;
|
|
736
|
+
});
|
|
737
|
+
});
|
|
738
|
+
},
|
|
739
|
+
tableReady() {
|
|
740
|
+
this.setTableSizes();
|
|
741
|
+
},
|
|
742
|
+
/**
|
|
743
|
+
* Creates a new throttled scroll handler
|
|
744
|
+
*/
|
|
745
|
+
// throttleScroll(handler) {
|
|
746
|
+
// let id = null;
|
|
747
|
+
// // Old Fired after frame
|
|
748
|
+
// return (event) => {
|
|
749
|
+
// if (id) window.cancelAnimationFrame(id);
|
|
750
|
+
// id = window.requestAnimationFrame(() => handler(event));
|
|
751
|
+
// };
|
|
752
|
+
// },
|
|
753
|
+
},
|
|
754
|
+
mounted() {
|
|
755
|
+
if (!SSR) {
|
|
756
|
+
this.attachHandlers();
|
|
757
|
+
this.checkOverflowX();
|
|
758
|
+
this.checkScrollability();
|
|
759
|
+
}
|
|
760
|
+
},
|
|
761
|
+
beforeUnmount() {
|
|
762
|
+
if (!SSR) {
|
|
763
|
+
this.removeHandlers();
|
|
764
|
+
}
|
|
765
|
+
},
|
|
766
|
+
unmounted() {
|
|
767
|
+
if (!SSR) {
|
|
768
|
+
// Allow resizer to be GC
|
|
769
|
+
this.columnResizeObserver.disconnect();
|
|
770
|
+
this.columnResizeObserver = null;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
};
|
|
774
|
+
</script>
|
|
775
|
+
|
|
776
|
+
<!--
|
|
777
|
+
Issues to Avoid:
|
|
778
|
+
- If we allow the user to pass components in column config, it will cause errors and performance issues when watching the column config for 'deep' changes
|
|
779
|
+
- Thoughts:
|
|
780
|
+
- 1. remove component option
|
|
781
|
+
- This would be fine and simplify the component, the user can already use slots to pass their own component. They could always
|
|
782
|
+
set up their own wrapper component for the UluTableSticky with the slot templates routed to the preferred components
|
|
783
|
+
- 2. Allow component option but only strings and have another property that is a lookup of components (shallow, markRaw)
|
|
784
|
+
- I think this is the best approach, as it is an advanced option
|
|
785
|
+
- 3. Remove the deep watch and force the user to have to provide a new array when changing column config (and probably row)
|
|
786
|
+
- Don't like this because, I think the table may redraw completely, but then again we are cloning the columns, so the whole
|
|
787
|
+
table is redrawn anyways
|
|
788
|
+
- We should probably avoid having components in objects anyways
|
|
789
|
+
- Decided this is not nessassary with slots the user can setup their own components
|
|
790
|
+
- Removed b/c bad practice to have components in reactive data (that's watched)
|
|
791
|
+
- Future this could be prop instead of in column.config
|
|
792
|
+
- Or this can be refactored to just use the props directly and keep the data for the plugin seperate but related
|
|
793
|
+
-->
|