@ulu/frontend-vue 0.1.0-beta.2 → 0.1.0-beta.21
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-ClT9bfZm.js → breakpoints-DOXmgVoG.js} +1 -1
- package/dist/frontend-vue.css +1 -1
- package/dist/frontend-vue.js +75 -73
- package/dist/index-BpmkfeZb.js +6671 -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/UluCheckboxMenu.vue +32 -31
- 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/index.js +1 -1
- 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 +1 -2
- 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/UluFacetsFilters.vue +73 -0
- package/lib/components/systems/facets/UluFacetsList.vue +13 -14
- package/lib/components/systems/facets/UluFacetsResults.vue +57 -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 +221 -0
- package/lib/components/systems/index.js +10 -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 +3 -1
- package/lib/composables/useDocumentTitle.js +47 -0
- package/lib/composables/usePageTitle.js +37 -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/toast/UluToast.vue +2 -2
- package/lib/utils/index.js +2 -0
- package/lib/utils/{vue-router.js → router.js} +106 -11
- package/package.json +37 -14
- 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 +7 -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 +18 -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 +141 -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/frontend-vue.umd.cjs +0 -561
- package/dist/index-P5Rwl_Dl.js +0 -7263
- 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
|
@@ -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
|
@@ -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>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { getPageTitle } from "../composables/usePageTitle.js";
|
|
1
2
|
/**
|
|
2
3
|
* This Module Creates Menus from route or router config
|
|
3
4
|
* - Note: Functions prefixed with "$" work with $route objects (running application, provided by vue-router ie $router, useRoute, etc),
|
|
4
|
-
* @module
|
|
5
|
+
* @module router-utils
|
|
5
6
|
*/
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -21,21 +22,56 @@
|
|
|
21
22
|
*/
|
|
22
23
|
export function createBaseMenu(routes, options) {
|
|
23
24
|
const defaults = {
|
|
24
|
-
qualifier
|
|
25
|
+
qualifier(route, parentPath) {
|
|
26
|
+
if (!parentPath) {
|
|
27
|
+
return isStaticBaseRoute(route);
|
|
28
|
+
} else {
|
|
29
|
+
return isStaticRoute(route);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
25
32
|
sort: sortByWeight,
|
|
26
|
-
item: {}
|
|
33
|
+
item: {},
|
|
34
|
+
includeChildren: false
|
|
27
35
|
};
|
|
28
36
|
const opts = Object.assign({}, defaults, options);
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
const getItemPath = (r, parentPath) => parentPath ? `${ parentPath }/${ r.path }` : r.path;
|
|
38
|
+
const toMenuItems = (currentRoutes, parentPath = null) => {
|
|
39
|
+
return currentRoutes
|
|
40
|
+
.filter(r => opts.qualifier(r, parentPath))
|
|
41
|
+
.map(r => {
|
|
42
|
+
// Need to grab meta from child but use the parent path
|
|
43
|
+
const menuRoute = r.children ? getChildIndexRoute(r.children) : r;
|
|
44
|
+
const children = r.children ? r.children.filter(child => child.path !== "") : false;
|
|
45
|
+
const item = createMenuItem(menuRoute, getItemPath(r, parentPath), opts.item);
|
|
46
|
+
if (opts.includeChildren && children.length) {
|
|
47
|
+
item.children = toMenuItems(children, item.path);
|
|
48
|
+
}
|
|
49
|
+
return item;
|
|
50
|
+
})
|
|
51
|
+
.sort(opts.sort);
|
|
52
|
+
};
|
|
53
|
+
return toMenuItems(routes);
|
|
37
54
|
}
|
|
38
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Returns menu flat (no parent children)
|
|
58
|
+
*/
|
|
59
|
+
export function flattenMenu(menu) {
|
|
60
|
+
function flatten(items) {
|
|
61
|
+
const result = [];
|
|
62
|
+
for (const item of items) {
|
|
63
|
+
const newItem = { ...item };
|
|
64
|
+
delete newItem.children;
|
|
65
|
+
result.push(newItem);
|
|
66
|
+
if (item.children) {
|
|
67
|
+
result.push(...flatten(item.children));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
return flatten(menu);
|
|
73
|
+
}
|
|
74
|
+
|
|
39
75
|
/**
|
|
40
76
|
* Print out a section's menu based on path
|
|
41
77
|
* @param {*} routes All routes
|
|
@@ -216,4 +252,63 @@ export function $createSectionMenu(route, options) {
|
|
|
216
252
|
.filter(includeIndex(opts.includeIndex))
|
|
217
253
|
.map(r => createMenuItem(r, `${ parent.path }/${ r.path }`, opts.item))
|
|
218
254
|
.sort(opts.sort);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* For a given $route, this will generate a breadcrumb trail.
|
|
259
|
+
* It iterates through `route.matched` to build the trail.
|
|
260
|
+
* - Prioritizes titles set via the `usePageTitle` composable for the current page.
|
|
261
|
+
* - Falls back to `meta.title` (string or function).
|
|
262
|
+
* - Skips routes where `meta.breadcrumb` is set to `false`.
|
|
263
|
+
* - Avoids duplicate crumbs for nested routes with empty paths.
|
|
264
|
+
* @param {Object} route The Vue Router `$route` object.
|
|
265
|
+
* @returns {Array.<{title: String, to: Object, current: Boolean}>} An array of breadcrumb items.
|
|
266
|
+
*/
|
|
267
|
+
export function $createBreadcrumb(route) {
|
|
268
|
+
const { matched, path: currentPath } = route;
|
|
269
|
+
let prevPath;
|
|
270
|
+
|
|
271
|
+
const crumbs = matched.reduce((arr, match, index) => {
|
|
272
|
+
// Skip routes configured to be hidden from breadcrumbs
|
|
273
|
+
if (match.meta?.breadcrumb === false) {
|
|
274
|
+
return arr;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Avoid duplicates from child routes with empty paths
|
|
278
|
+
if (match.path === prevPath) {
|
|
279
|
+
return arr;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const isLast = index === matched.length - 1;
|
|
283
|
+
let title;
|
|
284
|
+
|
|
285
|
+
// 1. Prioritize component-defined title for the current page
|
|
286
|
+
if (isLast) {
|
|
287
|
+
title = getPageTitle(currentPath);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// 2. Fallback to meta.title (function or string)
|
|
291
|
+
if (!title) {
|
|
292
|
+
const metaTitle = match.meta?.title;
|
|
293
|
+
if (typeof metaTitle === 'function') {
|
|
294
|
+
title = metaTitle(route);
|
|
295
|
+
} else {
|
|
296
|
+
title = metaTitle;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// 3. Final fallback
|
|
301
|
+
title = title || 'Missing Title';
|
|
302
|
+
|
|
303
|
+
arr.push({
|
|
304
|
+
title,
|
|
305
|
+
to: { path: isLast ? currentPath : match.path },
|
|
306
|
+
current: isLast,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
prevPath = match.path;
|
|
310
|
+
return arr;
|
|
311
|
+
}, []);
|
|
312
|
+
|
|
313
|
+
return crumbs;
|
|
219
314
|
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ulu/frontend-vue",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.0-beta.21",
|
|
4
|
+
"description": "A modular and tree-shakeable Vue 3 component library for the Ulu frontend",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"files"
|
|
6
|
+
"files": [
|
|
7
7
|
"lib",
|
|
8
|
-
"dist"
|
|
8
|
+
"dist",
|
|
9
|
+
"types"
|
|
9
10
|
],
|
|
10
11
|
"module": "./lib/index.js",
|
|
11
12
|
"exports": {
|
|
@@ -18,17 +19,36 @@
|
|
|
18
19
|
"./*": "./lib/*",
|
|
19
20
|
"./scss": "./lib/_index.scss"
|
|
20
21
|
},
|
|
22
|
+
"typesVersions": {
|
|
23
|
+
"*": {
|
|
24
|
+
"lib/*": [
|
|
25
|
+
"./types/*"
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
},
|
|
21
29
|
"repository": {
|
|
22
30
|
"type": "git",
|
|
23
31
|
"url": "git+https://github.com/Jscherbe/frontend-vue.git"
|
|
24
32
|
},
|
|
25
33
|
"scripts": {
|
|
26
34
|
"dev": "storybook dev -p 6006",
|
|
27
|
-
"docs:build": "storybook build -o docs
|
|
35
|
+
"docs:build": "storybook build -o docs",
|
|
28
36
|
"build": "vite build",
|
|
29
|
-
"
|
|
37
|
+
"types": "npx tsc",
|
|
38
|
+
"deploy": "npm run types && npm run build && npm run docs:build",
|
|
39
|
+
"update-contexts": "rm -rf .contexts && mkdir -p .contexts/frontend && cp -R node_modules/@ulu/frontend/scss node_modules/@ulu/frontend/js .contexts/frontend/"
|
|
30
40
|
},
|
|
31
|
-
"keywords": [
|
|
41
|
+
"keywords": [
|
|
42
|
+
"vue",
|
|
43
|
+
"vue3",
|
|
44
|
+
"ui",
|
|
45
|
+
"components",
|
|
46
|
+
"component-library",
|
|
47
|
+
"design-system",
|
|
48
|
+
"ulu",
|
|
49
|
+
"frontend",
|
|
50
|
+
"scss"
|
|
51
|
+
],
|
|
32
52
|
"author": "Joseph Scherben <jscherbe@gmail.com>",
|
|
33
53
|
"license": "MIT",
|
|
34
54
|
"bugs": {
|
|
@@ -37,29 +57,30 @@
|
|
|
37
57
|
"homepage": "https://github.com/Jscherbe/frontend-vue#readme",
|
|
38
58
|
"peerDependencies": {
|
|
39
59
|
"@headlessui/vue": "^1.7.23",
|
|
40
|
-
"@ulu/frontend": "^0.1.0-beta.
|
|
60
|
+
"@ulu/frontend": "^0.1.0-beta.102",
|
|
41
61
|
"vue": "^3.5.17",
|
|
42
|
-
"vue-router": "^4.5.1"
|
|
62
|
+
"vue-router": "^4.5.1",
|
|
63
|
+
"@unhead/vue": "^2.0.11"
|
|
43
64
|
},
|
|
44
65
|
"optionalDependencies": {
|
|
45
|
-
"gsap": "^3.13.0",
|
|
46
66
|
"fuse.js": "^6.6.2",
|
|
67
|
+
"gsap": "^3.13.0",
|
|
47
68
|
"vue3-dropzone": "^2.2.1"
|
|
48
69
|
},
|
|
49
70
|
"dependencies": {
|
|
50
71
|
"@floating-ui/vue": "^1.1.8",
|
|
51
72
|
"@ulu/utils": "^0.0.30",
|
|
52
|
-
"lodash"
|
|
73
|
+
"lodash-es": "^4.17.21"
|
|
53
74
|
},
|
|
54
75
|
"devDependencies": {
|
|
55
76
|
"@fortawesome/fontawesome-svg-core": "^6.7.2",
|
|
56
77
|
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
|
57
78
|
"@fortawesome/vue-fontawesome": "^3.0.8",
|
|
58
79
|
"@storybook/addon-docs": "^9.1.1",
|
|
59
|
-
"@storybook/addon-links": "^9.1.1",
|
|
60
80
|
"@storybook/addon-essentials": "^9.0.0-alpha.12",
|
|
81
|
+
"@storybook/addon-links": "^9.1.1",
|
|
61
82
|
"@storybook/vue3-vite": "^9.1.1",
|
|
62
|
-
"@ulu/frontend": "^0.1.0-beta.
|
|
83
|
+
"@ulu/frontend": "^0.1.0-beta.102",
|
|
63
84
|
"@vitejs/plugin-vue": "^6.0.0",
|
|
64
85
|
"ollama": "^0.5.16",
|
|
65
86
|
"react": "^19.1.1",
|
|
@@ -67,8 +88,10 @@
|
|
|
67
88
|
"sass-embedded": "^1.89.2",
|
|
68
89
|
"storybook": "^9.1.1",
|
|
69
90
|
"storybook-addon-vue-mdx": "^2.0.2",
|
|
91
|
+
"typescript": "^5.3.3",
|
|
70
92
|
"vite": "^7.0.0",
|
|
71
|
-
"vue-router": "^4.5.1"
|
|
93
|
+
"vue-router": "^4.5.1",
|
|
94
|
+
"@unhead/vue": "^2.0.11"
|
|
72
95
|
},
|
|
73
96
|
"volta": {
|
|
74
97
|
"node": "22.17.0"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/components/index.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const initialMockFacets: {
|
|
2
|
+
name: string;
|
|
3
|
+
uid: string;
|
|
4
|
+
open: boolean;
|
|
5
|
+
children: {
|
|
6
|
+
uid: string;
|
|
7
|
+
label: string;
|
|
8
|
+
}[];
|
|
9
|
+
}[];
|
|
10
|
+
export const mockItems: {
|
|
11
|
+
id: number;
|
|
12
|
+
title: string;
|
|
13
|
+
description: string;
|
|
14
|
+
category: string[];
|
|
15
|
+
author: string[];
|
|
16
|
+
date: Date;
|
|
17
|
+
}[];
|
|
18
|
+
//# sourceMappingURL=_mock-data.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_mock-data.d.ts","sourceRoot":"","sources":["../../../../lib/components/systems/facets/_mock-data.js"],"names":[],"mappings":"AAAA;;;;;;;;IAwBE;AAEF;;;;;;;IAaE"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A composable for handling client-side faceted search, filtering, and sorting.
|
|
3
|
+
* @param {import('vue').Ref<Array<Object>>} allItems - A Vue ref containing the full list of items to be processed.
|
|
4
|
+
* @param {Object} options - Configuration options for the composable.
|
|
5
|
+
* @param {Array} [options.initialFacets] - The initial configuration for the facets. Can be generated automatically if `facetFields` is provided.
|
|
6
|
+
* @param {Array} [options.facetFields] - A simpler configuration to automatically generate facets from items. Each item can have `uid`, `name`, `open`, `getValue` and `getLabel`.
|
|
7
|
+
* @param {String} [options.initialSearchValue=''] - The initial value for the search input.
|
|
8
|
+
* @param {String} [options.initialSortType='az'] - The initial sort type.
|
|
9
|
+
* @param {Boolean} [options.noDefaultSorts=false] - If true, the default 'A-Z' and 'Z-A' sorts will not be included.
|
|
10
|
+
* @param {Object} [options.extraSortTypes={}] - Additional sort types to be merged with the default ones.
|
|
11
|
+
* @param {Object} [options.searchOptions={}] - Configuration options for Fuse.js.
|
|
12
|
+
* @param {Function} [options.getItemFacet] - A function to retrieve facet information from an item. Should always return an array of values.
|
|
13
|
+
* @param {Function} [options.getSortValue] - A function to get the value to sort by from an item.
|
|
14
|
+
*/
|
|
15
|
+
export function useFacets(allItems: import("vue").Ref<Array<any>>, options?: {
|
|
16
|
+
initialFacets?: any[];
|
|
17
|
+
facetFields?: any[];
|
|
18
|
+
initialSearchValue?: string;
|
|
19
|
+
initialSortType?: string;
|
|
20
|
+
noDefaultSorts?: boolean;
|
|
21
|
+
extraSortTypes?: any;
|
|
22
|
+
searchOptions?: any;
|
|
23
|
+
getItemFacet?: Function;
|
|
24
|
+
getSortValue?: Function;
|
|
25
|
+
}): {
|
|
26
|
+
facets: import("vue").Ref<any, any>;
|
|
27
|
+
searchValue: import("vue").Ref<string, string>;
|
|
28
|
+
selectedSort: import("vue").Ref<string, string>;
|
|
29
|
+
sortTypes: import("vue").ComputedRef<any>;
|
|
30
|
+
displayItems: import("vue").ComputedRef<any>;
|
|
31
|
+
selectedFacets: import("vue").ComputedRef<any[]>;
|
|
32
|
+
clearFilters: () => void;
|
|
33
|
+
handleFacetChange: ({ groupUid, facetUid, selected }: {
|
|
34
|
+
groupUid: any;
|
|
35
|
+
facetUid: any;
|
|
36
|
+
selected: any;
|
|
37
|
+
}) => void;
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=useFacets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFacets.d.ts","sourceRoot":"","sources":["../../../../lib/components/systems/facets/useFacets.js"],"names":[],"mappings":"AAsCA;;;;;;;;;;;;;GAaG;AACH,oCAZW,OAAO,KAAK,EAAE,GAAG,CAAC,KAAK,KAAQ,CAAC,YAExC;IAAwB,aAAa;IACb,WAAW;IACV,kBAAkB;IAClB,eAAe;IACd,cAAc;IACf,cAAc;IACd,aAAa;IACX,YAAY;IACZ,YAAY;CACzC;;;;;;;;;;;;;EAyKA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../lib/components/systems/index.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symbols.d.ts","sourceRoot":"","sources":["../../../../lib/components/systems/scroll-anchors/symbols.js"],"names":[],"mappings":"AAAA;;GAEG;AACH,2BAAiC;AACjC,6BAAmC;AACnC,2BAAiC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { useIcon } from "./useIcon.js";
|
|
2
|
+
export { useModifiers } from "./useModifiers.js";
|
|
3
|
+
export { useWindowResize } from "./useWindowResize.js";
|
|
4
|
+
export { useRequiredInject } from "./useRequiredInject.js";
|
|
5
|
+
export { useBreakpointManager } from "./useBreakpointManager.js";
|
|
6
|
+
export { usePagination } from "./usePagination.js";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/composables/index.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Use the CssBreakpoints module in Vue
|
|
3
|
+
* - Normally use only once, unless you have different sets of breakpoints
|
|
4
|
+
* @param {Object} options Configuration options overrides
|
|
5
|
+
* @return {Object} { manager, active, direction } (all are null in SSR environment until init)
|
|
6
|
+
*/
|
|
7
|
+
export function useBreakpointManager(options: any): any;
|
|
8
|
+
//# sourceMappingURL=useBreakpointManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBreakpointManager.d.ts","sourceRoot":"","sources":["../../lib/composables/useBreakpointManager.js"],"names":[],"mappings":"AAoBA;;;;;GAKG;AACH,wDAyCC"}
|