webcoreui 1.1.0-beta.3 → 1.2.0-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 (49) hide show
  1. package/README.md +47 -4
  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/ContextMenu/ContextMenu.astro +8 -3
  11. package/components/ContextMenu/ContextMenu.svelte +7 -2
  12. package/components/ContextMenu/ContextMenu.tsx +7 -2
  13. package/components/Copy/Copy.astro +3 -5
  14. package/components/Copy/Copy.svelte +1 -1
  15. package/components/Copy/Copy.tsx +1 -1
  16. package/components/Input/input.ts +62 -62
  17. package/components/Modal/Modal.astro +75 -75
  18. package/components/Modal/modal.ts +25 -25
  19. package/components/OTPInput/OTPInput.astro +194 -96
  20. package/components/OTPInput/OTPInput.svelte +141 -26
  21. package/components/OTPInput/OTPInput.tsx +140 -36
  22. package/components/OTPInput/otpinput.module.scss +59 -85
  23. package/components/Pagination/Pagination.astro +3 -3
  24. package/components/Pagination/Pagination.svelte +4 -4
  25. package/components/Pagination/pagination.module.scss +3 -3
  26. package/components/RangeSlider/RangeSlider.astro +270 -0
  27. package/components/RangeSlider/RangeSlider.svelte +188 -0
  28. package/components/RangeSlider/RangeSlider.tsx +205 -0
  29. package/components/RangeSlider/rangeslider.module.scss +143 -0
  30. package/components/RangeSlider/rangeslider.ts +37 -0
  31. package/components/Sidebar/Sidebar.astro +1 -3
  32. package/components/Stepper/Stepper.astro +1 -3
  33. package/index.d.ts +23 -4
  34. package/index.js +2 -0
  35. package/package.json +109 -103
  36. package/react.d.ts +5 -0
  37. package/react.js +2 -0
  38. package/scss/global/breakpoints.scss +15 -0
  39. package/scss/setup.scss +7 -1
  40. package/svelte.d.ts +5 -0
  41. package/svelte.js +2 -0
  42. package/utils/DOMUtils.ts +27 -2
  43. package/utils/bodyFreeze.ts +1 -1
  44. package/utils/context.ts +2 -2
  45. package/utils/getBreakpoint.ts +17 -0
  46. package/utils/isOneOf.ts +5 -0
  47. package/utils/modal.ts +54 -55
  48. package/utils/toast.ts +1 -1
  49. package/utils/webcore.ts +0 -28
@@ -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>
@@ -8,7 +8,7 @@
8
8
  import styles from './otpinput.module.scss'
9
9
 
10
10
  let {
11
- name,
11
+ name = 'otp',
12
12
  disabled,
13
13
  length = 6,
14
14
  groupLength = 0,
@@ -25,44 +25,159 @@
25
25
  className
26
26
  ])
27
27
 
28
- const inputPlaceholders = Array.from({ length }, (_, i) => i + 1)
28
+ const inputs = Array.from({ length }, (_, i) => i + 1)
29
29
  .reduce<(number | string)[]>((acc, num, i) =>
30
30
  groupLength > 0 && i % groupLength === 0 && i !== 0
31
31
  ? [...acc, separator, num]
32
32
  : [...acc, num]
33
33
  , [])
34
34
 
