@ulu/frontend-vue 0.1.0-beta.3 → 0.1.0-beta.31
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/README.md +113 -2
- package/dist/{breakpoints-Cq2oSdYS.js → breakpoints-BJNvnXsD.js} +1 -1
- package/dist/frontend-vue.css +1 -1
- package/dist/frontend-vue.js +78 -72
- package/dist/index-BjwifaTk.js +6946 -0
- package/lib/components/collapsible/UluAccordion.vue +1 -1
- package/lib/components/collapsible/UluModal.vue +4 -5
- package/lib/components/collapsible/UluOverflowPopover.vue +1 -1
- package/lib/components/elements/UluAlert.vue +1 -2
- package/lib/components/elements/UluBadge.vue +27 -28
- package/lib/components/elements/UluBadgeStack.vue +8 -13
- package/lib/components/elements/UluButton.vue +2 -2
- package/lib/components/elements/UluButtonVerbose.vue +119 -0
- package/lib/components/elements/UluCard.vue +1 -1
- package/lib/components/elements/UluDefinitionList.vue +14 -17
- package/lib/components/elements/UluExternalLink.vue +22 -29
- package/lib/components/elements/UluIcon.vue +22 -17
- package/lib/components/elements/UluList.vue +53 -55
- package/lib/components/elements/UluSpokeSpinner.vue +12 -18
- package/lib/components/elements/UluTag.vue +35 -35
- package/lib/components/forms/UluFileDisplay.vue +40 -31
- package/lib/components/forms/UluFormFile.vue +22 -24
- package/lib/components/forms/UluFormMessage.vue +7 -10
- package/lib/components/forms/UluFormSelect.vue +16 -16
- package/lib/components/forms/UluFormText.vue +15 -15
- package/lib/components/forms/UluSearchForm.vue +8 -10
- package/lib/components/forms/UluSelectableMenu.vue +78 -0
- package/lib/components/index.js +2 -2
- package/lib/components/layout/UluAdaptiveLayout.vue +3 -5
- package/lib/components/layout/UluTitleRail.vue +9 -5
- package/lib/components/layout/UluWhenBreakpoint.vue +71 -77
- package/lib/components/navigation/UluBreadcrumb.vue +10 -4
- package/lib/components/navigation/UluMenu.vue +3 -3
- package/lib/components/navigation/UluPager.vue +102 -0
- package/lib/components/systems/facets/ExampleFacetsWithPagination.vue +119 -0
- package/lib/components/systems/facets/UluFacetsFilterLists.vue +84 -0
- package/lib/components/systems/facets/UluFacetsFilterPopovers.vue +114 -0
- package/lib/components/systems/facets/UluFacetsFilterSelects.vue +71 -0
- package/lib/components/systems/facets/UluFacetsHeaderLayout.vue +24 -0
- package/lib/components/systems/facets/UluFacetsList.vue +61 -33
- package/lib/components/systems/facets/UluFacetsResults.vue +63 -0
- package/lib/components/systems/facets/UluFacetsSearch.vue +26 -49
- package/lib/components/systems/facets/UluFacetsSidebarLayout.vue +31 -0
- package/lib/components/systems/facets/UluFacetsSort.vue +45 -0
- package/lib/components/systems/facets/_facets.scss +2 -3
- package/lib/components/systems/facets/_mock-data.js +40 -0
- package/lib/components/systems/facets/useFacets.js +229 -0
- package/lib/components/systems/index.js +13 -2
- package/lib/components/systems/scroll-anchors/UluScrollAnchors.vue +2 -1
- package/lib/components/systems/skeleton/UluShowSkeleton.vue +9 -8
- package/lib/components/systems/skeleton/UluSkeletonContent.vue +39 -43
- package/lib/components/systems/skeleton/UluSkeletonMedia.vue +4 -6
- package/lib/components/systems/skeleton/UluSkeletonText.vue +27 -0
- package/lib/components/systems/slider/UluImageSlideShow.vue +1 -1
- package/lib/components/systems/slider/UluSlideShow.vue +8 -3
- package/lib/components/systems/table-sticky/UluTableSticky.vue +8 -8
- package/lib/components/systems/table-sticky/UluTableStickyTable.vue +3 -3
- package/lib/composables/index.js +4 -1
- package/lib/composables/useDocumentTitle.js +61 -0
- package/lib/composables/usePagination.js +122 -0
- package/lib/composables/useRequiredInject.js +26 -0
- package/lib/index.js +1 -1
- package/lib/meta.js +14 -0
- package/lib/plugins/core/index.js +91 -0
- package/lib/plugins/index.js +1 -0
- package/lib/plugins/popovers/UluPopover.vue +3 -1
- package/lib/plugins/toast/UluToast.vue +2 -2
- package/lib/utils/index.js +2 -0
- package/lib/utils/{vue-router.js → router.js} +114 -30
- package/package.json +38 -13
- package/types/components/index.d.ts +2 -0
- package/types/components/index.d.ts.map +1 -0
- package/types/components/systems/facets/_mock-data.d.ts +18 -0
- package/types/components/systems/facets/_mock-data.d.ts.map +1 -0
- package/types/components/systems/facets/useFacets.d.ts +39 -0
- package/types/components/systems/facets/useFacets.d.ts.map +1 -0
- package/types/components/systems/index.d.ts +2 -0
- package/types/components/systems/index.d.ts.map +1 -0
- package/types/components/systems/scroll-anchors/symbols.d.ts +7 -0
- package/types/components/systems/scroll-anchors/symbols.d.ts.map +1 -0
- package/types/composables/index.d.ts +8 -0
- package/types/composables/index.d.ts.map +1 -0
- package/types/composables/useBreakpointManager.d.ts +8 -0
- package/types/composables/useBreakpointManager.d.ts.map +1 -0
- package/types/composables/useDocumentTitle.d.ts +22 -0
- package/types/composables/useDocumentTitle.d.ts.map +1 -0
- package/types/composables/useIcon.d.ts +6 -0
- package/types/composables/useIcon.d.ts.map +1 -0
- package/types/composables/useModifiers.d.ts +69 -0
- package/types/composables/useModifiers.d.ts.map +1 -0
- package/types/composables/usePageTitle.d.ts +19 -0
- package/types/composables/usePageTitle.d.ts.map +1 -0
- package/types/composables/usePagination.d.ts +25 -0
- package/types/composables/usePagination.d.ts.map +1 -0
- package/types/composables/useRequiredInject.d.ts +8 -0
- package/types/composables/useRequiredInject.d.ts.map +1 -0
- package/types/composables/useWindowResize.d.ts +6 -0
- package/types/composables/useWindowResize.d.ts.map +1 -0
- package/types/index.d.ts +5 -0
- package/types/index.d.ts.map +1 -0
- package/types/meta.d.ts +10 -0
- package/types/meta.d.ts.map +1 -0
- package/types/plugins/breakpoints/index.d.ts +2 -0
- package/types/plugins/breakpoints/index.d.ts.map +1 -0
- package/types/plugins/core/index.d.ts +3 -0
- package/types/plugins/core/index.d.ts.map +1 -0
- package/types/plugins/index.d.ts +6 -0
- package/types/plugins/index.d.ts.map +1 -0
- package/types/plugins/modals/api.d.ts +34 -0
- package/types/plugins/modals/api.d.ts.map +1 -0
- package/types/plugins/modals/index.d.ts +28 -0
- package/types/plugins/modals/index.d.ts.map +1 -0
- package/types/plugins/modals/useModals.d.ts +2 -0
- package/types/plugins/modals/useModals.d.ts.map +1 -0
- package/types/plugins/popovers/defaults.d.ts +14 -0
- package/types/plugins/popovers/defaults.d.ts.map +1 -0
- package/types/plugins/popovers/directive.d.ts +8 -0
- package/types/plugins/popovers/directive.d.ts.map +1 -0
- package/types/plugins/popovers/index.d.ts +7 -0
- package/types/plugins/popovers/index.d.ts.map +1 -0
- package/types/plugins/popovers/manager.d.ts +52 -0
- package/types/plugins/popovers/manager.d.ts.map +1 -0
- package/types/plugins/popovers/useFollow.d.ts +31 -0
- package/types/plugins/popovers/useFollow.d.ts.map +1 -0
- package/types/plugins/popovers/utils.d.ts +2 -0
- package/types/plugins/popovers/utils.d.ts.map +1 -0
- package/types/plugins/toast/defaults.d.ts +15 -0
- package/types/plugins/toast/defaults.d.ts.map +1 -0
- package/types/plugins/toast/index.d.ts +5 -0
- package/types/plugins/toast/index.d.ts.map +1 -0
- package/types/plugins/toast/store.d.ts +22 -0
- package/types/plugins/toast/store.d.ts.map +1 -0
- package/types/plugins/toast/useToast.d.ts +2 -0
- package/types/plugins/toast/useToast.d.ts.map +1 -0
- package/types/utils/dom.d.ts +8 -0
- package/types/utils/dom.d.ts.map +1 -0
- package/types/utils/index.d.ts +3 -0
- package/types/utils/index.d.ts.map +1 -0
- package/types/utils/placeholder.d.ts +8 -0
- package/types/utils/placeholder.d.ts.map +1 -0
- package/types/utils/router.d.ts +144 -0
- package/types/utils/router.d.ts.map +1 -0
- package/types/utils/vue-router.d.ts +122 -0
- package/types/utils/vue-router.d.ts.map +1 -0
- package/dist/index-CMGxe_M1.js +0 -6466
- package/lib/components/forms/UluCheckboxMenu.vue +0 -36
- package/lib/components/forms/UluFormDropzone.vue +0 -62
- package/lib/components/systems/facets/UluFacets.vue +0 -380
- package/lib/components/systems/skeleton/UluSkeletonTextInline.vue +0 -9
- package/lib/settings.js +0 -119
- package/lib/utils/placeholder.js +0 -6
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="
|
|
3
|
-
<
|
|
2
|
+
<div class="skeleton skeleton-block--media">
|
|
3
|
+
<UluIcon icon="type:image"/>
|
|
4
4
|
</div>
|
|
5
5
|
</template>
|
|
6
6
|
|
|
7
|
-
<script>
|
|
8
|
-
|
|
9
|
-
name: "SkeletonMedia",
|
|
10
|
-
};
|
|
7
|
+
<script setup>
|
|
8
|
+
import UluIcon from "../../elements/UluIcon.vue";
|
|
11
9
|
</script>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<span
|
|
3
|
+
class="skeleton skeleton--text"
|
|
4
|
+
:class="{
|
|
5
|
+
'skeleton--inline' : inline,
|
|
6
|
+
'skeleton--background-alt' : alt,
|
|
7
|
+
[`skeleton--width-${ width }`] : width
|
|
8
|
+
}"
|
|
9
|
+
></span>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script setup>
|
|
13
|
+
defineProps({
|
|
14
|
+
/**
|
|
15
|
+
* Inline modifier
|
|
16
|
+
*/
|
|
17
|
+
inline: Boolean,
|
|
18
|
+
/**
|
|
19
|
+
* Use alternate background color
|
|
20
|
+
*/
|
|
21
|
+
alt: Boolean,
|
|
22
|
+
/**
|
|
23
|
+
* Optional size (width) - should correspond with width setup in scss component
|
|
24
|
+
*/
|
|
25
|
+
width: String
|
|
26
|
+
});
|
|
27
|
+
</script>
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
@click="previous"
|
|
51
51
|
:disabled="!canScrollLeft"
|
|
52
52
|
>
|
|
53
|
-
<
|
|
53
|
+
<UluIcon class="slideshow__control-icon" icon="type:next"/>
|
|
54
54
|
</button>
|
|
55
55
|
</li>
|
|
56
56
|
<li class="slideshow__controls-item slideshow__controls-item--next">
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
@click="next"
|
|
61
61
|
:disabled="!canScrollRight"
|
|
62
62
|
>
|
|
63
|
-
<
|
|
63
|
+
<UluIcon class="slideshow__control-icon" icon="type:previous" />
|
|
64
64
|
</button>
|
|
65
65
|
</li>
|
|
66
66
|
</ul>
|
|
@@ -97,8 +97,13 @@
|
|
|
97
97
|
</template>
|
|
98
98
|
|
|
99
99
|
<script>
|
|
100
|
+
import UluIcon from "../../elements/UluIcon.vue";
|
|
100
101
|
export default {
|
|
101
102
|
name: 'SlideShow',
|
|
103
|
+
emits: ['slide-change'],
|
|
104
|
+
components: {
|
|
105
|
+
UluIcon
|
|
106
|
+
},
|
|
102
107
|
props: {
|
|
103
108
|
/**
|
|
104
109
|
* Should slides be focusable by tab key
|
|
@@ -278,7 +283,7 @@
|
|
|
278
283
|
this.$nextTick(() => {
|
|
279
284
|
const slide = this.getSlideByElement(entry.target);
|
|
280
285
|
slide.active = entry.isIntersecting;
|
|
281
|
-
this.$emit('
|
|
286
|
+
this.$emit('slide-change', { slide, track, nav });
|
|
282
287
|
});
|
|
283
288
|
});
|
|
284
289
|
};
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
pointerEvents: sizesCalculated ? 'auto' : 'none',
|
|
44
44
|
width: tableWidth
|
|
45
45
|
}"
|
|
46
|
-
@
|
|
46
|
+
@column-sorted="applySort"
|
|
47
47
|
>
|
|
48
48
|
<template v-for="(_, name) in $slots" v-slot:[name]="slotData">
|
|
49
49
|
<slot :name="name" v-bind="slotData" />
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
opacity: headerOpacityX,
|
|
67
67
|
pointerEvents: headerVisibleX ? 'auto' : 'none'
|
|
68
68
|
}"
|
|
69
|
-
@
|
|
69
|
+
@column-sorted="applySort"
|
|
70
70
|
>
|
|
71
71
|
<template v-for="(_, name) in $slots" v-slot:[name]="slotData">
|
|
72
72
|
<slot :name="name" v-bind="slotData" />
|
|
@@ -138,9 +138,9 @@
|
|
|
138
138
|
:getRowValue="getRowValue"
|
|
139
139
|
:getColumnTitle="getColumnTitle"
|
|
140
140
|
@vue:mounted="tableReady"
|
|
141
|
-
@
|
|
142
|
-
@
|
|
143
|
-
@
|
|
141
|
+
@actual-header-removed="headerRemoved"
|
|
142
|
+
@actual-header-added="headerAdded"
|
|
143
|
+
@column-sorted="applySort"
|
|
144
144
|
>
|
|
145
145
|
<template v-for="(_, name) in $slots" v-slot:[name]="slotData">
|
|
146
146
|
<slot :name="name" v-bind="slotData" />
|
|
@@ -167,7 +167,7 @@
|
|
|
167
167
|
opacity: headerOpacityX,
|
|
168
168
|
pointerEvents: headerVisibleX ? 'auto' : 'none'
|
|
169
169
|
}"
|
|
170
|
-
@
|
|
170
|
+
@column-sorted="applySort"
|
|
171
171
|
>
|
|
172
172
|
<template v-for="(_, name) in $slots" v-slot:[name]="slotData">
|
|
173
173
|
<slot :name="name" v-bind="slotData" />
|
|
@@ -180,7 +180,7 @@
|
|
|
180
180
|
import UluTableStickyTable from "./UluTableStickyTable.vue";
|
|
181
181
|
import { debounce } from "@ulu/utils/performance.js";
|
|
182
182
|
import { runAfterFramePaint } from "@ulu/utils/browser/performance.js";
|
|
183
|
-
import cloneDeep from "lodash.
|
|
183
|
+
import cloneDeep from "lodash-es/cloneDeep.js";
|
|
184
184
|
|
|
185
185
|
const arrayOfObjects = a => a.every(o => typeof o === "object");
|
|
186
186
|
const required = true;
|
|
@@ -443,7 +443,7 @@
|
|
|
443
443
|
} else {
|
|
444
444
|
column.sortApplied = true;
|
|
445
445
|
}
|
|
446
|
-
this.$emit("
|
|
446
|
+
this.$emit("column-sort", column);
|
|
447
447
|
},
|
|
448
448
|
onColumnResize() {
|
|
449
449
|
if (this.sizesPainted) {
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
:class="{
|
|
51
51
|
'table-sticky__sort-button--focused' : column.sortFocused,
|
|
52
52
|
}"
|
|
53
|
-
@click="$emit('
|
|
53
|
+
@click="$emit('column-sorted', column)"
|
|
54
54
|
@focus="handleSortFocus(column, true)"
|
|
55
55
|
@blur="handleSortFocus(column, false)"
|
|
56
56
|
:aria-pressed="column.sortApplied ? 'true' : 'false'"
|
|
@@ -199,9 +199,9 @@
|
|
|
199
199
|
const { id } = column;
|
|
200
200
|
const old = headerRefs[id];
|
|
201
201
|
if (old) {
|
|
202
|
-
this.$emit("
|
|
202
|
+
this.$emit("actual-header-removed", old);
|
|
203
203
|
}
|
|
204
|
-
this.$emit("
|
|
204
|
+
this.$emit("actual-header-added", el);
|
|
205
205
|
headerRefs[id] = el;
|
|
206
206
|
},
|
|
207
207
|
/**
|
package/lib/composables/index.js
CHANGED
|
@@ -7,4 +7,7 @@
|
|
|
7
7
|
export { useIcon } from './useIcon.js';
|
|
8
8
|
export { useModifiers } from './useModifiers.js';
|
|
9
9
|
export { useWindowResize } from './useWindowResize.js';
|
|
10
|
-
export {
|
|
10
|
+
export { useRequiredInject } from './useRequiredInject.js';
|
|
11
|
+
export { useBreakpointManager } from './useBreakpointManager.js';
|
|
12
|
+
export { usePagination } from './usePagination.js';
|
|
13
|
+
export { useDocumentTitle } from './useDocumentTitle.js';
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { reactive, watchEffect, onUnmounted, unref, computed } from "vue";
|
|
2
|
+
import { useHead as defaultUseHead } from "@unhead/vue";
|
|
3
|
+
import { useRoute as defaultUseRoute } from "vue-router";
|
|
4
|
+
import { getRouteTitle } from "../utils/router.js";
|
|
5
|
+
|
|
6
|
+
// A reactive map to store component-defined titles.
|
|
7
|
+
const componentTitles = reactive({});
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A composable to manage the document title.
|
|
11
|
+
*
|
|
12
|
+
* When called with a `title` option, it sets a dynamic title for the current page.
|
|
13
|
+
* This is for use within specific components.
|
|
14
|
+
*
|
|
15
|
+
* When called without a `title` option (typically in App.vue), it manages the
|
|
16
|
+
* document title for the whole app, using titles from components or route meta.
|
|
17
|
+
*
|
|
18
|
+
* @param {object} options
|
|
19
|
+
* @param {import('vue').Ref<string> | string} [options.title] - The dynamic title to set for the current page.
|
|
20
|
+
* @param {string} [options.titleTemplate='%s'] - The template for the document title, e.g., '%s | My Site'.
|
|
21
|
+
* @param {Function} [options.useRoute=defaultUseRoute] - Injectable `useRoute` for testing.
|
|
22
|
+
* @param {Function} [options.useHead=defaultUseHead] - Injectable `useHead` for testing.
|
|
23
|
+
*/
|
|
24
|
+
export function useDocumentTitle(options = {}) {
|
|
25
|
+
const {
|
|
26
|
+
title,
|
|
27
|
+
titleTemplate = "%s",
|
|
28
|
+
useRoute = defaultUseRoute,
|
|
29
|
+
useHead = defaultUseHead,
|
|
30
|
+
} = options;
|
|
31
|
+
|
|
32
|
+
const route = useRoute();
|
|
33
|
+
const path = route.path;
|
|
34
|
+
|
|
35
|
+
// --- Setter Mode ---
|
|
36
|
+
// If a title is provided, we're in "setter" mode, used within a component.
|
|
37
|
+
if (title !== undefined) {
|
|
38
|
+
watchEffect(() => {
|
|
39
|
+
componentTitles[path] = unref(title);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
onUnmounted(() => {
|
|
43
|
+
delete componentTitles[path];
|
|
44
|
+
});
|
|
45
|
+
return; // End execution for setter mode.
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// --- Manager Mode ---
|
|
49
|
+
// If no title is provided, we're in "manager" mode, used in App.vue.
|
|
50
|
+
const documentTitle = computed(() => {
|
|
51
|
+
const titleFromComponent = componentTitles[route.path];
|
|
52
|
+
const titleFromMeta = getRouteTitle(route, route);
|
|
53
|
+
const resolvedTitle = titleFromComponent || titleFromMeta;
|
|
54
|
+
|
|
55
|
+
return resolvedTitle ? titleTemplate.replace("%s", resolvedTitle) : "App";
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
useHead({
|
|
59
|
+
title: documentTitle,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { computed, watch } from "vue";
|
|
2
|
+
import { useRoute, useRouter } from "vue-router";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A Vue composable for handling pagination logic.
|
|
6
|
+
* It interacts with vue-router to keep the current page in the URL query string.
|
|
7
|
+
*
|
|
8
|
+
* @param {import('vue').Ref<Array<any>>} items - A ref containing the full list of items to be paginated.
|
|
9
|
+
* @param {number} itemsPerPage - The number of items to display per page.
|
|
10
|
+
* @returns {{
|
|
11
|
+
* currentPage: import('vue').ComputedRef<number>,
|
|
12
|
+
* totalPages: import('vue').ComputedRef<number>,
|
|
13
|
+
* paginatedItems: import('vue').ComputedRef<Array<any>>,
|
|
14
|
+
* pagerItems: import('vue').ComputedRef<object|null>,
|
|
15
|
+
* pagerEllipses: import('vue').ComputedRef<{previous: boolean, next: boolean}>
|
|
16
|
+
* }} - An object containing reactive properties for pagination.
|
|
17
|
+
*/
|
|
18
|
+
export function usePagination(items, itemsPerPage) {
|
|
19
|
+
const route = useRoute();
|
|
20
|
+
const router = useRouter();
|
|
21
|
+
|
|
22
|
+
const currentPage = computed(() => {
|
|
23
|
+
const page = parseInt(route.query.page || '1', 10);
|
|
24
|
+
return isNaN(page) || page < 1 ? 1 : page;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const totalPages = computed(() => {
|
|
28
|
+
if (!items.value || items.value.length === 0) return 1;
|
|
29
|
+
return Math.ceil(items.value.length / itemsPerPage);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
watch(totalPages, (newTotalPages) => {
|
|
33
|
+
if (currentPage.value > newTotalPages) {
|
|
34
|
+
router.push({ query: { ...route.query, page: newTotalPages } });
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const paginatedItems = computed(() => {
|
|
39
|
+
const start = (currentPage.value - 1) * itemsPerPage;
|
|
40
|
+
const end = start + itemsPerPage;
|
|
41
|
+
return items.value.slice(start, end);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const pagerItems = computed(() => {
|
|
45
|
+
if (totalPages.value <= 1) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const items = {
|
|
50
|
+
pages: {}
|
|
51
|
+
};
|
|
52
|
+
const page = currentPage.value;
|
|
53
|
+
const total = totalPages.value;
|
|
54
|
+
const maxPagesToShow = 5;
|
|
55
|
+
|
|
56
|
+
const createRoute = (p) => {
|
|
57
|
+
return { query: { ...route.query, page: p } };
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
if (page > 1) {
|
|
61
|
+
items.first = { href: createRoute(1) };
|
|
62
|
+
items.previous = { href: createRoute(page - 1) };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (page < total) {
|
|
66
|
+
items.next = { href: createRoute(page + 1) };
|
|
67
|
+
items.last = { href: createRoute(total) };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let startPage, endPage;
|
|
71
|
+
if (total <= maxPagesToShow) {
|
|
72
|
+
startPage = 1;
|
|
73
|
+
endPage = total;
|
|
74
|
+
} else {
|
|
75
|
+
const maxPagesBeforeCurrent = Math.floor(maxPagesToShow / 2);
|
|
76
|
+
const maxPagesAfterCurrent = Math.ceil(maxPagesToShow / 2) - 1;
|
|
77
|
+
if (page <= maxPagesBeforeCurrent) {
|
|
78
|
+
startPage = 1;
|
|
79
|
+
endPage = maxPagesToShow;
|
|
80
|
+
} else if (page + maxPagesAfterCurrent >= total) {
|
|
81
|
+
startPage = total - maxPagesToShow + 1;
|
|
82
|
+
endPage = total;
|
|
83
|
+
} else {
|
|
84
|
+
startPage = page - maxPagesBeforeCurrent;
|
|
85
|
+
endPage = page + maxPagesAfterCurrent;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for (let i = startPage; i <= endPage; i++) {
|
|
90
|
+
items.pages[i] = { href: createRoute(i) };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return items;
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const pagerEllipses = computed(() => {
|
|
97
|
+
const ellipses = { previous: false, next: false };
|
|
98
|
+
if (!pagerItems.value || !pagerItems.value.pages) return ellipses;
|
|
99
|
+
|
|
100
|
+
const pageKeys = Object.keys(pagerItems.value.pages).map(Number);
|
|
101
|
+
if (pageKeys.length === 0) return ellipses;
|
|
102
|
+
|
|
103
|
+
const firstPageInPager = Math.min(...pageKeys);
|
|
104
|
+
const lastPageInPager = Math.max(...pageKeys);
|
|
105
|
+
|
|
106
|
+
if (firstPageInPager > 1) {
|
|
107
|
+
ellipses.previous = true;
|
|
108
|
+
}
|
|
109
|
+
if (lastPageInPager < totalPages.value) {
|
|
110
|
+
ellipses.next = true;
|
|
111
|
+
}
|
|
112
|
+
return ellipses;
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
currentPage,
|
|
117
|
+
totalPages,
|
|
118
|
+
paginatedItems,
|
|
119
|
+
pagerItems,
|
|
120
|
+
pagerEllipses
|
|
121
|
+
};
|
|
122
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { inject } from 'vue';
|
|
2
|
+
import { injectRegistry } from "../meta.js";
|
|
3
|
+
|
|
4
|
+
// A unique sentinel object to detect if a value was provided.
|
|
5
|
+
// This is used to differentiate between a provider not existing vs.
|
|
6
|
+
// a provider explicitly giving a `null` value.
|
|
7
|
+
const NOT_FOUND = {};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Injects a dependency from a plugin (or other required inject) and throws an error if it's not available.
|
|
11
|
+
*
|
|
12
|
+
* @param {string} key - The injection key (e.g., 'uluBreakpointManager').
|
|
13
|
+
* @returns The injected value.
|
|
14
|
+
*/
|
|
15
|
+
export function useRequiredInject(key) {
|
|
16
|
+
const dependency = inject(key, NOT_FOUND);
|
|
17
|
+
|
|
18
|
+
if (dependency === NOT_FOUND) {
|
|
19
|
+
const plugin = injectRegistry[key] || '';
|
|
20
|
+
const pluginInfo = plugin ? ` from the '${ plugin }' plugin` : "";
|
|
21
|
+
const action = plugin ? "Please install missing plugin." : "";
|
|
22
|
+
throw new Error(`Required inject: '${ key }'${ pluginInfo } was not provided. ${ action }`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return dependency;
|
|
26
|
+
}
|
package/lib/index.js
CHANGED
package/lib/meta.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module lib/meta
|
|
3
|
+
* @description A central lookup for meta like info in the system
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export const injectRegistry = {
|
|
7
|
+
'uluCore': 'Core',
|
|
8
|
+
'uluIsMobile': 'Breakpoints',
|
|
9
|
+
'uluBreakpointActive': 'Breakpoints',
|
|
10
|
+
'uluBreakpointDirection': 'Breakpoints',
|
|
11
|
+
'uluBreakpointManager': 'Breakpoints',
|
|
12
|
+
'uluModals': 'Modals',
|
|
13
|
+
'uluToast': 'Toast',
|
|
14
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module plugins/core/index.js
|
|
3
|
+
* @description Core plugin for managing shared configuration for the library.
|
|
4
|
+
*/
|
|
5
|
+
import { reactive } from 'vue';
|
|
6
|
+
|
|
7
|
+
const defaults = {
|
|
8
|
+
fontAwesomeStatic: false,
|
|
9
|
+
iconComponent: null,
|
|
10
|
+
iconPropResolver: (definition) => ({ icon: definition }),
|
|
11
|
+
iconsByType: {
|
|
12
|
+
danger: "fas fa-triangle-exclamation",
|
|
13
|
+
warning: "fas fa-circle-exclamation",
|
|
14
|
+
info: "fas fa-circle-info",
|
|
15
|
+
success: "fas fa-circle-check",
|
|
16
|
+
externalLink: "fas fa-arrow-up-right-from-square",
|
|
17
|
+
close: "fas fa-xmark",
|
|
18
|
+
expand: "fas fa-plus",
|
|
19
|
+
collapse: "fas fa-minus",
|
|
20
|
+
resizeHorizontal: "fas fa-grip-lines-vertical",
|
|
21
|
+
resizeVertical: "fas fa-grip-lines",
|
|
22
|
+
resizeBoth: "fas fa-grip",
|
|
23
|
+
ellipsis: "fas fa-ellipsis",
|
|
24
|
+
pathSeparator: "fas fa-chevron-right",
|
|
25
|
+
image: "fas fa-image",
|
|
26
|
+
file: "fas fa-file",
|
|
27
|
+
next: "fas fa-chevron-left",
|
|
28
|
+
previous: "fas fa-chevron-right"
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const iconKeys = Object.keys(defaults.iconsByType);
|
|
33
|
+
|
|
34
|
+
export default function install(app, userSettings = {}) {
|
|
35
|
+
// A single reactive object for all settings
|
|
36
|
+
const settings = reactive({ ...defaults });
|
|
37
|
+
|
|
38
|
+
// Separate icon overrides from other options to handle them safely
|
|
39
|
+
const { iconsByType: iconOverrides, ...otherOptions } = userSettings || {};
|
|
40
|
+
|
|
41
|
+
// Merge any user-provided options during installation
|
|
42
|
+
if (otherOptions) {
|
|
43
|
+
Object.assign(settings, otherOptions);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const api = {
|
|
47
|
+
// Methods to interact with settings
|
|
48
|
+
getSettings() {
|
|
49
|
+
return settings;
|
|
50
|
+
},
|
|
51
|
+
getDefaultSettings() {
|
|
52
|
+
return { ...defaults };
|
|
53
|
+
},
|
|
54
|
+
updateSettings(changes) {
|
|
55
|
+
return Object.assign(settings, changes);
|
|
56
|
+
},
|
|
57
|
+
getSetting(key) {
|
|
58
|
+
if (!settings.hasOwnProperty(key)) {
|
|
59
|
+
console.warn(`Attempted to access non-existent setting: ${key}`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
return settings[key];
|
|
63
|
+
},
|
|
64
|
+
updateSetting(key, value) {
|
|
65
|
+
if (typeof key !== "string") {
|
|
66
|
+
throw new Error("Expected key to be string");
|
|
67
|
+
}
|
|
68
|
+
settings[key] = value;
|
|
69
|
+
},
|
|
70
|
+
getIcon(type) {
|
|
71
|
+
const icons = settings.iconsByType;
|
|
72
|
+
if (!icons[type]) {
|
|
73
|
+
throw new Error(`Icon type "${type}" not found!`);
|
|
74
|
+
}
|
|
75
|
+
return icons[type];
|
|
76
|
+
},
|
|
77
|
+
setIcon(type, definition) {
|
|
78
|
+
settings.iconsByType[type] = definition;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Apply any individual icon overrides passed during installation
|
|
83
|
+
if (iconOverrides) {
|
|
84
|
+
for (const [type, definition] of Object.entries(iconOverrides)) {
|
|
85
|
+
api.setIcon(type, definition);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
app.provide('uluCore', api);
|
|
90
|
+
app.config.globalProperties.$uluCore = api;
|
|
91
|
+
}
|
package/lib/plugins/index.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
type="button"
|
|
5
5
|
ref="trigger"
|
|
6
6
|
@click="toggle"
|
|
7
|
+
:id="triggerId"
|
|
7
8
|
:disabled="disabled"
|
|
8
9
|
:class="[
|
|
9
10
|
{ [activeClass] : isOpen },
|
|
@@ -27,7 +28,7 @@
|
|
|
27
28
|
},
|
|
28
29
|
classes.content,
|
|
29
30
|
]"
|
|
30
|
-
:aria-
|
|
31
|
+
:aria-labelledby="triggerId"
|
|
31
32
|
:id="id"
|
|
32
33
|
:style="floatingStyles"
|
|
33
34
|
:data-placement="placement"
|
|
@@ -95,6 +96,7 @@
|
|
|
95
96
|
});
|
|
96
97
|
|
|
97
98
|
const id = newUid();
|
|
99
|
+
const triggerId = newUid();
|
|
98
100
|
const config = Object.assign({}, defaults.popover, props.config);
|
|
99
101
|
const isOpen = ref(props.startOpen || false);
|
|
100
102
|
const trigger = ref(null);
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
>
|
|
14
14
|
<div v-if="toast.icon || $slots.icon" class="toast__icon" :class="classes.icon">
|
|
15
15
|
<slot name="icon" :toast="toast">
|
|
16
|
-
<
|
|
16
|
+
<UluIcon v-if="toast.icon" :icon="toast.icon"/>
|
|
17
17
|
</slot>
|
|
18
18
|
</div>
|
|
19
19
|
<div class="toast__content" :class="classes.content">
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
</button>
|
|
44
44
|
</div>
|
|
45
45
|
<button class="toast__close" :class="classes.closeButton" @click="toast.close">
|
|
46
|
-
<UluIcon
|
|
46
|
+
<UluIcon :icon="'type:close'"/>
|
|
47
47
|
</button>
|
|
48
48
|
</div>
|
|
49
49
|
</template>
|