webcoreui 1.1.0 → 1.2.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/README.md +9 -1
- package/astro.d.ts +5 -0
- package/astro.js +2 -0
- package/components/BottomNavigation/BottomNavigation.astro +1 -3
- package/components/Breadcrumb/Breadcrumb.astro +1 -3
- package/components/Carousel/Carousel.astro +79 -9
- package/components/Carousel/Carousel.svelte +40 -9
- package/components/Carousel/Carousel.tsx +46 -11
- package/components/Carousel/carousel.ts +3 -1
- package/components/Copy/Copy.astro +3 -5
- package/components/Copy/Copy.svelte +1 -1
- package/components/Copy/Copy.tsx +1 -1
- package/components/Input/input.ts +62 -62
- package/components/Modal/Modal.astro +75 -75
- package/components/Modal/modal.ts +25 -25
- package/components/OTPInput/OTPInput.astro +194 -96
- package/components/OTPInput/OTPInput.svelte +141 -26
- package/components/OTPInput/OTPInput.tsx +140 -36
- package/components/OTPInput/otpinput.module.scss +59 -85
- package/components/Pagination/Pagination.astro +3 -3
- package/components/Pagination/Pagination.svelte +4 -4
- package/components/Pagination/pagination.module.scss +3 -3
- package/components/RangeSlider/RangeSlider.astro +270 -0
- package/components/RangeSlider/RangeSlider.svelte +188 -0
- package/components/RangeSlider/RangeSlider.tsx +205 -0
- package/components/RangeSlider/rangeslider.module.scss +143 -0
- package/components/RangeSlider/rangeslider.ts +37 -0
- package/components/Sidebar/Sidebar.astro +1 -3
- package/components/Stepper/Stepper.astro +1 -3
- package/index.d.ts +23 -4
- package/index.js +2 -0
- package/package.json +109 -103
- package/react.d.ts +5 -0
- package/react.js +2 -0
- package/scss/global/breakpoints.scss +15 -0
- package/scss/setup.scss +7 -1
- package/svelte.d.ts +5 -0
- package/svelte.js +2 -0
- package/utils/DOMUtils.ts +27 -2
- package/utils/bodyFreeze.ts +1 -1
- package/utils/context.ts +2 -2
- package/utils/getBreakpoint.ts +17 -0
- package/utils/isOneOf.ts +5 -0
- package/utils/modal.ts +54 -55
- package/utils/toast.ts +1 -1
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
import type { Snippet } from 'svelte'
|
|
2
|
-
|
|
3
|
-
export type InputTarget = {
|
|
4
|
-
currentTarget: HTMLInputElement
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export type InputProps = {
|
|
8
|
-
type?: 'text'
|
|
9
|
-
| 'email'
|
|
10
|
-
| 'password'
|
|
11
|
-
| 'number'
|
|
12
|
-
| 'tel'
|
|
13
|
-
| 'url'
|
|
14
|
-
| 'search'
|
|
15
|
-
| 'file'
|
|
16
|
-
| 'date'
|
|
17
|
-
| 'datetime-local'
|
|
18
|
-
| 'month'
|
|
19
|
-
| 'week'
|
|
20
|
-
| 'time'
|
|
21
|
-
| 'color'
|
|
22
|
-
theme?: 'info'
|
|
23
|
-
| 'success'
|
|
24
|
-
| 'warning'
|
|
25
|
-
| 'alert'
|
|
26
|
-
| 'fill'
|
|
27
|
-
| null
|
|
28
|
-
value?: string | number
|
|
29
|
-
name?: string
|
|
30
|
-
placeholder?: string
|
|
31
|
-
label?: string
|
|
32
|
-
disabled?: boolean
|
|
33
|
-
subText?: string
|
|
34
|
-
maxLength?: number
|
|
35
|
-
min?: number
|
|
36
|
-
max?: number
|
|
37
|
-
step?: number
|
|
38
|
-
multiple?: boolean
|
|
39
|
-
pattern?: string
|
|
40
|
-
required?: boolean
|
|
41
|
-
autofocus?: boolean
|
|
42
|
-
autocomplete?: 'on' | 'off'
|
|
43
|
-
className?: string
|
|
44
|
-
labelClassName?: string
|
|
45
|
-
[key: string]: any
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export type SvelteInputProps = {
|
|
49
|
-
onChange?: (event: Event & InputTarget) => void
|
|
50
|
-
onKeyUp?: (event: KeyboardEvent & InputTarget) => void
|
|
51
|
-
onInput?: (event: any) => void
|
|
52
|
-
onClick?: (event: MouseEvent & InputTarget) => void
|
|
53
|
-
children?: Snippet
|
|
54
|
-
} & InputProps
|
|
55
|
-
|
|
56
|
-
export type ReactInputProps = {
|
|
57
|
-
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
|
|
58
|
-
onKeyUp?: (event: React.KeyboardEvent<HTMLInputElement>) => void
|
|
59
|
-
onInput?: (event: React.FormEvent<HTMLInputElement>) => void
|
|
60
|
-
onClick?: (event: React.MouseEvent<HTMLInputElement>) => void
|
|
61
|
-
children?: React.ReactNode
|
|
62
|
-
} & InputProps
|
|
1
|
+
import type { Snippet } from 'svelte'
|
|
2
|
+
|
|
3
|
+
export type InputTarget = {
|
|
4
|
+
currentTarget: HTMLInputElement
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export type InputProps = {
|
|
8
|
+
type?: 'text'
|
|
9
|
+
| 'email'
|
|
10
|
+
| 'password'
|
|
11
|
+
| 'number'
|
|
12
|
+
| 'tel'
|
|
13
|
+
| 'url'
|
|
14
|
+
| 'search'
|
|
15
|
+
| 'file'
|
|
16
|
+
| 'date'
|
|
17
|
+
| 'datetime-local'
|
|
18
|
+
| 'month'
|
|
19
|
+
| 'week'
|
|
20
|
+
| 'time'
|
|
21
|
+
| 'color'
|
|
22
|
+
theme?: 'info'
|
|
23
|
+
| 'success'
|
|
24
|
+
| 'warning'
|
|
25
|
+
| 'alert'
|
|
26
|
+
| 'fill'
|
|
27
|
+
| null
|
|
28
|
+
value?: string | number
|
|
29
|
+
name?: string
|
|
30
|
+
placeholder?: string
|
|
31
|
+
label?: string
|
|
32
|
+
disabled?: boolean
|
|
33
|
+
subText?: string
|
|
34
|
+
maxLength?: number
|
|
35
|
+
min?: number
|
|
36
|
+
max?: number
|
|
37
|
+
step?: number
|
|
38
|
+
multiple?: boolean
|
|
39
|
+
pattern?: string
|
|
40
|
+
required?: boolean
|
|
41
|
+
autofocus?: boolean
|
|
42
|
+
autocomplete?: 'on' | 'off' | 'one-time-code'
|
|
43
|
+
className?: string
|
|
44
|
+
labelClassName?: string
|
|
45
|
+
[key: string]: any
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type SvelteInputProps = {
|
|
49
|
+
onChange?: (event: Event & InputTarget) => void
|
|
50
|
+
onKeyUp?: (event: KeyboardEvent & InputTarget) => void
|
|
51
|
+
onInput?: (event: any) => void
|
|
52
|
+
onClick?: (event: MouseEvent & InputTarget) => void
|
|
53
|
+
children?: Snippet
|
|
54
|
+
} & InputProps
|
|
55
|
+
|
|
56
|
+
export type ReactInputProps = {
|
|
57
|
+
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
|
|
58
|
+
onKeyUp?: (event: React.KeyboardEvent<HTMLInputElement>) => void
|
|
59
|
+
onInput?: (event: React.FormEvent<HTMLInputElement>) => void
|
|
60
|
+
onClick?: (event: React.MouseEvent<HTMLInputElement>) => void
|
|
61
|
+
children?: React.ReactNode
|
|
62
|
+
} & InputProps
|
|
@@ -1,75 +1,75 @@
|
|
|
1
|
-
---
|
|
2
|
-
import type { ModalProps } from './modal'
|
|
3
|
-
|
|
4
|
-
import Button from '../Button/Button.astro'
|
|
5
|
-
|
|
6
|
-
import alert from '../../icons/alert.svg?raw'
|
|
7
|
-
import success from '../../icons/circle-check.svg?raw'
|
|
8
|
-
import closeIcon from '../../icons/close.svg?raw'
|
|
9
|
-
import info from '../../icons/info.svg?raw'
|
|
10
|
-
import warning from '../../icons/warning.svg?raw'
|
|
11
|
-
|
|
12
|
-
import styles from './modal.module.scss'
|
|
13
|
-
|
|
14
|
-
interface Props extends ModalProps {}
|
|
15
|
-
|
|
16
|
-
const iconMap = {
|
|
17
|
-
info,
|
|
18
|
-
success,
|
|
19
|
-
warning,
|
|
20
|
-
alert
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const {
|
|
24
|
-
title,
|
|
25
|
-
subTitle,
|
|
26
|
-
showCloseIcon = true,
|
|
27
|
-
closeOnEsc = true,
|
|
28
|
-
closeOnOverlay = true,
|
|
29
|
-
transparent,
|
|
30
|
-
theme,
|
|
31
|
-
id,
|
|
32
|
-
className,
|
|
33
|
-
...rest
|
|
34
|
-
} = Astro.props
|
|
35
|
-
|
|
36
|
-
const close = [
|
|
37
|
-
showCloseIcon && 'icon',
|
|
38
|
-
closeOnEsc && 'esc',
|
|
39
|
-
closeOnOverlay && 'overlay'
|
|
40
|
-
].filter(Boolean).join(',')
|
|
41
|
-
|
|
42
|
-
const classes = [
|
|
43
|
-
styles.modal,
|
|
44
|
-
transparent && styles.transparent,
|
|
45
|
-
theme && styles[theme],
|
|
46
|
-
className
|
|
47
|
-
]
|
|
48
|
-
---
|
|
49
|
-
|
|
50
|
-
<dialog
|
|
51
|
-
class:list={classes}
|
|
52
|
-
id={id}
|
|
53
|
-
data-close={close.length ? close : undefined}
|
|
54
|
-
{...rest}
|
|
55
|
-
>
|
|
56
|
-
{showCloseIcon && (
|
|
57
|
-
<Button
|
|
58
|
-
theme="flat"
|
|
59
|
-
className={styles.close}
|
|
60
|
-
data-id="close"
|
|
61
|
-
aria-label="close"
|
|
62
|
-
>
|
|
63
|
-
<Fragment set:html={closeIcon} />
|
|
64
|
-
</Button>
|
|
65
|
-
)}
|
|
66
|
-
{title && (
|
|
67
|
-
<strong class={styles.title}>
|
|
68
|
-
{theme && <Fragment set:html={iconMap[theme]} />}
|
|
69
|
-
{title}
|
|
70
|
-
</strong>
|
|
71
|
-
)}
|
|
72
|
-
{subTitle && <div class={styles.subTitle}>{subTitle}</div>}
|
|
73
|
-
<slot />
|
|
74
|
-
</dialog>
|
|
75
|
-
<div class={styles.overlay} />
|
|
1
|
+
---
|
|
2
|
+
import type { ModalProps } from './modal'
|
|
3
|
+
|
|
4
|
+
import Button from '../Button/Button.astro'
|
|
5
|
+
|
|
6
|
+
import alert from '../../icons/alert.svg?raw'
|
|
7
|
+
import success from '../../icons/circle-check.svg?raw'
|
|
8
|
+
import closeIcon from '../../icons/close.svg?raw'
|
|
9
|
+
import info from '../../icons/info.svg?raw'
|
|
10
|
+
import warning from '../../icons/warning.svg?raw'
|
|
11
|
+
|
|
12
|
+
import styles from './modal.module.scss'
|
|
13
|
+
|
|
14
|
+
interface Props extends ModalProps {}
|
|
15
|
+
|
|
16
|
+
const iconMap = {
|
|
17
|
+
info,
|
|
18
|
+
success,
|
|
19
|
+
warning,
|
|
20
|
+
alert
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const {
|
|
24
|
+
title,
|
|
25
|
+
subTitle,
|
|
26
|
+
showCloseIcon = true,
|
|
27
|
+
closeOnEsc = true,
|
|
28
|
+
closeOnOverlay = true,
|
|
29
|
+
transparent,
|
|
30
|
+
theme,
|
|
31
|
+
id,
|
|
32
|
+
className,
|
|
33
|
+
...rest
|
|
34
|
+
} = Astro.props
|
|
35
|
+
|
|
36
|
+
const close = [
|
|
37
|
+
showCloseIcon && 'icon',
|
|
38
|
+
closeOnEsc && 'esc',
|
|
39
|
+
closeOnOverlay && 'overlay'
|
|
40
|
+
].filter(Boolean).join(',')
|
|
41
|
+
|
|
42
|
+
const classes = [
|
|
43
|
+
styles.modal,
|
|
44
|
+
transparent && styles.transparent,
|
|
45
|
+
theme && styles[theme],
|
|
46
|
+
className
|
|
47
|
+
]
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
<dialog
|
|
51
|
+
class:list={classes}
|
|
52
|
+
id={id}
|
|
53
|
+
data-close={close.length ? close : undefined}
|
|
54
|
+
{...rest}
|
|
55
|
+
>
|
|
56
|
+
{showCloseIcon && (
|
|
57
|
+
<Button
|
|
58
|
+
theme="flat"
|
|
59
|
+
className={styles.close}
|
|
60
|
+
data-id="close"
|
|
61
|
+
aria-label="close"
|
|
62
|
+
>
|
|
63
|
+
<Fragment set:html={closeIcon} />
|
|
64
|
+
</Button>
|
|
65
|
+
)}
|
|
66
|
+
{title && (
|
|
67
|
+
<strong class={styles.title}>
|
|
68
|
+
{theme && <Fragment set:html={iconMap[theme]} />}
|
|
69
|
+
{title}
|
|
70
|
+
</strong>
|
|
71
|
+
)}
|
|
72
|
+
{subTitle && <div class={styles.subTitle}>{subTitle}</div>}
|
|
73
|
+
<slot />
|
|
74
|
+
</dialog>
|
|
75
|
+
<div class={styles.overlay} />
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import type { Snippet } from 'svelte'
|
|
2
|
-
|
|
3
|
-
export type ModalProps = {
|
|
4
|
-
title?: string
|
|
5
|
-
subTitle?: string
|
|
6
|
-
showCloseIcon?: boolean
|
|
7
|
-
closeOnEsc?: boolean
|
|
8
|
-
closeOnOverlay?: boolean
|
|
9
|
-
transparent?: boolean
|
|
10
|
-
id?: string
|
|
11
|
-
className?: string
|
|
12
|
-
theme?: 'info'
|
|
13
|
-
| 'success'
|
|
14
|
-
| 'warning'
|
|
15
|
-
| 'alert'
|
|
16
|
-
[key: string]: any
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export type SvelteModalProps = {
|
|
20
|
-
children: Snippet
|
|
21
|
-
} & ModalProps
|
|
22
|
-
|
|
23
|
-
export type ReactModalProps = {
|
|
24
|
-
children?: React.ReactNode
|
|
25
|
-
} & ModalProps
|
|
1
|
+
import type { Snippet } from 'svelte'
|
|
2
|
+
|
|
3
|
+
export type ModalProps = {
|
|
4
|
+
title?: string
|
|
5
|
+
subTitle?: string
|
|
6
|
+
showCloseIcon?: boolean
|
|
7
|
+
closeOnEsc?: boolean
|
|
8
|
+
closeOnOverlay?: boolean
|
|
9
|
+
transparent?: boolean
|
|
10
|
+
id?: string
|
|
11
|
+
className?: string
|
|
12
|
+
theme?: 'info'
|
|
13
|
+
| 'success'
|
|
14
|
+
| 'warning'
|
|
15
|
+
| 'alert'
|
|
16
|
+
[key: string]: any
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type SvelteModalProps = {
|
|
20
|
+
children: Snippet
|
|
21
|
+
} & ModalProps
|
|
22
|
+
|
|
23
|
+
export type ReactModalProps = {
|
|
24
|
+
children?: React.ReactNode
|
|
25
|
+
} & ModalProps
|
|
@@ -1,96 +1,194 @@
|
|
|
1
|
-
---
|
|
2
|
-
import type { OTPInputProps } from './otpinput'
|
|
3
|
-
|
|
4
|
-
import Input from '../Input/Input.astro'
|
|
5
|
-
|
|
6
|
-
import styles from './otpinput.module.scss'
|
|
7
|
-
|
|
8
|
-
interface Props extends OTPInputProps {}
|
|
9
|
-
|
|
10
|
-
const {
|
|
11
|
-
name,
|
|
12
|
-
disabled,
|
|
13
|
-
length = 6,
|
|
14
|
-
groupLength = 0,
|
|
15
|
-
separator = '•',
|
|
16
|
-
label,
|
|
17
|
-
subText,
|
|
18
|
-
className,
|
|
19
|
-
...rest
|
|
20
|
-
} = Astro.props
|
|
21
|
-
|
|
22
|
-
const classes = [
|
|
23
|
-
styles.wrapper,
|
|
24
|
-
className
|
|
25
|
-
]
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
.reduce<(number | string)[]>((acc, num, i) =>
|
|
29
|
-
groupLength > 0 && i % groupLength === 0 && i !== 0
|
|
30
|
-
? [...acc, separator, num]
|
|
31
|
-
: [...acc, num]
|
|
32
|
-
, [])
|
|
33
|
-
---
|
|
34
|
-
|
|
35
|
-
<div class:list={classes}>
|
|
36
|
-
{label && (
|
|
37
|
-
<label
|
|
38
|
-
for={name}
|
|
39
|
-
class={styles.label}
|
|
40
|
-
set:html={label}
|
|
41
|
-
/>
|
|
42
|
-
)}
|
|
43
|
-
|
|
44
|
-
<div class={styles['input-wrapper']}>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
data-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
1
|
+
---
|
|
2
|
+
import type { OTPInputProps } from './otpinput'
|
|
3
|
+
|
|
4
|
+
import Input from '../Input/Input.astro'
|
|
5
|
+
|
|
6
|
+
import styles from './otpinput.module.scss'
|
|
7
|
+
|
|
8
|
+
interface Props extends OTPInputProps {}
|
|
9
|
+
|
|
10
|
+
const {
|
|
11
|
+
name = 'otp',
|
|
12
|
+
disabled,
|
|
13
|
+
length = 6,
|
|
14
|
+
groupLength = 0,
|
|
15
|
+
separator = '•',
|
|
16
|
+
label,
|
|
17
|
+
subText,
|
|
18
|
+
className,
|
|
19
|
+
...rest
|
|
20
|
+
} = Astro.props
|
|
21
|
+
|
|
22
|
+
const classes = [
|
|
23
|
+
styles.wrapper,
|
|
24
|
+
className
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
const inputs = Array.from({ length }, (_, i) => i + 1)
|
|
28
|
+
.reduce<(number | string)[]>((acc, num, i) =>
|
|
29
|
+
groupLength > 0 && i % groupLength === 0 && i !== 0
|
|
30
|
+
? [...acc, separator, num]
|
|
31
|
+
: [...acc, num]
|
|
32
|
+
, [])
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
<div class:list={classes}>
|
|
36
|
+
{label && (
|
|
37
|
+
<label
|
|
38
|
+
for={`${name}-0`}
|
|
39
|
+
class={styles.label}
|
|
40
|
+
set:html={label}
|
|
41
|
+
/>
|
|
42
|
+
)}
|
|
43
|
+
|
|
44
|
+
<div class={styles['input-wrapper']} data-length={length}>
|
|
45
|
+
{inputs.map((input, index) =>
|
|
46
|
+
typeof input === 'string' ? (
|
|
47
|
+
<div class={styles.separator}>{input}</div>
|
|
48
|
+
) : (
|
|
49
|
+
<Input
|
|
50
|
+
id={`${name}-${index}`}
|
|
51
|
+
class={styles.input}
|
|
52
|
+
type="text"
|
|
53
|
+
maxlength="1"
|
|
54
|
+
disabled={disabled}
|
|
55
|
+
inputmode="numeric"
|
|
56
|
+
autocomplete="one-time-code"
|
|
57
|
+
data-id="w-input-otp"
|
|
58
|
+
data-index={input}
|
|
59
|
+
aria-label={`OTP digit ${input + 1}`}
|
|
60
|
+
{...rest}
|
|
61
|
+
/>
|
|
62
|
+
)
|
|
63
|
+
)}
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
{subText && <div class={styles.subtext}>{subText}</div>}
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<script>
|
|
70
|
+
import { on } from '../../utils/DOMUtils'
|
|
71
|
+
import { dispatch } from '../../utils/event'
|
|
72
|
+
|
|
73
|
+
const focus = (direction: 'next' | 'prev', wrapper: HTMLElement | null, clear?: boolean) => {
|
|
74
|
+
const index = Number(wrapper?.dataset.active)
|
|
75
|
+
const nextIndex = direction === 'next' ? index + 1 : index - 1
|
|
76
|
+
|
|
77
|
+
const input = wrapper?.querySelector(`[data-index="${nextIndex}"]`)
|
|
78
|
+
|
|
79
|
+
if (input instanceof HTMLInputElement) {
|
|
80
|
+
input.focus()
|
|
81
|
+
|
|
82
|
+
if (clear) {
|
|
83
|
+
input.value = ''
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const addEventListeners = () => {
|
|
89
|
+
on('[data-id="w-input-otp"]', 'keydown', (event: KeyboardEvent) => {
|
|
90
|
+
const target = event.target as HTMLInputElement
|
|
91
|
+
|
|
92
|
+
if (event.key === 'Backspace' || event.key === 'Delete') {
|
|
93
|
+
if (!target.value) {
|
|
94
|
+
focus('prev', target.parentElement, true)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (event.key === 'ArrowLeft') {
|
|
99
|
+
focus('prev', target.parentElement)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (event.key === 'ArrowRight') {
|
|
103
|
+
focus('next', target.parentElement)
|
|
104
|
+
}
|
|
105
|
+
}, true)
|
|
106
|
+
|
|
107
|
+
on('[data-id="w-input-otp"]', 'input', (event: Event) => {
|
|
108
|
+
const target = event.target
|
|
109
|
+
|
|
110
|
+
if (!(target instanceof HTMLInputElement)) {
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const index = Number(target.dataset.index)
|
|
115
|
+
const emptyIndex = Array.from(target.parentElement?.querySelectorAll('input') || [])
|
|
116
|
+
.findIndex(element => !element.value) + 1
|
|
117
|
+
|
|
118
|
+
if (emptyIndex !== 0 && emptyIndex < index) {
|
|
119
|
+
const emptyElement = target.parentElement?.querySelector(`[data-index="${emptyIndex}"]`)
|
|
120
|
+
const nextFocusElement = target.parentElement?.querySelector(`[data-index="${emptyIndex + 1}"]`)
|
|
121
|
+
|
|
122
|
+
if (emptyElement instanceof HTMLInputElement) {
|
|
123
|
+
emptyElement.value = target.value
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (nextFocusElement instanceof HTMLInputElement) {
|
|
127
|
+
nextFocusElement.focus()
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
target.value = ''
|
|
131
|
+
|
|
132
|
+
return
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (target.value) {
|
|
136
|
+
focus('next', target.parentElement)
|
|
137
|
+
}
|
|
138
|
+
}, true)
|
|
139
|
+
|
|
140
|
+
on('[data-id="w-input-otp"]', 'keyup', (event: Event) => {
|
|
141
|
+
const target = event.target
|
|
142
|
+
const container = target instanceof HTMLInputElement ? target.parentElement : null
|
|
143
|
+
|
|
144
|
+
if (container) {
|
|
145
|
+
const value = Array.from(container.querySelectorAll('input') || [])
|
|
146
|
+
.map(input => input.value)
|
|
147
|
+
.join('')
|
|
148
|
+
|
|
149
|
+
dispatch('otpOnChange', value)
|
|
150
|
+
}
|
|
151
|
+
}, true)
|
|
152
|
+
|
|
153
|
+
on('[data-id="w-input-otp"]', 'paste', (event: ClipboardEvent) => {
|
|
154
|
+
event.preventDefault()
|
|
155
|
+
|
|
156
|
+
const target = event.target
|
|
157
|
+
const container = target instanceof HTMLInputElement ? target.parentElement : null
|
|
158
|
+
|
|
159
|
+
if (container) {
|
|
160
|
+
const inputLength = Number(container.dataset.length)
|
|
161
|
+
const paste = event.clipboardData?.getData('text') ?? ''
|
|
162
|
+
const nextIndex = Math.min(paste.length + 1, inputLength)
|
|
163
|
+
const focusInput = container.querySelector(`[data-index="${nextIndex}"]`)
|
|
164
|
+
|
|
165
|
+
if (focusInput instanceof HTMLInputElement) {
|
|
166
|
+
focusInput.focus()
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
paste.split('').slice(0, inputLength).forEach((char, i) => {
|
|
170
|
+
const input = container.querySelector(`[data-index="${i + 1}"]`)
|
|
171
|
+
|
|
172
|
+
if (input instanceof HTMLInputElement) {
|
|
173
|
+
input.value = char
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
}
|
|
177
|
+
}, true)
|
|
178
|
+
|
|
179
|
+
on('[data-id="w-input-otp"]', 'focus', (event: Event) => {
|
|
180
|
+
const target = event.target
|
|
181
|
+
|
|
182
|
+
if (target instanceof HTMLInputElement) {
|
|
183
|
+
if (target.parentElement) {
|
|
184
|
+
target.parentElement.dataset.active = target.dataset.index
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
setTimeout(() => target.select(), 0)
|
|
188
|
+
}
|
|
189
|
+
}, true)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
on(document, 'astro:after-swap', addEventListeners)
|
|
193
|
+
addEventListeners()
|
|
194
|
+
</script>
|