@resee-movies/nuxt-ux 0.9.0 → 0.10.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.
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@resee-movies/nuxt-ux",
3
3
  "configKey": "ux",
4
- "version": "0.9.0",
4
+ "version": "0.10.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.0"
@@ -4,10 +4,20 @@
4
4
  :as = "props.is"
5
5
  :name = "props.name"
6
6
  :resolver = "validatorFunction"
7
- :class = "['input-field', props.class, { disabled: isDisabled, readonly: isReadonly, required: props.required }]"
7
+ :class = "[
8
+ props.class,
9
+ {
10
+ 'input-field': props.is !== 'fieldset',
11
+ 'input-fieldset': props.is === 'fieldset',
12
+ disabled: isDisabled,
13
+ readonly: isReadonly,
14
+ required: props.required
15
+ }
16
+ ]"
8
17
  >
9
18
  <FormLabelInputPair
10
19
  :input-id = "inputId"
20
+ :label-is = "props.is === 'fieldset' ? 'legend' : void 0"
11
21
  :label-id = "labelId"
12
22
  :label-text = "labelText"
13
23
  :label-position = "props.labelPosition"
@@ -1,12 +1,12 @@
1
1
  <template>
2
2
  <div class="@container">
3
- <div class="grid grid-cols-1 gap-x-4 gap-y-6 @lg:grid-cols-2 @lg:gap-y-7">
3
+ <div class="grid grid-cols-1 gap-x-4 gap-y-6 sm:@lg:grid-cols-2 sm:@lg:gap-y-7">
4
4
  <template v-for="(field, index) of props.fields" :key="field.name ?? index">
5
5
  <Component
6
6
  :is = "getComponent(field)"
7
7
  v-bind = "field"
8
8
  :class = "{
9
- '@lg:col-span-2': field.width !== 'half'
9
+ 'sm:@lg:col-span-2': field.width !== 'half'
10
10
  }"
11
11
  />
12
12
  </template>
@@ -25,6 +25,7 @@ import CheckboxField from "./FormFieldCheckbox.vue";
25
25
  import SelectField from "./FormFieldSelect.vue";
26
26
  import SelectButtonField from "./FormFieldSelectButton.vue";
27
27
  import SubmitButton from "./FormSubmitButton.vue";
28
+ import RadioGroup from "./FormFieldRadioGroup.vue";
28
29
  import TextField from "./FormFieldText.vue";
29
30
  import TextareaField from "./FormFieldTextarea.vue";
30
31
  import ToggleSwitchField from "./FormFieldToggleSwitch.vue";
