@iyulab/components 1.35.7 → 1.37.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/CHANGELOG.md +36 -0
- package/README.md +1 -1
- package/dist/assets/locales/ar.json.js +8 -2
- package/dist/assets/locales/de.json.js +8 -2
- package/dist/assets/locales/en.json.js +8 -2
- package/dist/assets/locales/es.json.js +8 -2
- package/dist/assets/locales/fr.json.js +8 -2
- package/dist/assets/locales/id.json.js +8 -2
- package/dist/assets/locales/ja.json.js +8 -2
- package/dist/assets/locales/ko.json.js +8 -2
- package/dist/assets/locales/pt-BR.json.js +8 -2
- package/dist/assets/locales/ru.json.js +8 -2
- package/dist/assets/locales/th.json.js +8 -2
- package/dist/assets/locales/vi.json.js +8 -2
- package/dist/assets/locales/zh-CN.json.js +8 -2
- package/dist/assets/locales/zh-TW.json.js +8 -2
- package/dist/components/UFloatingElement.d.ts +9 -1
- package/dist/components/date-picker/UDatePicker.d.ts +5 -0
- package/dist/components/date-picker/UDatePicker.js +1 -0
- package/dist/components/file-input/UFileInput.d.ts +57 -0
- package/dist/components/file-input/UFileInput.js +140 -0
- package/dist/components/file-input/UFileInput.styles.d.ts +1 -0
- package/dist/components/file-input/UFileInput.styles.js +80 -0
- package/dist/components/select/USelect.d.ts +4 -0
- package/dist/components/select/USelect.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -1
- package/dist/react/UFileInput.d.ts +13 -0
- package/dist/react/UFileInput.js +12 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.js +1 -0
- package/dist/utilities/Locale.d.ts +1 -1
- package/package.json +1 -1
- package/skills/iyulab-components/SKILL.md +2 -0
- package/skills/iyulab-components/references/components/file-input.md +81 -0
- package/skills/iyulab-components/references/components/popover.md +7 -1
- package/skills/iyulab-components/references/components/tooltip.md +7 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.37.0] - 2026-09-06
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`u-date-picker` and `u-select` expose a `:state(open)` custom state.**
|
|
8
|
+
Both already tracked their popover's open/closed state privately; it is now
|
|
9
|
+
surfaced through `ElementInternals.states` so consumers can style the trigger
|
|
10
|
+
while the popover is showing — `u-date-picker:state(open)::part(container)`,
|
|
11
|
+
`u-select:state(open)::part(container)`. Deliberately not a reflected
|
|
12
|
+
attribute: the state stays private to the component and exists only as a CSS
|
|
13
|
+
hook, so no new public attribute enters the API surface.
|
|
14
|
+
|
|
15
|
+
### Documentation
|
|
16
|
+
|
|
17
|
+
- **`UFloatingElement.placement` documents that `flip` is always applied.**
|
|
18
|
+
Whenever `placement` is set, floating-ui's `flip` middleware runs (switching
|
|
19
|
+
to the opposite side when the placed side has no room) — this has always been
|
|
20
|
+
the behavior but was undocumented, and `shift`'s description ("keeps the
|
|
21
|
+
element on screen") over-promised, since `shift` only corrects the cross axis
|
|
22
|
+
*within* a side. Both properties now describe the split, and a regression test
|
|
23
|
+
covers the flip fallback.
|
|
24
|
+
|
|
25
|
+
## [1.36.0] - 2026-09-04
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- **`UFileInput` (`u-file-input`), a form control wrapping a native
|
|
30
|
+
`<input type="file">`.** Trigger button, selected-file(s) status text,
|
|
31
|
+
and a clear button, styled to match the rest of the form control
|
|
32
|
+
family. `value` is `File[] | null`; native `<form>`/`u-form`
|
|
33
|
+
submission works out of the box via `ElementInternals` (single file)
|
|
34
|
+
or a `FormData` with the file repeatedly appended under the same key
|
|
35
|
+
(multiple files, matching native multi-file submission semantics).
|
|
36
|
+
`accept`/`multiple` pass through to the underlying input.
|
|
37
|
+
Drag-and-drop is out of scope for this release.
|
|
38
|
+
|
|
3
39
|
## [1.35.7] - 2026-09-03
|
|
4
40
|
|
|
5
41
|
### Fixed
|
package/README.md
CHANGED
|
@@ -70,7 +70,7 @@ npx skills add ./node_modules/@iyulab/components
|
|
|
70
70
|
|
|
71
71
|
**Buttons & Actions** — `u-button`, `u-button-group`, `u-icon-button`, `u-copy-button`, `u-chip`
|
|
72
72
|
|
|
73
|
-
**Form Controls** — `u-input`, `u-textarea`, `u-select`, `u-date-picker`, `u-checkbox`, `u-radio`, `u-switch`, `u-slider`, `u-rating`, `u-field`, `u-form`, `u-option`
|
|
73
|
+
**Form Controls** — `u-input`, `u-textarea`, `u-select`, `u-date-picker`, `u-file-input`, `u-checkbox`, `u-radio`, `u-switch`, `u-slider`, `u-rating`, `u-field`, `u-form`, `u-option`
|
|
74
74
|
|
|
75
75
|
**Overlay & Floating** — `u-dialog`, `u-drawer`, `u-popover`, `u-tooltip`
|
|
76
76
|
|
|
@@ -24,6 +24,9 @@ var nextMonth = "الشهر التالي";
|
|
|
24
24
|
var close = "إغلاق";
|
|
25
25
|
var copy = "نسخ";
|
|
26
26
|
var copied = "تم النسخ";
|
|
27
|
+
var chooseFile = "اختر ملفًا";
|
|
28
|
+
var noFileChosen = "لم يتم اختيار أي ملف";
|
|
29
|
+
var filesSelected = "تم اختيار {count} ملفات";
|
|
27
30
|
var ar_default = {
|
|
28
31
|
valueMissing,
|
|
29
32
|
badInput,
|
|
@@ -49,7 +52,10 @@ var ar_default = {
|
|
|
49
52
|
nextMonth,
|
|
50
53
|
close,
|
|
51
54
|
copy,
|
|
52
|
-
copied
|
|
55
|
+
copied,
|
|
56
|
+
chooseFile,
|
|
57
|
+
noFileChosen,
|
|
58
|
+
filesSelected
|
|
53
59
|
};
|
|
54
60
|
//#endregion
|
|
55
|
-
export { badInput, chooseDate, clear, close, copied, copy, decrement, ar_default as default, goToSlide, hidePassword, increment, nextMonth, nextSlide, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
61
|
+
export { badInput, chooseDate, chooseFile, clear, close, copied, copy, decrement, ar_default as default, filesSelected, goToSlide, hidePassword, increment, nextMonth, nextSlide, noFileChosen, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
@@ -24,6 +24,9 @@ var nextMonth = "Nächster Monat";
|
|
|
24
24
|
var close = "Schließen";
|
|
25
25
|
var copy = "Kopieren";
|
|
26
26
|
var copied = "Kopiert";
|
|
27
|
+
var chooseFile = "Datei auswählen";
|
|
28
|
+
var noFileChosen = "Keine Datei ausgewählt";
|
|
29
|
+
var filesSelected = "{count} Dateien ausgewählt";
|
|
27
30
|
var de_default = {
|
|
28
31
|
valueMissing,
|
|
29
32
|
badInput,
|
|
@@ -49,7 +52,10 @@ var de_default = {
|
|
|
49
52
|
nextMonth,
|
|
50
53
|
close,
|
|
51
54
|
copy,
|
|
52
|
-
copied
|
|
55
|
+
copied,
|
|
56
|
+
chooseFile,
|
|
57
|
+
noFileChosen,
|
|
58
|
+
filesSelected
|
|
53
59
|
};
|
|
54
60
|
//#endregion
|
|
55
|
-
export { badInput, chooseDate, clear, close, copied, copy, decrement, de_default as default, goToSlide, hidePassword, increment, nextMonth, nextSlide, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
61
|
+
export { badInput, chooseDate, chooseFile, clear, close, copied, copy, decrement, de_default as default, filesSelected, goToSlide, hidePassword, increment, nextMonth, nextSlide, noFileChosen, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
@@ -24,6 +24,9 @@ var nextMonth = "Next month";
|
|
|
24
24
|
var close = "Close";
|
|
25
25
|
var copy = "Copy";
|
|
26
26
|
var copied = "Copied";
|
|
27
|
+
var chooseFile = "Choose file";
|
|
28
|
+
var noFileChosen = "No file chosen";
|
|
29
|
+
var filesSelected = "{count} files selected";
|
|
27
30
|
var en_default = {
|
|
28
31
|
valueMissing,
|
|
29
32
|
badInput,
|
|
@@ -49,7 +52,10 @@ var en_default = {
|
|
|
49
52
|
nextMonth,
|
|
50
53
|
close,
|
|
51
54
|
copy,
|
|
52
|
-
copied
|
|
55
|
+
copied,
|
|
56
|
+
chooseFile,
|
|
57
|
+
noFileChosen,
|
|
58
|
+
filesSelected
|
|
53
59
|
};
|
|
54
60
|
//#endregion
|
|
55
|
-
export { badInput, chooseDate, clear, close, copied, copy, decrement, en_default as default, goToSlide, hidePassword, increment, nextMonth, nextSlide, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
61
|
+
export { badInput, chooseDate, chooseFile, clear, close, copied, copy, decrement, en_default as default, filesSelected, goToSlide, hidePassword, increment, nextMonth, nextSlide, noFileChosen, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
@@ -23,6 +23,9 @@ var nextMonth = "Mes siguiente";
|
|
|
23
23
|
var close = "Cerrar";
|
|
24
24
|
var copy = "Copiar";
|
|
25
25
|
var copied = "Copiado";
|
|
26
|
+
var chooseFile = "Elegir archivo";
|
|
27
|
+
var noFileChosen = "Ningún archivo elegido";
|
|
28
|
+
var filesSelected = "{count} archivos seleccionados";
|
|
26
29
|
var es_default = {
|
|
27
30
|
valueMissing,
|
|
28
31
|
badInput,
|
|
@@ -48,7 +51,10 @@ var es_default = {
|
|
|
48
51
|
nextMonth,
|
|
49
52
|
close,
|
|
50
53
|
copy,
|
|
51
|
-
copied
|
|
54
|
+
copied,
|
|
55
|
+
chooseFile,
|
|
56
|
+
noFileChosen,
|
|
57
|
+
filesSelected
|
|
52
58
|
};
|
|
53
59
|
//#endregion
|
|
54
|
-
export { badInput, chooseDate, clear, close, copied, copy, decrement, es_default as default, goToSlide, hidePassword, increment, nextMonth, nextSlide, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, tooLong, tooShort, typeMismatch, valueMissing };
|
|
60
|
+
export { badInput, chooseDate, chooseFile, clear, close, copied, copy, decrement, es_default as default, filesSelected, goToSlide, hidePassword, increment, nextMonth, nextSlide, noFileChosen, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, tooLong, tooShort, typeMismatch, valueMissing };
|
|
@@ -24,6 +24,9 @@ var nextMonth = "Mois suivant";
|
|
|
24
24
|
var close = "Fermer";
|
|
25
25
|
var copy = "Copier";
|
|
26
26
|
var copied = "Copié";
|
|
27
|
+
var chooseFile = "Choisir un fichier";
|
|
28
|
+
var noFileChosen = "Aucun fichier choisi";
|
|
29
|
+
var filesSelected = "{count} fichiers sélectionnés";
|
|
27
30
|
var fr_default = {
|
|
28
31
|
valueMissing,
|
|
29
32
|
badInput,
|
|
@@ -49,7 +52,10 @@ var fr_default = {
|
|
|
49
52
|
nextMonth,
|
|
50
53
|
close,
|
|
51
54
|
copy,
|
|
52
|
-
copied
|
|
55
|
+
copied,
|
|
56
|
+
chooseFile,
|
|
57
|
+
noFileChosen,
|
|
58
|
+
filesSelected
|
|
53
59
|
};
|
|
54
60
|
//#endregion
|
|
55
|
-
export { badInput, chooseDate, clear, close, copied, copy, decrement, fr_default as default, goToSlide, hidePassword, increment, nextMonth, nextSlide, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
61
|
+
export { badInput, chooseDate, chooseFile, clear, close, copied, copy, decrement, fr_default as default, filesSelected, goToSlide, hidePassword, increment, nextMonth, nextSlide, noFileChosen, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
@@ -24,6 +24,9 @@ var nextMonth = "Bulan berikutnya";
|
|
|
24
24
|
var close = "Tutup";
|
|
25
25
|
var copy = "Salin";
|
|
26
26
|
var copied = "Disalin";
|
|
27
|
+
var chooseFile = "Pilih berkas";
|
|
28
|
+
var noFileChosen = "Tidak ada berkas dipilih";
|
|
29
|
+
var filesSelected = "{count} berkas dipilih";
|
|
27
30
|
var id_default = {
|
|
28
31
|
valueMissing,
|
|
29
32
|
badInput,
|
|
@@ -49,7 +52,10 @@ var id_default = {
|
|
|
49
52
|
nextMonth,
|
|
50
53
|
close,
|
|
51
54
|
copy,
|
|
52
|
-
copied
|
|
55
|
+
copied,
|
|
56
|
+
chooseFile,
|
|
57
|
+
noFileChosen,
|
|
58
|
+
filesSelected
|
|
53
59
|
};
|
|
54
60
|
//#endregion
|
|
55
|
-
export { badInput, chooseDate, clear, close, copied, copy, decrement, id_default as default, goToSlide, hidePassword, increment, nextMonth, nextSlide, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
61
|
+
export { badInput, chooseDate, chooseFile, clear, close, copied, copy, decrement, id_default as default, filesSelected, goToSlide, hidePassword, increment, nextMonth, nextSlide, noFileChosen, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
@@ -24,6 +24,9 @@ var nextMonth = "翌月";
|
|
|
24
24
|
var close = "閉じる";
|
|
25
25
|
var copy = "コピー";
|
|
26
26
|
var copied = "コピーしました";
|
|
27
|
+
var chooseFile = "ファイルを選択";
|
|
28
|
+
var noFileChosen = "ファイルが選択されていません";
|
|
29
|
+
var filesSelected = "{count}個のファイルを選択しました";
|
|
27
30
|
var ja_default = {
|
|
28
31
|
valueMissing,
|
|
29
32
|
badInput,
|
|
@@ -49,7 +52,10 @@ var ja_default = {
|
|
|
49
52
|
nextMonth,
|
|
50
53
|
close,
|
|
51
54
|
copy,
|
|
52
|
-
copied
|
|
55
|
+
copied,
|
|
56
|
+
chooseFile,
|
|
57
|
+
noFileChosen,
|
|
58
|
+
filesSelected
|
|
53
59
|
};
|
|
54
60
|
//#endregion
|
|
55
|
-
export { badInput, chooseDate, clear, close, copied, copy, decrement, ja_default as default, goToSlide, hidePassword, increment, nextMonth, nextSlide, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
61
|
+
export { badInput, chooseDate, chooseFile, clear, close, copied, copy, decrement, ja_default as default, filesSelected, goToSlide, hidePassword, increment, nextMonth, nextSlide, noFileChosen, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
@@ -24,6 +24,9 @@ var nextMonth = "다음 달";
|
|
|
24
24
|
var close = "닫기";
|
|
25
25
|
var copy = "복사";
|
|
26
26
|
var copied = "복사됨";
|
|
27
|
+
var chooseFile = "파일 선택";
|
|
28
|
+
var noFileChosen = "선택된 파일 없음";
|
|
29
|
+
var filesSelected = "{count}개 파일 선택됨";
|
|
27
30
|
var ko_default = {
|
|
28
31
|
valueMissing,
|
|
29
32
|
badInput,
|
|
@@ -49,7 +52,10 @@ var ko_default = {
|
|
|
49
52
|
nextMonth,
|
|
50
53
|
close,
|
|
51
54
|
copy,
|
|
52
|
-
copied
|
|
55
|
+
copied,
|
|
56
|
+
chooseFile,
|
|
57
|
+
noFileChosen,
|
|
58
|
+
filesSelected
|
|
53
59
|
};
|
|
54
60
|
//#endregion
|
|
55
|
-
export { badInput, chooseDate, clear, close, copied, copy, decrement, ko_default as default, goToSlide, hidePassword, increment, nextMonth, nextSlide, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
61
|
+
export { badInput, chooseDate, chooseFile, clear, close, copied, copy, decrement, ko_default as default, filesSelected, goToSlide, hidePassword, increment, nextMonth, nextSlide, noFileChosen, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
@@ -24,6 +24,9 @@ var nextMonth = "Próximo mês";
|
|
|
24
24
|
var close = "Fechar";
|
|
25
25
|
var copy = "Copiar";
|
|
26
26
|
var copied = "Copiado";
|
|
27
|
+
var chooseFile = "Escolher arquivo";
|
|
28
|
+
var noFileChosen = "Nenhum arquivo escolhido";
|
|
29
|
+
var filesSelected = "{count} arquivos selecionados";
|
|
27
30
|
var pt_BR_default = {
|
|
28
31
|
valueMissing,
|
|
29
32
|
badInput,
|
|
@@ -49,7 +52,10 @@ var pt_BR_default = {
|
|
|
49
52
|
nextMonth,
|
|
50
53
|
close,
|
|
51
54
|
copy,
|
|
52
|
-
copied
|
|
55
|
+
copied,
|
|
56
|
+
chooseFile,
|
|
57
|
+
noFileChosen,
|
|
58
|
+
filesSelected
|
|
53
59
|
};
|
|
54
60
|
//#endregion
|
|
55
|
-
export { badInput, chooseDate, clear, close, copied, copy, decrement, pt_BR_default as default, goToSlide, hidePassword, increment, nextMonth, nextSlide, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
61
|
+
export { badInput, chooseDate, chooseFile, clear, close, copied, copy, decrement, pt_BR_default as default, filesSelected, goToSlide, hidePassword, increment, nextMonth, nextSlide, noFileChosen, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
@@ -24,6 +24,9 @@ var nextMonth = "Следующий месяц";
|
|
|
24
24
|
var close = "Закрыть";
|
|
25
25
|
var copy = "Копировать";
|
|
26
26
|
var copied = "Скопировано";
|
|
27
|
+
var chooseFile = "Выбрать файл";
|
|
28
|
+
var noFileChosen = "Файл не выбран";
|
|
29
|
+
var filesSelected = "Выбрано файлов: {count}";
|
|
27
30
|
var ru_default = {
|
|
28
31
|
valueMissing,
|
|
29
32
|
badInput,
|
|
@@ -49,7 +52,10 @@ var ru_default = {
|
|
|
49
52
|
nextMonth,
|
|
50
53
|
close,
|
|
51
54
|
copy,
|
|
52
|
-
copied
|
|
55
|
+
copied,
|
|
56
|
+
chooseFile,
|
|
57
|
+
noFileChosen,
|
|
58
|
+
filesSelected
|
|
53
59
|
};
|
|
54
60
|
//#endregion
|
|
55
|
-
export { badInput, chooseDate, clear, close, copied, copy, decrement, ru_default as default, goToSlide, hidePassword, increment, nextMonth, nextSlide, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
61
|
+
export { badInput, chooseDate, chooseFile, clear, close, copied, copy, decrement, ru_default as default, filesSelected, goToSlide, hidePassword, increment, nextMonth, nextSlide, noFileChosen, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
@@ -24,6 +24,9 @@ var nextMonth = "เดือนถัดไป";
|
|
|
24
24
|
var close = "ปิด";
|
|
25
25
|
var copy = "คัดลอก";
|
|
26
26
|
var copied = "คัดลอกแล้ว";
|
|
27
|
+
var chooseFile = "เลือกไฟล์";
|
|
28
|
+
var noFileChosen = "ยังไม่ได้เลือกไฟล์";
|
|
29
|
+
var filesSelected = "เลือกแล้ว {count} ไฟล์";
|
|
27
30
|
var th_default = {
|
|
28
31
|
valueMissing,
|
|
29
32
|
badInput,
|
|
@@ -49,7 +52,10 @@ var th_default = {
|
|
|
49
52
|
nextMonth,
|
|
50
53
|
close,
|
|
51
54
|
copy,
|
|
52
|
-
copied
|
|
55
|
+
copied,
|
|
56
|
+
chooseFile,
|
|
57
|
+
noFileChosen,
|
|
58
|
+
filesSelected
|
|
53
59
|
};
|
|
54
60
|
//#endregion
|
|
55
|
-
export { badInput, chooseDate, clear, close, copied, copy, decrement, th_default as default, goToSlide, hidePassword, increment, nextMonth, nextSlide, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
61
|
+
export { badInput, chooseDate, chooseFile, clear, close, copied, copy, decrement, th_default as default, filesSelected, goToSlide, hidePassword, increment, nextMonth, nextSlide, noFileChosen, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
@@ -24,6 +24,9 @@ var nextMonth = "Tháng sau";
|
|
|
24
24
|
var close = "Đóng";
|
|
25
25
|
var copy = "Sao chép";
|
|
26
26
|
var copied = "Đã sao chép";
|
|
27
|
+
var chooseFile = "Chọn tệp";
|
|
28
|
+
var noFileChosen = "Chưa chọn tệp nào";
|
|
29
|
+
var filesSelected = "Đã chọn {count} tệp";
|
|
27
30
|
var vi_default = {
|
|
28
31
|
valueMissing,
|
|
29
32
|
badInput,
|
|
@@ -49,7 +52,10 @@ var vi_default = {
|
|
|
49
52
|
nextMonth,
|
|
50
53
|
close,
|
|
51
54
|
copy,
|
|
52
|
-
copied
|
|
55
|
+
copied,
|
|
56
|
+
chooseFile,
|
|
57
|
+
noFileChosen,
|
|
58
|
+
filesSelected
|
|
53
59
|
};
|
|
54
60
|
//#endregion
|
|
55
|
-
export { badInput, chooseDate, clear, close, copied, copy, decrement, vi_default as default, goToSlide, hidePassword, increment, nextMonth, nextSlide, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
61
|
+
export { badInput, chooseDate, chooseFile, clear, close, copied, copy, decrement, vi_default as default, filesSelected, goToSlide, hidePassword, increment, nextMonth, nextSlide, noFileChosen, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
@@ -24,6 +24,9 @@ var nextMonth = "下个月";
|
|
|
24
24
|
var close = "关闭";
|
|
25
25
|
var copy = "复制";
|
|
26
26
|
var copied = "已复制";
|
|
27
|
+
var chooseFile = "选择文件";
|
|
28
|
+
var noFileChosen = "未选择文件";
|
|
29
|
+
var filesSelected = "已选择{count}个文件";
|
|
27
30
|
var zh_CN_default = {
|
|
28
31
|
valueMissing,
|
|
29
32
|
badInput,
|
|
@@ -49,7 +52,10 @@ var zh_CN_default = {
|
|
|
49
52
|
nextMonth,
|
|
50
53
|
close,
|
|
51
54
|
copy,
|
|
52
|
-
copied
|
|
55
|
+
copied,
|
|
56
|
+
chooseFile,
|
|
57
|
+
noFileChosen,
|
|
58
|
+
filesSelected
|
|
53
59
|
};
|
|
54
60
|
//#endregion
|
|
55
|
-
export { badInput, chooseDate, clear, close, copied, copy, decrement, zh_CN_default as default, goToSlide, hidePassword, increment, nextMonth, nextSlide, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
61
|
+
export { badInput, chooseDate, chooseFile, clear, close, copied, copy, decrement, zh_CN_default as default, filesSelected, goToSlide, hidePassword, increment, nextMonth, nextSlide, noFileChosen, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
@@ -24,6 +24,9 @@ var nextMonth = "下個月";
|
|
|
24
24
|
var close = "關閉";
|
|
25
25
|
var copy = "複製";
|
|
26
26
|
var copied = "已複製";
|
|
27
|
+
var chooseFile = "選擇檔案";
|
|
28
|
+
var noFileChosen = "未選擇檔案";
|
|
29
|
+
var filesSelected = "已選擇{count}個檔案";
|
|
27
30
|
var zh_TW_default = {
|
|
28
31
|
valueMissing,
|
|
29
32
|
badInput,
|
|
@@ -49,7 +52,10 @@ var zh_TW_default = {
|
|
|
49
52
|
nextMonth,
|
|
50
53
|
close,
|
|
51
54
|
copy,
|
|
52
|
-
copied
|
|
55
|
+
copied,
|
|
56
|
+
chooseFile,
|
|
57
|
+
noFileChosen,
|
|
58
|
+
filesSelected
|
|
53
59
|
};
|
|
54
60
|
//#endregion
|
|
55
|
-
export { badInput, chooseDate, clear, close, copied, copy, decrement, zh_TW_default as default, goToSlide, hidePassword, increment, nextMonth, nextSlide, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
61
|
+
export { badInput, chooseDate, chooseFile, clear, close, copied, copy, decrement, zh_TW_default as default, filesSelected, goToSlide, hidePassword, increment, nextMonth, nextSlide, noFileChosen, patternMismatch, previousMonth, previousSlide, rangeOverflow, rangeUnderflow, showPassword, stepMismatch, time, today, tooLong, tooShort, typeMismatch, valueMissing };
|
|
@@ -51,6 +51,12 @@ export declare class UFloatingElement extends UElement {
|
|
|
51
51
|
* 대상 엘리먼트로부터의 배치 위치입니다.
|
|
52
52
|
* 지정하지 않으면 자동으로 가장 적절한 위치가 선택됩니다.
|
|
53
53
|
*
|
|
54
|
+
* @remarks
|
|
55
|
+
* 지정한 변(side)에 공간이 없으면 반대 변으로 자동 전환됩니다(floating-ui의
|
|
56
|
+
* `flip` 미들웨어가 `placement`를 지정했을 때 항상 함께 적용됩니다 — 별도
|
|
57
|
+
* 옵션으로 끌 수 없습니다). `shift`는 이 전환과 별개로, 전환된(또는 지정된)
|
|
58
|
+
* 변 **안에서** 교차축 위치만 보정합니다.
|
|
59
|
+
*
|
|
54
60
|
* @default undefined
|
|
55
61
|
*/
|
|
56
62
|
placement?: Placement;
|
|
@@ -62,7 +68,9 @@ export declare class UFloatingElement extends UElement {
|
|
|
62
68
|
*/
|
|
63
69
|
offset: OffsetOptions;
|
|
64
70
|
/**
|
|
65
|
-
*
|
|
71
|
+
* 지정된(또는 `flip`으로 전환된) 배치 변 **안에서** 교차축 위치를 자동으로
|
|
72
|
+
* 보정할지 여부입니다 — 그 변 자체에 공간이 없어 반대 변으로 넘어가는 것은
|
|
73
|
+
* `placement` 설명의 `flip` 동작이 담당합니다.
|
|
66
74
|
*
|
|
67
75
|
* @default false
|
|
68
76
|
*/
|
|
@@ -16,6 +16,11 @@ export type DatePickerMode = 'date' | 'datetime';
|
|
|
16
16
|
* first day of week from `Intl.Locale(locale).weekInfo?.firstDay`, falling back to Sunday
|
|
17
17
|
* where unsupported.
|
|
18
18
|
*
|
|
19
|
+
* Exposes a `:state(open)` custom state (via `ElementInternals.states`) while the calendar
|
|
20
|
+
* popover is showing — style with `u-date-picker:state(open)::part(container)`. Unlike
|
|
21
|
+
* `mode`/`clearable`, this state is never reflected as a public attribute — it exists purely
|
|
22
|
+
* as a CSS hook for consumers who want to react to the open/closed state from outside.
|
|
23
|
+
*
|
|
19
24
|
* @csspart field - the u-field element
|
|
20
25
|
* @csspart container - the element wrapping the trigger area
|
|
21
26
|
* @csspart popover - the popover element showing the calendar
|
|
@@ -178,6 +178,7 @@ var UDatePicker = class UDatePicker extends UFormControlElement {
|
|
|
178
178
|
updated(changed) {
|
|
179
179
|
super.updated(changed);
|
|
180
180
|
if (changed.has("value")) this.internals?.setFormValue(this.value ?? "");
|
|
181
|
+
if (changed.has("open")) this.internals?.states[this.open ? "add" : "delete"]("open");
|
|
181
182
|
if (changed.has("open") && this.open || changed.has("focusedDate") && this.open && this.grabFocusOnUpdate) {
|
|
182
183
|
this.grabFocusOnUpdate = false;
|
|
183
184
|
this.popoverEl?.updateComplete.then(() => this.focusDayButton(this.focusedDate));
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { PropertyValues } from 'lit';
|
|
2
|
+
import { UFormControlElement } from '../UFormControlElement.js';
|
|
3
|
+
/**
|
|
4
|
+
* 파일 선택 입력 컴포넌트입니다. 네이티브 `<input type="file">`를 감싸
|
|
5
|
+
* 디자인시스템 톤의 트리거 버튼 + 선택 결과 표시로 렌더링합니다.
|
|
6
|
+
*
|
|
7
|
+
* 값은 항상 `File[] | null`입니다 — `multiple`이 꺼져 있어도 선택된 파일은
|
|
8
|
+
* 배열(0개 또는 1개)로 담깁니다. 폼 제출 시 파일이 1개면 그 `File`을,
|
|
9
|
+
* 여러 개면 같은 `name`으로 반복 append한 `FormData`를 `ElementInternals`에
|
|
10
|
+
* 전달합니다 — 네이티브 `<input type="file" multiple>`과 같은 제출 형태입니다.
|
|
11
|
+
*
|
|
12
|
+
* @csspart field - u-field 요소
|
|
13
|
+
* @csspart container - 트리거·상태 텍스트·지우기 버튼을 감싸는 컨테이너
|
|
14
|
+
* @csspart trigger - 파일 선택 트리거 버튼
|
|
15
|
+
* @csspart status - 선택 상태 텍스트("선택된 파일 없음" · 파일명 · "N개 파일 선택됨")
|
|
16
|
+
* @csspart clear-button - 선택 해제 버튼(u-icon)
|
|
17
|
+
* @csspart input - 숨겨진 네이티브 `<input type="file">`
|
|
18
|
+
*
|
|
19
|
+
* @cssprop --u-file-input-display - 호스트의 display (기본값: inline-block)
|
|
20
|
+
* @cssprop --u-file-input-width - 호스트의 width (기본값: auto)
|
|
21
|
+
*
|
|
22
|
+
* @event change - 선택된 파일이 바뀔 때 발생(선택·지우기 공통)
|
|
23
|
+
*/
|
|
24
|
+
export declare class UFileInput extends UFormControlElement<File[] | null> {
|
|
25
|
+
static styles: import('lit').CSSResultGroup[];
|
|
26
|
+
/** 값은 attribute로 표현될 수 없다 — 항상 프로그램적으로만 설정된다. */
|
|
27
|
+
value: File[] | null;
|
|
28
|
+
/** 선택 가능한 파일 형식을 제한한다(네이티브 `accept` 속성 그대로 통과). */
|
|
29
|
+
accept?: string;
|
|
30
|
+
/** 여러 파일을 한 번에 선택할 수 있는지 여부. */
|
|
31
|
+
multiple: boolean;
|
|
32
|
+
triggerEl?: HTMLButtonElement;
|
|
33
|
+
inputEl?: HTMLInputElement;
|
|
34
|
+
/** `value`가 바뀌는 모든 경로(선택·지우기)에서 폼 제출값을 동기화한다 —
|
|
35
|
+
* `UInput`/`UCheckbox`와 같은 경계(`DL-289-1`). 파일은 문자열로 표현할 수
|
|
36
|
+
* 없으므로 1개면 `File`을, 여러 개면 같은 키로 반복 append한 `FormData`를
|
|
37
|
+
* 넘긴다 — 네이티브 `<input type="file" multiple>`의 제출 형태와 동일하다. */
|
|
38
|
+
protected updated(changedProperties: PropertyValues): void;
|
|
39
|
+
private syncFormValue;
|
|
40
|
+
render(): import('lit-html').TemplateResult<1>;
|
|
41
|
+
protected setValidity(): void;
|
|
42
|
+
reset(): void;
|
|
43
|
+
focus(options?: FocusOptions): void;
|
|
44
|
+
blur(): void;
|
|
45
|
+
private handleTriggerClick;
|
|
46
|
+
private handleInputChange;
|
|
47
|
+
private handleClearButtonClick;
|
|
48
|
+
/** `clear-btn`은 순수 표시 요소(`u-icon`)라 네이티브 키보드 활성화가 없다 —
|
|
49
|
+
* `role="button"`+`tabindex="0"`로 포커스 가능하게 한 뒤, Enter/Space를 같은
|
|
50
|
+
* 클릭 핸들러로 릴레이한다(`UInput.handleSuffixIconKeydown`과 같은 패턴). */
|
|
51
|
+
private handleClearButtonKeydown;
|
|
52
|
+
}
|
|
53
|
+
declare global {
|
|
54
|
+
interface HTMLElementTagNameMap {
|
|
55
|
+
'u-file-input': UFileInput;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import __decorateMetadata from "../../_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateMetadata.js";
|
|
2
|
+
import __decorate from "../../_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js";
|
|
3
|
+
import { UFormControlElement } from "../UFormControlElement.js";
|
|
4
|
+
import "../icon/UIcon.js";
|
|
5
|
+
import { Locale } from "../../utilities/Locale.js";
|
|
6
|
+
import "../field/UField.js";
|
|
7
|
+
import { styles } from "./UFileInput.styles.js";
|
|
8
|
+
import { html } from "lit";
|
|
9
|
+
import { customElement, property, query } from "lit/decorators.js";
|
|
10
|
+
import { ifDefined } from "lit/directives/if-defined.js";
|
|
11
|
+
//#region src/components/file-input/UFileInput.ts
|
|
12
|
+
var UFileInput = class UFileInput extends UFormControlElement {
|
|
13
|
+
constructor(..._args) {
|
|
14
|
+
super(..._args);
|
|
15
|
+
this.value = null;
|
|
16
|
+
this.multiple = false;
|
|
17
|
+
this.handleTriggerClick = (e) => {
|
|
18
|
+
e.stopImmediatePropagation();
|
|
19
|
+
if (this.disabled || this.readonly) return;
|
|
20
|
+
this.inputEl?.click();
|
|
21
|
+
};
|
|
22
|
+
this.handleInputChange = (e) => {
|
|
23
|
+
const input = e.target;
|
|
24
|
+
this.value = input.files && input.files.length > 0 ? Array.from(input.files) : null;
|
|
25
|
+
if (!this.novalidate) this.validate();
|
|
26
|
+
this.relay(e);
|
|
27
|
+
};
|
|
28
|
+
this.handleClearButtonClick = (e) => {
|
|
29
|
+
e.stopImmediatePropagation();
|
|
30
|
+
this.reset();
|
|
31
|
+
this.dispatchEvent(new InputEvent("input", {
|
|
32
|
+
bubbles: true,
|
|
33
|
+
composed: true
|
|
34
|
+
}));
|
|
35
|
+
this.dispatchEvent(new Event("change", {
|
|
36
|
+
bubbles: true,
|
|
37
|
+
composed: true
|
|
38
|
+
}));
|
|
39
|
+
this.focus();
|
|
40
|
+
};
|
|
41
|
+
this.handleClearButtonKeydown = (e) => {
|
|
42
|
+
if (e.key !== "Enter" && e.key !== " ") return;
|
|
43
|
+
e.preventDefault();
|
|
44
|
+
this.handleClearButtonClick(e);
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
static {
|
|
48
|
+
this.styles = [super.styles, styles];
|
|
49
|
+
}
|
|
50
|
+
/** `value`가 바뀌는 모든 경로(선택·지우기)에서 폼 제출값을 동기화한다 —
|
|
51
|
+
* `UInput`/`UCheckbox`와 같은 경계(`DL-289-1`). 파일은 문자열로 표현할 수
|
|
52
|
+
* 없으므로 1개면 `File`을, 여러 개면 같은 키로 반복 append한 `FormData`를
|
|
53
|
+
* 넘긴다 — 네이티브 `<input type="file" multiple>`의 제출 형태와 동일하다. */
|
|
54
|
+
updated(changedProperties) {
|
|
55
|
+
super.updated(changedProperties);
|
|
56
|
+
if (changedProperties.has("value")) this.syncFormValue();
|
|
57
|
+
}
|
|
58
|
+
syncFormValue() {
|
|
59
|
+
const files = this.value ?? [];
|
|
60
|
+
if (files.length === 0) this.internals?.setFormValue(null);
|
|
61
|
+
else if (files.length === 1) this.internals?.setFormValue(files[0]);
|
|
62
|
+
else {
|
|
63
|
+
const fd = new FormData();
|
|
64
|
+
for (const file of files) fd.append(this.name ?? "", file);
|
|
65
|
+
this.internals?.setFormValue(fd);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
render() {
|
|
69
|
+
const files = this.value ?? [];
|
|
70
|
+
const hasFiles = files.length > 0;
|
|
71
|
+
const status = !hasFiles ? Locale.getValue("noFileChosen") : files.length === 1 ? files[0].name : Locale.getValue("filesSelected", { count: files.length });
|
|
72
|
+
return html`
|
|
73
|
+
<u-field part="field"
|
|
74
|
+
?required=${this.required}
|
|
75
|
+
?disabled=${this.disabled}
|
|
76
|
+
?invalid=${this.invalid}
|
|
77
|
+
.label=${this.label}
|
|
78
|
+
.description=${this.description}
|
|
79
|
+
.validationMessage=${this.validationMessage}
|
|
80
|
+
>
|
|
81
|
+
<div class="container" part="container">
|
|
82
|
+
<button class="trigger" part="trigger"
|
|
83
|
+
type="button"
|
|
84
|
+
?disabled=${this.disabled || this.readonly}
|
|
85
|
+
@click=${this.handleTriggerClick}
|
|
86
|
+
>${Locale.getValue("chooseFile")}</button>
|
|
87
|
+
|
|
88
|
+
<span class="status" part="status" ?data-empty=${!hasFiles}>${status}</span>
|
|
89
|
+
|
|
90
|
+
<u-icon class="clear-btn" part="clear-button"
|
|
91
|
+
?hidden=${!hasFiles || this.disabled || this.readonly}
|
|
92
|
+
role="button"
|
|
93
|
+
tabindex="0"
|
|
94
|
+
aria-label=${Locale.getValue("clear")}
|
|
95
|
+
lib="internal"
|
|
96
|
+
name="x"
|
|
97
|
+
@click=${this.handleClearButtonClick}
|
|
98
|
+
@keydown=${this.handleClearButtonKeydown}
|
|
99
|
+
></u-icon>
|
|
100
|
+
|
|
101
|
+
<input class="native-input" part="input"
|
|
102
|
+
type="file"
|
|
103
|
+
hidden
|
|
104
|
+
name=${ifDefined(this.name)}
|
|
105
|
+
accept=${ifDefined(this.accept)}
|
|
106
|
+
?multiple=${this.multiple}
|
|
107
|
+
?disabled=${this.disabled || this.readonly}
|
|
108
|
+
@change=${this.handleInputChange}
|
|
109
|
+
/>
|
|
110
|
+
</div>
|
|
111
|
+
</u-field>
|
|
112
|
+
`;
|
|
113
|
+
}
|
|
114
|
+
setValidity() {
|
|
115
|
+
const missing = this.required && (!this.value || this.value.length === 0);
|
|
116
|
+
this.commit(missing ? { valueMissing: true } : {}, missing ? Locale.getValue("valueMissing") : "", this.triggerEl ?? void 0);
|
|
117
|
+
}
|
|
118
|
+
reset() {
|
|
119
|
+
this.value = null;
|
|
120
|
+
this.invalid = false;
|
|
121
|
+
if (this.inputEl) this.inputEl.value = "";
|
|
122
|
+
}
|
|
123
|
+
focus(options) {
|
|
124
|
+
this.triggerEl?.focus(options);
|
|
125
|
+
}
|
|
126
|
+
blur() {
|
|
127
|
+
this.triggerEl?.blur();
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
__decorate([property({ attribute: false }), __decorateMetadata("design:type", Object)], UFileInput.prototype, "value", void 0);
|
|
131
|
+
__decorate([property({ type: String }), __decorateMetadata("design:type", String)], UFileInput.prototype, "accept", void 0);
|
|
132
|
+
__decorate([property({
|
|
133
|
+
type: Boolean,
|
|
134
|
+
reflect: true
|
|
135
|
+
}), __decorateMetadata("design:type", Boolean)], UFileInput.prototype, "multiple", void 0);
|
|
136
|
+
__decorate([query(".trigger", true), __decorateMetadata("design:type", typeof HTMLButtonElement === "undefined" ? Object : HTMLButtonElement)], UFileInput.prototype, "triggerEl", void 0);
|
|
137
|
+
__decorate([query("input[type=\"file\"]", true), __decorateMetadata("design:type", typeof HTMLInputElement === "undefined" ? Object : HTMLInputElement)], UFileInput.prototype, "inputEl", void 0);
|
|
138
|
+
UFileInput = __decorate([customElement("u-file-input")], UFileInput);
|
|
139
|
+
//#endregion
|
|
140
|
+
export { UFileInput };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const styles: import('lit').CSSResult;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { css } from "lit";
|
|
2
|
+
//#region src/components/file-input/UFileInput.styles.ts
|
|
3
|
+
var styles = css`
|
|
4
|
+
:host {
|
|
5
|
+
display: var(--u-file-input-display, inline-block);
|
|
6
|
+
width: var(--u-file-input-width, auto);
|
|
7
|
+
color: var(--u-txt-color, #212121);
|
|
8
|
+
font-size: inherit;
|
|
9
|
+
font-family: var(--u-font-base);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.container {
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: row;
|
|
15
|
+
align-items: center;
|
|
16
|
+
gap: 0.6em;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.trigger {
|
|
20
|
+
flex-shrink: 0;
|
|
21
|
+
display: inline-flex;
|
|
22
|
+
align-items: center;
|
|
23
|
+
padding: 0.4em 0.9em;
|
|
24
|
+
border: 1px solid var(--u-input-border-color, #E0E0E0);
|
|
25
|
+
border-radius: 0.25em;
|
|
26
|
+
background-color: var(--u-panel-bg-color, #FFFFFF);
|
|
27
|
+
color: var(--u-primary-color, #1976D2);
|
|
28
|
+
font: inherit;
|
|
29
|
+
font-weight: 500;
|
|
30
|
+
cursor: pointer;
|
|
31
|
+
transition: border-color var(--u-duration-normal, 220ms) var(--u-ease-standard, cubic-bezier(0.2, 0, 0, 1)), background-color var(--u-duration-normal, 220ms) var(--u-ease-standard, cubic-bezier(0.2, 0, 0, 1));
|
|
32
|
+
}
|
|
33
|
+
.trigger:hover:not(:disabled) {
|
|
34
|
+
background-color: var(--u-neutral-100, #F5F5F5);
|
|
35
|
+
border-color: var(--u-input-border-color-hover, #BDBDBD);
|
|
36
|
+
}
|
|
37
|
+
.trigger:focus-visible {
|
|
38
|
+
outline: none;
|
|
39
|
+
box-shadow: 0 0 0 1px var(--u-input-border-color-focus, #1565C0),
|
|
40
|
+
0 0 0 3px color-mix(in srgb, var(--u-primary-color-strong, #1565C0) 22%, transparent);
|
|
41
|
+
}
|
|
42
|
+
.trigger:disabled {
|
|
43
|
+
color: var(--u-txt-color-weak, #757575);
|
|
44
|
+
background-color: var(--u-bg-color-disabled, #FAFAFA);
|
|
45
|
+
border-color: var(--u-border-color-weak, #EEEEEE);
|
|
46
|
+
cursor: not-allowed;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.status {
|
|
50
|
+
flex: 1 1 auto;
|
|
51
|
+
min-width: 0;
|
|
52
|
+
overflow: hidden;
|
|
53
|
+
text-overflow: ellipsis;
|
|
54
|
+
white-space: nowrap;
|
|
55
|
+
font-size: 0.9em;
|
|
56
|
+
}
|
|
57
|
+
.status[data-empty] {
|
|
58
|
+
color: var(--u-txt-color-weak, #757575);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.clear-btn {
|
|
62
|
+
flex-shrink: 0;
|
|
63
|
+
color: var(--u-icon-color, #616161);
|
|
64
|
+
font-size: 1em;
|
|
65
|
+
cursor: pointer;
|
|
66
|
+
transition: color var(--u-duration-normal, 220ms) var(--u-ease-standard, cubic-bezier(0.2, 0, 0, 1));
|
|
67
|
+
}
|
|
68
|
+
.clear-btn:hover {
|
|
69
|
+
color: var(--u-icon-color-hover, #1565C0);
|
|
70
|
+
}
|
|
71
|
+
.clear-btn:active {
|
|
72
|
+
color: var(--u-icon-color-active, #1565C0);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
:host([invalid]) .trigger {
|
|
76
|
+
border-color: var(--u-input-border-color-invalid, #C62828);
|
|
77
|
+
}
|
|
78
|
+
`;
|
|
79
|
+
//#endregion
|
|
80
|
+
export { styles };
|
|
@@ -12,6 +12,10 @@ export type SelectVariant = 'outlined' | 'filled' | 'underlined' | 'borderless';
|
|
|
12
12
|
* 팝오버 목록에서 사라지기 때문. 리치 콘텐츠가 필요하면 이 slot에 별도 마크업을 할당하고
|
|
13
13
|
* `change` 이벤트에서 직접 갱신한다.)
|
|
14
14
|
*
|
|
15
|
+
* 옵션 목록 팝오버가 열려 있는 동안 `:state(open)` 커스텀 상태를 노출한다(via
|
|
16
|
+
* `ElementInternals.states`) — `u-select:state(open)::part(container)`로 스타일링할 수
|
|
17
|
+
* 있다. 공개 속성으로 반사되지 않는다.
|
|
18
|
+
*
|
|
15
19
|
* @csspart field - u-field 요소
|
|
16
20
|
* @csspart container - 트리거 영역을 감싸는 요소
|
|
17
21
|
* @csspart popover - 옵션 목록이 표시되는 팝오버 요소
|
|
@@ -153,6 +153,7 @@ var USelect = class USelect extends UFormControlElement {
|
|
|
153
153
|
updated(changedProperties) {
|
|
154
154
|
super.updated(changedProperties);
|
|
155
155
|
if (["value", "options"].some((k) => changedProperties.has(k))) this.onChangeValue();
|
|
156
|
+
if (changedProperties.has("open")) this.internals?.states[this.open ? "add" : "delete"]("open");
|
|
156
157
|
}
|
|
157
158
|
render() {
|
|
158
159
|
return html`
|
package/dist/index.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export * from './components/divider/UDivider.js';
|
|
|
20
20
|
export * from './components/drawer/UDrawer.js';
|
|
21
21
|
export * from './components/expander/UExpander.js';
|
|
22
22
|
export * from './components/field/UField.js';
|
|
23
|
+
export * from './components/file-input/UFileInput.js';
|
|
23
24
|
export * from './components/form/UForm.js';
|
|
24
25
|
export * from './components/icon/UIcon.js';
|
|
25
26
|
export * from './components/icon-button/UIconButton.js';
|
package/dist/index.js
CHANGED
|
@@ -32,6 +32,7 @@ import { UDialog } from "./components/dialog/UDialog.js";
|
|
|
32
32
|
import { UDivider } from "./components/divider/UDivider.js";
|
|
33
33
|
import { UDrawer } from "./components/drawer/UDrawer.js";
|
|
34
34
|
import { UExpander } from "./components/expander/UExpander.js";
|
|
35
|
+
import { UFileInput } from "./components/file-input/UFileInput.js";
|
|
35
36
|
import { UForm } from "./components/form/UForm.js";
|
|
36
37
|
import { UOption } from "./components/option/UOption.js";
|
|
37
38
|
import { UInput } from "./components/input/UInput.js";
|
|
@@ -57,4 +58,4 @@ import { BrowserStorage } from "./utilities/BrowserStorage.js";
|
|
|
57
58
|
import { Dialog } from "./utilities/Dialog.js";
|
|
58
59
|
import { Theme } from "./utilities/Theme.js";
|
|
59
60
|
import { Toast } from "./utilities/Toast.js";
|
|
60
|
-
export { BrowserStorage, Dialog, IconCache, IconRegistry, Locale, OverlayManager, Theme, Toast, UAlert, UAvatar, UBadge, UBreadcrumb, UBreadcrumbItem, UButton, UButtonGroup, UCard, UCarousel, UCheckbox, UChip, UCopyButton, UDatePicker, UDialog, UDivider, UDrawer, UElement, UExpander, UField, UFloatingElement, UForm, UFormControlElement, UIcon, UIconButton, UInput, UMenu, UMenuItem, UOption, UOverlayElement, UPanel, UPopover, UProgressBar, UProgressRing, URadio, URating, USelect, USkeleton, USlider, USpinner, USplitPanel, USwitch, UTab, UTabPanel, UTag, UText, UTextarea, UTooltip, UTree, UTreeItem, arrayAttrConverter, booleanAttrConverter, dateAttrConverter, formatCurrency, formatDate, formatNumber, getDefaultBaseUrl, getParentElement, isCoarsePointer, jsonAttrConverter, querySelectorAllWithin, querySelectorWithin, setDefaultBaseUrl, urlAttrConverter };
|
|
61
|
+
export { BrowserStorage, Dialog, IconCache, IconRegistry, Locale, OverlayManager, Theme, Toast, UAlert, UAvatar, UBadge, UBreadcrumb, UBreadcrumbItem, UButton, UButtonGroup, UCard, UCarousel, UCheckbox, UChip, UCopyButton, UDatePicker, UDialog, UDivider, UDrawer, UElement, UExpander, UField, UFileInput, UFloatingElement, UForm, UFormControlElement, UIcon, UIconButton, UInput, UMenu, UMenuItem, UOption, UOverlayElement, UPanel, UPopover, UProgressBar, UProgressRing, URadio, URating, USelect, USkeleton, USlider, USpinner, USplitPanel, USwitch, UTab, UTabPanel, UTag, UText, UTextarea, UTooltip, UTree, UTreeItem, arrayAttrConverter, booleanAttrConverter, dateAttrConverter, formatCurrency, formatDate, formatNumber, getDefaultBaseUrl, getParentElement, isCoarsePointer, jsonAttrConverter, querySelectorAllWithin, querySelectorWithin, setDefaultBaseUrl, urlAttrConverter };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { UFileInput as UFileInputElement } from '../components/file-input/UFileInput';
|
|
3
|
+
|
|
4
|
+
export declare const UFileInput: React.ForwardRefExoticComponent<
|
|
5
|
+
Omit<Partial<UFileInputElement>, keyof React.HTMLAttributes<UFileInputElement>>
|
|
6
|
+
& Omit<React.HTMLAttributes<UFileInputElement>, 'onChange'>
|
|
7
|
+
& React.RefAttributes<UFileInputElement>
|
|
8
|
+
& {
|
|
9
|
+
onChange?: (event: CustomEvent) => void;
|
|
10
|
+
}
|
|
11
|
+
>;
|
|
12
|
+
|
|
13
|
+
export type UFileInputProps = React.ComponentProps<typeof UFileInput>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { createComponent } from '@lit/react';
|
|
3
|
+
import { UFileInput as UFileInputElement } from '../components/file-input/UFileInput.js';
|
|
4
|
+
|
|
5
|
+
export const UFileInput = createComponent({
|
|
6
|
+
react: React,
|
|
7
|
+
tagName: 'u-file-input',
|
|
8
|
+
elementClass: UFileInputElement,
|
|
9
|
+
events: {
|
|
10
|
+
onChange: 'change',
|
|
11
|
+
},
|
|
12
|
+
});
|
package/dist/react/index.d.ts
CHANGED
|
@@ -25,6 +25,7 @@ export { UInput, UInputProps } from './UInput';
|
|
|
25
25
|
export { UIconButton, UIconButtonProps } from './UIconButton';
|
|
26
26
|
export { UIcon, UIconProps } from './UIcon';
|
|
27
27
|
export { UForm, UFormProps } from './UForm';
|
|
28
|
+
export { UFileInput, UFileInputProps } from './UFileInput';
|
|
28
29
|
export { UField, UFieldProps } from './UField';
|
|
29
30
|
export { UExpander, UExpanderProps } from './UExpander';
|
|
30
31
|
export { UDrawer, UDrawerProps } from './UDrawer';
|
package/dist/react/index.js
CHANGED
|
@@ -25,6 +25,7 @@ export { UInput } from './UInput.js';
|
|
|
25
25
|
export { UIconButton } from './UIconButton.js';
|
|
26
26
|
export { UIcon } from './UIcon.js';
|
|
27
27
|
export { UForm } from './UForm.js';
|
|
28
|
+
export { UFileInput } from './UFileInput.js';
|
|
28
29
|
export { UField } from './UField.js';
|
|
29
30
|
export { UExpander } from './UExpander.js';
|
|
30
31
|
export { UDrawer } from './UDrawer.js';
|
|
@@ -12,7 +12,7 @@ export type SupportedLocale = 'en' | 'ko' | 'ja' | 'zh-CN' | 'zh-TW' | 'es' | 'f
|
|
|
12
12
|
/** 내장 로케일은 자동완성되지만, 임의의 BCP47 태그도 그대로 받는다. */
|
|
13
13
|
export type LocaleTag = SupportedLocale | (string & {});
|
|
14
14
|
/** 라이브러리가 제공하는 chrome 문자열 키. 지금은 검증 메시지뿐이지만 이후 다른 UI 문구도 추가될 수 있다. */
|
|
15
|
-
export type LocaleMessageKey = 'valueMissing' | 'badInput' | 'typeMismatch' | 'patternMismatch' | 'rangeUnderflow' | 'rangeOverflow' | 'stepMismatch' | 'tooShort' | 'tooLong' | 'increment' | 'decrement' | 'clear' | 'today' | 'time' | 'showPassword' | 'hidePassword' | 'previousSlide' | 'nextSlide' | 'goToSlide' | 'chooseDate' | 'previousMonth' | 'nextMonth' | 'close' | 'copy' | 'copied';
|
|
15
|
+
export type LocaleMessageKey = 'valueMissing' | 'badInput' | 'typeMismatch' | 'patternMismatch' | 'rangeUnderflow' | 'rangeOverflow' | 'stepMismatch' | 'tooShort' | 'tooLong' | 'increment' | 'decrement' | 'clear' | 'today' | 'time' | 'showPassword' | 'hidePassword' | 'previousSlide' | 'nextSlide' | 'goToSlide' | 'chooseDate' | 'previousMonth' | 'nextMonth' | 'close' | 'copy' | 'copied' | 'chooseFile' | 'noFileChosen' | 'filesSelected';
|
|
16
16
|
type LocaleTable = Record<LocaleMessageKey, string>;
|
|
17
17
|
/**
|
|
18
18
|
* 한 패키지(또는 앱 영역)의 문자열 묶음.
|
package/package.json
CHANGED
|
@@ -67,6 +67,8 @@ import { UButton, UInput } from '@iyulab/components/react';
|
|
|
67
67
|
- [`u-input`](./references/components/input.md) — Text input; combobox mode when `u-option` children are present
|
|
68
68
|
- [`u-textarea`](./references/components/textarea.md) — Multi-line text input with auto-resize and character counter
|
|
69
69
|
- [`u-select`](./references/components/select.md) — Dropdown select; single or multiple, with search
|
|
70
|
+
- [`u-date-picker`](./references/components/date-picker.md) — Single-date(-time) selection with a popover calendar
|
|
71
|
+
- [`u-file-input`](./references/components/file-input.md) — File picker with a design-system trigger button and selected-file(s) display
|
|
70
72
|
- [`u-checkbox`](./references/components/checkbox.md) — Checkbox with indeterminate state support
|
|
71
73
|
- [`u-radio`](./references/components/radio.md) — Radio group built from `u-option` children
|
|
72
74
|
- [`u-switch`](./references/components/switch.md) — Toggle switch
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# u-file-input
|
|
2
|
+
|
|
3
|
+
```ts
|
|
4
|
+
import '@iyulab/components/dist/components/file-input/UFileInput.js';
|
|
5
|
+
```
|
|
6
|
+
|
|
7
|
+
**Tag:** `u-file-input`
|
|
8
|
+
|
|
9
|
+
File picker with a design-system-styled trigger button and a selected-file(s) status display,
|
|
10
|
+
wrapping a hidden native `<input type="file">`. Form-associated (`formAssociated = true`).
|
|
11
|
+
|
|
12
|
+
`value` is always `File[] | null` — even with `multiple` off, a selected file is held as a
|
|
13
|
+
one-element array. On form submission: 0 files → nothing is sent for `name`; 1 file → the `File`
|
|
14
|
+
itself; more than 1 file → a `FormData` with the same `File` repeatedly appended under `name`,
|
|
15
|
+
matching what a native `<input type="file" multiple>` submits.
|
|
16
|
+
|
|
17
|
+
```html
|
|
18
|
+
<u-file-input name="attachment" required accept="image/*"></u-file-input>
|
|
19
|
+
|
|
20
|
+
<u-file-input name="attachments" multiple accept=".csv,.xlsx"></u-file-input>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
const picker = document.querySelector('u-file-input');
|
|
25
|
+
picker.addEventListener('change', () => {
|
|
26
|
+
console.log(picker.value); // File[] | null
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
No drag-and-drop in this version — it is a minimal wrapper (trigger + status + clear), by design.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Properties
|
|
35
|
+
|
|
36
|
+
| Property | Type | Default | Reflect | Description |
|
|
37
|
+
|----------|------|---------|---------|-------------|
|
|
38
|
+
| `value` | `File[] \| null` | `null` | — | Selected files (always an array, even for single-file selection) |
|
|
39
|
+
| `accept` | `string` | — | — | Passed through to the native `accept` attribute |
|
|
40
|
+
| `multiple` | `boolean` | `false` | ✓ | Allow selecting more than one file |
|
|
41
|
+
| `disabled` | `boolean` | `false` | ✓ | Disable the control |
|
|
42
|
+
| `readonly` | `boolean` | `false` | ✓ | Read-only (trigger and clear button inert) |
|
|
43
|
+
| `required` | `boolean` | `false` | ✓ | Required field |
|
|
44
|
+
| `invalid` | `boolean` | `false` | ✓ | Validation failed state |
|
|
45
|
+
| `name` | `string` | — | — | Form field name |
|
|
46
|
+
| `label` | `string` | — | — | Label text |
|
|
47
|
+
| `description` | `string` | — | — | Helper text below the control |
|
|
48
|
+
| `validationMessage` | `string` | — | — | Custom validation message |
|
|
49
|
+
|
|
50
|
+
## Methods
|
|
51
|
+
|
|
52
|
+
| Method | Returns | Description |
|
|
53
|
+
|--------|---------|-------------|
|
|
54
|
+
| `validate()` | `boolean` | Run validation; sets `invalid` |
|
|
55
|
+
| `reset()` | `void` | Clear the selection and validation state |
|
|
56
|
+
| `focus(options?)` | `void` | Focus the trigger button |
|
|
57
|
+
| `blur()` | `void` | Blur the trigger button |
|
|
58
|
+
|
|
59
|
+
## CSS Parts
|
|
60
|
+
|
|
61
|
+
| Part | Description |
|
|
62
|
+
|------|-------------|
|
|
63
|
+
| `field` | Inner `u-field` element (label/description/validation layout) |
|
|
64
|
+
| `container` | Wrapper around the trigger, status text, and clear button |
|
|
65
|
+
| `trigger` | The file-picker trigger button |
|
|
66
|
+
| `status` | Status text — "No file chosen", the filename, or "N files selected" |
|
|
67
|
+
| `clear-button` | Clear-selection button (a `u-icon`) |
|
|
68
|
+
| `input` | The hidden native `<input type="file">` |
|
|
69
|
+
|
|
70
|
+
## CSS Custom Properties
|
|
71
|
+
|
|
72
|
+
| Property | Description |
|
|
73
|
+
|----------|--------------|
|
|
74
|
+
| `--u-file-input-display` | Host `display` (default: `inline-block`) |
|
|
75
|
+
| `--u-file-input-width` | Host `width` (default: `auto`) |
|
|
76
|
+
|
|
77
|
+
## Events
|
|
78
|
+
|
|
79
|
+
| Event | Description |
|
|
80
|
+
|-------|-------------|
|
|
81
|
+
| `change` | Fires when the selection changes (picking files or clearing) |
|
|
@@ -40,7 +40,7 @@ Inherits all `UFloatingElement` properties (see [floating.md](../extensions/floa
|
|
|
40
40
|
| `disabled` | `boolean` | `false` | ✓ | Disable opening |
|
|
41
41
|
| `placement` | `Placement` | — | — | Preferred placement (`top`, `bottom`, `left`, `right`, `*-start`, `*-end`) |
|
|
42
42
|
| `offset` | `OffsetOptions` | `0` | — | Distance from anchor |
|
|
43
|
-
| `shift` | `boolean` | `false` | — |
|
|
43
|
+
| `shift` | `boolean` | `false` | — | Correct the cross-axis position *within* the placed side (see note below) |
|
|
44
44
|
| `arrow` | `boolean` | `false` | — | Show arrow pointing to anchor |
|
|
45
45
|
| `showDelay` | `number` | `0` | — | Open delay in ms |
|
|
46
46
|
| `hideDelay` | `number` | `0` | — | Close delay in ms |
|
|
@@ -48,6 +48,12 @@ Inherits all `UFloatingElement` properties (see [floating.md](../extensions/floa
|
|
|
48
48
|
| `dismiss` | `string[]` | `['click','escape','scroll','resize']` | ✓ | Close triggers — see note below |
|
|
49
49
|
| `autofocus` | `boolean` | `false` | ✓ | Focus first focusable element on open |
|
|
50
50
|
|
|
51
|
+
> **Placement fallback**: whenever `placement` is set, floating-ui's `flip` middleware is
|
|
52
|
+
> always applied — if the placed side has no room, the element switches to the opposite side.
|
|
53
|
+
> `shift` is a separate, narrower correction: it nudges the element along the cross axis
|
|
54
|
+
> *within* whichever side ended up being used. Neither can be turned off independently.
|
|
55
|
+
|
|
56
|
+
|
|
51
57
|
### `dismiss` semantics
|
|
52
58
|
|
|
53
59
|
`click` and `escape` express user intent and always close the popover.
|
|
@@ -43,13 +43,19 @@ Inherits all `UFloatingElement` properties (see [floating.md](../extensions/floa
|
|
|
43
43
|
| `open` | `boolean` | `false` | ✓ | Visibility state |
|
|
44
44
|
| `placement` | `Placement` | — | — | Preferred placement |
|
|
45
45
|
| `offset` | `OffsetOptions` | `0` | — | Gap from target |
|
|
46
|
-
| `shift` | `boolean` | `false` | — |
|
|
46
|
+
| `shift` | `boolean` | `false` | — | Correct the cross-axis position *within* the placed side (see note below) |
|
|
47
47
|
| `arrow` | `boolean` | `false` | — | Show pointing arrow |
|
|
48
48
|
| `showDelay` | `number` | `0` | — | Open delay in ms |
|
|
49
49
|
| `hideDelay` | `number` | `0` | — | Close delay in ms |
|
|
50
50
|
| `interactive` | `boolean` | `false` | ✓ | Keep open when mouse is over tooltip |
|
|
51
51
|
| `tracking` | `boolean` | `false` | ✓ | Follow mouse cursor position |
|
|
52
52
|
|
|
53
|
+
> **Placement fallback**: whenever `placement` is set, floating-ui's `flip` middleware is
|
|
54
|
+
> always applied — if the placed side has no room, the element switches to the opposite side.
|
|
55
|
+
> `shift` is a separate, narrower correction: it nudges the element along the cross axis
|
|
56
|
+
> *within* whichever side ended up being used. Neither can be turned off independently.
|
|
57
|
+
|
|
58
|
+
|
|
53
59
|
## Events
|
|
54
60
|
|
|
55
61
|
| Event | Cancelable | Description |
|