nuance-ui 0.2.18 → 0.2.20

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 (57) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/center.d.vue.ts +18 -0
  3. package/dist/runtime/components/center.vue +26 -0
  4. package/dist/runtime/components/center.vue.d.ts +18 -0
  5. package/dist/runtime/components/dialog/index.d.ts +2 -0
  6. package/dist/runtime/components/dialog/ui/dialog-footer.d.vue.ts +16 -0
  7. package/dist/runtime/components/dialog/ui/dialog-footer.vue +16 -0
  8. package/dist/runtime/components/dialog/ui/dialog-footer.vue.d.ts +16 -0
  9. package/dist/runtime/components/dialog/ui/dialog-root.d.vue.ts +2 -2
  10. package/dist/runtime/components/dialog/ui/dialog-root.vue +9 -4
  11. package/dist/runtime/components/dialog/ui/dialog-root.vue.d.ts +2 -2
  12. package/dist/runtime/components/dialog/ui/dialog-section.d.vue.ts +17 -0
  13. package/dist/runtime/components/dialog/ui/dialog-section.vue +17 -0
  14. package/dist/runtime/components/dialog/ui/dialog-section.vue.d.ts +17 -0
  15. package/dist/runtime/components/dialog/ui/dialog.module.css +1 -1
  16. package/dist/runtime/components/drawer/drawer-footer.d.vue.ts +16 -0
  17. package/dist/runtime/components/drawer/drawer-footer.vue +13 -0
  18. package/dist/runtime/components/drawer/drawer-footer.vue.d.ts +16 -0
  19. package/dist/runtime/components/drawer/drawer-root.vue +1 -0
  20. package/dist/runtime/components/drawer/drawer-section.d.vue.ts +16 -0
  21. package/dist/runtime/components/drawer/drawer-section.vue +14 -0
  22. package/dist/runtime/components/drawer/drawer-section.vue.d.ts +16 -0
  23. package/dist/runtime/components/drawer/index.d.ts +2 -0
  24. package/dist/runtime/components/group.d.vue.ts +28 -0
  25. package/dist/runtime/components/group.vue +39 -0
  26. package/dist/runtime/components/group.vue.d.ts +28 -0
  27. package/dist/runtime/components/input/ui/input-base.d.vue.ts +1 -0
  28. package/dist/runtime/components/input/ui/input-base.vue +3 -2
  29. package/dist/runtime/components/input/ui/input-base.vue.d.ts +1 -0
  30. package/dist/runtime/components/modal/index.d.ts +0 -2
  31. package/dist/runtime/components/modal/modal-footer.d.vue.ts +14 -0
  32. package/dist/runtime/components/modal/modal-footer.vue +13 -0
  33. package/dist/runtime/components/modal/modal-footer.vue.d.ts +14 -0
  34. package/dist/runtime/components/modal/modal-header.d.vue.ts +1 -3
  35. package/dist/runtime/components/modal/modal-header.vue.d.ts +1 -3
  36. package/dist/runtime/components/modal/modal-root.d.vue.ts +1 -2
  37. package/dist/runtime/components/modal/modal-root.vue +3 -1
  38. package/dist/runtime/components/modal/modal-root.vue.d.ts +1 -2
  39. package/dist/runtime/components/modal/modal-section.d.vue.ts +14 -0
  40. package/dist/runtime/components/modal/modal-section.vue +14 -0
  41. package/dist/runtime/components/modal/modal-section.vue.d.ts +14 -0
  42. package/dist/runtime/components/modal/modal-title.d.vue.ts +1 -3
  43. package/dist/runtime/components/modal/modal-title.vue.d.ts +1 -3
  44. package/dist/runtime/components/pin-input/lib.d.ts +2 -0
  45. package/dist/runtime/components/pin-input/lib.js +19 -0
  46. package/dist/runtime/components/pin-input/pin-input.d.vue.ts +55 -0
  47. package/dist/runtime/components/pin-input/pin-input.vue +171 -0
  48. package/dist/runtime/components/pin-input/pin-input.vue.d.ts +55 -0
  49. package/dist/runtime/components/pin-input/use-pin-input.d.ts +18 -0
  50. package/dist/runtime/components/pin-input/use-pin-input.js +94 -0
  51. package/dist/runtime/components/stack.d.vue.ts +24 -0
  52. package/dist/runtime/components/stack.vue +33 -0
  53. package/dist/runtime/components/stack.vue.d.ts +24 -0
  54. package/dist/runtime/modals/_confirm-modal/confirm-modal.vue +16 -6
  55. package/dist/runtime/styles/const.css +1 -1
  56. package/dist/runtime/types/styling.d.ts +2 -1
  57. package/package.json +1 -1
