@ulu/frontend-vue 0.5.16 → 0.6.0

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 (64) hide show
  1. package/dist/components/elements/UluButton.vue.d.ts +4 -0
  2. package/dist/components/elements/UluButton.vue.d.ts.map +1 -1
  3. package/dist/components/elements/UluButton.vue.js +31 -16
  4. package/dist/components/elements/UluIcon.vue.js +21 -36
  5. package/dist/components/forms/UluFormCheckbox.vue.d.ts +3 -19
  6. package/dist/components/forms/UluFormCheckbox.vue.d.ts.map +1 -1
  7. package/dist/components/forms/UluFormCheckbox.vue.js +10 -31
  8. package/dist/components/forms/UluFormFile.vue.d.ts +3 -25
  9. package/dist/components/forms/UluFormFile.vue.d.ts.map +1 -1
  10. package/dist/components/forms/UluFormFile.vue.js +11 -49
  11. package/dist/components/forms/UluFormItem.vue.d.ts +23 -8
  12. package/dist/components/forms/UluFormItem.vue.d.ts.map +1 -1
  13. package/dist/components/forms/UluFormItem.vue.js +126 -29
  14. package/dist/components/forms/UluFormLabel.vue.d.ts +24 -0
  15. package/dist/components/forms/UluFormLabel.vue.d.ts.map +1 -0
  16. package/dist/components/forms/UluFormLabel.vue.js +34 -0
  17. package/dist/components/forms/UluFormRadio.vue.d.ts +7 -25
  18. package/dist/components/forms/UluFormRadio.vue.d.ts.map +1 -1
  19. package/dist/components/forms/UluFormRadio.vue.js +11 -37
  20. package/dist/components/forms/UluFormSelect.vue.d.ts +7 -23
  21. package/dist/components/forms/UluFormSelect.vue.d.ts.map +1 -1
  22. package/dist/components/forms/UluFormSelect.vue.js +24 -43
  23. package/dist/components/forms/UluFormText.vue.d.ts +5 -23
  24. package/dist/components/forms/UluFormText.vue.d.ts.map +1 -1
  25. package/dist/components/forms/UluFormText.vue.js +10 -38
  26. package/dist/components/forms/UluFormTextarea.vue.d.ts +5 -23
  27. package/dist/components/forms/UluFormTextarea.vue.d.ts.map +1 -1
  28. package/dist/components/forms/UluFormTextarea.vue.js +10 -37
  29. package/dist/components/forms/UluSearchForm.vue.d.ts +24 -3
  30. package/dist/components/forms/UluSearchForm.vue.d.ts.map +1 -1
  31. package/dist/components/forms/UluSearchForm.vue.js +67 -22
  32. package/dist/components/index.d.ts +1 -0
  33. package/dist/components/systems/facets/UluFacetsFilterSelects.vue.d.ts.map +1 -1
  34. package/dist/components/systems/facets/UluFacetsFilterSelects.vue.js +21 -22
  35. package/dist/components/utils/UluAction.vue.d.ts +2 -0
  36. package/dist/components/utils/UluAction.vue.d.ts.map +1 -1
  37. package/dist/components/utils/UluAction.vue.js +9 -5
  38. package/dist/components/visualizations/UluProgressBar.vue.d.ts +2 -2
  39. package/dist/index.js +130 -128
  40. package/dist/plugins/core/index.d.ts.map +1 -1
  41. package/dist/plugins/core/index.js +17 -16
  42. package/dist/utils/props.d.ts +7 -0
  43. package/dist/utils/props.d.ts.map +1 -1
  44. package/dist/utils/props.js +8 -2
  45. package/lib/components/elements/UluButton.vue +18 -3
  46. package/lib/components/elements/UluIcon.vue +8 -26
  47. package/lib/components/forms/UluForm.vue +25 -25
  48. package/lib/components/forms/UluFormCheckbox.vue +11 -25
  49. package/lib/components/forms/UluFormFieldset.vue +6 -6
  50. package/lib/components/forms/UluFormFile.vue +10 -40
  51. package/lib/components/forms/UluFormItem.vue +150 -39
  52. package/lib/components/forms/UluFormLabel.vue +30 -0
  53. package/lib/components/forms/UluFormRadio.vue +15 -34
  54. package/lib/components/forms/UluFormSelect.vue +19 -24
  55. package/lib/components/forms/UluFormText.vue +7 -25
  56. package/lib/components/forms/UluFormTextarea.vue +7 -25
  57. package/lib/components/forms/UluSearchForm.vue +67 -19
  58. package/lib/components/forms/UluSelectableMenu.vue +62 -62
  59. package/lib/components/index.js +1 -0
  60. package/lib/components/systems/facets/UluFacetsFilterSelects.vue +11 -14
  61. package/lib/components/utils/UluAction.vue +6 -2
  62. package/lib/plugins/core/index.js +2 -1
  63. package/lib/utils/props.js +14 -0
  64. package/package.json +3 -3
