free-astro-components 0.0.25 → 0.0.26
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/.vscode/settings.json +3 -0
- package/package.json +1 -1
- package/src/components/Checkbox.astro +4 -0
- package/src/components/Input.astro +3 -11
- package/src/components/Select.astro +372 -0
- package/src/components/Textarea.astro +1 -10
- package/src/index.js +1 -0
- package/src/pages/index.astro +56 -2
- package/src/types/index.d.ts +5 -1
- package/src/utils/input.ts +9 -0
- package/src/utils/select.ts +108 -0
- package/src/utils/utils.ts +0 -10
package/package.json
CHANGED
|
@@ -6,8 +6,6 @@ import Icon from './Icon.astro'
|
|
|
6
6
|
interface Props {
|
|
7
7
|
icon?: string
|
|
8
8
|
label?: string
|
|
9
|
-
placeholder?: string
|
|
10
|
-
disabled?: boolean
|
|
11
9
|
helperText?: string
|
|
12
10
|
status?: 'default' | 'error' | 'success'
|
|
13
11
|
class?: string
|
|
@@ -16,8 +14,6 @@ interface Props {
|
|
|
16
14
|
const {
|
|
17
15
|
icon,
|
|
18
16
|
label,
|
|
19
|
-
placeholder = '',
|
|
20
|
-
disabled = false,
|
|
21
17
|
helperText = '',
|
|
22
18
|
status = 'default',
|
|
23
19
|
class: className,
|
|
@@ -43,12 +39,7 @@ const inputClasses = ['ac-input', statusClasses, className]
|
|
|
43
39
|
<Icon icon="search" class="ac-input-icon ac-input-icon--left" />
|
|
44
40
|
)
|
|
45
41
|
}
|
|
46
|
-
<input
|
|
47
|
-
class={inputClasses}
|
|
48
|
-
placeholder={placeholder}
|
|
49
|
-
disabled={disabled}
|
|
50
|
-
{...props}
|
|
51
|
-
/>
|
|
42
|
+
<input class={inputClasses} {...props} />
|
|
52
43
|
{
|
|
53
44
|
icon && props.type !== 'password' && (
|
|
54
45
|
<Icon icon={icon} class="ac-input-icon ac-input-icon--right" />
|
|
@@ -218,7 +209,8 @@ const inputClasses = ['ac-input', statusClasses, className]
|
|
|
218
209
|
</style>
|
|
219
210
|
|
|
220
211
|
<script>
|
|
221
|
-
import { DOMLoaded
|
|
212
|
+
import { DOMLoaded } from '../utils/utils.ts'
|
|
213
|
+
import { toggleInputPassword } from '../utils/input'
|
|
222
214
|
|
|
223
215
|
DOMLoaded(() => {
|
|
224
216
|
const inputPasswordButtons = document.querySelectorAll(
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
---
|
|
2
|
+
import '../css/main.css'
|
|
3
|
+
import Icon from './Icon.astro'
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
label?: string
|
|
7
|
+
placeholder?: string
|
|
8
|
+
helperText?: string
|
|
9
|
+
status?: 'default' | 'error' | 'success'
|
|
10
|
+
options?: { label: string; value: string | number; selected?: boolean }[]
|
|
11
|
+
class?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
label,
|
|
16
|
+
placeholder = '',
|
|
17
|
+
helperText,
|
|
18
|
+
status = 'default',
|
|
19
|
+
options = [
|
|
20
|
+
{ label: 'Option 1', value: '1' },
|
|
21
|
+
{ label: 'Option 2', value: '2' },
|
|
22
|
+
{ label: 'Option 3', value: '3' },
|
|
23
|
+
],
|
|
24
|
+
class: className,
|
|
25
|
+
...props
|
|
26
|
+
} = Astro.props
|
|
27
|
+
|
|
28
|
+
const hasSelectedOption = options.some((option) => option.selected)
|
|
29
|
+
|
|
30
|
+
const statusClasses = {
|
|
31
|
+
default: '',
|
|
32
|
+
error: 'ac-select--error',
|
|
33
|
+
success: 'ac-select--success',
|
|
34
|
+
}[status]
|
|
35
|
+
|
|
36
|
+
const selectClasses = [
|
|
37
|
+
'ac-select',
|
|
38
|
+
statusClasses,
|
|
39
|
+
className,
|
|
40
|
+
hasSelectedOption ? 'is-selected' : '',
|
|
41
|
+
]
|
|
42
|
+
.filter(Boolean)
|
|
43
|
+
.join(' ')
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
<label class="ac-select-wrapper">
|
|
47
|
+
{label && <span class="ac-select-label">{label}</span>}
|
|
48
|
+
|
|
49
|
+
<div>
|
|
50
|
+
<select class={selectClasses} {...props}>
|
|
51
|
+
{
|
|
52
|
+
hasSelectedOption ? (
|
|
53
|
+
<option disabled hidden>
|
|
54
|
+
{placeholder}
|
|
55
|
+
</option>
|
|
56
|
+
) : (
|
|
57
|
+
<option disabled selected hidden>
|
|
58
|
+
{placeholder}
|
|
59
|
+
</option>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
{
|
|
64
|
+
options.map((option) => (
|
|
65
|
+
<option value={option.value} selected={option.selected}>
|
|
66
|
+
{option.label}
|
|
67
|
+
</option>
|
|
68
|
+
))
|
|
69
|
+
}
|
|
70
|
+
</select>
|
|
71
|
+
|
|
72
|
+
<Icon icon="chevron-down" class="ac-select-icon" />
|
|
73
|
+
|
|
74
|
+
<div class="ac-select-popover">
|
|
75
|
+
<div>
|
|
76
|
+
<ul class="ac-select-list">
|
|
77
|
+
{
|
|
78
|
+
options.map((option) => (
|
|
79
|
+
<li>
|
|
80
|
+
<button disabled class={option.selected ? 'is-selected' : ''}>
|
|
81
|
+
<span>{option.label}</span>
|
|
82
|
+
<Icon icon="check" />
|
|
83
|
+
</button>
|
|
84
|
+
</li>
|
|
85
|
+
))
|
|
86
|
+
}
|
|
87
|
+
</ul>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
{
|
|
93
|
+
helperText && (
|
|
94
|
+
<span class="ac-select-helper-text">
|
|
95
|
+
{status === 'error' && <Icon icon="warning" />}
|
|
96
|
+
{status === 'success' && <Icon icon="check-circle" />}
|
|
97
|
+
{helperText}
|
|
98
|
+
</span>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
</label>
|
|
102
|
+
|
|
103
|
+
<style>
|
|
104
|
+
:root {
|
|
105
|
+
--ac-select-border-radius: var(--ac-rounded-xl);
|
|
106
|
+
--ac-select-padding: var(--ac-spacing-4);
|
|
107
|
+
--ac-select-height: var(--ac-spacing-12);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.ac-select-wrapper {
|
|
111
|
+
display: flex;
|
|
112
|
+
flex-direction: column;
|
|
113
|
+
font-family: var(--ac-font-sans);
|
|
114
|
+
gap: var(--ac-spacing-2);
|
|
115
|
+
|
|
116
|
+
> div {
|
|
117
|
+
position: relative;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
&:has(.ac-select.is-open) .ac-select-icon {
|
|
121
|
+
transform: translateY(-50%) rotate(180deg);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
&:has(.ac-select.is-open) .ac-select-popover {
|
|
125
|
+
grid-template-rows: 1fr;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
&:has(.ac-select:disabled) {
|
|
129
|
+
opacity: var(--ac-disabled-opacity);
|
|
130
|
+
pointer-events: none;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
&:has(.ac-select--error) .ac-select-helper-text {
|
|
134
|
+
color: rgb(var(--ac-danger));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
&:has(.ac-select--success) .ac-select-helper-text {
|
|
138
|
+
color: rgb(var(--ac-success));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.ac-select {
|
|
143
|
+
appearance: none;
|
|
144
|
+
background-color: rgb(var(--ac-white));
|
|
145
|
+
background-image: none;
|
|
146
|
+
border-color: rgb(var(--ac-gray-100));
|
|
147
|
+
border-radius: var(--ac-select-border-radius);
|
|
148
|
+
border-width: var(--ac-border-2);
|
|
149
|
+
color: rgb(var(--ac-gray-300));
|
|
150
|
+
cursor: pointer;
|
|
151
|
+
font-size: var(--ac-text-base);
|
|
152
|
+
font-weight: var(--ac-font-normal);
|
|
153
|
+
height: var(--ac-select-height);
|
|
154
|
+
line-height: var(--ac-leading-normal);
|
|
155
|
+
padding-left: var(--ac-select-padding);
|
|
156
|
+
padding-right: var(--ac-select-padding);
|
|
157
|
+
transition: all 0.3s ease-in-out;
|
|
158
|
+
width: 100%;
|
|
159
|
+
|
|
160
|
+
&.is-selected {
|
|
161
|
+
color: rgb(var(--ac-dark));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
&.is-open,
|
|
165
|
+
&.is-open:hover {
|
|
166
|
+
border-color: var(--ac-primary-hover);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
&:hover {
|
|
170
|
+
border-color: rgb(var(--ac-gray-200));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
&:focus,
|
|
174
|
+
&.ac-select--error:focus,
|
|
175
|
+
&:focus:hover {
|
|
176
|
+
border-color: var(--ac-primary-hover);
|
|
177
|
+
outline: none;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
&:disabled {
|
|
181
|
+
background-color: rgb(var(--ac-gray-100));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
&.ac-select--error,
|
|
185
|
+
&.ac-select--error:hover {
|
|
186
|
+
border-color: rgb(var(--ac-danger));
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.ac-select-label {
|
|
191
|
+
color: rgb(var(--ac-gray-400));
|
|
192
|
+
font-size: var(--ac-text-sm);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.ac-select-helper-text {
|
|
196
|
+
align-items: center;
|
|
197
|
+
color: rgb(var(--ac-gray-300));
|
|
198
|
+
display: flex;
|
|
199
|
+
font-size: var(--ac-text-sm);
|
|
200
|
+
gap: var(--ac-spacing-1);
|
|
201
|
+
|
|
202
|
+
svg {
|
|
203
|
+
flex-shrink: 0;
|
|
204
|
+
height: var(--ac-spacing-4);
|
|
205
|
+
width: var(--ac-spacing-4);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.ac-select-icon {
|
|
210
|
+
color: rgb(var(--ac-gray-300));
|
|
211
|
+
position: absolute;
|
|
212
|
+
right: var(--ac-select-padding);
|
|
213
|
+
top: 50%;
|
|
214
|
+
transform-origin: center;
|
|
215
|
+
transform: translateY(-50%);
|
|
216
|
+
transition: all 0.3s ease-in-out;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.ac-select-popover {
|
|
220
|
+
display: grid;
|
|
221
|
+
grid-template-rows: 0fr;
|
|
222
|
+
left: 0;
|
|
223
|
+
position: absolute;
|
|
224
|
+
position: absolute;
|
|
225
|
+
right: 0;
|
|
226
|
+
top: calc(100% + var(--ac-spacing-1));
|
|
227
|
+
transition: grid-template-rows 0.3s ease-in-out;
|
|
228
|
+
z-index: 1;
|
|
229
|
+
|
|
230
|
+
> div {
|
|
231
|
+
background-color: rgb(var(--ac-white));
|
|
232
|
+
border-radius: var(--ac-select-border-radius);
|
|
233
|
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
234
|
+
list-style: none;
|
|
235
|
+
margin: 0;
|
|
236
|
+
overflow: hidden;
|
|
237
|
+
padding: 0;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.ac-select-list {
|
|
242
|
+
-ms-overflow-style: none;
|
|
243
|
+
border-color: rgb(var(--ac-gray-100));
|
|
244
|
+
border-radius: var(--ac-rounded-xl);
|
|
245
|
+
border-width: var(--ac-border-1);
|
|
246
|
+
display: flex;
|
|
247
|
+
flex-direction: column;
|
|
248
|
+
gap: var(--ac-spacing-1);
|
|
249
|
+
list-style: none;
|
|
250
|
+
margin: 0;
|
|
251
|
+
max-height: 14.188rem;
|
|
252
|
+
overflow-y: auto;
|
|
253
|
+
padding: var(--ac-spacing-1);
|
|
254
|
+
scrollbar-width: none;
|
|
255
|
+
|
|
256
|
+
> li > button {
|
|
257
|
+
align-items: center;
|
|
258
|
+
background-color: rgb(var(--ac-white));
|
|
259
|
+
border-radius: var(--ac-rounded-lg);
|
|
260
|
+
color: rgb(var(--ac-gray-400));
|
|
261
|
+
cursor: pointer;
|
|
262
|
+
display: flex;
|
|
263
|
+
font-size: var(--ac-text-sm);
|
|
264
|
+
justify-content: space-between;
|
|
265
|
+
padding: var(--ac-spacing-2) var(--ac-select-padding);
|
|
266
|
+
transition: all 0.3s ease-in-out;
|
|
267
|
+
width: 100%;
|
|
268
|
+
|
|
269
|
+
> svg {
|
|
270
|
+
color: rgb(var(--ac-primary));
|
|
271
|
+
opacity: 0;
|
|
272
|
+
transition: all 0.3s ease-in-out;
|
|
273
|
+
width: var(--ac-spacing-6);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
&:hover {
|
|
277
|
+
color: var(--ac-primary-hover);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
&:hover,
|
|
281
|
+
&:focus {
|
|
282
|
+
background-color: rgba(var(--ac-primary), 0.1);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
&:focus {
|
|
286
|
+
outline: none;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
&.is-selected {
|
|
290
|
+
background-color: rgba(var(--ac-primary), 0.1);
|
|
291
|
+
color: rgb(var(--ac-dark));
|
|
292
|
+
|
|
293
|
+
> svg {
|
|
294
|
+
opacity: 1;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
</style>
|
|
300
|
+
|
|
301
|
+
<script>
|
|
302
|
+
import { DOMLoaded } from '../utils/utils'
|
|
303
|
+
import {
|
|
304
|
+
closeSelect,
|
|
305
|
+
openSelect,
|
|
306
|
+
selectOption,
|
|
307
|
+
handleDocumentMousedown,
|
|
308
|
+
handleDocumentKeydown,
|
|
309
|
+
} from '../utils/select.ts'
|
|
310
|
+
|
|
311
|
+
DOMLoaded(() => {
|
|
312
|
+
const selects = document.querySelectorAll('.ac-select')
|
|
313
|
+
|
|
314
|
+
selects.forEach((item) => {
|
|
315
|
+
const select = item as HTMLSelectElement
|
|
316
|
+
const selectWrapper = select.closest('.ac-select-wrapper')
|
|
317
|
+
const popover = selectWrapper?.querySelector(
|
|
318
|
+
'.ac-select-popover'
|
|
319
|
+
) as HTMLElement
|
|
320
|
+
const options = popover?.querySelectorAll(
|
|
321
|
+
'.ac-select-list li button'
|
|
322
|
+
) as NodeListOf<HTMLButtonElement>
|
|
323
|
+
|
|
324
|
+
let isOpen = false
|
|
325
|
+
|
|
326
|
+
const setIsOpen = (value: boolean) => {
|
|
327
|
+
isOpen = value
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
select.addEventListener('mousedown', (event) =>
|
|
331
|
+
openSelect(event, options, select).then(() => {
|
|
332
|
+
setIsOpen(true)
|
|
333
|
+
})
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
select.addEventListener('keydown', (event) => {
|
|
337
|
+
if (
|
|
338
|
+
event.key === 'Enter' ||
|
|
339
|
+
event.code === 'Space' ||
|
|
340
|
+
event.key === 'ArrowDown' ||
|
|
341
|
+
event.key === 'ArrowUp'
|
|
342
|
+
)
|
|
343
|
+
openSelect(event, options, select).then(() => {
|
|
344
|
+
setIsOpen(true)
|
|
345
|
+
})
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
document.addEventListener('mousedown', (event) => {
|
|
349
|
+
handleDocumentMousedown(
|
|
350
|
+
event,
|
|
351
|
+
options,
|
|
352
|
+
select,
|
|
353
|
+
popover,
|
|
354
|
+
isOpen,
|
|
355
|
+
setIsOpen
|
|
356
|
+
)
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
document.addEventListener('keydown', (event) => {
|
|
360
|
+
handleDocumentKeydown(event, options, select, isOpen, setIsOpen)
|
|
361
|
+
})
|
|
362
|
+
|
|
363
|
+
options?.forEach((option, index) => {
|
|
364
|
+
option.addEventListener('click', () => {
|
|
365
|
+
selectOption(index, options, option, select)
|
|
366
|
+
closeSelect(options, select)
|
|
367
|
+
setIsOpen(false)
|
|
368
|
+
})
|
|
369
|
+
})
|
|
370
|
+
})
|
|
371
|
+
})
|
|
372
|
+
</script>
|
|
@@ -7,8 +7,6 @@ interface Props {
|
|
|
7
7
|
icon?: string
|
|
8
8
|
label?: string
|
|
9
9
|
value?: string
|
|
10
|
-
placeholder?: string
|
|
11
|
-
disabled?: boolean
|
|
12
10
|
helperText?: string
|
|
13
11
|
status?: 'default' | 'error' | 'success'
|
|
14
12
|
class?: string
|
|
@@ -18,8 +16,6 @@ const {
|
|
|
18
16
|
icon,
|
|
19
17
|
label,
|
|
20
18
|
value,
|
|
21
|
-
placeholder = '',
|
|
22
|
-
disabled = false,
|
|
23
19
|
helperText = '',
|
|
24
20
|
status = 'default',
|
|
25
21
|
class: className,
|
|
@@ -40,12 +36,7 @@ const inputClasses = ['ac-textarea', statusClasses, className]
|
|
|
40
36
|
<label class="ac-textarea-wrapper">
|
|
41
37
|
{label && <span class="ac-textarea-label">{label}</span>}
|
|
42
38
|
<div>
|
|
43
|
-
<textarea
|
|
44
|
-
class={inputClasses}
|
|
45
|
-
placeholder={placeholder}
|
|
46
|
-
disabled={disabled}
|
|
47
|
-
{...props}>{value}</textarea
|
|
48
|
-
>
|
|
39
|
+
<textarea class={inputClasses} {...props}>{value}</textarea>
|
|
49
40
|
{
|
|
50
41
|
icon && (
|
|
51
42
|
<Icon icon={icon} class="ac-textarea-icon ac-textarea-icon--right" />
|
package/src/index.js
CHANGED
|
@@ -5,3 +5,4 @@ export { default as Radio } from './components/Radio.astro'
|
|
|
5
5
|
export { default as Switch } from './components/Switch.astro'
|
|
6
6
|
export { default as Input } from './components/Input.astro'
|
|
7
7
|
export { default as Textarea } from './components/Textarea.astro'
|
|
8
|
+
export { default as Select } from './components/Select.astro'
|
package/src/pages/index.astro
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import Layout from '../layouts/Layout.astro'
|
|
3
3
|
import Input from '../components/Input.astro'
|
|
4
4
|
import Textarea from '../components/Textarea.astro'
|
|
5
|
+
import Select from '../components/Select.astro'
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
<Layout title="Welcome to Astro.">
|
|
@@ -136,8 +137,61 @@ import Textarea from '../components/Textarea.astro'
|
|
|
136
137
|
<Textarea
|
|
137
138
|
icon="star"
|
|
138
139
|
label="Textarea label"
|
|
139
|
-
placeholder="
|
|
140
|
+
placeholder="Textarea with icon"
|
|
141
|
+
helperText="Helper text"
|
|
142
|
+
/>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<h2>Select</h2>
|
|
146
|
+
<div class="controls-wrapper">
|
|
147
|
+
<Select
|
|
148
|
+
label="Select label"
|
|
149
|
+
placeholder="Select an option"
|
|
150
|
+
helperText="Helper text"
|
|
151
|
+
/>
|
|
152
|
+
<Select
|
|
153
|
+
label="Select label"
|
|
154
|
+
placeholder="Select an option"
|
|
155
|
+
options={[
|
|
156
|
+
{ label: 'Option 1', value: '1' },
|
|
157
|
+
{ label: 'Option 2', value: '2', selected: true },
|
|
158
|
+
{ label: 'Option 3', value: '3' },
|
|
159
|
+
]}
|
|
160
|
+
helperText="Helper text"
|
|
161
|
+
/>
|
|
162
|
+
<Select
|
|
163
|
+
label="Select label"
|
|
164
|
+
placeholder="Select an option"
|
|
140
165
|
helperText="Helper text"
|
|
166
|
+
disabled
|
|
167
|
+
/>
|
|
168
|
+
<Select
|
|
169
|
+
label="Select label"
|
|
170
|
+
placeholder="Select an option"
|
|
171
|
+
options={[
|
|
172
|
+
{ label: 'Option 1', value: '1' },
|
|
173
|
+
{ label: 'Option 2', value: '2', selected: true },
|
|
174
|
+
{ label: 'Option 3', value: '3' },
|
|
175
|
+
]}
|
|
176
|
+
helperText="Helper text"
|
|
177
|
+
disabled
|
|
178
|
+
/>
|
|
179
|
+
<Select
|
|
180
|
+
label="Select label"
|
|
181
|
+
placeholder="Select an option"
|
|
182
|
+
helperText="Helper text"
|
|
183
|
+
status="error"
|
|
184
|
+
/>
|
|
185
|
+
<Select
|
|
186
|
+
label="Select label"
|
|
187
|
+
placeholder="Select an option"
|
|
188
|
+
options={[
|
|
189
|
+
{ label: 'Option 1', value: '1' },
|
|
190
|
+
{ label: 'Option 2', value: '2', selected: true },
|
|
191
|
+
{ label: 'Option 3', value: '3' },
|
|
192
|
+
]}
|
|
193
|
+
helperText="Helper text"
|
|
194
|
+
status="success"
|
|
141
195
|
/>
|
|
142
196
|
</div>
|
|
143
197
|
</div>
|
|
@@ -148,7 +202,7 @@ import Textarea from '../components/Textarea.astro'
|
|
|
148
202
|
/*@import '../css/preflight.css';*/
|
|
149
203
|
|
|
150
204
|
.content {
|
|
151
|
-
padding: var(--ac-spacing-8);
|
|
205
|
+
padding: var(--ac-spacing-8) var(--ac-spacing-8) 500px;
|
|
152
206
|
}
|
|
153
207
|
|
|
154
208
|
.controls-wrapper {
|
package/src/types/index.d.ts
CHANGED
|
@@ -24,4 +24,8 @@ export const Input: Input
|
|
|
24
24
|
|
|
25
25
|
// Textarea component
|
|
26
26
|
export type Textarea = typeof import('../index.js').Textarea
|
|
27
|
-
export const Textarea: Textarea
|
|
27
|
+
export const Textarea: Textarea
|
|
28
|
+
|
|
29
|
+
// Select component
|
|
30
|
+
export type Select = typeof import('../index.js').Select
|
|
31
|
+
export const Select: Select
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const toggleInputPassword = (button: Element, input: HTMLInputElement) => {
|
|
2
|
+
if (button.classList.contains('is-visible')) {
|
|
3
|
+
input.type = 'password'
|
|
4
|
+
button.classList.remove('is-visible')
|
|
5
|
+
} else {
|
|
6
|
+
input.type = 'text'
|
|
7
|
+
button.classList.add('is-visible')
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
export const openSelect = (
|
|
2
|
+
event: Event,
|
|
3
|
+
options: NodeListOf<HTMLButtonElement>,
|
|
4
|
+
select: HTMLSelectElement
|
|
5
|
+
): Promise<void> => {
|
|
6
|
+
event.preventDefault()
|
|
7
|
+
select.classList.add('is-open')
|
|
8
|
+
|
|
9
|
+
return new Promise((resolve) => {
|
|
10
|
+
let hasSelectedOption = false
|
|
11
|
+
|
|
12
|
+
options.forEach((option: HTMLButtonElement) => {
|
|
13
|
+
option.removeAttribute('disabled')
|
|
14
|
+
if (option.classList.contains('is-selected')) {
|
|
15
|
+
hasSelectedOption = true
|
|
16
|
+
option.focus()
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
if (!hasSelectedOption && options.length > 0) {
|
|
21
|
+
options[0].focus()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
setTimeout(() => resolve(), 0)
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const closeSelect = (
|
|
29
|
+
options: NodeListOf<HTMLButtonElement>,
|
|
30
|
+
select: HTMLSelectElement
|
|
31
|
+
) => {
|
|
32
|
+
select.classList.remove('is-open')
|
|
33
|
+
options.forEach((option: HTMLButtonElement) => {
|
|
34
|
+
option.setAttribute('disabled', 'disabled')
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const selectOption = (
|
|
39
|
+
index: number,
|
|
40
|
+
options: NodeListOf<HTMLButtonElement>,
|
|
41
|
+
option: Element,
|
|
42
|
+
select: HTMLSelectElement
|
|
43
|
+
) => {
|
|
44
|
+
select.selectedIndex = index + 1
|
|
45
|
+
select.classList.add('is-selected')
|
|
46
|
+
options.forEach((option: Element) => option.classList.remove('is-selected'))
|
|
47
|
+
option.classList.add('is-selected')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const handleDocumentMousedown = (
|
|
51
|
+
event: MouseEvent,
|
|
52
|
+
options: NodeListOf<HTMLButtonElement>,
|
|
53
|
+
select: HTMLSelectElement,
|
|
54
|
+
popover: HTMLElement,
|
|
55
|
+
isOpen: boolean,
|
|
56
|
+
setIsOpen: (isOpen: boolean) => void
|
|
57
|
+
) => {
|
|
58
|
+
if (!isOpen) return
|
|
59
|
+
|
|
60
|
+
const target = event.target as HTMLElement | null
|
|
61
|
+
const insideSelect = select.contains(target)
|
|
62
|
+
const insidePopover = popover?.contains(target)
|
|
63
|
+
|
|
64
|
+
if (!insideSelect && !insidePopover) {
|
|
65
|
+
closeSelect(options, select)
|
|
66
|
+
setIsOpen(false)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const handleDocumentKeydown = (
|
|
71
|
+
event: KeyboardEvent,
|
|
72
|
+
options: NodeListOf<HTMLButtonElement>,
|
|
73
|
+
select: HTMLSelectElement,
|
|
74
|
+
isOpen: boolean,
|
|
75
|
+
setIsOpen: (isOpen: boolean) => void
|
|
76
|
+
) => {
|
|
77
|
+
if (!isOpen) return
|
|
78
|
+
|
|
79
|
+
const activeElement = document.activeElement as HTMLElement
|
|
80
|
+
const focusedOption = Array.from(options).indexOf(
|
|
81
|
+
activeElement as HTMLButtonElement
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
if (event.key === 'Escape') {
|
|
85
|
+
closeSelect(options, select)
|
|
86
|
+
setIsOpen(false)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (event.key === 'ArrowDown') {
|
|
90
|
+
event.preventDefault()
|
|
91
|
+
focusNextOption(focusedOption, options, 1)
|
|
92
|
+
}
|
|
93
|
+
if (event.key === 'ArrowUp') {
|
|
94
|
+
event.preventDefault()
|
|
95
|
+
focusNextOption(focusedOption, options, -1)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const focusNextOption = (
|
|
100
|
+
currentIndex: number,
|
|
101
|
+
options: NodeListOf<HTMLButtonElement>,
|
|
102
|
+
direction: number
|
|
103
|
+
) => {
|
|
104
|
+
if (currentIndex === -1) return
|
|
105
|
+
const nextIndex =
|
|
106
|
+
(currentIndex + direction + options.length) % options.length
|
|
107
|
+
options[nextIndex].focus()
|
|
108
|
+
}
|
package/src/utils/utils.ts
CHANGED
|
@@ -4,14 +4,4 @@ export const DOMLoaded = (callback: () => void) => {
|
|
|
4
4
|
} else {
|
|
5
5
|
callback()
|
|
6
6
|
}
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export const toggleInputPassword = (button: Element, input: HTMLInputElement) => {
|
|
10
|
-
if (button.classList.contains('is-visible')) {
|
|
11
|
-
input.type = 'password'
|
|
12
|
-
button.classList.remove('is-visible')
|
|
13
|
-
} else {
|
|
14
|
-
input.type = 'text'
|
|
15
|
-
button.classList.add('is-visible')
|
|
16
|
-
}
|
|
17
7
|
}
|