@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,73 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<tr
|
|
3
|
+
v-for="(row, rowIndex) in rows"
|
|
4
|
+
:key="`br-${ rowIndex }`"
|
|
5
|
+
:id="optionalAttr(isActual && row.id)"
|
|
6
|
+
:class="resolveClasses(classes.row, { row: row.data, rowIndex, isActual, foot })"
|
|
7
|
+
:style="{
|
|
8
|
+
height: row.height
|
|
9
|
+
}"
|
|
10
|
+
>
|
|
11
|
+
<component
|
|
12
|
+
v-for="(column, index) in rowColumns"
|
|
13
|
+
:is="column.rowHeader ? 'th' : 'td'"
|
|
14
|
+
:id="optionalAttr(isActual && column.rowHeader && column.getRowHeaderId(rowIndex))"
|
|
15
|
+
:scope="optionalAttr(isActual && column.rowHeader && 'row')"
|
|
16
|
+
:key="`bc-${ index }`"
|
|
17
|
+
:headers="optionalAttr(isActual && getCellHeaders(column, rowIndex))"
|
|
18
|
+
:class="resolveClasses(column.class, { column, index, isActual, row, rowIndex, foot })"
|
|
19
|
+
:style="{
|
|
20
|
+
width: columnWidth
|
|
21
|
+
}"
|
|
22
|
+
>
|
|
23
|
+
<template v-if="$slots[column.slot]">
|
|
24
|
+
<slot
|
|
25
|
+
:name="column.slot"
|
|
26
|
+
:row="row.data"
|
|
27
|
+
:column="column"
|
|
28
|
+
:rowIndex="rowIndex"
|
|
29
|
+
:index="index"
|
|
30
|
+
:foot="foot"
|
|
31
|
+
:isActual="isActual"
|
|
32
|
+
/>
|
|
33
|
+
</template>
|
|
34
|
+
<!--
|
|
35
|
+
<component
|
|
36
|
+
v-else-if="column.component"
|
|
37
|
+
:is="column.component"
|
|
38
|
+
:row="row.data"
|
|
39
|
+
:column="column"
|
|
40
|
+
:rowIndex="rowIndex"
|
|
41
|
+
:index="index"
|
|
42
|
+
/> -->
|
|
43
|
+
<div
|
|
44
|
+
v-else-if="column.html"
|
|
45
|
+
v-html="value({ row, column, rowIndex, isActual, foot })"
|
|
46
|
+
></div>
|
|
47
|
+
<template v-else>
|
|
48
|
+
{{ value({ row, column, rowIndex, isActual, foot }) }}
|
|
49
|
+
</template>
|
|
50
|
+
</component>
|
|
51
|
+
</tr>
|
|
52
|
+
</template>
|
|
53
|
+
|
|
54
|
+
<script>
|
|
55
|
+
export default {
|
|
56
|
+
name: "UluTableStickyRows",
|
|
57
|
+
props: {
|
|
58
|
+
rows: Array,
|
|
59
|
+
rowColumns: Array,
|
|
60
|
+
columnWidth: String,
|
|
61
|
+
optionalAttr: Function,
|
|
62
|
+
resolveClasses: Function,
|
|
63
|
+
getCellHeaders: Function,
|
|
64
|
+
value: Function,
|
|
65
|
+
isActual: Boolean,
|
|
66
|
+
classes: Object,
|
|
67
|
+
foot: {
|
|
68
|
+
type: Boolean,
|
|
69
|
+
default: false
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
</script>
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<table
|
|
3
|
+
:class="resolveClasses(classes.table, { isActual })"
|
|
4
|
+
:aria-hidden="isActual ? 'false' : 'true'"
|
|
5
|
+
>
|
|
6
|
+
<caption v-if="caption" class="table-sticky__caption">
|
|
7
|
+
{{ caption }}
|
|
8
|
+
</caption>
|
|
9
|
+
<thead>
|
|
10
|
+
<tr
|
|
11
|
+
v-for="(row, rowIndex) in headerRows"
|
|
12
|
+
:key="`hr-${ rowIndex }`"
|
|
13
|
+
:id="optionalAttr(isActual && row.id)"
|
|
14
|
+
:class="resolveClasses(classes.rowHeader, { row, rowIndex, isActual })"
|
|
15
|
+
:style="{
|
|
16
|
+
height: row.height
|
|
17
|
+
}"
|
|
18
|
+
>
|
|
19
|
+
<th
|
|
20
|
+
v-for="(column, index) in row.columns"
|
|
21
|
+
:key="`hc-${ index }`"
|
|
22
|
+
:id="optionalAttr(isActual && column.id)"
|
|
23
|
+
:rowspan="column.rowspan"
|
|
24
|
+
:colspan="column.colspan"
|
|
25
|
+
:data-child-columns="column.columns && column.columns.length"
|
|
26
|
+
:class="[
|
|
27
|
+
{
|
|
28
|
+
'sort-active' : column.sortApplied,
|
|
29
|
+
'sort-ascending' : column.sortApplied && column.sortAscending,
|
|
30
|
+
'sort-descending' : column.sortApplied && !column.sortAscending,
|
|
31
|
+
},
|
|
32
|
+
resolveClasses(column.classHeader, { column, index, isActual })
|
|
33
|
+
]"
|
|
34
|
+
:style="{
|
|
35
|
+
width: column.width
|
|
36
|
+
}"
|
|
37
|
+
:aria-sort="column.sort ? column.sortAscending ? 'ascending' : 'descending' : null"
|
|
38
|
+
:scope="optionalAttr(isActual && (column.colspan > 1 ? 'colgroup' : 'col'))"
|
|
39
|
+
:headers="optionalAttr(isActual && getHeaderHeaders(column, rowIndex))"
|
|
40
|
+
:ref="(el) => addHeaderRef(column, el)"
|
|
41
|
+
>
|
|
42
|
+
<!--
|
|
43
|
+
If sortable print button but div on fake headers (click only)
|
|
44
|
+
Focus will go back to real header (focus and blur will update styling)
|
|
45
|
+
-->
|
|
46
|
+
<component
|
|
47
|
+
v-if="column.sortable"
|
|
48
|
+
:is="isActual ? 'button' : 'div'"
|
|
49
|
+
class="table-sticky__sort-button"
|
|
50
|
+
:class="{
|
|
51
|
+
'table-sticky__sort-button--focused' : column.sortFocused,
|
|
52
|
+
}"
|
|
53
|
+
@click="$emit('columnSorted', column)"
|
|
54
|
+
@focus="handleSortFocus(column, true)"
|
|
55
|
+
@blur="handleSortFocus(column, false)"
|
|
56
|
+
:aria-pressed="column.sortApplied ? 'true' : 'false'"
|
|
57
|
+
>
|
|
58
|
+
<template v-if="$slots[column.slotHeader]">
|
|
59
|
+
<slot
|
|
60
|
+
:name="column.slotHeader"
|
|
61
|
+
:isActual="isActual"
|
|
62
|
+
:column="column"
|
|
63
|
+
:index="index"
|
|
64
|
+
/>
|
|
65
|
+
</template>
|
|
66
|
+
<div v-else-if="column.htmlTitle" v-html="getColumnTitle({ column, index, isActual })"></div>
|
|
67
|
+
<template v-else>
|
|
68
|
+
{{ getColumnTitle({ column, index, isActual }) }}
|
|
69
|
+
</template>
|
|
70
|
+
<span class="table-sticky__sort-icon" aria-hidden="true">
|
|
71
|
+
<span class="table-sticky__sort-icon-inner">
|
|
72
|
+
<slot name="sortIcon">▼</slot>
|
|
73
|
+
</span>
|
|
74
|
+
</span>
|
|
75
|
+
</component>
|
|
76
|
+
<template v-else>
|
|
77
|
+
<template v-if="$slots[column.slotHeader]">
|
|
78
|
+
<slot
|
|
79
|
+
:name="column.slotHeader"
|
|
80
|
+
:isActual="isActual"
|
|
81
|
+
:column="column"
|
|
82
|
+
:index="index"
|
|
83
|
+
/>
|
|
84
|
+
</template>
|
|
85
|
+
<div v-else-if="column.htmlTitle" v-html="getColumnTitle({ column, index, isActual })"></div>
|
|
86
|
+
<template v-else>
|
|
87
|
+
{{ getColumnTitle({ column, index, isActual }) }}
|
|
88
|
+
</template>
|
|
89
|
+
</template>
|
|
90
|
+
</th>
|
|
91
|
+
</tr>
|
|
92
|
+
</thead>
|
|
93
|
+
<template v-if="rows">
|
|
94
|
+
<tbody>
|
|
95
|
+
<UluTableStickyRows
|
|
96
|
+
:rows="rows"
|
|
97
|
+
:rowColumns="rowColumns"
|
|
98
|
+
:optionalAttr="optionalAttr"
|
|
99
|
+
:resolveClasses="resolveClasses"
|
|
100
|
+
:getCellHeaders="getCellHeaders"
|
|
101
|
+
:isActual="isActual"
|
|
102
|
+
:columnWidth="columnWidth"
|
|
103
|
+
:classes="classes"
|
|
104
|
+
:value="value"
|
|
105
|
+
>
|
|
106
|
+
<template v-for="(_, name) in $slots" v-slot:[name]="slotData">
|
|
107
|
+
<slot :name="name" v-bind="slotData" />
|
|
108
|
+
</template>
|
|
109
|
+
</UluTableStickyRows>
|
|
110
|
+
</tbody>
|
|
111
|
+
</template>
|
|
112
|
+
<template v-if="footerRows">
|
|
113
|
+
<tfoot>
|
|
114
|
+
<UluTableStickyRows
|
|
115
|
+
:rows="footerRows"
|
|
116
|
+
:rowColumns="rowColumns"
|
|
117
|
+
:optionalAttr="optionalAttr"
|
|
118
|
+
:resolveClasses="resolveClasses"
|
|
119
|
+
:getCellHeaders="getCellHeaders"
|
|
120
|
+
:isActual="isActual"
|
|
121
|
+
:columnWidth="columnWidth"
|
|
122
|
+
:classes="classes"
|
|
123
|
+
:value="value"
|
|
124
|
+
foot
|
|
125
|
+
>
|
|
126
|
+
<template v-for="(_, name) in $slots" v-slot:[name]="slotData">
|
|
127
|
+
<slot :name="name" v-bind="slotData" />
|
|
128
|
+
</template>
|
|
129
|
+
</UluTableStickyRows>
|
|
130
|
+
</tfoot>
|
|
131
|
+
</template>
|
|
132
|
+
</table>
|
|
133
|
+
</template>
|
|
134
|
+
|
|
135
|
+
<script>
|
|
136
|
+
import UluTableStickyRows from "./UluTableStickyRows.vue";
|
|
137
|
+
export default {
|
|
138
|
+
name: "UluTableStickyTable",
|
|
139
|
+
components: {
|
|
140
|
+
UluTableStickyRows,
|
|
141
|
+
},
|
|
142
|
+
props: {
|
|
143
|
+
resolveClasses: Function,
|
|
144
|
+
classes: {
|
|
145
|
+
type: Object,
|
|
146
|
+
default: () => ({})
|
|
147
|
+
},
|
|
148
|
+
caption: String,
|
|
149
|
+
idPrefix: String,
|
|
150
|
+
headerRows: {
|
|
151
|
+
type: Array,
|
|
152
|
+
required: true
|
|
153
|
+
},
|
|
154
|
+
rows: Array,
|
|
155
|
+
footerRows: Array,
|
|
156
|
+
rowColumns: Array,
|
|
157
|
+
/**
|
|
158
|
+
* Is the actual table not a clone for sticky headers
|
|
159
|
+
*/
|
|
160
|
+
isActual: {
|
|
161
|
+
type: Boolean
|
|
162
|
+
},
|
|
163
|
+
columnWidth: {
|
|
164
|
+
type: String
|
|
165
|
+
},
|
|
166
|
+
/**
|
|
167
|
+
* Optional user overridden value getter (for rows)
|
|
168
|
+
* @param {Object} row The current row
|
|
169
|
+
* @param {Object} column The current column in the row
|
|
170
|
+
*/
|
|
171
|
+
getRowValue: {
|
|
172
|
+
type: Function,
|
|
173
|
+
default: ({ row, column }) => row[column.key]
|
|
174
|
+
},
|
|
175
|
+
/**
|
|
176
|
+
* Optional user overridden value getter (for rows)
|
|
177
|
+
* @param {Object} row The current row
|
|
178
|
+
* @param {Object} column The current column in the row
|
|
179
|
+
*/
|
|
180
|
+
getColumnTitle: {
|
|
181
|
+
type: Function,
|
|
182
|
+
default: ({ column }) => column.title
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
data() {
|
|
186
|
+
return {
|
|
187
|
+
headerRefs: {}
|
|
188
|
+
};
|
|
189
|
+
},
|
|
190
|
+
methods: {
|
|
191
|
+
handleSortFocus(column, isFocused) {
|
|
192
|
+
if (this.isActual) {
|
|
193
|
+
column.sortFocused = isFocused;
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
addHeaderRef(column, el) {
|
|
197
|
+
const { headerRefs, isActual } = this;
|
|
198
|
+
if (!isActual || !el) return;
|
|
199
|
+
const { id } = column;
|
|
200
|
+
const old = headerRefs[id];
|
|
201
|
+
if (old) {
|
|
202
|
+
this.$emit("actualHeaderRemoved", old);
|
|
203
|
+
}
|
|
204
|
+
this.$emit("actualHeaderAdded", el);
|
|
205
|
+
headerRefs[id] = el;
|
|
206
|
+
},
|
|
207
|
+
/**
|
|
208
|
+
* False is no longer not printed
|
|
209
|
+
*/
|
|
210
|
+
optionalAttr(val) {
|
|
211
|
+
return val ? val : null;
|
|
212
|
+
},
|
|
213
|
+
value({ row, column, rowIndex }) {
|
|
214
|
+
const value = column.value;
|
|
215
|
+
// Column has value function, pass to user
|
|
216
|
+
if (value) {
|
|
217
|
+
return value({ row: row.data, column, rowIndex });
|
|
218
|
+
} else {
|
|
219
|
+
// added .value to work with data. Should refactor depending on what we need
|
|
220
|
+
return this.getRowValue({ row: row.data, column, rowIndex });
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
getCellHeaders(column, rowIndex) {
|
|
224
|
+
const headers = column.headers.join(" ");
|
|
225
|
+
const rowHeaders = column.getRowHeaders(rowIndex);
|
|
226
|
+
const s = rowHeaders.length ? " " : "";
|
|
227
|
+
return `${ headers }${ s }${ rowHeaders }`;
|
|
228
|
+
},
|
|
229
|
+
getHeaderHeaders(column) {
|
|
230
|
+
const headersArray = column.headers.filter(id => id !== column.id);
|
|
231
|
+
if (headersArray.length) {
|
|
232
|
+
return headersArray.join(" ");
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
</script>
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
////
|
|
2
|
+
/// @group table-sticky
|
|
3
|
+
/// For use with table-sticky plugin (vue) or other framework implementations, not output by default must be enabled.
|
|
4
|
+
////
|
|
5
|
+
|
|
6
|
+
@use "sass:map";
|
|
7
|
+
@use "sass:meta";
|
|
8
|
+
|
|
9
|
+
@use "@ulu/frontend/scss/selector";
|
|
10
|
+
@use "@ulu/frontend/scss/utils";
|
|
11
|
+
@use "@ulu/frontend/scss/color";
|
|
12
|
+
@use "@ulu/frontend/scss/element";
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
// Used for function fallback
|
|
16
|
+
$-fallbacks: (
|
|
17
|
+
"box-shadow" : (
|
|
18
|
+
"function" : meta.get-function("get", false, "element"),
|
|
19
|
+
"property" : "box-shadow"
|
|
20
|
+
),
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
/// Module Settings
|
|
24
|
+
/// @type Map
|
|
25
|
+
/// @prop {CssValue} box-shadow [true] Box shadow for controls, defaults to element box-shadow
|
|
26
|
+
|
|
27
|
+
$config: (
|
|
28
|
+
"box-shadow": true,
|
|
29
|
+
"ui-color-disabled": #6490af,
|
|
30
|
+
) !default;
|
|
31
|
+
|
|
32
|
+
/// Change modules $config
|
|
33
|
+
/// @param {Map} $changes Map of changes
|
|
34
|
+
/// @example scss
|
|
35
|
+
/// @include ulu.component-table-sticky-set(( "property" : value ));
|
|
36
|
+
|
|
37
|
+
@mixin set($changes) {
|
|
38
|
+
$config: map.merge($config, $changes) !global;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// Get a config option
|
|
42
|
+
/// @param {Map} $name Name of property
|
|
43
|
+
/// @example scss
|
|
44
|
+
/// @include ulu.component-table-sticky-get("property");
|
|
45
|
+
|
|
46
|
+
@function get($name) {
|
|
47
|
+
$value: utils.require-map-get($config, $name, "table-sticky [config]");
|
|
48
|
+
@return utils.function-fallback($name, $value, $-fallbacks);
|
|
49
|
+
}
|
|
50
|
+
/// Prints component styles
|
|
51
|
+
/// @demo table-sticky
|
|
52
|
+
/// @example scss
|
|
53
|
+
/// @include ulu.component-table-sticky-styles();
|
|
54
|
+
|
|
55
|
+
@mixin styles {
|
|
56
|
+
$prefix: selector.class("table-sticky");
|
|
57
|
+
|
|
58
|
+
#{ $prefix } {
|
|
59
|
+
position: relative; // For controls
|
|
60
|
+
width: 100%;
|
|
61
|
+
* {
|
|
62
|
+
box-sizing: border-box;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
#{ $prefix }__display {
|
|
66
|
+
overflow-x: auto;
|
|
67
|
+
overflow-y: hidden;
|
|
68
|
+
// scroll-behavior: smooth;
|
|
69
|
+
}
|
|
70
|
+
#{ $prefix }__table {
|
|
71
|
+
// border-collapse: collapse;
|
|
72
|
+
margin: 0;
|
|
73
|
+
padding: 0;
|
|
74
|
+
width: 100%;
|
|
75
|
+
}
|
|
76
|
+
#{ $prefix }__sticky-wrap {
|
|
77
|
+
position: sticky;
|
|
78
|
+
top: -1px;
|
|
79
|
+
left: 0;
|
|
80
|
+
width: 100%;
|
|
81
|
+
z-index: 2;
|
|
82
|
+
margin-bottom: 200px; // Distance from bottom of table to disable
|
|
83
|
+
margin-top: -200px; // Distance from bottom of table to disable
|
|
84
|
+
}
|
|
85
|
+
#{ $prefix }__sticky-wrap--first-column-header {
|
|
86
|
+
z-index: 4;
|
|
87
|
+
}
|
|
88
|
+
#{ $prefix }__sticky-wrap--controls {
|
|
89
|
+
margin-top: -52vh;
|
|
90
|
+
margin-bottom: 52vh;
|
|
91
|
+
}
|
|
92
|
+
#{ $prefix }__header-wrap {
|
|
93
|
+
position: absolute;
|
|
94
|
+
top: 0;
|
|
95
|
+
left: 0;
|
|
96
|
+
right: 0;
|
|
97
|
+
overflow: hidden;
|
|
98
|
+
}
|
|
99
|
+
#{ $prefix }__table--first-column-header,
|
|
100
|
+
#{ $prefix }__table--first-column {
|
|
101
|
+
position: absolute;
|
|
102
|
+
top: 0;
|
|
103
|
+
left: 0;
|
|
104
|
+
width: auto !important;
|
|
105
|
+
}
|
|
106
|
+
#{ $prefix }__table--first-column {
|
|
107
|
+
z-index: 3;
|
|
108
|
+
}
|
|
109
|
+
#{ $prefix }__table--header {
|
|
110
|
+
will-change: transform;
|
|
111
|
+
}
|
|
112
|
+
#{ $prefix }__hidden-visually {
|
|
113
|
+
position: absolute;
|
|
114
|
+
left: -10000px;
|
|
115
|
+
top: auto;
|
|
116
|
+
width: 1px;
|
|
117
|
+
height: 1px;
|
|
118
|
+
overflow: hidden;
|
|
119
|
+
}
|
|
120
|
+
// NOTE: The table caption needs to be positioned normally
|
|
121
|
+
// as display table-cell. Making absolute for hidden-visually
|
|
122
|
+
// is causing chrome to add an anonymous cell to the table resulting in a 1px
|
|
123
|
+
// cell under the header. Which is messing up alignments. The only solution
|
|
124
|
+
// without removing the <caption> (not good for WCAG) is to position it at the
|
|
125
|
+
// of the table so it doesn't affect the header alignments. Then cropping it to
|
|
126
|
+
// a pixel and using negative margin to remove it's pixel from the flow beneath
|
|
127
|
+
#{ $prefix }__caption {
|
|
128
|
+
margin-bottom: -1px;
|
|
129
|
+
height: 1px;
|
|
130
|
+
padding: 0 !important;
|
|
131
|
+
margin: 0 !important;
|
|
132
|
+
overflow: hidden;
|
|
133
|
+
display: none;
|
|
134
|
+
}
|
|
135
|
+
// Allow initiating scrolling by dragging first column
|
|
136
|
+
#{ $prefix }--can-scroll-right:not(#{ $prefix }--can-scroll-left) {
|
|
137
|
+
#{ $prefix }__table--first-column-header,
|
|
138
|
+
#{ $prefix }__table--first-column {
|
|
139
|
+
// pointer-events: none;
|
|
140
|
+
visibility: hidden;
|
|
141
|
+
// opacity: 0 !important;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
#{ $prefix }__controls {
|
|
145
|
+
position: absolute;
|
|
146
|
+
top: 50vh;
|
|
147
|
+
right: 20px;
|
|
148
|
+
}
|
|
149
|
+
#{ $prefix }__controls-inner {
|
|
150
|
+
display: flex;
|
|
151
|
+
}
|
|
152
|
+
#{ $prefix }__control {
|
|
153
|
+
box-shadow: get("box-shadow");
|
|
154
|
+
}
|
|
155
|
+
#{ $prefix }__sort-button {
|
|
156
|
+
display: flex;
|
|
157
|
+
align-items: center;
|
|
158
|
+
font-weight: inherit;
|
|
159
|
+
font-style: inherit;
|
|
160
|
+
font-size: inherit;
|
|
161
|
+
}
|
|
162
|
+
#{ $prefix }__sort-button--focused {
|
|
163
|
+
outline: 2px auto Highlight;
|
|
164
|
+
outline: 2px auto -webkit-focus-ring-color;
|
|
165
|
+
}
|
|
166
|
+
#{ $prefix }__sort-icon {
|
|
167
|
+
margin-left: auto;
|
|
168
|
+
padding-left: 1em;
|
|
169
|
+
display: block;
|
|
170
|
+
opacity: 0.5;
|
|
171
|
+
.sort-active & {
|
|
172
|
+
opacity: 1;
|
|
173
|
+
}
|
|
174
|
+
.sort-ascending & {
|
|
175
|
+
#{ $prefix }__sort-icon-inner {
|
|
176
|
+
transform: rotate(180deg);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
#{ $prefix }__sort-icon-inner {
|
|
181
|
+
display: block;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<!-- Intentional Coercion to null (undefined and null, else print) -->
|
|
3
|
+
<component v-if="text != null" :is="element">
|
|
4
|
+
{{ text }}
|
|
5
|
+
</component>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script>
|
|
9
|
+
/**
|
|
10
|
+
* Print out text if set (has value)
|
|
11
|
+
*/
|
|
12
|
+
export default {
|
|
13
|
+
name: "UluCondText",
|
|
14
|
+
props: {
|
|
15
|
+
/**
|
|
16
|
+
* Text to print in element
|
|
17
|
+
*/
|
|
18
|
+
text: [String, Number, Array, Object],
|
|
19
|
+
/**
|
|
20
|
+
* Element type to render (ie. h1, h2, p, etc)
|
|
21
|
+
*/
|
|
22
|
+
element: {
|
|
23
|
+
type: String,
|
|
24
|
+
default: "p"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
</script>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<img :src="src" :alt="alt">
|
|
3
|
+
</template>
|
|
4
|
+
<script>
|
|
5
|
+
import { randomInt } from "@ulu/utils/random.js";
|
|
6
|
+
export default {
|
|
7
|
+
name: "UluPlaceholderImage",
|
|
8
|
+
props: {
|
|
9
|
+
imageId: String,
|
|
10
|
+
/**
|
|
11
|
+
* Width of the image
|
|
12
|
+
*/
|
|
13
|
+
width: {
|
|
14
|
+
type: [String, Number],
|
|
15
|
+
default: "300"
|
|
16
|
+
},
|
|
17
|
+
/**
|
|
18
|
+
* Height of the image
|
|
19
|
+
*/
|
|
20
|
+
height: {
|
|
21
|
+
type: [String, Number],
|
|
22
|
+
default: "400"
|
|
23
|
+
},
|
|
24
|
+
/**
|
|
25
|
+
* Alt text for placeholder image
|
|
26
|
+
*/
|
|
27
|
+
alt: String,
|
|
28
|
+
/**
|
|
29
|
+
* Random size
|
|
30
|
+
*/
|
|
31
|
+
random: Boolean
|
|
32
|
+
},
|
|
33
|
+
computed: {
|
|
34
|
+
src() {
|
|
35
|
+
const { imageId } = this;
|
|
36
|
+
const { width, height } = this.size;
|
|
37
|
+
const id = imageId ? `id/${ imageId }/` : "";
|
|
38
|
+
return `https://picsum.photos/${ id }${ width }/${ height }`;
|
|
39
|
+
},
|
|
40
|
+
size() {
|
|
41
|
+
const { random, width, height } = this;
|
|
42
|
+
if (random) {
|
|
43
|
+
return {
|
|
44
|
+
width: randomInt(500, 1000),
|
|
45
|
+
height: randomInt(500, 1000),
|
|
46
|
+
};
|
|
47
|
+
} else {
|
|
48
|
+
return { width, height };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
</script>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<component
|
|
3
|
+
v-for="index in parseInt(amount)"
|
|
4
|
+
:key="index"
|
|
5
|
+
:is="element"
|
|
6
|
+
>
|
|
7
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed semper erat tincidunt tellus vestibulum dictum. Fusce vel augue commodo, egestas diam sed, accumsan leo. Maecenas congue nec nisl et ullamcorper. Maecenas tincidunt, tortor et viverra eleifend, enim leo vestibulum ipsum, quis placerat mi nisi nec ex. Vivamus a justo volutpat, scelerisque elit eget, lacinia ex. Phasellus dapibus sollicitudin tortor, vitae suscipit nunc condimentum ut. Cras suscipit feugiat nibh nec consectetur. Phasellus vitae quam blandit, cursus metus ut, placerat mi. Sed tempor lacus non est interdum imperdiet nec quis metus. Praesent vel eleifend diam. Donec tincidunt eget purus sed posuere.
|
|
8
|
+
</component>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script>
|
|
12
|
+
export default {
|
|
13
|
+
name: "PlaceholderText",
|
|
14
|
+
props: {
|
|
15
|
+
amount: {
|
|
16
|
+
type: Number,
|
|
17
|
+
default: 1
|
|
18
|
+
},
|
|
19
|
+
element: {
|
|
20
|
+
type: String,
|
|
21
|
+
default: "p"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
</script>
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Route Announcer:
|
|
3
|
+
- Used to provide accessible title after route (page) change
|
|
4
|
+
- Will ignore any routes that have hashes
|
|
5
|
+
- Props
|
|
6
|
+
- exclude {Array} You can exclude specific routes () by exact path or path with wildcard at end, alternatively use the 'confirm' prop for complete control
|
|
7
|
+
- validator {Function} Pass a function to determine if current route should be announced
|
|
8
|
+
- getTitle {Function} Provide method for extracting title from route return string
|
|
9
|
+
|
|
10
|
+
Should include <UluRouteAnnouncer /> as first component in app
|
|
11
|
+
-->
|
|
12
|
+
<template>
|
|
13
|
+
<p
|
|
14
|
+
v-if="title"
|
|
15
|
+
tabindex="-1"
|
|
16
|
+
class="hidden-visually"
|
|
17
|
+
ref="el"
|
|
18
|
+
>
|
|
19
|
+
{{ title }}
|
|
20
|
+
</p>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<script>
|
|
24
|
+
export default {
|
|
25
|
+
name: "RouteAnnouncer",
|
|
26
|
+
props: {
|
|
27
|
+
/**
|
|
28
|
+
* Allow user to bypass this functionality
|
|
29
|
+
* - Function should return true if the page should be annouced
|
|
30
|
+
* - Function is passed (to, from, $route) => {}
|
|
31
|
+
* - to/from are path strings
|
|
32
|
+
*/
|
|
33
|
+
validator: {
|
|
34
|
+
type: Function,
|
|
35
|
+
default: () => true
|
|
36
|
+
},
|
|
37
|
+
/**
|
|
38
|
+
* Array of paths to exclude
|
|
39
|
+
* - Can be exact path "/about"
|
|
40
|
+
* - Or can be path with wildcard "/about/*" (match all paths under about)
|
|
41
|
+
*/
|
|
42
|
+
exclude: {
|
|
43
|
+
type: Array,
|
|
44
|
+
default: () => []
|
|
45
|
+
},
|
|
46
|
+
/**
|
|
47
|
+
* Function to retrieve routes title
|
|
48
|
+
*/
|
|
49
|
+
getTitle: {
|
|
50
|
+
type: Function,
|
|
51
|
+
default: (route) => route.meta?.title
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
watch: {
|
|
55
|
+
"$route.path"(to, from) {
|
|
56
|
+
if (this.$route.hash) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const isValid = this.validator(to, from, this.$route);
|
|
60
|
+
const isExcluded = this.exclude.some(ex => {
|
|
61
|
+
// Allow wildcard at end to exclude entire sections, etc
|
|
62
|
+
if (ex.endsWith("*")) {
|
|
63
|
+
return to.startsWith(ex.slice(0, ex.length - 1));
|
|
64
|
+
} else {
|
|
65
|
+
return to === ex;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
if (isValid && !isExcluded) {
|
|
69
|
+
this.$refs.el.focus();
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
computed: {
|
|
74
|
+
title() {
|
|
75
|
+
const title = this.getTitle(this.$route);
|
|
76
|
+
if (!title) {
|
|
77
|
+
console.warn("RouteAnnouncer: No page title!");
|
|
78
|
+
}
|
|
79
|
+
return title;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
</script>
|