@ulu/frontend-vue 0.1.0-beta.1 → 0.1.0-beta.11
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-DS8rH5b6.js} +1 -1
- package/dist/frontend-vue.js +47 -51
- package/dist/index-C5pNSAJH.js +6579 -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/UluButton.vue +2 -2
- package/lib/components/elements/UluButtonVerbose.vue +119 -0
- package/lib/components/elements/UluExternalLink.vue +1 -2
- package/lib/components/elements/UluIcon.vue +11 -16
- package/lib/components/elements/UluTag.vue +1 -1
- 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 +1 -1
- 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/_mock-data.js +40 -0
- package/lib/components/systems/facets/useFacets.js +140 -0
- package/lib/components/systems/index.js +4 -1
- package/lib/components/systems/table-sticky/UluTableSticky.vue +1 -1
- package/lib/composables/index.js +1 -0
- package/lib/composables/useRequiredInject.js +26 -0
- package/lib/index.js +0 -1
- package/lib/meta.js +14 -0
- package/lib/plugins/core/index.js +87 -0
- package/lib/plugins/index.js +1 -0
- package/lib/plugins/toast/UluToast.vue +1 -1
- package/package.json +31 -9
- package/types/components/index.d.ts +2 -0
- package/types/components/index.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 +6 -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/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/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 +4 -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/placeholder.d.ts +8 -0
- package/types/utils/placeholder.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/settings.js +0 -119
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="UluFacetsResults">
|
|
3
|
+
<transition-group
|
|
4
|
+
v-if="items.length"
|
|
5
|
+
:tag="tag"
|
|
6
|
+
:name="transitionName"
|
|
7
|
+
class="UluFacetsResults__list"
|
|
8
|
+
>
|
|
9
|
+
<li
|
|
10
|
+
class="UluFacetsResults__item"
|
|
11
|
+
v-for="(item, index) in items"
|
|
12
|
+
:key="item.id || index"
|
|
13
|
+
>
|
|
14
|
+
<slot name="item" :item="item" :index="index"></slot>
|
|
15
|
+
</li>
|
|
16
|
+
</transition-group>
|
|
17
|
+
<div v-else class="UluFacetsResults__empty">
|
|
18
|
+
<slot name="empty">
|
|
19
|
+
<p>No matching items found.</p>
|
|
20
|
+
</slot>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<script setup>
|
|
26
|
+
defineProps({
|
|
27
|
+
items: {
|
|
28
|
+
type: Array,
|
|
29
|
+
required: true
|
|
30
|
+
},
|
|
31
|
+
tag: {
|
|
32
|
+
type: String,
|
|
33
|
+
default: 'ul'
|
|
34
|
+
},
|
|
35
|
+
transitionName: {
|
|
36
|
+
type: String,
|
|
37
|
+
default: 'UluFacetsFade'
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<style lang="scss">
|
|
43
|
+
.UluFacetsResults__list {
|
|
44
|
+
list-style: none;
|
|
45
|
+
padding: 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.UluFacetsFade-enter-active,
|
|
49
|
+
.UluFacetsFade-leave-active {
|
|
50
|
+
transition: opacity 0.25s ease;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.UluFacetsFade-enter-from,
|
|
54
|
+
.UluFacetsFade-leave-to {
|
|
55
|
+
opacity: 0;
|
|
56
|
+
}
|
|
57
|
+
</style>
|
|
@@ -10,58 +10,35 @@
|
|
|
10
10
|
type="text"
|
|
11
11
|
:placeholder="placeholder"
|
|
12
12
|
>
|
|
13
|
-
<!-- <button
|
|
14
|
-
v-if="value"
|
|
15
|
-
:class="classes.searchClear"
|
|
16
|
-
@click="clear"
|
|
17
|
-
:aria-label="classes.searchClearIcon ? 'Clear Search' : false"
|
|
18
|
-
type="button"
|
|
19
|
-
>
|
|
20
|
-
<span
|
|
21
|
-
v-if="classes.searchClearIcon"
|
|
22
|
-
:class="classes.searchClearIcon"
|
|
23
|
-
aria-hidden="true"
|
|
24
|
-
></span>
|
|
25
|
-
<span v-else>
|
|
26
|
-
Clear
|
|
27
|
-
</span>
|
|
28
|
-
</button> -->
|
|
29
13
|
</div>
|
|
30
14
|
</template>
|
|
31
15
|
|
|
32
|
-
<script>
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
placeholder: {
|
|
40
|
-
type: String,
|
|
41
|
-
default: "Keywords…"
|
|
42
|
-
}
|
|
43
|
-
},
|
|
44
|
-
data() {
|
|
45
|
-
return {
|
|
46
|
-
id: `facet-view-keyword-${ ++uid }`
|
|
47
|
-
}
|
|
16
|
+
<script setup>
|
|
17
|
+
import { computed } from 'vue';
|
|
18
|
+
|
|
19
|
+
const props = defineProps({
|
|
20
|
+
classes: {
|
|
21
|
+
type: Object,
|
|
22
|
+
default: () => ({})
|
|
48
23
|
},
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
24
|
+
modelValue: String,
|
|
25
|
+
placeholder: {
|
|
26
|
+
type: String,
|
|
27
|
+
default: "Keywords…"
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const emit = defineEmits(['update:modelValue']);
|
|
32
|
+
|
|
33
|
+
let uid = 0;
|
|
34
|
+
const id = `facet-view-keyword-${++uid}`;
|
|
35
|
+
|
|
36
|
+
const localValue = computed({
|
|
37
|
+
get() {
|
|
38
|
+
return props.modelValue;
|
|
58
39
|
},
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
// this.value = null;
|
|
62
|
-
// this.applied = false;
|
|
63
|
-
// this.$emit("search", null);
|
|
64
|
-
}
|
|
40
|
+
set(val) {
|
|
41
|
+
emit('update:modelValue', val);
|
|
65
42
|
}
|
|
66
|
-
}
|
|
67
|
-
</script>
|
|
43
|
+
});
|
|
44
|
+
</script>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="UluFacetsSidebarLayout">
|
|
3
|
+
<div class="UluFacetsSidebarLayout__header">
|
|
4
|
+
<slot name="header"></slot>
|
|
5
|
+
</div>
|
|
6
|
+
<div class="UluFacetsSidebarLayout__body">
|
|
7
|
+
<div class="UluFacetsSidebarLayout__sidebar">
|
|
8
|
+
<slot name="sidebar"></slot>
|
|
9
|
+
</div>
|
|
10
|
+
<div class="UluFacetsSidebarLayout__main">
|
|
11
|
+
<slot name="main"></slot>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
</template>
|
|
16
|
+
|
|
17
|
+
<script setup>
|
|
18
|
+
// This component is purely for layout, no logic needed.
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<style lang="scss">
|
|
22
|
+
.UluFacetsSidebarLayout__body {
|
|
23
|
+
display: grid;
|
|
24
|
+
grid-template-columns: 1fr;
|
|
25
|
+
gap: 2rem;
|
|
26
|
+
|
|
27
|
+
@media (min-width: 768px) {
|
|
28
|
+
grid-template-columns: 250px 1fr;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
</style>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="UluFacetsSort" :class="classes.sortForm">
|
|
3
|
+
<label
|
|
4
|
+
:for="sortId"
|
|
5
|
+
:class="classes.sortFormLabel"
|
|
6
|
+
>
|
|
7
|
+
<slot>Sort:</slot>
|
|
8
|
+
</label>
|
|
9
|
+
<select
|
|
10
|
+
:value="modelValue"
|
|
11
|
+
@change="emit('update:modelValue', $event.target.value)"
|
|
12
|
+
:id="sortId"
|
|
13
|
+
:class="classes.sortFormSelect"
|
|
14
|
+
>
|
|
15
|
+
<option v-for="(item, key) in sortTypes" :value="key" :key="key">
|
|
16
|
+
{{ item.text }}
|
|
17
|
+
</option>
|
|
18
|
+
</select>
|
|
19
|
+
</div>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script setup>
|
|
23
|
+
import { ref } from 'vue';
|
|
24
|
+
|
|
25
|
+
let idCounter = 0;
|
|
26
|
+
|
|
27
|
+
defineProps({
|
|
28
|
+
classes: {
|
|
29
|
+
type: Object,
|
|
30
|
+
default: () => ({})
|
|
31
|
+
},
|
|
32
|
+
sortTypes: {
|
|
33
|
+
type: Object,
|
|
34
|
+
default: () => ({})
|
|
35
|
+
},
|
|
36
|
+
modelValue: {
|
|
37
|
+
type: String,
|
|
38
|
+
default: ''
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const emit = defineEmits(['update:modelValue']);
|
|
43
|
+
|
|
44
|
+
const sortId = ref(`ulu-facet-sort-${++idCounter}`);
|
|
45
|
+
</script>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export const initialMockFacets = [
|
|
2
|
+
{
|
|
3
|
+
name: 'Category',
|
|
4
|
+
uid: 'category',
|
|
5
|
+
open: true,
|
|
6
|
+
children: [
|
|
7
|
+
{ uid: 'cat1', label: 'Design' },
|
|
8
|
+
{ uid: 'cat2', label: 'Development' },
|
|
9
|
+
{ uid: 'cat3', label: 'Marketing' },
|
|
10
|
+
{ uid: 'cat4', label: 'Business' },
|
|
11
|
+
{ uid: 'cat5', label: 'Lifestyle' },
|
|
12
|
+
{ uid: 'cat6', label: 'Technology' },
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
name: 'Author',
|
|
17
|
+
uid: 'author',
|
|
18
|
+
open: true,
|
|
19
|
+
children: [
|
|
20
|
+
{ uid: 'jane-doe', label: 'Jane Doe' },
|
|
21
|
+
{ uid: 'john-smith', label: 'John Smith' },
|
|
22
|
+
{ uid: 'peter-jones', label: 'Peter Jones' },
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
export const mockItems = [
|
|
28
|
+
{ id: 1, title: 'The Art of UI Design', description: 'A deep dive into creating beautiful user interfaces.', category: ['cat1', 'cat2'], author: ['jane-doe'], date: new Date(2023, 5, 15) },
|
|
29
|
+
{ id: 2, title: 'Vue.js for Beginners', description: 'Getting started with the popular JavaScript framework.', category: ['cat2', 'cat6'], author: ['john-smith'], date: new Date(2023, 8, 22) },
|
|
30
|
+
{ id: 3, title: 'Content Marketing Strategies', description: 'How to attract and retain customers with great content.', category: ['cat3'], author: ['peter-jones'], date: new Date(2022, 11, 10) },
|
|
31
|
+
{ id: 4, title: 'Startup Funding 101', description: 'A guide to raising capital for your new venture.', category: ['cat4'], author: ['jane-doe'], date: new Date(2024, 1, 5) },
|
|
32
|
+
{ id: 5, title: 'Minimalist Living', description: 'Declutter your life and find more happiness.', category: ['cat5'], author: ['john-smith'], date: new Date(2023, 3, 30) },
|
|
33
|
+
{ id: 6, title: 'The Future of AI', description: 'Exploring the impact of artificial intelligence on society.', category: ['cat6'], author: ['peter-jones'], date: new Date(2024, 0, 1) },
|
|
34
|
+
{ id: 7, title: 'Advanced CSS Techniques', description: 'Take your styling skills to the next level.', category: ['cat1', 'cat2'], author: ['jane-doe'], date: new Date(2023, 10, 18) },
|
|
35
|
+
{ id: 8, title: 'Building a Scalable API', description: 'Best practices for designing and implementing APIs.', category: ['cat2', 'cat6'], author: ['john-smith'], date: new Date(2023, 7, 3) },
|
|
36
|
+
{ id: 9, title: 'Social Media for Business', description: 'Leveraging social platforms for growth.', category: ['cat3'], author: ['peter-jones'], date: new Date(2022, 9, 14) },
|
|
37
|
+
{ id: 10, title: 'Negotiation and Deal Making', description: 'Master the art of getting what you want.', category: ['cat4'], author: ['jane-doe'], date: new Date(2023, 6, 25) },
|
|
38
|
+
{ id: 11, title: 'Healthy Eating Habits', description: 'A guide to a balanced and nutritious diet.', category: ['cat5'], author: ['john-smith'], date: new Date(2024, 2, 12) },
|
|
39
|
+
{ id: 12, title: 'Quantum Computing Explained', description: 'A simple introduction to a complex topic.', category: ['cat6'], author: ['peter-jones'], date: new Date(2023, 9, 9) },
|
|
40
|
+
];
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { ref, computed } from 'vue';
|
|
2
|
+
import Fuse from 'fuse.js';
|
|
3
|
+
|
|
4
|
+
const sortAlpha = items => {
|
|
5
|
+
const getTitle = i => (i.title || i.label || "");
|
|
6
|
+
return items.sort((a, b) => getTitle(a).localeCompare(getTitle(b)));
|
|
7
|
+
}
|
|
8
|
+
const defaultSorts = {
|
|
9
|
+
az: { text: "A-Z", sort: sortAlpha },
|
|
10
|
+
za: { text: "Z-A", sort: items => sortAlpha(items).reverse() },
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A composable for handling client-side faceted search, filtering, and sorting.
|
|
15
|
+
* @param {import('vue').Ref<Array<Object>>} allItems - A Vue ref containing the full list of items to be processed.
|
|
16
|
+
* @param {Object} options - Configuration options for the composable.
|
|
17
|
+
* @param {Array} [options.initialFacets=[]] - The initial configuration for the facets.
|
|
18
|
+
* @param {String} [options.initialSearchValue=''] - The initial value for the search input.
|
|
19
|
+
* @param {String} [options.initialSortType='az'] - The initial sort type.
|
|
20
|
+
* @param {Boolean} [options.noDefaultSorts=false] - If true, the default 'A-Z' and 'Z-A' sorts will not be included.
|
|
21
|
+
* @param {Object} [options.extraSortTypes={}] - Additional sort types to be merged with the default ones.
|
|
22
|
+
* @param {Object} [options.searchOptions={}] - Configuration options for Fuse.js.
|
|
23
|
+
* @param {Function} [options.getItemFacet] - A function to retrieve facet information from an item.
|
|
24
|
+
*/
|
|
25
|
+
export function useFacets(allItems, options = {}) {
|
|
26
|
+
const {
|
|
27
|
+
initialFacets = [],
|
|
28
|
+
initialSearchValue = '',
|
|
29
|
+
initialSortType = 'az',
|
|
30
|
+
noDefaultSorts = false,
|
|
31
|
+
extraSortTypes = {},
|
|
32
|
+
searchOptions: initialSearchOptions = {},
|
|
33
|
+
getItemFacet = (item, uid) => item[uid]
|
|
34
|
+
} = options;
|
|
35
|
+
|
|
36
|
+
// --- State ---
|
|
37
|
+
const facets = ref(createFacets(initialFacets));
|
|
38
|
+
const searchValue = ref(initialSearchValue);
|
|
39
|
+
const selectedSort = ref(initialSortType);
|
|
40
|
+
|
|
41
|
+
// --- Helpers ---
|
|
42
|
+
function createFacets(initial) {
|
|
43
|
+
return initial.map(group => ({
|
|
44
|
+
...group,
|
|
45
|
+
open: group.open || false,
|
|
46
|
+
children: group.children.map(facet => ({
|
|
47
|
+
...facet,
|
|
48
|
+
selected: facet.selected || false
|
|
49
|
+
})),
|
|
50
|
+
selectedCount: 0
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// --- Computed ---
|
|
55
|
+
const sortTypes = computed(() => ({
|
|
56
|
+
...(noDefaultSorts ? {} : defaultSorts),
|
|
57
|
+
...extraSortTypes
|
|
58
|
+
}));
|
|
59
|
+
|
|
60
|
+
const searchOptions = computed(() => ({
|
|
61
|
+
shouldSort: true,
|
|
62
|
+
keys: ["title", "label", "description", "author"],
|
|
63
|
+
...initialSearchOptions
|
|
64
|
+
}));
|
|
65
|
+
|
|
66
|
+
const selectedFacets = computed(() => {
|
|
67
|
+
const selected = [];
|
|
68
|
+
facets.value.forEach((group) => {
|
|
69
|
+
const { name, uid, children } = group;
|
|
70
|
+
let count = 0;
|
|
71
|
+
let added = false;
|
|
72
|
+
if (children) {
|
|
73
|
+
children.forEach(child => {
|
|
74
|
+
if (child.selected) {
|
|
75
|
+
++count;
|
|
76
|
+
if (!added) {
|
|
77
|
+
selected.push({ uid, name, children: [] });
|
|
78
|
+
added = true;
|
|
79
|
+
}
|
|
80
|
+
selected[selected.length - 1].children.push(child);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
group.selectedCount = count;
|
|
85
|
+
});
|
|
86
|
+
return selected;
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const filteredItems = computed(() => {
|
|
90
|
+
if (!selectedFacets.value.length) {
|
|
91
|
+
return allItems.value;
|
|
92
|
+
}
|
|
93
|
+
return allItems.value.filter(item => {
|
|
94
|
+
return selectedFacets.value.some(group => {
|
|
95
|
+
const cats = getItemFacet(item, group.uid);
|
|
96
|
+
if (cats && cats.length) {
|
|
97
|
+
return group.children.some(facet => cats.includes(facet.uid));
|
|
98
|
+
}
|
|
99
|
+
return false;
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const searchedItems = computed(() => {
|
|
105
|
+
if (!searchValue.value?.length) {
|
|
106
|
+
return filteredItems.value;
|
|
107
|
+
}
|
|
108
|
+
const fuse = new Fuse(filteredItems.value, searchOptions.value);
|
|
109
|
+
return fuse.search(searchValue.value).map(result => result.item);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const displayItems = computed(() => {
|
|
113
|
+
const sortFn = sortTypes.value[selectedSort.value]?.sort;
|
|
114
|
+
if (typeof sortFn !== 'function') {
|
|
115
|
+
return searchedItems.value;
|
|
116
|
+
}
|
|
117
|
+
// The sort function should not mutate the original array
|
|
118
|
+
return sortFn([...searchedItems.value]);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// --- Methods ---
|
|
122
|
+
function clearFilters() {
|
|
123
|
+
facets.value = createFacets(initialFacets);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
// State
|
|
128
|
+
facets,
|
|
129
|
+
searchValue,
|
|
130
|
+
selectedSort,
|
|
131
|
+
sortTypes,
|
|
132
|
+
|
|
133
|
+
// Computed
|
|
134
|
+
displayItems,
|
|
135
|
+
selectedFacets,
|
|
136
|
+
|
|
137
|
+
// Methods
|
|
138
|
+
clearFilters
|
|
139
|
+
};
|
|
140
|
+
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
export { default as
|
|
1
|
+
export { default as UluFacetsFilters } from './facets/UluFacetsFilters.vue';
|
|
2
|
+
export { default as UluFacetsResults } from './facets/UluFacetsResults.vue';
|
|
2
3
|
export { default as UluFacetsSearch } from './facets/UluFacetsSearch.vue';
|
|
4
|
+
export { default as UluFacetsSidebarLayout } from './facets/UluFacetsSidebarLayout.vue';
|
|
5
|
+
export { default as UluFacetsSort } from './facets/UluFacetsSort.vue';
|
|
3
6
|
export { default as UluFacetsList } from './facets/UluFacetsList.vue';
|
|
4
7
|
export { default as UluScrollAnchors } from './scroll-anchors/UluScrollAnchors.vue';
|
|
5
8
|
export { default as UluScrollAnchorsNav } from './scroll-anchors/UluScrollAnchorsNav.vue';
|
|
@@ -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/cloneDeep";
|
|
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;
|
package/lib/composables/index.js
CHANGED
|
@@ -7,4 +7,5 @@
|
|
|
7
7
|
export { useIcon } from './useIcon.js';
|
|
8
8
|
export { useModifiers } from './useModifiers.js';
|
|
9
9
|
export { useWindowResize } from './useWindowResize.js';
|
|
10
|
+
export { useRequiredInject } from './useRequiredInject.js';
|
|
10
11
|
export { useBreakpointManager } from './useBreakpointManager.js';
|
|
@@ -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,87 @@
|
|
|
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
|
+
}
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const iconKeys = Object.keys(defaults.iconsByType);
|
|
29
|
+
|
|
30
|
+
export default function install(app, userSettings = {}) {
|
|
31
|
+
// A single reactive object for all settings
|
|
32
|
+
const settings = reactive({ ...defaults });
|
|
33
|
+
|
|
34
|
+
// Separate icon overrides from other options to handle them safely
|
|
35
|
+
const { iconsByType: iconOverrides, ...otherOptions } = userSettings || {};
|
|
36
|
+
|
|
37
|
+
// Merge any user-provided options during installation
|
|
38
|
+
if (otherOptions) {
|
|
39
|
+
Object.assign(settings, otherOptions);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const api = {
|
|
43
|
+
// Methods to interact with settings
|
|
44
|
+
getSettings() {
|
|
45
|
+
return settings;
|
|
46
|
+
},
|
|
47
|
+
getDefaultSettings() {
|
|
48
|
+
return { ...defaults };
|
|
49
|
+
},
|
|
50
|
+
updateSettings(changes) {
|
|
51
|
+
return Object.assign(settings, changes);
|
|
52
|
+
},
|
|
53
|
+
getSetting(key) {
|
|
54
|
+
if (!settings.hasOwnProperty(key)) {
|
|
55
|
+
console.warn(`Attempted to access non-existent setting: ${key}`);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
return settings[key];
|
|
59
|
+
},
|
|
60
|
+
updateSetting(key, value) {
|
|
61
|
+
if (typeof key !== "string") {
|
|
62
|
+
throw new Error("Expected key to be string");
|
|
63
|
+
}
|
|
64
|
+
settings[key] = value;
|
|
65
|
+
},
|
|
66
|
+
getIcon(type) {
|
|
67
|
+
const icons = settings.iconsByType;
|
|
68
|
+
if (!icons[type]) {
|
|
69
|
+
throw new Error(`Icon type "${type}" not found!`);
|
|
70
|
+
}
|
|
71
|
+
return icons[type];
|
|
72
|
+
},
|
|
73
|
+
setIcon(type, definition) {
|
|
74
|
+
settings.iconsByType[type] = definition;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// Apply any individual icon overrides passed during installation
|
|
79
|
+
if (iconOverrides) {
|
|
80
|
+
for (const [type, definition] of Object.entries(iconOverrides)) {
|
|
81
|
+
api.setIcon(type, definition);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
app.provide('uluCore', api);
|
|
86
|
+
app.config.globalProperties.$uluCore = api;
|
|
87
|
+
}
|
package/lib/plugins/index.js
CHANGED
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.11",
|
|
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,35 @@
|
|
|
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"
|
|
30
39
|
},
|
|
31
|
-
"keywords": [
|
|
40
|
+
"keywords": [
|
|
41
|
+
"vue",
|
|
42
|
+
"vue3",
|
|
43
|
+
"ui",
|
|
44
|
+
"components",
|
|
45
|
+
"component-library",
|
|
46
|
+
"design-system",
|
|
47
|
+
"ulu",
|
|
48
|
+
"frontend",
|
|
49
|
+
"scss"
|
|
50
|
+
],
|
|
32
51
|
"author": "Joseph Scherben <jscherbe@gmail.com>",
|
|
33
52
|
"license": "MIT",
|
|
34
53
|
"bugs": {
|
|
@@ -48,7 +67,8 @@
|
|
|
48
67
|
},
|
|
49
68
|
"dependencies": {
|
|
50
69
|
"@floating-ui/vue": "^1.1.8",
|
|
51
|
-
"@ulu/utils": "^0.0.30"
|
|
70
|
+
"@ulu/utils": "^0.0.30",
|
|
71
|
+
"lodash-es": "^4.17.21"
|
|
52
72
|
},
|
|
53
73
|
"devDependencies": {
|
|
54
74
|
"@fortawesome/fontawesome-svg-core": "^6.7.2",
|
|
@@ -67,9 +87,11 @@
|
|
|
67
87
|
"storybook": "^9.1.1",
|
|
68
88
|
"storybook-addon-vue-mdx": "^2.0.2",
|
|
69
89
|
"vite": "^7.0.0",
|
|
70
|
-
"vue-router": "^4.5.1"
|
|
90
|
+
"vue-router": "^4.5.1",
|
|
91
|
+
"typescript": "^5.3.3"
|
|
71
92
|
},
|
|
72
93
|
"volta": {
|
|
73
94
|
"node": "22.17.0"
|
|
74
95
|
}
|
|
75
96
|
}
|
|
97
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/components/index.js"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../lib/components/systems/index.js"],"names":[],"mappings":""}
|