@@ -40,6 +41,8 @@ function getComponent(field) {
40
41
  return SelectField;
41
42
  case "select-button":
42
43
  return SelectButtonField;
44
+ case "radio":
45
+ return RadioGroup;
43
46
  case "text":
44
47
  return TextField;
45
48
  case "textarea":
@@ -1,4 +1,5 @@
1
1
  import type { FormFieldCheckboxProps } from './FormFieldCheckbox.vue.js';
2
+ import type { FormFieldRadioGroupProps } from './FormFieldRadioGroup.vue.js';
2
3
  import type { FormFieldSelectProps } from './FormFieldSelect.vue.js';
3
4
  import type { FormFieldSelectButtonProps } from './FormFieldSelectButton.vue.js';
4
5
  import type { FormFieldTextProps } from './FormFieldText.vue.js';
@@ -18,6 +19,9 @@ export interface SelectField extends FormFieldSelectProps, CommonOptions {
18
19
  export interface SelectButtonField extends FormFieldSelectButtonProps, CommonOptions {
19
20
  fieldType: 'select-button';
20
21
  }
22
+ export interface RadioGroup extends FormFieldRadioGroupProps, CommonOptions {
23
+ fieldType: 'radio';
24
+ }
21
25
  export interface TextField extends FormFieldTextProps, CommonOptions {
22
26
  fieldType: 'text';
23
27
  }
@@ -33,7 +37,7 @@ export interface TurnstileField extends FormFieldTurnstileProps, CommonOptions {
33
37
  export interface SubmitButton extends FormSubmitButtonProps, CommonOptions {
34
38
  fieldType: 'submit';
35
39
  }
36
- export type FormFieldBuilderOption = CheckboxField | SelectField | SelectButtonField | TextField | TextareaField | ToggleSwitchField | TurnstileField | SubmitButton;
40
+ export type FormFieldBuilderOption = CheckboxField | SelectField | SelectButtonField | RadioGroup | TextField | TextareaField | ToggleSwitchField | TurnstileField | SubmitButton;
37
41
  export interface FormFieldBuilderProps {
38
42
  fields: undefined | FormFieldBuilderOption[];
39
43
  }
@@ -0,0 +1,65 @@
1
+ <template>
2
+ <FormField v-bind="props" is="fieldset" :validator="validatorFunction">
3
+ <template #label>
4
+ <slot name="label" />
5
+ </template>
6
+
7
+ <template #default="{ inputId, messageId, disabled, readonly, invalid }">
8
+ <template v-for="(option, index) of options" :key="`${inputId}_${index}`">
9
+ <FormLabelInputPair
10
+ :label-position = "props.optionLabelPosition"
11
+ :disabled = "disabled"
12
+ :readonly = "readonly"
13
+ :input-id = "`${inputId}_${index}`"
14
+ :label-text = "utils.getOptionLabel(option)"
15
+ >
16
+ <template #input>
17
+ <PrimeRadioButton
18
+ :value = "utils.getOptionValue(option)"
19
+ :input-id = "`${inputId}_${index}`"
20
+ :disabled = "disabled || utils.getOptionDisabled(option) || readonly"
21
+ :invalid = "invalid"
22
+ :pt:input:readonly = "readonly"
23
+ :pt:input:aria-describedby = "messageId"
24
+ class = "input-group"
25
+ />
26
+ </template>
27
+ </FormLabelInputPair>
28
+ </template>
29
+ </template>
30
+ </FormField>
31
+ </template>
32
+
33
+ <script>
34
+
35
+ </script>
36
+
37
+ <script setup>
38
+ import PrimeRadioButton from "primevue/radiobutton";
39
+ import { computed } from "vue";
40
+ import { useOptionListMethods } from "../../utils/form";
41
+ import { createBooleanValidator } from "../../utils/validation";
42
+ import FormField from "./FormField.vue";
43
+ import FormLabelInputPair from "./FormLabelInputPair.vue";
44
+ const props = defineProps({
45
+ options: { type: Array, required: true },
46
+ optionLabel: { type: String, required: false, default: void 0 },
47
+ optionValue: { type: String, required: false, default: void 0 },
48
+ optionDisabled: { type: String, required: false, default: void 0 },
49
+ optionLabelPosition: { type: String, required: false, default: "after" },
50
+ name: { type: String, required: true },
51
+ label: { type: String, required: false },
52
+ is: { type: String, required: false },
53
+ required: { type: Boolean, required: false },
54
+ disabled: { type: Boolean, required: false },
55
+ readonly: { type: Boolean, required: false },
56
+ fauxLabel: { type: Boolean, required: false },
57
+ labelSrOnly: { type: Boolean, required: false },
58
+ labelPosition: { type: String, required: false },
59
+ class: { type: null, required: false }
60
+ });
61
+ const utils = useOptionListMethods(props);
62
+ const validatorFunction = computed(() => {
63
+ return () => createBooleanValidator({ required: props.required });
64
+ });
65
+ </script>
@@ -0,0 +1,19 @@
1
+ import type { FormFieldProps } from './FormField.vue.js';
2
+ import type { FormLabelInputPairProps } from './FormLabelInputPair.vue.js';
3
+ export interface FormFieldRadioGroupProps extends Omit<FormFieldProps, 'validator'> {
4
+ options: unknown[];
5
+ optionLabel?: string;
6
+ optionValue?: string;
7
+ optionDisabled?: string;
8
+ optionLabelPosition?: FormLabelInputPairProps['labelPosition'];
9
+ }
10
+ declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<FormFieldRadioGroupProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<FormFieldRadioGroupProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
11
+ label?: (props: {}) => any;
12
+ }>;
13
+ declare const _default: typeof __VLS_export;
14
+ export default _default;
15
+ type __VLS_WithSlots<T, S> = T & {
16
+ new (): {
17
+ $slots: S;
18
+ };
19
+ };
@@ -1,9 +1,9 @@
1
1
  <template>
2
2
  <div :class="['input-label-pair', `label-${props.labelPosition}`]">
3
3
  <Component
4
- :is = "props.fauxLabel ? 'span' : 'label'"
4
+ :is = "labelTagName"
5
5
  :id = "props.labelId"
6
- :for = "props.fauxLabel ? void 0 : props.inputId"
6
+ :for = "labelTagName === 'label' ? props.inputId : void 0"
7
7
  :class = "[
8
8
  'input-label',
9
9
  {
@@ -29,14 +29,19 @@
29
29
  </script>
30
30
 
31
31
  <script setup>
32
+ import { computed } from "vue";
32
33
  const props = defineProps({
33
34
  inputId: { type: String, required: false },
34
35
  labelId: { type: String, required: false },
35
36
  labelText: { type: String, required: false },
36
37
  labelPosition: { type: String, required: false, default: "above" },
37
38
  labelSrOnly: { type: Boolean, required: false, default: false },
39
+ labelIs: { type: String, required: false, default: void 0 },
38
40
  required: { type: Boolean, required: false, default: false },
39
41
  disabled: { type: Boolean, required: false, default: false },
40
42
  fauxLabel: { type: Boolean, required: false, default: false }
41
43
  });
44
+ const labelTagName = computed(
45
+ () => props.labelIs ?? (props.fauxLabel ? "span" : "label")
46
+ );
42
47
  </script>
@@ -1,9 +1,11 @@
1
+ import type { HintedString } from '../../types/index.js';
1
2
  export interface FormLabelInputPairProps {
2
3
  inputId?: string;
3
4
  labelId?: string;
4
5
  labelText?: string;
5
6
  labelPosition?: 'above' | 'below' | 'before' | 'after';
6
7
  labelSrOnly?: boolean;
8
+ labelIs?: HintedString<'label' | 'legend' | 'span'>;
7
9
  required?: boolean;
8
10
  disabled?: boolean;
9
11
  fauxLabel?: boolean;
@@ -5,16 +5,16 @@
5
5
  :aria-labelledby = "props.ariaLabelledby"
6
6
  :aria-describedby = "props.ariaDescribedby"
7
7
  >
8
- <template v-for="(option, index) of props.options" :key="getOptionRenderKey(option) ?? index">
8
+ <template v-for="(option, index) of props.options" :key="utils.getOptionRenderKey(option) ?? index">
9
9
  <ToggleButton
10
10
  class = "input-button"
11
11
  severity = "unset"
12
- :pressed = "getSelectedOptionIndex(option) !== -1"
13
- :text = "props.iconOnly ? void 0 : getOptionLabel(option)"
14
- :icon = "getOptionIcon(option)"
12
+ :pressed = "utils.getOptionIndex(option, value) !== -1"
13
+ :text = "props.iconOnly ? void 0 : utils.getOptionLabel(option)"
14
+ :icon = "utils.getOptionIcon(option)"
15
15
  :disabled = "getOptionDisabled(option)"
16
- :aria-label = "props.iconOnly ? getOptionLabel(option) : void 0"
17
- :tooltip = "props.iconOnly ? getOptionLabel(option) : void 0"
16
+ :aria-label = "props.iconOnly ? utils.getOptionLabel(option) : void 0"
17
+ :tooltip = "props.iconOnly ? utils.getOptionLabel(option) : void 0"
18
18
  @click = "(pressed) => handleToggleChange(option, pressed)"
19
19
  />
20
20
  </template>
@@ -32,9 +32,7 @@ export class ChangeEvent extends Event {
32
32
  </script>
33
33
 
34
34
  <script setup>
35
- import { equals, resolveFieldData } from "@primeuix/utils/object";
36
- import { isNumber } from "@resee-movies/utilities/numbers/is-number";
37
- import { isString } from "@resee-movies/utilities/strings/is-string";
35
+ import { useOptionListMethods } from "../../../utils/form";
38
36
  import ToggleButton from "../../ToggleButton.vue";
39
37
  const props = defineProps({
40
38
  options: { type: Array, required: true },
@@ -53,58 +51,32 @@ const props = defineProps({
53
51
  });
54
52
  const emits = defineEmits(["change"]);
55
53
  const value = defineModel("value", { type: null, ...{ default: null } });
56
- function getOptionLabel(option) {
57
- return String(props.optionLabel ? resolveFieldData(option, props.optionLabel) : option);
58
- }
59
- function getOptionValue(option) {
60
- return props.optionValue ? resolveFieldData(option, props.optionValue) : option;
61
- }
62
- function getOptionIcon(option) {
63
- return props.optionIcon ? String(resolveFieldData(option, props.optionIcon)) : void 0;
64
- }
54
+ const utils = useOptionListMethods(props);
65
55
  function getOptionDisabled(option) {
66
- if (props.disabled || props.readonly) {
67
- return true;
68
- }
69
- if (props.optionDisabled && resolveFieldData(option, props.optionDisabled)) {
56
+ if (props.disabled || props.readonly || utils.value.getOptionDisabled(option)) {
70
57
  return true;
71
58
  }
72
59
  if (props.multiple) {
73
60
  const limit = props.selectionLimit ?? Number.POSITIVE_INFINITY;
74
61
  const length = Array.isArray(value.value) ? value.value.length : 0;
75
- if (limit <= length && getSelectedOptionIndex(option) === -1) {
62
+ if (limit <= length && utils.value.getOptionIndex(option, value) === -1) {
76
63
  return true;
77
64
  }
78
65
  }
79
66
  return false;
80
67
  }
81
- function getOptionRenderKey(option) {
82
- const result = props.optionDataKey ? resolveFieldData(option, props.optionDataKey) : getOptionLabel(option);
83
- return isString(result) || isNumber(result) ? result : void 0;
84
- }
85
- function getSelectedOptionIndex(option) {
86
- if (!value.value) {
87
- return -1;
88
- }
89
- const equalityKey = props.optionValue ? void 0 : props.optionDataKey;
90
- const optionValue = getOptionValue(option);
91
- if (Array.isArray(value.value)) {
92
- return value.value.findIndex((entry) => equals(entry, optionValue, equalityKey));
93
- }
94
- return equals(value.value, optionValue, equalityKey) ? 0 : -1;
95
- }
96
68
  function handleToggleChange(option, pressed) {
97
69
  if (props.multiple) {
98
- const selectedIndex = getSelectedOptionIndex(option);
70
+ const selectedIndex = utils.value.getOptionIndex(option, value);
99
71
  const selectedItems = Array.isArray(value.value) ? [...value.value] : [];
100
72
  if (selectedIndex === -1) {
101
- selectedItems.push(getOptionValue(option));
73
+ selectedItems.push(utils.value.getOptionValue(option));
102
74
  } else {
103
75
  selectedItems.splice(selectedIndex, 1);
104
76
  }
105
77
  value.value = selectedItems.length === 0 ? null : selectedItems;
106
78
  } else {
107
- value.value = pressed ? getOptionValue(option) : null;
79
+ value.value = pressed ? utils.value.getOptionValue(option) : null;
108
80
  }
109
81
  emits("change", new ChangeEvent(value.value));
110
82
  }
@@ -14,7 +14,7 @@
14
14
  :show-clear = "props.showClear"
15
15
  :show-toggle-all = "showSelectAllCheckbox"
16
16
  :filter = "showFilter"
17
- :filter-placeholder = "props.filterPlaceholder ?? locale.form.filterPlaceholder"
17
+ :filter-placeholder = "props.filterPlaceholder ?? reseeUx.locale.form.filterPlaceholder"
18
18
  :loading = "props.loading"
19
19
  :pt = "props.multiple ? multiSelectPassthroughProps : selectPassthroughProps"
20
20
  :append-to = "TeleportId"
@@ -58,11 +58,11 @@
58
58
  </template>
59
59
 
60
60
  <template #emptyfilter>
61
- <span>{{ locale.form.filterNoResults }}</span>
61
+ <span>{{ reseeUx.locale.form.filterNoResults }}</span>
62
62
  </template>
63
63
 
64
64
  <template #empty>
65
- <span>{{ locale.form.noOptionsAvailable }}</span>
65
+ <span>{{ reseeUx.locale.form.noOptionsAvailable }}</span>
66
66
  </template>
67
67
 
68
68
  <template #option="{ option, selected, index }" v-if="slots.option">
@@ -76,14 +76,14 @@ import {} from "primevue/select";
76
76
  </script>
77
77
 
78
78
  <script setup>
79
- import { equals, resolveFieldData } from "@primeuix/utils/object";
80
- import { useBreakpoints, breakpointsTailwind } from "@vueuse/core";
79
+ import { breakpointsTailwind, useBreakpoints } from "@vueuse/core";
81
80
  import PrimeMultiSelect from "primevue/multiselect";
82
81
  import PrimeSelect from "primevue/select";
83
82
  import { computed, useSlots } from "vue";
84
83
  import { useReseeUx } from "../../../composables/use-resee-ux";
85
84
  import { TeleportId } from "../../../constants";
86
85
  import { blockBodyScroll } from "../../../utils/dom";
86
+ import { useOptionListMethods } from "../../../utils/form";
87
87
  import { swapStringPlaceholders } from "../../../utils/string";
88
88
  import Button from "../../Button.vue";
89
89
  import Icon from "../../Icon.vue";
@@ -156,7 +156,8 @@ const props = defineProps({
156
156
  unstyled: { type: Boolean, required: false }
157
157
  });
158
158
  const slots = useSlots();
159
- const { locale } = useReseeUx();
159
+ const utils = useOptionListMethods(props);
160
+ const reseeUx = useReseeUx();
160
161
  const isSmall = useBreakpoints(breakpointsTailwind).smallerOrEqual("sm");
161
162
  const showFilter = computed(() => {
162
163
  return props.showOptionFilter ?? (props.options?.length ?? 0) > 20;
@@ -219,7 +220,7 @@ const multiSelectPassthroughProps = computed(() => {
219
220
  },
220
221
  input: {
221
222
  "id": `${props.inputId}_select_all`,
222
- "aria-label": locale.form.selectAllOptions
223
+ "aria-label": reseeUx.locale.form.selectAllOptions
223
224
  }
224
225
  },
225
226
  pcOptionCheckbox: {
@@ -229,30 +230,19 @@ const multiSelectPassthroughProps = computed(() => {
229
230
  }
230
231
  };
231
232
  });
232
- function getOptionValue(option) {
233
- return props.optionValue ? resolveFieldData(option, props.optionValue) : option;
234
- }
235
- function getOptionLabel(option) {
236
- return props.optionLabel ? resolveFieldData(option, props.optionLabel) : option;
237
- }
238
- function findOptionByValue(value) {
239
- return props.options?.find(
240
- (option) => equals(getOptionValue(option), value, props.optionValue ? void 0 : props.dataKey)
241
- );
242
- }
243
233
  function toLabel(value) {
244
234
  if (!value) {
245
235
  return void 0;
246
236
  }
247
237
  if (!Array.isArray(value)) {
248
- return getOptionLabel(findOptionByValue(value));
238
+ return utils.value.getOptionLabel(utils.value.getOptionByValue(value, props.options));
249
239
  }
250
240
  if (value.length === 0) {
251
241
  return void 0;
252
242
  }
253
243
  if (value.length === 1) {
254
- return getOptionLabel(findOptionByValue(value[0]));
244
+ return utils.value.getOptionLabel(utils.value.getOptionByValue(value[0], props.options));
255
245
  }
256
- return swapStringPlaceholders(locale.form.numOptionsSelected, { count: value.length });
246
+ return swapStringPlaceholders(reseeUx.locale.form.numOptionsSelected, { count: value.length });
257
247
  }
258
248
  </script>
@@ -1,3 +1 @@
1
- @layer components{.input-label{display:block;-webkit-user-select:none;-moz-user-select:none;user-select:none}.input-label.required:after{color:var(--color-danger);content:"*";display:inline-block;margin-inline-start:--spacing(1)}.input-label.disabled{cursor:not-allowed;opacity:.6}.input-label-pair{display:flex;flex-direction:column;gap:--spacing(1);margin-block-end:--spacing(1)}.input-label-pair.label-after,.input-label-pair.label-before{align-items:center;flex-direction:row;gap:--spacing(3)}.input-label-pair.label-after .input-label,.input-label-pair.label-below .input-label{order:2}.input-validation{color:var(--color-danger);font-size:var(--text-sm);height:0;line-clamp:2}.input-field{display:flex;flex-direction:column}.input-control{flex-grow:1;padding:--spacing(2) --spacing(3)}.input-control::-moz-placeholder{color:var(--color-global-foreground-accent)}.input-control .placeholder,.input-control::placeholder{color:var(--color-global-foreground-accent)}.input-group{align-items:center;display:flex}.input-group.input-group-loading{cursor:wait}.input-group .input-group-addon{color:var(--color-global-foreground-accent);padding:--spacing(1.5) --spacing(2)}.input-group button.input-group-addon{cursor:pointer}.input-group .input-control{outline:none}.input-control:where(:not(.input-group .input-control)),.input-group{background-color:var(--color-global-background);border-radius:--spacing(1);cursor:pointer;outline:solid 2px var(--color-background-scale-e);overflow:clip;transition:color,background-color,outline-color,box-shadow;transition-duration:calc(var(--default-transition-duration)*2);width:100%}.input-control:where(:not(.input-group .input-control)):where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-control:where(:not(.input-group .input-control)))),.input-group:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-group)){cursor:default}.input-control:where(:not(.input-group .input-control)):not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-control:where(:not(.input-group .input-control))))):where(.disabled,:disabled:not(.p-hidden-accessible input),:is(.disabled .input-control:where(:not(.input-group .input-control)):not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-control:where(:not(.input-group .input-control))))))),.input-group:not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-group))):where(.disabled,:disabled:not(.p-hidden-accessible input),:is(.disabled .input-group:not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-group))))){cursor:not-allowed;opacity:.6}.input-control:where(:not(.input-group .input-control)):not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-control:where(:not(.input-group .input-control))))):not(:where(.disabled,:disabled:not(.p-hidden-accessible input),:is(.disabled .input-control:where(:not(.input-group .input-control)):not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-control:where(:not(.input-group .input-control)))))))):focus-within,.input-control:where(:not(.input-group .input-control)):not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-control:where(:not(.input-group .input-control))))):not(:where(.disabled,:disabled:not(.p-hidden-accessible input),:is(.disabled .input-control:where(:not(.input-group .input-control)):not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-control:where(:not(.input-group .input-control)))))))):hover,.input-group:not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-group))):not(:where(.disabled,:disabled:not(.p-hidden-accessible input),:is(.disabled .input-group:not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-group)))))):focus-within,.input-group:not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-group))):not(:where(.disabled,:disabled:not(.p-hidden-accessible input),:is(.disabled .input-group:not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-group)))))):hover{outline-color:var(--color-global-foreground-accent)}.input-control:where(:not(.input-group .input-control)):not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-control:where(:not(.input-group .input-control))))):not(:where(.disabled,:disabled:not(.p-hidden-accessible input),:is(.disabled .input-control:where(:not(.input-group .input-control)):not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-control:where(:not(.input-group .input-control)))))))):focus-within,.input-group:not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-group))):not(:where(.disabled,:disabled:not(.p-hidden-accessible input),:is(.disabled .input-group:not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-group)))))):focus-within{box-shadow:var(--shadow-heavy)}.input-control:where(:not(.input-group .input-control)):not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-control:where(:not(.input-group .input-control))))):not(:where(.disabled,:disabled:not(.p-hidden-accessible input),:is(.disabled .input-control:where(:not(.input-group .input-control)):not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-control:where(:not(.input-group .input-control)))))))):not(:has(>input[type=checkbox]),.input-button-bar):focus-within,.input-group:not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-group))):not(:where(.disabled,:disabled:not(.p-hidden-accessible input),:is(.disabled .input-group:not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-group)))))):not(:has(>input[type=checkbox]),.input-button-bar):focus-within{background-color:var(--color-background-scale-b)}.input-group:where(:has(input[type=checkbox])){flex-shrink:0;height:1.5rem;position:relative;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:1.5rem}
2
-
3
- /* !important is to overwrite what gets attached to i-* icons in the utility layer */.input-group:where(:has(input[type=checkbox])) span[class^=i-],.input-group:where(:has(input[type=checkbox])) svg{display:block!important;font-size:1rem;height:1rem!important;line-height:1;margin-left:.25rem;width:1rem!important}.input-group:where(:has(input[type=checkbox])) input{-webkit-appearance:none;-moz-appearance:none;appearance:none;height:100%;inset:0;opacity:0;position:absolute;z-index:10}.input-group:where(:has(input[type=checkbox])) input:not(:disabled,[readonly]){cursor:pointer}.input-group:where(:has(input[type=checkbox])).toggle-switch{border-radius:1rem;width:2.5rem}.input-group:where(:has(input[type=checkbox])).toggle-switch:has(:checked) .slider:before{background-color:var(--color-global-foreground);transform:translateX(1rem)}.input-group:where(:has(input[type=checkbox])).toggle-switch .slider{border-radius:1rem;inset:0;position:absolute}.input-group:where(:has(input[type=checkbox])).toggle-switch .slider:before{background-color:var(--color-background-scale-d);border-radius:var(--radius-full);box-shadow:var(--shadow-md);content:"";height:1rem;left:.25rem;position:absolute;top:.25rem;transition:transform,background-color;transition-duration:var(--default-transition-duration);width:1rem}.input-menu .input-control,.input-menu .input-group{background-color:transparent}.input-menu .input-group:where(:has(input[type=checkbox])){height:1.2rem;width:1.2rem}.input-menu .input-group:where(:has(input[type=checkbox])) span[class^=i-],.input-menu .input-group:where(:has(input[type=checkbox])) svg{margin-left:.1rem;margin-top:-.1rem}[aria-disabled=true] .input-menu .input-group:where(:has(input[type=checkbox])):focus-within,[aria-disabled=true] .input-menu .input-group:where(:has(input[type=checkbox])):hover{outline-color:var(--color-background-scale-e)!important}[aria-disabled=true] .input-menu .input-group:where(:has(input[type=checkbox])):focus-within input,[aria-disabled=true] .input-menu .input-group:where(:has(input[type=checkbox])):hover input{cursor:not-allowed!important}.input-menu-header{align-items:center;display:flex;gap:--spacing(2)}.input-menu-header .input-menu-filter{align-items:center;color:var(--color-global-foreground-accent);display:flex;flex-grow:1;transition:color var(--default-transition-duration) var(--default-transition-timing-function)}.input-menu-header .input-menu-filter input{flex-grow:1;outline:none;padding:--spacing(.5) 0}.input-menu-header .input-menu-filter input::-moz-placeholder{color:var(--color-global-foreground-accent)}.input-menu-header .input-menu-filter input::placeholder{color:var(--color-global-foreground-accent)}.input-menu-header .input-menu-filter:focus-within{color:var(--color-global-foreground)}.input-button{cursor:pointer;padding:--spacing(1.5) --spacing(2);transition:background-color var(--default-transition-duration) var(--default-transition-timing-function)}.input-button[aria-pressed=true]{background-color:var(--color-background-scale-f)}.input-button:where(.readonly .input-button){cursor:default}.input-button:where(:disabled,.disabled .input-button):not(:where(.readonly .input-button)){cursor:not-allowed;opacity:.6}.input-button:not(:disabled,:where(.disabled .input-button),:where(.readonly .input-button),[aria-pressed=true]):focus-visible,.input-button:not(:disabled,:where(.disabled .input-button),:where(.readonly .input-button),[aria-pressed=true]):hover{background-color:var(--color-background-scale-b)}.input-button-bar{gap:--spacing(.5);padding:--spacing(.5);width:-moz-fit-content;width:fit-content}.input-button-bar button:first-child{border-bottom-left-radius:var(--radius-sm);border-top-left-radius:var(--radius-sm)}.input-button-bar button:last-child{border-bottom-right-radius:var(--radius-sm);border-top-right-radius:var(--radius-sm)}}
1
+ @layer components{.input-label{display:block;-webkit-user-select:none;-moz-user-select:none;user-select:none}.input-label.required:after{color:var(--color-danger);content:"*";display:inline-block;margin-inline-start:--spacing(1)}.input-label.disabled{cursor:not-allowed;opacity:.6}.input-label:not(fieldset .input-label,.label-before .input-label,.label-after .input-label),.input-label:where(fieldset legend.input-label){font-size:var(--text-lg);font-variant:small-caps;letter-spacing:.11rem}.input-label-pair{display:flex;flex-direction:column;gap:--spacing(1);margin-block-end:--spacing(1)}.input-label-pair.label-after,.input-label-pair.label-before{align-items:center;flex-direction:row;gap:--spacing(3)}.input-label-pair.label-after .input-label,.input-label-pair.label-below .input-label{order:2}.input-validation{color:var(--color-danger);font-size:var(--text-sm);height:0;line-clamp:2}.input-field{display:flex;flex-direction:column}.input-control{flex-grow:1;padding:--spacing(2) --spacing(3)}.input-control::-moz-placeholder{color:var(--color-global-foreground-accent)}.input-control .placeholder,.input-control::placeholder{color:var(--color-global-foreground-accent)}.input-group{align-items:center;display:flex}.input-group.input-group-loading{cursor:wait}.input-group .input-group-addon{color:var(--color-global-foreground-accent);padding:--spacing(1.5) --spacing(2)}.input-group button.input-group-addon{cursor:pointer}.input-group .input-control{outline:none}.input-control:where(:not(.input-group .input-control)),.input-group{background-color:var(--color-global-background);border-radius:--spacing(1);cursor:pointer;outline:solid 2px var(--color-background-scale-e);overflow:clip;transition:color,background-color,outline-color,box-shadow;transition-duration:calc(var(--default-transition-duration)*2);width:100%}.input-control:where(:not(.input-group .input-control)):where(input[type=text],textarea),.input-group:where(input[type=text],textarea){cursor:text}.input-control:where(:not(.input-group .input-control)):where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-control:where(:not(.input-group .input-control)))),.input-group:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-group)){cursor:default}.input-control:where(:not(.input-group .input-control)):not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-control:where(:not(.input-group .input-control))))):where(.disabled,:disabled:not(.p-hidden-accessible input),:is(.disabled .input-control:where(:not(.input-group .input-control)):not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-control:where(:not(.input-group .input-control))))))),.input-group:not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-group))):where(.disabled,:disabled:not(.p-hidden-accessible input),:is(.disabled .input-group:not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-group))))){cursor:not-allowed;opacity:.6}.input-control:where(:not(.input-group .input-control)):not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-control:where(:not(.input-group .input-control))))):not(:where(.disabled,:disabled:not(.p-hidden-accessible input),:is(.disabled .input-control:where(:not(.input-group .input-control)):not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-control:where(:not(.input-group .input-control)))))))):focus-within,.input-control:where(:not(.input-group .input-control)):not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-control:where(:not(.input-group .input-control))))):not(:where(.disabled,:disabled:not(.p-hidden-accessible input),:is(.disabled .input-control:where(:not(.input-group .input-control)):not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-control:where(:not(.input-group .input-control)))))))):hover,.input-group:not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-group))):not(:where(.disabled,:disabled:not(.p-hidden-accessible input),:is(.disabled .input-group:not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-group)))))):focus-within,.input-group:not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-group))):not(:where(.disabled,:disabled:not(.p-hidden-accessible input),:is(.disabled .input-group:not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-group)))))):hover{outline-color:var(--color-global-foreground-accent)}.input-control:where(:not(.input-group .input-control)):not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-control:where(:not(.input-group .input-control))))):not(:where(.disabled,:disabled:not(.p-hidden-accessible input),:is(.disabled .input-control:where(:not(.input-group .input-control)):not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-control:where(:not(.input-group .input-control)))))))):focus-within,.input-group:not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-group))):not(:where(.disabled,:disabled:not(.p-hidden-accessible input),:is(.disabled .input-group:not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-group)))))):focus-within{box-shadow:var(--shadow-heavy)}.input-control:where(:not(.input-group .input-control)):not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-control:where(:not(.input-group .input-control))))):not(:where(.disabled,:disabled:not(.p-hidden-accessible input),:is(.disabled .input-control:where(:not(.input-group .input-control)):not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-control:where(:not(.input-group .input-control)))))))):not(:has(>input[type=checkbox]),.input-button-bar):focus-within,.input-group:not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-group))):not(:where(.disabled,:disabled:not(.p-hidden-accessible input),:is(.disabled .input-group:not(:where(.readonly,[readonly]:not(.p-hidden-accessible input),:is(.readonly .input-group)))))):not(:has(>input[type=checkbox]),.input-button-bar):focus-within{background-color:var(--color-background-scale-b)}.input-group:where(:has(input[type=checkbox])),.input-group:where(:has(input[type=radio])){flex-shrink:0;height:1.5rem;position:relative;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:1.5rem}.input-group:where(:has(input[type=checkbox])):has(input[type=radio]),.input-group:where(:has(input[type=radio])):has(input[type=radio]){border-radius:var(--radius-full);height:1.2rem;width:1.2rem}.input-group:where(:has(input[type=checkbox])):has(input[type=radio])>div,.input-group:where(:has(input[type=radio])):has(input[type=radio])>div{border-radius:var(--radius-full);height:.7rem;left:.25rem;position:absolute;top:.25rem;transition:box-shadow,background-color;transition-duration:var(--default-transition-duration);width:.7rem}.input-group:where(:has(input[type=checkbox])):has(input[type=radio]):has(:checked)>div,.input-group:where(:has(input[type=radio])):has(input[type=radio]):has(:checked)>div{background-color:var(--color-global-foreground);box-shadow:var(--shadow-md)}.input-group:where(:has(input[type=checkbox])) span[class^=i-],.input-group:where(:has(input[type=checkbox])) svg,.input-group:where(:has(input[type=radio])) span[class^=i-],.input-group:where(:has(input[type=radio])) svg{display:block!important;font-size:1rem;height:1rem!important;line-height:1;margin-left:.25rem;width:1rem!important}.input-group:where(:has(input[type=checkbox])) input,.input-group:where(:has(input[type=radio])) input{-webkit-appearance:none;-moz-appearance:none;appearance:none;height:100%;inset:0;opacity:0;position:absolute;z-index:10}.input-group:where(:has(input[type=checkbox])) input:not(:disabled,[readonly]),.input-group:where(:has(input[type=radio])) input:not(:disabled,[readonly]){cursor:pointer}.input-group:where(:has(input[type=checkbox])).toggle-switch,.input-group:where(:has(input[type=radio])).toggle-switch{border-radius:1rem;width:2.5rem}.input-group:where(:has(input[type=checkbox])).toggle-switch:has(:checked) .slider:before,.input-group:where(:has(input[type=radio])).toggle-switch:has(:checked) .slider:before{background-color:var(--color-global-foreground);transform:translateX(1rem)}.input-group:where(:has(input[type=checkbox])).toggle-switch .slider,.input-group:where(:has(input[type=radio])).toggle-switch .slider{border-radius:1rem;inset:0;position:absolute}.input-group:where(:has(input[type=checkbox])).toggle-switch .slider:before,.input-group:where(:has(input[type=radio])).toggle-switch .slider:before{background-color:var(--color-background-scale-d);border-radius:var(--radius-full);box-shadow:var(--shadow-md);content:"";height:1rem;left:.25rem;position:absolute;top:.25rem;transition:transform,background-color;transition-duration:var(--default-transition-duration);width:1rem}.input-menu .input-control,.input-menu .input-group{background-color:transparent}.input-menu .input-group:where(:has(input[type=checkbox])){height:1.2rem;width:1.2rem}.input-menu .input-group:where(:has(input[type=checkbox])) span[class^=i-],.input-menu .input-group:where(:has(input[type=checkbox])) svg{margin-left:.1rem;margin-top:-.1rem}[aria-disabled=true] .input-menu .input-group:where(:has(input[type=checkbox])):focus-within,[aria-disabled=true] .input-menu .input-group:where(:has(input[type=checkbox])):hover{outline-color:var(--color-background-scale-e)!important}[aria-disabled=true] .input-menu .input-group:where(:has(input[type=checkbox])):focus-within input,[aria-disabled=true] .input-menu .input-group:where(:has(input[type=checkbox])):hover input{cursor:not-allowed!important}.input-menu-header{align-items:center;display:flex;gap:--spacing(2)}.input-menu-header .input-menu-filter{align-items:center;color:var(--color-global-foreground-accent);display:flex;flex-grow:1;transition:color var(--default-transition-duration) var(--default-transition-timing-function)}.input-menu-header .input-menu-filter input{flex-grow:1;outline:none;padding:--spacing(.5) 0}.input-menu-header .input-menu-filter input::-moz-placeholder{color:var(--color-global-foreground-accent)}.input-menu-header .input-menu-filter input::placeholder{color:var(--color-global-foreground-accent)}.input-menu-header .input-menu-filter:focus-within{color:var(--color-global-foreground)}.input-button{cursor:pointer;padding:--spacing(1.5) --spacing(2);transition:background-color var(--default-transition-duration) var(--default-transition-timing-function)}.input-button[aria-pressed=true]{background-color:var(--color-background-scale-f)}.input-button:where(.readonly .input-button){cursor:default}.input-button:where(:disabled,.disabled .input-button):not(:where(.readonly .input-button)){cursor:not-allowed;opacity:.6}.input-button:not(:disabled,:where(.disabled .input-button),:where(.readonly .input-button),[aria-pressed=true]):focus-visible,.input-button:not(:disabled,:where(.disabled .input-button),:where(.readonly .input-button),[aria-pressed=true]):hover{background-color:var(--color-background-scale-b)}.input-button-bar{gap:--spacing(.5);padding:--spacing(.5);width:-moz-fit-content;width:fit-content}.input-button-bar button:first-child{border-bottom-left-radius:var(--radius-sm);border-top-left-radius:var(--radius-sm)}.input-button-bar button:last-child{border-bottom-right-radius:var(--radius-sm);border-top-right-radius:var(--radius-sm)}}
@@ -1,5 +1,5 @@
1
1
  import type { FormFieldState } from '@primevue/forms';
