@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.
Files changed (151) hide show
  1. package/README.md +113 -2
  2. package/dist/{breakpoints-Cq2oSdYS.js → breakpoints-BJNvnXsD.js} +1 -1
  3. package/dist/frontend-vue.css +1 -1
  4. package/dist/frontend-vue.js +78 -72
  5. package/dist/index-BjwifaTk.js +6946 -0
  6. package/lib/components/collapsible/UluAccordion.vue +1 -1
  7. package/lib/components/collapsible/UluModal.vue +4 -5
  8. package/lib/components/collapsible/UluOverflowPopover.vue +1 -1
  9. package/lib/components/elements/UluAlert.vue +1 -2
  10. package/lib/components/elements/UluBadge.vue +27 -28
  11. package/lib/components/elements/UluBadgeStack.vue +8 -13
  12. package/lib/components/elements/UluButton.vue +2 -2
  13. package/lib/components/elements/UluButtonVerbose.vue +119 -0
  14. package/lib/components/elements/UluCard.vue +1 -1
  15. package/lib/components/elements/UluDefinitionList.vue +14 -17
  16. package/lib/components/elements/UluExternalLink.vue +22 -29
  17. package/lib/components/elements/UluIcon.vue +22 -17
  18. package/lib/components/elements/UluList.vue +53 -55
  19. package/lib/components/elements/UluSpokeSpinner.vue +12 -18
  20. package/lib/components/elements/UluTag.vue +35 -35
  21. package/lib/components/forms/UluFileDisplay.vue +40 -31
  22. package/lib/components/forms/UluFormFile.vue +22 -24
  23. package/lib/components/forms/UluFormMessage.vue +7 -10
  24. package/lib/components/forms/UluFormSelect.vue +16 -16
  25. package/lib/components/forms/UluFormText.vue +15 -15
  26. package/lib/components/forms/UluSearchForm.vue +8 -10
  27. package/lib/components/forms/UluSelectableMenu.vue +78 -0
  28. package/lib/components/index.js +2 -2
  29. package/lib/components/layout/UluAdaptiveLayout.vue +3 -5
  30. package/lib/components/layout/UluTitleRail.vue +9 -5
  31. package/lib/components/layout/UluWhenBreakpoint.vue +71 -77
  32. package/lib/components/navigation/UluBreadcrumb.vue +10 -4
  33. package/lib/components/navigation/UluMenu.vue +3 -3
  34. package/lib/components/navigation/UluPager.vue +102 -0
  35. package/lib/components/systems/facets/ExampleFacetsWithPagination.vue +119 -0
  36. package/lib/components/systems/facets/UluFacetsFilterLists.vue +84 -0
  37. package/lib/components/systems/facets/UluFacetsFilterPopovers.vue +114 -0
  38. package/lib/components/systems/facets/UluFacetsFilterSelects.vue +71 -0
  39. package/lib/components/systems/facets/UluFacetsHeaderLayout.vue +24 -0
  40. package/lib/components/systems/facets/UluFacetsList.vue +61 -33
  41. package/lib/components/systems/facets/UluFacetsResults.vue +63 -0
  42. package/lib/components/systems/facets/UluFacetsSearch.vue +26 -49
  43. package/lib/components/systems/facets/UluFacetsSidebarLayout.vue +31 -0
  44. package/lib/components/systems/facets/UluFacetsSort.vue +45 -0
  45. package/lib/components/systems/facets/_facets.scss +2 -3
  46. package/lib/components/systems/facets/_mock-data.js +40 -0
  47. package/lib/components/systems/facets/useFacets.js +229 -0
  48. package/lib/components/systems/index.js +13 -2
  49. package/lib/components/systems/scroll-anchors/UluScrollAnchors.vue +2 -1
  50. package/lib/components/systems/skeleton/UluShowSkeleton.vue +9 -8
  51. package/lib/components/systems/skeleton/UluSkeletonContent.vue +39 -43
  52. package/lib/components/systems/skeleton/UluSkeletonMedia.vue +4 -6
  53. package/lib/components/systems/skeleton/UluSkeletonText.vue +27 -0
  54. package/lib/components/systems/slider/UluImageSlideShow.vue +1 -1
  55. package/lib/components/systems/slider/UluSlideShow.vue +8 -3
  56. package/lib/components/systems/table-sticky/UluTableSticky.vue +8 -8
  57. package/lib/components/systems/table-sticky/UluTableStickyTable.vue +3 -3
  58. package/lib/composables/index.js +4 -1
  59. package/lib/composables/useDocumentTitle.js +61 -0
  60. package/lib/composables/usePagination.js +122 -0
  61. package/lib/composables/useRequiredInject.js +26 -0
  62. package/lib/index.js +1 -1
  63. package/lib/meta.js +14 -0
  64. package/lib/plugins/core/index.js +91 -0
  65. package/lib/plugins/index.js +1 -0
  66. package/lib/plugins/popovers/UluPopover.vue +3 -1
  67. package/lib/plugins/toast/UluToast.vue +2 -2
  68. package/lib/utils/index.js +2 -0
  69. package/lib/utils/{vue-router.js → router.js} +114 -30
  70. package/package.json +38 -13
  71. package/types/components/index.d.ts +2 -0
  72. package/types/components/index.d.ts.map +1 -0
  73. package/types/components/systems/facets/_mock-data.d.ts +18 -0
  74. package/types/components/systems/facets/_mock-data.d.ts.map +1 -0
  75. package/types/components/systems/facets/useFacets.d.ts +39 -0
  76. package/types/components/systems/facets/useFacets.d.ts.map +1 -0
  77. package/types/components/systems/index.d.ts +2 -0
  78. package/types/components/systems/index.d.ts.map +1 -0
  79. package/types/components/systems/scroll-anchors/symbols.d.ts +7 -0
  80. package/types/components/systems/scroll-anchors/symbols.d.ts.map +1 -0
  81. package/types/composables/index.d.ts +8 -0
  82. package/types/composables/index.d.ts.map +1 -0
  83. package/types/composables/useBreakpointManager.d.ts +8 -0
  84. package/types/composables/useBreakpointManager.d.ts.map +1 -0
  85. package/types/composables/useDocumentTitle.d.ts +22 -0
  86. package/types/composables/useDocumentTitle.d.ts.map +1 -0
  87. package/types/composables/useIcon.d.ts +6 -0
  88. package/types/composables/useIcon.d.ts.map +1 -0
  89. package/types/composables/useModifiers.d.ts +69 -0
  90. package/types/composables/useModifiers.d.ts.map +1 -0
  91. package/types/composables/usePageTitle.d.ts +19 -0
  92. package/types/composables/usePageTitle.d.ts.map +1 -0
  93. package/types/composables/usePagination.d.ts +25 -0
  94. package/types/composables/usePagination.d.ts.map +1 -0
  95. package/types/composables/useRequiredInject.d.ts +8 -0
  96. package/types/composables/useRequiredInject.d.ts.map +1 -0
  97. package/types/composables/useWindowResize.d.ts +6 -0
  98. package/types/composables/useWindowResize.d.ts.map +1 -0
  99. package/types/index.d.ts +5 -0
  100. package/types/index.d.ts.map +1 -0
  101. package/types/meta.d.ts +10 -0
  102. package/types/meta.d.ts.map +1 -0
  103. package/types/plugins/breakpoints/index.d.ts +2 -0
  104. package/types/plugins/breakpoints/index.d.ts.map +1 -0
  105. package/types/plugins/core/index.d.ts +3 -0
  106. package/types/plugins/core/index.d.ts.map +1 -0
  107. package/types/plugins/index.d.ts +6 -0
  108. package/types/plugins/index.d.ts.map +1 -0
  109. package/types/plugins/modals/api.d.ts +34 -0
  110. package/types/plugins/modals/api.d.ts.map +1 -0
  111. package/types/plugins/modals/index.d.ts +28 -0
  112. package/types/plugins/modals/index.d.ts.map +1 -0
  113. package/types/plugins/modals/useModals.d.ts +2 -0
  114. package/types/plugins/modals/useModals.d.ts.map +1 -0
  115. package/types/plugins/popovers/defaults.d.ts +14 -0
  116. package/types/plugins/popovers/defaults.d.ts.map +1 -0
  117. package/types/plugins/popovers/directive.d.ts +8 -0
  118. package/types/plugins/popovers/directive.d.ts.map +1 -0
  119. package/types/plugins/popovers/index.d.ts +7 -0
  120. package/types/plugins/popovers/index.d.ts.map +1 -0
  121. package/types/plugins/popovers/manager.d.ts +52 -0
  122. package/types/plugins/popovers/manager.d.ts.map +1 -0
  123. package/types/plugins/popovers/useFollow.d.ts +31 -0
  124. package/types/plugins/popovers/useFollow.d.ts.map +1 -0
  125. package/types/plugins/popovers/utils.d.ts +2 -0
  126. package/types/plugins/popovers/utils.d.ts.map +1 -0
  127. package/types/plugins/toast/defaults.d.ts +15 -0
  128. package/types/plugins/toast/defaults.d.ts.map +1 -0
  129. package/types/plugins/toast/index.d.ts +5 -0
  130. package/types/plugins/toast/index.d.ts.map +1 -0
  131. package/types/plugins/toast/store.d.ts +22 -0
  132. package/types/plugins/toast/store.d.ts.map +1 -0
  133. package/types/plugins/toast/useToast.d.ts +2 -0
  134. package/types/plugins/toast/useToast.d.ts.map +1 -0
  135. package/types/utils/dom.d.ts +8 -0
  136. package/types/utils/dom.d.ts.map +1 -0
  137. package/types/utils/index.d.ts +3 -0
  138. package/types/utils/index.d.ts.map +1 -0
  139. package/types/utils/placeholder.d.ts +8 -0
  140. package/types/utils/placeholder.d.ts.map +1 -0
  141. package/types/utils/router.d.ts +144 -0
  142. package/types/utils/router.d.ts.map +1 -0
  143. package/types/utils/vue-router.d.ts +122 -0
  144. package/types/utils/vue-router.d.ts.map +1 -0
  145. package/dist/index-CMGxe_M1.js +0 -6466
  146. package/lib/components/forms/UluCheckboxMenu.vue +0 -36
  147. package/lib/components/forms/UluFormDropzone.vue +0 -62
  148. package/lib/components/systems/facets/UluFacets.vue +0 -380
  149. package/lib/components/systems/skeleton/UluSkeletonTextInline.vue +0 -9
  150. package/lib/settings.js +0 -119
  151. package/lib/utils/placeholder.js +0 -6
