@iyulab/components 1.35.6 → 1.36.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 +26 -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/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/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/utilities/dialog.md +13 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.36.0] - 2026-09-04
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **`UFileInput` (`u-file-input`), a form control wrapping a native
|
|
8
|
+
`<input type="file">`.** Trigger button, selected-file(s) status text,
|
|
9
|
+
and a clear button, styled to match the rest of the form control
|
|
10
|
+
family. `value` is `File[] | null`; native `<form>`/`u-form`
|
|
11
|
+
submission works out of the box via `ElementInternals` (single file)
|
|
12
|
+
or a `FormData` with the file repeatedly appended under the same key
|
|
13
|
+
(multiple files, matching native multi-file submission semantics).
|
|
14
|
+
`accept`/`multiple` pass through to the underlying input.
|
|
15
|
+
Drag-and-drop is out of scope for this release.
|
|
16
|
+
|
|
17
|
+
## [1.35.7] - 2026-09-03
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **`DialogOptions`/`PromptDialogOptions` skill-doc reference had drifted substantially.**
|
|
22
|
+
`DialogOptions` listed a `closable` field that has never existed and was missing five real
|
|
23
|
+
fields (`offset`/`modal`/`buttonClose`/`escapeClose`/`backdropClose`). `PromptDialogOptions`
|
|
24
|
+
claimed `extends DialogOptions` (source: `extends ConfirmDialogOptions`) and a `default`
|
|
25
|
+
field that doesn't exist — the real field is `defaultValue` — while `type` was missing
|
|
26
|
+
entirely. Fixed the reference and the usage example. Found by a new internal tool
|
|
27
|
+
(`type-doc-check.js`) that diffs hand-copied TS interface doc snippets against source.
|
|
28
|
+
|
|
3
29
|
## [1.35.6] - 2026-09-03
|
|
4
30
|
|
|
5
31
|
### 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 };
|
|
@@ -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 };
|
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) |
|
|
@@ -17,7 +17,7 @@ const confirmed = await Dialog.confirm('Delete this item?');
|
|
|
17
17
|
if (confirmed) deleteItem();
|
|
18
18
|
|
|
19
19
|
// Prompt — returns entered string or null if cancelled
|
|
20
|
-
const name = await Dialog.prompt('Enter your name:', {
|
|
20
|
+
const name = await Dialog.prompt('Enter your name:', { defaultValue: 'Alice' });
|
|
21
21
|
|
|
22
22
|
// Custom dialog with action buttons
|
|
23
23
|
const result = await Dialog.show({
|
|
@@ -44,26 +44,29 @@ const result = await Dialog.show({
|
|
|
44
44
|
|
|
45
45
|
```ts
|
|
46
46
|
interface DialogOptions {
|
|
47
|
-
target?: HTMLElement;
|
|
47
|
+
target?: HTMLElement; // shows contained instead of as an overlay when set
|
|
48
48
|
title?: string;
|
|
49
|
-
closable?: boolean;
|
|
50
49
|
placement?: DialogPlacement;
|
|
50
|
+
offset?: number; // gap from the edge, px
|
|
51
|
+
modal?: boolean; // default: true
|
|
52
|
+
buttonClose?: boolean; // show an explicit close button — default: false
|
|
53
|
+
escapeClose?: boolean; // close on Esc — default: true
|
|
54
|
+
backdropClose?: boolean; // close on backdrop click — default: true
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
interface ConfirmDialogOptions extends DialogOptions {
|
|
54
|
-
confirmLabel?: string;
|
|
55
|
-
cancelLabel?: string;
|
|
58
|
+
confirmLabel?: string; // default: 'Confirm'
|
|
59
|
+
cancelLabel?: string; // default: 'Cancel'
|
|
56
60
|
}
|
|
57
61
|
|
|
58
|
-
interface PromptDialogOptions extends
|
|
59
|
-
|
|
62
|
+
interface PromptDialogOptions extends ConfirmDialogOptions {
|
|
63
|
+
type?: InputType; // input field type — default: 'text'
|
|
64
|
+
defaultValue?: string;
|
|
60
65
|
placeholder?: string;
|
|
61
|
-
confirmLabel?: string;
|
|
62
|
-
cancelLabel?: string;
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
interface CustomDialogOptions extends DialogOptions {
|
|
66
|
-
content
|
|
69
|
+
content: string | TemplateResult;
|
|
67
70
|
actions?: DialogAction[];
|
|
68
71
|
}
|
|
69
72
|
|