2
- import { type InjectionKey } from 'vue';
2
+ import { type InjectionKey, type MaybeRefOrGetter } from 'vue';
3
3
  import type { FormInstance, FormValues } from '../types/form.js';
4
4
  /**
5
5
  * The injection key for acquiring a {@link FormInstance} object within the
@@ -24,11 +24,40 @@ export declare function provideFormInstance(): FormInstance;
24
24
  /**
25
25
  * Injects the stateful object provided by an ancestor Form instance. If not
26
26
  * available, a dummy state object is generated.
27
+ *
28
+ * @private
27
29
  */
28
30
  export declare function injectFormInstance(): FormInstance;
29
31
  /**
30
32
  * Takes a Primevue Form's `states` object, and extracts the current values
31
33
  * of each named property. In doing so, it will deref proxies, and create
32
34
  * shallow clones of any arrays/objects it encounters.
35
+ *
36
+ * @private
33
37
  */
34
38
  export declare function getValuesFromFormState<T extends FormValues = FormValues>(state: Record<string, FormFieldState>): T;
39
+ /**
40
+ * @private
41
+ */
42
+ export type UseOptionListMethodsConfig = {
43
+ optionLabel?: string | ((data: unknown) => string);
44
+ optionValue?: string | ((data: unknown) => string);
45
+ optionIcon?: string | ((data: unknown) => string);
46
+ optionDisabled?: string | ((data: unknown) => boolean);
47
+ optionDataKey?: string;
48
+ };
49
+ /**
50
+ * Creates a set of common methods for interacting with an array of "options" like what
51
+ * gets provided to a Select or RadioGroup.
52
+ *
53
+ * @private
54
+ */
55
+ export declare function useOptionListMethods(config: MaybeRefOrGetter<UseOptionListMethodsConfig>): import("vue").ComputedRef<{
56
+ getOptionLabel: (option: unknown) => string;
57
+ getOptionValue: (option: unknown) => unknown;
58
+ getOptionIcon: (option: unknown) => string | undefined;
59
+ getOptionDisabled: (option: unknown) => boolean;
60
+ getOptionRenderKey: (option: unknown) => string | number | undefined;
61
+ getOptionIndex: (needle: unknown, haystack: unknown) => number;
62
+ getOptionByValue: <T>(needle: unknown, haystack: MaybeRefOrGetter<T[] | undefined>) => T | undefined;
63
+ }>;
@@ -1,5 +1,8 @@
1
+ import { equals, resolveFieldData } from "@primeuix/utils/object";
2
+ import { isNumber } from "@resee-movies/utilities/numbers/is-number";
1
3
  import { isObjectLike } from "@resee-movies/utilities/objects/is-object-like";