@@ -1,11 +1,9 @@
1
1
  <template>
2
- <div class="skeleton__block skeleton__block--media layout-flex-center-all">
3
- <FaIcon style="font-size: 2rem" icon="image"/>
2
+ <div class="skeleton skeleton-block--media">
3
+ <UluIcon icon="type:image"/>
4
4
  </div>
5
5
  </template>
6
6
 
7
- <script>
8
- export default {
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>
@@ -9,7 +9,7 @@
9
9
  <UluSlideShow
10
10
  class="slideshow--images"
11
11
  :items="images"
12
- @slideChange="slideChange"
12
+ @slide-change="slideChange"
13
13
  >
14
14
  <template #slide="{ item }">
15
15
  <img :src="item.src" :alt="item.alt">
@@ -50,7 +50,7 @@
50
50
  @click="previous"
51
51
  :disabled="!canScrollLeft"
52
52
  >
53
- <FaIcon class="slideshow__control-icon" icon="fas fa-chevron-left"/>
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
- <FaIcon class="slideshow__control-icon" icon="fas fa-chevron-right"/>
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('slideChange', { slide, track, nav });
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
- @columnSorted="applySort"
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
- @columnSorted="applySort"
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
- @actualHeaderRemoved="headerRemoved"
142
- @actualHeaderAdded="headerAdded"
143
- @columnSorted="applySort"
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
- @columnSorted="applySort"
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.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;
@@ -443,7 +443,7 @@
443
443
  } else {
444
444
  column.sortApplied = true;
445
445
  }
