@ulu/frontend-vue 0.1.0-beta.8 → 0.1.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/README.md +7 -0
  2. package/dist/{breakpoints-D2CIgld-.js → breakpoints-DM-CBTtb.js} +1 -1
  3. package/dist/frontend-vue.css +1 -1
  4. package/dist/frontend-vue.js +79 -68
  5. package/dist/index-BNRZ3Apw.js +7541 -0
  6. package/lib/components/collapsible/UluAccordion.vue +71 -53
  7. package/lib/components/collapsible/UluAccordionGroup.vue +54 -0
  8. package/lib/components/collapsible/UluCollapsible.vue +144 -0
  9. package/lib/components/collapsible/UluDropdown.vue +29 -29
  10. package/lib/components/collapsible/UluOverflowPopover.vue +1 -1
  11. package/lib/components/elements/UluBadge.vue +51 -28
  12. package/lib/components/elements/UluBadgeStack.vue +8 -13
  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 +21 -27
  17. package/lib/components/elements/UluIcon.vue +11 -1
  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 +49 -31
  22. package/lib/components/forms/UluFormFile.vue +37 -24
  23. package/lib/components/forms/UluFormMessage.vue +13 -10
  24. package/lib/components/forms/UluFormSelect.vue +28 -16
  25. package/lib/components/forms/UluFormText.vue +24 -15
  26. package/lib/components/forms/UluSearchForm.vue +11 -10
  27. package/lib/components/forms/UluSelectableMenu.vue +99 -0
  28. package/lib/components/index.js +4 -3
  29. package/lib/components/layout/UluTitleRail.vue +25 -1
  30. package/lib/components/layout/UluWhenBreakpoint.vue +9 -0
  31. package/lib/components/navigation/UluBreadcrumb.vue +9 -2
  32. package/lib/components/navigation/UluMenu.vue +8 -3
  33. package/lib/components/navigation/UluMenuStack.vue +3 -1
  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 +91 -0
  37. package/lib/components/systems/facets/UluFacetsFilterPopovers.vue +125 -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 +62 -34
  41. package/lib/components/systems/facets/UluFacetsResults.vue +63 -0
  42. package/lib/components/systems/facets/UluFacetsSearch.vue +27 -50
  43. package/lib/components/systems/facets/UluFacetsSidebarLayout.vue +70 -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 +268 -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 +7 -7
  57. package/lib/components/systems/table-sticky/UluTableStickyTable.vue +3 -3
  58. package/lib/components/visualizations/UluAnimateNumber.vue +7 -1
  59. package/lib/components/visualizations/UluProgressBar.vue +99 -68
  60. package/lib/components/visualizations/UluProgressCircle.vue +146 -0
  61. package/lib/components/visualizations/progress-bar-examples.html +175 -0
  62. package/lib/composables/index.js +3 -1
  63. package/lib/composables/useDocumentTitle.js +61 -0
  64. package/lib/composables/usePagination.js +122 -0
  65. package/lib/index.js +1 -0
  66. package/lib/plugins/core/index.js +6 -1
  67. package/lib/plugins/popovers/UluPopover.vue +8 -3
  68. package/lib/plugins/toast/UluToast.vue +1 -1
  69. package/lib/plugins/toast/UluToastDisplay.vue +19 -2
  70. package/lib/utils/dom.js +12 -0
  71. package/lib/utils/index.js +2 -0
  72. package/lib/utils/{vue-router.js → router.js} +114 -30
  73. package/package.json +17 -11
  74. package/types/components/systems/facets/_mock-data.d.ts +18 -0
  75. package/types/components/systems/facets/_mock-data.d.ts.map +1 -0
  76. package/types/components/systems/facets/useFacets.d.ts +39 -0
  77. package/types/components/systems/facets/useFacets.d.ts.map +1 -0
  78. package/types/components/systems/index.d.ts +1 -1
  79. package/types/composables/index.d.ts +2 -0
  80. package/types/composables/useDocumentTitle.d.ts +22 -0
  81. package/types/composables/useDocumentTitle.d.ts.map +1 -0
  82. package/types/composables/usePageTitle.d.ts +19 -0
  83. package/types/composables/usePageTitle.d.ts.map +1 -0
  84. package/types/composables/usePagination.d.ts +25 -0
  85. package/types/composables/usePagination.d.ts.map +1 -0
  86. package/types/index.d.ts +1 -0
  87. package/types/plugins/core/index.d.ts.map +1 -1
  88. package/types/utils/dom.d.ts +1 -0
  89. package/types/utils/dom.d.ts.map +1 -1
  90. package/types/utils/index.d.ts +3 -0
  91. package/types/utils/index.d.ts.map +1 -0
  92. package/types/utils/router.d.ts +144 -0
  93. package/types/utils/router.d.ts.map +1 -0
  94. package/dist/index-BuKf_eWi.js +0 -6464
  95. package/lib/components/collapsible/UluCollapsibleRegion.vue +0 -278
  96. package/lib/components/forms/UluCheckboxMenu.vue +0 -36
  97. package/lib/components/systems/facets/UluFacets.vue +0 -380
  98. package/lib/components/systems/skeleton/UluSkeletonTextInline.vue +0 -9
  99. package/lib/components/visualizations/UluProgressDonut.vue +0 -97
  100. package/lib/utils/placeholder.js +0 -6