@@ -1,11 +1,9 @@
1
1
  import type { DialogHeaderProps } from '../dialog/index.js';
2
- export interface ModalHeaderProps extends DialogHeaderProps {
3
- }
4
2
  declare var __VLS_8: {};
5
3
  type __VLS_Slots = {} & {
6
4
  default?: (props: typeof __VLS_8) => any;
7
5
  };
8
- declare const __VLS_base: import("vue").DefineComponent<ModalHeaderProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<ModalHeaderProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
6
+ declare const __VLS_base: import("vue").DefineComponent<DialogHeaderProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<DialogHeaderProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
7
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
10
8
  declare const _default: typeof __VLS_export;
11
9
  export default _default;
@@ -1,6 +1,5 @@
1
1
  import type { DialogModel, DialogRootProps } from '../dialog/index.js';
2
- type OmittedDialogRootProps = Omit<DialogRootProps, 'rootClass'>;
3
- export interface ModalRootProps extends OmittedDialogRootProps {
2
+ export interface ModalRootProps extends DialogRootProps {
4
3
  /** If set, the modal is centered vertically @default `false` */
5
4
  centered?: boolean;
6
5
  /** If set, the modal takes the entire screen @default `false` */
@@ -8,6 +8,7 @@ const {
8
8
  withoutOverlay = false,
9
9
  withinPortal = false,
10
10
  closeOnClickOutside = true,
11
+ classes,
11
12
  ...rest
12
13
  } = defineProps({
13
14
  centered: { type: Boolean, required: false },
@@ -15,6 +16,7 @@ const {
15
16
  closeOnClickOutside: { type: Boolean, required: false },
16
17
  withinPortal: { type: Boolean, required: false },
17
18
  withoutOverlay: { type: Boolean, required: false },
19
+ classes: { type: Object, required: false },
18
20
  portalTarget: { type: [String, Object, null], required: false },
19
21
  is: { type: null, required: false },
20
22
  mod: { type: [Object, Array, null], required: false },
@@ -34,11 +36,11 @@ const opened = defineModel("open", { type: Boolean, ...{ default: false } });
34
36
  <DialogRoot
35
37
  v-model:open='opened'
36
38
  :mod='[{ centered, "full-screen": fullScreen }, mod]'
37
- :root-class='css.root'
38
39
  :within-portal
39
40
  :close-on-click-outside
40
41
  :without-overlay
41
42
  v-bind='rest'
43
+ :classes='{ ...classes, root: [css.root, classes?.root] }'
42
44
  @open='$emit("open")'
43
45
  @close='$emit("close")'
44
46
  >
@@ -1,6 +1,5 @@
1
1
  import type { DialogModel, DialogRootProps } from '../dialog/index.js';
2
- type OmittedDialogRootProps = Omit<DialogRootProps, 'rootClass'>;
3
- export interface ModalRootProps extends OmittedDialogRootProps {
2
+ export interface ModalRootProps extends DialogRootProps {
4
3
  /** If set, the modal is centered vertically @default `false` */
5
4
  centered?: boolean;
6
5
  /** If set, the modal takes the entire screen @default `false` */
@@ -0,0 +1,14 @@
1
+ import type { DialogSectionProps } from '../dialog/index.js';
2
+ declare var __VLS_8: {};
3
+ type __VLS_Slots = {} & {
4
+ default?: (props: typeof __VLS_8) => any;
5
+ };
6
+ declare const __VLS_base: import("vue").DefineComponent<DialogSectionProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<DialogSectionProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
8
+ declare const _default: typeof __VLS_export;
9
+ export default _default;
10
+ type __VLS_WithSlots<T, S> = T & {
11
+ new (): {
12
+ $slots: S;
13
+ };
14
+ };
@@ -0,0 +1,14 @@
1
+ <script setup>
2
+ import DialogSection from "../dialog/ui/dialog-section.vue";
3
+ const props = defineProps({
4
+ bordered: { type: Boolean, required: false },
5
+ is: { type: null, required: false },
6
+ mod: { type: [Object, Array, null], required: false }
7
+ });
8
+ </script>
9
+
10
+ <template>
11
+ <DialogSection v-bind='props'>
12
+ <slot />
13
+ </DialogSection>
14
+ </template>
@@ -0,0 +1,14 @@
1
+ import type { DialogSectionProps } from '../dialog/index.js';
2
+ declare var __VLS_8: {};
3
+ type __VLS_Slots = {} & {
4
+ default?: (props: typeof __VLS_8) => any;
5
+ };
6
+ declare const __VLS_base: import("vue").DefineComponent<DialogSectionProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<DialogSectionProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
7
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
8
+ declare const _default: typeof __VLS_export;
9
+ export default _default;
10
+ type __VLS_WithSlots<T, S> = T & {
11
+ new (): {
12
+ $slots: S;
13
+ };
14
+ };
@@ -1,11 +1,9 @@
1
1
  import type { DialogTitleProps } from '../dialog/index.js';
2
- export interface ModalTitleProps extends DialogTitleProps {
3
- }
4
2
  declare var __VLS_8: {};
5
3
  type __VLS_Slots = {} & {
6
4
  default?: (props: typeof __VLS_8) => any;
7
5
  };
8
- declare const __VLS_base: import("vue").DefineComponent<ModalTitleProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<ModalTitleProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
6
+ declare const __VLS_base: import("vue").DefineComponent<DialogTitleProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<DialogTitleProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
7
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
10
8
  declare const _default: typeof __VLS_export;
11
9
  export default _default;
@@ -1,11 +1,9 @@
1
1
  import type { DialogTitleProps } from '../dialog/index.js';
2
- export interface ModalTitleProps extends DialogTitleProps {
3
- }
4
2
  declare var __VLS_8: {};
5
3
  type __VLS_Slots = {} & {
6
4
  default?: (props: typeof __VLS_8) => any;
7
5
  };
8
- declare const __VLS_base: import("vue").DefineComponent<ModalTitleProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<ModalTitleProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
6
+ declare const __VLS_base: import("vue").DefineComponent<DialogTitleProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<DialogTitleProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
9
7
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
10
8
  declare const _default: typeof __VLS_export;
11
9
  export default _default;
@@ -0,0 +1,2 @@
1
+ export declare function pinValidate(code: string, type: 'alphanumeric' | 'number' | RegExp): boolean;
2
+ export declare function createPinArray(length: number, value: string): string[];
@@ -0,0 +1,19 @@
1
+ const regex = {
2
+ number: /^\d+$/,
3
+ alphanumeric: /^[a-z0-9]+$/i
4
+ };
5
+ export function pinValidate(code, type) {
6
+ const re = type instanceof RegExp ? type : regex[type] ?? null;
7
+ return re?.test(code);
8
+ }
9
+ export function createPinArray(length, value) {
10
+ if (length < 1)
11
+ return [];
12
+ const values = Array.from({ length }).fill("");
13
+ if (value) {
14
+ const splitted = value.trim().split("");
15
+ for (let i = 0; i < Math.min(length, splitted.length); i += 1)
16
+ values[i] = splitted[i] === " " ? "" : splitted[i];
17
+ }
18
+ return values;
19
+ }
@@ -0,0 +1,55 @@
1
+ import type { NuanceRadius, NuanceSize, NuanceSpacing } from '../../types/index.js';
2
+ import type { BoxProps } from '../box.vue.js';
3
+ export interface PinInputEmits {
4
+ complete: [value: string];
5
+ }
6
+ export interface PinInputProps extends BoxProps {
7
+ /** Id auto-generated if not provided */
8
+ id?: string;
9
+ /** Hidden input `name` attribute */
10
+ name?: string;
11
+ /** Key of `theme.spacing` or any valid CSS value to set `gap` between inputs, numbers are converted to rem @default 'md' */
12
+ gap?: NuanceSpacing;
13
+ /** Key of `theme.radius` or any valid CSS value to set `border-radius`, numbers are converted to rem @default 'md' */
14
+ radius?: NuanceRadius;
15
+ /** Controls inputs `width` and `height` @default 'sm' */
16
+ size?: NuanceSize;
17
+ /** If set, the first input is focused when component is mounted @default false */
18
+ autoFocus?: boolean;
19
+ /** Inputs placeholder */
20
+ placeholder?: string;
21
+ /** Determines whether focus should be moved automatically to the next input once filled @default true */
22
+ manageFocus?: boolean;
23
+ /** Determines whether `autocomplete="one-time-code"` attribute should be set on all inputs @default true */
24
+ otp?: boolean;
25
+ /** Adds disabled attribute to all inputs */
26
+ disabled?: boolean;
27
+ /** Sets `aria-invalid` attribute and applies error styles to all inputs */
28
+ error?: boolean;
29
+ /** Determines which values can be entered @default 'alphanumeric' */
30
+ type?: 'alphanumeric' | 'number' | RegExp;
31
+ /** Changes input type to `"password"` @default false */
32
+ mask?: boolean;
33
+ /** Number of inputs @default 4 */
34
+ length?: number;
35
+ /** If set, the user cannot edit the value */
36
+ readOnly?: boolean;
37
+ /** `aria-label` attribute */
38
+ ariaLabel?: string;
39
+ /** Marks the field as required */
40
+ required?: boolean;
41
+ }
42
+ type __VLS_Props = PinInputProps;
43
+ type __VLS_ModelProps = {
44
+ modelValue?: string;
45
+ };
46
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
47
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
48
+ "update:modelValue": (value: string) => any;
49
+ complete: (value: string) => any;
50
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
51
+ "onUpdate:modelValue"?: ((value: string) => any) | undefined;
52
+ onComplete?: ((value: string) => any) | undefined;
53
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
54
+ declare const _default: typeof __VLS_export;
55
+ export default _default;
@@ -0,0 +1,171 @@
1
+ <script setup>
2
+ import { useVarsResolver } from "@nui/composables";
3
+ import { getSize } from "@nui/utils";
4
+ import { computed, useId } from "vue";
5
+ import Group from "../group.vue";
6
+ import InputBase from "../input/ui/input-base.vue";
7
+ import VisuallyHiddenInput from "../visually-hidden/visually-hidden-input.vue";
8
+ import { createPinArray, pinValidate } from "./lib";
9
+ import { usePinInput } from "./use-pin-input";
10
+ const {
11
+ id,
12
+ name,
13
+ disabled,
14
+ required,
15
+ length = 4,
16
+ otp = true,
17
+ type = "number",
18
+ mask,
19
+ gap = "xs",
20
+ placeholder,
21
+ autoFocus,
22
+ manageFocus = true,
23
+ size,
24
+ radius = "md",
25
+ readOnly,
26
+ error
27
+ } = defineProps({
28
+ id: { type: String, required: false },
29
+ name: { type: String, required: false },
30
+ gap: { type: [String, Number], required: false },
31
+ radius: { type: [String, Number], required: false },
32
+ size: { type: String, required: false },
33
+ autoFocus: { type: Boolean, required: false },
34
+ placeholder: { type: String, required: false },
35
+ manageFocus: { type: Boolean, required: false },
36
+ otp: { type: Boolean, required: false },
37
+ disabled: { type: Boolean, required: false },
38
+ error: { type: Boolean, required: false },
39
+ type: { type: null, required: false },
40
+ mask: { type: Boolean, required: false },
41
+ length: { type: Number, required: false },
42
+ readOnly: { type: Boolean, required: false },
43
+ ariaLabel: { type: String, required: false },
44
+ required: { type: Boolean, required: false },
45
+ is: { type: null, required: false },
46
+ mod: { type: [Object, Array, null], required: false }
47
+ });
48
+ defineEmits(["complete"]);
49
+ const uuid = computed(() => id ?? useId());
50
+ const inputMode = computed(() => type === "number" ? "numeric" : "text");
51
+ const model = defineModel({ type: String, ...{ default: "" } });
52
+ const {
53
+ refs,
54
+ focus,
55
+ focusedIx,
56
+ cells,
57
+ setFieldValue,
58
+ handleKeydown
59
+ } = usePinInput({
60
+ length: () => length,
61
+ manageFocus,
62
+ value: model,
63
+ inputMode
64
+ });
65
+ function handleInput(event, ix) {
66
+ const target = event.target;
67
+ const inputValue = target.value;
68
+ if (inputValue.length > 1) {
69
+ const isPaste = inputValue.length > 2;
70
+ if (isPaste) {
71
+ const isValid = pinValidate(inputValue, type);
72
+ if (!isValid)
73
+ return;
74
+ cells.value = createPinArray(length, inputValue);
75
+ const filledCount = Math.min(inputValue.length, length);
76
+ if (filledCount < length)
77
+ focus("next", filledCount - 1);
78
+ return;
79
+ }
80
+ const newChar = inputValue.split("")[inputValue.length - 1];
81
+ if (pinValidate(newChar, type)) {
82
+ setFieldValue(newChar, ix);
83
+ focus("next", ix);
84
+ }
85
+ return;
86
+ }
87
+ if (inputValue.length === 1) {
88
+ if (pinValidate(inputValue, type)) {
89
+ setFieldValue(inputValue, ix);
90
+ focus("next", ix);
91
+ } else {
92
+ setFieldValue("", ix);
93
+ }
94
+ } else if (inputValue.length === 0) {
95
+ setFieldValue("", ix);
96
+ }
97
+ }
98
+ function handlePaste(event) {
99
+ const pasteData = event.clipboardData?.getData("text/plain").replace(/\s+/g, "").trim();
100
+ const isValid = pinValidate(pasteData, type);
101
+ if (!isValid)
102
+ return;
103
+ const pasteArray = createPinArray(length, pasteData);
104
+ cells.value = pasteArray;
105
+ const filledCount = pasteArray.filter((v) => v !== "").length;
106
+ const focusIx = filledCount >= length ? length - 1 : filledCount;
107
+ refs.value[focusIx]?.focus();
108
+ }
109
+ const style = useVarsResolver(() => ({
110
+ root: {
111
+ "--pin-size": getSize(size, "pin-size"),
112
+ "--pin-fz": getSize(size, "pin-fz")
113
+ }
114
+ }));
115
+ </script>
116
+
117
+ <template>
118
+ <Group
119
+ role='group'
120
+ wrap='nowrap'
121
+ :gap
122
+ dir='ltr'
123
+ :class='$style.root'
124
+ :style='style.root'
125
+ >
126
+ <InputBase
127
+ v-for='(ids, ix) in length'
128
+ :id='`${uuid}-${ix + 1}`'
129
+ :key='ids'
130
+ :error
131
+ :class='$style.pinRoot'
132
+ :radius
133
+ >
134
+ <template #default='{ id: _id, css }'>
135
+ <input
136
+ :id='_id'
137
+ :key='ids'
138
+ :ref='refs.set'
139
+ :class='[css, $style.pin]'
140
+ :value='cells[ix]'
141
+ :autocomplete='otp ? "one-time-code" : void 0'
142
+ :input-mode
143
+ :type='mask ? "password" : "text"'
144
+ :placeholder
145
+ :autofocus='autoFocus && ix === 0'
146
+ :readonly='readOnly'
147
+ :disabled
148
+ @input='handleInput($event, ix)'
149
+ @focus='(event) => {
150
+ if (!readOnly) {
151
+ event.target?.select();
152
+ focusedIx = ix;
153
+ }
154
+ }'
155
+ @blur='focusedIx = -1'
156
+ @paste.prevent='handlePaste'
157
+ @keydown='handleKeydown($event, ix)'
158
+ >
159
+ </template>
160
+ </InputBase>
161
+ </Group>
162
+ <VisuallyHiddenInput
163
+ :name
164
+ :disabled
165
+ :required
166
+ />
167
+ </template>
168
+
169
+ <style module>
170
+ .root{--pin-size-xs:rem(30px);--pin-size-sm:rem(36px);--pin-size-md:rem(42px);--pin-size-lg:rem(50px);--pin-size-xl:rem(60px);--pin-size:var(--pin-size-sm);--pin-fz-xs:1.125rem;--pin-fz-sm:1.5rem;--pin-fz-md:1.75rem;--pin-fz-lg:2rem;--pin-fz-xl:2.5rem;--pin-fz:var(--pin-fz-sm)}.pinRoot{@mixin where-light{--input-bd:var(--color-gray-2);--input-bg:var(--color-gray-0)}@mixin where-dark{--input-bd:var(--color-gray-7);--input-bg:var(--color-dark-9)}}.pin{font-size:var(--pin-fz);font-weight:600;height:calc(var(--pin-size) + var(--pin-size)/3);line-height:1;padding:0;text-align:center;width:var(--pin-size)}
171
+ </style>
@@ -0,0 +1,55 @@
1
+ import type { NuanceRadius, NuanceSize, NuanceSpacing } from '../../types/index.js';
2
+ import type { BoxProps } from '../box.vue.js';
3
+ export interface PinInputEmits {
4
+ complete: [value: string];
5
+ }
6
+ export interface PinInputProps extends BoxProps {
7
+ /** Id auto-generated if not provided */
8
+ id?: string;
9
+ /** Hidden input `name` attribute */
10
+ name?: string;
11
+ /** Key of `theme.spacing` or any valid CSS value to set `gap` between inputs, numbers are converted to rem @default 'md' */
12
+ gap?: NuanceSpacing;
13
+ /** Key of `theme.radius` or any valid CSS value to set `border-radius`, numbers are converted to rem @default 'md' */
14
+ radius?: NuanceRadius;
15
+ /** Controls inputs `width` and `height` @default 'sm' */
16
+ size?: NuanceSize;
17
+ /** If set, the first input is focused when component is mounted @default false */
18
+ autoFocus?: boolean;
19
+ /** Inputs placeholder */
20
+ placeholder?: string;
21
+ /** Determines whether focus should be moved automatically to the next input once filled @default true */
22
+ manageFocus?: boolean;
23
+ /** Determines whether `autocomplete="one-time-code"` attribute should be set on all inputs @default true */
24
+ otp?: boolean;
25
+ /** Adds disabled attribute to all inputs */
26
+ disabled?: boolean;
27
+ /** Sets `aria-invalid` attribute and applies error styles to all inputs */
28
+ error?: boolean;
29
+ /** Determines which values can be entered @default 'alphanumeric' */
30
+ type?: 'alphanumeric' | 'number' | RegExp;
31
+ /** Changes input type to `"password"` @default false */
32
+ mask?: boolean;
33
+ /** Number of inputs @default 4 */
34
+ length?: number;
35
+ /** If set, the user cannot edit the value */
36
+ readOnly?: boolean;
37
+ /** `aria-label` attribute */
38
+ ariaLabel?: string;
39
+ /** Marks the field as required */
40
+ required?: boolean;
41
+ }
42
+ type __VLS_Props = PinInputProps;
43
+ type __VLS_ModelProps = {
44
+ modelValue?: string;
45
+ };
46
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
47
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_PublicProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
48
+ "update:modelValue": (value: string) => any;
49
+ complete: (value: string) => any;
50
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
51
+ "onUpdate:modelValue"?: ((value: string) => any) | undefined;
52
+ onComplete?: ((value: string) => any) | undefined;
53
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
54
+ declare const _default: typeof __VLS_export;
55
+ export default _default;
@@ -0,0 +1,18 @@
1
+ import type { MaybeRefOrGetter, ModelRef } from 'vue';
2
+ export declare function usePinInput<Element extends HTMLInputElement>({ value: model, length, allowedKeys, manageFocus, inputMode, }: {
3
+ value: ModelRef<string>;
4
+ /** current length */
5
+ length: MaybeRefOrGetter<number>;
6
+ /** @default ['Backspace', 'Tab', 'Control', 'Delete', 'ArrowLeft', 'ArrowRight'] */
7
+ allowedKeys?: string[];
8
+ /** @default true */
9
+ manageFocus?: boolean;
10
+ inputMode: MaybeRefOrGetter<'numeric' | 'text'>;
11
+ }): {
12
+ refs: Readonly<import("vue").Ref<Readonly<import("@vueuse/core").TemplateRefsList<Element>>, Readonly<import("@vueuse/core").TemplateRefsList<Element>>>>;
13
+ cells: import("vue").WritableComputedRef<string[], string[]>;
14
+ focusedIx: import("vue").Ref<number, number>;
15
+ focus: (dir: "next" | "prev", ix: number) => void;
16
+ handleKeydown: (event: KeyboardEvent, ix: number) => void;
17
+ setFieldValue: (value: string, ix: number) => string[];
18
+ };
@@ -0,0 +1,94 @@
1
+ import { useTemplateRefsList } from "@vueuse/core";
2
+ import { computed, ref, toValue } from "vue";
3
+ import { createPinArray } from "./lib.js";
4
+ export function usePinInput({
5
+ value: model,
6
+ length,
7
+ allowedKeys = ["Backspace", "Tab", "Control", "Delete", "ArrowLeft", "ArrowRight"],
8
+ manageFocus = true,
9
+ inputMode
10
+ }) {
11
+ const refs = useTemplateRefsList();
12
+ const focusedIx = ref(-1);
13
+ const cells = computed({
14
+ get: () => createPinArray(toValue(length), model.value),
15
+ set: (value) => {
16
+ model.value = value.join("").trim();
17
+ }
18
+ });
19
+ function setFieldValue(value, ix) {
20
+ const values = [...cells.value];
21
+ values[ix] = value;
22
+ cells.value = values;
23
+ return values;
24
+ }
25
+ function focus(dir, ix) {
26
+ if (dir === "next") {
27
+ const nextIx = ix + 1;
28
+ if (nextIx < toValue(length))
29
+ refs.value[nextIx]?.focus();
30
+ } else if (dir === "prev") {
31
+ const prevIx = ix - 1;
32
+ if (prevIx >= 0)
33
+ refs.value[prevIx]?.focus();
34
+ }
35
+ }
36
+ function handleKeydown(event, ix) {
37
+ const inputValue = event.target.value;
38
+ if (inputMode === "numeric") {
39
+ const isModifierShortcut = event.ctrlKey || event.metaKey;
40
+ const isAllowedKey = allowedKeys.includes(event.key) || isModifierShortcut || !Number.isNaN(Number(event.key));
41
+ if (!isAllowedKey)
42
+ return event.preventDefault();
43
+ }
44
+ switch (event.key) {
45
+ case "ArrowLeft":
46
+ event.preventDefault();
47
+ focus("prev", ix);
48
+ break;
49
+ case "ArrowRight":
50
+ event.preventDefault();
51
+ focus("next", ix);
52
+ break;
53
+ case "Tab":
54
+ if (event.shiftKey && ix > 0 && manageFocus) {
55
+ event.preventDefault();
56
+ focus("prev", ix);
57
+ }
58
+ break;
59
+ case " ":
60
+ event.preventDefault();
61
+ focus("next", ix);
62
+ break;
63
+ case "Delete":
64
+ event.preventDefault();
65
+ setFieldValue("", ix);
66
+ break;
67
+ case "Backspace":
68
+ if (inputValue === "") {
69
+ event.preventDefault();
70
+ focus("prev", ix);
71
+ } else {
72
+ setFieldValue("", ix);
73
+ if (ix < toValue(length) - 1) {
74
+ event.preventDefault();
75
+ focus("prev", ix);
76
+ }
77
+ }
78
+ break;
79
+ default:
80
+ if (inputValue.length > 0 && event.key === cells.value[ix]) {
81
+ event.preventDefault();
82
+ focus("next", ix);
83
+ }
84
+ }
85
+ }
86
+ return {
87
+ refs,
88
+ cells,
89
+ focusedIx,
90
+ focus,
91
+ handleKeydown,
92
+ setFieldValue
93
+ };
94
+ }
@@ -0,0 +1,24 @@
1
+ import type { CSSProperties } from 'vue';
2
+ import type { NuanceSpacing } from '../types/index.js';
3
+ import type { BoxProps } from './box.vue.js';
4
+ export interface StackProps extends BoxProps {
5
+ /** Key of `theme.spacing` or any valid CSS value to set `gap` property, numbers are converted to rem @default 'md' */
6
+ gap?: NuanceSpacing;
7
+ /** Controls `align-items` CSS property @default 'stretch' */
8
+ align?: CSSProperties['alignItems'];
9
+ /** Controls `justify-content` CSS property @default 'flex-start' */
10
+ justify?: CSSProperties['justifyContent'];
11
+ }
12
+ declare var __VLS_8: {};
13
+ type __VLS_Slots = {} & {
14
+ default?: (props: typeof __VLS_8) => any;
15
+ };
16
+ declare const __VLS_base: import("vue").DefineComponent<StackProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<StackProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
17
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
18
+ declare const _default: typeof __VLS_export;
19
+ export default _default;
20
+ type __VLS_WithSlots<T, S> = T & {
21
+ new (): {
22
+ $slots: S;
23
+ };
24
+ };
@@ -0,0 +1,33 @@
1
+ <script setup>
2
+ import { getSpacing, useVarsResolver } from "#imports";
3
+ import Box from "./box.vue";
4
+ const {
5
+ gap,
6
+ align,
7
+ justify,
8
+ ...props
9
+ } = defineProps({
10
+ gap: { type: [String, Number], required: false },
11
+ align: { type: void 0, required: false },
12
+ justify: { type: void 0, required: false },
13
+ is: { type: null, required: false },
14
+ mod: { type: [Object, Array, null], required: false }
15
+ });
16
+ const style = useVarsResolver(() => ({
17
+ root: {
18
+ "--stack-gap": getSpacing(gap),
19
+ "--stack-align": align ?? void 0,
20
+ "--stack-justify": justify ?? void 0
21
+ }
22
+ }));
23
+ </script>
24
+
25
+ <template>
26
+ <Box v-bind='props' :style='style.root' :class='$style.root'>
27
+ <slot />
28
+ </Box>
29
+ </template>
30
+
31
+ <style module>
32
+ .root{align-items:var(--stack-align,stretch);display:flex;flex-direction:column;gap:var(--stack-gap,var(--spacing-md));justify-content:var(--stack-justify,flex-start)}
33
+ </style>
@@ -0,0 +1,24 @@
1
+ import type { CSSProperties } from 'vue';
2
+ import type { NuanceSpacing } from '../types/index.js';
3
+ import type { BoxProps } from './box.vue.js';
4
+ export interface StackProps extends BoxProps {
5
+ /** Key of `theme.spacing` or any valid CSS value to set `gap` property, numbers are converted to rem @default 'md' */
6
+ gap?: NuanceSpacing;
7
+ /** Controls `align-items` CSS property @default 'stretch' */
8
+ align?: CSSProperties['alignItems'];
9
+ /** Controls `justify-content` CSS property @default 'flex-start' */
10
+ justify?: CSSProperties['justifyContent'];
11
+ }
12
+ declare var __VLS_8: {};
13
+ type __VLS_Slots = {} & {
14
+ default?: (props: typeof __VLS_8) => any;
15
+ };
16
+ declare const __VLS_base: import("vue").DefineComponent<StackProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<StackProps> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
17
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
18
+ declare const _default: typeof __VLS_export;
19
+ export default _default;
20
+ type __VLS_WithSlots<T, S> = T & {
21
+ new (): {
22
+ $slots: S;
23
+ };
24
+ };