446
- this.$emit("columnSort", column);
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('columnSorted', column)"
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("actualHeaderRemoved", old);
202
+ this.$emit("actual-header-removed", old);
203
203
  }
204
- this.$emit("actualHeaderAdded", el);
204
+ this.$emit("actual-header-added", el);
205
205
  headerRefs[id] = el;
206
206
  },
207
207
  /**
@@ -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 { useBreakpointManager } from './useBreakpointManager.js';
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
@@ -7,4 +7,4 @@
7
7
  export * from './plugins/index.js';
8
8
  export * from './components/index.js';
9
9
  export * from './composables/index.js';
10
- export * from './settings.js';
10
+ export * as utils from './utils/index.js';
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
+ }
@@ -5,6 +5,7 @@
5
5
  */
6
6
 
7
7
 
8
+ export { default as corePlugin } from './core/index.js';
8
9
  export { default as popoversPlugin } from './popovers/index.js';
9
10
  export { default as modalsPlugin } from './modals/index.js';
10
11
  export { default as toastPlugin } from './toast/index.js';
@@ -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-hidden="isOpen ? 'false' : 'true'"
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
- <FaIcon v-if="toast.icon" :icon="toast.icon"/>
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 type="close"/>
46
+ <UluIcon :icon="'type:close'"/>
47
47
  </button>
48
48
  </div>
49
49
  </template>
@@ -0,0 +1,2 @@
1
+ export * as dom from './dom.js';
2
+ export * as router from './router.js';