@@ -1,82 +1,100 @@
1
1
  <template>
2
- <Disclosure :defaultOpen="defaultOpen" v-slot="{ open }">
3
- <div
4
- class="accordion"
5
- :class="[
6
- { [activeClass]: open },
7
- classes.container,
8
- resolvedModifiers
9
- ]"
10
- >
11
- <DisclosureButton
12
- class="accordion__summary"
13
- :class="[
14
- { [activeClass]: open },
15
- classes.summary
16
- ]"
17
- >
18
- <slot name="summary" :open="open">
19
- <component :is="summaryTextElement">
20
- {{ summaryText }}
21
- </component>
22
- </slot>
23
- <slot name="icon" :open="open">
24
- <span class="accordion__icon" :class="classes.icon">
25
- <UluIcon
26
- :icon="open ? 'type:collapse' : 'type:expand'"
27
- style="display: inline;"
28
- />
29
- </span>
30
- </slot>
31
- </DisclosureButton>
32
- <DisclosurePanel class="accordion__content" :class="classes.content">
33
- <slot :open="open"/>
34
- </DisclosurePanel>
35
- </div>
36
- </Disclosure>
2
+ <UluCollapsible
3
+ :model-value="modelValue"
4
+ :start-open="startOpen"
5
+ :trigger-text="triggerText"
6
+ :classes="mergedClasses"
7
+ :animate="animate"
8
+ @update:model-value="$emit('update:modelValue', $event)"
9
+ >
10
+ <template #trigger="{ isOpen: open, toggle }">
11
+ <slot name="trigger" :open="open" :toggle="toggle">
12
+ <component :is="triggerTextElement">
13
+ {{ triggerText }}
14
+ </component>
15
+ </slot>
16
+ <slot name="icon" :open="open">
17
+ <span class="accordion__icon" :class="classes.icon">
18
+ <UluIcon
19
+ :icon="open ? 'type:collapse' : 'type:expand'"
20
+ style="display: inline;"
21
+ />
22
+ </span>
23
+ </slot>
24
+ </template>
25
+
26
+ <template #default="{ isOpen: open, toggle }">
27
+ <slot :open="open" :toggle="toggle"/>
28
+ </template>
29
+ </UluCollapsible>
37
30
  </template>
38
31
 
39
32
  <script setup>
33
+ import { computed } from 'vue';
40
34
  import UluIcon from "../elements/UluIcon.vue";
35
+ import UluCollapsible from "./UluCollapsible.vue";
41
36
  import { useModifiers } from "../../composables/useModifiers.js";
