@resee-movies/nuxt-ux 0.12.0 → 0.13.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.12.0",
4
+ "version": "0.13.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.0"
package/dist/module.mjs CHANGED
@@ -244,6 +244,12 @@ const module$1 = defineNuxtModule({
244
244
  nuxt.hook("modules:done", async () => {
245
245
  await importCSS(nuxt, { sources, plugins, imports });
246
246
  });
247
+ const inlineStyles = nuxt.options.app.head.style ?? [];
248
+ inlineStyles.push({
249
+ tagPriority: "critical",
250
+ textContent: "@layer properties, theme, base, components, utilities;"
251
+ });
252
+ nuxt.options.app.head.style = inlineStyles;
247
253
  let primePassthroughFilename = await resolver.resolvePath("./runtime/theme/primevue/index", {
248
254
  extensions: ["ts", "js"]
249
255
  });
@@ -7,7 +7,7 @@
7
7
  :validate-on-submit = "true"
8
8
  :validate-on-value-update = "true"
9
9
  :validate-on-blur = "false"
10
- :initial-values = "values"
10
+ :initial-values = "props.initialValues ?? values"
11
11
  :aria-disabled = "props.disabled"
12
12
  @submit = "handleFormSubmit"
13
13
  >
@@ -37,7 +37,9 @@ import { provideFormInstance, getValuesFromFormState } from "../../utils/form";
37
37
  const props = defineProps({
38
38
  disabled: { type: Boolean, required: false, default: false },
39
39
  onSubmit: { type: [Function, Array], required: false, default: void 0 },
40
- onChange: { type: Function, required: false, default: void 0 },
40
+ onChange: { type: [Function, Array], required: false, default: void 0 },
41
+ changeDelay: { type: Number, required: false, default: 1 },
42
+ initialValues: { type: Object, required: false, default: void 0 },
41
43
  fields: { type: Array, required: false, default: void 0 }
42
44
  });
43
45
  const form = useTemplateRef("form");
@@ -45,18 +47,13 @@ const values = defineModel("values", { type: null, ...{ default: void 0 } });
45
47
  defineEmits(["submit", "change"]);
46
48
  const formInstance = provideFormInstance();
47
49
  syncRefs(() => props.disabled, formInstance.isDisabled);
48
- const emitOnChange = useDebounceFn(() => {
49
- const state = form.value?.states;
50
- if (state) {
51
- props.onChange?.(getValuesFromFormState(state));
52
- }
53
- }, 1);
54
50
  useReactiveObjectsSync({
55
51
  left: () => form.value?.states,
56
52
  right: () => values.value,
57
53
  keySource: "left",
54
+ debounceMsLtr: props.changeDelay,
55
+ onChange: () => handleOnChange(),
58
56
  leftOptions: {
59
- onChange: () => emitOnChange(),
60
57
  getter(obj, key) {
61
58
  return obj[key]?.value;
62
59
  },
@@ -65,11 +62,23 @@ useReactiveObjectsSync({
65
62
  obj[key].value = val ?? null;
66
63
  }
67
64
  }
68
- },
69
- rightOptions: {
70
- onChange: () => emitOnChange()
71
65
  }
72
66
  });
67
+ async function handleOnChange() {
68
+ const state = form.value?.states;
69
+ if (state && props.onChange) {
70
+ const newEvent = {
71
+ values: getValuesFromFormState(state),
72
+ valid: form.value?.valid ?? true
73
+ };
74
+ const results = toNonNullableArray(props.onChange).map((handler) => handler(newEvent));
75
+ if (!!results.find((result) => isPromiseLike(result))) {
76
+ formInstance.isSubmitting.value = true;
77
+ await Promise.allSettled(results);
78
+ formInstance.isSubmitting.value = false;
79
+ }
80
+ }
81
+ }
73
82
  async function handleFormSubmit(event) {
74
83
  if (props.disabled) {
75
84
  return;
@@ -1,10 +1,13 @@
1
- import type { FormSubmitEvent, FormSubmitHandler, FormValues } from '../../types/form.js';
1
+ import type { FormChangeEvent } from '../../types/form.js';
2
+ import type { FormChangeHandler, FormSubmitEvent, FormSubmitHandler, FormValues } from '../../types/form.js';
2
3
  import type { FormFieldBuilderOption } from './FormFieldBuilder.vue.js';
3
4
  export * from '../../types/form.js';
4
5
  export interface FormProps<T extends FormValues = FormValues> {
5
6
  disabled?: boolean;
6
7
  onSubmit?: FormSubmitHandler<T> | FormSubmitHandler<T>[];
7
- onChange?: (values: T | null) => void;
8
+ onChange?: FormChangeHandler<T> | FormChangeHandler<T>[];
9
+ changeDelay?: number;
10
+ initialValues?: Partial<T>;
8
11
  fields?: FormFieldBuilderOption[];
9
12
  }
10
13
  declare const __VLS_export: <T extends FormValues>(__VLS_props: NonNullable<Awaited<typeof __VLS_setup>>["props"], __VLS_ctx?: __VLS_PrettifyLocal<Pick<NonNullable<Awaited<typeof __VLS_setup>>, "attrs" | "emit" | "slots">>, __VLS_expose?: NonNullable<Awaited<typeof __VLS_setup>>["expose"], __VLS_setup?: Promise<{
@@ -12,7 +15,7 @@ declare const __VLS_export: <T extends FormValues>(__VLS_props: NonNullable<Awai
12
15
  values?: Partial<T> | undefined;
13
16
  }) & __VLS_EmitsToProps<__VLS_NormalizeEmits<{
14
17
  (e: "submit", evt: FormSubmitEvent<T>): (void | Promise<void>);
15
- (e: "change", values: T): void;
18
+ (e: "change", evt: FormChangeEvent<T>): (void | Promise<void>);
16
19
  } & ((evt: "update:values", value: Partial<T> | undefined) => void)>>> & import("vue").PublicProps & (typeof globalThis extends {
17
20
  __VLS_PROPS_FALLBACK: infer P;
18
21
  } ? P : {});
@@ -33,7 +36,7 @@ declare const __VLS_export: <T extends FormValues>(__VLS_props: NonNullable<Awai
33
36
  };
34
37
  emit: {
35
38
  (e: "submit", evt: FormSubmitEvent<T>): (void | Promise<void>);
36
- (e: "change", values: T): void;
39
+ (e: "change", evt: FormChangeEvent<T>): (void | Promise<void>);
37
40
  } & ((evt: "update:values", value: Partial<T> | undefined) => void);
38
41
  }>) => import("vue").VNode & {
39
42
  __ctx?: Awaited<typeof __VLS_setup>;
@@ -0,0 +1,24 @@
1
+ import { type Ref, type WatchOptions } from 'vue';
2
+ export type Side = 'left' | 'right';
3
+ /**
4
+ * Configuration for the {@link useDebouncedSyncRef} composable.
5
+ */
6
+ export type UseDebouncedSyncRefOptions<L = unknown, R = unknown> = WatchOptions & {
7
+ direction?: 'both' | Side;
8
+ debounceMs?: number;
9
+ debounceMsLtr?: number;
10
+ debounceMsRtl?: number;
11
+ onChange?: <S extends Side>(side: S, value: S extends 'left' ? L : R) => void;
12
+ };
13
+ /**
14
+ * Two-way refs synchronization, with the option to debounce changes going
15
+ * in each direction.
16
+ *
17
+ * This is a simplified version of the Vueuse `syncRef` utility.
18
+ * @see https://vueuse.org/shared/syncRef/
19
+ */
20
+ export declare function useDebouncedSyncRef<L, R>(left: Ref<L>, right: Ref<R>, options?: UseDebouncedSyncRefOptions<L, R>): {
21
+ stop: () => void;
22
+ pause: () => void;
23
+ resume: () => void;
24
+ };
@@ -0,0 +1,48 @@
1
+ import { watchDebounced } from "@vueuse/core";
2
+ export function useDebouncedSyncRef(left, right, options) {
3
+ const watchers = [];
4
+ const stop = () => watchers.forEach((handle) => handle.stop());
5
+ const pause = () => watchers.forEach((handle) => handle.pause());
6
+ const resume = () => watchers.forEach((handle) => handle.resume());
7
+ const watchOptions = {
8
+ flush: options?.flush ?? "sync",
9
+ deep: options?.deep ?? false,
10
+ immediate: options?.immediate ?? false
11
+ };
12
+ const syncDirection = options?.direction ?? "both";
13
+ const debounceLeft = options?.debounceMsLtr ?? options?.debounceMs ?? 0;
14
+ const debounceRight = options?.debounceMsRtl ?? options?.debounceMs ?? 0;
15
+ let ignoreLeft = false;
16
+ let ignoreRight = false;
17
+ if (syncDirection === "both" || syncDirection === "left") {
18
+ watchers.push(watchDebounced(
19
+ left,
20
+ (newValue) => {
21
+ if (ignoreLeft) {
22
+ ignoreLeft = false;
23
+ return;
24
+ }
25
+ ignoreRight = true;
26
+ right.value = newValue;
27
+ options?.onChange?.("right", right.value);
28
+ },
29
+ { ...watchOptions, debounce: debounceLeft }
30
+ ));
31
+ }
32
+ if (syncDirection === "both" || syncDirection === "right") {
33
+ watchers.push(watchDebounced(
34
+ right,
35
+ (newValue) => {
36
+ if (ignoreRight) {
37
+ ignoreRight = false;
38
+ return;
39
+ }
40
+ ignoreLeft = true;
41
+ left.value = newValue;
42
+ options?.onChange?.("left", left.value);
43
+ },
44
+ { ...watchOptions, debounce: debounceRight }
45
+ ));
46
+ }
47
+ return { stop, pause, resume };
48
+ }
@@ -1,7 +1,13 @@
1
1
  /**
2
2
  * Two-way bind to URL query parameters.
3
3
  */
4
- export declare function useQueryParameters<D extends QueryParamDefinitionMap>(params: D): UseQueryParametersReturn<D>;
4
+ export declare function useQueryParameters<D extends QueryParamDefinitionMap>(params: D, options?: UseQueryParametersOptions): UseQueryParametersReturn<D>;
5
+ /**
6
+ * Configuration for the {@link useQueryParameters} composable.
7
+ */
8
+ export type UseQueryParametersOptions = {
9
+ mode?: 'replace' | 'push';
10
+ };
5
11
  /**
6
12
  * Configuration for a single URL query parameter.
7
13
  */
@@ -3,13 +3,13 @@ import { areEqual } from "@resee-movies/utilities/objects/are-equal";
3
3
  import { isString } from "@resee-movies/utilities/strings/is-string";
4
4
  import { slugify } from "@resee-movies/utilities/strings/slugify";
5
5
  import { customRef, nextTick, onScopeDispose, reactive, toValue, watch } from "vue";
6
- export function useQueryParameters(params) {
6
+ export function useQueryParameters(params, options) {
7
7
  const router = useRouter();
8
- const queue = getQueueForRouter(router);
8
+ const queue = getQueueForRouter(router, options?.mode);
9
9
  const refs = {};
10
10
  for (const [name, definition] of Object.entries(params)) {
11
11
  refs[name] = getQueryRef({
12
- slug: slugify(name),
12
+ slug: slugify(name, { decamelize: true }),
13
13
  type: definition.type,
14
14
  setCallback: queue.add,
15
15
  defaultValue: definition.defaultValue
@@ -18,7 +18,7 @@ export function useQueryParameters(params) {
18
18
  return reactive(refs);
19
19
  }
20
20
  const _queue = /* @__PURE__ */ new WeakMap();
21
- function getQueueForRouter(router) {
21
+ function getQueueForRouter(router, mode = "push") {
22
22
  if (!_queue.has(router)) {
23
23
  _queue.set(router, /* @__PURE__ */ new Map());
24
24
  }
@@ -32,7 +32,7 @@ function getQueueForRouter(router) {
32
32
  const newValues = Object.fromEntries(routerQueue.entries());
33
33
  routerQueue.clear();
34
34
  const { params, query, hash } = router.currentRoute.value;
35
- await router.replace({
35
+ await router[mode]({
36
36
  params,
37
37
  query: { ...query, ...newValues },
38
38
  hash
@@ -1,18 +1,23 @@
1
1
  import { type MaybeRefOrGetter } from 'vue';
2
+ import { type UseDebouncedSyncRefOptions, type Side } from './use-debounced-sync-ref.js';
2
3
  export type SyncObject = Record<string | number | symbol, unknown>;
3
4
  export type KeyOf<S extends SyncObject> = keyof S;
5
+ export type KeyOfSide<S extends Side, L extends SyncObject, R extends SyncObject> = S extends 'left' ? KeyOf<L> : KeyOf<R>;
4
6
  export type Getter<S extends SyncObject> = <R>(obj: S, key: KeyOf<S>) => R;
5
7
  export type Setter<S extends SyncObject> = (obj: S, key: KeyOf<S>, value: unknown) => void;
6
8
  export type ComputedReadWriteOptions<S extends SyncObject> = {
7
9
  getter?: Getter<S>;
8
10
  setter?: Setter<S>;
9
- onChange?: (key: KeyOf<S>, value: unknown) => void;
10
11
  };
11
12
  export type UseReactiveObjectsSyncOptions<L extends SyncObject, R extends SyncObject> = {
12
13
  left: MaybeRefOrGetter<L | undefined>;
13
14
  right: MaybeRefOrGetter<R | undefined>;
14
- keySource?: 'left' | 'right' | (KeyOf<L> | KeyOf<R>)[];
15
+ keySource?: Side | (KeyOf<L> | KeyOf<R>)[];
16
+ onChange?: <S extends Side>(side: S, key: KeyOfSide<S, L, R>, value: unknown) => void;
15
17
  leftOptions?: ComputedReadWriteOptions<L>;
16
18
  rightOptions?: ComputedReadWriteOptions<R>;
17
- };
19
+ } & Omit<UseDebouncedSyncRefOptions, 'onChange'>;
20
+ /**
21
+ * Provides two-way binding between a pair of reactive objects.
22
+ */
18
23
  export declare function useReactiveObjectsSync<L extends SyncObject, R extends SyncObject>(options: UseReactiveObjectsSyncOptions<L, R>): void;
@@ -1,4 +1,3 @@
1
- import { syncRef } from "@vueuse/core";
2
1
  import {
3
2
  toValue,
4
3
  watchEffect,
@@ -6,6 +5,7 @@ import {
6
5
  computed,
7
6
  shallowRef
8
7
  } from "vue";
8
+ import { useDebouncedSyncRef } from "./use-debounced-sync-ref.js";
9
9
  export function useReactiveObjectsSync(options) {
10
10
  const { left, right } = options;
11
11
  const watchKeys = aggregateKeys(options);
@@ -24,8 +24,16 @@ export function useReactiveObjectsSync(options) {
24
24
  }
25
25
  const leftRef = computedReadWrite(left, key, options?.leftOptions);
26
26
  const rightRef = computedReadWrite(right, key, options?.rightOptions);
27
- const stopSync = syncRef(leftRef, rightRef);
28
- syncHandle.set(key, { leftRef, rightRef, stopSync });
27
+ const handles = useDebouncedSyncRef(leftRef, rightRef, {
28
+ direction: options?.direction,
29
+ debounceMs: options?.debounceMs,
30
+ debounceMsLtr: options?.debounceMsLtr,
31
+ debounceMsRtl: options?.debounceMsRtl,
32
+ onChange(side, value) {
33
+ options?.onChange?.(side, key, value);
34
+ }
35
+ });
36
+ syncHandle.set(key, { leftRef, rightRef, stopSync: handles.stop });
29
37
  }
30
38
  });
31
39
  }
@@ -66,7 +74,6 @@ function computedReadWrite(source, key, options) {
66
74
  } else {
67
75
  target[key] = val;
68
76
  }
69
- options?.onChange?.(key, val);
70
77
  }
71
78
  }
72
79
  });
@@ -1 +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:not(fieldset .input-label,.label-before .input-label,.label-after .input-label),.input-label:where(fieldset legend.input-label){font-size:var(--text-lg)}.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],input[type=email],input[type=url],textarea),.input-group:where(input[type=text],input[type=email],input[type=url],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
+ @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)}.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{display:block;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],input[type=email],input[type=url],textarea),.input-group:where(input[type=text],input[type=email],input[type=url],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)}}
@@ -14,10 +14,21 @@ export interface FormInstance {
14
14
  isDisabled: Ref<boolean>;
15
15
  }
16
16
  /**
17
- * The event argument of a Form's `onsubmit` callback.
17
+ * The event argument of a Form's `onSubmit` callback.
18
18
  */
19
19
  export type FormSubmitEvent<T extends FormValues = FormValues> = PrimeFormSubmitEvent<T>;
20
20
  /**
21
- * The `onsubmit` callback method of a Form.
21
+ * The `onSubmit` callback method of a Form.
22
22
  */
23
23
  export type FormSubmitHandler<T extends FormValues = FormValues> = (event: FormSubmitEvent<T>) => void | Promise<void>;
24
+ /**
25
+ * The event argument of a Form's `onChange` callback.
26
+ */
27
+ export type FormChangeEvent<T extends FormValues = FormValues> = {
28
+ values: T | null;
29
+ valid: boolean;
30
+ };
31
+ /**
32
+ * The `onChange` callback method of a Form.
33
+ */
34
+ export type FormChangeHandler<T extends FormValues = FormValues> = (event: FormChangeEvent<T>) => void | Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@resee-movies/nuxt-ux",
3
- "version": "0.12.0",
3
+ "version": "0.13.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"
@@ -36,7 +36,7 @@
36
36
  ],
37
37
  "scripts": {
38
38
  "dev": "npm run dev:prepare && nuxi dev playground --local --port 3003",
39
- "dev:preview": "npm run dev:build && nuxi preview playground",
39
+ "dev:preview": "npm run dev:build && nuxi preview playground --port 3003",
40
40
  "dev:build": "nuxi build playground",
41
41
  "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
42
42
  "prepare": "nuxt-module-build prepare && nuxt-module-build build",
@@ -79,7 +79,7 @@
79
79
  "@primeuix/utils": "^0.6.1",
80
80
  "@primevue/forms": "^4.5.1",
81
81
  "@primevue/nuxt-module": "^4.5.1",
82
- "@resee-movies/utilities": "^0.10.0",
82
+ "@resee-movies/utilities": "^0.11.0",
83
83
  "@tailwindcss/postcss": "^4.1.17",
84
84
  "@tailwindcss/vite": "^4.1.17",
85
85
  "@vueuse/core": "^14.1.0",