@@ -1,42 +1,24 @@
1
1
  <template>
2
- <label :class="{ 'hidden-visually' : labelHidden }" :for="id">
3
- <slot name="label">
4
- {{ label }}<UluFormRequiredChar v-if="required" />
5
- </slot>
6
- </label>
7
2
  <input
8
3
  type="text"
4
+ v-bind="fieldAttrs"
9
5
  :value="modelValue"
10
6
  @input="$emit('update:modelValue', $event.target.value)"
11
- :id="id"
12
- :required="required"
13
7
  >
14
8
  </template>
15
9
 
16
10
  <script setup>
17
- import { newId } from "../../utils/dom.js";
18
- import UluFormRequiredChar from "./UluFormRequiredChar.vue";
11
+ import { inject, computed } from "vue";
19
12
 
20
- defineProps({
21
- /**
22
- * The label for the text input.
23
- */
24
- label: String,
13
+ const props = defineProps({
25
14
  /**
26
15
  * The value of the text input (for v-model).
27
16
  */
28
- modelValue: String,
29
- /**
30
- * If true, the label will be visually hidden.
31
- */
32
- labelHidden: Boolean,
33
- /**
34
- * If true, the field will be required.
35
- */
36
- required: Boolean
17
+ modelValue: [String, Number]
37
18
  });
38
19
 
39
- defineEmits(['update:modelValue']);
20
+ defineEmits(["update:modelValue"]);
40
21
 
41
- const id = newId();
22
+ const injectedAttrs = inject("uluFormFieldAttrs", null);
23
+ const fieldAttrs = computed(() => injectedAttrs ? injectedAttrs.value : {});
42
24
  </script>
@@ -1,41 +1,23 @@
1
1
  <template>
2
- <label :class="{ 'hidden-visually' : labelHidden }" :for="id">
3
- <slot name="label">
4
- {{ label }}<UluFormRequiredChar v-if="required" />
5
- </slot>
6
- </label>
7
2
  <textarea
3
+ v-bind="fieldAttrs"
8
4
  :value="modelValue"
9
5
  @input="$emit('update:modelValue', $event.target.value)"
10
- :id="id"
11
- :required="required"
12
6
  ></textarea>
13
7
  </template>
14
8
 
15
9
  <script setup>
16
- import { newId } from "../../utils/dom.js";
17
- import UluFormRequiredChar from "./UluFormRequiredChar.vue";
10
+ import { inject, computed } from "vue";
18
11
 
19
- defineProps({
20
- /**
21
- * The label for the textarea.
22
- */
23
- label: String,
12
+ const props = defineProps({
24
13
  /**
25
14
  * The value of the textarea (for v-model).
26
15
  */
27
- modelValue: String,
28
- /**
29
- * If true, the label will be visually hidden.
30
- */
31
- labelHidden: Boolean,
32
- /**
33
- * If true, the field will be required.
34
- */
35
- required: Boolean
16
+ modelValue: [String, Number]
36
17
  });
37
18
 
38
- defineEmits(['update:modelValue']);
19
+ defineEmits(["update:modelValue"]);
39
20
 
40
- const id = newId();
21
+ const injectedAttrs = inject("uluFormFieldAttrs", null);
22
+ const fieldAttrs = computed(() => injectedAttrs ? injectedAttrs.value : {});
41
23
  </script>
@@ -1,32 +1,80 @@
1
1
  <template>
2
- <div class="form-theme search-form type-small">
3
- <div class="search-form__field">
4
- <label class="hidden-visually">Search</label>
5
- <input
6
- class="search-form__input"
7
- type="text"
8
- id="example-input"
9
- :placeholder="placeholder"
10
- >
2
+ <form class="form-theme" @submit.prevent="onSubmit">
3
+ <div class="input-group input-group--joined">
4
+ <div class="input-group__item input-group__item--field">
5
+ <label :for="inputId" class="hidden-visually">{{ label }}</label>
6
+ <input
7
+ type="search"
8
+ :id="inputId"
9
+ class="input-group__input"
10
+ :placeholder="placeholder"
11
+ :value="modelValue"
12
+ @input="$emit('update:modelValue', $event.target.value)"
13
+ >
14
+ </div>
15
+ <div class="input-group__item">
16
+ <slot name="submit">
17
+ <UluButton
18
+ class="input-group__button"
19
+ v-bind="submitButtonProps"
20
+ />
21
+ </slot>
22
+ </div>
11
23
  </div>
12
- <button
13
- class="search-form__submit button button--primary"
14
- aria-label="Submit Search"
15
- >
16
- <UluIcon icon="type:search" />
17
- </button>
18
- </div>
24
+ </form>
19
25
  </template>
20
26
 
21
27
  <script setup>
22
- import UluIcon from "../elements/UluIcon.vue";
23
- defineProps({
28
+ import { computed } from "vue";
29
+ import { newId } from "../../utils/dom.js";
30
+ import UluButton from "../elements/UluButton.vue";
31
+
32
+ const props = defineProps({
33
+ /**
34
+ * The search input value (for v-model).
35
+ */
36
+ modelValue: {
37
+ type: String,
38
+ default: ""
39
+ },
24
40
  /**
25
41
  * The placeholder text for the search input.
26
42
  */
27
43
  placeholder: {
28
44
  type: String,
29
45
  default: "Titles, keyword…"
30
- }
46
+ },
47
+ /**
48
+ * The visually hidden label for the search input.
49
+ */
50
+ label: {
51
+ type: String,
52
+ default: "Search"
53
+ },
54
+ /**
55
+ * Props to pass to the default UluButton component (used for submit button)
56
+ * - Alternately use 'submit' slot
57
+ */
58
+ submitButtonProps: {
59
+ type: Object,
60
+ default: () => ({
61
+ type: "submit",
62
+ primary: true,
63
+ icon: "type:search",
64
+ ariaLabel: "Submit Search"
65
+ })
66
+ },
67
+ /**
68
+ * Optional ID for the input element. If not provided, a unique ID is generated.
69
+ */
70
+ id: String
31
71
  });
72
+
73
+ const emit = defineEmits(["update:modelValue", "submit"]);
74
+
75
+ const inputId = computed(() => props.id || newId());
76
+
77
+ const onSubmit = () => {
78
+ emit("submit", props.modelValue);
79
+ };
32
80
  </script>
@@ -32,75 +32,75 @@
32
32
  </template>
33
33
 
34
34
  <script setup>
35
- import { computed } from 'vue';
35
+ import { computed } from 'vue';
36
36
 
37
- const props = defineProps({
38
- /**
39
- * The legend for the menu.
40
- */
41
- legend: String,
42
- /**
43
- * An array of options for the menu.
44
- */
45
- options: Array,
46
- /**
47
- * Use compact modifier on menu stack
48
- */
49
- compact: Boolean,
50
- /**
51
- * The type of input to use ('checkbox' or 'radio').
52
- */
53
- type: {
54
- type: String,
55
- default: 'checkbox',
56
- },
57
- /**
58
- * The value of the menu (for v-model).
59
- */
60
- modelValue: [String, Array],
61
- /**
62
- * If true, the input elements will be visually hidden.
63
- */
64
- hideInputs: Boolean
65
- });
37
+ const props = defineProps({
38
+ /**
39
+ * The legend for the menu.
40
+ */
41
+ legend: String,
42
+ /**
43
+ * An array of options for the menu.
44
+ */
45
+ options: Array,
46
+ /**
47
+ * Use compact modifier on menu stack
48
+ */
49
+ compact: Boolean,
50
+ /**
51
+ * The type of input to use ('checkbox' or 'radio').
52
+ */
53
+ type: {
54
+ type: String,
55
+ default: 'checkbox',
56
+ },
57
+ /**
58
+ * The value of the menu (for v-model).
59
+ */
60
+ modelValue: [String, Array],
61
+ /**
62
+ * If true, the input elements will be visually hidden.
63
+ */
64
+ hideInputs: Boolean
65
+ });
66
66
 
67
- const emit = defineEmits(['update:modelValue']);
67
+ const emit = defineEmits(['update:modelValue']);
68
68
 
69
- const name = computed(() => props.legend ? props.legend.toLowerCase().replace(/\s+/g, '-') : `menu-${ Math.random().toString(36).substring(7) }`);
70
- const legendId = computed(() => name.value ? `${name.value}-legend` : null);
71
- const groupRole = computed(() => props.type === 'radio' ? 'radiogroup' : 'group');
69
+ const name = computed(() => props.legend ? props.legend.toLowerCase().replace(/\s+/g, '-') : `menu-${ Math.random().toString(36).substring(7) }`);
70
+ const legendId = computed(() => name.value ? `${name.value}-legend` : null);
71
+ const groupRole = computed(() => props.type === 'radio' ? 'radiogroup' : 'group');
72
72
 
73
- const getId = (option) => `${name.value}-${option.uid}`;
73
+ const getId = (option) => `${name.value}-${option.uid}`;
74
74
 
75
- const isChecked = (option) => {
76
- if (props.type === 'radio') {
77
- return props.modelValue === option.uid;
78
- }
79
- if (Array.isArray(props.modelValue)) {
80
- return props.modelValue.includes(option.uid);
81
- }
82
- if (props.type === 'checkbox') {
83
- return option.checked || false;
84
- }
85
- return false;
86
- };
87
-
88
- const handleChange = (option, event) => {
89
- if (props.type === 'radio') {
90
- emit('update:modelValue', option.uid);
91
- } else {
75
+ const isChecked = (option) => {
76
+ if (props.type === 'radio') {
77
+ return props.modelValue === option.uid;
78
+ }
92
79
  if (Array.isArray(props.modelValue)) {
93
- const newValue = [...props.modelValue];
94
- const index = newValue.indexOf(option.uid);
95
- if (index > -1) {
96
- newValue.splice(index, 1);
80
+ return props.modelValue.includes(option.uid);
81
+ }
82
+ if (props.type === 'checkbox') {
83
+ return option.checked || false;
84
+ }
85
+ return false;
86
+ };
87
+
88
+ const handleChange = (option, event) => {
89
+ if (props.type === 'radio') {
90
+ emit('update:modelValue', option.uid);
91
+ } else {
92
+ if (Array.isArray(props.modelValue)) {
93
+ const newValue = [...props.modelValue];
94
+ const index = newValue.indexOf(option.uid);
95
+ if (index > -1) {
96
+ newValue.splice(index, 1);
97
+ } else {
98
+ newValue.push(option.uid);
99
+ }
100
+ emit('update:modelValue', newValue);
97
101
  } else {
98
- newValue.push(option.uid);
102
+ option.checked = event.target.checked;
99
103
  }
100
- emit('update:modelValue', newValue);
101
- } else {
102
- option.checked = event.target.checked;
103
104
  }
104
- }
105
- };
105
+ };
106
106
  </script>
@@ -56,6 +56,7 @@ export { default as UluFormActions } from './forms/UluFormActions.vue';
56
56
  export { default as UluFormCheckbox } from './forms/UluFormCheckbox.vue';
57
57
  export { default as UluFormFieldset } from './forms/UluFormFieldset.vue';
58
58
  export { default as UluFormItem } from './forms/UluFormItem.vue';
59
+ export { default as UluFormLabel } from './forms/UluFormLabel.vue';
59
60
  export { default as UluFormItemsInline } from './forms/UluFormItemsInline.vue';
60
61
  export { default as UluFormRadio } from './forms/UluFormRadio.vue';
61
62
  export { default as UluFormRequiredChar } from './forms/UluFormRequiredChar.vue';
@@ -59,9 +59,6 @@ const props = defineProps({
59
59
  },
60
60
  });
61
61
 
62
- console.log(props);
63
-
64
-
65
62
  const emit = defineEmits(['facet-change']);
66
63
 
67
64
  function onFilterChange(group, event) {
@@ -84,15 +81,15 @@ function onFilterChange(group, event) {
84
81
  </script>
85
82
 
86
83
  <style lang="scss">
87
- .facets-dropdown-filters {
88
- display: flex;
89
- gap: 1rem;
90
- align-items: center;
91
- flex-wrap: wrap;
92
- }
93
- .facets-dropdown-filters__group {
94
- display: flex;
95
- gap: 0.5rem;
96
- align-items: center;
97
- }
84
+ .facets-dropdown-filters {
85
+ display: flex;
86
+ gap: 1rem;
87
+ align-items: center;
88
+ flex-wrap: wrap;
89
+ }
90
+ .facets-dropdown-filters__group {
91
+ display: flex;
92
+ gap: 0.5rem;
93
+ align-items: center;
94
+ }
98
95
  </style>
@@ -48,7 +48,11 @@
48
48
  * Allows passing 'click' as a prop to signify this is an action (used in UluMenu data objects).
49
49
  * Note: The actual @click listener should be attached via fallthrough attrs, this is just for logic routing.
50
50
  */
51
- click: Function
51
+ click: Function,
52
+ /**
53
+ * Button type (e.g. 'submit', 'reset', 'button'). Defaults to 'button' to prevent accidental form submissions.
54
+ */
55
+ type: String
52
56
  });
53
57
 
54
58
  const resolvedElement = computed(() => {
@@ -73,7 +77,7 @@
73
77
  }
74
78
  } else if (!props.element || props.element === "button") {
75
79
  // It's a button, ensure it doesn't accidentally submit forms unless requested
76
- attrs.type = "button";
80
+ attrs.type = props.type || "button";
77
81
  }
78
82
 
79
83
  return attrs;
@@ -27,7 +27,8 @@ const defaults = {
27
27
  file: "fas fa-file",
28
28
  previous: "fas fa-chevron-left",
29
29
  next: "fas fa-chevron-right",
30
- dropdownExpand: "fas fa-caret-down"
30
+ dropdownExpand: "fas fa-caret-down",
31
+ search: "fas fa-search"
31
32
  }
32
33
  };
33
34
 
@@ -5,4 +5,18 @@
5
5
  */
6
6
  export function isArrayOfObjects(array) {
7
7
  return array.every(item => typeof item === "object");
8
+ }
9
+
10
+ /**
11
+ * Checks for deprecated props and calls a callback for each match
12
+ * @param {object} props - The current props object
13
+ * @param {string[]} deprecatedNames - Array of prop names to check
14
+ * @param {function} callback - Function called for each match, receiving the prop name
15
+ */
16
+ export function checkDeprecatedProps(props, deprecatedNames, callback) {
17
+ deprecatedNames.forEach(name => {
18
+ if (props[name] !== undefined) {
19
+ callback(name);
20
+ }
21
+ });
8
22
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ulu/frontend-vue",
3
- "version": "0.5.16",
3
+ "version": "0.6.0",
4
4
  "description": "A modular, tree-shakeable Vue 3 component library for the Ulu Frontend theming system, plus general utilities for Vue development",
5
5
  "type": "module",
6
6
  "files": [
@@ -65,7 +65,7 @@
65
65
  "@fortawesome/vue-fontawesome": "^3.0.8",
66
66
  "@headlessui/vue": "^1.7.23",
67
67
  "@portabletext/vue": "^1.0.14",
68
- "@ulu/frontend": "^0.5.0",
68
+ "@ulu/frontend": "^0.6.8",
69
69
  "@ulu/utils": "^0.0.34",
70
70
  "@unhead/vue": "^2.0.11",
71
71
  "fuse.js": "^6.6.2",
@@ -87,7 +87,7 @@
87
87
  "@storybook/addon-essentials": "^9.0.0-alpha.12",
88
88
  "@storybook/addon-links": "^9.1.1",
89
89
  "@storybook/vue3-vite": "^9.1.1",
90
- "@ulu/frontend": "^0.4.11",
90
+ "@ulu/frontend": "^0.6.8",
91
91
  "@ulu/utils": "^0.0.34",
92
92
  "@unhead/vue": "^2.0.11",
93
93
  "@vitejs/plugin-vue": "^6.0.0",