42
- import { Disclosure, DisclosureButton, DisclosurePanel } from "@headlessui/vue";
37
+
43
38
  const props = defineProps({
39
+ /**
40
+ * v-model for controlling open state (optional)
41
+ */
42
+ modelValue: {
43
+ type: Boolean,
44
+ default: undefined
45
+ },
44
46
  /**
45
47
  * Whether the accordion is open by default
46
48
  */
47
- defaultOpen: Boolean,
49
+ startOpen: Boolean,
50
+ /**
51
+ * Enable or configure animations.
52
+ * - `false` (default) to disable all animations.
53
+ * - `true` to enable animations with default settings.
54
+ * - An object to provide custom options to auto-animate (e.g., { duration: 100, easing: 'linear' }).
55
+ */
56
+ animate: {
57
+ type: [Boolean, Object],
58
+ default: false
59
+ },
48
60
  /**
49
- * Test to use for accordion, alternatively use #toggle slot
61
+ * Text to use for accordion, alternatively use #trigger slot
50
62
  */
51
- summaryText: String,
63
+ triggerText: String,
52
64
  /**
53
65
  * If using summary text sets the inner element the text is wrapped in, usually a headline or strong
54
66
  */
55
- summaryTextElement: {
67
+ triggerTextElement: {
56
68
  type: String,
57
69
  default: "strong"
58
70
  },
59
71
  /**
60
- * Classes for elements ({ container, summary, icon, content })
72
+ * Classes for elements. See UluCollapsible for all available class keys (toggle, content, etc).
73
+ * The 'icon' key is also available for the icon span.
61
74
  * - Any valid class binding value per element
62
75
  */
63
76
  classes: {
64
77
  type: Object,
65
- default: () => ({})
78
+ default: () => ({
79
+ container: 'accordion',
80
+ toggle: 'accordion__summary',
81
+ content: 'accordion__content',
82
+ containerOpen: 'is-active'
83
+ })
66
84
  },
67
85
  /**
68
- * Active class output on container and toggle elements
69
- */
70
- activeClass: {
71
- type: String,
72
- default: "is-active"
73
- },
74
- /**
75
- * Modifiers for tag class
86
+ * Class modifiers (ie. 'transparent', 'secondary', etc)
76
87
  */
77
88
  modifiers: [String, Array]
78
89
  });
79
90
 
80
- const { resolvedModifiers } = useModifiers({ props, baseClass: "button" });
81
-
82
- </script>
91
+ const emit = defineEmits(['update:modelValue']);
92
+
93
+ const { resolvedModifiers } = useModifiers({ props, baseClass: "accordion" });
94
+
95
+ const mergedClasses = computed(() => {
96
+ const merged = { ...props.classes };
97
+ merged.container = [merged.container, resolvedModifiers.value];
98
+ return merged;
99
+ });
100
+ </script>
@@ -0,0 +1,54 @@
1
+ <template>
2
+ <div class="accordion-group">
3
+ <UluAccordion
4
+ v-for="(item, index) in internalItems"
5
+ :key="index"
6
+ :model-value="item.isOpen"
7
+ @update:modelValue="(newValue) => handleToggle(index, newValue)"
8
+ :trigger-text="item.title"
9
+ :classes="item.classes"
10
+ >
11
+ <slot name="item" :item="item" :index="index">
12
+ {{ item.content }}
13
+ </slot>
14
+ </UluAccordion>
15
+ </div>
16
+ </template>
17
+
18
+ <script setup>
19
+ import { ref, watch } from 'vue';
20
+ import UluAccordion from './UluAccordion.vue';
21
+
22
+ const props = defineProps({
23
+ /**
24
+ * Array of items to render as accordions.
25
+ * Each item can have: title, content, isOpen, classes
26
+ */
27
+ items: {
28
+ type: Array,
29
+ default: () => []
30
+ }
31
+ });
32
+
33
+ const internalItems = ref([]);
34
+
35
+ watch(() => props.items, (newItems) => {
36
+ internalItems.value = newItems.map(item => ({
37
+ ...item,
38
+ isOpen: item.isOpen || false
39
+ }));
40
+ }, { immediate: true, deep: true });
41
+
42
+
43
+ function handleToggle(selectedIndex, newValue) {
44
+ if (newValue) {
45
+ // if opening, close all others
46
+ internalItems.value.forEach((item, index) => {
47
+ item.isOpen = index === selectedIndex;
48
+ });
49
+ } else {
50
+ // if closing, just close that one
51
+ internalItems.value[selectedIndex].isOpen = false;
52
+ }
53
+ }
54
+ </script>
@@ -0,0 +1,144 @@
1
+ <template>
2
+ <div
3
+ ref="container"
4
+ @keydown.esc="handleEscape"
5
+ :class="[classes.container, containerStateClasses]"
6
+ >
7
+ <button
8
+ :class="classes.toggle"
9
+ :id="toggleId"
10
+ :aria-controls="contentId"
11
+ :aria-expanded="isOpen"
12
+ @click="toggle"
13
+ >
14
+ <slot name="trigger" :isOpen="isOpen" :toggle="toggle">
15
+ {{ triggerText }}
16
+ </slot>
17
+ </button>
18
+ <div
19
+ v-if="isOpen"
20
+ :class="classes.content"
21
+ tabindex="-1"
22
+ :id="contentId"
23
+ :aria-hidden="!isOpen"
24
+ :aria-labelledby="toggleId"
25
+ >
26
+ <div :class="classes.contentInner">
27
+ <slot :isOpen="isOpen" :toggle="toggle" />
28
+ </div>
29
+ </div>
30
+ </div>
31
+ </template>
32
+
33
+ <script setup>
34
+ import { ref, computed, watch, onMounted } from 'vue';
35
+ import { useAutoAnimate } from '@formkit/auto-animate/vue';
36
+ import { newId } from '../../utils/dom.js';
37
+
38
+ const props = defineProps({
39
+ /**
40
+ * v-model for controlling open state
41
+ */
42
+ modelValue: {
43
+ type: Boolean,
44
+ default: undefined
45
+ },
46
+ /**
47
+ * Set text for trigger (instead of using slot)
48
+ */
49
+ triggerText: String,
50
+ /**
51
+ * Closes with escape key
52
+ */
53
+ closeOnEscape: Boolean,
54
+ /**
55
+ * When the component is shown it should start visible or hidden
56
+ */
57
+ startOpen: Boolean,
58
+ /**
59
+ * Enable or configure animations.
60
+ * - `true` to enable animations with default settings (default)
61
+ * - `false` (disable)
62
+ * - An object to provide custom options to auto-animate (e.g., { duration: 100, easing: 'linear' }).
63
+ */
64
+ animate: {
65
+ type: [Boolean, Object],
66
+ default: true
67
+ },
68
+ /**
69
+ * Classes for elements ({ container, toggle, content, contentInner })
70
+ * - Any valid class binding value per element
71
+ */
72
+ classes: {
73
+ type: Object,
74
+ default: () => ({
75
+ container: 'ulu-collapsible',
76
+ toggle: 'ulu-collapsible__toggle',
77
+ content: 'ulu-collapsible__content',
78
+ contentInner: 'ulu-collapsible__content-inner',
79
+ containerOpen: 'ulu-collapsible--open',
80
+ containerClosed: 'ulu-collapsible--closed'
81
+ })
82
+ },
83
+ });
84
+
85
+ const emit = defineEmits(['update:modelValue']);
86
+
87
+ const autoAnimateOptions = computed(() => {
88
+ if (typeof props.animate === 'object') {
89
+ return props.animate;
90
+ }
91
+ return {};
92
+ });
93
+
94
+ const [container, enableAnimations] = useAutoAnimate(autoAnimateOptions);
95
+
96
+ // Animation needs to be disabled after the component is mounted
97
+ onMounted(() => { enableAnimations(!!props.animate) });
98
+ // Then we can watch for changes
99
+ watch(() => props.animate, v => { enableAnimations(!!v) });
100
+
101
+ const userControlled = computed(() => props.modelValue !== undefined);
102
+ const internalIsOpen = ref(props.startOpen);
103
+
104
+ const isOpen = computed({
105
+ get() {
106
+ return userControlled.value ? props.modelValue : internalIsOpen.value;
107
+ },
108
+ set(newValue) {
109
+ if (userControlled.value) {
110
+ emit('update:modelValue', newValue);
111
+ } else {
112
+ internalIsOpen.value = newValue;
113
+ }
114
+ }
115
+ });
116
+
117
+ const toggleId = ref(newId('ulu-collapsible-toggle'));
118
+ const contentId = ref(newId('ulu-collapsible-content'));
119
+
120
+ const containerStateClasses = computed(() => {
121
+ const c = props.classes;
122
+ const stateClasses = {};
123
+ if (c.containerOpen && isOpen.value) {
124
+ stateClasses[c.containerOpen] = true;
125
+ }
126
+ if (c.containerClosed && !isOpen.value) {
127
+ stateClasses[c.containerClosed] = true;
128
+ }
129
+ return stateClasses;
130
+ });
131
+
132
+ /**
133
+ * Function used to toggle the collapsible
134
+ */
135
+ function toggle() {
136
+ isOpen.value = !isOpen.value;
137
+ }
138
+
139
+ function handleEscape() {
140
+ if (props.closeOnEscape && isOpen.value) {
141
+ isOpen.value = false;
142
+ }
143
+ }
144
+ </script>
@@ -1,42 +1,42 @@
1
1
  <template>
2
2
  <UluPopover :classes="popoverClasses">
3
3
  <template #trigger="{ isOpen }">
4
- <slot :isOpen="isOpen"/>
4
+ <slot name="trigger" :isOpen="isOpen">
5
+ <span>{{ triggerText }}</span>
6
+ <UluIcon
7
+ class="button__icon"
8
+ icon="type:dropdownExpand"
9
+ :style="{ transform: isOpen ? 'rotate(180deg)' : 'rotate(0deg)', transition: 'transform 0.2s' }"
10
+ />
11
+ </slot>
5
12
  </template>
6
- <template #content>
13
+ <template #default>
7
14
  <UluMenuStack :items="items"/>
8
15
  </template>
9
16
  </UluPopover>
10
17
  </template>
11
18
 
12
- <script>
19
+ <script setup>
13
20
  import UluPopover from "../../plugins/popovers/UluPopover.vue";
14
21
  import UluMenuStack from "../navigation/UluMenuStack.vue";
15
- export default {
16
- name: "UluDropdown",
17
- components: {
18
- UluPopover,
19
- UluMenuStack
20
- },
21
- props: {
22
- /**
23
- * Dropdown menu items (to be passed to UluMenu)
24
- */
25
- items: Array,
26
- /**
27
- * Class to use on trigger
28
- */
29
- triggerClass: {
30
- type: [String, Object, Array],
31
- default: "button"
32
- },
33
- /**
34
- * Pass classes object to UluPopover classes prop
35
- */
36
- popoverClasses: {
37
- type: Object,
38
- default: () => ({})
39
- }
22
+ import UluIcon from "../elements/UluIcon.vue";
23
+ defineProps({
24
+ /**
25
+ * Dropdown menu items (to be passed to UluMenu)
26
+ */
27
+ items: Array,
28
+ /**
29
+ * Optional text if not using slot
30
+ */
31
+ triggerText: String,
32
+ /**
33
+ * Pass classes object to UluPopover classes prop
34
+ */
35
+ popoverClasses: {
36
+ type: Object,
37
+ default: () => ({
38
+ trigger: "button"
39
+ })
40
40
  }
41
- };
41
+ });
42
42
  </script>
@@ -11,7 +11,7 @@
11
11
  <template #trigger>
12
12
  <UluIcon :icon="triggerIcon || 'type:ellipsis'"/>
13
13
  </template>
14
- <template #content>
14
+ <template #default>
15
15
  <div class="type-word-break">
16
16
  <slot/>
17
17
  </div>
@@ -26,33 +26,56 @@
26
26
  </component>
27
27
  </template>
28
28
 
29
- <script>
29
+ <script setup>
30
+ import { computed } from "vue";
30
31
  import { RouterLink } from "vue-router";
31
- export default {
32
- name: "UluBadge",
33
- props: {
34
- skeleton: Boolean,
35
- size: String,
36
- text: String,
37
- alt: String,
38
- type: String,
39
- click: Function,
40
- to: [Object, String],
41
- href: String,
42
- },
43
- computed: {
44
- isInteractive() {
45
- return Boolean(this.to || this.click);
46
- },
47
- element() {
48
- const { click, to, href } = this;
49
- /* eslint-disable */
50
- return click ? 'button' :
51
- to ? RouterLink :
52
- href ? 'a' :
53
- 'span';
54
- /* eslint-enable */
55
- }
56
- }
57
- };
32
+
33
+ const props = defineProps({
34
+ /**
35
+ * Whether to display a skeleton loading state.
36
+ */
37
+ skeleton: Boolean,
38
+ /**
39
+ * The size of the badge (e.g., 'small', 'large').
40
+ */
41
+ size: String,
42
+ /**
43
+ * The text content of the badge.
44
+ */
45
+ text: String,
46
+ /**
47
+ * Alt text for the badge, for accessibility.
48
+ */
49
+ alt: String,
50
+ /**
51
+ * The type or style of the badge (e.g., 'primary', 'secondary').
52
+ */
53
+ type: String,
54
+ /**
55
+ * A function to call when the badge is clicked. Renders as a <button>.
56
+ */
57
+ click: Function,
58
+ /**
59
+ * A Vue Router link location. Renders as a <router-link>.
60
+ */
61
+ to: [Object, String],
62
+ /**
63
+ * A URL. Renders as a standard <a> tag.
64
+ */
65
+ href: String,
66
+ });
67
+
68
+ const isInteractive = computed(() => {
69
+ return Boolean(props.to || props.click);
70
+ });
71
+
72
+ const element = computed(() => {
73
+ const { click, to, href } = props;
74
+ /* eslint-disable */
75
+ return click ? "button" :
76
+ to ? RouterLink :
77
+ href ? "a" :
78
+ "span";
79
+ /* eslint-enable */
80
+ });
58
81
  </script>
@@ -10,18 +10,13 @@
10
10
  </ul>
11
11
  </template>
12
12
 
13
- <script>
13
+ <script setup>
14
14
  import UluBadge from "./UluBadge.vue";
15
- export default {
16
- name: 'UluBadgeStack',
17
- components: {
18
- UluBadge
19
- },
20
- props: {
21
- /**
22
- * Array of props for each badge
23
- */
24
- items: Array
25
- }
26
- }
15
+
16
+ defineProps({
17
+ /**
18
+ * Array of props for each badge
19
+ */
20
+ items: Array
21
+ });
27
22
  </script>
@@ -0,0 +1,119 @@
1
+ <template>
2
+ <component
3
+ :is="element"
4
+ class="button-verbose"
5
+ :class="[
6
+ {
7
+ 'button-verbose--inline': inline,
8
+ 'button-verbose--full-width': fullWidth,
9
+ },
10
+ resolvedModifiers
11
+ ]"
12
+ v-bind="attrs"
13
+ >
14
+ <component
15
+ v-if="$slots.title || title"
16
+ :is="titleElement"
17
+ class="button-verbose__title"
18
+ >
19
+ <slot name="title">
20
+ {{ title }}
21
+ </slot>
22
+ </component>
23
+ <span v-if="$slots.default || body" class="button-verbose__body">
24
+ <slot>
25
+ {{ body }}
26
+ </slot>
27
+ </span>
28
+ <UluIcon
29
+ v-if="icon"
30
+ :icon="icon"
31
+ class="button-verbose__icon"
32
+ aria-hidden="true"
33
+ />
34
+ </component>
35
+ </template>
36
+
37
+ <script>
38
+ import { RouterLink } from "vue-router";
39
+ import UluIcon from "./UluIcon.vue";
40
+ import { useModifiers } from "../../composables/useModifiers.js";
41
+
42
+ export default {
43
+ name: "UluButtonVerbose",
44
+ components: {
45
+ UluIcon
46
+ },
47
+ props: {
48
+ /**
49
+ * The title of the button. Can also be passed via slot.
50
+ */
51
+ title: String,
52
+ /**
53
+ * Optional element to use for title
54
+ */
55
+ titleElement: {
56
+ type: String,
57
+ default: "strong"
58
+ },
59
+ /**
60
+ * The body text of the button. Can also be passed via slot.
61
+ */
62
+ body: String,
63
+ /**
64
+ * Icon prop, if used will set the icon for the button, will use UluIcon (which uses font-awesome icons conditionally)
65
+ */
66
+ icon: [String, Array],
67
+ /**
68
+ * If set will use router-link for button component and pass to prop
69
+ */
70
+ to: [String, Object],
71
+ /**
72
+ * Sets the button to a link with this href
73
+ */
74
+ href: String,
75
+ /**
76
+ * Set a value for target attribute when button is a link
77
+ */
78
+ target: String,
79
+ /**
80
+ * Sets the download attribute on the link (passing string [filename] will populate the download attribute, true will just include it as boolean attribute)
81
+ */
82
+ download: [Boolean, String],
83
+ /**
84
+ * Preset to set inline style
85
+ */
86
+ inline: Boolean,
87
+ /**
88
+ * Preset to set full-width style
89
+ */
90
+ fullWidth: Boolean,
91
+ /**
92
+ * Modifiers (to add any modifier classes based on base class [ie. 'tertiary'])
93
+ */
94
+ modifiers: [String, Array]
95
+ },
96
+ setup(props) {
97
+ const { resolvedModifiers } = useModifiers({ props, baseClass: "button-verbose" });
98
+ return { resolvedModifiers };
99
+ },
100
+ computed: {
101
+ element() {
102
+ return this.to ? RouterLink : this.href ? "a" : "button";
103
+ },
104
+ attrs() {
105
+ const { to, href, download, target } = this;
106
+ const attrs = to ? { to } : href ? { href } : {};
107
+ if (href) {
108
+ if (target) {
109
+ attrs.target = target;
110
+ }
111
+ if (download) {
112
+ attrs.download = typeof download === "string" ? download : true;
113
+ }
114
+ }
115
+ return attrs;
116
+ }
117
+ }
118
+ };
119
+ </script>
@@ -54,7 +54,7 @@
54
54
  </component>
55
55
  <slot name="body"/>
56
56
  </div>
57
- <div class="card__aside" v-if="$slots.body">
57
+ <div class="card__aside" v-if="$slots.aside">
58
58
  <slot name="aside"/>
59
59
  </div>
60
60
  </div>