@chenzy-design/svelte 0.1.0 → 0.2.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/components.json +1 -1
- package/package.json +7 -8
- package/src/_floating/use-floating.ts +0 -208
- package/src/anchor/Anchor.a11y.test.ts +0 -49
- package/src/anchor/Anchor.kbd.test.ts +0 -70
- package/src/anchor/Anchor.svelte +0 -359
- package/src/anchor/AnchorKbdFixture.svelte +0 -24
- package/src/anchor/index.ts +0 -3
- package/src/anchor/meta.ts +0 -70
- package/src/anchor/types.ts +0 -8
- package/src/autocomplete/AutoComplete.a11y.test.ts +0 -49
- package/src/autocomplete/AutoComplete.kbd.test.ts +0 -69
- package/src/autocomplete/AutoComplete.svelte +0 -706
- package/src/autocomplete/AutoCompleteKbdFixture.svelte +0 -24
- package/src/autocomplete/index.ts +0 -2
- package/src/autocomplete/meta.ts +0 -41
- package/src/avatar/Avatar.a11y.test.ts +0 -37
- package/src/avatar/Avatar.svelte +0 -315
- package/src/avatar/AvatarGroup.svelte +0 -142
- package/src/avatar/context.ts +0 -21
- package/src/avatar/index.ts +0 -10
- package/src/avatar/meta.ts +0 -73
- package/src/back-top/BackTop.a11y.test.ts +0 -40
- package/src/back-top/BackTop.svelte +0 -314
- package/src/back-top/index.ts +0 -2
- package/src/back-top/meta.ts +0 -108
- package/src/badge/Badge.a11y.test.ts +0 -35
- package/src/badge/Badge.svelte +0 -328
- package/src/badge/BadgeA11yFixture.svelte +0 -18
- package/src/badge/index.ts +0 -2
- package/src/badge/meta.ts +0 -62
- package/src/banner/Banner.a11y.test.ts +0 -48
- package/src/banner/Banner.svelte +0 -368
- package/src/banner/index.ts +0 -3
- package/src/banner/meta.ts +0 -121
- package/src/breadcrumb/Breadcrumb.a11y.test.ts +0 -35
- package/src/breadcrumb/Breadcrumb.kbd.test.ts +0 -45
- package/src/breadcrumb/Breadcrumb.svelte +0 -414
- package/src/breadcrumb/BreadcrumbKbdFixture.svelte +0 -23
- package/src/breadcrumb/Item.svelte +0 -77
- package/src/breadcrumb/context.ts +0 -27
- package/src/breadcrumb/index.ts +0 -21
- package/src/breadcrumb/meta.ts +0 -128
- package/src/breadcrumb/types.ts +0 -4
- package/src/button/Button.a11y.test.ts +0 -54
- package/src/button/Button.svelte +0 -176
- package/src/button/index.ts +0 -2
- package/src/button/meta.ts +0 -28
- package/src/calendar/Calendar.a11y.test.ts +0 -51
- package/src/calendar/Calendar.kbd.test.ts +0 -59
- package/src/calendar/Calendar.svelte +0 -1344
- package/src/calendar/CalendarKbdFixture.svelte +0 -23
- package/src/calendar/index.ts +0 -3
- package/src/calendar/meta.ts +0 -117
- package/src/card/Card.a11y.test.ts +0 -38
- package/src/card/Card.svelte +0 -341
- package/src/card/CardMeta.svelte +0 -60
- package/src/card/index.ts +0 -8
- package/src/card/meta.ts +0 -83
- package/src/carousel/Carousel.a11y.test.ts +0 -53
- package/src/carousel/Carousel.kbd.test.ts +0 -67
- package/src/carousel/Carousel.svelte +0 -806
- package/src/carousel/CarouselA11yFixture.svelte +0 -25
- package/src/carousel/CarouselKbdFixture.svelte +0 -24
- package/src/carousel/index.ts +0 -2
- package/src/carousel/meta.ts +0 -74
- package/src/cascader/Cascader.a11y.test.ts +0 -59
- package/src/cascader/Cascader.kbd.test.ts +0 -56
- package/src/cascader/Cascader.svelte +0 -1570
- package/src/cascader/CascaderKbdFixture.svelte +0 -31
- package/src/cascader/index.ts +0 -3
- package/src/cascader/meta.ts +0 -57
- package/src/cascader/types.ts +0 -9
- package/src/checkbox/Checkbox.svelte +0 -281
- package/src/checkbox/CheckboxGroup.a11y.test.ts +0 -43
- package/src/checkbox/CheckboxGroup.kbd.test.ts +0 -40
- package/src/checkbox/CheckboxGroup.svelte +0 -122
- package/src/checkbox/CheckboxGroupKbdFixture.svelte +0 -20
- package/src/checkbox/context.ts +0 -31
- package/src/checkbox/index.ts +0 -4
- package/src/checkbox/meta.ts +0 -67
- package/src/collapse/Collapse.a11y.test.ts +0 -53
- package/src/collapse/Collapse.kbd.test.ts +0 -89
- package/src/collapse/Collapse.svelte +0 -406
- package/src/collapse/CollapseA11yFixture.svelte +0 -27
- package/src/collapse/CollapseKbdFixture.svelte +0 -25
- package/src/collapse/CollapsePanel.svelte +0 -102
- package/src/collapse/context.ts +0 -54
- package/src/collapse/index.ts +0 -17
- package/src/collapse/meta.ts +0 -79
- package/src/collapse/types.ts +0 -5
- package/src/color-picker/ColorPicker.a11y.test.ts +0 -60
- package/src/color-picker/ColorPicker.kbd.test.ts +0 -73
- package/src/color-picker/ColorPicker.svelte +0 -985
- package/src/color-picker/ColorPickerKbdFixture.svelte +0 -25
- package/src/color-picker/index.ts +0 -2
- package/src/color-picker/meta.ts +0 -47
- package/src/config-provider/ConfigProvider.svelte +0 -227
- package/src/config-provider/context.ts +0 -29
- package/src/config-provider/index.ts +0 -48
- package/src/config-provider/meta.ts +0 -128
- package/src/date-picker/DatePicker.a11y.test.ts +0 -45
- package/src/date-picker/DatePicker.kbd.test.ts +0 -71
- package/src/date-picker/DatePicker.svelte +0 -1798
- package/src/date-picker/DatePickerKbdFixture.svelte +0 -26
- package/src/date-picker/RangePicker.kbd.test.ts +0 -69
- package/src/date-picker/RangePicker.svelte +0 -775
- package/src/date-picker/RangePickerKbdFixture.svelte +0 -30
- package/src/date-picker/index.ts +0 -4
- package/src/date-picker/meta.ts +0 -67
- package/src/date-picker/range-meta.ts +0 -44
- package/src/descriptions/Descriptions.a11y.test.ts +0 -33
- package/src/descriptions/Descriptions.svelte +0 -287
- package/src/descriptions/DescriptionsItem.svelte +0 -43
- package/src/descriptions/context.ts +0 -21
- package/src/descriptions/index.ts +0 -16
- package/src/descriptions/meta.ts +0 -45
- package/src/descriptions/types.ts +0 -6
- package/src/divider/Divider.a11y.test.ts +0 -23
- package/src/divider/Divider.svelte +0 -154
- package/src/divider/index.ts +0 -2
- package/src/divider/meta.ts +0 -23
- package/src/drawer/Drawer.a11y.test.ts +0 -41
- package/src/drawer/Drawer.kbd.test.ts +0 -67
- package/src/drawer/Drawer.svelte +0 -554
- package/src/drawer/DrawerKbdFixture.svelte +0 -28
- package/src/drawer/index.ts +0 -2
- package/src/drawer/meta.ts +0 -127
- package/src/dropdown/Dropdown.a11y.test.ts +0 -46
- package/src/dropdown/Dropdown.kbd.test.ts +0 -56
- package/src/dropdown/Dropdown.svelte +0 -573
- package/src/dropdown/DropdownA11yFixture.svelte +0 -24
- package/src/dropdown/DropdownDivider.svelte +0 -17
- package/src/dropdown/DropdownItem.svelte +0 -127
- package/src/dropdown/DropdownItemNode.svelte +0 -355
- package/src/dropdown/DropdownKbdFixture.svelte +0 -26
- package/src/dropdown/DropdownMenu.svelte +0 -24
- package/src/dropdown/DropdownSubMenu.svelte +0 -212
- package/src/dropdown/DropdownTitle.svelte +0 -34
- package/src/dropdown/context.ts +0 -13
- package/src/dropdown/index.ts +0 -36
- package/src/dropdown/meta.ts +0 -97
- package/src/dropdown/types.test.ts +0 -50
- package/src/dropdown/types.ts +0 -67
- package/src/empty/Empty.a11y.test.ts +0 -24
- package/src/empty/Empty.svelte +0 -304
- package/src/empty/index.ts +0 -2
- package/src/empty/meta.ts +0 -41
- package/src/form/Field.svelte +0 -492
- package/src/form/FieldArray.svelte +0 -75
- package/src/form/Form.a11y.test.ts +0 -39
- package/src/form/Form.svelte +0 -243
- package/src/form/FormA11yFixture.svelte +0 -31
- package/src/form/FormCascader.svelte +0 -82
- package/src/form/FormCheckbox.svelte +0 -83
- package/src/form/FormDatePicker.svelte +0 -80
- package/src/form/FormErrorMessage.svelte +0 -44
- package/src/form/FormInput.svelte +0 -95
- package/src/form/FormLabel.svelte +0 -46
- package/src/form/FormRadio.svelte +0 -83
- package/src/form/FormRating.svelte +0 -85
- package/src/form/FormSection.svelte +0 -45
- package/src/form/FormSelect.svelte +0 -96
- package/src/form/FormSlider.svelte +0 -95
- package/src/form/FormSlot.svelte +0 -51
- package/src/form/FormSwitch.svelte +0 -82
- package/src/form/FormTagInput.svelte +0 -79
- package/src/form/FormTreeSelect.svelte +0 -85
- package/src/form/FormUpload.svelte +0 -92
- package/src/form/context.ts +0 -47
- package/src/form/index.ts +0 -22
- package/src/form/meta.ts +0 -85
- package/src/grid/Col.svelte +0 -142
- package/src/grid/Grid.a11y.test.ts +0 -14
- package/src/grid/GridA11yFixture.svelte +0 -12
- package/src/grid/Row.svelte +0 -116
- package/src/grid/index.ts +0 -3
- package/src/grid/meta.ts +0 -72
- package/src/grid/use-breakpoint.svelte.ts +0 -48
- package/src/highlight/Highlight.a11y.test.ts +0 -25
- package/src/highlight/Highlight.svelte +0 -99
- package/src/highlight/index.ts +0 -2
- package/src/highlight/meta.ts +0 -80
- package/src/icon/Icon.svelte +0 -156
- package/src/icon/index.ts +0 -2
- package/src/icon/meta.ts +0 -41
- package/src/image/Image.a11y.test.ts +0 -30
- package/src/image/Image.svelte +0 -387
- package/src/image/ImagePreview.kbd.test.ts +0 -52
- package/src/image/ImagePreview.svelte +0 -309
- package/src/image/ImagePreviewGroup.svelte +0 -93
- package/src/image/ImagePreviewKbdFixture.svelte +0 -32
- package/src/image/context.ts +0 -26
- package/src/image/index.ts +0 -15
- package/src/image/meta.ts +0 -131
- package/src/index.ts +0 -208
- package/src/input/Input.a11y.test.ts +0 -58
- package/src/input/Input.svelte +0 -505
- package/src/input/index.ts +0 -2
- package/src/input/meta.ts +0 -40
- package/src/input-number/InputNumber.a11y.test.ts +0 -43
- package/src/input-number/InputNumber.kbd.test.ts +0 -41
- package/src/input-number/InputNumber.svelte +0 -674
- package/src/input-number/InputNumberKbdFixture.svelte +0 -15
- package/src/input-number/index.ts +0 -2
- package/src/input-number/meta.ts +0 -65
- package/src/layout/Content.svelte +0 -37
- package/src/layout/Footer.svelte +0 -49
- package/src/layout/Header.svelte +0 -46
- package/src/layout/Layout.a11y.test.ts +0 -34
- package/src/layout/Layout.svelte +0 -60
- package/src/layout/LayoutA11yFixture.svelte +0 -21
- package/src/layout/Sider.svelte +0 -207
- package/src/layout/index.ts +0 -26
- package/src/layout/meta.ts +0 -116
- package/src/list/List.a11y.test.ts +0 -69
- package/src/list/List.bench.ts +0 -26
- package/src/list/List.kbd.test.ts +0 -71
- package/src/list/List.svelte +0 -920
- package/src/list/ListA11yFixture.svelte +0 -38
- package/src/list/ListBenchFixture.svelte +0 -22
- package/src/list/ListItem.svelte +0 -175
- package/src/list/ListKbdFixture.svelte +0 -31
- package/src/list/ListMeta.svelte +0 -73
- package/src/list/context.ts +0 -39
- package/src/list/index.ts +0 -21
- package/src/list/meta.ts +0 -99
- package/src/locale-provider/LocaleProvider.svelte +0 -110
- package/src/locale-provider/context.ts +0 -22
- package/src/locale-provider/index.ts +0 -21
- package/src/locale-provider/meta.ts +0 -93
- package/src/locale-provider/use-locale.ts +0 -25
- package/src/lottie-icon/LottieIcon.svelte +0 -330
- package/src/lottie-icon/index.ts +0 -9
- package/src/lottie-icon/meta.ts +0 -162
- package/src/menu/Menu.a11y.test.ts +0 -54
- package/src/menu/Menu.kbd.test.ts +0 -69
- package/src/menu/Menu.svelte +0 -762
- package/src/menu/MenuKbdFixture.svelte +0 -22
- package/src/menu/MenuPopupNode.svelte +0 -525
- package/src/menu/index.ts +0 -4
- package/src/menu/meta.ts +0 -137
- package/src/menu/types.ts +0 -120
- package/src/meta.test.ts +0 -173
- package/src/modal/ConfirmModal.svelte +0 -173
- package/src/modal/Modal.a11y.test.ts +0 -46
- package/src/modal/Modal.kbd.test.ts +0 -65
- package/src/modal/Modal.svelte +0 -511
- package/src/modal/ModalKbdFixture.svelte +0 -24
- package/src/modal/command.svelte.ts +0 -103
- package/src/modal/index.ts +0 -30
- package/src/modal/meta.ts +0 -165
- package/src/modal/z-stack.ts +0 -26
- package/src/notification/NotificationContainer.svelte +0 -192
- package/src/notification/NotificationItem.a11y.test.ts +0 -73
- package/src/notification/NotificationItem.svelte +0 -368
- package/src/notification/index.ts +0 -9
- package/src/notification/meta.ts +0 -120
- package/src/notification/store.ts +0 -191
- package/src/overflow-list/OverflowList.a11y.test.ts +0 -46
- package/src/overflow-list/OverflowList.kbd.test.ts +0 -34
- package/src/overflow-list/OverflowList.svelte +0 -539
- package/src/overflow-list/OverflowListA11yFixture.svelte +0 -22
- package/src/overflow-list/OverflowListKbdFixture.svelte +0 -21
- package/src/overflow-list/index.ts +0 -2
- package/src/overflow-list/meta.ts +0 -138
- package/src/pagination/Pagination.a11y.test.ts +0 -62
- package/src/pagination/Pagination.kbd.test.ts +0 -66
- package/src/pagination/Pagination.svelte +0 -428
- package/src/pagination/PaginationKbdFixture.svelte +0 -15
- package/src/pagination/index.ts +0 -2
- package/src/pagination/meta.ts +0 -73
- package/src/popconfirm/Popconfirm.a11y.test.ts +0 -46
- package/src/popconfirm/Popconfirm.kbd.test.ts +0 -42
- package/src/popconfirm/Popconfirm.svelte +0 -724
- package/src/popconfirm/PopconfirmA11yFixture.svelte +0 -34
- package/src/popconfirm/PopconfirmKbdFixture.svelte +0 -17
- package/src/popconfirm/index.ts +0 -2
- package/src/popconfirm/meta.ts +0 -200
- package/src/popover/Popover.a11y.test.ts +0 -58
- package/src/popover/Popover.kbd.test.ts +0 -42
- package/src/popover/Popover.svelte +0 -643
- package/src/popover/PopoverA11yFixture.svelte +0 -35
- package/src/popover/PopoverKbdFixture.svelte +0 -18
- package/src/popover/index.ts +0 -2
- package/src/popover/meta.ts +0 -103
- package/src/progress/Progress.a11y.test.ts +0 -37
- package/src/progress/Progress.svelte +0 -423
- package/src/progress/index.ts +0 -3
- package/src/progress/meta.ts +0 -132
- package/src/radio/Radio.svelte +0 -330
- package/src/radio/RadioGroup.a11y.test.ts +0 -43
- package/src/radio/RadioGroup.kbd.test.ts +0 -75
- package/src/radio/RadioGroup.svelte +0 -199
- package/src/radio/RadioGroupKbdFixture.svelte +0 -21
- package/src/radio/context.ts +0 -42
- package/src/radio/index.ts +0 -4
- package/src/radio/meta.ts +0 -68
- package/src/rating/Rating.a11y.test.ts +0 -41
- package/src/rating/Rating.kbd.test.ts +0 -48
- package/src/rating/Rating.svelte +0 -407
- package/src/rating/RatingKbdFixture.svelte +0 -15
- package/src/rating/index.ts +0 -2
- package/src/rating/meta.ts +0 -40
- package/src/resize-observer/ResizeObserver.svelte +0 -169
- package/src/resize-observer/action.ts +0 -80
- package/src/resize-observer/index.ts +0 -9
- package/src/resize-observer/meta.ts +0 -102
- package/src/scroll-list/ScrollColumn.svelte +0 -592
- package/src/scroll-list/ScrollList.svelte +0 -257
- package/src/scroll-list/index.ts +0 -4
- package/src/scroll-list/meta.ts +0 -93
- package/src/scroll-list/types.ts +0 -18
- package/src/select/Select.a11y.test.ts +0 -59
- package/src/select/Select.kbd.test.ts +0 -59
- package/src/select/Select.svelte +0 -1155
- package/src/select/SelectKbdFixture.svelte +0 -29
- package/src/select/index.ts +0 -3
- package/src/select/meta.ts +0 -78
- package/src/select/types.ts +0 -2
- package/src/side-sheet/SideSheet.a11y.test.ts +0 -53
- package/src/side-sheet/SideSheet.kbd.test.ts +0 -63
- package/src/side-sheet/SideSheet.svelte +0 -659
- package/src/side-sheet/SideSheetKbdFixture.svelte +0 -30
- package/src/side-sheet/index.ts +0 -2
- package/src/side-sheet/meta.ts +0 -116
- package/src/skeleton/Skeleton.a11y.test.ts +0 -35
- package/src/skeleton/Skeleton.svelte +0 -131
- package/src/skeleton/SkeletonAvatar.svelte +0 -82
- package/src/skeleton/SkeletonButton.svelte +0 -90
- package/src/skeleton/SkeletonImage.svelte +0 -100
- package/src/skeleton/SkeletonParagraph.svelte +0 -103
- package/src/skeleton/SkeletonTitle.svelte +0 -74
- package/src/skeleton/context.ts +0 -11
- package/src/skeleton/index.ts +0 -8
- package/src/skeleton/meta.ts +0 -65
- package/src/slider/Slider.a11y.test.ts +0 -44
- package/src/slider/Slider.kbd.test.ts +0 -74
- package/src/slider/Slider.svelte +0 -716
- package/src/slider/SliderKbdFixture.svelte +0 -21
- package/src/slider/index.ts +0 -2
- package/src/slider/meta.ts +0 -55
- package/src/space/Space.a11y.test.ts +0 -22
- package/src/space/Space.svelte +0 -109
- package/src/space/SpaceA11yFixture.svelte +0 -17
- package/src/space/index.ts +0 -2
- package/src/space/meta.ts +0 -27
- package/src/spin/Spin.a11y.test.ts +0 -29
- package/src/spin/Spin.svelte +0 -293
- package/src/spin/SpinA11yFixture.svelte +0 -18
- package/src/spin/index.ts +0 -2
- package/src/spin/meta.ts +0 -72
- package/src/steps/Steps.a11y.test.ts +0 -39
- package/src/steps/Steps.kbd.test.ts +0 -73
- package/src/steps/Steps.svelte +0 -392
- package/src/steps/StepsKbdFixture.svelte +0 -21
- package/src/steps/index.ts +0 -3
- package/src/steps/meta.ts +0 -59
- package/src/steps/types.ts +0 -8
- package/src/switch/Switch.a11y.test.ts +0 -46
- package/src/switch/Switch.kbd.test.ts +0 -40
- package/src/switch/Switch.svelte +0 -310
- package/src/switch/SwitchKbdFixture.svelte +0 -22
- package/src/switch/index.ts +0 -2
- package/src/switch/meta.ts +0 -44
- package/src/table/Table.a11y.test.ts +0 -70
- package/src/table/Table.bench.ts +0 -51
- package/src/table/Table.kbd.test.ts +0 -94
- package/src/table/Table.svelte +0 -2327
- package/src/table/TableKbdFixture.svelte +0 -35
- package/src/table/grid-nav.test.ts +0 -173
- package/src/table/grid-nav.ts +0 -146
- package/src/table/index.ts +0 -13
- package/src/table/meta.ts +0 -121
- package/src/table/types.ts +0 -133
- package/src/tabs/TabPane.svelte +0 -92
- package/src/tabs/Tabs.a11y.test.ts +0 -47
- package/src/tabs/Tabs.kbd.test.ts +0 -74
- package/src/tabs/Tabs.svelte +0 -1128
- package/src/tabs/TabsA11yFixture.svelte +0 -20
- package/src/tabs/TabsKbdFixture.svelte +0 -25
- package/src/tabs/context.ts +0 -43
- package/src/tabs/index.ts +0 -14
- package/src/tabs/meta.ts +0 -152
- package/src/tabs/types.ts +0 -6
- package/src/tag/Tag.a11y.test.ts +0 -33
- package/src/tag/Tag.svelte +0 -366
- package/src/tag/TagA11yFixture.svelte +0 -18
- package/src/tag/index.ts +0 -2
- package/src/tag/meta.ts +0 -92
- package/src/tag-input/TagInput.a11y.test.ts +0 -41
- package/src/tag-input/TagInput.svelte +0 -544
- package/src/tag-input/index.ts +0 -2
- package/src/tag-input/meta.ts +0 -43
- package/src/tag-input/reorder.test.ts +0 -59
- package/src/tag-input/reorder.ts +0 -41
- package/src/test-utils/LocaleHarness.svelte +0 -29
- package/src/test-utils/a11y.ts +0 -108
- package/src/test-utils/kbd.ts +0 -52
- package/src/textarea/TextArea.a11y.test.ts +0 -44
- package/src/textarea/TextArea.svelte +0 -420
- package/src/textarea/index.ts +0 -2
- package/src/textarea/meta.ts +0 -104
- package/src/time-picker/TimePicker.a11y.test.ts +0 -45
- package/src/time-picker/TimePicker.kbd.test.ts +0 -70
- package/src/time-picker/TimePicker.svelte +0 -1023
- package/src/time-picker/TimePickerKbdFixture.svelte +0 -23
- package/src/time-picker/index.ts +0 -2
- package/src/time-picker/meta.ts +0 -64
- package/src/timeline/Timeline.a11y.test.ts +0 -54
- package/src/timeline/Timeline.svelte +0 -599
- package/src/timeline/TimelineItem.svelte +0 -164
- package/src/timeline/context.ts +0 -28
- package/src/timeline/index.ts +0 -16
- package/src/timeline/meta.ts +0 -129
- package/src/timeline/types.ts +0 -9
- package/src/toast/Toast.a11y.test.ts +0 -67
- package/src/toast/ToastContainer.svelte +0 -184
- package/src/toast/ToastItem.svelte +0 -247
- package/src/toast/index.ts +0 -9
- package/src/toast/live-region.ts +0 -78
- package/src/toast/meta.ts +0 -95
- package/src/toast/store.ts +0 -187
- package/src/tooltip/Tooltip.a11y.test.ts +0 -41
- package/src/tooltip/Tooltip.kbd.test.ts +0 -37
- package/src/tooltip/Tooltip.svelte +0 -416
- package/src/tooltip/TooltipA11yFixture.svelte +0 -31
- package/src/tooltip/TooltipKbdFixture.svelte +0 -16
- package/src/tooltip/index.ts +0 -2
- package/src/tooltip/meta.ts +0 -59
- package/src/tooltip/placement.ts +0 -6
- package/src/transfer/Transfer.a11y.test.ts +0 -63
- package/src/transfer/Transfer.kbd.test.ts +0 -75
- package/src/transfer/Transfer.svelte +0 -1451
- package/src/transfer/TransferKbdFixture.svelte +0 -23
- package/src/transfer/group.test.ts +0 -55
- package/src/transfer/group.ts +0 -61
- package/src/transfer/index.ts +0 -3
- package/src/transfer/meta.ts +0 -55
- package/src/transfer/reorder.test.ts +0 -52
- package/src/transfer/reorder.ts +0 -42
- package/src/transfer/tree.test.ts +0 -44
- package/src/transfer/tree.ts +0 -52
- package/src/transfer/types.ts +0 -32
- package/src/tree/Tree.a11y.test.ts +0 -63
- package/src/tree/Tree.kbd.test.ts +0 -91
- package/src/tree/Tree.svelte +0 -1500
- package/src/tree/TreeKbdFixture.svelte +0 -30
- package/src/tree/index.ts +0 -3
- package/src/tree/meta.ts +0 -152
- package/src/tree-select/TreeSelect.a11y.test.ts +0 -52
- package/src/tree-select/TreeSelect.kbd.test.ts +0 -53
- package/src/tree-select/TreeSelect.svelte +0 -1611
- package/src/tree-select/TreeSelectKbdFixture.svelte +0 -20
- package/src/tree-select/index.ts +0 -3
- package/src/tree-select/meta.ts +0 -73
- package/src/tree-select/types.ts +0 -11
- package/src/tree-select/virtual.test.ts +0 -104
- package/src/typography/Link.svelte +0 -120
- package/src/typography/Paragraph.svelte +0 -92
- package/src/typography/Text.svelte +0 -93
- package/src/typography/Title.svelte +0 -91
- package/src/typography/Typography.a11y.test.ts +0 -34
- package/src/typography/TypographyA11yFixture.svelte +0 -27
- package/src/typography/TypographyBase.svelte +0 -807
- package/src/typography/index.ts +0 -16
- package/src/typography/meta.ts +0 -158
- package/src/upload/Upload.a11y.test.ts +0 -58
- package/src/upload/Upload.svelte +0 -1094
- package/src/upload/UploadA11yFixture.svelte +0 -23
- package/src/upload/index.ts +0 -3
- package/src/upload/meta.ts +0 -64
- package/src/upload/types.ts +0 -16
- package/src/virtual-list/VirtualList.svelte +0 -401
- package/src/virtual-list/index.ts +0 -2
- package/src/virtual-list/meta.ts +0 -106
package/src/tabs/Tabs.svelte
DELETED
|
@@ -1,1128 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
Tabs — see specs/components/navigation/Tabs.spec.md
|
|
3
|
-
基础子集:line/card 类型、top 位置、数据驱动 tabList + 声明式 TabPane、
|
|
4
|
-
roving tabindex + 键盘、closable。
|
|
5
|
-
溢出滚动:标签超出容器主轴时显示前/后滚动箭头(top/bottom 横向 scrollLeft、
|
|
6
|
-
left/right 纵向 scrollTop),激活标签自动滚到可视区;几何测量 + ResizeObserver +
|
|
7
|
-
滚动操作全部命令式 + cleanup(红线 #3)。addable:末尾「+」按钮触发 onAdd(红线 #1 受控)。
|
|
8
|
-
溢出收纳:overflow='dropdown' 时把放不下的标签收进末尾「更多」下拉(复用 Dropdown),
|
|
9
|
-
离屏测量层命令式测宽 + computeTabOverflow(core 纯函数)算可见/溢出集,激活标签始终保持可见。
|
|
10
|
-
type='button':分段按钮组样式(外包 fill 底,选中段填主色)。
|
|
11
|
-
纯声明式自动收集:未传 tabList 时,从子 <Tabs.Pane> 的 tab/itemKey/disabled/closable
|
|
12
|
-
自动收集推导 tabList(按源码顺序);TabPane 在 mount/unmount/同步副作用里写父级簿记
|
|
13
|
-
普通数组 + bump version $state,父 render 据 version 重建快照(红线 #2:副作用写 /
|
|
14
|
-
渲染读分离,子 effect 绝不读快照 → 无 effect_update_depth_exceeded 自循环)。
|
|
15
|
-
renderTabBar:Snippet 完全自绘标签栏(接收 tab 列表 + 当前激活 key + 切换回调),
|
|
16
|
-
自定义渲染时跳过内置标签栏/溢出逻辑,面板内容仍按 activeKey 显隐。
|
|
17
|
-
|
|
18
|
-
约束:传 tabList 时标签栏数据驱动(与旧版完全一致);仅当不传 tabList 时才走声明式收集。
|
|
19
|
-
|
|
20
|
-
新增 props(本次补全):
|
|
21
|
-
- tabBarExtraContent:标签栏右侧额外内容 Snippet
|
|
22
|
-
- more:溢出折叠配置(数字或 {count,render,dropdownProps})
|
|
23
|
-
- arrowPosition:折叠箭头位置('start'|'end'|'both',默认 'both')
|
|
24
|
-
- renderArrow:自定义箭头 Snippet<[{type:'start'|'end', onClick}]>
|
|
25
|
-
- showRestInDropdown:折叠模式是否展示收起 tabs(默认 true)
|
|
26
|
-
- dropdownProps:透传给「更多」Dropdown 的参数
|
|
27
|
-
- onVisibleTabsChange:溢出项变化回调(携带可见 tab keys)
|
|
28
|
-
- contentStyle:内容区外层样式(string | CSSProperties)
|
|
29
|
-
- preventScroll:Tab 聚焦是否阻止滚动
|
|
30
|
-
- tabPaneMotion:面板切换动画(默认 true)
|
|
31
|
-
-->
|
|
32
|
-
<script lang="ts">
|
|
33
|
-
import type { Snippet } from 'svelte';
|
|
34
|
-
import { useId, computeTabOverflow } from '@chenzy-design/core';
|
|
35
|
-
import { setTabsContext, type TabPaneRegistration } from './context.js';
|
|
36
|
-
import { useLocale } from '../locale-provider/index.js';
|
|
37
|
-
import Dropdown from '../dropdown/Dropdown.svelte';
|
|
38
|
-
import type { DropdownItem } from '../dropdown/types.js';
|
|
39
|
-
import type { TabItem } from './types.js';
|
|
40
|
-
|
|
41
|
-
type TabKey = string | number;
|
|
42
|
-
type TabType = 'line' | 'card' | 'button';
|
|
43
|
-
type TabSize = 'small' | 'default' | 'large';
|
|
44
|
-
type TabPosition = 'top' | 'bottom' | 'left' | 'right';
|
|
45
|
-
type KeyboardActivation = 'auto' | 'manual';
|
|
46
|
-
type OverflowMode = 'scroll' | 'dropdown';
|
|
47
|
-
|
|
48
|
-
type MoreConfig = number | {
|
|
49
|
-
count?: number;
|
|
50
|
-
render?: Snippet;
|
|
51
|
-
dropdownProps?: Record<string, unknown>;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
interface Props {
|
|
55
|
-
value?: TabKey;
|
|
56
|
-
defaultValue?: TabKey;
|
|
57
|
-
type?: TabType;
|
|
58
|
-
size?: TabSize;
|
|
59
|
-
tabPosition?: TabPosition;
|
|
60
|
-
tabList?: TabItem[];
|
|
61
|
-
closable?: boolean;
|
|
62
|
-
keyboardActivation?: KeyboardActivation;
|
|
63
|
-
/**
|
|
64
|
-
* 横向溢出处理方式:
|
|
65
|
-
* - 'scroll'(默认):标签超出容器时显示前/后滚动箭头,命令式滚动;
|
|
66
|
-
* - 'dropdown':把放不下的标签收纳进末尾「更多」下拉,点下拉项跳到对应 tab。
|
|
67
|
-
* 仅 top/bottom 横向标签栏生效;纵向(left/right)始终走滚动。
|
|
68
|
-
*/
|
|
69
|
-
overflow?: OverflowMode;
|
|
70
|
-
/**
|
|
71
|
-
* line 风格横向溢出时启用折叠收纳(与 overflow='dropdown' 同效:放不下的标签进末尾「更多」下拉)。
|
|
72
|
-
* 便捷开关:为 true 时强制走 dropdown 收纳(仅 top/bottom 横向生效)。默认 false。
|
|
73
|
-
*/
|
|
74
|
-
collapsible?: boolean;
|
|
75
|
-
/** 首次激活后才挂载面板内容 */
|
|
76
|
-
lazy?: boolean;
|
|
77
|
-
/** 激活过的面板切走后保留 DOM(display:none),而非卸载 */
|
|
78
|
-
keepDOM?: boolean;
|
|
79
|
-
/** 标签栏末尾显示「+」按钮,点击触发 onAdd(受控数据,由父组件追加) */
|
|
80
|
-
addable?: boolean;
|
|
81
|
-
/**
|
|
82
|
-
* 溢出折叠配置:数字时等价于 { count: n };对象时含 count(收起阈值)、
|
|
83
|
-
* render(自定义「更多」触发器 Snippet)、dropdownProps(透传下拉参数)。
|
|
84
|
-
* 仅 overflow='dropdown'(或 collapsible=true)时生效。
|
|
85
|
-
*/
|
|
86
|
-
more?: MoreConfig;
|
|
87
|
-
/** 标签栏末尾「更多」下拉折叠时,折叠箭头位置(scroll 模式中的前/后箭头)。默认 'both'。 */
|
|
88
|
-
arrowPosition?: 'start' | 'end' | 'both';
|
|
89
|
-
/** 自定义前/后折叠箭头 Snippet,接收 { type:'start'|'end', onClick:()=>void } */
|
|
90
|
-
renderArrow?: Snippet<[{ type: 'start' | 'end'; onClick: () => void }]>;
|
|
91
|
-
/** dropdown 折叠模式是否在下拉中展示收起 tabs(默认 true) */
|
|
92
|
-
showRestInDropdown?: boolean;
|
|
93
|
-
/** 透传给「更多」Dropdown 组件的 props */
|
|
94
|
-
dropdownProps?: Record<string, unknown>;
|
|
95
|
-
/** 溢出项变化回调,携带当前可见 tab keys */
|
|
96
|
-
onVisibleTabsChange?: (visibleTabKeys: TabKey[]) => void;
|
|
97
|
-
/** 内容区外层样式(string 或 CSSProperties 对象) */
|
|
98
|
-
contentStyle?: string | Record<string, string>;
|
|
99
|
-
/** Tab 聚焦是否阻止页面滚动(默认 false) */
|
|
100
|
-
preventScroll?: boolean;
|
|
101
|
-
/** 面板切换是否启用动画(默认 true) */
|
|
102
|
-
tabPaneMotion?: boolean;
|
|
103
|
-
/** 标签栏右侧额外内容 */
|
|
104
|
-
tabBarExtraContent?: Snippet;
|
|
105
|
-
onChange?: (key: TabKey) => void;
|
|
106
|
-
onTabClose?: (key: TabKey) => void;
|
|
107
|
-
/**
|
|
108
|
-
* 标签被点击回调(spec §4:on:tabClick)。含已选中标签(未必触发 onChange),
|
|
109
|
-
* 在 disabled 拦截前发出,可用于埋点。
|
|
110
|
-
*/
|
|
111
|
-
onTabClick?: (key: TabKey, event: MouseEvent) => void;
|
|
112
|
-
/** addable=true 时点击「+」按钮回调(红线 #1:组件内部不改 tabList) */
|
|
113
|
-
onAdd?: () => void;
|
|
114
|
-
/**
|
|
115
|
-
* 自定义整个标签栏的渲染(调用方完全自绘标签栏)。
|
|
116
|
-
* 接收:当前 tab 列表(数据驱动 tabList 或声明式收集结果)、当前激活 key、
|
|
117
|
-
* 切换回调 setActive(受控时仅触发 onChange,不回写 value,红线 #1)。
|
|
118
|
-
* 传入时跳过内置标签栏与溢出处理;面板内容仍按 activeKey 显隐。
|
|
119
|
-
*/
|
|
120
|
-
renderTabBar?: Snippet<[TabItem[], TabKey | undefined, (key: TabKey) => void]>;
|
|
121
|
-
children?: Snippet;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
let {
|
|
125
|
-
value,
|
|
126
|
-
defaultValue,
|
|
127
|
-
type = 'line',
|
|
128
|
-
size = 'default',
|
|
129
|
-
tabPosition = 'top',
|
|
130
|
-
tabList: tabListProp,
|
|
131
|
-
closable = false,
|
|
132
|
-
keyboardActivation = 'auto',
|
|
133
|
-
overflow = 'scroll',
|
|
134
|
-
collapsible = false,
|
|
135
|
-
lazy = false,
|
|
136
|
-
keepDOM = false,
|
|
137
|
-
addable = false,
|
|
138
|
-
more,
|
|
139
|
-
arrowPosition = 'both',
|
|
140
|
-
renderArrow,
|
|
141
|
-
showRestInDropdown = true,
|
|
142
|
-
dropdownProps,
|
|
143
|
-
onVisibleTabsChange,
|
|
144
|
-
contentStyle,
|
|
145
|
-
preventScroll = false,
|
|
146
|
-
tabPaneMotion = true,
|
|
147
|
-
tabBarExtraContent,
|
|
148
|
-
onChange,
|
|
149
|
-
onTabClose,
|
|
150
|
-
onTabClick,
|
|
151
|
-
onAdd,
|
|
152
|
-
renderTabBar,
|
|
153
|
-
children,
|
|
154
|
-
}: Props = $props();
|
|
155
|
-
|
|
156
|
-
const baseId = useId('cd-tabs');
|
|
157
|
-
const loc = useLocale();
|
|
158
|
-
|
|
159
|
-
// --- 纯声明式自动收集 (红线 #2) ---
|
|
160
|
-
// 仅当父未传 tabList(undefined)时启用:子 <Tabs.Pane> 在 mount/unmount/同步副作用里
|
|
161
|
-
// 注册标签元数据。簿记 `paneOrder` 为普通数组(非 $state),避免在子注册 $effect 内
|
|
162
|
-
// 既「读」又「写」同一 $state 数组(代理 push 读 length 再写元素)形成自循环;render 真正
|
|
163
|
-
// 需要的「收集结果快照」仅由单独的 version $state 触发重建——子 effect 只 bump version,
|
|
164
|
-
// 绝不读快照 → 副作用写 / 渲染读分离,无 effect_update_depth_exceeded。
|
|
165
|
-
const usesDeclarativeTabs = $derived(tabListProp === undefined);
|
|
166
|
-
|
|
167
|
-
interface PaneEntry extends TabPaneRegistration {
|
|
168
|
-
id: number;
|
|
169
|
-
}
|
|
170
|
-
let paneNextId = 0;
|
|
171
|
-
const paneOrder: PaneEntry[] = [];
|
|
172
|
-
let paneVersion = $state(0);
|
|
173
|
-
|
|
174
|
-
function bumpPaneVersion(): void {
|
|
175
|
-
paneVersion += 1;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function registerPane(reg: TabPaneRegistration): number {
|
|
179
|
-
const id = paneNextId++;
|
|
180
|
-
paneOrder.push({ id, ...reg });
|
|
181
|
-
bumpPaneVersion();
|
|
182
|
-
return id;
|
|
183
|
-
}
|
|
184
|
-
function updatePane(id: number, reg: TabPaneRegistration): void {
|
|
185
|
-
const entry = paneOrder.find((p) => p.id === id);
|
|
186
|
-
if (!entry) return;
|
|
187
|
-
// 仅在元数据实际变化时 bump(去重,避免无谓 render)。
|
|
188
|
-
if (
|
|
189
|
-
entry.itemKey === reg.itemKey &&
|
|
190
|
-
entry.tab === reg.tab &&
|
|
191
|
-
entry.disabled === reg.disabled &&
|
|
192
|
-
entry.closable === reg.closable
|
|
193
|
-
)
|
|
194
|
-
return;
|
|
195
|
-
Object.assign(entry, reg);
|
|
196
|
-
bumpPaneVersion();
|
|
197
|
-
}
|
|
198
|
-
function unregisterPane(id: number): void {
|
|
199
|
-
const i = paneOrder.findIndex((p) => p.id === id);
|
|
200
|
-
if (i !== -1) {
|
|
201
|
-
paneOrder.splice(i, 1);
|
|
202
|
-
bumpPaneVersion();
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// 收集结果快照(纯派生,render 期只读):仅依赖 version(重建触发器)。
|
|
207
|
-
// 重建只读普通数组 paneOrder,绝不在此写任何 $state。
|
|
208
|
-
const collectedTabs = $derived.by<TabItem[]>(() => {
|
|
209
|
-
void paneVersion; // 收集顺序/内容变化触发重建
|
|
210
|
-
return paneOrder.map((p) => ({
|
|
211
|
-
tab: p.tab,
|
|
212
|
-
itemKey: p.itemKey,
|
|
213
|
-
...(p.disabled !== undefined ? { disabled: p.disabled } : {}),
|
|
214
|
-
...(p.closable !== undefined ? { closable: p.closable } : {}),
|
|
215
|
-
}));
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
// 标签栏实际数据源:传 tabList 用之(数据驱动,向后兼容);否则用声明式收集结果。
|
|
219
|
-
const tabList = $derived<TabItem[]>(usesDeclarativeTabs ? collectedTabs : (tabListProp ?? []));
|
|
220
|
-
|
|
221
|
-
// top/bottom 为横向滚动(主轴 = inline / scrollLeft);left/right 为纵向(scrollTop)。
|
|
222
|
-
const isVertical = $derived(tabPosition === 'left' || tabPosition === 'right');
|
|
223
|
-
|
|
224
|
-
// dropdown 收纳仅在横向标签栏生效;纵向(left/right)始终走滚动(红线 #4 向后兼容:
|
|
225
|
-
// 不传 overflow 时为 'scroll',行为与旧版完全一致)。
|
|
226
|
-
// collapsible 为便捷开关:等价于 overflow='dropdown'(line 溢出折叠收纳)。
|
|
227
|
-
const dropdownMode = $derived((overflow === 'dropdown' || collapsible) && !isVertical);
|
|
228
|
-
|
|
229
|
-
// --- 受控 value (红线 #1):不无条件回写 value,仅 onChange ---
|
|
230
|
-
const isControlled = $derived(value !== undefined);
|
|
231
|
-
// inner 初值:defaultValue 优先,否则数据驱动取首项 key(声明式收集首项需待 pane 注册,
|
|
232
|
-
// 故此处不读 derived tabList,初值可能为 undefined,由 activeKey 派生兜底首个可用标签)。
|
|
233
|
-
let inner = $state<TabKey | undefined>(getInitialValue());
|
|
234
|
-
|
|
235
|
-
function getInitialValue(): TabKey | undefined {
|
|
236
|
-
if (defaultValue !== undefined) return defaultValue;
|
|
237
|
-
return tabListProp?.[0]?.itemKey;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// activeKey 纯派生(红线 #1/#2,render 期只读):
|
|
241
|
-
// 受控取 value;非受控取 inner,inner 未定(如声明式首帧或被关闭后失效)则兜底首个标签 key。
|
|
242
|
-
const activeKey = $derived<TabKey | undefined>(
|
|
243
|
-
isControlled ? value : (resolveUncontrolledKey()),
|
|
244
|
-
);
|
|
245
|
-
|
|
246
|
-
function resolveUncontrolledKey(): TabKey | undefined {
|
|
247
|
-
if (inner !== undefined && tabList.some((t) => t.itemKey === inner)) return inner;
|
|
248
|
-
return tabList[0]?.itemKey;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
function setActive(key: TabKey) {
|
|
252
|
-
if (key === activeKey) return;
|
|
253
|
-
if (!isControlled) inner = key;
|
|
254
|
-
onChange?.(key);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// 声明式 TabPane 通过 context 读取激活 key 决定显隐(红线 #2:getter,非数组注册)。
|
|
258
|
-
// lazy/keepDOM 也经 context 暴露给 TabPane 决定挂载/保留策略。
|
|
259
|
-
setTabsContext({
|
|
260
|
-
getActiveKey: () => activeKey,
|
|
261
|
-
getLazy: () => lazy,
|
|
262
|
-
getKeepDOM: () => keepDOM,
|
|
263
|
-
getTabId: tabId,
|
|
264
|
-
getPanelId: panelId,
|
|
265
|
-
registerPane,
|
|
266
|
-
updatePane,
|
|
267
|
-
unregisterPane,
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
function tabId(key: TabKey): string {
|
|
271
|
-
return `${baseId}-tab-${key}`;
|
|
272
|
-
}
|
|
273
|
-
function panelId(key: TabKey): string {
|
|
274
|
-
return `${baseId}-panel-${key}`;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function isClosable(item: TabItem): boolean {
|
|
278
|
-
return item.closable ?? closable;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// 内部激活:仅切换激活态(disabled 拦截),不发 on:tabClick(无原始事件来源)。
|
|
282
|
-
function activateTab(item: TabItem) {
|
|
283
|
-
if (item.disabled) return;
|
|
284
|
-
setActive(item.itemKey);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// 标签按钮点击:先发 spec §4 on:tabClick(含已选中、disabled 拦截前,可埋点),再激活。
|
|
288
|
-
function handleTabClick(event: MouseEvent, item: TabItem) {
|
|
289
|
-
onTabClick?.(item.itemKey, event);
|
|
290
|
-
activateTab(item);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
function closeTab(e: MouseEvent, item: TabItem) {
|
|
294
|
-
e.stopPropagation();
|
|
295
|
-
if (item.disabled) return;
|
|
296
|
-
onTabClose?.(item.itemKey);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// --- 键盘导航 (红线 #2):roving tabindex 仅由 activeKey 决定,不读挂载数组 ---
|
|
300
|
-
// tablist 内移动焦点到相邻未禁用 tab;auto 聚焦即激活,manual 需 Enter/Space。
|
|
301
|
-
function enabledIndexes(): number[] {
|
|
302
|
-
const out: number[] = [];
|
|
303
|
-
for (let i = 0; i < tabList.length; i += 1) {
|
|
304
|
-
if (!tabList[i]?.disabled) out.push(i);
|
|
305
|
-
}
|
|
306
|
-
return out;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
function focusTab(index: number) {
|
|
310
|
-
const item = tabList[index];
|
|
311
|
-
if (!item) return;
|
|
312
|
-
const el = document.getElementById(tabId(item.itemKey));
|
|
313
|
-
el?.focus({ preventScroll });
|
|
314
|
-
if (keyboardActivation === 'auto') setActive(item.itemKey);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
function onTabKeydown(e: KeyboardEvent, item: TabItem) {
|
|
318
|
-
const enabled = enabledIndexes();
|
|
319
|
-
if (enabled.length === 0) return;
|
|
320
|
-
const currentIdx = enabled.indexOf(tabList.indexOf(item));
|
|
321
|
-
switch (e.key) {
|
|
322
|
-
case 'ArrowRight':
|
|
323
|
-
case 'ArrowDown': {
|
|
324
|
-
e.preventDefault();
|
|
325
|
-
const next = enabled[(currentIdx + 1) % enabled.length];
|
|
326
|
-
if (next !== undefined) focusTab(next);
|
|
327
|
-
break;
|
|
328
|
-
}
|
|
329
|
-
case 'ArrowLeft':
|
|
330
|
-
case 'ArrowUp': {
|
|
331
|
-
e.preventDefault();
|
|
332
|
-
const prev = enabled[(currentIdx - 1 + enabled.length) % enabled.length];
|
|
333
|
-
if (prev !== undefined) focusTab(prev);
|
|
334
|
-
break;
|
|
335
|
-
}
|
|
336
|
-
case 'Home': {
|
|
337
|
-
e.preventDefault();
|
|
338
|
-
const first = enabled[0];
|
|
339
|
-
if (first !== undefined) focusTab(first);
|
|
340
|
-
break;
|
|
341
|
-
}
|
|
342
|
-
case 'End': {
|
|
343
|
-
e.preventDefault();
|
|
344
|
-
const lastIdx = enabled[enabled.length - 1];
|
|
345
|
-
if (lastIdx !== undefined) focusTab(lastIdx);
|
|
346
|
-
break;
|
|
347
|
-
}
|
|
348
|
-
case 'Enter':
|
|
349
|
-
case ' ': {
|
|
350
|
-
if (keyboardActivation === 'manual') {
|
|
351
|
-
e.preventDefault();
|
|
352
|
-
setActive(item.itemKey);
|
|
353
|
-
}
|
|
354
|
-
break;
|
|
355
|
-
}
|
|
356
|
-
default:
|
|
357
|
-
break;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// --- 溢出滚动 (红线 #3):几何测量/ResizeObserver/滚动操作全部命令式 + cleanup ---
|
|
362
|
-
// navEl = 滚动视口(overflow:hidden,scrollLeft/scrollTop 在其上);listEl = 内部 role=tablist。
|
|
363
|
-
// 横向比 scrollWidth>clientWidth,纵向比 scrollHeight>clientHeight 判断是否溢出。
|
|
364
|
-
let navEl = $state<HTMLElement | null>(null);
|
|
365
|
-
let listEl = $state<HTMLElement | null>(null);
|
|
366
|
-
|
|
367
|
-
// 几何 $state(红线 #2):仅由命令式 measure() 写入,render 期只读,
|
|
368
|
-
// 绝不在 render/$derived 里读 DOM 几何。
|
|
369
|
-
let overflowing = $state(false); // 是否溢出(决定箭头是否渲染)
|
|
370
|
-
let canScrollPrev = $state(false); // 前向箭头可用态
|
|
371
|
-
let canScrollNext = $state(false); // 后向箭头可用态
|
|
372
|
-
|
|
373
|
-
// 普通(非响应式)变量:rAF 句柄。
|
|
374
|
-
let rafId = 0;
|
|
375
|
-
|
|
376
|
-
// 每次滚动按钮点击的滚动步长(视口主轴尺寸的 ~80%)。
|
|
377
|
-
const SCROLL_RATIO = 0.8;
|
|
378
|
-
|
|
379
|
-
/** 命令式测量:读视口几何 → 写 overflowing / canScrollPrev / canScrollNext $state。 */
|
|
380
|
-
function measure(): void {
|
|
381
|
-
const nav = navEl;
|
|
382
|
-
if (!nav) return;
|
|
383
|
-
let viewport: number;
|
|
384
|
-
let scrollSize: number;
|
|
385
|
-
let scrollPos: number;
|
|
386
|
-
if (isVertical) {
|
|
387
|
-
viewport = nav.clientHeight;
|
|
388
|
-
scrollSize = nav.scrollHeight;
|
|
389
|
-
scrollPos = nav.scrollTop;
|
|
390
|
-
} else {
|
|
391
|
-
viewport = nav.clientWidth;
|
|
392
|
-
scrollSize = nav.scrollWidth;
|
|
393
|
-
scrollPos = nav.scrollLeft;
|
|
394
|
-
}
|
|
395
|
-
// 1px 容差,避免亚像素抖动。
|
|
396
|
-
const over = scrollSize - viewport > 1;
|
|
397
|
-
const prev = over && scrollPos > 1;
|
|
398
|
-
const next = over && scrollPos < scrollSize - viewport - 1;
|
|
399
|
-
if (over !== overflowing) overflowing = over;
|
|
400
|
-
if (prev !== canScrollPrev) canScrollPrev = prev;
|
|
401
|
-
if (next !== canScrollNext) canScrollNext = next;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
function scheduleMeasure(): void {
|
|
405
|
-
if (rafId) cancelAnimationFrame(rafId);
|
|
406
|
-
rafId = requestAnimationFrame(() => {
|
|
407
|
-
rafId = 0;
|
|
408
|
-
measure();
|
|
409
|
-
});
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
/** 命令式滚动一步(dir=-1 前 / 1 后)。 */
|
|
413
|
-
function scrollByStep(dir: -1 | 1): void {
|
|
414
|
-
const nav = navEl;
|
|
415
|
-
if (!nav) return;
|
|
416
|
-
if (isVertical) {
|
|
417
|
-
const step = nav.clientHeight * SCROLL_RATIO * dir;
|
|
418
|
-
nav.scrollTo({ top: nav.scrollTop + step, behavior: 'smooth' });
|
|
419
|
-
} else {
|
|
420
|
-
const step = nav.clientWidth * SCROLL_RATIO * dir;
|
|
421
|
-
nav.scrollTo({ left: nav.scrollLeft + step, behavior: 'smooth' });
|
|
422
|
-
}
|
|
423
|
-
// 滚动后箭头可用态由 scroll 事件回调更新。
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
/** 把激活标签命令式滚动到可视区(若不在视口内)。 */
|
|
427
|
-
function scrollActiveIntoView(): void {
|
|
428
|
-
const nav = navEl;
|
|
429
|
-
if (!nav || activeKey === undefined) return;
|
|
430
|
-
const el = document.getElementById(tabId(activeKey));
|
|
431
|
-
if (!el) return;
|
|
432
|
-
if (isVertical) {
|
|
433
|
-
const top = el.offsetTop;
|
|
434
|
-
const bottom = top + el.offsetHeight;
|
|
435
|
-
const viewTop = nav.scrollTop;
|
|
436
|
-
const viewBottom = viewTop + nav.clientHeight;
|
|
437
|
-
if (top < viewTop) nav.scrollTo({ top, behavior: 'smooth' });
|
|
438
|
-
else if (bottom > viewBottom)
|
|
439
|
-
nav.scrollTo({ top: bottom - nav.clientHeight, behavior: 'smooth' });
|
|
440
|
-
} else {
|
|
441
|
-
const left = el.offsetLeft;
|
|
442
|
-
const right = left + el.offsetWidth;
|
|
443
|
-
const viewLeft = nav.scrollLeft;
|
|
444
|
-
const viewRight = viewLeft + nav.clientWidth;
|
|
445
|
-
if (left < viewLeft) nav.scrollTo({ left, behavior: 'smooth' });
|
|
446
|
-
else if (right > viewRight)
|
|
447
|
-
nav.scrollTo({ left: right - nav.clientWidth, behavior: 'smooth' });
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
// ResizeObserver:命令式创建 + observe(navEl) + observe(listEl),
|
|
452
|
-
// 视口或内容尺寸变 → rAF 合批 measure;nav 滚动事件直接 measure。
|
|
453
|
-
// 不依赖 overflowing/canScroll* $state,故 measure 写它们不会重跑此 effect(零循环)。
|
|
454
|
-
$effect(() => {
|
|
455
|
-
const nav = navEl;
|
|
456
|
-
const list = listEl;
|
|
457
|
-
if (!nav) return;
|
|
458
|
-
measure();
|
|
459
|
-
const ro = new ResizeObserver(() => scheduleMeasure());
|
|
460
|
-
ro.observe(nav);
|
|
461
|
-
if (list) ro.observe(list);
|
|
462
|
-
const onScroll = (): void => measure();
|
|
463
|
-
nav.addEventListener('scroll', onScroll, { passive: true });
|
|
464
|
-
return () => {
|
|
465
|
-
ro.disconnect();
|
|
466
|
-
nav.removeEventListener('scroll', onScroll);
|
|
467
|
-
if (rafId) {
|
|
468
|
-
cancelAnimationFrame(rafId);
|
|
469
|
-
rafId = 0;
|
|
470
|
-
}
|
|
471
|
-
};
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
// tabList 内容变化重测(仅依赖 tabList.length,不依赖几何 $state)。
|
|
475
|
-
$effect(() => {
|
|
476
|
-
void tabList.length;
|
|
477
|
-
scheduleMeasure();
|
|
478
|
-
});
|
|
479
|
-
|
|
480
|
-
// 激活 key 变化时把激活标签滚到可视区(仅依赖 activeKey)。
|
|
481
|
-
// 仅滚动模式需要——dropdown 模式由 measureDropdown 保证激活标签留可见。
|
|
482
|
-
$effect(() => {
|
|
483
|
-
void activeKey;
|
|
484
|
-
if (dropdownMode) return;
|
|
485
|
-
requestAnimationFrame(() => scrollActiveIntoView());
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
// --- dropdown 收纳 (红线 #2/#3):与 OverflowList 同构 ---
|
|
489
|
-
// 离屏测量层渲染全部标签 + 「更多」样本,命令式读 offsetWidth → computeTabOverflow,
|
|
490
|
-
// 把可见/溢出索引集写入 $state,render 期只读切片。measure 只由 RO(容器尺寸变)
|
|
491
|
-
// 和 tabList/activeKey 变触发,绝不依赖 visible/overflow $state → 零循环。
|
|
492
|
-
let barEl = $state<HTMLElement | null>(null); // dropdown 模式根(RO 观测对象 + containerSize 源)
|
|
493
|
-
let measureEl = $state<HTMLElement | null>(null); // 离屏测量层
|
|
494
|
-
|
|
495
|
-
// 几何 $state:仅由命令式 measureDropdown() 写入,render 期只读。初值全显(首帧降级防闪)。
|
|
496
|
-
let visibleIdx = $state<number[]>([]);
|
|
497
|
-
let overflowIdx = $state<number[]>([]);
|
|
498
|
-
|
|
499
|
-
// 普通(非响应式)变量:rAF 句柄 + 上次结果指纹(去重写 $state)。
|
|
500
|
-
let dropRaf = 0;
|
|
501
|
-
let prevVisibleKey = '';
|
|
502
|
-
let prevOverflowKey = '';
|
|
503
|
-
|
|
504
|
-
/** 命令式测量(普通函数,绝不 $derived/不依赖 visible/overflow $state)。 */
|
|
505
|
-
function measureDropdown(): void {
|
|
506
|
-
const bar = barEl;
|
|
507
|
-
const root = measureEl;
|
|
508
|
-
if (!bar || !root) return;
|
|
509
|
-
// 容器主轴可用宽:读 bar.clientWidth(block,随父宽变化,已扣 padding)。
|
|
510
|
-
const containerSize = bar.clientWidth;
|
|
511
|
-
const tabNodes = root.querySelectorAll<HTMLElement>('[data-cd-measure-tab]');
|
|
512
|
-
const tabSizes: number[] = [];
|
|
513
|
-
for (const node of tabNodes) tabSizes.push(node.offsetWidth);
|
|
514
|
-
const moreNode = root.querySelector<HTMLElement>('[data-cd-measure-more]');
|
|
515
|
-
const moreSize = moreNode ? moreNode.offsetWidth : 0;
|
|
516
|
-
// gap 与 CSS 实际像素一致:dropdown 模式标签间距。
|
|
517
|
-
const gapPx = size === 'small' ? 0 : size === 'large' ? 4 : 2;
|
|
518
|
-
const activeIndex = activeKey === undefined ? -1 : tabList.findIndex((t) => t.itemKey === activeKey);
|
|
519
|
-
const r = computeTabOverflow({ tabSizes, containerSize, moreSize, gap: gapPx, activeIndex });
|
|
520
|
-
// 仅在结果实际变化时写 $state(写 $state 触发 render,但不回触发 measure)。
|
|
521
|
-
const vKey = r.visibleIndexes.join(',');
|
|
522
|
-
const oKey = r.overflowIndexes.join(',');
|
|
523
|
-
if (vKey !== prevVisibleKey) {
|
|
524
|
-
prevVisibleKey = vKey;
|
|
525
|
-
visibleIdx = r.visibleIndexes;
|
|
526
|
-
}
|
|
527
|
-
if (oKey !== prevOverflowKey) {
|
|
528
|
-
prevOverflowKey = oKey;
|
|
529
|
-
overflowIdx = r.overflowIndexes;
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
function scheduleDropMeasure(): void {
|
|
534
|
-
if (dropRaf) cancelAnimationFrame(dropRaf);
|
|
535
|
-
dropRaf = requestAnimationFrame(() => {
|
|
536
|
-
dropRaf = 0;
|
|
537
|
-
measureDropdown();
|
|
538
|
-
});
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
// ResizeObserver:dropdown 模式命令式观测 barEl;容器尺寸变 → rAF 合批 measureDropdown。
|
|
542
|
-
// 依赖 barEl + dropdownMode;不依赖 visible/overflow $state → 写它们不重跑此 effect。
|
|
543
|
-
$effect(() => {
|
|
544
|
-
if (!dropdownMode) return;
|
|
545
|
-
const bar = barEl;
|
|
546
|
-
if (!bar) return;
|
|
547
|
-
measureDropdown();
|
|
548
|
-
const ro = new ResizeObserver(() => scheduleDropMeasure());
|
|
549
|
-
ro.observe(bar);
|
|
550
|
-
return () => {
|
|
551
|
-
ro.disconnect();
|
|
552
|
-
if (dropRaf) {
|
|
553
|
-
cancelAnimationFrame(dropRaf);
|
|
554
|
-
dropRaf = 0;
|
|
555
|
-
}
|
|
556
|
-
};
|
|
557
|
-
});
|
|
558
|
-
|
|
559
|
-
// tabList 内容 / 激活标签变化 → 重测(仅依赖 tabList.length / activeKey,不依赖几何 $state)。
|
|
560
|
-
$effect(() => {
|
|
561
|
-
if (!dropdownMode) return;
|
|
562
|
-
void tabList.length;
|
|
563
|
-
void activeKey;
|
|
564
|
-
scheduleDropMeasure();
|
|
565
|
-
});
|
|
566
|
-
|
|
567
|
-
// 渲染派生(红线 #2):可见/溢出标签集为纯 $derived,仅依赖 tabList + 索引集,不读 DOM。
|
|
568
|
-
const visibleTabs = $derived(visibleIdx.map((i) => tabList[i]).filter(Boolean) as TabItem[]);
|
|
569
|
-
const overflowTabs = $derived(overflowIdx.map((i) => tabList[i]).filter(Boolean) as TabItem[]);
|
|
570
|
-
|
|
571
|
-
// onVisibleTabsChange:dropdown 模式下可见 tabs 变化时回调(仅依赖 visibleTabs,不读 DOM)。
|
|
572
|
-
// 用普通变量存上次指纹,仅在真正变化时发出回调(去重),命令式 $effect,不返回 $derived。
|
|
573
|
-
let prevVisibleTabsKey = '';
|
|
574
|
-
$effect(() => {
|
|
575
|
-
if (!dropdownMode || !onVisibleTabsChange) return;
|
|
576
|
-
const key = visibleTabs.map((t) => t.itemKey).join(',');
|
|
577
|
-
if (key === prevVisibleTabsKey) return;
|
|
578
|
-
prevVisibleTabsKey = key;
|
|
579
|
-
onVisibleTabsChange(visibleTabs.map((t) => t.itemKey));
|
|
580
|
-
});
|
|
581
|
-
|
|
582
|
-
// 溢出集中是否含激活标签(决定「更多」触发器高亮)。
|
|
583
|
-
const moreActive = $derived(
|
|
584
|
-
activeKey !== undefined && overflowTabs.some((t) => t.itemKey === activeKey),
|
|
585
|
-
);
|
|
586
|
-
|
|
587
|
-
// more 配置解析:支持 number 简写或对象形式
|
|
588
|
-
const moreCount = $derived(
|
|
589
|
-
typeof more === 'number' ? more : (more?.count ?? undefined),
|
|
590
|
-
);
|
|
591
|
-
const moreRender = $derived(
|
|
592
|
-
typeof more === 'object' && more !== null ? more.render : undefined,
|
|
593
|
-
);
|
|
594
|
-
const moreDropdownProps = $derived(
|
|
595
|
-
typeof more === 'object' && more !== null ? (more.dropdownProps ?? {}) : {},
|
|
596
|
-
);
|
|
597
|
-
|
|
598
|
-
// showRestInDropdown=false 时,「更多」下拉中不展示收起 tabs(只展示 overflow tabs 中被 moreCount 限制的部分)
|
|
599
|
-
// 当前实现:showRestInDropdown=false 时隐藏溢出下拉(不渲染 Dropdown)
|
|
600
|
-
const moreItems = $derived<DropdownItem[]>(
|
|
601
|
-
showRestInDropdown
|
|
602
|
-
? overflowTabs.map((t) =>
|
|
603
|
-
t.disabled ? { key: t.itemKey, label: t.tab, disabled: true } : { key: t.itemKey, label: t.tab },
|
|
604
|
-
)
|
|
605
|
-
: [],
|
|
606
|
-
);
|
|
607
|
-
|
|
608
|
-
// 「更多」按钮是否显示:有溢出 tabs 且 showRestInDropdown=true
|
|
609
|
-
const showMoreDropdown = $derived(overflowTabs.length > 0 && showRestInDropdown);
|
|
610
|
-
|
|
611
|
-
// contentStyle:字符串直接用;对象转 CSS 字符串。
|
|
612
|
-
const contentStyleStr = $derived(
|
|
613
|
-
contentStyle === undefined
|
|
614
|
-
? undefined
|
|
615
|
-
: typeof contentStyle === 'string'
|
|
616
|
-
? contentStyle
|
|
617
|
-
: Object.entries(contentStyle)
|
|
618
|
-
.map(([k, v]) => `${k.replace(/([A-Z])/g, '-$1').toLowerCase()}:${v}`)
|
|
619
|
-
.join(';'),
|
|
620
|
-
);
|
|
621
|
-
|
|
622
|
-
function onMoreSelect(key: string | number): void {
|
|
623
|
-
const item = tabList.find((t) => t.itemKey === key);
|
|
624
|
-
// 「更多」下拉项选择无原始 MouseEvent,仅激活(不发 on:tabClick)。
|
|
625
|
-
if (item) activateTab(item);
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
const cls = $derived(
|
|
629
|
-
[
|
|
630
|
-
'cd-tabs',
|
|
631
|
-
`cd-tabs--${type}`,
|
|
632
|
-
`cd-tabs--${size}`,
|
|
633
|
-
`cd-tabs--${tabPosition}`,
|
|
634
|
-
dropdownMode ? 'cd-tabs--dropdown' : '',
|
|
635
|
-
!dropdownMode && overflowing ? 'cd-tabs--scrollable' : '',
|
|
636
|
-
]
|
|
637
|
-
.filter(Boolean)
|
|
638
|
-
.join(' '),
|
|
639
|
-
);
|
|
640
|
-
|
|
641
|
-
// 单个标签的渲染片段在 scroll/dropdown 两种模式共用(避免重复模板)。
|
|
642
|
-
</script>
|
|
643
|
-
|
|
644
|
-
<!-- 单个标签片段:scroll/dropdown 两模式共用,保证渲染/键盘/关闭逻辑唯一来源。 -->
|
|
645
|
-
{#snippet tabNode(item: TabItem)}
|
|
646
|
-
{@const selected = item.itemKey === activeKey}
|
|
647
|
-
<div
|
|
648
|
-
class="cd-tabs__tab"
|
|
649
|
-
class:cd-tabs__tab--active={selected}
|
|
650
|
-
class:cd-tabs__tab--disabled={item.disabled}
|
|
651
|
-
>
|
|
652
|
-
<button
|
|
653
|
-
type="button"
|
|
654
|
-
class="cd-tabs__tab-btn"
|
|
655
|
-
role="tab"
|
|
656
|
-
id={tabId(item.itemKey)}
|
|
657
|
-
aria-selected={selected}
|
|
658
|
-
aria-controls={panelId(item.itemKey)}
|
|
659
|
-
tabindex={selected ? 0 : -1}
|
|
660
|
-
disabled={item.disabled ?? false}
|
|
661
|
-
onclick={(e) => handleTabClick(e, item)}
|
|
662
|
-
onkeydown={(e) => onTabKeydown(e, item)}
|
|
663
|
-
>
|
|
664
|
-
{item.tab}
|
|
665
|
-
</button>
|
|
666
|
-
{#if isClosable(item)}
|
|
667
|
-
<button
|
|
668
|
-
type="button"
|
|
669
|
-
class="cd-tabs__close"
|
|
670
|
-
aria-label={loc().t('Tabs.closeTab', { tab: item.tab })}
|
|
671
|
-
tabindex={selected ? 0 : -1}
|
|
672
|
-
onclick={(e) => closeTab(e, item)}
|
|
673
|
-
>
|
|
674
|
-
<svg viewBox="0 0 16 16" width="10" height="10" aria-hidden="true" focusable="false">
|
|
675
|
-
<path
|
|
676
|
-
fill="currentColor"
|
|
677
|
-
d="M9.1 8l3.2-3.2-1.1-1.1L8 6.9 4.8 3.7 3.7 4.8 6.9 8l-3.2 3.2 1.1 1.1L8 9.1l3.2 3.2 1.1-1.1L9.1 8Z"
|
|
678
|
-
/>
|
|
679
|
-
</svg>
|
|
680
|
-
</button>
|
|
681
|
-
{/if}
|
|
682
|
-
</div>
|
|
683
|
-
{/snippet}
|
|
684
|
-
|
|
685
|
-
{#snippet addBtn()}
|
|
686
|
-
{#if addable}
|
|
687
|
-
<button
|
|
688
|
-
type="button"
|
|
689
|
-
class="cd-tabs__add"
|
|
690
|
-
aria-label={loc().t('Tabs.add')}
|
|
691
|
-
onclick={() => onAdd?.()}
|
|
692
|
-
>
|
|
693
|
-
<svg viewBox="0 0 16 16" width="12" height="12" aria-hidden="true" focusable="false">
|
|
694
|
-
<path fill="currentColor" d="M8.8 7.2V2.5H7.2v4.7H2.5v1.6h4.7v4.7h1.6V8.8h4.7V7.2H8.8Z" />
|
|
695
|
-
</svg>
|
|
696
|
-
</button>
|
|
697
|
-
{/if}
|
|
698
|
-
{/snippet}
|
|
699
|
-
|
|
700
|
-
<div class={cls} class:cd-tabs--no-motion={!tabPaneMotion}>
|
|
701
|
-
{#if renderTabBar}
|
|
702
|
-
<!-- renderTabBar:调用方完全自绘标签栏;跳过内置标签栏/溢出逻辑,面板内容仍按 activeKey 显隐。 -->
|
|
703
|
-
{@render renderTabBar(tabList, activeKey, setActive)}
|
|
704
|
-
{:else if dropdownMode}
|
|
705
|
-
<!-- dropdown 收纳:只渲染可见标签,溢出标签进末尾「更多」下拉。 -->
|
|
706
|
-
<div class="cd-tabs__bar" bind:this={barEl}>
|
|
707
|
-
<div class="cd-tabs__list" role="tablist">
|
|
708
|
-
{#each visibleTabs as item (item.itemKey)}
|
|
709
|
-
{@render tabNode(item)}
|
|
710
|
-
{/each}
|
|
711
|
-
</div>
|
|
712
|
-
|
|
713
|
-
{#if showMoreDropdown}
|
|
714
|
-
<div class="cd-tabs__more" class:cd-tabs__more--active={moreActive}>
|
|
715
|
-
<Dropdown items={moreItems} trigger="click" onSelect={onMoreSelect} {...(moreDropdownProps ?? {})} {...(dropdownProps ?? {})}>
|
|
716
|
-
{#snippet triggerContent()}
|
|
717
|
-
{#if moreRender}
|
|
718
|
-
{@render moreRender()}
|
|
719
|
-
{:else}
|
|
720
|
-
<button
|
|
721
|
-
type="button"
|
|
722
|
-
class="cd-tabs__more-btn"
|
|
723
|
-
aria-label={loc().t('Tabs.more')}
|
|
724
|
-
aria-haspopup="menu"
|
|
725
|
-
>
|
|
726
|
-
{loc().t('Tabs.more')}
|
|
727
|
-
<svg viewBox="0 0 16 16" width="10" height="10" aria-hidden="true" focusable="false">
|
|
728
|
-
<path fill="currentColor" d="M3.2 5.5 8 10.3l4.8-4.8 1.1 1.1L8 12.5 2.1 6.6l1.1-1.1Z" />
|
|
729
|
-
</svg>
|
|
730
|
-
</button>
|
|
731
|
-
{/if}
|
|
732
|
-
{/snippet}
|
|
733
|
-
</Dropdown>
|
|
734
|
-
</div>
|
|
735
|
-
{/if}
|
|
736
|
-
|
|
737
|
-
{@render addBtn()}
|
|
738
|
-
|
|
739
|
-
{#if tabBarExtraContent}
|
|
740
|
-
<div class="cd-tabs__extra">{@render tabBarExtraContent()}</div>
|
|
741
|
-
{/if}
|
|
742
|
-
</div>
|
|
743
|
-
|
|
744
|
-
<!-- 离屏测量层:渲染全部标签 + 「更多」样本,仅供命令式测宽,不进可视布局/Tab 序。 -->
|
|
745
|
-
<div class="cd-tabs__measure" bind:this={measureEl} aria-hidden="true">
|
|
746
|
-
{#each tabList as item (item.itemKey)}
|
|
747
|
-
<div class="cd-tabs__tab" data-cd-measure-tab>
|
|
748
|
-
<span class="cd-tabs__tab-btn">{item.tab}</span>
|
|
749
|
-
{#if isClosable(item)}<span class="cd-tabs__close" style="width:18px"></span>{/if}
|
|
750
|
-
</div>
|
|
751
|
-
{/each}
|
|
752
|
-
<span class="cd-tabs__more-btn" data-cd-measure-more>
|
|
753
|
-
{loc().t('Tabs.more')}
|
|
754
|
-
<svg viewBox="0 0 16 16" width="10" height="10" aria-hidden="true" focusable="false">
|
|
755
|
-
<path fill="currentColor" d="M3.2 5.5 8 10.3l4.8-4.8 1.1 1.1L8 12.5 2.1 6.6l1.1-1.1Z" />
|
|
756
|
-
</svg>
|
|
757
|
-
</span>
|
|
758
|
-
</div>
|
|
759
|
-
{:else}
|
|
760
|
-
<!-- scroll 模式(默认):前/后箭头 + 可滚动视口,行为与旧版一致。 -->
|
|
761
|
-
<div class="cd-tabs__bar">
|
|
762
|
-
{#if overflowing && (arrowPosition === 'start' || arrowPosition === 'both')}
|
|
763
|
-
{#if renderArrow}
|
|
764
|
-
{@render renderArrow({ type: 'start', onClick: () => scrollByStep(-1) })}
|
|
765
|
-
{:else}
|
|
766
|
-
<button
|
|
767
|
-
type="button"
|
|
768
|
-
class="cd-tabs__scroll-btn cd-tabs__scroll-btn--prev"
|
|
769
|
-
aria-label={loc().t('Tabs.scrollPrev')}
|
|
770
|
-
disabled={!canScrollPrev}
|
|
771
|
-
tabindex="-1"
|
|
772
|
-
onclick={() => scrollByStep(-1)}
|
|
773
|
-
>
|
|
774
|
-
<svg viewBox="0 0 16 16" width="12" height="12" aria-hidden="true" focusable="false">
|
|
775
|
-
<path fill="currentColor" d="M10.5 3.2 5.7 8l4.8 4.8 1.1-1.1L7.9 8l3.7-3.7-1.1-1.1Z" />
|
|
776
|
-
</svg>
|
|
777
|
-
</button>
|
|
778
|
-
{/if}
|
|
779
|
-
{/if}
|
|
780
|
-
|
|
781
|
-
<div class="cd-tabs__nav" bind:this={navEl}>
|
|
782
|
-
<div
|
|
783
|
-
class="cd-tabs__list"
|
|
784
|
-
role="tablist"
|
|
785
|
-
aria-orientation={isVertical ? 'vertical' : undefined}
|
|
786
|
-
bind:this={listEl}
|
|
787
|
-
>
|
|
788
|
-
{#each tabList as item (item.itemKey)}
|
|
789
|
-
{@render tabNode(item)}
|
|
790
|
-
{/each}
|
|
791
|
-
</div>
|
|
792
|
-
</div>
|
|
793
|
-
|
|
794
|
-
{#if overflowing && (arrowPosition === 'end' || arrowPosition === 'both')}
|
|
795
|
-
{#if renderArrow}
|
|
796
|
-
{@render renderArrow({ type: 'end', onClick: () => scrollByStep(1) })}
|
|
797
|
-
{:else}
|
|
798
|
-
<button
|
|
799
|
-
type="button"
|
|
800
|
-
class="cd-tabs__scroll-btn cd-tabs__scroll-btn--next"
|
|
801
|
-
aria-label={loc().t('Tabs.scrollNext')}
|
|
802
|
-
disabled={!canScrollNext}
|
|
803
|
-
tabindex="-1"
|
|
804
|
-
onclick={() => scrollByStep(1)}
|
|
805
|
-
>
|
|
806
|
-
<svg viewBox="0 0 16 16" width="12" height="12" aria-hidden="true" focusable="false">
|
|
807
|
-
<path fill="currentColor" d="M5.5 3.2 10.3 8l-4.8 4.8-1.1-1.1L8.1 8 4.4 4.3l1.1-1.1Z" />
|
|
808
|
-
</svg>
|
|
809
|
-
</button>
|
|
810
|
-
{/if}
|
|
811
|
-
{/if}
|
|
812
|
-
|
|
813
|
-
{@render addBtn()}
|
|
814
|
-
|
|
815
|
-
{#if tabBarExtraContent}
|
|
816
|
-
<div class="cd-tabs__extra">{@render tabBarExtraContent()}</div>
|
|
817
|
-
{/if}
|
|
818
|
-
</div>
|
|
819
|
-
{/if}
|
|
820
|
-
|
|
821
|
-
{#if children}
|
|
822
|
-
<div class="cd-tabs__content" style={contentStyleStr}>
|
|
823
|
-
{@render children()}
|
|
824
|
-
</div>
|
|
825
|
-
{/if}
|
|
826
|
-
</div>
|
|
827
|
-
|
|
828
|
-
<style>
|
|
829
|
-
.cd-tabs {
|
|
830
|
-
display: flex;
|
|
831
|
-
flex-direction: column;
|
|
832
|
-
font-size: var(--cd-tabs-tab-font-size);
|
|
833
|
-
}
|
|
834
|
-
.cd-tabs__bar {
|
|
835
|
-
display: flex;
|
|
836
|
-
flex-direction: row;
|
|
837
|
-
align-items: stretch;
|
|
838
|
-
}
|
|
839
|
-
.cd-tabs--line .cd-tabs__bar {
|
|
840
|
-
border-block-end: 1px solid var(--cd-tabs-bar-border);
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
/* 滚动视口:裁剪溢出,主轴可滚动;隐藏原生滚动条(滚动由箭头驱动)。 */
|
|
844
|
-
.cd-tabs__nav {
|
|
845
|
-
flex: 1 1 auto;
|
|
846
|
-
min-inline-size: 0;
|
|
847
|
-
overflow: hidden;
|
|
848
|
-
}
|
|
849
|
-
.cd-tabs__list {
|
|
850
|
-
display: flex;
|
|
851
|
-
flex-direction: row;
|
|
852
|
-
flex-wrap: nowrap;
|
|
853
|
-
align-items: stretch;
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
/* 滚动箭头 + 新增按钮:不收缩,垂直居中对齐标签栏。 */
|
|
857
|
-
.cd-tabs__scroll-btn,
|
|
858
|
-
.cd-tabs__add {
|
|
859
|
-
flex: 0 0 auto;
|
|
860
|
-
display: inline-flex;
|
|
861
|
-
align-items: center;
|
|
862
|
-
justify-content: center;
|
|
863
|
-
padding: 0 var(--cd-spacing-1);
|
|
864
|
-
border: none;
|
|
865
|
-
background: transparent;
|
|
866
|
-
color: var(--cd-tabs-tab-color);
|
|
867
|
-
cursor: pointer;
|
|
868
|
-
transition: color var(--cd-motion-duration-fast) var(--cd-motion-ease-standard);
|
|
869
|
-
}
|
|
870
|
-
.cd-tabs__scroll-btn:hover:not(:disabled),
|
|
871
|
-
.cd-tabs__add:hover {
|
|
872
|
-
color: var(--cd-tabs-tab-color-active);
|
|
873
|
-
}
|
|
874
|
-
.cd-tabs__scroll-btn:disabled {
|
|
875
|
-
color: var(--cd-tabs-tab-color-disabled);
|
|
876
|
-
cursor: not-allowed;
|
|
877
|
-
}
|
|
878
|
-
.cd-tabs__scroll-btn:focus-visible,
|
|
879
|
-
.cd-tabs__add:focus-visible {
|
|
880
|
-
outline: none;
|
|
881
|
-
box-shadow: var(--cd-focus-ring);
|
|
882
|
-
border-radius: var(--cd-radius-1);
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
/* --- overflow=dropdown:bar 为可换行无关的单行 flex,溢出标签收进「更多」--- */
|
|
886
|
-
.cd-tabs--dropdown .cd-tabs__bar {
|
|
887
|
-
position: relative;
|
|
888
|
-
flex-wrap: nowrap;
|
|
889
|
-
min-inline-size: 0;
|
|
890
|
-
}
|
|
891
|
-
.cd-tabs--dropdown .cd-tabs__list {
|
|
892
|
-
flex: 0 1 auto;
|
|
893
|
-
min-inline-size: 0;
|
|
894
|
-
overflow: hidden;
|
|
895
|
-
}
|
|
896
|
-
.cd-tabs__more {
|
|
897
|
-
flex: 0 0 auto;
|
|
898
|
-
display: inline-flex;
|
|
899
|
-
align-items: stretch;
|
|
900
|
-
}
|
|
901
|
-
.cd-tabs__more-btn {
|
|
902
|
-
display: inline-flex;
|
|
903
|
-
align-items: center;
|
|
904
|
-
gap: var(--cd-spacing-1);
|
|
905
|
-
margin: 0;
|
|
906
|
-
padding: var(--cd-tabs-tab-padding);
|
|
907
|
-
border: none;
|
|
908
|
-
background: transparent;
|
|
909
|
-
color: var(--cd-tabs-tab-color);
|
|
910
|
-
font: inherit;
|
|
911
|
-
white-space: nowrap;
|
|
912
|
-
cursor: pointer;
|
|
913
|
-
transition: color var(--cd-motion-duration-fast) var(--cd-motion-ease-standard);
|
|
914
|
-
}
|
|
915
|
-
.cd-tabs__more-btn:hover {
|
|
916
|
-
color: var(--cd-tabs-tab-color-active);
|
|
917
|
-
}
|
|
918
|
-
.cd-tabs__more--active .cd-tabs__more-btn {
|
|
919
|
-
color: var(--cd-tabs-tab-color-active);
|
|
920
|
-
}
|
|
921
|
-
.cd-tabs__more-btn:focus-visible {
|
|
922
|
-
outline: none;
|
|
923
|
-
box-shadow: var(--cd-focus-ring);
|
|
924
|
-
border-radius: var(--cd-radius-1);
|
|
925
|
-
}
|
|
926
|
-
/* 离屏测量层:渲染全部标签,不可见、不可点、不进 Tab 序、不参与可视布局。 */
|
|
927
|
-
.cd-tabs__measure {
|
|
928
|
-
position: absolute;
|
|
929
|
-
inset-block-start: 0;
|
|
930
|
-
inset-inline-start: 0;
|
|
931
|
-
display: flex;
|
|
932
|
-
flex-direction: row;
|
|
933
|
-
flex-wrap: nowrap;
|
|
934
|
-
align-items: stretch;
|
|
935
|
-
visibility: hidden;
|
|
936
|
-
pointer-events: none;
|
|
937
|
-
z-index: -1;
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
/* --- type=button:分段按钮组,整体包一层 fill 底,选中段填主色 --- */
|
|
941
|
-
.cd-tabs--button .cd-tabs__bar {
|
|
942
|
-
border-block-end: none;
|
|
943
|
-
}
|
|
944
|
-
.cd-tabs--button .cd-tabs__list {
|
|
945
|
-
gap: var(--cd-tabs-button-gap);
|
|
946
|
-
padding: var(--cd-tabs-button-pad);
|
|
947
|
-
background: var(--cd-tabs-button-bg);
|
|
948
|
-
border-radius: var(--cd-tabs-card-radius);
|
|
949
|
-
}
|
|
950
|
-
.cd-tabs--button .cd-tabs__tab {
|
|
951
|
-
border-radius: var(--cd-tabs-card-radius);
|
|
952
|
-
}
|
|
953
|
-
.cd-tabs--button .cd-tabs__tab-btn {
|
|
954
|
-
border: none;
|
|
955
|
-
border-radius: var(--cd-tabs-card-radius);
|
|
956
|
-
}
|
|
957
|
-
.cd-tabs--button .cd-tabs__tab:hover:not(.cd-tabs__tab--disabled):not(.cd-tabs__tab--active) {
|
|
958
|
-
background: var(--cd-tabs-button-bg-hover);
|
|
959
|
-
}
|
|
960
|
-
.cd-tabs--button .cd-tabs__tab--active {
|
|
961
|
-
background: var(--cd-tabs-button-bg-active);
|
|
962
|
-
}
|
|
963
|
-
.cd-tabs--button .cd-tabs__tab--active .cd-tabs__tab-btn {
|
|
964
|
-
color: var(--cd-tabs-button-color-active);
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
/* --- tabPosition: bottom(bar 在内容下方)--- */
|
|
968
|
-
.cd-tabs--bottom {
|
|
969
|
-
flex-direction: column-reverse;
|
|
970
|
-
}
|
|
971
|
-
.cd-tabs--bottom.cd-tabs--line .cd-tabs__bar {
|
|
972
|
-
border-block-end: none;
|
|
973
|
-
border-block-start: 1px solid var(--cd-tabs-bar-border);
|
|
974
|
-
}
|
|
975
|
-
.cd-tabs--bottom.cd-tabs--line .cd-tabs__tab-btn {
|
|
976
|
-
border-block-end: none;
|
|
977
|
-
border-block-start: var(--cd-tabs-ink-height) solid transparent;
|
|
978
|
-
}
|
|
979
|
-
.cd-tabs--bottom.cd-tabs--line .cd-tabs__tab--active .cd-tabs__tab-btn {
|
|
980
|
-
border-block-start-color: var(--cd-tabs-ink-color);
|
|
981
|
-
}
|
|
982
|
-
.cd-tabs--bottom .cd-tabs__content {
|
|
983
|
-
padding-block-start: 0;
|
|
984
|
-
padding-block-end: var(--cd-spacing-3);
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
/* --- tabPosition: left / right(bar 竖向在侧)--- */
|
|
988
|
-
.cd-tabs--left,
|
|
989
|
-
.cd-tabs--right {
|
|
990
|
-
flex-direction: row;
|
|
991
|
-
}
|
|
992
|
-
.cd-tabs--right {
|
|
993
|
-
flex-direction: row-reverse;
|
|
994
|
-
}
|
|
995
|
-
.cd-tabs--left .cd-tabs__bar,
|
|
996
|
-
.cd-tabs--right .cd-tabs__bar {
|
|
997
|
-
flex-direction: column;
|
|
998
|
-
align-items: stretch;
|
|
999
|
-
/* 纵向滚动需要可约束的高度:随内容/父容器,溢出时由 nav 裁剪滚动。 */
|
|
1000
|
-
max-block-size: 100%;
|
|
1001
|
-
}
|
|
1002
|
-
/* 纵向:nav 沿块轴裁剪滚动,list 纵向排列。 */
|
|
1003
|
-
.cd-tabs--left .cd-tabs__nav,
|
|
1004
|
-
.cd-tabs--right .cd-tabs__nav {
|
|
1005
|
-
flex: 1 1 auto;
|
|
1006
|
-
min-block-size: 0;
|
|
1007
|
-
min-inline-size: 0;
|
|
1008
|
-
}
|
|
1009
|
-
.cd-tabs--left .cd-tabs__list,
|
|
1010
|
-
.cd-tabs--right .cd-tabs__list {
|
|
1011
|
-
flex-direction: column;
|
|
1012
|
-
align-items: stretch;
|
|
1013
|
-
}
|
|
1014
|
-
.cd-tabs--left.cd-tabs--line .cd-tabs__bar {
|
|
1015
|
-
border-block-end: none;
|
|
1016
|
-
border-inline-end: 1px solid var(--cd-tabs-bar-border);
|
|
1017
|
-
}
|
|
1018
|
-
.cd-tabs--right.cd-tabs--line .cd-tabs__bar {
|
|
1019
|
-
border-block-end: none;
|
|
1020
|
-
border-inline-start: 1px solid var(--cd-tabs-bar-border);
|
|
1021
|
-
}
|
|
1022
|
-
.cd-tabs--left .cd-tabs__tab-btn,
|
|
1023
|
-
.cd-tabs--right .cd-tabs__tab-btn {
|
|
1024
|
-
inline-size: 100%;
|
|
1025
|
-
text-align: start;
|
|
1026
|
-
}
|
|
1027
|
-
.cd-tabs--left.cd-tabs--line .cd-tabs__tab-btn {
|
|
1028
|
-
border-block-end: none;
|
|
1029
|
-
border-inline-end: var(--cd-tabs-ink-height) solid transparent;
|
|
1030
|
-
}
|
|
1031
|
-
.cd-tabs--right.cd-tabs--line .cd-tabs__tab-btn {
|
|
1032
|
-
border-block-end: none;
|
|
1033
|
-
border-inline-start: var(--cd-tabs-ink-height) solid transparent;
|
|
1034
|
-
}
|
|
1035
|
-
.cd-tabs--left.cd-tabs--line .cd-tabs__tab--active .cd-tabs__tab-btn {
|
|
1036
|
-
border-inline-end-color: var(--cd-tabs-ink-color);
|
|
1037
|
-
}
|
|
1038
|
-
.cd-tabs--right.cd-tabs--line .cd-tabs__tab--active .cd-tabs__tab-btn {
|
|
1039
|
-
border-inline-start-color: var(--cd-tabs-ink-color);
|
|
1040
|
-
}
|
|
1041
|
-
.cd-tabs--left .cd-tabs__content,
|
|
1042
|
-
.cd-tabs--right .cd-tabs__content {
|
|
1043
|
-
flex: 1 1 auto;
|
|
1044
|
-
padding-block-start: 0;
|
|
1045
|
-
padding-inline: var(--cd-spacing-4);
|
|
1046
|
-
}
|
|
1047
|
-
.cd-tabs__tab {
|
|
1048
|
-
flex: 0 0 auto;
|
|
1049
|
-
display: inline-flex;
|
|
1050
|
-
align-items: center;
|
|
1051
|
-
gap: var(--cd-spacing-1);
|
|
1052
|
-
white-space: nowrap;
|
|
1053
|
-
}
|
|
1054
|
-
.cd-tabs--card .cd-tabs__tab {
|
|
1055
|
-
background: var(--cd-tabs-card-bg);
|
|
1056
|
-
border-radius: var(--cd-tabs-card-radius) var(--cd-tabs-card-radius) 0 0;
|
|
1057
|
-
}
|
|
1058
|
-
.cd-tabs--card .cd-tabs__tab--active {
|
|
1059
|
-
background: var(--cd-tabs-card-bg-active);
|
|
1060
|
-
}
|
|
1061
|
-
.cd-tabs__tab-btn {
|
|
1062
|
-
margin: 0;
|
|
1063
|
-
padding: var(--cd-tabs-tab-padding);
|
|
1064
|
-
border: none;
|
|
1065
|
-
background: transparent;
|
|
1066
|
-
color: var(--cd-tabs-tab-color);
|
|
1067
|
-
font: inherit;
|
|
1068
|
-
cursor: pointer;
|
|
1069
|
-
transition: color var(--cd-motion-duration-fast) var(--cd-motion-ease-standard);
|
|
1070
|
-
}
|
|
1071
|
-
.cd-tabs--line .cd-tabs__tab-btn {
|
|
1072
|
-
border-block-end: var(--cd-tabs-ink-height) solid transparent;
|
|
1073
|
-
}
|
|
1074
|
-
.cd-tabs__tab--active .cd-tabs__tab-btn {
|
|
1075
|
-
color: var(--cd-tabs-tab-color-active);
|
|
1076
|
-
}
|
|
1077
|
-
.cd-tabs--line .cd-tabs__tab--active .cd-tabs__tab-btn {
|
|
1078
|
-
border-block-end-color: var(--cd-tabs-ink-color);
|
|
1079
|
-
}
|
|
1080
|
-
.cd-tabs__tab-btn:focus-visible {
|
|
1081
|
-
outline: none;
|
|
1082
|
-
box-shadow: var(--cd-focus-ring);
|
|
1083
|
-
border-radius: var(--cd-radius-1);
|
|
1084
|
-
}
|
|
1085
|
-
.cd-tabs__tab--disabled .cd-tabs__tab-btn {
|
|
1086
|
-
color: var(--cd-tabs-tab-color-disabled);
|
|
1087
|
-
cursor: not-allowed;
|
|
1088
|
-
}
|
|
1089
|
-
.cd-tabs__close {
|
|
1090
|
-
display: inline-flex;
|
|
1091
|
-
align-items: center;
|
|
1092
|
-
justify-content: center;
|
|
1093
|
-
margin-inline-end: var(--cd-spacing-2);
|
|
1094
|
-
padding: 0;
|
|
1095
|
-
border: none;
|
|
1096
|
-
background: transparent;
|
|
1097
|
-
color: var(--cd-tabs-tab-color);
|
|
1098
|
-
cursor: pointer;
|
|
1099
|
-
}
|
|
1100
|
-
.cd-tabs__close:hover {
|
|
1101
|
-
color: var(--cd-tabs-tab-color-active);
|
|
1102
|
-
}
|
|
1103
|
-
.cd-tabs__close:focus-visible {
|
|
1104
|
-
outline: none;
|
|
1105
|
-
box-shadow: var(--cd-focus-ring);
|
|
1106
|
-
border-radius: var(--cd-radius-1);
|
|
1107
|
-
}
|
|
1108
|
-
.cd-tabs__content {
|
|
1109
|
-
padding-block-start: var(--cd-spacing-3);
|
|
1110
|
-
}
|
|
1111
|
-
/* tabBarExtraContent:标签栏右侧额外内容,靠右对齐 */
|
|
1112
|
-
.cd-tabs__extra {
|
|
1113
|
-
flex: 1 0 auto;
|
|
1114
|
-
display: flex;
|
|
1115
|
-
align-items: center;
|
|
1116
|
-
justify-content: flex-end;
|
|
1117
|
-
padding-inline-start: var(--cd-spacing-2);
|
|
1118
|
-
}
|
|
1119
|
-
/* tabPaneMotion=false:禁用面板切换过渡动画 */
|
|
1120
|
-
.cd-tabs--no-motion .cd-tabs__content {
|
|
1121
|
-
transition: none;
|
|
1122
|
-
}
|
|
1123
|
-
@media (prefers-reduced-motion: reduce) {
|
|
1124
|
-
.cd-tabs__tab-btn {
|
|
1125
|
-
transition: none;
|
|
1126
|
-
}
|
|
1127
|
-
}
|
|
1128
|
-
</style>
|