2
- import { inject, provide, ref, toRaw, toValue } from "vue";
4
+ import { isString } from "@resee-movies/utilities/strings/is-string";
5
+ import { computed, inject, provide, ref, toRaw, toValue } from "vue";
3
6
  export const FormSymbol = Symbol("form");
4
7
  export function createFormInstance() {
5
8
  return {
@@ -32,3 +35,54 @@ export function getValuesFromFormState(state) {
32
35
  }
33
36
  return result;
34
37
  }
38
+ export function useOptionListMethods(config) {
39
+ return computed(() => {
40
+ const {
41
+ optionLabel,
42
+ optionValue,
43
+ optionIcon,
44
+ optionDisabled,
45
+ optionDataKey
46
+ } = toValue(config);
47
+ const getOptionLabel = (option) => {
48
+ return String(optionLabel ? resolveFieldData(option, optionLabel) : option);
49
+ };
50
+ const getOptionValue = (option) => {
51
+ return optionValue ? resolveFieldData(option, optionValue) : option;
52
+ };
53
+ const getOptionIcon = (option) => {
54
+ return optionIcon ? String(resolveFieldData(option, optionIcon)) : void 0;
55
+ };
56
+ const getOptionDisabled = (option) => {
57
+ return Boolean(optionDisabled ? resolveFieldData(option, optionDisabled) : false);
58
+ };
59
+ const getOptionRenderKey = (option) => {
60
+ const result = optionDataKey ? resolveFieldData(option, optionDataKey) : getOptionLabel(option);
61
+ return isString(result) || isNumber(result) ? result : void 0;
62
+ };
63
+ const getOptionIndex = (needle, haystack) => {
64
+ const rawHaystack = toValue(haystack);
65
+ const optionValue2 = getOptionValue(needle);
66
+ const equalityKey = optionValue2 ? void 0 : optionDataKey;
67
+ if (Array.isArray(rawHaystack)) {
68
+ return rawHaystack.findIndex((entry) => equals(entry, optionValue2, equalityKey));
69
+ }
70
+ return equals(rawHaystack, optionValue2, equalityKey) ? 0 : -1;
71
+ };
72
+ const getOptionByValue = (needle, haystack) => {
73
+ const equalityKey = optionValue ? void 0 : optionDataKey;
74
+ return toValue(haystack)?.find(
75
+ (option) => equals(getOptionValue(option), needle, equalityKey)
76
+ );
77
+ };
78
+ return {
79
+ getOptionLabel,
80
+ getOptionValue,
81
+ getOptionIcon,
82
+ getOptionDisabled,
83
+ getOptionRenderKey,
84
+ getOptionIndex,
85
+ getOptionByValue
86
+ };
87
+ });
88
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@resee-movies/nuxt-ux",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "The next-gen user experience library for ReSee Movies - currently in development. ",
5
5
  "repository": {
6
6
  "url": "https://github.com/ReSee-Movies/nuxt-ux.git"