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.
Files changed (45) hide show
  1. package/README.md +9 -1
  2. package/astro.d.ts +5 -0
  3. package/astro.js +2 -0
  4. package/components/BottomNavigation/BottomNavigation.astro +1 -3
  5. package/components/Breadcrumb/Breadcrumb.astro +1 -3
  6. package/components/Carousel/Carousel.astro +79 -9
  7. package/components/Carousel/Carousel.svelte +40 -9
  8. package/components/Carousel/Carousel.tsx +46 -11
  9. package/components/Carousel/carousel.ts +3 -1
  10. package/components/Copy/Copy.astro +3 -5
  11. package/components/Copy/Copy.svelte +1 -1
  12. package/components/Copy/Copy.tsx +1 -1
  13. package/components/Input/input.ts +62 -62
  14. package/components/Modal/Modal.astro +75 -75
  15. package/components/Modal/modal.ts +25 -25
  16. package/components/OTPInput/OTPInput.astro +194 -96
  17. package/components/OTPInput/OTPInput.svelte +141 -26
  18. package/components/OTPInput/OTPInput.tsx +140 -36
  19. package/components/OTPInput/otpinput.module.scss +59 -85
  20. package/components/Pagination/Pagination.astro +3 -3
  21. package/components/Pagination/Pagination.svelte +4 -4
  22. package/components/Pagination/pagination.module.scss +3 -3
  23. package/components/RangeSlider/RangeSlider.astro +270 -0
  24. package/components/RangeSlider/RangeSlider.svelte +188 -0
  25. package/components/RangeSlider/RangeSlider.tsx +205 -0
  26. package/components/RangeSlider/rangeslider.module.scss +143 -0
  27. package/components/RangeSlider/rangeslider.ts +37 -0
  28. package/components/Sidebar/Sidebar.astro +1 -3
  29. package/components/Stepper/Stepper.astro +1 -3
  30. package/index.d.ts +23 -4
  31. package/index.js +2 -0
  32. package/package.json +109 -103
  33. package/react.d.ts +5 -0
  34. package/react.js +2 -0
  35. package/scss/global/breakpoints.scss +15 -0
  36. package/scss/setup.scss +7 -1
  37. package/svelte.d.ts +5 -0
  38. package/svelte.js +2 -0
  39. package/utils/DOMUtils.ts +27 -2
  40. package/utils/bodyFreeze.ts +1 -1
  41. package/utils/context.ts +2 -2
  42. package/utils/getBreakpoint.ts +17 -0
  43. package/utils/isOneOf.ts +5 -0
  44. package/utils/modal.ts +54 -55
  45. 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 inputPlaceholders = 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}
39
- class={styles.label}
40
- set:html={label}
41
- />
42
- )}
43
-
44
- <div class={styles['input-wrapper']}>
45
- <Input
46
- name={name || 'otp'}
47
- data-id="w-input-otp"
48
- disabled={disabled}
49
- maxlength={length}
50
- required={true}
51
- {...rest}
52
- />
53
-
54
- <div class={styles.placeholders}>
55
- {inputPlaceholders.map((placeholder, index) => (
56
- <div
57
- class={typeof placeholder === 'string' ? styles.separator : styles.placeholder}
58
- data-active={index === 0 ? true : undefined}
59
- data-separator={typeof placeholder === 'string' ? true : undefined}
60
- >
61
- {typeof placeholder === 'string' ? placeholder : ''}
62
- </div>
63
- ))}
64
- </div>
65
- </div>
66
-
67
- {subText && (
68
- <div class={styles.subtext} set:html={subText} />
69
- )}
70
- </div>
71
-
72
- <script>
73
- import { on } from '../../utils/DOMUtils'
74
-
75
- const addEventListeners = () => {
76
- on('[data-id="w-input-otp"]', 'input', (event: Event) => {
77
- const target = event.target as HTMLInputElement
78
- const value = target.value
79
- const placeholders = Array.from(target.nextElementSibling!.children)
80
- .filter(child => !(child as HTMLDivElement).dataset.separator)
81
-
82
-
83
- placeholders.forEach((placeholder, index) => {
84
- const placeholderElement = placeholder as HTMLDivElement
85
-
86
- placeholderElement.innerText = value[index] || ''
87
- placeholderElement.dataset.active = value.length === index
88
- ? 'true'
89
- : 'false'
90
- })
91
- }, true)
92
- }
93
-
94
- on(document, 'astro:after-swap', addEventListeners)
95
- addEventListeners()
96
- </script>
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>