@godxjp/ui 20.2.1 → 22.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/app-provider.js +2 -1
- package/dist/components/data-display/badge.d.ts +28 -1
- package/dist/components/data-display/badge.js +2 -0
- package/dist/components/data-display/chat-bubble.d.ts +30 -0
- package/dist/components/data-display/chat-bubble.js +229 -0
- package/dist/components/data-display/data-table.d.ts +18 -2
- package/dist/components/data-display/data-table.js +4 -0
- package/dist/components/data-display/descriptions.js +4 -1
- package/dist/components/data-display/index.d.ts +5 -3
- package/dist/components/data-display/index.js +5 -2
- package/dist/components/data-display/popover.d.ts +19 -1
- package/dist/components/data-display/popover.js +12 -3
- package/dist/components/data-display/progress.d.ts +37 -0
- package/dist/components/data-display/progress.js +57 -5
- package/dist/components/data-display/scroll-area.js +26 -1
- package/dist/components/data-display/tree.d.ts +8 -0
- package/dist/components/data-display/tree.js +426 -0
- package/dist/components/data-entry/chat-composer.d.ts +50 -0
- package/dist/components/data-entry/chat-composer.js +163 -0
- package/dist/components/data-entry/chat-suggestion.d.ts +28 -0
- package/dist/components/data-entry/chat-suggestion.js +285 -0
- package/dist/components/data-entry/date-picker.d.ts +10 -3
- package/dist/components/data-entry/date-picker.js +503 -238
- package/dist/components/data-entry/form-field.d.ts +1 -1
- package/dist/components/data-entry/form-field.js +4 -1
- package/dist/components/data-entry/form.js +10 -1
- package/dist/components/data-entry/index.d.ts +4 -6
- package/dist/components/data-entry/index.js +4 -6
- package/dist/components/data-entry/label.d.ts +14 -1
- package/dist/components/data-entry/label.js +6 -1
- package/dist/components/data-entry/radio.d.ts +27 -6
- package/dist/components/data-entry/radio.js +88 -43
- package/dist/components/data-entry/search-select.js +8 -1
- package/dist/components/data-entry/select.js +2 -0
- package/dist/components/data-entry/switch.d.ts +6 -3
- package/dist/components/data-entry/switch.js +34 -11
- package/dist/components/data-entry/textarea.js +3 -1
- package/dist/components/data-entry/tree-utils.d.ts +10 -48
- package/dist/components/data-entry/tree-utils.js +1 -154
- package/dist/components/layout/mobile-shell.d.ts +1 -1
- package/dist/components/layout/mobile-shell.js +2 -0
- package/dist/components/navigation/menubar.d.ts +10 -3
- package/dist/components/navigation/steps.d.ts +1 -1
- package/dist/components/navigation/steps.js +3 -1
- package/dist/components/navigation/tabs-scroll.d.ts +27 -0
- package/dist/components/navigation/tabs-scroll.js +52 -1
- package/dist/components/navigation/tabs.d.ts +2 -2
- package/dist/components/navigation/tabs.js +88 -23
- package/dist/components/ui/index.d.ts +0 -1
- package/dist/components/ui/index.js +0 -1
- package/dist/components/ui/segmented.d.ts +15 -1
- package/dist/components/ui/segmented.js +13 -6
- package/dist/i18n/messages/en.json +48 -2
- package/dist/i18n/messages/ja.json +46 -2
- package/dist/i18n/messages/vi.json +47 -2
- package/dist/lib/datetime/picker-format.d.ts +18 -0
- package/dist/lib/datetime/picker-format.js +35 -1
- package/dist/lib/field-a11y.d.ts +12 -0
- package/dist/lib/field-a11y.js +14 -1
- package/dist/lib/tree.d.ts +53 -0
- package/dist/lib/tree.js +155 -0
- package/dist/props/components/data-display.prop.d.ts +194 -1
- package/dist/props/components/data-entry.prop.d.ts +307 -99
- package/dist/props/components/index.d.ts +1 -1
- package/dist/props/components/layout.prop.d.ts +12 -1
- package/dist/props/components/navigation.prop.d.ts +76 -1
- package/dist/props/registry.d.ts +156 -19
- package/dist/props/registry.js +218 -18
- package/dist/props/vocabulary/data.prop.d.ts +12 -0
- package/dist/props/vocabulary/index.d.ts +2 -2
- package/dist/props/vocabulary/layout.prop.d.ts +11 -0
- package/dist/styles/badge-layout.css +4 -0
- package/dist/styles/card-layout.css +6 -6
- package/dist/styles/control.css +22 -6
- package/dist/styles/data-display-layout.css +385 -68
- package/dist/styles/data-entry-layout.css +64 -0
- package/dist/styles/focus-ring.css +4 -2
- package/dist/styles/navigation-layout.css +25 -0
- package/dist/styles/shell-layout.css +7 -1
- package/dist/styles/table-layout.css +34 -0
- package/dist/tokens/base.css +3 -0
- package/dist/tokens/components/chat-bubble.css +36 -0
- package/dist/tokens/components/chat-composer.css +19 -0
- package/dist/tokens/components/data-display.css +9 -6
- package/dist/tokens/components/descriptions.css +4 -0
- package/dist/tokens/components/navigation.css +6 -0
- package/dist/tokens/components/segmented.css +0 -2
- package/dist/tokens/components/shell.css +3 -0
- package/dist/tokens/components/table.css +3 -0
- package/dist/tokens/components/tree.css +27 -0
- package/dist/tokens/foundation.css +7 -0
- package/docs/COMPONENTS.md +14 -14
- package/docs/CONSUMER-RULES.md +24 -1
- package/docs/FRAME-COVERAGE-LEDGER.md +1 -1
- package/docs/FRAME-COVERAGE-REPORT.md +9 -7
- package/docs/TOKENS.md +65 -0
- package/docs/data-display/badge.tsx +37 -0
- package/docs/data-display/chat-bubble.tsx +397 -0
- package/docs/data-display/data-table/index.tsx +18 -0
- package/docs/data-display/popover.tsx +41 -1
- package/docs/data-display/progress.tsx +28 -0
- package/docs/data-display/timeline.tsx +46 -0
- package/docs/data-display/tree.tsx +394 -0
- package/docs/data-entry/calendar.tsx +2 -2
- package/docs/data-entry/chat-composer.tsx +464 -0
- package/docs/data-entry/chat-suggestion.tsx +301 -0
- package/docs/data-entry/date-picker.tsx +143 -3
- package/docs/data-entry/form/examples/invoice-form.tsx +2 -2
- package/docs/data-entry/form-field/examples/a11y-contract.tsx +3 -5
- package/docs/data-entry/form.tsx +4 -4
- package/docs/data-entry/number-input.tsx +31 -0
- package/docs/data-entry/segmented.tsx +103 -1
- package/docs/layout/mobile-shell.tsx +5 -0
- package/docs/navigation/tabs.tsx +149 -0
- package/docs/roadmap/ai-chat-components.md +207 -0
- package/docs/roadmap/antd-parity.md +155 -0
- package/docs/roadmap/badge-tag-chip-count.md +172 -0
- package/docs/roadmap/list-masonry.md +159 -0
- package/docs/roadmap/parity-audit-data-display-feedback.md +567 -0
- package/docs/roadmap/parity-audit-data-entry.md +357 -0
- package/docs/roadmap/parity-audit-layout-navigation-general.md +464 -0
- package/docs/roadmap/parity-backlog.md +78 -0
- package/docs/roadmap/tree-components.md +151 -0
- package/docs/showcase/acme-portal.tsx +7 -0
- package/docs/showcase/table-tree-rows.tsx +4 -4
- package/package.json +13 -23
- package/scripts/ui-audit.mjs +17 -4
- package/dist/components/data-display/tree-list.d.ts +0 -13
- package/dist/components/data-display/tree-list.js +0 -26
- package/dist/components/data-entry/date-range-picker.d.ts +0 -11
- package/dist/components/data-entry/date-range-picker.js +0 -349
- package/dist/components/data-entry/month-picker.d.ts +0 -10
- package/dist/components/data-entry/month-picker.js +0 -241
- package/dist/components/data-entry/month-range-picker.d.ts +0 -10
- package/dist/components/data-entry/month-range-picker.js +0 -301
- package/dist/components/ui/date-range-picker.d.ts +0 -1
- package/dist/components/ui/date-range-picker.js +0 -2
- package/docs/data-display/tree-list.tsx +0 -107
- package/docs/data-entry/date-range-picker.tsx +0 -175
- package/docs/data-entry/month-picker.tsx +0 -101
- package/docs/data-entry/month-range-picker.tsx +0 -115
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
"openGrid": "Open month grid",
|
|
133
133
|
"previousYear": "Previous year",
|
|
134
134
|
"nextYear": "Next year",
|
|
135
|
-
"placeholder": "
|
|
135
|
+
"placeholder": "Select month"
|
|
136
136
|
},
|
|
137
137
|
"branchScope": {
|
|
138
138
|
"label": "Branch scope",
|
|
@@ -162,6 +162,22 @@
|
|
|
162
162
|
"error": "Invalid",
|
|
163
163
|
"validating": "Validating",
|
|
164
164
|
"submitFailed": "Could not submit the form. Please try again."
|
|
165
|
+
},
|
|
166
|
+
"chatComposer": {
|
|
167
|
+
"label": "Message",
|
|
168
|
+
"placeholder": "Write a message",
|
|
169
|
+
"send": "Send message",
|
|
170
|
+
"cancel": "Stop generating",
|
|
171
|
+
"hintEnter": "Enter to send · Shift + Enter for a new line",
|
|
172
|
+
"hintShiftEnter": "Shift + Enter to send · Enter for a new line"
|
|
173
|
+
},
|
|
174
|
+
"chatSuggestion": {
|
|
175
|
+
"label": "Suggestions",
|
|
176
|
+
"empty": "No suggestions match",
|
|
177
|
+
"count": {
|
|
178
|
+
"one": "{count} suggestion",
|
|
179
|
+
"other": "{count} suggestions"
|
|
180
|
+
}
|
|
165
181
|
}
|
|
166
182
|
},
|
|
167
183
|
"feedback": {
|
|
@@ -289,6 +305,13 @@
|
|
|
289
305
|
"progress": {
|
|
290
306
|
"ariaLabel": "Progress",
|
|
291
307
|
"breakdownSeparator": ", "
|
|
308
|
+
},
|
|
309
|
+
"tree": {
|
|
310
|
+
"expand": "Expand",
|
|
311
|
+
"collapse": "Collapse",
|
|
312
|
+
"loading": "Loading child nodes…",
|
|
313
|
+
"empty": "Nothing to show",
|
|
314
|
+
"selected": "Selected"
|
|
292
315
|
}
|
|
293
316
|
},
|
|
294
317
|
"ui": {
|
|
@@ -324,7 +347,8 @@
|
|
|
324
347
|
"navigation": {
|
|
325
348
|
"tabs": {
|
|
326
349
|
"addTab": "Add tab",
|
|
327
|
-
"removeTab": "Close tab"
|
|
350
|
+
"removeTab": "Close tab",
|
|
351
|
+
"moreTabs": "More tabs"
|
|
328
352
|
},
|
|
329
353
|
"steps": {
|
|
330
354
|
"ariaLabel": "Progress",
|
|
@@ -536,5 +560,27 @@
|
|
|
536
560
|
"textExamples": {
|
|
537
561
|
"preservedText": "Plain text",
|
|
538
562
|
"sampleText": "First line\n Indented line\n\nLast line"
|
|
563
|
+
},
|
|
564
|
+
"chat": {
|
|
565
|
+
"bubble": {
|
|
566
|
+
"typing": "Typing…",
|
|
567
|
+
"loading": "Loading the message",
|
|
568
|
+
"you": "You",
|
|
569
|
+
"assistant": "Assistant",
|
|
570
|
+
"tone": {
|
|
571
|
+
"info": "Information",
|
|
572
|
+
"success": "Success",
|
|
573
|
+
"warning": "Warning",
|
|
574
|
+
"destructive": "Error"
|
|
575
|
+
}
|
|
576
|
+
},
|
|
577
|
+
"list": {
|
|
578
|
+
"label": "Conversation",
|
|
579
|
+
"jumpToLatest": "Jump to latest",
|
|
580
|
+
"newMessages": {
|
|
581
|
+
"one": "{count} new message",
|
|
582
|
+
"other": "{count} new messages"
|
|
583
|
+
}
|
|
584
|
+
}
|
|
539
585
|
}
|
|
540
586
|
}
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
"openGrid": "月選択を開く",
|
|
133
133
|
"previousYear": "前年へ",
|
|
134
134
|
"nextYear": "翌年へ",
|
|
135
|
-
"placeholder": "
|
|
135
|
+
"placeholder": "年月を選択"
|
|
136
136
|
},
|
|
137
137
|
"branchScope": {
|
|
138
138
|
"label": "ブランチ範囲",
|
|
@@ -159,6 +159,21 @@
|
|
|
159
159
|
"error": "入力エラー",
|
|
160
160
|
"validating": "確認中",
|
|
161
161
|
"submitFailed": "フォームを送信できませんでした。再試行してください。"
|
|
162
|
+
},
|
|
163
|
+
"chatComposer": {
|
|
164
|
+
"label": "メッセージ",
|
|
165
|
+
"placeholder": "メッセージを入力",
|
|
166
|
+
"send": "メッセージを送信",
|
|
167
|
+
"cancel": "生成を停止",
|
|
168
|
+
"hintEnter": "Enter で送信 · Shift + Enter で改行",
|
|
169
|
+
"hintShiftEnter": "Shift + Enter で送信 · Enter で改行"
|
|
170
|
+
},
|
|
171
|
+
"chatSuggestion": {
|
|
172
|
+
"label": "候補",
|
|
173
|
+
"empty": "一致する候補がありません",
|
|
174
|
+
"count": {
|
|
175
|
+
"other": "候補 {count} 件"
|
|
176
|
+
}
|
|
162
177
|
}
|
|
163
178
|
},
|
|
164
179
|
"feedback": {
|
|
@@ -283,6 +298,13 @@
|
|
|
283
298
|
"progress": {
|
|
284
299
|
"ariaLabel": "進捗",
|
|
285
300
|
"breakdownSeparator": "、"
|
|
301
|
+
},
|
|
302
|
+
"tree": {
|
|
303
|
+
"expand": "展開",
|
|
304
|
+
"collapse": "折りたたむ",
|
|
305
|
+
"loading": "子ノードを読み込み中…",
|
|
306
|
+
"empty": "表示する項目がありません",
|
|
307
|
+
"selected": "選択中"
|
|
286
308
|
}
|
|
287
309
|
},
|
|
288
310
|
"ui": {
|
|
@@ -318,7 +340,8 @@
|
|
|
318
340
|
"navigation": {
|
|
319
341
|
"tabs": {
|
|
320
342
|
"addTab": "タブを追加",
|
|
321
|
-
"removeTab": "タブを閉じる"
|
|
343
|
+
"removeTab": "タブを閉じる",
|
|
344
|
+
"moreTabs": "他のタブ"
|
|
322
345
|
},
|
|
323
346
|
"steps": {
|
|
324
347
|
"ariaLabel": "進行状況",
|
|
@@ -524,5 +547,26 @@
|
|
|
524
547
|
"textExamples": {
|
|
525
548
|
"preservedText": "プレーンテキスト",
|
|
526
549
|
"sampleText": "最初の行\n 字下げした行\n\n最後の行"
|
|
550
|
+
},
|
|
551
|
+
"chat": {
|
|
552
|
+
"bubble": {
|
|
553
|
+
"typing": "入力中",
|
|
554
|
+
"loading": "メッセージを読み込み中",
|
|
555
|
+
"you": "自分",
|
|
556
|
+
"assistant": "アシスタント",
|
|
557
|
+
"tone": {
|
|
558
|
+
"info": "情報",
|
|
559
|
+
"success": "成功",
|
|
560
|
+
"warning": "警告",
|
|
561
|
+
"destructive": "エラー"
|
|
562
|
+
}
|
|
563
|
+
},
|
|
564
|
+
"list": {
|
|
565
|
+
"label": "会話",
|
|
566
|
+
"jumpToLatest": "最新へ移動",
|
|
567
|
+
"newMessages": {
|
|
568
|
+
"other": "新着 {count} 件"
|
|
569
|
+
}
|
|
570
|
+
}
|
|
527
571
|
}
|
|
528
572
|
}
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
"openGrid": "Mở chọn tháng",
|
|
133
133
|
"previousYear": "Năm trước",
|
|
134
134
|
"nextYear": "Năm sau",
|
|
135
|
-
"placeholder": "
|
|
135
|
+
"placeholder": "Chọn tháng"
|
|
136
136
|
},
|
|
137
137
|
"branchScope": {
|
|
138
138
|
"label": "Phạm vi chi nhánh",
|
|
@@ -159,6 +159,22 @@
|
|
|
159
159
|
"error": "Không hợp lệ",
|
|
160
160
|
"validating": "Đang kiểm tra",
|
|
161
161
|
"submitFailed": "Không thể gửi biểu mẫu. Vui lòng thử lại."
|
|
162
|
+
},
|
|
163
|
+
"chatComposer": {
|
|
164
|
+
"label": "Tin nhắn",
|
|
165
|
+
"placeholder": "Nhập tin nhắn",
|
|
166
|
+
"send": "Gửi tin nhắn",
|
|
167
|
+
"cancel": "Dừng tạo câu trả lời",
|
|
168
|
+
"hintEnter": "Enter để gửi · Shift + Enter để xuống dòng",
|
|
169
|
+
"hintShiftEnter": "Shift + Enter để gửi · Enter để xuống dòng"
|
|
170
|
+
},
|
|
171
|
+
"chatSuggestion": {
|
|
172
|
+
"label": "Gợi ý",
|
|
173
|
+
"empty": "Không có gợi ý phù hợp",
|
|
174
|
+
"count": {
|
|
175
|
+
"one": "{count} gợi ý",
|
|
176
|
+
"other": "{count} gợi ý"
|
|
177
|
+
}
|
|
162
178
|
}
|
|
163
179
|
},
|
|
164
180
|
"feedback": {
|
|
@@ -283,6 +299,13 @@
|
|
|
283
299
|
"progress": {
|
|
284
300
|
"ariaLabel": "Tiến độ",
|
|
285
301
|
"breakdownSeparator": ", "
|
|
302
|
+
},
|
|
303
|
+
"tree": {
|
|
304
|
+
"expand": "Mở rộng",
|
|
305
|
+
"collapse": "Thu gọn",
|
|
306
|
+
"loading": "Đang tải nút con…",
|
|
307
|
+
"empty": "Không có gì để hiển thị",
|
|
308
|
+
"selected": "Đã chọn"
|
|
286
309
|
}
|
|
287
310
|
},
|
|
288
311
|
"ui": {
|
|
@@ -318,7 +341,8 @@
|
|
|
318
341
|
"navigation": {
|
|
319
342
|
"tabs": {
|
|
320
343
|
"addTab": "Thêm tab",
|
|
321
|
-
"removeTab": "Đóng tab"
|
|
344
|
+
"removeTab": "Đóng tab",
|
|
345
|
+
"moreTabs": "Tab khác"
|
|
322
346
|
},
|
|
323
347
|
"steps": {
|
|
324
348
|
"ariaLabel": "Tiến trình",
|
|
@@ -524,5 +548,26 @@
|
|
|
524
548
|
"textExamples": {
|
|
525
549
|
"preservedText": "Văn bản thuần",
|
|
526
550
|
"sampleText": "Dòng đầu\n Dòng thụt vào\n\nDòng cuối"
|
|
551
|
+
},
|
|
552
|
+
"chat": {
|
|
553
|
+
"bubble": {
|
|
554
|
+
"typing": "Đang nhập…",
|
|
555
|
+
"loading": "Đang tải tin nhắn",
|
|
556
|
+
"you": "Bạn",
|
|
557
|
+
"assistant": "Trợ lý",
|
|
558
|
+
"tone": {
|
|
559
|
+
"info": "Thông tin",
|
|
560
|
+
"success": "Thành công",
|
|
561
|
+
"warning": "Cảnh báo",
|
|
562
|
+
"destructive": "Lỗi"
|
|
563
|
+
}
|
|
564
|
+
},
|
|
565
|
+
"list": {
|
|
566
|
+
"label": "Cuộc trò chuyện",
|
|
567
|
+
"jumpToLatest": "Đến tin mới nhất",
|
|
568
|
+
"newMessages": {
|
|
569
|
+
"other": "{count} tin nhắn mới"
|
|
570
|
+
}
|
|
571
|
+
}
|
|
527
572
|
}
|
|
528
573
|
}
|
|
@@ -1,8 +1,26 @@
|
|
|
1
1
|
import { type Locale } from "date-fns";
|
|
2
2
|
import type { DatePickerBaseProp, PickerDateFormatProp } from "../../props/components/data-entry.prop.js";
|
|
3
|
+
/** The granularity axis a picker selects at — `DatePickerProp["picker"]`, resolved. */
|
|
4
|
+
export type DatePickerPicker = NonNullable<DatePickerBaseProp["picker"]>;
|
|
3
5
|
/** Display is independent from the canonical value submitted by the field. */
|
|
4
6
|
export declare function formatPickerDate(date: Date | undefined, format: PickerDateFormatProp | undefined, locale: string, withTime?: boolean): string;
|
|
5
7
|
export declare function parsePickerDate(text: string, format: PickerDateFormatProp | undefined, parser?: (text: string) => Date | undefined, withTime?: boolean): Date | undefined;
|
|
6
8
|
export declare function pickerDateAllowed(date: Date, min: Date | undefined, max: Date | undefined, disabled?: (date: Date) => boolean): boolean;
|
|
7
9
|
/** Normalize a reporting period with the active calendar's week convention. */
|
|
8
10
|
export declare function pickerPeriodStart(date: Date, picker: DatePickerBaseProp["picker"], locale: Pick<Locale, "options">): Date;
|
|
11
|
+
/**
|
|
12
|
+
* ISO-8601 at the precision the picker actually selects.
|
|
13
|
+
*
|
|
14
|
+
* The standard defines reduced forms, and a month picker that submits `2026/03` is simply using
|
|
15
|
+
* none of them: `/` is not an ISO separator and a server parsing an ISO date will reject it. Each
|
|
16
|
+
* granularity gets the shortest ISO form that loses nothing:
|
|
17
|
+
* year → `2026` · month → `2026-03` · quarter → `2026-01` (the quarter's first month, which is
|
|
18
|
+
* what the value IS) · week → `2026-W07` (ISO week-numbering year + week) · date → `2026-03-01`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function toIsoPeriod(date: Date | undefined, picker: DatePickerPicker): string;
|
|
21
|
+
/**
|
|
22
|
+
* The inverse of `toIsoPeriod`: read back the reduced ISO form the field displays for this
|
|
23
|
+
* granularity. `date` is left to `parsePickerDate`, which already owns the full `yyyy-MM-dd` path
|
|
24
|
+
* (custom parser, `format` pattern, era display).
|
|
25
|
+
*/
|
|
26
|
+
export declare function parseIsoPeriod(raw: string, picker: DatePickerPicker): Date | undefined;
|
|
@@ -46,9 +46,43 @@ function pickerPeriodStart(date, picker, locale) {
|
|
|
46
46
|
return date;
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
+
function toIsoPeriod(date, picker) {
|
|
50
|
+
if (!date || Number.isNaN(date.getTime())) return "";
|
|
51
|
+
switch (picker) {
|
|
52
|
+
case "year":
|
|
53
|
+
return String(date.getFullYear()).padStart(4, "0");
|
|
54
|
+
case "quarter":
|
|
55
|
+
case "month":
|
|
56
|
+
return toIsoDate(date).slice(0, 7);
|
|
57
|
+
case "week":
|
|
58
|
+
return formatDate(date, "RRRR-'W'II");
|
|
59
|
+
default:
|
|
60
|
+
return toIsoDate(date);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function parseIsoPeriod(raw, picker) {
|
|
64
|
+
const text = raw.trim();
|
|
65
|
+
if (picker === "year") {
|
|
66
|
+
const m = /^(\d{4})$/.exec(text);
|
|
67
|
+
return m ? new Date(Number(m[1]), 0, 1) : void 0;
|
|
68
|
+
}
|
|
69
|
+
if (picker === "month" || picker === "quarter") {
|
|
70
|
+
const m = /^(\d{4})-(\d{1,2})$/.exec(text);
|
|
71
|
+
if (!m) return void 0;
|
|
72
|
+
const month = Number(m[2]);
|
|
73
|
+
return month >= 1 && month <= 12 ? new Date(Number(m[1]), month - 1, 1) : void 0;
|
|
74
|
+
}
|
|
75
|
+
if (picker === "week") {
|
|
76
|
+
const parsed = parse(text, "RRRR-'W'II", new Date(2e3, 0, 1));
|
|
77
|
+
return isValid(parsed) ? parsed : void 0;
|
|
78
|
+
}
|
|
79
|
+
return void 0;
|
|
80
|
+
}
|
|
49
81
|
export {
|
|
50
82
|
formatPickerDate,
|
|
83
|
+
parseIsoPeriod,
|
|
51
84
|
parsePickerDate,
|
|
52
85
|
pickerDateAllowed,
|
|
53
|
-
pickerPeriodStart
|
|
86
|
+
pickerPeriodStart,
|
|
87
|
+
toIsoPeriod
|
|
54
88
|
};
|
package/dist/lib/field-a11y.d.ts
CHANGED
|
@@ -51,6 +51,18 @@ export interface FieldNameContextValue {
|
|
|
51
51
|
/** The label's plain-text content, when it is a string (belt-and-suspenders `aria-label`). */
|
|
52
52
|
label?: string;
|
|
53
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Write attributes onto the real `<input>` react-aria hides inside a Checkbox / Radio / Switch.
|
|
56
|
+
*
|
|
57
|
+
* react-aria renders the painted control as a `<label>` and the FOCUS TARGET as a visually hidden
|
|
58
|
+
* `<input>` inside it — then strips every `data-*` off that input (`removeDataAttributes`) and
|
|
59
|
+
* forwards only the aria attributes its own props model knows about. So the two contracts this
|
|
60
|
+
* library puts on the semantic focus target — `data-field` (gh#337, the machine key automation
|
|
61
|
+
* addresses) and the aria state a control has no react-aria prop for — have to be written by hand.
|
|
62
|
+
*
|
|
63
|
+
* Deliberately NOT left on the root as well: one field must resolve to exactly one node.
|
|
64
|
+
*/
|
|
65
|
+
export declare function useMirroredInputAttributes(inputRef: React.RefObject<HTMLInputElement | null>, attributes: Record<string, string | undefined>): void;
|
|
54
66
|
export declare const FieldNameContext: React.Context<FieldNameContextValue | null>;
|
|
55
67
|
/**
|
|
56
68
|
* Resolve the last-resort accessible name for a control's semantic focus target (see
|
package/dist/lib/field-a11y.js
CHANGED
|
@@ -37,6 +37,18 @@ function resolveFieldA11y(props, intrinsicAriaLabel) {
|
|
|
37
37
|
}
|
|
38
38
|
return picked;
|
|
39
39
|
}
|
|
40
|
+
function useMirroredInputAttributes(inputRef, attributes) {
|
|
41
|
+
const key = JSON.stringify(attributes);
|
|
42
|
+
React.useLayoutEffect(() => {
|
|
43
|
+
const input = inputRef.current;
|
|
44
|
+
if (!input) return;
|
|
45
|
+
const entries = JSON.parse(key);
|
|
46
|
+
for (const [name, value] of Object.entries(entries)) {
|
|
47
|
+
if (value === void 0) input.removeAttribute(name);
|
|
48
|
+
else input.setAttribute(name, value);
|
|
49
|
+
}
|
|
50
|
+
}, [inputRef, key]);
|
|
51
|
+
}
|
|
40
52
|
const FieldNameContext = React.createContext(null);
|
|
41
53
|
function useFieldNameFallback(name) {
|
|
42
54
|
const field = React.useContext(FieldNameContext);
|
|
@@ -75,5 +87,6 @@ export {
|
|
|
75
87
|
pickGroupFieldA11y,
|
|
76
88
|
resolveFieldA11y,
|
|
77
89
|
useFieldIdentity,
|
|
78
|
-
useFieldNameFallback
|
|
90
|
+
useFieldNameFallback,
|
|
91
|
+
useMirroredInputAttributes
|
|
79
92
|
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
/** Normalized tree node — the conventional `treeData` / Cascader `options` shape. */
|
|
3
|
+
export type TreeOption = {
|
|
4
|
+
value: string;
|
|
5
|
+
label: React.ReactNode;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
disableCheckbox?: boolean;
|
|
8
|
+
/** When false with `loadData`, shows expand affordance */
|
|
9
|
+
isLeaf?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Per-node glyph, drawn by `Tree` when `showIcon` is on. Carried through the normalizer so the
|
|
12
|
+
* page tree and the dropdown tree keep ONE node shape; `TreeSelect` simply never reads it.
|
|
13
|
+
*/
|
|
14
|
+
icon?: React.ReactNode;
|
|
15
|
+
children?: TreeOption[];
|
|
16
|
+
};
|
|
17
|
+
export type TreeFieldNames = {
|
|
18
|
+
label?: string;
|
|
19
|
+
value?: string;
|
|
20
|
+
children?: string;
|
|
21
|
+
};
|
|
22
|
+
export type NormalizedTreeOption = TreeOption & {
|
|
23
|
+
children?: NormalizedTreeOption[];
|
|
24
|
+
};
|
|
25
|
+
type RawTreeNode = Record<string, unknown>;
|
|
26
|
+
export declare function reactNodeText(value: React.ReactNode): string;
|
|
27
|
+
export declare function normalizeTreeOptions(nodes: RawTreeNode[] | undefined, fieldNames?: TreeFieldNames): NormalizedTreeOption[];
|
|
28
|
+
export declare function getNodeByPath(options: NormalizedTreeOption[], path: string[]): NormalizedTreeOption[];
|
|
29
|
+
export declare function getOptionsAtPath(options: NormalizedTreeOption[], path: string[]): NormalizedTreeOption[];
|
|
30
|
+
export declare function formatPathLabels(chain: NormalizedTreeOption[], separator?: string): string;
|
|
31
|
+
export type TreePath = {
|
|
32
|
+
path: string[];
|
|
33
|
+
labels: string[];
|
|
34
|
+
};
|
|
35
|
+
export declare function collectLeafPaths(options: NormalizedTreeOption[], prefix?: string[], root?: NormalizedTreeOption[]): TreePath[];
|
|
36
|
+
export declare function collectAllPaths(options: NormalizedTreeOption[], prefix?: string[], root?: NormalizedTreeOption[]): TreePath[];
|
|
37
|
+
export declare function pathKey(path: string[]): string;
|
|
38
|
+
export declare function pathsEqual(a: string[], b: string[]): boolean;
|
|
39
|
+
export declare function filterTreeOptions(options: NormalizedTreeOption[], query: string, filter?: (query: string, path: NormalizedTreeOption[]) => boolean): TreePath[];
|
|
40
|
+
export declare function getDescendantValues(node: NormalizedTreeOption): string[];
|
|
41
|
+
export declare function flattenVisibleTree(options: NormalizedTreeOption[], expandedKeys: Set<string>, depth?: number): {
|
|
42
|
+
node: NormalizedTreeOption;
|
|
43
|
+
depth: number;
|
|
44
|
+
hasChildren: boolean;
|
|
45
|
+
}[];
|
|
46
|
+
export declare function filterVisibleTree(options: NormalizedTreeOption[], query: string): {
|
|
47
|
+
node: NormalizedTreeOption;
|
|
48
|
+
depth: number;
|
|
49
|
+
hasChildren: boolean;
|
|
50
|
+
}[];
|
|
51
|
+
export declare function collectAllExpandableKeys(options: NormalizedTreeOption[]): string[];
|
|
52
|
+
export declare function findNodeByValue(options: NormalizedTreeOption[], value: string): NormalizedTreeOption | undefined;
|
|
53
|
+
export {};
|
package/dist/lib/tree.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
function reactNodeText(value) {
|
|
2
|
+
if (value == null || typeof value === "boolean") return "";
|
|
3
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "bigint") {
|
|
4
|
+
return String(value);
|
|
5
|
+
}
|
|
6
|
+
if (Array.isArray(value)) {
|
|
7
|
+
return value.map((item) => reactNodeText(item)).join("");
|
|
8
|
+
}
|
|
9
|
+
return "";
|
|
10
|
+
}
|
|
11
|
+
function unknownText(value) {
|
|
12
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "bigint") {
|
|
13
|
+
return String(value);
|
|
14
|
+
}
|
|
15
|
+
return "";
|
|
16
|
+
}
|
|
17
|
+
function normalizeTreeOptions(nodes, fieldNames) {
|
|
18
|
+
if (!nodes?.length) return [];
|
|
19
|
+
const labelKey = fieldNames?.label ?? "label";
|
|
20
|
+
const valueKey = fieldNames?.value ?? "value";
|
|
21
|
+
const childrenKey = fieldNames?.children ?? "children";
|
|
22
|
+
return nodes.map((node) => {
|
|
23
|
+
const children = node[childrenKey];
|
|
24
|
+
const value = unknownText(node[valueKey]);
|
|
25
|
+
const label = node[labelKey];
|
|
26
|
+
return {
|
|
27
|
+
value,
|
|
28
|
+
label: label ?? value,
|
|
29
|
+
disabled: Boolean(node.disabled),
|
|
30
|
+
disableCheckbox: Boolean(node.disableCheckbox),
|
|
31
|
+
isLeaf: node.isLeaf,
|
|
32
|
+
icon: node.icon,
|
|
33
|
+
children: Array.isArray(children) ? normalizeTreeOptions(children, fieldNames) : void 0
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function getNodeByPath(options, path) {
|
|
38
|
+
const chain = [];
|
|
39
|
+
let level = options;
|
|
40
|
+
for (const segment of path) {
|
|
41
|
+
const found = level.find((n) => n.value === segment);
|
|
42
|
+
if (!found) break;
|
|
43
|
+
chain.push(found);
|
|
44
|
+
level = found.children ?? [];
|
|
45
|
+
}
|
|
46
|
+
return chain;
|
|
47
|
+
}
|
|
48
|
+
function getOptionsAtPath(options, path) {
|
|
49
|
+
if (!path.length) return options;
|
|
50
|
+
const chain = getNodeByPath(options, path);
|
|
51
|
+
return chain.at(-1)?.children ?? [];
|
|
52
|
+
}
|
|
53
|
+
function formatPathLabels(chain, separator = " / ") {
|
|
54
|
+
return chain.map((n) => reactNodeText(n.label)).join(separator);
|
|
55
|
+
}
|
|
56
|
+
function collectLeafPaths(options, prefix = [], root = options) {
|
|
57
|
+
const out = [];
|
|
58
|
+
for (const node of options) {
|
|
59
|
+
const path = [...prefix, node.value];
|
|
60
|
+
const hasChildren = (node.children?.length ?? 0) > 0;
|
|
61
|
+
if (!hasChildren || node.isLeaf === true) {
|
|
62
|
+
out.push({ path, labels: getNodeByPath(root, path).map((n) => reactNodeText(n.label)) });
|
|
63
|
+
}
|
|
64
|
+
if (hasChildren) out.push(...collectLeafPaths(node.children, path, root));
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
function collectAllPaths(options, prefix = [], root = options) {
|
|
69
|
+
const out = [];
|
|
70
|
+
for (const node of options) {
|
|
71
|
+
const path = [...prefix, node.value];
|
|
72
|
+
out.push({ path, labels: getNodeByPath(root, path).map((n) => reactNodeText(n.label)) });
|
|
73
|
+
if (node.children?.length) out.push(...collectAllPaths(node.children, path, root));
|
|
74
|
+
}
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
function pathKey(path) {
|
|
78
|
+
return path.join("\0");
|
|
79
|
+
}
|
|
80
|
+
function pathsEqual(a, b) {
|
|
81
|
+
return a.length === b.length && a.every((v, i) => v === b[i]);
|
|
82
|
+
}
|
|
83
|
+
function filterTreeOptions(options, query, filter) {
|
|
84
|
+
const q = query.trim().toLowerCase();
|
|
85
|
+
if (!q) return [];
|
|
86
|
+
const paths = collectLeafPaths(options);
|
|
87
|
+
return paths.filter(({ path }) => {
|
|
88
|
+
const chain = getNodeByPath(options, path);
|
|
89
|
+
if (filter) return filter(query, chain);
|
|
90
|
+
return chain.some((n) => reactNodeText(n.label).toLowerCase().includes(q));
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
function getDescendantValues(node) {
|
|
94
|
+
const values = [node.value];
|
|
95
|
+
for (const child of node.children ?? []) values.push(...getDescendantValues(child));
|
|
96
|
+
return values;
|
|
97
|
+
}
|
|
98
|
+
function flattenVisibleTree(options, expandedKeys, depth = 0) {
|
|
99
|
+
const out = [];
|
|
100
|
+
for (const node of options) {
|
|
101
|
+
const hasChildren = (node.children?.length ?? 0) > 0 && node.isLeaf !== true;
|
|
102
|
+
out.push({ node, depth, hasChildren });
|
|
103
|
+
if (hasChildren && expandedKeys.has(node.value)) {
|
|
104
|
+
out.push(...flattenVisibleTree(node.children, expandedKeys, depth + 1));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
function filterVisibleTree(options, query) {
|
|
110
|
+
const q = query.trim().toLowerCase();
|
|
111
|
+
if (!q) return flattenVisibleTree(options, new Set(collectAllExpandableKeys(options)));
|
|
112
|
+
function matches(nodes, depth) {
|
|
113
|
+
return nodes.flatMap((node) => {
|
|
114
|
+
const children = node.isLeaf ? [] : matches(node.children ?? [], depth + 1);
|
|
115
|
+
if (!reactNodeText(node.label).toLowerCase().includes(q) && children.length === 0) return [];
|
|
116
|
+
return [{ node, depth, hasChildren: children.length > 0 }, ...children];
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return matches(options, 0);
|
|
120
|
+
}
|
|
121
|
+
function collectAllExpandableKeys(options) {
|
|
122
|
+
const keys = [];
|
|
123
|
+
for (const node of options) {
|
|
124
|
+
if ((node.children?.length ?? 0) > 0 && node.isLeaf !== true) {
|
|
125
|
+
keys.push(node.value);
|
|
126
|
+
keys.push(...collectAllExpandableKeys(node.children));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return keys;
|
|
130
|
+
}
|
|
131
|
+
function findNodeByValue(options, value) {
|
|
132
|
+
for (const node of options) {
|
|
133
|
+
if (node.value === value) return node;
|
|
134
|
+
const nested = node.children ? findNodeByValue(node.children, value) : void 0;
|
|
135
|
+
if (nested) return nested;
|
|
136
|
+
}
|
|
137
|
+
return void 0;
|
|
138
|
+
}
|
|
139
|
+
export {
|
|
140
|
+
collectAllExpandableKeys,
|
|
141
|
+
collectAllPaths,
|
|
142
|
+
collectLeafPaths,
|
|
143
|
+
filterTreeOptions,
|
|
144
|
+
filterVisibleTree,
|
|
145
|
+
findNodeByValue,
|
|
146
|
+
flattenVisibleTree,
|
|
147
|
+
formatPathLabels,
|
|
148
|
+
getDescendantValues,
|
|
149
|
+
getNodeByPath,
|
|
150
|
+
getOptionsAtPath,
|
|
151
|
+
normalizeTreeOptions,
|
|
152
|
+
pathKey,
|
|
153
|
+
pathsEqual,
|
|
154
|
+
reactNodeText
|
|
155
|
+
};
|