35
- const getAdjustedIndex = (index: number) => inputPlaceholders
36
- .slice(0, index)
37
- .filter(placeholder => typeof placeholder !== 'string')
38
- .length
35
+ const focus = (direction: 'next' | 'prev', wrapper: HTMLElement | null, clear?: boolean) => {
36
+ const index = Number(wrapper?.dataset.active)
37
+ const nextIndex = direction === 'next' ? index + 1 : index - 1
38
+
39
+ const input = wrapper?.querySelector(`[data-index="${nextIndex}"]`)
40
+
41
+ if (input instanceof HTMLInputElement) {
42
+ input.focus()
43
+
44
+ if (clear) {
45
+ input.value = ''
46
+ }
47
+ }
48
+ }
49
+
50
+ const handleKeyDown = (event: KeyboardEvent) => {
51
+ const target = event.target as HTMLInputElement
52
+
53
+ if (event.key === 'Backspace' || event.key === 'Delete') {
54
+ if (!target.value) {
55
+ focus('prev', target.parentElement, true)
56
+ }
57
+ }
58
+
59
+ if (event.key === 'ArrowLeft') {
60
+ focus('prev', target.parentElement)
61
+ }
62
+
63
+ if (event.key === 'ArrowRight') {
64
+ focus('next', target.parentElement)
65
+ }
66
+ }
67
+
68
+ const handleInput = (event: Event) => {
69
+ const target = event.target
70
+
71
+ if (!(target instanceof HTMLInputElement)) {
72
+ return
73
+ }
74
+
75
+ const index = Number(target.dataset.index)
76
+ const emptyIndex = Array.from(target.parentElement?.querySelectorAll('input') || [])
77
+ .findIndex(element => !element.value) + 1
78
+
79
+ if (emptyIndex !== 0 && emptyIndex < index) {
80
+ const emptyElement = target.parentElement?.querySelector(`[data-index="${emptyIndex}"]`)
81
+ const nextFocusElement = target.parentElement?.querySelector(`[data-index="${emptyIndex + 1}"]`)
82
+
83
+ if (emptyElement instanceof HTMLInputElement) {
84
+ emptyElement.value = target.value
85
+ }
86
+
87
+ if (nextFocusElement instanceof HTMLInputElement) {
88
+ nextFocusElement.focus()
89
+ }
90
+
91
+ target.value = ''
92
+
93
+ return
94
+ }
95
+
96
+ if (target.value) {
97
+ focus('next', target.parentElement)
98
+ }
99
+ }
100
+
101
+ const handlePaste = (event: ClipboardEvent) => {
102
+ event.preventDefault()
103
+
104
+ const target = event.target
105
+ const container = target instanceof HTMLInputElement ? target.parentElement : null
106
+
107
+ if (container) {
108
+ const paste = event.clipboardData?.getData('text') ?? ''
109
+ const nextIndex = Math.min(paste.length + 1, length)
110
+ const focusInput = container.querySelector(`[data-index="${nextIndex}"]`)
111
+
112
+ if (focusInput instanceof HTMLInputElement) {
113
+ focusInput.focus()
114
+ }
115
+
116
+ paste.split('').slice(0, length).forEach((char, i) => {
117
+ const input = container.querySelector(`[data-index="${i + 1}"]`)
118
+
119
+ if (input instanceof HTMLInputElement) {
120
+ input.value = char
121
+ }
122
+ })
123
+ }
124
+ }
125
+
126
+ const handleFocus = (event: Event) => {
127
+ const target = event.target
128
+
129
+ if (target instanceof HTMLInputElement) {
130
+ if (target.parentElement) {
131
+ target.parentElement.dataset.active = target.dataset.index
132
+ }
133
+
134
+ setTimeout(() => target.select(), 0)
135
+ }
136
+ }
137
+
138
+ const handleKeyUp = (event: Event) => {
139
+ const target = event.target
140
+ const container = target instanceof HTMLInputElement ? target.parentElement : null
141
+
142
+ if (container) {
143
+ const newValue = Array.from(container.querySelectorAll('input') || [])
144
+ .map(input => input.value)
145
+ .join('')
146
+
147
+ value = newValue
148
+ }
149
+ }
39
150
  </script>
40
151
 
41
152
  <div class={classes}>
42
153
  {#if label}
43
- <label for={name} class={styles.label}>{@html label}</label>
154
+ <label for={`${name}-0`} class={styles.label}>{@html label}</label>
44
155
  {/if}
45
156
 
46
157
  <div class={styles['input-wrapper']}>
47
- <Input
48
- name={name || 'otp'}
49
- disabled={disabled}
50
- maxlength={length}
51
- required={true}
52
- bind:value
53
- {...rest}
54
- />
55
-
56
- <div class={styles.placeholders}>
57
- {#each inputPlaceholders as placeholder, index}
58
- <div
59
- class={typeof placeholder === 'string' ? styles.separator : styles.placeholder}
60
- data-active={getAdjustedIndex(index) === value.length ? true : undefined}
61
- >
62
- {typeof placeholder === 'string' ? placeholder : value[getAdjustedIndex(index)]}
63
- </div>
64
- {/each}
65
- </div>
158
+ {#each inputs as input, index}
159
+ {#if typeof input === 'string'}
160
+ <div class={styles.separator}>{input}</div>
161
+ {:else}
162
+ <Input
163
+ id={`${name}-${index}`}
164
+ className={styles.input}
165
+ type="text"
166
+ maxlength="1"
167
+ disabled={disabled}
168
+ inputmode="numeric"
169
+ autocomplete="one-time-code"
170
+ data-index={input}
171
+ aria-label={`OTP digit ${input + 1}`}
172
+ onkeydown={handleKeyDown}
173
+ onKeyUp={handleKeyUp}
174
+ onfocus={handleFocus}
175
+ onInput={handleInput}
176
+ onpaste={handlePaste}
177
+ {...rest}
178
+ />
179
+ {/if}
180
+ {/each}
66
181
  </div>
67
182
 
68
183
  {#if subText}