@ecoma-io/loom 0.3.0 → 0.4.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/index.js +38 -6
- package/dist/packages/blocks/app-header/src/AppHeader.js.map +1 -1
- package/dist/packages/blocks/app-header/src/AppHeader.vue.d.ts.map +1 -1
- package/dist/packages/blocks/app-header/src/AppHeader.vue_vue_type_script_setup_true_lang.js +1 -0
- package/dist/packages/blocks/app-header/src/AppHeader.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/blocks/desktop-app-shell/src/DesktopAppShell.vue.d.ts +3 -3
- package/dist/packages/blocks/metric-card/src/MetricCard.js.map +1 -1
- package/dist/packages/blocks/metric-card/src/MetricCard.vue.d.ts +22 -4
- package/dist/packages/blocks/metric-card/src/MetricCard.vue.d.ts.map +1 -1
- package/dist/packages/blocks/metric-card/src/MetricCard.vue_vue_type_script_setup_true_lang.js +38 -15
- package/dist/packages/blocks/metric-card/src/MetricCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/blocks/title-bar/src/TitleBar.vue.d.ts +3 -3
- package/dist/packages/blocks/toast-stack/src/ToastStack.vue.d.ts +3 -3
- package/dist/packages/composition/scroll-reel/src/ScrollReel.js +2 -2
- package/dist/packages/composition/scroll-reel/src/ScrollReel.js.map +1 -1
- package/dist/packages/composition/scroll-reel/src/ScrollReel.vue.d.ts +26 -0
- package/dist/packages/composition/scroll-reel/src/ScrollReel.vue_vue_type_script_setup_true_lang.js +27 -7
- package/dist/packages/composition/scroll-reel/src/ScrollReel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/composition/scroll-reel/src/index.d.ts +2 -1
- package/dist/packages/composition/scroll-reel/src/index.js +2 -1
- package/dist/packages/core/src/index.d.ts +1 -1
- package/dist/packages/core/src/index.d.ts.map +1 -1
- package/dist/packages/core/src/index.js +2 -2
- package/dist/packages/core/src/motion.d.ts +13 -0
- package/dist/packages/core/src/motion.d.ts.map +1 -1
- package/dist/packages/core/src/motion.js +20 -13
- package/dist/packages/core/src/motion.js.map +1 -1
- package/dist/packages/core/src/theme.d.ts +9 -0
- package/dist/packages/core/src/theme.d.ts.map +1 -1
- package/dist/packages/core/src/theme.js +36 -10
- package/dist/packages/core/src/theme.js.map +1 -1
- package/dist/packages/labels/src/ancestor-disabled.d.ts +5 -1
- package/dist/packages/labels/src/ancestor-disabled.d.ts.map +1 -1
- package/dist/packages/labels/src/ancestor-disabled.js +5 -1
- package/dist/packages/labels/src/ancestor-disabled.js.map +1 -1
- package/dist/packages/labels/src/component-shapes.d.ts +73 -0
- package/dist/packages/labels/src/component-shapes.d.ts.map +1 -1
- package/dist/packages/labels/src/date-labels.d.ts +8 -6
- package/dist/packages/labels/src/date-labels.d.ts.map +1 -1
- package/dist/packages/labels/src/date-labels.js +8 -6
- package/dist/packages/labels/src/date-labels.js.map +1 -1
- package/dist/packages/labels/src/index.d.ts +2 -2
- package/dist/packages/labels/src/index.d.ts.map +1 -1
- package/dist/packages/labels/src/label-registry.d.ts +7 -1
- package/dist/packages/labels/src/label-registry.d.ts.map +1 -1
- package/dist/packages/layouts/reading/src/Reading.js.map +1 -1
- package/dist/packages/layouts/reading/src/Reading.vue.d.ts +16 -15
- package/dist/packages/layouts/reading/src/Reading.vue.d.ts.map +1 -1
- package/dist/packages/layouts/reading/src/Reading.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/layouts/reading/src/index.d.ts +1 -0
- package/dist/packages/layouts/reading/src/index.d.ts.map +1 -1
- package/dist/packages/loom/src/a11y.d.ts +1 -1
- package/dist/packages/loom/src/index.d.ts +191 -174
- package/dist/packages/loom/src/theme.d.ts +3 -0
- package/dist/packages/primitives/alert/src/Alert.js +7 -0
- package/dist/packages/primitives/alert/src/Alert.js.map +1 -0
- package/dist/packages/primitives/alert/src/Alert.vue.d.ts +73 -0
- package/dist/packages/primitives/alert/src/Alert.vue_vue_type_script_setup_true_lang.js +127 -0
- package/dist/packages/primitives/alert/src/Alert.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/alert/src/index.d.ts +4 -0
- package/dist/packages/primitives/alert/src/index.js +3 -0
- package/dist/packages/primitives/alert-dialog/src/AlertDialog.js.map +1 -1
- package/dist/packages/primitives/alert-dialog/src/AlertDialog.vue.d.ts +2 -2
- package/dist/packages/primitives/alert-dialog/src/AlertDialog.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/packages/primitives/alert-dialog/src/AlertDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/alert-dialog/src/index.d.ts +1 -1
- package/dist/packages/primitives/avatar-group/src/AvatarGroup.vue.d.ts +3 -3
- package/dist/packages/primitives/avatar-group/src/index.d.ts +1 -1
- package/dist/packages/primitives/badge/src/Badge.js.map +1 -1
- package/dist/packages/primitives/badge/src/Badge.vue.d.ts.map +1 -1
- package/dist/packages/primitives/badge/src/Badge.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/badge/src/Badge.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/breadcrumb/src/Breadcrumb.js +2 -2
- package/dist/packages/primitives/breadcrumb/src/Breadcrumb.js.map +1 -1
- package/dist/packages/primitives/breadcrumb/src/Breadcrumb.vue.d.ts +28 -0
- package/dist/packages/primitives/breadcrumb/src/Breadcrumb.vue_vue_type_script_setup_true_lang.js +16 -5
- package/dist/packages/primitives/breadcrumb/src/Breadcrumb.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/breadcrumb/src/index.d.ts +2 -1
- package/dist/packages/primitives/breadcrumb/src/index.js +2 -1
- package/dist/packages/primitives/button/src/Button.js.map +1 -1
- package/dist/packages/primitives/button/src/Button.vue.d.ts.map +1 -1
- package/dist/packages/primitives/button/src/Button.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/button/src/index.js +2 -2
- package/dist/packages/primitives/card/src/Card.js +7 -0
- package/dist/packages/primitives/card/src/Card.js.map +1 -0
- package/dist/packages/primitives/card/src/Card.vue.d.ts +72 -0
- package/dist/packages/primitives/card/src/Card.vue_vue_type_script_setup_true_lang.js +65 -0
- package/dist/packages/primitives/card/src/Card.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/card/src/index.d.ts +3 -0
- package/dist/packages/primitives/card/src/index.d.ts.map +1 -0
- package/dist/packages/primitives/card/src/index.js +2 -0
- package/dist/packages/primitives/carousel/src/Carousel.js +7 -0
- package/dist/packages/primitives/carousel/src/Carousel.js.map +1 -0
- package/dist/packages/primitives/carousel/src/Carousel.vue.d.ts +34 -0
- package/dist/packages/primitives/carousel/src/Carousel.vue_vue_type_script_setup_true_lang.js +175 -0
- package/dist/packages/primitives/carousel/src/Carousel.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/carousel/src/index.d.ts +3 -0
- package/dist/packages/primitives/carousel/src/index.js +3 -0
- package/dist/packages/primitives/checkbox/src/Checkbox.js.map +1 -1
- package/dist/packages/primitives/checkbox/src/Checkbox.vue.d.ts +1 -1
- package/dist/packages/primitives/checkbox/src/Checkbox.vue.d.ts.map +1 -1
- package/dist/packages/primitives/checkbox/src/Checkbox.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/packages/primitives/checkbox/src/Checkbox.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/chip/src/Chip.js.map +1 -1
- package/dist/packages/primitives/chip/src/Chip.vue.d.ts.map +1 -1
- package/dist/packages/primitives/chip/src/Chip.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/chip/src/Chip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/collapse/src/Collapse.js +7 -0
- package/dist/packages/primitives/collapse/src/Collapse.js.map +1 -0
- package/dist/packages/primitives/collapse/src/Collapse.vue.d.ts +56 -0
- package/dist/packages/primitives/collapse/src/Collapse.vue.d.ts.map +1 -0
- package/dist/packages/primitives/collapse/src/Collapse.vue_vue_type_script_setup_true_lang.js +81 -0
- package/dist/packages/primitives/collapse/src/Collapse.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/collapse/src/index.d.ts +2 -0
- package/dist/packages/primitives/collapse/src/index.d.ts.map +1 -0
- package/dist/packages/primitives/collapse/src/index.js +2 -0
- package/dist/packages/primitives/color-picker/src/ColorPicker.js.map +1 -1
- package/dist/packages/primitives/color-picker/src/ColorPicker.vue.d.ts +4 -4
- package/dist/packages/primitives/color-picker/src/ColorPicker.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/packages/primitives/color-picker/src/ColorPicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/color-picker/src/index.d.ts +1 -1
- package/dist/packages/primitives/combobox/src/Combobox.js.map +1 -1
- package/dist/packages/primitives/combobox/src/Combobox.vue.d.ts +2 -2
- package/dist/packages/primitives/combobox/src/Combobox.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/packages/primitives/combobox/src/Combobox.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/combobox/src/index.d.ts +1 -1
- package/dist/packages/primitives/context-menu/src/ContextMenu.js.map +1 -1
- package/dist/packages/primitives/context-menu/src/ContextMenu.vue.d.ts +12 -0
- package/dist/packages/primitives/context-menu/src/ContextMenu.vue.d.ts.map +1 -1
- package/dist/packages/primitives/context-menu/src/ContextMenu.vue_vue_type_script_setup_true_lang.js +7 -3
- package/dist/packages/primitives/context-menu/src/ContextMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/date-picker/src/DatePicker.js.map +1 -1
- package/dist/packages/primitives/date-picker/src/DatePicker.vue.d.ts +2 -2
- package/dist/packages/primitives/date-picker/src/DatePicker.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/date-picker/src/DatePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/date-range-picker/src/DateRangePicker.js.map +1 -1
- package/dist/packages/primitives/date-range-picker/src/DateRangePicker.vue.d.ts +3 -3
- package/dist/packages/primitives/date-range-picker/src/DateRangePicker.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/date-range-picker/src/DateRangePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/date-range-picker/src/index.d.ts +1 -1
- package/dist/packages/primitives/date-time-picker/src/DateTimePicker.js.map +1 -1
- package/dist/packages/primitives/date-time-picker/src/DateTimePicker.vue.d.ts +2 -2
- package/dist/packages/primitives/date-time-picker/src/DateTimePicker.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/date-time-picker/src/DateTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/date-time-range-picker/src/DateTimeRangePicker.js.map +1 -1
- package/dist/packages/primitives/date-time-range-picker/src/DateTimeRangePicker.vue.d.ts +3 -3
- package/dist/packages/primitives/date-time-range-picker/src/DateTimeRangePicker.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/date-time-range-picker/src/DateTimeRangePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/date-time-range-picker/src/index.d.ts +1 -1
- package/dist/packages/primitives/dialog/src/Dialog.js.map +1 -1
- package/dist/packages/primitives/dialog/src/Dialog.vue.d.ts +12 -4
- package/dist/packages/primitives/dialog/src/Dialog.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/dialog/src/Dialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/dialog/src/index.d.ts +1 -1
- package/dist/packages/primitives/drawer/src/Drawer.js.map +1 -1
- package/dist/packages/primitives/drawer/src/Drawer.vue.d.ts +2 -2
- package/dist/packages/primitives/drawer/src/Drawer.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/packages/primitives/drawer/src/Drawer.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/drawer/src/index.d.ts +1 -1
- package/dist/packages/primitives/dropdown-menu/src/DropdownMenu.js.map +1 -1
- package/dist/packages/primitives/dropdown-menu/src/DropdownMenu.vue.d.ts +26 -0
- package/dist/packages/primitives/dropdown-menu/src/DropdownMenu.vue.d.ts.map +1 -1
- package/dist/packages/primitives/dropdown-menu/src/DropdownMenu.vue_vue_type_script_setup_true_lang.js +10 -4
- package/dist/packages/primitives/dropdown-menu/src/DropdownMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/editable/src/Editable.js.map +1 -1
- package/dist/packages/primitives/editable/src/Editable.vue.d.ts +2 -2
- package/dist/packages/primitives/editable/src/Editable.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/editable/src/Editable.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/editable/src/index.d.ts +1 -1
- package/dist/packages/primitives/file-upload/src/FileUpload.js.map +1 -1
- package/dist/packages/primitives/file-upload/src/FileUpload.vue.d.ts +2 -2
- package/dist/packages/primitives/file-upload/src/FileUpload.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/file-upload/src/FileUpload.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/file-upload/src/index.d.ts +1 -1
- package/dist/packages/primitives/hover-card/src/HoverCard.js.map +1 -1
- package/dist/packages/primitives/hover-card/src/HoverCard.vue.d.ts.map +1 -1
- package/dist/packages/primitives/hover-card/src/HoverCard.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/hover-card/src/HoverCard.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/icon-button/src/IconButton.js.map +1 -1
- package/dist/packages/primitives/icon-button/src/IconButton.vue.d.ts +10 -4
- package/dist/packages/primitives/icon-button/src/IconButton.vue.d.ts.map +1 -1
- package/dist/packages/primitives/icon-button/src/IconButton.vue_vue_type_script_setup_true_lang.js +18 -6
- package/dist/packages/primitives/icon-button/src/IconButton.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/indicator/src/Indicator.vue.d.ts +2 -2
- package/dist/packages/primitives/indicator/src/index.d.ts +1 -1
- package/dist/packages/primitives/kbd/src/Kbd.js +7 -0
- package/dist/packages/primitives/kbd/src/Kbd.js.map +1 -0
- package/dist/packages/primitives/kbd/src/Kbd.vue.d.ts +30 -0
- package/dist/packages/primitives/kbd/src/Kbd.vue.d.ts.map +1 -0
- package/dist/packages/primitives/kbd/src/Kbd.vue_vue_type_script_setup_true_lang.js +29 -0
- package/dist/packages/primitives/kbd/src/Kbd.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/kbd/src/index.d.ts +3 -0
- package/dist/packages/primitives/kbd/src/index.d.ts.map +1 -0
- package/dist/packages/primitives/kbd/src/index.js +3 -0
- package/dist/packages/primitives/link/src/Link.js.map +1 -1
- package/dist/packages/primitives/link/src/Link.vue.d.ts.map +1 -1
- package/dist/packages/primitives/link/src/Link.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/packages/primitives/link/src/Link.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/list/src/List.js +7 -0
- package/dist/packages/primitives/list/src/List.js.map +1 -0
- package/dist/packages/primitives/list/src/List.vue.d.ts +22 -0
- package/dist/packages/primitives/list/src/List.vue.d.ts.map +1 -0
- package/dist/packages/primitives/list/src/List.vue_vue_type_script_setup_true_lang.js +27 -0
- package/dist/packages/primitives/list/src/List.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/list/src/ListItem.js +7 -0
- package/dist/packages/primitives/list/src/ListItem.js.map +1 -0
- package/dist/packages/primitives/list/src/ListItem.vue.d.ts +75 -0
- package/dist/packages/primitives/list/src/ListItem.vue.d.ts.map +1 -0
- package/dist/packages/primitives/list/src/ListItem.vue_vue_type_script_setup_true_lang.js +101 -0
- package/dist/packages/primitives/list/src/ListItem.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/list/src/index.d.ts +4 -0
- package/dist/packages/primitives/list/src/index.d.ts.map +1 -0
- package/dist/packages/primitives/list/src/index.js +3 -0
- package/dist/packages/primitives/menubar/src/Menubar.js.map +1 -1
- package/dist/packages/primitives/menubar/src/Menubar.vue.d.ts +20 -6
- package/dist/packages/primitives/menubar/src/Menubar.vue.d.ts.map +1 -1
- package/dist/packages/primitives/menubar/src/Menubar.vue_vue_type_script_setup_true_lang.js +178 -64
- package/dist/packages/primitives/menubar/src/Menubar.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/number-field/src/NumberField.js.map +1 -1
- package/dist/packages/primitives/number-field/src/NumberField.vue.d.ts +2 -2
- package/dist/packages/primitives/number-field/src/NumberField.vue_vue_type_script_setup_true_lang.js +1 -0
- package/dist/packages/primitives/number-field/src/NumberField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/number-field/src/index.d.ts +1 -1
- package/dist/packages/primitives/otp-input/src/OtpInput.vue.d.ts +2 -2
- package/dist/packages/primitives/otp-input/src/index.d.ts +1 -1
- package/dist/packages/primitives/pagination/src/Pagination.js.map +1 -1
- package/dist/packages/primitives/pagination/src/Pagination.vue.d.ts +3 -3
- package/dist/packages/primitives/pagination/src/Pagination.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/pagination/src/Pagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/pagination/src/index.d.ts +1 -1
- package/dist/packages/primitives/popover/src/Popover.js.map +1 -1
- package/dist/packages/primitives/popover/src/Popover.vue.d.ts.map +1 -1
- package/dist/packages/primitives/popover/src/Popover.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/popover/src/Popover.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/progress/src/Progress.vue.d.ts +2 -2
- package/dist/packages/primitives/progress/src/index.d.ts +1 -1
- package/dist/packages/primitives/radial-progress/src/RadialProgress.vue.d.ts +2 -2
- package/dist/packages/primitives/radial-progress/src/index.d.ts +1 -1
- package/dist/packages/primitives/radio-group/src/RadioGroup.js.map +1 -1
- package/dist/packages/primitives/radio-group/src/RadioGroup.vue.d.ts.map +1 -1
- package/dist/packages/primitives/radio-group/src/RadioGroup.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/radio-group/src/RadioGroup.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/rating/src/Rating.vue.d.ts +2 -2
- package/dist/packages/primitives/rating/src/index.d.ts +1 -1
- package/dist/packages/primitives/segmented-control/src/SegmentedControl.js.map +1 -1
- package/dist/packages/primitives/segmented-control/src/SegmentedControl.vue.d.ts.map +1 -1
- package/dist/packages/primitives/segmented-control/src/SegmentedControl.vue_vue_type_script_setup_true_lang.js +2 -3
- package/dist/packages/primitives/segmented-control/src/SegmentedControl.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/select/src/Select.js.map +1 -1
- package/dist/packages/primitives/select/src/Select.vue.d.ts.map +1 -1
- package/dist/packages/primitives/select/src/Select.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/select/src/Select.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/slider/src/Slider.js.map +1 -1
- package/dist/packages/primitives/slider/src/Slider.vue.d.ts +6 -3
- package/dist/packages/primitives/slider/src/Slider.vue.d.ts.map +1 -1
- package/dist/packages/primitives/slider/src/Slider.vue_vue_type_script_setup_true_lang.js +15 -10
- package/dist/packages/primitives/slider/src/Slider.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/stepper/src/Stepper.vue.d.ts +2 -2
- package/dist/packages/primitives/stepper/src/index.d.ts +1 -1
- package/dist/packages/primitives/switch/src/Switch.js.map +1 -1
- package/dist/packages/primitives/switch/src/Switch.vue.d.ts +1 -1
- package/dist/packages/primitives/switch/src/Switch.vue.d.ts.map +1 -1
- package/dist/packages/primitives/switch/src/Switch.vue_vue_type_script_setup_true_lang.js +3 -3
- package/dist/packages/primitives/switch/src/Switch.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/table/src/Table.js +7 -0
- package/dist/packages/primitives/table/src/Table.js.map +1 -0
- package/dist/packages/primitives/table/src/Table.vue.d.ts +45 -0
- package/dist/packages/primitives/table/src/Table.vue_vue_type_script_setup_true_lang.js +72 -0
- package/dist/packages/primitives/table/src/Table.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/table/src/TableCell.js +7 -0
- package/dist/packages/primitives/table/src/TableCell.js.map +1 -0
- package/dist/packages/primitives/table/src/TableCell.vue.d.ts +19 -0
- package/dist/packages/primitives/table/src/TableCell.vue.d.ts.map +1 -0
- package/dist/packages/primitives/table/src/TableCell.vue_vue_type_script_setup_true_lang.js +17 -0
- package/dist/packages/primitives/table/src/TableCell.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/table/src/TableHead.js +7 -0
- package/dist/packages/primitives/table/src/TableHead.js.map +1 -0
- package/dist/packages/primitives/table/src/TableHead.vue.d.ts +56 -0
- package/dist/packages/primitives/table/src/TableHead.vue_vue_type_script_setup_true_lang.js +79 -0
- package/dist/packages/primitives/table/src/TableHead.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/table/src/TableRow.js +7 -0
- package/dist/packages/primitives/table/src/TableRow.js.map +1 -0
- package/dist/packages/primitives/table/src/TableRow.vue.d.ts +39 -0
- package/dist/packages/primitives/table/src/TableRow.vue.d.ts.map +1 -0
- package/dist/packages/primitives/table/src/TableRow.vue_vue_type_script_setup_true_lang.js +61 -0
- package/dist/packages/primitives/table/src/TableRow.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/table/src/index.d.ts +10 -0
- package/dist/packages/primitives/table/src/index.js +8 -0
- package/dist/packages/primitives/tabs/src/Tabs.js.map +1 -1
- package/dist/packages/primitives/tabs/src/Tabs.vue.d.ts.map +1 -1
- package/dist/packages/primitives/tabs/src/Tabs.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/tabs/src/Tabs.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/tags-input/src/TagsInput.js.map +1 -1
- package/dist/packages/primitives/tags-input/src/TagsInput.vue.d.ts +2 -2
- package/dist/packages/primitives/tags-input/src/TagsInput.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/tags-input/src/TagsInput.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/tags-input/src/index.d.ts +1 -1
- package/dist/packages/primitives/text-field/src/TextField.js.map +1 -1
- package/dist/packages/primitives/text-field/src/TextField.vue.d.ts +2 -2
- package/dist/packages/primitives/text-field/src/TextField.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/text-field/src/TextField.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/text-field/src/index.d.ts +1 -1
- package/dist/packages/primitives/text-field/src/index.js +2 -2
- package/dist/packages/primitives/textarea/src/Textarea.js.map +1 -1
- package/dist/packages/primitives/textarea/src/Textarea.vue.d.ts +2 -2
- package/dist/packages/primitives/textarea/src/Textarea.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/textarea/src/Textarea.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/textarea/src/index.d.ts +1 -1
- package/dist/packages/primitives/textarea/src/index.js +2 -2
- package/dist/packages/primitives/time-picker/src/TimePicker.vue.d.ts +2 -2
- package/dist/packages/primitives/timeline/src/Timeline.js +7 -0
- package/dist/packages/primitives/timeline/src/Timeline.js.map +1 -0
- package/dist/packages/primitives/timeline/src/Timeline.vue.d.ts +37 -0
- package/dist/packages/primitives/timeline/src/Timeline.vue_vue_type_script_setup_true_lang.js +44 -0
- package/dist/packages/primitives/timeline/src/Timeline.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/timeline/src/TimelineItem.js +7 -0
- package/dist/packages/primitives/timeline/src/TimelineItem.js.map +1 -0
- package/dist/packages/primitives/timeline/src/TimelineItem.vue.d.ts +50 -0
- package/dist/packages/primitives/timeline/src/TimelineItem.vue_vue_type_script_setup_true_lang.js +96 -0
- package/dist/packages/primitives/timeline/src/TimelineItem.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/packages/primitives/timeline/src/index.d.ts +5 -0
- package/dist/packages/primitives/timeline/src/index.js +5 -0
- package/dist/packages/primitives/toast/src/Toast.vue.d.ts +2 -2
- package/dist/packages/primitives/toast/src/ToastItem.js.map +1 -1
- package/dist/packages/primitives/toast/src/ToastItem.vue.d.ts +2 -2
- package/dist/packages/primitives/toast/src/ToastItem.vue_vue_type_script_setup_true_lang.js +2 -2
- package/dist/packages/primitives/toast/src/ToastItem.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/toast/src/index.d.ts +1 -1
- package/dist/packages/primitives/tooltip/src/Tooltip.js.map +1 -1
- package/dist/packages/primitives/tooltip/src/Tooltip.vue.d.ts.map +1 -1
- package/dist/packages/primitives/tooltip/src/Tooltip.vue_vue_type_script_setup_true_lang.js +1 -1
- package/dist/packages/primitives/tooltip/src/Tooltip.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/packages/primitives/window-controls/src/WindowControls.vue.d.ts +2 -2
- package/dist/packages/primitives/window-controls/src/index.d.ts +1 -1
- package/dist/styles/global.css +59 -0
- package/dist/styles/theme.css +13 -0
- package/dist/theme.js +3 -0
- package/package.json +13 -7
- package/dist/packages/blocks/desktop-app-shell/src/DesktopAppShell.vue.d.ts.map +0 -1
- package/dist/packages/blocks/title-bar/src/TitleBar.vue.d.ts.map +0 -1
- package/dist/packages/blocks/toast-stack/src/ToastStack.vue.d.ts.map +0 -1
- package/dist/packages/composition/scroll-reel/src/ScrollReel.vue.d.ts.map +0 -1
- package/dist/packages/composition/scroll-reel/src/index.d.ts.map +0 -1
- package/dist/packages/loom/src/a11y.d.ts.map +0 -1
- package/dist/packages/loom/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/alert-dialog/src/AlertDialog.vue.d.ts.map +0 -1
- package/dist/packages/primitives/alert-dialog/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/avatar-group/src/AvatarGroup.vue.d.ts.map +0 -1
- package/dist/packages/primitives/avatar-group/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/breadcrumb/src/Breadcrumb.vue.d.ts.map +0 -1
- package/dist/packages/primitives/breadcrumb/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/color-picker/src/ColorPicker.vue.d.ts.map +0 -1
- package/dist/packages/primitives/color-picker/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/combobox/src/Combobox.vue.d.ts.map +0 -1
- package/dist/packages/primitives/combobox/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/date-picker/src/DatePicker.vue.d.ts.map +0 -1
- package/dist/packages/primitives/date-range-picker/src/DateRangePicker.vue.d.ts.map +0 -1
- package/dist/packages/primitives/date-range-picker/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/date-time-picker/src/DateTimePicker.vue.d.ts.map +0 -1
- package/dist/packages/primitives/date-time-range-picker/src/DateTimeRangePicker.vue.d.ts.map +0 -1
- package/dist/packages/primitives/date-time-range-picker/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/dialog/src/Dialog.vue.d.ts.map +0 -1
- package/dist/packages/primitives/dialog/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/drawer/src/Drawer.vue.d.ts.map +0 -1
- package/dist/packages/primitives/drawer/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/editable/src/Editable.vue.d.ts.map +0 -1
- package/dist/packages/primitives/editable/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/file-upload/src/FileUpload.vue.d.ts.map +0 -1
- package/dist/packages/primitives/file-upload/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/indicator/src/Indicator.vue.d.ts.map +0 -1
- package/dist/packages/primitives/indicator/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/number-field/src/NumberField.vue.d.ts.map +0 -1
- package/dist/packages/primitives/number-field/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/otp-input/src/OtpInput.vue.d.ts.map +0 -1
- package/dist/packages/primitives/otp-input/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/pagination/src/Pagination.vue.d.ts.map +0 -1
- package/dist/packages/primitives/pagination/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/progress/src/Progress.vue.d.ts.map +0 -1
- package/dist/packages/primitives/progress/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/radial-progress/src/RadialProgress.vue.d.ts.map +0 -1
- package/dist/packages/primitives/radial-progress/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/rating/src/Rating.vue.d.ts.map +0 -1
- package/dist/packages/primitives/rating/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/stepper/src/Stepper.vue.d.ts.map +0 -1
- package/dist/packages/primitives/stepper/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/tags-input/src/TagsInput.vue.d.ts.map +0 -1
- package/dist/packages/primitives/tags-input/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/text-field/src/TextField.vue.d.ts.map +0 -1
- package/dist/packages/primitives/text-field/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/textarea/src/Textarea.vue.d.ts.map +0 -1
- package/dist/packages/primitives/textarea/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/time-picker/src/TimePicker.vue.d.ts.map +0 -1
- package/dist/packages/primitives/toast/src/Toast.vue.d.ts.map +0 -1
- package/dist/packages/primitives/toast/src/ToastItem.vue.d.ts.map +0 -1
- package/dist/packages/primitives/toast/src/index.d.ts.map +0 -1
- package/dist/packages/primitives/window-controls/src/WindowControls.vue.d.ts.map +0 -1
- package/dist/packages/primitives/window-controls/src/index.d.ts.map +0 -1
- package/dist/styles/theme.contrast.test.ts +0 -460
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkbox.js","names":[],"sources":["../../../../../packages/primitives/checkbox/src/Checkbox.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { CheckboxIndicator, CheckboxRoot } from \"reka-ui\";\nimport { Check, Minus } from \"@lucide/vue\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { optional } from \"@ecoma-io/loom-core\";\nimport { useSplitAttrs } from \"@ecoma-io/loom-core\";\nimport { useFieldControl } from \"@ecoma-io/loom-labels\";\n\n/**\n * Checkbox — a single boolean choice inside a form (as opposed to Switch,\n * which flips a setting immediately, no form/\"Save\" involved). Built on Reka\n * UI's Checkbox (role=\"checkbox\" + aria-checked, Space toggles); also\n * supports the third \"indeterminate\" state for a parent checkbox over a\n * partially-selected group — shown as a dash instead of a check.\n *\n * An inline `label` wraps the control in a real `<label>` (a11y name comes\n * free); omit it and pass `ariaLabel`/`ariaLabelledby` instead when the\n * visible label lives elsewhere (e.g. a table header cell).\n *\n * Inside a [Field](../Field/Field.vue) it wires itself through\n * `useFieldControl()`. CheckboxRoot renders a labelable `<button\n * role=\"checkbox\">`, so the row's id lands on the box and the row's\n * `<label for>` names it, with the row's description, `name`, `required` and\n * `invalid` arriving alongside. **Name it in one place.** A `label` here *and*\n * a label on the row gives one control two accessible names, read one after\n * the other, and nothing reports it — not axe, which has no rule for a name\n * that is merely doubled.\n *\n * There is no `readonly`, and a row's is ignored rather than approximated. A\n * checkbox that may be read but not toggled is a disabled checkbox: the native\n * `readonly` attribute is a no-op on `<input type=\"checkbox\">` for exactly that\n * reason, and painting one as editable-but-filled would promise an edit that\n * never lands.\n */\nconst props = withDefaults(\n defineProps<{\n /** `true`/`false` for a plain toggle, `\"indeterminate\"` for a parent over a partially-selected group. Omit to let the box own its own state. */\n modelValue?: boolean | \"indeterminate\" | undefined;\n /** Blocks both click and keyboard input, and dims the box. Unset defers to a wrapping Field. */\n disabled?: boolean | undefined;\n /** Visible text, rendered inside the wrapping `<label>`. Omit and use `ariaLabel`/`ariaLabelledby` when the label lives elsewhere. */\n label?: string;\n /** Accessible name when no visible `label` is rendered by this component. */\n ariaLabel?: string;\n /** Accessible name sourced from another element's id, when no visible `label` is rendered by this component. */\n ariaLabelledby?: string;\n }>(),\n {\n // Not `false`. A concrete value here would mean \"the host owns this\", and\n // a host that passes nothing would get a box that emits its update event\n // and never moves — a control that looks broken rather than one that\n // reports an error. `undefined` is what leaves it self-managing, and the\n // explicit default is what survives Vue's absent-Boolean-casts-to-false\n // rule.\n modelValue: undefined,\n // Not `false` either, and for the second half of the same reason: absent\n // has to stay tellable from `false` all the way into `useFieldControl()`,\n // or a Field above could never disable the row while `:disabled=\"false\"`\n // could never overrule one that did.\n disabled: undefined,\n },\n);\n\ndefineEmits<{ \"update:modelValue\": [value: boolean | \"indeterminate\"] }>();\n\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest: boxAttrs } = useSplitAttrs();\n\n// Routed by hand because nothing was routing it: the two branches and the\n// comment between them make this component's root a *fragment*, and Vue never\n// auto-inherits onto one — it warns and drops the attributes. So `id`,\n// `data-testid` and a caller's `class` all went nowhere until this split.\n// `class` now lands on whichever branch's root renders (a caller sizes the\n// labelled row, or the bare box) and everything else on the CheckboxRoot in\n// both, which is where a row's id has to be: `<label for>` resolves to a\n// labelable element, and the `<button role=\"checkbox\">` is the one here.\n//\n// `field.attrs` spreads **after** `boxAttrs` — see TextField.vue for the full\n// reasoning. The bag has already resolved the caller's own id and merged their\n// description with the row's, and it drops every key that resolved to nothing,\n// so it can only add.\n//\n// `name`, `required` and `invalid` are absent from the bag below because this\n// component has no prop for any of them; the row's answer then stands\n// unopposed, which is what wrapping it in a Field asked for. `readonly` is\n// absent for the different reason the docblock gives.\n//\n// `required` is then the one resolved value that cannot ride in on\n// `field.attrs`: CheckboxRoot renders `aria-required` itself, from its own\n// prop, *after* it spreads the fallthrough attrs — so an `aria-required` in\n// that bag is overwritten by a `false` and the row is silently unheard. It goes\n// through `:required` on the element below instead. That prop also reaches the\n// hidden input Reka mints inside a `<form>`, so unlike TextField this control\n// does carry native enforcement with it; the alternative is a checkbox that\n// tells a screen reader it is optional while its row's asterisk says otherwise.\nconst field = useFieldControl(() => ({\n id: attrs.id as string | undefined,\n describedBy: attrs[\"aria-describedby\"] as string | undefined,\n disabled: props.disabled,\n}));\n\nconst boxClass = cn(\n \"flex h-4 w-4 shrink-0 items-center justify-center rounded-sm border border-input bg-background\",\n // Transform (the press squish) rides --ease-spring for a restrained release,\n // while fill/border stay on the instant --ease-out — the \"springy transform,\n // steady color\" split (boxStyle below), same language as Button/Switch.\n \"active:scale-90\",\n // Checked = a decision (Loom law): the box fills with primary.\n \"data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground\",\n \"data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground\",\n // Focus draws the weave tight: the brand ring blooms.\n \"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo\",\n \"disabled:cursor-not-allowed\",\n);\n\nconst boxStyle =\n \"transition: transform var(--duration-fast) var(--ease-spring), background-color var(--duration-instant) var(--ease-out), border-color var(--duration-instant) var(--ease-out), box-shadow var(--duration-fast) var(--ease-out);\";\n</script>\n\n<template>\n <!-- CheckboxRoot is a labelable <button role=\"checkbox\">, so wrapping it in\n <label> names it exactly like a native control; the rule's nesting/id\n heuristic just doesn't recognize reka-ui's custom element as a\n \"control\". -->\n <!-- eslint-disable-next-line vuejs-accessibility/label-has-for -->\n <label\n v-if=\"label\"\n :class=\"\n cn(\n 'inline-flex min-h-6 items-center gap-2 text-sm text-foreground',\n // Disabled text stays readable (AA on the light ground) — only the box dims.\n field.disabled ? 'cursor-not-allowed text-muted-foreground' : 'cursor-pointer',\n attrs.class as string,\n )\n \"\n >\n <CheckboxRoot\n v-bind=\"{ ...optional({ modelValue }), ...boxAttrs, ...field.attrs }\"\n :disabled=\"field.disabled\"\n :required=\"field.required\"\n :class=\"cn(boxClass, 'disabled:opacity-50')\"\n :style=\"boxStyle\"\n @update:model-value=\"$emit('update:modelValue', $event)\"\n >\n <template #default=\"{ state }\">\n <!-- The tick pops in with scale-in — a small causal \"it's checked now\".\n stroke-width 2.5 is the ≤12px iconography rule, not a local taste:\n the inherited 1.5 renders 0.75 device px at 12px and reads washed\n out inside the filled box. Both branches carry it identically. -->\n <CheckboxIndicator class=\"flex animate-scale-in items-center justify-center text-current\">\n <Minus v-if=\"state === 'indeterminate'\" class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n <Check v-else class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n </CheckboxIndicator>\n </template>\n </CheckboxRoot>\n {{ label }}\n </label>\n <!-- The control always dims itself when disabled; label text stays\n readable muted — same stance in both branches. -->\n <CheckboxRoot\n v-else\n v-bind=\"{ ...optional({ modelValue }), ...boxAttrs, ...field.attrs }\"\n :disabled=\"field.disabled\"\n :required=\"field.required\"\n :aria-label=\"ariaLabel\"\n :aria-labelledby=\"ariaLabelledby\"\n :class=\"cn(boxClass, 'disabled:opacity-50', 'min-h-6 min-w-6', attrs.class as string)\"\n :style=\"boxStyle\"\n @update:model-value=\"$emit('update:modelValue', $event)\"\n >\n <template #default=\"{ state }\">\n <CheckboxIndicator class=\"flex animate-scale-in items-center justify-center text-current\">\n <Minus v-if=\"state === 'indeterminate'\" class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n <Check v-else class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n </CheckboxIndicator>\n </template>\n </CheckboxRoot>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"Checkbox.js","names":[],"sources":["../../../../../packages/primitives/checkbox/src/Checkbox.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { CheckboxIndicator, CheckboxRoot } from \"reka-ui\";\nimport { Check, Minus } from \"@lucide/vue\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { optional } from \"@ecoma-io/loom-core\";\nimport { useSplitAttrs } from \"@ecoma-io/loom-core\";\nimport { useFieldControl } from \"@ecoma-io/loom-labels\";\n\n/**\n * Checkbox — a single boolean choice inside a form (as opposed to Switch,\n * which flips a setting immediately, no form/\"Save\" involved). Built on Reka\n * UI's Checkbox (role=\"checkbox\" + aria-checked, Space toggles); also\n * supports the third \"indeterminate\" state for a parent checkbox over a\n * partially-selected group — shown as a dash instead of a check.\n *\n * An inline `label` wraps the control in a real `<label>` (a11y name comes\n * free); omit it and pass `ariaLabel`/`ariaLabelledby` instead when the\n * visible label lives elsewhere (e.g. a table header cell).\n *\n * Inside a [Field](../Field/Field.vue) it wires itself through\n * `useFieldControl()`. CheckboxRoot renders a labelable `<button\n * role=\"checkbox\">`, so the row's id lands on the box and the row's\n * `<label for>` names it, with the row's description, `name`, `required` and\n * `invalid` arriving alongside. **Name it in one place.** A `label` here *and*\n * a label on the row gives one control two accessible names, read one after\n * the other, and nothing reports it — not axe, which has no rule for a name\n * that is merely doubled.\n *\n * There is no `readonly`, and a row's is ignored rather than approximated. A\n * checkbox that may be read but not toggled is a disabled checkbox: the native\n * `readonly` attribute is a no-op on `<input type=\"checkbox\">` for exactly that\n * reason, and painting one as editable-but-filled would promise an edit that\n * never lands.\n */\nconst props = withDefaults(\n defineProps<{\n /** `true`/`false` for a plain toggle, `\"indeterminate\"` for a parent over a partially-selected group. Omit to let the box own its own state. */\n modelValue?: boolean | \"indeterminate\" | undefined;\n /** Blocks both click and keyboard input, and drains the box to the neutral well. Unset defers to a wrapping Field. */\n disabled?: boolean | undefined;\n /** Visible text, rendered inside the wrapping `<label>`. Omit and use `ariaLabel`/`ariaLabelledby` when the label lives elsewhere. */\n label?: string;\n /** Accessible name when no visible `label` is rendered by this component. */\n ariaLabel?: string;\n /** Accessible name sourced from another element's id, when no visible `label` is rendered by this component. */\n ariaLabelledby?: string;\n }>(),\n {\n // Not `false`. A concrete value here would mean \"the host owns this\", and\n // a host that passes nothing would get a box that emits its update event\n // and never moves — a control that looks broken rather than one that\n // reports an error. `undefined` is what leaves it self-managing, and the\n // explicit default is what survives Vue's absent-Boolean-casts-to-false\n // rule.\n modelValue: undefined,\n // Not `false` either, and for the second half of the same reason: absent\n // has to stay tellable from `false` all the way into `useFieldControl()`,\n // or a Field above could never disable the row while `:disabled=\"false\"`\n // could never overrule one that did.\n disabled: undefined,\n },\n);\n\ndefineEmits<{ \"update:modelValue\": [value: boolean | \"indeterminate\"] }>();\n\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest: boxAttrs } = useSplitAttrs();\n\n// Routed by hand because nothing was routing it: the two branches and the\n// comment between them make this component's root a *fragment*, and Vue never\n// auto-inherits onto one — it warns and drops the attributes. So `id`,\n// `data-testid` and a caller's `class` all went nowhere until this split.\n// `class` now lands on whichever branch's root renders (a caller sizes the\n// labelled row, or the bare box) and everything else on the CheckboxRoot in\n// both, which is where a row's id has to be: `<label for>` resolves to a\n// labelable element, and the `<button role=\"checkbox\">` is the one here.\n//\n// `field.attrs` spreads **after** `boxAttrs` — see TextField.vue for the full\n// reasoning. The bag has already resolved the caller's own id and merged their\n// description with the row's, and it drops every key that resolved to nothing,\n// so it can only add.\n//\n// `name`, `required` and `invalid` are absent from the bag below because this\n// component has no prop for any of them; the row's answer then stands\n// unopposed, which is what wrapping it in a Field asked for. `readonly` is\n// absent for the different reason the docblock gives.\n//\n// `required` is then the one resolved value that cannot ride in on\n// `field.attrs`: CheckboxRoot renders `aria-required` itself, from its own\n// prop, *after* it spreads the fallthrough attrs — so an `aria-required` in\n// that bag is overwritten by a `false` and the row is silently unheard. It goes\n// through `:required` on the element below instead. That prop also reaches the\n// hidden input Reka mints inside a `<form>`, so unlike TextField this control\n// does carry native enforcement with it; the alternative is a checkbox that\n// tells a screen reader it is optional while its row's asterisk says otherwise.\nconst field = useFieldControl(() => ({\n id: attrs.id as string | undefined,\n describedBy: attrs[\"aria-describedby\"] as string | undefined,\n disabled: props.disabled,\n}));\n\nconst boxClass = cn(\n \"flex h-4 w-4 shrink-0 items-center justify-center rounded-sm border border-input bg-background\",\n // Transform (the press squish) rides --ease-spring for a restrained release,\n // while fill/border stay on the instant --ease-out — the \"springy transform,\n // steady color\" split (boxStyle below), same language as Button/Switch.\n \"active:scale-90\",\n // Checked = a decision (Loom law): the box fills with primary.\n \"data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground\",\n \"data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground\",\n // Focus draws the weave tight: the brand ring blooms.\n \"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo\",\n \"disabled:cursor-not-allowed\",\n // Drained, not dimmed. `opacity-50` faded box and border together — the\n // failure Button records for its own dim, and no smaller for holding a\n // glyph rather than words. An unchecked box falls to `bg-muted` with the\n // rim every unavailable control slackens to; a checked or indeterminate\n // one keeps its primary fill untouched, because switched-on is information\n // — the same verdict Switch's thumb and RadioGroup's dot render, so a\n // selected control reads as selected whether it is available or not, and\n // the native `disabled` attribute carries unavailability regardless. The\n // compound `disabled:data-[state=unchecked]:` form is deliberate: Tailwind\n // sorts data variants after plain `disabled:`, so a bare `disabled:bg-muted`\n // would lose to `data-[state=checked]:bg-primary` and never fire.\n \"disabled:data-[state=unchecked]:border-border disabled:data-[state=unchecked]:bg-muted\",\n);\n\nconst boxStyle =\n \"transition: transform var(--duration-fast) var(--ease-spring), background-color var(--duration-instant) var(--ease-out), border-color var(--duration-instant) var(--ease-out), box-shadow var(--duration-fast) var(--ease-out);\";\n</script>\n\n<template>\n <!-- CheckboxRoot is a labelable <button role=\"checkbox\">, so wrapping it in\n <label> names it exactly like a native control; the rule's nesting/id\n heuristic just doesn't recognize reka-ui's custom element as a\n \"control\". -->\n <!-- eslint-disable-next-line vuejs-accessibility/label-has-for -->\n <label\n v-if=\"label\"\n :class=\"\n cn(\n 'inline-flex min-h-6 items-center gap-2 text-sm text-foreground',\n // Disabled text stays readable (AA on the light ground) — only the box drains.\n field.disabled ? 'cursor-not-allowed text-muted-foreground' : 'cursor-pointer',\n attrs.class as string,\n )\n \"\n >\n <CheckboxRoot\n v-bind=\"{ ...optional({ modelValue }), ...boxAttrs, ...field.attrs }\"\n :disabled=\"field.disabled\"\n :required=\"field.required\"\n :class=\"cn(boxClass)\"\n :style=\"boxStyle\"\n @update:model-value=\"$emit('update:modelValue', $event)\"\n >\n <template #default=\"{ state }\">\n <!-- The tick pops in with scale-in — a small causal \"it's checked now\".\n stroke-width 2.5 is the ≤12px iconography rule, not a local taste:\n the inherited 1.5 renders 0.75 device px at 12px and reads washed\n out inside the filled box. Both branches carry it identically. -->\n <CheckboxIndicator class=\"flex animate-scale-in items-center justify-center text-current\">\n <Minus v-if=\"state === 'indeterminate'\" class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n <Check v-else class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n </CheckboxIndicator>\n </template>\n </CheckboxRoot>\n {{ label }}\n </label>\n <!-- The control always drains itself when disabled; label text stays\n readable muted — same stance in both branches. -->\n <CheckboxRoot\n v-else\n v-bind=\"{ ...optional({ modelValue }), ...boxAttrs, ...field.attrs }\"\n :disabled=\"field.disabled\"\n :required=\"field.required\"\n :aria-label=\"ariaLabel\"\n :aria-labelledby=\"ariaLabelledby\"\n :class=\"cn(boxClass, 'min-h-6 min-w-6', attrs.class as string)\"\n :style=\"boxStyle\"\n @update:model-value=\"$emit('update:modelValue', $event)\"\n >\n <template #default=\"{ state }\">\n <CheckboxIndicator class=\"flex animate-scale-in items-center justify-center text-current\">\n <Minus v-if=\"state === 'indeterminate'\" class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n <Check v-else class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n </CheckboxIndicator>\n </template>\n </CheckboxRoot>\n</template>\n"],"mappings":""}
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
type __VLS_Props = {
|
|
28
28
|
/** `true`/`false` for a plain toggle, `"indeterminate"` for a parent over a partially-selected group. Omit to let the box own its own state. */
|
|
29
29
|
modelValue?: boolean | "indeterminate" | undefined;
|
|
30
|
-
/** Blocks both click and keyboard input, and
|
|
30
|
+
/** Blocks both click and keyboard input, and drains the box to the neutral well. Unset defers to a wrapping Field. */
|
|
31
31
|
disabled?: boolean | undefined;
|
|
32
32
|
/** Visible text, rendered inside the wrapping `<label>`. Omit and use `ariaLabel`/`ariaLabelledby` when the label lives elsewhere. */
|
|
33
33
|
label?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkbox.vue.d.ts","sourceRoot":"","sources":["../../../../../packages/primitives/checkbox/src/Checkbox.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Checkbox.vue.d.ts","sourceRoot":"","sources":["../../../../../packages/primitives/checkbox/src/Checkbox.vue"],"names":[],"mappings":"AAwMA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,KAAK,WAAW,GAAG;IACf,gJAAgJ;IAChJ,UAAU,CAAC,EAAE,OAAO,GAAG,eAAe,GAAG,SAAS,CAAC;IACnD,sHAAsH;IACtH,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC/B,sIAAsI;IACtI,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gHAAgH;IAChH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAmVJ,QAAA,MAAM,YAAY;;;;;cA1VH,OAAO,GAAG,SAAS;gBAFjB,OAAO,GAAG,eAAe,GAAG,SAAS;6EAgWpD,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
|
package/dist/packages/primitives/checkbox/src/Checkbox.vue_vue_type_script_setup_true_lang.js
CHANGED
|
@@ -60,7 +60,7 @@ var Checkbox_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineC
|
|
|
60
60
|
describedBy: attrs["aria-describedby"],
|
|
61
61
|
disabled: props.disabled
|
|
62
62
|
}));
|
|
63
|
-
const boxClass = cn("flex h-4 w-4 shrink-0 items-center justify-center rounded-sm border border-input bg-background", "active:scale-90", "data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground", "data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground", "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo", "disabled:cursor-not-allowed");
|
|
63
|
+
const boxClass = cn("flex h-4 w-4 shrink-0 items-center justify-center rounded-sm border border-input bg-background", "active:scale-90", "data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground", "data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground", "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo", "disabled:cursor-not-allowed", "disabled:data-[state=unchecked]:border-border disabled:data-[state=unchecked]:bg-muted");
|
|
64
64
|
return (_ctx, _cache) => {
|
|
65
65
|
return __props.label ? (openBlock(), createElementBlock("label", {
|
|
66
66
|
key: 0,
|
|
@@ -72,7 +72,7 @@ var Checkbox_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineC
|
|
|
72
72
|
}, {
|
|
73
73
|
disabled: unref(field).disabled,
|
|
74
74
|
required: unref(field).required,
|
|
75
|
-
class: unref(cn)(unref(boxClass)
|
|
75
|
+
class: unref(cn)(unref(boxClass)),
|
|
76
76
|
style: boxStyle,
|
|
77
77
|
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => _ctx.$emit("update:modelValue", $event))
|
|
78
78
|
}), {
|
|
@@ -102,7 +102,7 @@ var Checkbox_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineC
|
|
|
102
102
|
required: unref(field).required,
|
|
103
103
|
"aria-label": __props.ariaLabel,
|
|
104
104
|
"aria-labelledby": __props.ariaLabelledby,
|
|
105
|
-
class: unref(cn)(unref(boxClass), "
|
|
105
|
+
class: unref(cn)(unref(boxClass), "min-h-6 min-w-6", unref(attrs).class),
|
|
106
106
|
style: boxStyle,
|
|
107
107
|
"onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => _ctx.$emit("update:modelValue", $event))
|
|
108
108
|
}), {
|
package/dist/packages/primitives/checkbox/src/Checkbox.vue_vue_type_script_setup_true_lang.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkbox.vue_vue_type_script_setup_true_lang.js","names":["$emit"],"sources":["../../../../../packages/primitives/checkbox/src/Checkbox.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { CheckboxIndicator, CheckboxRoot } from \"reka-ui\";\nimport { Check, Minus } from \"@lucide/vue\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { optional } from \"@ecoma-io/loom-core\";\nimport { useSplitAttrs } from \"@ecoma-io/loom-core\";\nimport { useFieldControl } from \"@ecoma-io/loom-labels\";\n\n/**\n * Checkbox — a single boolean choice inside a form (as opposed to Switch,\n * which flips a setting immediately, no form/\"Save\" involved). Built on Reka\n * UI's Checkbox (role=\"checkbox\" + aria-checked, Space toggles); also\n * supports the third \"indeterminate\" state for a parent checkbox over a\n * partially-selected group — shown as a dash instead of a check.\n *\n * An inline `label` wraps the control in a real `<label>` (a11y name comes\n * free); omit it and pass `ariaLabel`/`ariaLabelledby` instead when the\n * visible label lives elsewhere (e.g. a table header cell).\n *\n * Inside a [Field](../Field/Field.vue) it wires itself through\n * `useFieldControl()`. CheckboxRoot renders a labelable `<button\n * role=\"checkbox\">`, so the row's id lands on the box and the row's\n * `<label for>` names it, with the row's description, `name`, `required` and\n * `invalid` arriving alongside. **Name it in one place.** A `label` here *and*\n * a label on the row gives one control two accessible names, read one after\n * the other, and nothing reports it — not axe, which has no rule for a name\n * that is merely doubled.\n *\n * There is no `readonly`, and a row's is ignored rather than approximated. A\n * checkbox that may be read but not toggled is a disabled checkbox: the native\n * `readonly` attribute is a no-op on `<input type=\"checkbox\">` for exactly that\n * reason, and painting one as editable-but-filled would promise an edit that\n * never lands.\n */\nconst props = withDefaults(\n defineProps<{\n /** `true`/`false` for a plain toggle, `\"indeterminate\"` for a parent over a partially-selected group. Omit to let the box own its own state. */\n modelValue?: boolean | \"indeterminate\" | undefined;\n /** Blocks both click and keyboard input, and dims the box. Unset defers to a wrapping Field. */\n disabled?: boolean | undefined;\n /** Visible text, rendered inside the wrapping `<label>`. Omit and use `ariaLabel`/`ariaLabelledby` when the label lives elsewhere. */\n label?: string;\n /** Accessible name when no visible `label` is rendered by this component. */\n ariaLabel?: string;\n /** Accessible name sourced from another element's id, when no visible `label` is rendered by this component. */\n ariaLabelledby?: string;\n }>(),\n {\n // Not `false`. A concrete value here would mean \"the host owns this\", and\n // a host that passes nothing would get a box that emits its update event\n // and never moves — a control that looks broken rather than one that\n // reports an error. `undefined` is what leaves it self-managing, and the\n // explicit default is what survives Vue's absent-Boolean-casts-to-false\n // rule.\n modelValue: undefined,\n // Not `false` either, and for the second half of the same reason: absent\n // has to stay tellable from `false` all the way into `useFieldControl()`,\n // or a Field above could never disable the row while `:disabled=\"false\"`\n // could never overrule one that did.\n disabled: undefined,\n },\n);\n\ndefineEmits<{ \"update:modelValue\": [value: boolean | \"indeterminate\"] }>();\n\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest: boxAttrs } = useSplitAttrs();\n\n// Routed by hand because nothing was routing it: the two branches and the\n// comment between them make this component's root a *fragment*, and Vue never\n// auto-inherits onto one — it warns and drops the attributes. So `id`,\n// `data-testid` and a caller's `class` all went nowhere until this split.\n// `class` now lands on whichever branch's root renders (a caller sizes the\n// labelled row, or the bare box) and everything else on the CheckboxRoot in\n// both, which is where a row's id has to be: `<label for>` resolves to a\n// labelable element, and the `<button role=\"checkbox\">` is the one here.\n//\n// `field.attrs` spreads **after** `boxAttrs` — see TextField.vue for the full\n// reasoning. The bag has already resolved the caller's own id and merged their\n// description with the row's, and it drops every key that resolved to nothing,\n// so it can only add.\n//\n// `name`, `required` and `invalid` are absent from the bag below because this\n// component has no prop for any of them; the row's answer then stands\n// unopposed, which is what wrapping it in a Field asked for. `readonly` is\n// absent for the different reason the docblock gives.\n//\n// `required` is then the one resolved value that cannot ride in on\n// `field.attrs`: CheckboxRoot renders `aria-required` itself, from its own\n// prop, *after* it spreads the fallthrough attrs — so an `aria-required` in\n// that bag is overwritten by a `false` and the row is silently unheard. It goes\n// through `:required` on the element below instead. That prop also reaches the\n// hidden input Reka mints inside a `<form>`, so unlike TextField this control\n// does carry native enforcement with it; the alternative is a checkbox that\n// tells a screen reader it is optional while its row's asterisk says otherwise.\nconst field = useFieldControl(() => ({\n id: attrs.id as string | undefined,\n describedBy: attrs[\"aria-describedby\"] as string | undefined,\n disabled: props.disabled,\n}));\n\nconst boxClass = cn(\n \"flex h-4 w-4 shrink-0 items-center justify-center rounded-sm border border-input bg-background\",\n // Transform (the press squish) rides --ease-spring for a restrained release,\n // while fill/border stay on the instant --ease-out — the \"springy transform,\n // steady color\" split (boxStyle below), same language as Button/Switch.\n \"active:scale-90\",\n // Checked = a decision (Loom law): the box fills with primary.\n \"data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground\",\n \"data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground\",\n // Focus draws the weave tight: the brand ring blooms.\n \"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo\",\n \"disabled:cursor-not-allowed\",\n);\n\nconst boxStyle =\n \"transition: transform var(--duration-fast) var(--ease-spring), background-color var(--duration-instant) var(--ease-out), border-color var(--duration-instant) var(--ease-out), box-shadow var(--duration-fast) var(--ease-out);\";\n</script>\n\n<template>\n <!-- CheckboxRoot is a labelable <button role=\"checkbox\">, so wrapping it in\n <label> names it exactly like a native control; the rule's nesting/id\n heuristic just doesn't recognize reka-ui's custom element as a\n \"control\". -->\n <!-- eslint-disable-next-line vuejs-accessibility/label-has-for -->\n <label\n v-if=\"label\"\n :class=\"\n cn(\n 'inline-flex min-h-6 items-center gap-2 text-sm text-foreground',\n // Disabled text stays readable (AA on the light ground) — only the box dims.\n field.disabled ? 'cursor-not-allowed text-muted-foreground' : 'cursor-pointer',\n attrs.class as string,\n )\n \"\n >\n <CheckboxRoot\n v-bind=\"{ ...optional({ modelValue }), ...boxAttrs, ...field.attrs }\"\n :disabled=\"field.disabled\"\n :required=\"field.required\"\n :class=\"cn(boxClass, 'disabled:opacity-50')\"\n :style=\"boxStyle\"\n @update:model-value=\"$emit('update:modelValue', $event)\"\n >\n <template #default=\"{ state }\">\n <!-- The tick pops in with scale-in — a small causal \"it's checked now\".\n stroke-width 2.5 is the ≤12px iconography rule, not a local taste:\n the inherited 1.5 renders 0.75 device px at 12px and reads washed\n out inside the filled box. Both branches carry it identically. -->\n <CheckboxIndicator class=\"flex animate-scale-in items-center justify-center text-current\">\n <Minus v-if=\"state === 'indeterminate'\" class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n <Check v-else class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n </CheckboxIndicator>\n </template>\n </CheckboxRoot>\n {{ label }}\n </label>\n <!-- The control always dims itself when disabled; label text stays\n readable muted — same stance in both branches. -->\n <CheckboxRoot\n v-else\n v-bind=\"{ ...optional({ modelValue }), ...boxAttrs, ...field.attrs }\"\n :disabled=\"field.disabled\"\n :required=\"field.required\"\n :aria-label=\"ariaLabel\"\n :aria-labelledby=\"ariaLabelledby\"\n :class=\"cn(boxClass, 'disabled:opacity-50', 'min-h-6 min-w-6', attrs.class as string)\"\n :style=\"boxStyle\"\n @update:model-value=\"$emit('update:modelValue', $event)\"\n >\n <template #default=\"{ state }\">\n <CheckboxIndicator class=\"flex animate-scale-in items-center justify-center text-current\">\n <Minus v-if=\"state === 'indeterminate'\" class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n <Check v-else class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n </CheckboxIndicator>\n </template>\n </CheckboxRoot>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmHA,IAAM,WACJ;;;;;;;;;;;;;;;;;;;EAlFF,MAAM,QAAQ;EAgCd,MAAM,EAAE,OAAO,MAAM,aAAa,cAAc;EA6BhD,MAAM,QAAQ,uBAAuB;GACnC,IAAI,MAAM;GACV,aAAa,MAAM;GACnB,UAAU,MAAM;EAClB,EAAE;EAEF,MAAM,WAAW,GACf,kGAIA,mBAEA,oHACA,sIAEA,+GACA,6BACF;;GAaU,OAAA,QAAA,SADR,UAAA,GAAA,mBA+BQ,SAAA;;IA7BL,OAAK,eAAS,MAAA,EAAA,CAAE,CAAA,kEAA0K,MAAA,KAAA,CAAK,CAAC,WAAQ,6CAAA,kBAA0E,MAAA,KAAA,CAAK,CAAC,KAAA,CAAA;GASzR,GAAA,CAAA,YAkBe,MAAA,YAAA,GAlBf,WAkBe;IAAA,GAjBA,MAAA,QAAA,CAAQ,CAAA,EAAA,YAAG,QAAA,WAAU,CAAA;IAAA,GAAQ,MAAA,QAAA;IAAQ,GAAK,MAAA,KAAA,CAAK,CAAC;GAAK,GAAA;IACjE,UAAU,MAAA,KAAA,CAAK,CAAC;IAChB,UAAU,MAAA,KAAA,CAAK,CAAC;IAChB,OAAO,MAAA,EAAA,CAAE,CAAC,MAAA,QAAA,GAAQ,qBAAA;IAClB,OAAO;IACP,uBAAkB,OAAA,OAAA,OAAA,MAAA,WAAEA,KAAAA,MAAK,qBAAsB,MAAM;;IAE3C,SAAO,SAQI,EARA,YAAK,CAKzB,YAGoB,MAAA,iBAAA,GAAA,EAHD,OAAM,iEAAgE,GAAA;KACvF,SAAA,cAA8E,CAAjE,UAAK,mBAAlB,UAAA,GAAA,YAA8E,MAAA,KAAA,GAAA;;MAAtC,OAAM;MAAW,gBAAc;KACvE,CAAA,MAAA,UAAA,GAAA,YAAoD,MAAA,KAAA,GAAA;;MAAtC,OAAM;MAAW,gBAAc;;;;;;;;;GAGpC,CAAA,GAAA,gBAAA,MACf,gBAAG,QAAA,KAAK,GAAA,CAAA,CAAA,GAAA,CAAA,MAIV,UAAA,GAAA,YAiBe,MAAA,YAAA,GAjBf,WAiBe,EAAA,KAAA,EAAA,GAAA;IAAA,GAfA,MAAA,QAAA,CAAQ,CAAA,EAAA,YAAG,QAAA,WAAU,CAAA;IAAA,GAAQ,MAAA,QAAA;IAAQ,GAAK,MAAA,KAAA,CAAK,CAAC;GAAK,GAAA;IACjE,UAAU,MAAA,KAAA,CAAK,CAAC;IAChB,UAAU,MAAA,KAAA,CAAK,CAAC;IAChB,cAAY,QAAA;IACZ,mBAAiB,QAAA;IACjB,OAAO,MAAA,EAAA,CAAE,CAAC,MAAA,QAAA,GAAQ,uBAAA,mBAA4C,MAAA,KAAA,CAAK,CAAC,KAAK;IACzE,OAAO;IACP,uBAAkB,OAAA,OAAA,OAAA,MAAA,WAAEA,KAAAA,MAAK,qBAAsB,MAAM;;IAE3C,SAAO,SAII,EAJA,YAAK,CACzB,YAGoB,MAAA,iBAAA,GAAA,EAHD,OAAM,iEAAgE,GAAA;KACvF,SAAA,cAA8E,CAAjE,UAAK,mBAAlB,UAAA,GAAA,YAA8E,MAAA,KAAA,GAAA;;MAAtC,OAAM;MAAW,gBAAc;KACvE,CAAA,MAAA,UAAA,GAAA,YAAoD,MAAA,KAAA,GAAA;;MAAtC,OAAM;MAAW,gBAAc"}
|
|
1
|
+
{"version":3,"file":"Checkbox.vue_vue_type_script_setup_true_lang.js","names":["$emit"],"sources":["../../../../../packages/primitives/checkbox/src/Checkbox.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { CheckboxIndicator, CheckboxRoot } from \"reka-ui\";\nimport { Check, Minus } from \"@lucide/vue\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { optional } from \"@ecoma-io/loom-core\";\nimport { useSplitAttrs } from \"@ecoma-io/loom-core\";\nimport { useFieldControl } from \"@ecoma-io/loom-labels\";\n\n/**\n * Checkbox — a single boolean choice inside a form (as opposed to Switch,\n * which flips a setting immediately, no form/\"Save\" involved). Built on Reka\n * UI's Checkbox (role=\"checkbox\" + aria-checked, Space toggles); also\n * supports the third \"indeterminate\" state for a parent checkbox over a\n * partially-selected group — shown as a dash instead of a check.\n *\n * An inline `label` wraps the control in a real `<label>` (a11y name comes\n * free); omit it and pass `ariaLabel`/`ariaLabelledby` instead when the\n * visible label lives elsewhere (e.g. a table header cell).\n *\n * Inside a [Field](../Field/Field.vue) it wires itself through\n * `useFieldControl()`. CheckboxRoot renders a labelable `<button\n * role=\"checkbox\">`, so the row's id lands on the box and the row's\n * `<label for>` names it, with the row's description, `name`, `required` and\n * `invalid` arriving alongside. **Name it in one place.** A `label` here *and*\n * a label on the row gives one control two accessible names, read one after\n * the other, and nothing reports it — not axe, which has no rule for a name\n * that is merely doubled.\n *\n * There is no `readonly`, and a row's is ignored rather than approximated. A\n * checkbox that may be read but not toggled is a disabled checkbox: the native\n * `readonly` attribute is a no-op on `<input type=\"checkbox\">` for exactly that\n * reason, and painting one as editable-but-filled would promise an edit that\n * never lands.\n */\nconst props = withDefaults(\n defineProps<{\n /** `true`/`false` for a plain toggle, `\"indeterminate\"` for a parent over a partially-selected group. Omit to let the box own its own state. */\n modelValue?: boolean | \"indeterminate\" | undefined;\n /** Blocks both click and keyboard input, and drains the box to the neutral well. Unset defers to a wrapping Field. */\n disabled?: boolean | undefined;\n /** Visible text, rendered inside the wrapping `<label>`. Omit and use `ariaLabel`/`ariaLabelledby` when the label lives elsewhere. */\n label?: string;\n /** Accessible name when no visible `label` is rendered by this component. */\n ariaLabel?: string;\n /** Accessible name sourced from another element's id, when no visible `label` is rendered by this component. */\n ariaLabelledby?: string;\n }>(),\n {\n // Not `false`. A concrete value here would mean \"the host owns this\", and\n // a host that passes nothing would get a box that emits its update event\n // and never moves — a control that looks broken rather than one that\n // reports an error. `undefined` is what leaves it self-managing, and the\n // explicit default is what survives Vue's absent-Boolean-casts-to-false\n // rule.\n modelValue: undefined,\n // Not `false` either, and for the second half of the same reason: absent\n // has to stay tellable from `false` all the way into `useFieldControl()`,\n // or a Field above could never disable the row while `:disabled=\"false\"`\n // could never overrule one that did.\n disabled: undefined,\n },\n);\n\ndefineEmits<{ \"update:modelValue\": [value: boolean | \"indeterminate\"] }>();\n\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest: boxAttrs } = useSplitAttrs();\n\n// Routed by hand because nothing was routing it: the two branches and the\n// comment between them make this component's root a *fragment*, and Vue never\n// auto-inherits onto one — it warns and drops the attributes. So `id`,\n// `data-testid` and a caller's `class` all went nowhere until this split.\n// `class` now lands on whichever branch's root renders (a caller sizes the\n// labelled row, or the bare box) and everything else on the CheckboxRoot in\n// both, which is where a row's id has to be: `<label for>` resolves to a\n// labelable element, and the `<button role=\"checkbox\">` is the one here.\n//\n// `field.attrs` spreads **after** `boxAttrs` — see TextField.vue for the full\n// reasoning. The bag has already resolved the caller's own id and merged their\n// description with the row's, and it drops every key that resolved to nothing,\n// so it can only add.\n//\n// `name`, `required` and `invalid` are absent from the bag below because this\n// component has no prop for any of them; the row's answer then stands\n// unopposed, which is what wrapping it in a Field asked for. `readonly` is\n// absent for the different reason the docblock gives.\n//\n// `required` is then the one resolved value that cannot ride in on\n// `field.attrs`: CheckboxRoot renders `aria-required` itself, from its own\n// prop, *after* it spreads the fallthrough attrs — so an `aria-required` in\n// that bag is overwritten by a `false` and the row is silently unheard. It goes\n// through `:required` on the element below instead. That prop also reaches the\n// hidden input Reka mints inside a `<form>`, so unlike TextField this control\n// does carry native enforcement with it; the alternative is a checkbox that\n// tells a screen reader it is optional while its row's asterisk says otherwise.\nconst field = useFieldControl(() => ({\n id: attrs.id as string | undefined,\n describedBy: attrs[\"aria-describedby\"] as string | undefined,\n disabled: props.disabled,\n}));\n\nconst boxClass = cn(\n \"flex h-4 w-4 shrink-0 items-center justify-center rounded-sm border border-input bg-background\",\n // Transform (the press squish) rides --ease-spring for a restrained release,\n // while fill/border stay on the instant --ease-out — the \"springy transform,\n // steady color\" split (boxStyle below), same language as Button/Switch.\n \"active:scale-90\",\n // Checked = a decision (Loom law): the box fills with primary.\n \"data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground\",\n \"data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground\",\n // Focus draws the weave tight: the brand ring blooms.\n \"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo\",\n \"disabled:cursor-not-allowed\",\n // Drained, not dimmed. `opacity-50` faded box and border together — the\n // failure Button records for its own dim, and no smaller for holding a\n // glyph rather than words. An unchecked box falls to `bg-muted` with the\n // rim every unavailable control slackens to; a checked or indeterminate\n // one keeps its primary fill untouched, because switched-on is information\n // — the same verdict Switch's thumb and RadioGroup's dot render, so a\n // selected control reads as selected whether it is available or not, and\n // the native `disabled` attribute carries unavailability regardless. The\n // compound `disabled:data-[state=unchecked]:` form is deliberate: Tailwind\n // sorts data variants after plain `disabled:`, so a bare `disabled:bg-muted`\n // would lose to `data-[state=checked]:bg-primary` and never fire.\n \"disabled:data-[state=unchecked]:border-border disabled:data-[state=unchecked]:bg-muted\",\n);\n\nconst boxStyle =\n \"transition: transform var(--duration-fast) var(--ease-spring), background-color var(--duration-instant) var(--ease-out), border-color var(--duration-instant) var(--ease-out), box-shadow var(--duration-fast) var(--ease-out);\";\n</script>\n\n<template>\n <!-- CheckboxRoot is a labelable <button role=\"checkbox\">, so wrapping it in\n <label> names it exactly like a native control; the rule's nesting/id\n heuristic just doesn't recognize reka-ui's custom element as a\n \"control\". -->\n <!-- eslint-disable-next-line vuejs-accessibility/label-has-for -->\n <label\n v-if=\"label\"\n :class=\"\n cn(\n 'inline-flex min-h-6 items-center gap-2 text-sm text-foreground',\n // Disabled text stays readable (AA on the light ground) — only the box drains.\n field.disabled ? 'cursor-not-allowed text-muted-foreground' : 'cursor-pointer',\n attrs.class as string,\n )\n \"\n >\n <CheckboxRoot\n v-bind=\"{ ...optional({ modelValue }), ...boxAttrs, ...field.attrs }\"\n :disabled=\"field.disabled\"\n :required=\"field.required\"\n :class=\"cn(boxClass)\"\n :style=\"boxStyle\"\n @update:model-value=\"$emit('update:modelValue', $event)\"\n >\n <template #default=\"{ state }\">\n <!-- The tick pops in with scale-in — a small causal \"it's checked now\".\n stroke-width 2.5 is the ≤12px iconography rule, not a local taste:\n the inherited 1.5 renders 0.75 device px at 12px and reads washed\n out inside the filled box. Both branches carry it identically. -->\n <CheckboxIndicator class=\"flex animate-scale-in items-center justify-center text-current\">\n <Minus v-if=\"state === 'indeterminate'\" class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n <Check v-else class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n </CheckboxIndicator>\n </template>\n </CheckboxRoot>\n {{ label }}\n </label>\n <!-- The control always drains itself when disabled; label text stays\n readable muted — same stance in both branches. -->\n <CheckboxRoot\n v-else\n v-bind=\"{ ...optional({ modelValue }), ...boxAttrs, ...field.attrs }\"\n :disabled=\"field.disabled\"\n :required=\"field.required\"\n :aria-label=\"ariaLabel\"\n :aria-labelledby=\"ariaLabelledby\"\n :class=\"cn(boxClass, 'min-h-6 min-w-6', attrs.class as string)\"\n :style=\"boxStyle\"\n @update:model-value=\"$emit('update:modelValue', $event)\"\n >\n <template #default=\"{ state }\">\n <CheckboxIndicator class=\"flex animate-scale-in items-center justify-center text-current\">\n <Minus v-if=\"state === 'indeterminate'\" class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n <Check v-else class=\"h-3 w-3\" :stroke-width=\"2.5\" />\n </CheckboxIndicator>\n </template>\n </CheckboxRoot>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+HA,IAAM,WACJ;;;;;;;;;;;;;;;;;;;EA9FF,MAAM,QAAQ;EAgCd,MAAM,EAAE,OAAO,MAAM,aAAa,cAAc;EA6BhD,MAAM,QAAQ,uBAAuB;GACnC,IAAI,MAAM;GACV,aAAa,MAAM;GACnB,UAAU,MAAM;EAClB,EAAE;EAEF,MAAM,WAAW,GACf,kGAIA,mBAEA,oHACA,sIAEA,+GACA,+BAYA,wFACF;;GAaU,OAAA,QAAA,SADR,UAAA,GAAA,mBA+BQ,SAAA;;IA7BL,OAAK,eAAS,MAAA,EAAA,CAAE,CAAA,kEAA4K,MAAA,KAAA,CAAK,CAAC,WAAQ,6CAAA,kBAA0E,MAAA,KAAA,CAAK,CAAC,KAAA,CAAA;GAS3R,GAAA,CAAA,YAkBe,MAAA,YAAA,GAlBf,WAkBe;IAAA,GAjBA,MAAA,QAAA,CAAQ,CAAA,EAAA,YAAG,QAAA,WAAU,CAAA;IAAA,GAAQ,MAAA,QAAA;IAAQ,GAAK,MAAA,KAAA,CAAK,CAAC;GAAK,GAAA;IACjE,UAAU,MAAA,KAAA,CAAK,CAAC;IAChB,UAAU,MAAA,KAAA,CAAK,CAAC;IAChB,OAAO,MAAA,EAAA,CAAE,CAAC,MAAA,QAAA,CAAQ;IAClB,OAAO;IACP,uBAAkB,OAAA,OAAA,OAAA,MAAA,WAAEA,KAAAA,MAAK,qBAAsB,MAAM;;IAE3C,SAAO,SAQI,EARA,YAAK,CAKzB,YAGoB,MAAA,iBAAA,GAAA,EAHD,OAAM,iEAAgE,GAAA;KACvF,SAAA,cAA8E,CAAjE,UAAK,mBAAlB,UAAA,GAAA,YAA8E,MAAA,KAAA,GAAA;;MAAtC,OAAM;MAAW,gBAAc;KACvE,CAAA,MAAA,UAAA,GAAA,YAAoD,MAAA,KAAA,GAAA;;MAAtC,OAAM;MAAW,gBAAc;;;;;;;;;GAGpC,CAAA,GAAA,gBAAA,MACf,gBAAG,QAAA,KAAK,GAAA,CAAA,CAAA,GAAA,CAAA,MAIV,UAAA,GAAA,YAiBe,MAAA,YAAA,GAjBf,WAiBe,EAAA,KAAA,EAAA,GAAA;IAAA,GAfA,MAAA,QAAA,CAAQ,CAAA,EAAA,YAAG,QAAA,WAAU,CAAA;IAAA,GAAQ,MAAA,QAAA;IAAQ,GAAK,MAAA,KAAA,CAAK,CAAC;GAAK,GAAA;IACjE,UAAU,MAAA,KAAA,CAAK,CAAC;IAChB,UAAU,MAAA,KAAA,CAAK,CAAC;IAChB,cAAY,QAAA;IACZ,mBAAiB,QAAA;IACjB,OAAO,MAAA,EAAA,CAAE,CAAC,MAAA,QAAA,GAAQ,mBAAqB,MAAA,KAAA,CAAK,CAAC,KAAK;IAClD,OAAO;IACP,uBAAkB,OAAA,OAAA,OAAA,MAAA,WAAEA,KAAAA,MAAK,qBAAsB,MAAM;;IAE3C,SAAO,SAII,EAJA,YAAK,CACzB,YAGoB,MAAA,iBAAA,GAAA,EAHD,OAAM,iEAAgE,GAAA;KACvF,SAAA,cAA8E,CAAjE,UAAK,mBAAlB,UAAA,GAAA,YAA8E,MAAA,KAAA,GAAA;;MAAtC,OAAM;MAAW,gBAAc;KACvE,CAAA,MAAA,UAAA,GAAA,YAAoD,MAAA,KAAA,GAAA;;MAAtC,OAAM;MAAW,gBAAc"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Chip.js","names":[],"sources":["../../../../../packages/primitives/chip/src/Chip.vue"],"sourcesContent":["<script lang=\"ts\">\nimport { cva } from \"class-variance-authority\";\n\n/**\n * The same eight names Badge carries, deliberately — a chip and a badge sitting\n * in one row are the same object in two moods, so `success` has to mean the\n * same green in both. A ninth name here would be a second vocabulary for one\n * palette.\n */\nexport type ChipVariant =\n \"neutral\" | \"outline\" | \"primary\" | \"success\" | \"warning\" | \"info\" | \"destructive\" | \"accent\";\n\n/**\n * Two heights off the shared control scale (the text input's `sm` and `md`), so\n * a row of chips above a field lines up with it. There is no `lg`: a chip is a\n * token, and one big enough to need the 44px step is a Button.\n */\nexport type ChipSize = \"sm\" | \"md\";\n\n/**\n * The container. It is the coloured pill, and it is deliberately not the\n * interactive node — see the docblock below for why that separation is the\n * whole component.\n *\n * Selection is a state the *edges* deepen: the hue-carrying variants draw a\n * stronger border around the wash they already sit in, the hueless ones swap\n * their fill for the primary wash. It is never any one signal alone that says\n * so — every selected chip also carries `aria-pressed=\"true\"` and a check\n * glyph, which is what keeps the state legible to a reader who cannot resolve\n * a deepened edge from a resting one.\n */\nexport const chipVariants = cva(\n [\n \"group inline-flex max-w-full items-center rounded-full border align-middle font-medium\",\n \"transition-[background-color,border-color,color] duration-fast ease-out\",\n // No `opacity` here, and that is the point. `data-[disabled]:opacity-50`\n // dimmed the pill *and* its label together, taking a 5.01:1 label down to\n // 2.78:1 against its own fill — halving the alpha more than halves the\n // contrast. Unavailability is painted in measured colours instead: the\n // fill and border below, and `disabledText` on the label and the glyph\n // beside it.\n \"data-[disabled]:cursor-not-allowed\",\n ],\n {\n // `satisfies` keeps the two halves honest: the union above is the name a\n // consumer imports and the documentation prints, the map below is the\n // classes. Adding a member to one and not the other stops compiling.\n variants: {\n variant: {\n // The two hueless variants take the primary wash when selected —\n // `--color-primary-muted` is the repository's selected/active fill, the\n // one Select's checked row already uses, so a chosen filter reads as a\n // human decision rather than as a new colour invented here.\n neutral:\n \"border-transparent bg-subtle text-subtle-foreground data-[selected]:border-primary/40 data-[selected]:bg-primary-muted data-[selected]:text-primary-text\",\n outline:\n \"border-border bg-transparent text-muted-foreground data-[selected]:border-primary/40 data-[selected]:bg-primary-muted data-[selected]:text-primary-text\",\n // The hue-carrying variants deepen their own hue instead of jumping to\n // the primary: a reader picked this chip *because* it is the destructive\n // one, and swapping its colour on selection would throw that away.\n // The tinted washes use the opaque `*-muted` tokens, not an alpha of\n // the base hue: `bg-*-/12` composites against the surface behind it, so\n // a chip renders at 4.46:1 on `bg-card` and differently on `bg-sunken`\n // — under the AA floor and inconsistent. `*-muted` is mixed to hold\n // ≥4.5:1 against its own `*-text` in both themes (see theme.css),\n // which is what these variants were named for.\n //\n // Selection deepens the border, not the fill: a `data-[selected]:bg-*-/20`\n // over the wash drops the text to ~4.2:1 in light mode (success, warning,\n // info, destructive), below the same floor the unselected state holds.\n // `*-muted` is the theme's \"selected/active fill\" anyway, so the state is\n // carried by the deeper border, the check glyph and `aria-pressed` — the\n // signals the docblock below already names as what makes selection legible.\n primary:\n \"border-transparent bg-primary-muted text-primary-text data-[selected]:border-primary/45\",\n success:\n \"border-transparent bg-success-muted text-success-text data-[selected]:border-success/45\",\n warning:\n \"border-transparent bg-warning-muted text-warning data-[selected]:border-warning/45\",\n info: \"border-transparent bg-info-muted text-info-text data-[selected]:border-info/45\",\n destructive:\n \"border-transparent bg-destructive-muted text-destructive-text data-[selected]:border-destructive/45\",\n accent:\n \"border-accent/40 bg-accent-muted text-accent-text data-[selected]:border-accent/70\",\n } satisfies Record<ChipVariant, string>,\n size: {\n sm: \"h-8 text-xs\",\n md: \"h-9 text-sm\",\n } satisfies Record<ChipSize, string>,\n },\n defaultVariants: { variant: \"neutral\", size: \"md\" },\n },\n);\n</script>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { Check, X } from \"@lucide/vue\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { useSplitAttrs } from \"@ecoma-io/loom-core\";\n\n/**\n * Chip — an interactive token: a filter that switches on, an entity that can be\n * dismissed, a recipient sitting in a picker.\n *\n * The line against Badge is the one worth knowing, because the two look almost\n * identical: **Badge is a status label a reader only looks at; Chip is a\n * control a reader acts on.** A Badge that gained a click handler would be a\n * Chip, and a Chip nobody can toggle or dismiss is a Badge wearing a button's\n * clothes — it takes a Tab stop and announces itself as pressable while doing\n * nothing. Reach for Button when the token *is* the action rather than a\n * standing state, and for SegmentedControl when exactly one of a small set must\n * be chosen; a row of chips is many-of-many, and none-of-them is a valid answer.\n *\n * The structure is the component. A chip that both toggles and dismisses has\n * two controls, and they are **siblings inside a non-interactive container** —\n * never a dismiss `<button>` nested inside a toggle `<button>`. Nested buttons\n * are invalid HTML: the parser reparents them, and screen readers disagree\n * about which one they are on and what a press means. So the pill is a `<span>`\n * that paints, and everything pressable is a child of it.\n *\n * Both controls stay on the natural Tab order rather than roving. They are two\n * different actions on one object, not N instances of one control, and a reader\n * who tabbed to the chip has to be able to reach \"remove\" without guessing an\n * arrow key.\n */\nconst props = withDefaults(\n defineProps<{\n /** Whether the chip is switched on. Binding it is what makes the chip a toggle — leave it unset for a chip that is only dismissable, and the label renders as inert text instead of a press target. */\n selected?: boolean | undefined;\n /** Renders a dismiss control beside the label, as a sibling of the toggle rather than inside it. */\n removable?: boolean;\n /** The accessible name of the dismiss control. Localise it, or qualify it — \"Remove Ana Duarte\" reads far better in a list of recipients. */\n removeLabel?: string;\n /** Unavailable: drains the pill to a neutral fill, mutes the label, and makes both controls inert to pointer and keyboard. */\n disabled?: boolean;\n /** Which status or meta the token carries. The names and colours are Badge's, so a chip and a badge never disagree. */\n variant?: ChipVariant;\n /** Chip height, off the shared control scale so a row of chips lines up with the field above it. */\n size?: ChipSize;\n }>(),\n {\n // Not `false`. `undefined` is the signal that this chip is not a toggle at\n // all, and Vue casts an absent Boolean prop to `false` unless a default\n // says otherwise — without this line every chip would claim to be a toggle\n // that is switched off, and a dismiss-only chip would grow a dead Tab stop.\n selected: undefined,\n removable: false,\n removeLabel: \"Remove\",\n disabled: false,\n variant: \"neutral\",\n size: \"md\",\n },\n);\n\nconst emit = defineEmits<{\n /** The flipped selection. The chip does not update itself: a filter chip's state is the query it stands for, and a chip that switched on locally would keep claiming a result set the host never fetched. */\n \"update:selected\": [value: boolean];\n /** The dismiss control was activated. Removing the chip from the list is the host's to do. */\n remove: [];\n}>();\n\nconst toggleable = computed(() => props.selected !== undefined);\n\n// The label's horizontal padding, shared by both branches below so the pill is\n// the same width whether or not it is pressable. The dismiss control supplies\n// its own trailing room, so the label gives its right side back when one is\n// present.\nconst labelPad = computed(() =>\n cn(props.size === \"sm\" ? \"px-2.5\" : \"px-3\", props.removable && \"pr-1\"),\n);\n\n// Springy transform, steady colour — the same split Button and Checkbox use, so\n// a chip press feels like every other press in the library.\nconst pressStyle =\n \"transition: transform var(--duration-fast) var(--ease-spring), background-color var(--duration-fast) var(--ease-out);\";\n\n// The unavailable label colour, and it belongs on the label rather than on the\n// pill around it. A colour set on the container is only *inherited* here, and\n// the container already carries `data-[selected]:text-primary-text` — an attribute\n// selector no plain class on the same element outranks. On the label itself it\n// is a declaration rather than an inheritance, so it wins whatever the pill\n// resolved to: 4.67:1 over the drained `--color-muted` fill, 4.77:1 over the\n// primary wash a selected chip keeps.\nconst disabledText = \"group-data-[disabled]:text-muted-foreground\";\n\n// Both inner controls are surfaces *inside* a 32px pill, so the standard\n// outward `outline-offset-2` would draw the toggle's ring straight through the\n// dismiss control sitting 4px to its right, and vice versa. Inset instead —\n// the ring lands just inside the pill's own border, which is where the eye\n// already is. WindowControls takes the same offset for the same reason.\nconst focusRing =\n \"focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo\";\n\n// Everything a host passes lands on the container: it is the node with an\n// identity (a data-testid, a key in a list), while the two controls inside it\n// are named by their own content and by `removeLabel`. `class` is pulled out of\n// that spread and merged through `cn()` instead — Vue's own class merge only\n// concatenates, so a caller's `max-w-32` would win or lose against the pill's\n// own utilities depending on Tailwind's emission order rather than on intent.\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest: containerAttrs } = useSplitAttrs();\n</script>\n\n<template>\n <span\n v-bind=\"containerAttrs\"\n :data-selected=\"selected || undefined\"\n :data-disabled=\"disabled || undefined\"\n :class=\"\n cn(\n chipVariants({ variant, size }),\n // Drained to the neutral well, with the hairline the coloured variants\n // hide: an unavailable chip gives up its hue rather than its legibility.\n // A *selected* one keeps the primary wash — `data-[selected]:bg-primary-muted`\n // outranks a plain class — which is right: switched-on is information,\n // and the label is muted over either fill.\n //\n // `border-border` and not the strong weight it used to take.\n // `theme.css` reserves the strong hairline for a block that must\n // *assert* its edge, which is the opposite of what this state says, and\n // the lighter rim is the same one every other unavailable control in the\n // library slackens to — so a disabled chip in a row of disabled\n // controls is one treatment rather than a second.\n disabled && 'border-border bg-muted',\n removable && 'pr-1',\n attrs.class as string,\n )\n \"\n >\n <button\n v-if=\"toggleable\"\n type=\"button\"\n :aria-pressed=\"selected\"\n :disabled=\"disabled\"\n :style=\"pressStyle\"\n :class=\"\n cn(\n 'inline-flex h-full min-w-0 select-none items-center gap-1 rounded-full',\n labelPad,\n // A neutral darkening rather than a token fill: the pill underneath\n // can be any of eight colours, and a hover state is not an action\n // colour.\n 'hover:bg-foreground/5 active:scale-press',\n focusRing,\n 'disabled:pointer-events-none',\n )\n \"\n @click=\"emit('update:selected', !selected)\"\n >\n <!-- The check is the half of the selected signal that survives a reader\n who cannot tell a 12% wash from a 20% one; `aria-pressed` is the\n other half. It collapses its own width rather than unmounting, so\n the pill grows into the glyph instead of snapping. -->\n <span\n aria-hidden=\"true\"\n class=\"inline-flex w-0 shrink-0 items-center overflow-hidden opacity-0 transition-[width,opacity] duration-fast ease-spring group-data-[selected]:w-4 group-data-[selected]:opacity-100\"\n >\n <Check class=\"h-3.5 w-3.5 shrink-0\" />\n </span>\n <span :class=\"cn('truncate', disabledText)\">\n <!-- @slot The chip's label. Keep it to a word or two — a token that needs a sentence is a row, not a chip. -->\n <slot />\n </span>\n </button>\n <span v-else :class=\"cn('inline-flex min-w-0 items-center', labelPad)\">\n <span :class=\"cn('truncate', disabledText)\"><slot /></span>\n </span>\n\n <!-- A sibling of the toggle, never a child of it. 24px square is the floor\n WCAG 2.2 SC 2.5.8 sets for a target, which is what decides the size\n here rather than the glyph's own 14px. -->\n <button\n v-if=\"removable\"\n type=\"button\"\n :aria-label=\"removeLabel\"\n :disabled=\"disabled\"\n :style=\"pressStyle\"\n :class=\"\n cn(\n 'inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-full',\n // Not `hover:bg-subtle`, the usual neutral well: it is opaque, and\n // over a coloured pill it punches a grey hole through the fill\n // instead of darkening it.\n 'hover:bg-foreground/10 active:scale-90',\n focusRing,\n disabledText,\n 'disabled:pointer-events-none',\n )\n \"\n @click=\"emit('remove')\"\n >\n <X class=\"h-3.5 w-3.5\" />\n </button>\n </span>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"Chip.js","names":[],"sources":["../../../../../packages/primitives/chip/src/Chip.vue"],"sourcesContent":["<script lang=\"ts\">\nimport { cva } from \"class-variance-authority\";\n\n/**\n * The same eight names Badge carries, deliberately — a chip and a badge sitting\n * in one row are the same object in two moods, so `success` has to mean the\n * same green in both. A ninth name here would be a second vocabulary for one\n * palette.\n */\nexport type ChipVariant =\n \"neutral\" | \"outline\" | \"primary\" | \"success\" | \"warning\" | \"info\" | \"destructive\" | \"accent\";\n\n/**\n * Two heights off the shared control scale (the text input's `sm` and `md`), so\n * a row of chips above a field lines up with it. There is no `lg`: a chip is a\n * token, and one big enough to need the 44px step is a Button.\n */\nexport type ChipSize = \"sm\" | \"md\";\n\n/**\n * The container. It is the coloured pill, and it is deliberately not the\n * interactive node — see the docblock below for why that separation is the\n * whole component.\n *\n * Selection is a state the *edges* deepen: the hue-carrying variants draw a\n * stronger border around the wash they already sit in, the hueless ones swap\n * their fill for the primary wash. It is never any one signal alone that says\n * so — every selected chip also carries `aria-pressed=\"true\"` and a check\n * glyph, which is what keeps the state legible to a reader who cannot resolve\n * a deepened edge from a resting one.\n */\nexport const chipVariants = cva(\n [\n \"group inline-flex max-w-full items-center rounded-full border align-middle font-medium\",\n \"transition-[background-color,border-color,color] duration-fast ease-out\",\n // No `opacity` here, and that is the point. `data-[disabled]:opacity-50`\n // dimmed the pill *and* its label together, taking a 5.01:1 label down to\n // 2.78:1 against its own fill — halving the alpha more than halves the\n // contrast. Unavailability is painted in measured colours instead: the\n // fill and border below, and `disabledText` on the label and the glyph\n // beside it.\n \"data-[disabled]:cursor-not-allowed\",\n ],\n {\n // `satisfies` keeps the two halves honest: the union above is the name a\n // consumer imports and the documentation prints, the map below is the\n // classes. Adding a member to one and not the other stops compiling.\n variants: {\n variant: {\n // The two hueless variants take the primary wash when selected —\n // `--color-primary-muted` is the repository's selected/active fill, the\n // one Select's checked row already uses, so a chosen filter reads as a\n // human decision rather than as a new colour invented here.\n neutral:\n \"border-transparent bg-subtle text-subtle-foreground data-[selected]:border-primary/40 data-[selected]:bg-primary-muted data-[selected]:text-primary-text\",\n outline:\n \"border-border bg-transparent text-muted-foreground data-[selected]:border-primary/40 data-[selected]:bg-primary-muted data-[selected]:text-primary-text\",\n // The hue-carrying variants deepen their own hue instead of jumping to\n // the primary: a reader picked this chip *because* it is the destructive\n // one, and swapping its colour on selection would throw that away.\n // The tinted washes use the opaque `*-muted` tokens, not an alpha of\n // the base hue: `bg-*-/12` composites against the surface behind it, so\n // a chip renders at 4.46:1 on `bg-card` and differently on `bg-sunken`\n // — under the AA floor and inconsistent. `*-muted` is mixed to hold\n // ≥4.5:1 against its own `*-text` in both themes (see theme.css),\n // which is what these variants were named for.\n //\n // Selection deepens the border, not the fill: a `data-[selected]:bg-*-/20`\n // over the wash drops the text to ~4.2:1 in light mode (success, warning,\n // info, destructive), below the same floor the unselected state holds.\n // `*-muted` is the theme's \"selected/active fill\" anyway, so the state is\n // carried by the deeper border, the check glyph and `aria-pressed` — the\n // signals the docblock below already names as what makes selection legible.\n primary:\n \"border-transparent bg-primary-muted text-primary-text data-[selected]:border-primary/45\",\n success:\n \"border-transparent bg-success-muted text-success-text data-[selected]:border-success/45\",\n // `-text`, matching Badge and the other functional siblings: the wash\n // is mixed to hold contrast against the `*-text` rung in both themes,\n // not against the base hue.\n warning:\n \"border-transparent bg-warning-muted text-warning-text data-[selected]:border-warning/45\",\n info: \"border-transparent bg-info-muted text-info-text data-[selected]:border-info/45\",\n destructive:\n \"border-transparent bg-destructive-muted text-destructive-text data-[selected]:border-destructive/45\",\n accent:\n \"border-accent/40 bg-accent-muted text-accent-text data-[selected]:border-accent/70\",\n } satisfies Record<ChipVariant, string>,\n size: {\n sm: \"h-8 text-xs\",\n md: \"h-9 text-sm\",\n } satisfies Record<ChipSize, string>,\n },\n defaultVariants: { variant: \"neutral\", size: \"md\" },\n },\n);\n</script>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { Check, X } from \"@lucide/vue\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { useSplitAttrs } from \"@ecoma-io/loom-core\";\n\n/**\n * Chip — an interactive token: a filter that switches on, an entity that can be\n * dismissed, a recipient sitting in a picker.\n *\n * The line against Badge is the one worth knowing, because the two look almost\n * identical: **Badge is a status label a reader only looks at; Chip is a\n * control a reader acts on.** A Badge that gained a click handler would be a\n * Chip, and a Chip nobody can toggle or dismiss is a Badge wearing a button's\n * clothes — it takes a Tab stop and announces itself as pressable while doing\n * nothing. Reach for Button when the token *is* the action rather than a\n * standing state, and for SegmentedControl when exactly one of a small set must\n * be chosen; a row of chips is many-of-many, and none-of-them is a valid answer.\n *\n * The structure is the component. A chip that both toggles and dismisses has\n * two controls, and they are **siblings inside a non-interactive container** —\n * never a dismiss `<button>` nested inside a toggle `<button>`. Nested buttons\n * are invalid HTML: the parser reparents them, and screen readers disagree\n * about which one they are on and what a press means. So the pill is a `<span>`\n * that paints, and everything pressable is a child of it.\n *\n * Both controls stay on the natural Tab order rather than roving. They are two\n * different actions on one object, not N instances of one control, and a reader\n * who tabbed to the chip has to be able to reach \"remove\" without guessing an\n * arrow key.\n */\nconst props = withDefaults(\n defineProps<{\n /** Whether the chip is switched on. Binding it is what makes the chip a toggle — leave it unset for a chip that is only dismissable, and the label renders as inert text instead of a press target. */\n selected?: boolean | undefined;\n /** Renders a dismiss control beside the label, as a sibling of the toggle rather than inside it. */\n removable?: boolean;\n /** The accessible name of the dismiss control. Localise it, or qualify it — \"Remove Ana Duarte\" reads far better in a list of recipients. */\n removeLabel?: string;\n /** Unavailable: drains the pill to a neutral fill, mutes the label, and makes both controls inert to pointer and keyboard. */\n disabled?: boolean;\n /** Which status or meta the token carries. The names and colours are Badge's, so a chip and a badge never disagree. */\n variant?: ChipVariant;\n /** Chip height, off the shared control scale so a row of chips lines up with the field above it. */\n size?: ChipSize;\n }>(),\n {\n // Not `false`. `undefined` is the signal that this chip is not a toggle at\n // all, and Vue casts an absent Boolean prop to `false` unless a default\n // says otherwise — without this line every chip would claim to be a toggle\n // that is switched off, and a dismiss-only chip would grow a dead Tab stop.\n selected: undefined,\n removable: false,\n removeLabel: \"Remove\",\n disabled: false,\n variant: \"neutral\",\n size: \"md\",\n },\n);\n\nconst emit = defineEmits<{\n /** The flipped selection. The chip does not update itself: a filter chip's state is the query it stands for, and a chip that switched on locally would keep claiming a result set the host never fetched. */\n \"update:selected\": [value: boolean];\n /** The dismiss control was activated. Removing the chip from the list is the host's to do. */\n remove: [];\n}>();\n\nconst toggleable = computed(() => props.selected !== undefined);\n\n// The label's horizontal padding, shared by both branches below so the pill is\n// the same width whether or not it is pressable. The dismiss control supplies\n// its own trailing room, so the label gives its right side back when one is\n// present.\nconst labelPad = computed(() =>\n cn(props.size === \"sm\" ? \"px-2.5\" : \"px-3\", props.removable && \"pr-1\"),\n);\n\n// Springy transform, steady colour — the same split Button and Checkbox use, so\n// a chip press feels like every other press in the library.\nconst pressStyle =\n \"transition: transform var(--duration-fast) var(--ease-spring), background-color var(--duration-fast) var(--ease-out);\";\n\n// The unavailable label colour, and it belongs on the label rather than on the\n// pill around it. A colour set on the container is only *inherited* here, and\n// the container already carries `data-[selected]:text-primary-text` — an attribute\n// selector no plain class on the same element outranks. On the label itself it\n// is a declaration rather than an inheritance, so it wins whatever the pill\n// resolved to: 4.67:1 over the drained `--color-muted` fill, 4.77:1 over the\n// primary wash a selected chip keeps.\nconst disabledText = \"group-data-[disabled]:text-muted-foreground\";\n\n// Both inner controls are surfaces *inside* a 32px pill, so the standard\n// outward `outline-offset-2` would draw the toggle's ring straight through the\n// dismiss control sitting 4px to its right, and vice versa. Inset instead —\n// the ring lands just inside the pill's own border, which is where the eye\n// already is. WindowControls takes the same offset for the same reason.\nconst focusRing =\n \"focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo\";\n\n// Everything a host passes lands on the container: it is the node with an\n// identity (a data-testid, a key in a list), while the two controls inside it\n// are named by their own content and by `removeLabel`. `class` is pulled out of\n// that spread and merged through `cn()` instead — Vue's own class merge only\n// concatenates, so a caller's `max-w-32` would win or lose against the pill's\n// own utilities depending on Tailwind's emission order rather than on intent.\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest: containerAttrs } = useSplitAttrs();\n</script>\n\n<template>\n <span\n v-bind=\"containerAttrs\"\n :data-selected=\"selected || undefined\"\n :data-disabled=\"disabled || undefined\"\n :class=\"\n cn(\n chipVariants({ variant, size }),\n // Drained to the neutral well, with the hairline the coloured variants\n // hide: an unavailable chip gives up its hue rather than its legibility.\n // A *selected* one keeps the primary wash — `data-[selected]:bg-primary-muted`\n // outranks a plain class — which is right: switched-on is information,\n // and the label is muted over either fill.\n //\n // `border-border` and not the strong weight it used to take.\n // `theme.css` reserves the strong hairline for a block that must\n // *assert* its edge, which is the opposite of what this state says, and\n // the lighter rim is the same one every other unavailable control in the\n // library slackens to — so a disabled chip in a row of disabled\n // controls is one treatment rather than a second.\n disabled && 'border-border bg-muted',\n removable && 'pr-1',\n attrs.class as string,\n )\n \"\n >\n <button\n v-if=\"toggleable\"\n type=\"button\"\n :aria-pressed=\"selected\"\n :disabled=\"disabled\"\n :style=\"pressStyle\"\n :class=\"\n cn(\n 'inline-flex h-full min-w-0 select-none items-center gap-1 rounded-full',\n labelPad,\n // A neutral darkening rather than a token fill: the pill underneath\n // can be any of eight colours, and a hover state is not an action\n // colour.\n 'hover:bg-foreground/5 active:scale-press',\n focusRing,\n 'disabled:pointer-events-none',\n )\n \"\n @click=\"emit('update:selected', !selected)\"\n >\n <!-- The check is the half of the selected signal that survives a reader\n who cannot tell a 12% wash from a 20% one; `aria-pressed` is the\n other half. It collapses its own width rather than unmounting, so\n the pill grows into the glyph instead of snapping. -->\n <span\n aria-hidden=\"true\"\n class=\"inline-flex w-0 shrink-0 items-center overflow-hidden opacity-0 transition-[width,opacity] duration-fast ease-spring group-data-[selected]:w-4 group-data-[selected]:opacity-100\"\n >\n <Check class=\"h-3.5 w-3.5 shrink-0\" />\n </span>\n <span :class=\"cn('truncate', disabledText)\">\n <!-- @slot The chip's label. Keep it to a word or two — a token that needs a sentence is a row, not a chip. -->\n <slot />\n </span>\n </button>\n <span v-else :class=\"cn('inline-flex min-w-0 items-center', labelPad)\">\n <span :class=\"cn('truncate', disabledText)\"><slot /></span>\n </span>\n\n <!-- A sibling of the toggle, never a child of it. 24px square is the floor\n WCAG 2.2 SC 2.5.8 sets for a target, which is what decides the size\n here rather than the glyph's own 14px. -->\n <button\n v-if=\"removable\"\n type=\"button\"\n :aria-label=\"removeLabel\"\n :disabled=\"disabled\"\n :style=\"pressStyle\"\n :class=\"\n cn(\n 'inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-full',\n // Not `hover:bg-subtle`, the usual neutral well: it is opaque, and\n // over a coloured pill it punches a grey hole through the fill\n // instead of darkening it.\n 'hover:bg-foreground/10 active:scale-90',\n focusRing,\n disabledText,\n 'disabled:pointer-events-none',\n )\n \"\n @click=\"emit('remove')\"\n >\n <X class=\"h-3.5 w-3.5\" />\n </button>\n </span>\n</template>\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Chip.vue.d.ts","sourceRoot":"","sources":["../../../../../packages/primitives/chip/src/Chip.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Chip.vue.d.ts","sourceRoot":"","sources":["../../../../../packages/primitives/chip/src/Chip.vue"],"names":[],"mappings":"AAsTA;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GACrB,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,aAAa,GAAG,QAAQ,CAAC;AAEhG;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG,IAAI,CAAC;AAEnC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,YAAY;;;8EAgExB,CAAC;wBACmB,OAAO,YAAY;AAAxC,wBAAyC;AACzC,QAAA,MAAM,YAAY;IA2Bd,uMAAuM;eAC5L,OAAO,GAAG,SAAS;IAC9B,oGAAoG;gBACxF,OAAO;IACnB,6IAA6I;kBAC/H,MAAM;IACpB,8HAA8H;eACnH,OAAO;IAClB,uHAAuH;cAC7G,WAAW;IACrB,oGAAoG;WAC7F,QAAQ;;;;;IAXf,uMAAuM;eAC5L,OAAO,GAAG,SAAS;IAC9B,oGAAoG;gBACxF,OAAO;IACnB,6IAA6I;kBAC/H,MAAM;IACpB,8HAA8H;eACnH,OAAO;IAClB,uHAAuH;cAC7G,WAAW;IACrB,oGAAoG;WAC7F,QAAQ;;;;;UAAR,QAAQ;cAJJ,OAAO;aAER,WAAW;cARV,OAAO,GAAG,SAAS;eAElB,OAAO;iBAEL,MAAM;;cA0PV,CAAC,KAAK,IAAgB,KAAK,GAAG;;cAC9B,CAAC,KAAK,IAAgB,KAAK,GAAG;EAWxC,CAAC;AAWL,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
|
|
@@ -60,7 +60,7 @@ var chipVariants = cva([
|
|
|
60
60
|
outline: "border-border bg-transparent text-muted-foreground data-[selected]:border-primary/40 data-[selected]:bg-primary-muted data-[selected]:text-primary-text",
|
|
61
61
|
primary: "border-transparent bg-primary-muted text-primary-text data-[selected]:border-primary/45",
|
|
62
62
|
success: "border-transparent bg-success-muted text-success-text data-[selected]:border-success/45",
|
|
63
|
-
warning: "border-transparent bg-warning-muted text-warning data-[selected]:border-warning/45",
|
|
63
|
+
warning: "border-transparent bg-warning-muted text-warning-text data-[selected]:border-warning/45",
|
|
64
64
|
info: "border-transparent bg-info-muted text-info-text data-[selected]:border-info/45",
|
|
65
65
|
destructive: "border-transparent bg-destructive-muted text-destructive-text data-[selected]:border-destructive/45",
|
|
66
66
|
accent: "border-accent/40 bg-accent-muted text-accent-text data-[selected]:border-accent/70"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Chip.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../../../packages/primitives/chip/src/Chip.vue"],"sourcesContent":["<script lang=\"ts\">\nimport { cva } from \"class-variance-authority\";\n\n/**\n * The same eight names Badge carries, deliberately — a chip and a badge sitting\n * in one row are the same object in two moods, so `success` has to mean the\n * same green in both. A ninth name here would be a second vocabulary for one\n * palette.\n */\nexport type ChipVariant =\n \"neutral\" | \"outline\" | \"primary\" | \"success\" | \"warning\" | \"info\" | \"destructive\" | \"accent\";\n\n/**\n * Two heights off the shared control scale (the text input's `sm` and `md`), so\n * a row of chips above a field lines up with it. There is no `lg`: a chip is a\n * token, and one big enough to need the 44px step is a Button.\n */\nexport type ChipSize = \"sm\" | \"md\";\n\n/**\n * The container. It is the coloured pill, and it is deliberately not the\n * interactive node — see the docblock below for why that separation is the\n * whole component.\n *\n * Selection is a state the *edges* deepen: the hue-carrying variants draw a\n * stronger border around the wash they already sit in, the hueless ones swap\n * their fill for the primary wash. It is never any one signal alone that says\n * so — every selected chip also carries `aria-pressed=\"true\"` and a check\n * glyph, which is what keeps the state legible to a reader who cannot resolve\n * a deepened edge from a resting one.\n */\nexport const chipVariants = cva(\n [\n \"group inline-flex max-w-full items-center rounded-full border align-middle font-medium\",\n \"transition-[background-color,border-color,color] duration-fast ease-out\",\n // No `opacity` here, and that is the point. `data-[disabled]:opacity-50`\n // dimmed the pill *and* its label together, taking a 5.01:1 label down to\n // 2.78:1 against its own fill — halving the alpha more than halves the\n // contrast. Unavailability is painted in measured colours instead: the\n // fill and border below, and `disabledText` on the label and the glyph\n // beside it.\n \"data-[disabled]:cursor-not-allowed\",\n ],\n {\n // `satisfies` keeps the two halves honest: the union above is the name a\n // consumer imports and the documentation prints, the map below is the\n // classes. Adding a member to one and not the other stops compiling.\n variants: {\n variant: {\n // The two hueless variants take the primary wash when selected —\n // `--color-primary-muted` is the repository's selected/active fill, the\n // one Select's checked row already uses, so a chosen filter reads as a\n // human decision rather than as a new colour invented here.\n neutral:\n \"border-transparent bg-subtle text-subtle-foreground data-[selected]:border-primary/40 data-[selected]:bg-primary-muted data-[selected]:text-primary-text\",\n outline:\n \"border-border bg-transparent text-muted-foreground data-[selected]:border-primary/40 data-[selected]:bg-primary-muted data-[selected]:text-primary-text\",\n // The hue-carrying variants deepen their own hue instead of jumping to\n // the primary: a reader picked this chip *because* it is the destructive\n // one, and swapping its colour on selection would throw that away.\n // The tinted washes use the opaque `*-muted` tokens, not an alpha of\n // the base hue: `bg-*-/12` composites against the surface behind it, so\n // a chip renders at 4.46:1 on `bg-card` and differently on `bg-sunken`\n // — under the AA floor and inconsistent. `*-muted` is mixed to hold\n // ≥4.5:1 against its own `*-text` in both themes (see theme.css),\n // which is what these variants were named for.\n //\n // Selection deepens the border, not the fill: a `data-[selected]:bg-*-/20`\n // over the wash drops the text to ~4.2:1 in light mode (success, warning,\n // info, destructive), below the same floor the unselected state holds.\n // `*-muted` is the theme's \"selected/active fill\" anyway, so the state is\n // carried by the deeper border, the check glyph and `aria-pressed` — the\n // signals the docblock below already names as what makes selection legible.\n primary:\n \"border-transparent bg-primary-muted text-primary-text data-[selected]:border-primary/45\",\n success:\n \"border-transparent bg-success-muted text-success-text data-[selected]:border-success/45\",\n warning:\n \"border-transparent bg-warning-muted text-warning data-[selected]:border-warning/45\",\n info: \"border-transparent bg-info-muted text-info-text data-[selected]:border-info/45\",\n destructive:\n \"border-transparent bg-destructive-muted text-destructive-text data-[selected]:border-destructive/45\",\n accent:\n \"border-accent/40 bg-accent-muted text-accent-text data-[selected]:border-accent/70\",\n } satisfies Record<ChipVariant, string>,\n size: {\n sm: \"h-8 text-xs\",\n md: \"h-9 text-sm\",\n } satisfies Record<ChipSize, string>,\n },\n defaultVariants: { variant: \"neutral\", size: \"md\" },\n },\n);\n</script>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { Check, X } from \"@lucide/vue\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { useSplitAttrs } from \"@ecoma-io/loom-core\";\n\n/**\n * Chip — an interactive token: a filter that switches on, an entity that can be\n * dismissed, a recipient sitting in a picker.\n *\n * The line against Badge is the one worth knowing, because the two look almost\n * identical: **Badge is a status label a reader only looks at; Chip is a\n * control a reader acts on.** A Badge that gained a click handler would be a\n * Chip, and a Chip nobody can toggle or dismiss is a Badge wearing a button's\n * clothes — it takes a Tab stop and announces itself as pressable while doing\n * nothing. Reach for Button when the token *is* the action rather than a\n * standing state, and for SegmentedControl when exactly one of a small set must\n * be chosen; a row of chips is many-of-many, and none-of-them is a valid answer.\n *\n * The structure is the component. A chip that both toggles and dismisses has\n * two controls, and they are **siblings inside a non-interactive container** —\n * never a dismiss `<button>` nested inside a toggle `<button>`. Nested buttons\n * are invalid HTML: the parser reparents them, and screen readers disagree\n * about which one they are on and what a press means. So the pill is a `<span>`\n * that paints, and everything pressable is a child of it.\n *\n * Both controls stay on the natural Tab order rather than roving. They are two\n * different actions on one object, not N instances of one control, and a reader\n * who tabbed to the chip has to be able to reach \"remove\" without guessing an\n * arrow key.\n */\nconst props = withDefaults(\n defineProps<{\n /** Whether the chip is switched on. Binding it is what makes the chip a toggle — leave it unset for a chip that is only dismissable, and the label renders as inert text instead of a press target. */\n selected?: boolean | undefined;\n /** Renders a dismiss control beside the label, as a sibling of the toggle rather than inside it. */\n removable?: boolean;\n /** The accessible name of the dismiss control. Localise it, or qualify it — \"Remove Ana Duarte\" reads far better in a list of recipients. */\n removeLabel?: string;\n /** Unavailable: drains the pill to a neutral fill, mutes the label, and makes both controls inert to pointer and keyboard. */\n disabled?: boolean;\n /** Which status or meta the token carries. The names and colours are Badge's, so a chip and a badge never disagree. */\n variant?: ChipVariant;\n /** Chip height, off the shared control scale so a row of chips lines up with the field above it. */\n size?: ChipSize;\n }>(),\n {\n // Not `false`. `undefined` is the signal that this chip is not a toggle at\n // all, and Vue casts an absent Boolean prop to `false` unless a default\n // says otherwise — without this line every chip would claim to be a toggle\n // that is switched off, and a dismiss-only chip would grow a dead Tab stop.\n selected: undefined,\n removable: false,\n removeLabel: \"Remove\",\n disabled: false,\n variant: \"neutral\",\n size: \"md\",\n },\n);\n\nconst emit = defineEmits<{\n /** The flipped selection. The chip does not update itself: a filter chip's state is the query it stands for, and a chip that switched on locally would keep claiming a result set the host never fetched. */\n \"update:selected\": [value: boolean];\n /** The dismiss control was activated. Removing the chip from the list is the host's to do. */\n remove: [];\n}>();\n\nconst toggleable = computed(() => props.selected !== undefined);\n\n// The label's horizontal padding, shared by both branches below so the pill is\n// the same width whether or not it is pressable. The dismiss control supplies\n// its own trailing room, so the label gives its right side back when one is\n// present.\nconst labelPad = computed(() =>\n cn(props.size === \"sm\" ? \"px-2.5\" : \"px-3\", props.removable && \"pr-1\"),\n);\n\n// Springy transform, steady colour — the same split Button and Checkbox use, so\n// a chip press feels like every other press in the library.\nconst pressStyle =\n \"transition: transform var(--duration-fast) var(--ease-spring), background-color var(--duration-fast) var(--ease-out);\";\n\n// The unavailable label colour, and it belongs on the label rather than on the\n// pill around it. A colour set on the container is only *inherited* here, and\n// the container already carries `data-[selected]:text-primary-text` — an attribute\n// selector no plain class on the same element outranks. On the label itself it\n// is a declaration rather than an inheritance, so it wins whatever the pill\n// resolved to: 4.67:1 over the drained `--color-muted` fill, 4.77:1 over the\n// primary wash a selected chip keeps.\nconst disabledText = \"group-data-[disabled]:text-muted-foreground\";\n\n// Both inner controls are surfaces *inside* a 32px pill, so the standard\n// outward `outline-offset-2` would draw the toggle's ring straight through the\n// dismiss control sitting 4px to its right, and vice versa. Inset instead —\n// the ring lands just inside the pill's own border, which is where the eye\n// already is. WindowControls takes the same offset for the same reason.\nconst focusRing =\n \"focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo\";\n\n// Everything a host passes lands on the container: it is the node with an\n// identity (a data-testid, a key in a list), while the two controls inside it\n// are named by their own content and by `removeLabel`. `class` is pulled out of\n// that spread and merged through `cn()` instead — Vue's own class merge only\n// concatenates, so a caller's `max-w-32` would win or lose against the pill's\n// own utilities depending on Tailwind's emission order rather than on intent.\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest: containerAttrs } = useSplitAttrs();\n</script>\n\n<template>\n <span\n v-bind=\"containerAttrs\"\n :data-selected=\"selected || undefined\"\n :data-disabled=\"disabled || undefined\"\n :class=\"\n cn(\n chipVariants({ variant, size }),\n // Drained to the neutral well, with the hairline the coloured variants\n // hide: an unavailable chip gives up its hue rather than its legibility.\n // A *selected* one keeps the primary wash — `data-[selected]:bg-primary-muted`\n // outranks a plain class — which is right: switched-on is information,\n // and the label is muted over either fill.\n //\n // `border-border` and not the strong weight it used to take.\n // `theme.css` reserves the strong hairline for a block that must\n // *assert* its edge, which is the opposite of what this state says, and\n // the lighter rim is the same one every other unavailable control in the\n // library slackens to — so a disabled chip in a row of disabled\n // controls is one treatment rather than a second.\n disabled && 'border-border bg-muted',\n removable && 'pr-1',\n attrs.class as string,\n )\n \"\n >\n <button\n v-if=\"toggleable\"\n type=\"button\"\n :aria-pressed=\"selected\"\n :disabled=\"disabled\"\n :style=\"pressStyle\"\n :class=\"\n cn(\n 'inline-flex h-full min-w-0 select-none items-center gap-1 rounded-full',\n labelPad,\n // A neutral darkening rather than a token fill: the pill underneath\n // can be any of eight colours, and a hover state is not an action\n // colour.\n 'hover:bg-foreground/5 active:scale-press',\n focusRing,\n 'disabled:pointer-events-none',\n )\n \"\n @click=\"emit('update:selected', !selected)\"\n >\n <!-- The check is the half of the selected signal that survives a reader\n who cannot tell a 12% wash from a 20% one; `aria-pressed` is the\n other half. It collapses its own width rather than unmounting, so\n the pill grows into the glyph instead of snapping. -->\n <span\n aria-hidden=\"true\"\n class=\"inline-flex w-0 shrink-0 items-center overflow-hidden opacity-0 transition-[width,opacity] duration-fast ease-spring group-data-[selected]:w-4 group-data-[selected]:opacity-100\"\n >\n <Check class=\"h-3.5 w-3.5 shrink-0\" />\n </span>\n <span :class=\"cn('truncate', disabledText)\">\n <!-- @slot The chip's label. Keep it to a word or two — a token that needs a sentence is a row, not a chip. -->\n <slot />\n </span>\n </button>\n <span v-else :class=\"cn('inline-flex min-w-0 items-center', labelPad)\">\n <span :class=\"cn('truncate', disabledText)\"><slot /></span>\n </span>\n\n <!-- A sibling of the toggle, never a child of it. 24px square is the floor\n WCAG 2.2 SC 2.5.8 sets for a target, which is what decides the size\n here rather than the glyph's own 14px. -->\n <button\n v-if=\"removable\"\n type=\"button\"\n :aria-label=\"removeLabel\"\n :disabled=\"disabled\"\n :style=\"pressStyle\"\n :class=\"\n cn(\n 'inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-full',\n // Not `hover:bg-subtle`, the usual neutral well: it is opaque, and\n // over a coloured pill it punches a grey hole through the fill\n // instead of darkening it.\n 'hover:bg-foreground/10 active:scale-90',\n focusRing,\n disabledText,\n 'disabled:pointer-events-none',\n )\n \"\n @click=\"emit('remove')\"\n >\n <X class=\"h-3.5 w-3.5\" />\n </button>\n </span>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,IAAa,eAAe,IAC1B;CACE;CACA;CAOA;AACF,GACA;CAIE,UAAU;EACR,SAAS;GAKP,SACE;GACF,SACE;GAiBF,SACE;GACF,SACE;GACF,SACE;GACF,MAAM;GACN,aACE;GACF,QACE;EACJ;EACA,MAAM;GACJ,IAAI;GACJ,IAAI;EACN;CACF;CACA,iBAAiB;EAAE,SAAS;EAAW,MAAM;CAAK;AACpD,CACF;;;;;;;;;;;;;;;;;;;;;;;EAkCA,MAAM,QAAQ;EA6Bd,MAAM,OAAO;EAOb,MAAM,aAAa,eAAe,MAAM,aAAa,KAAA,CAAS;EAM9D,MAAM,WAAW,eACf,GAAG,MAAM,SAAS,OAAO,WAAW,QAAQ,MAAM,aAAa,MAAM,CACvE;EAIA,MAAM,aACJ;EASF,MAAM,eAAe;EAOrB,MAAM,YACJ;EASF,MAAM,EAAE,OAAO,MAAM,mBAAmB,cAAc;;GAIpD,OAAA,UAAA,GAAA,mBAyFO,QAzFP,WACU,MAwFH,cAAA,GAxFiB;IACrB,iBAAe,QAAA,YAAY,KAAA;IAC3B,iBAAe,QAAA,YAAY,KAAA;IAC3B,OAAc,MAAA,EAAA,CAAE,CAAU,MAAA,YAAA,CAAY,CAAA;KAAA,SAAG,QAAA;KAAO,MAAE,QAAA;IAAI,CAAA,GAA60B,QAAA,YAAQ,0BAAsC,QAAA,aAAS,QAAoB,MAAA,KAAA,CAAK,CAAC,KAAA;GAsB78B,CAAA,GAAA,CAAA,WAAA,SADR,UAAA,GAAA,mBAkCS,UAAA;;IAhCP,MAAK;IACJ,gBAAc,QAAA;IACd,UAAU,QAAA;IACV,OAAO;IACP,OAAK,eAAW,MAAA,EAAA,CAAE,CAAA,0EAAgG,SAAA,OAAA,4CAA2P,WAAA,8BAAA,CAAA;IAY7W,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,mBAAA,CAAqB,QAAA,QAAQ;GAMzC,GAAA,CAAA,mBAKO,QALP,YAKO,CADL,YAAsC,MAAA,KAAA,GAAA,EAA/B,OAAM,uBAAsB,CAAA,CAAA,CAAA,GAErC,mBAGO,QAAA,EAHA,OAAK,eAAE,MAAA,EAAA,CAAE,CAAA,YAAa,YAAY,CAAA,EAAA,GAAA,CAEvC,WAAQ,KAAA,QAAA,SAAA,CAAA,GAAA,CAAA,CAAA,GAAA,IAAA,UAAA,MAGZ,UAAA,GAAA,mBAEO,QAAA;;IAFO,OAAK,eAAE,MAAA,EAAA,CAAE,CAAA,oCAAqC,SAAA,KAAQ,CAAA;GAClE,GAAA,CAAA,mBAA2D,QAAA,EAApD,OAAK,eAAE,MAAA,EAAA,CAAE,CAAA,YAAa,YAAY,CAAA,EAAA,GAAA,CAAG,WAAQ,KAAA,QAAA,SAAA,CAAA,GAAA,CAAA,CAAA,GAAA,CAAA,IAO9C,QAAA,aADR,UAAA,GAAA,mBAqBS,UAAA;;IAnBP,MAAK;IACJ,cAAY,QAAA;IACZ,UAAU,QAAA;IACV,OAAO;IACP,OAAK,eAAW,MAAA,EAAA,CAAE,CAAA,yEAAA,0CAAiV,WAAqB,cAAA,8BAAA,CAAA;IAYxX,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,QAAA;GAEZ,GAAA,CAAA,YAAyB,MAAA,CAAA,GAAA,EAAtB,OAAM,cAAa,CAAA,CAAA,GAAA,IAAA,UAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,GAAA,IAAA,UAAA"}
|
|
1
|
+
{"version":3,"file":"Chip.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../../../packages/primitives/chip/src/Chip.vue"],"sourcesContent":["<script lang=\"ts\">\nimport { cva } from \"class-variance-authority\";\n\n/**\n * The same eight names Badge carries, deliberately — a chip and a badge sitting\n * in one row are the same object in two moods, so `success` has to mean the\n * same green in both. A ninth name here would be a second vocabulary for one\n * palette.\n */\nexport type ChipVariant =\n \"neutral\" | \"outline\" | \"primary\" | \"success\" | \"warning\" | \"info\" | \"destructive\" | \"accent\";\n\n/**\n * Two heights off the shared control scale (the text input's `sm` and `md`), so\n * a row of chips above a field lines up with it. There is no `lg`: a chip is a\n * token, and one big enough to need the 44px step is a Button.\n */\nexport type ChipSize = \"sm\" | \"md\";\n\n/**\n * The container. It is the coloured pill, and it is deliberately not the\n * interactive node — see the docblock below for why that separation is the\n * whole component.\n *\n * Selection is a state the *edges* deepen: the hue-carrying variants draw a\n * stronger border around the wash they already sit in, the hueless ones swap\n * their fill for the primary wash. It is never any one signal alone that says\n * so — every selected chip also carries `aria-pressed=\"true\"` and a check\n * glyph, which is what keeps the state legible to a reader who cannot resolve\n * a deepened edge from a resting one.\n */\nexport const chipVariants = cva(\n [\n \"group inline-flex max-w-full items-center rounded-full border align-middle font-medium\",\n \"transition-[background-color,border-color,color] duration-fast ease-out\",\n // No `opacity` here, and that is the point. `data-[disabled]:opacity-50`\n // dimmed the pill *and* its label together, taking a 5.01:1 label down to\n // 2.78:1 against its own fill — halving the alpha more than halves the\n // contrast. Unavailability is painted in measured colours instead: the\n // fill and border below, and `disabledText` on the label and the glyph\n // beside it.\n \"data-[disabled]:cursor-not-allowed\",\n ],\n {\n // `satisfies` keeps the two halves honest: the union above is the name a\n // consumer imports and the documentation prints, the map below is the\n // classes. Adding a member to one and not the other stops compiling.\n variants: {\n variant: {\n // The two hueless variants take the primary wash when selected —\n // `--color-primary-muted` is the repository's selected/active fill, the\n // one Select's checked row already uses, so a chosen filter reads as a\n // human decision rather than as a new colour invented here.\n neutral:\n \"border-transparent bg-subtle text-subtle-foreground data-[selected]:border-primary/40 data-[selected]:bg-primary-muted data-[selected]:text-primary-text\",\n outline:\n \"border-border bg-transparent text-muted-foreground data-[selected]:border-primary/40 data-[selected]:bg-primary-muted data-[selected]:text-primary-text\",\n // The hue-carrying variants deepen their own hue instead of jumping to\n // the primary: a reader picked this chip *because* it is the destructive\n // one, and swapping its colour on selection would throw that away.\n // The tinted washes use the opaque `*-muted` tokens, not an alpha of\n // the base hue: `bg-*-/12` composites against the surface behind it, so\n // a chip renders at 4.46:1 on `bg-card` and differently on `bg-sunken`\n // — under the AA floor and inconsistent. `*-muted` is mixed to hold\n // ≥4.5:1 against its own `*-text` in both themes (see theme.css),\n // which is what these variants were named for.\n //\n // Selection deepens the border, not the fill: a `data-[selected]:bg-*-/20`\n // over the wash drops the text to ~4.2:1 in light mode (success, warning,\n // info, destructive), below the same floor the unselected state holds.\n // `*-muted` is the theme's \"selected/active fill\" anyway, so the state is\n // carried by the deeper border, the check glyph and `aria-pressed` — the\n // signals the docblock below already names as what makes selection legible.\n primary:\n \"border-transparent bg-primary-muted text-primary-text data-[selected]:border-primary/45\",\n success:\n \"border-transparent bg-success-muted text-success-text data-[selected]:border-success/45\",\n // `-text`, matching Badge and the other functional siblings: the wash\n // is mixed to hold contrast against the `*-text` rung in both themes,\n // not against the base hue.\n warning:\n \"border-transparent bg-warning-muted text-warning-text data-[selected]:border-warning/45\",\n info: \"border-transparent bg-info-muted text-info-text data-[selected]:border-info/45\",\n destructive:\n \"border-transparent bg-destructive-muted text-destructive-text data-[selected]:border-destructive/45\",\n accent:\n \"border-accent/40 bg-accent-muted text-accent-text data-[selected]:border-accent/70\",\n } satisfies Record<ChipVariant, string>,\n size: {\n sm: \"h-8 text-xs\",\n md: \"h-9 text-sm\",\n } satisfies Record<ChipSize, string>,\n },\n defaultVariants: { variant: \"neutral\", size: \"md\" },\n },\n);\n</script>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { Check, X } from \"@lucide/vue\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { useSplitAttrs } from \"@ecoma-io/loom-core\";\n\n/**\n * Chip — an interactive token: a filter that switches on, an entity that can be\n * dismissed, a recipient sitting in a picker.\n *\n * The line against Badge is the one worth knowing, because the two look almost\n * identical: **Badge is a status label a reader only looks at; Chip is a\n * control a reader acts on.** A Badge that gained a click handler would be a\n * Chip, and a Chip nobody can toggle or dismiss is a Badge wearing a button's\n * clothes — it takes a Tab stop and announces itself as pressable while doing\n * nothing. Reach for Button when the token *is* the action rather than a\n * standing state, and for SegmentedControl when exactly one of a small set must\n * be chosen; a row of chips is many-of-many, and none-of-them is a valid answer.\n *\n * The structure is the component. A chip that both toggles and dismisses has\n * two controls, and they are **siblings inside a non-interactive container** —\n * never a dismiss `<button>` nested inside a toggle `<button>`. Nested buttons\n * are invalid HTML: the parser reparents them, and screen readers disagree\n * about which one they are on and what a press means. So the pill is a `<span>`\n * that paints, and everything pressable is a child of it.\n *\n * Both controls stay on the natural Tab order rather than roving. They are two\n * different actions on one object, not N instances of one control, and a reader\n * who tabbed to the chip has to be able to reach \"remove\" without guessing an\n * arrow key.\n */\nconst props = withDefaults(\n defineProps<{\n /** Whether the chip is switched on. Binding it is what makes the chip a toggle — leave it unset for a chip that is only dismissable, and the label renders as inert text instead of a press target. */\n selected?: boolean | undefined;\n /** Renders a dismiss control beside the label, as a sibling of the toggle rather than inside it. */\n removable?: boolean;\n /** The accessible name of the dismiss control. Localise it, or qualify it — \"Remove Ana Duarte\" reads far better in a list of recipients. */\n removeLabel?: string;\n /** Unavailable: drains the pill to a neutral fill, mutes the label, and makes both controls inert to pointer and keyboard. */\n disabled?: boolean;\n /** Which status or meta the token carries. The names and colours are Badge's, so a chip and a badge never disagree. */\n variant?: ChipVariant;\n /** Chip height, off the shared control scale so a row of chips lines up with the field above it. */\n size?: ChipSize;\n }>(),\n {\n // Not `false`. `undefined` is the signal that this chip is not a toggle at\n // all, and Vue casts an absent Boolean prop to `false` unless a default\n // says otherwise — without this line every chip would claim to be a toggle\n // that is switched off, and a dismiss-only chip would grow a dead Tab stop.\n selected: undefined,\n removable: false,\n removeLabel: \"Remove\",\n disabled: false,\n variant: \"neutral\",\n size: \"md\",\n },\n);\n\nconst emit = defineEmits<{\n /** The flipped selection. The chip does not update itself: a filter chip's state is the query it stands for, and a chip that switched on locally would keep claiming a result set the host never fetched. */\n \"update:selected\": [value: boolean];\n /** The dismiss control was activated. Removing the chip from the list is the host's to do. */\n remove: [];\n}>();\n\nconst toggleable = computed(() => props.selected !== undefined);\n\n// The label's horizontal padding, shared by both branches below so the pill is\n// the same width whether or not it is pressable. The dismiss control supplies\n// its own trailing room, so the label gives its right side back when one is\n// present.\nconst labelPad = computed(() =>\n cn(props.size === \"sm\" ? \"px-2.5\" : \"px-3\", props.removable && \"pr-1\"),\n);\n\n// Springy transform, steady colour — the same split Button and Checkbox use, so\n// a chip press feels like every other press in the library.\nconst pressStyle =\n \"transition: transform var(--duration-fast) var(--ease-spring), background-color var(--duration-fast) var(--ease-out);\";\n\n// The unavailable label colour, and it belongs on the label rather than on the\n// pill around it. A colour set on the container is only *inherited* here, and\n// the container already carries `data-[selected]:text-primary-text` — an attribute\n// selector no plain class on the same element outranks. On the label itself it\n// is a declaration rather than an inheritance, so it wins whatever the pill\n// resolved to: 4.67:1 over the drained `--color-muted` fill, 4.77:1 over the\n// primary wash a selected chip keeps.\nconst disabledText = \"group-data-[disabled]:text-muted-foreground\";\n\n// Both inner controls are surfaces *inside* a 32px pill, so the standard\n// outward `outline-offset-2` would draw the toggle's ring straight through the\n// dismiss control sitting 4px to its right, and vice versa. Inset instead —\n// the ring lands just inside the pill's own border, which is where the eye\n// already is. WindowControls takes the same offset for the same reason.\nconst focusRing =\n \"focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo\";\n\n// Everything a host passes lands on the container: it is the node with an\n// identity (a data-testid, a key in a list), while the two controls inside it\n// are named by their own content and by `removeLabel`. `class` is pulled out of\n// that spread and merged through `cn()` instead — Vue's own class merge only\n// concatenates, so a caller's `max-w-32` would win or lose against the pill's\n// own utilities depending on Tailwind's emission order rather than on intent.\ndefineOptions({ inheritAttrs: false });\nconst { attrs, rest: containerAttrs } = useSplitAttrs();\n</script>\n\n<template>\n <span\n v-bind=\"containerAttrs\"\n :data-selected=\"selected || undefined\"\n :data-disabled=\"disabled || undefined\"\n :class=\"\n cn(\n chipVariants({ variant, size }),\n // Drained to the neutral well, with the hairline the coloured variants\n // hide: an unavailable chip gives up its hue rather than its legibility.\n // A *selected* one keeps the primary wash — `data-[selected]:bg-primary-muted`\n // outranks a plain class — which is right: switched-on is information,\n // and the label is muted over either fill.\n //\n // `border-border` and not the strong weight it used to take.\n // `theme.css` reserves the strong hairline for a block that must\n // *assert* its edge, which is the opposite of what this state says, and\n // the lighter rim is the same one every other unavailable control in the\n // library slackens to — so a disabled chip in a row of disabled\n // controls is one treatment rather than a second.\n disabled && 'border-border bg-muted',\n removable && 'pr-1',\n attrs.class as string,\n )\n \"\n >\n <button\n v-if=\"toggleable\"\n type=\"button\"\n :aria-pressed=\"selected\"\n :disabled=\"disabled\"\n :style=\"pressStyle\"\n :class=\"\n cn(\n 'inline-flex h-full min-w-0 select-none items-center gap-1 rounded-full',\n labelPad,\n // A neutral darkening rather than a token fill: the pill underneath\n // can be any of eight colours, and a hover state is not an action\n // colour.\n 'hover:bg-foreground/5 active:scale-press',\n focusRing,\n 'disabled:pointer-events-none',\n )\n \"\n @click=\"emit('update:selected', !selected)\"\n >\n <!-- The check is the half of the selected signal that survives a reader\n who cannot tell a 12% wash from a 20% one; `aria-pressed` is the\n other half. It collapses its own width rather than unmounting, so\n the pill grows into the glyph instead of snapping. -->\n <span\n aria-hidden=\"true\"\n class=\"inline-flex w-0 shrink-0 items-center overflow-hidden opacity-0 transition-[width,opacity] duration-fast ease-spring group-data-[selected]:w-4 group-data-[selected]:opacity-100\"\n >\n <Check class=\"h-3.5 w-3.5 shrink-0\" />\n </span>\n <span :class=\"cn('truncate', disabledText)\">\n <!-- @slot The chip's label. Keep it to a word or two — a token that needs a sentence is a row, not a chip. -->\n <slot />\n </span>\n </button>\n <span v-else :class=\"cn('inline-flex min-w-0 items-center', labelPad)\">\n <span :class=\"cn('truncate', disabledText)\"><slot /></span>\n </span>\n\n <!-- A sibling of the toggle, never a child of it. 24px square is the floor\n WCAG 2.2 SC 2.5.8 sets for a target, which is what decides the size\n here rather than the glyph's own 14px. -->\n <button\n v-if=\"removable\"\n type=\"button\"\n :aria-label=\"removeLabel\"\n :disabled=\"disabled\"\n :style=\"pressStyle\"\n :class=\"\n cn(\n 'inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-full',\n // Not `hover:bg-subtle`, the usual neutral well: it is opaque, and\n // over a coloured pill it punches a grey hole through the fill\n // instead of darkening it.\n 'hover:bg-foreground/10 active:scale-90',\n focusRing,\n disabledText,\n 'disabled:pointer-events-none',\n )\n \"\n @click=\"emit('remove')\"\n >\n <X class=\"h-3.5 w-3.5\" />\n </button>\n </span>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,IAAa,eAAe,IAC1B;CACE;CACA;CAOA;AACF,GACA;CAIE,UAAU;EACR,SAAS;GAKP,SACE;GACF,SACE;GAiBF,SACE;GACF,SACE;GAIF,SACE;GACF,MAAM;GACN,aACE;GACF,QACE;EACJ;EACA,MAAM;GACJ,IAAI;GACJ,IAAI;EACN;CACF;CACA,iBAAiB;EAAE,SAAS;EAAW,MAAM;CAAK;AACpD,CACF;;;;;;;;;;;;;;;;;;;;;;;EAkCA,MAAM,QAAQ;EA6Bd,MAAM,OAAO;EAOb,MAAM,aAAa,eAAe,MAAM,aAAa,KAAA,CAAS;EAM9D,MAAM,WAAW,eACf,GAAG,MAAM,SAAS,OAAO,WAAW,QAAQ,MAAM,aAAa,MAAM,CACvE;EAIA,MAAM,aACJ;EASF,MAAM,eAAe;EAOrB,MAAM,YACJ;EASF,MAAM,EAAE,OAAO,MAAM,mBAAmB,cAAc;;GAIpD,OAAA,UAAA,GAAA,mBAyFO,QAzFP,WACU,MAwFH,cAAA,GAxFiB;IACrB,iBAAe,QAAA,YAAY,KAAA;IAC3B,iBAAe,QAAA,YAAY,KAAA;IAC3B,OAAc,MAAA,EAAA,CAAE,CAAU,MAAA,YAAA,CAAY,CAAA;KAAA,SAAG,QAAA;KAAO,MAAE,QAAA;IAAI,CAAA,GAA60B,QAAA,YAAQ,0BAAsC,QAAA,aAAS,QAAoB,MAAA,KAAA,CAAK,CAAC,KAAA;GAsB78B,CAAA,GAAA,CAAA,WAAA,SADR,UAAA,GAAA,mBAkCS,UAAA;;IAhCP,MAAK;IACJ,gBAAc,QAAA;IACd,UAAU,QAAA;IACV,OAAO;IACP,OAAK,eAAW,MAAA,EAAA,CAAE,CAAA,0EAAgG,SAAA,OAAA,4CAA2P,WAAA,8BAAA,CAAA;IAY7W,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,mBAAA,CAAqB,QAAA,QAAQ;GAMzC,GAAA,CAAA,mBAKO,QALP,YAKO,CADL,YAAsC,MAAA,KAAA,GAAA,EAA/B,OAAM,uBAAsB,CAAA,CAAA,CAAA,GAErC,mBAGO,QAAA,EAHA,OAAK,eAAE,MAAA,EAAA,CAAE,CAAA,YAAa,YAAY,CAAA,EAAA,GAAA,CAEvC,WAAQ,KAAA,QAAA,SAAA,CAAA,GAAA,CAAA,CAAA,GAAA,IAAA,UAAA,MAGZ,UAAA,GAAA,mBAEO,QAAA;;IAFO,OAAK,eAAE,MAAA,EAAA,CAAE,CAAA,oCAAqC,SAAA,KAAQ,CAAA;GAClE,GAAA,CAAA,mBAA2D,QAAA,EAApD,OAAK,eAAE,MAAA,EAAA,CAAE,CAAA,YAAa,YAAY,CAAA,EAAA,GAAA,CAAG,WAAQ,KAAA,QAAA,SAAA,CAAA,GAAA,CAAA,CAAA,GAAA,CAAA,IAO9C,QAAA,aADR,UAAA,GAAA,mBAqBS,UAAA;;IAnBP,MAAK;IACJ,cAAY,QAAA;IACZ,UAAU,QAAA;IACV,OAAO;IACP,OAAK,eAAW,MAAA,EAAA,CAAE,CAAA,yEAAA,0CAAiV,WAAqB,cAAA,8BAAA,CAAA;IAYxX,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAI,QAAA;GAEZ,GAAA,CAAA,YAAyB,MAAA,CAAA,GAAA,EAAtB,OAAM,cAAa,CAAA,CAAA,GAAA,IAAA,UAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,GAAA,IAAA,UAAA"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import Collapse_vue_vue_type_script_setup_true_lang_default from "./Collapse.vue_vue_type_script_setup_true_lang.js";
|
|
2
|
+
//#region packages/primitives/collapse/src/Collapse.vue
|
|
3
|
+
var Collapse_default = Collapse_vue_vue_type_script_setup_true_lang_default;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { Collapse_default as default };
|
|
6
|
+
|
|
7
|
+
//# sourceMappingURL=Collapse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Collapse.js","names":[],"sources":["../../../../../packages/primitives/collapse/src/Collapse.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Collapse — one disclosure: a trigger that shows and hides a single region\n * of content. The generic primitive under Accordion's row-group; the state\n * machinery is Reka's Collapsible, so controlled and uncontrolled use,\n * aria-expanded/aria-controls wiring and rapid open/close are all owned\n * there rather than reimplemented here.\n *\n * Expansion animates on the shared height pair (`--animate-expand` /\n * `--animate-collapse`) keyed to Reka's measured\n * `--reka-collapsible-content-height`; padding lives on an inner wrapper\n * because a padded outer box would pop at both ends of the animation while\n * its height interpolated.\n */\n</script>\n\n<script setup lang=\"ts\">\nimport { ChevronDown } from \"@lucide/vue\";\nimport { CollapsibleContent, CollapsibleRoot, CollapsibleTrigger } from \"reka-ui\";\nimport { cn, optional } from \"@ecoma-io/loom-core\";\nimport { useId } from \"vue\";\n\nwithDefaults(\n defineProps<{\n /**\n * Controls visibility; pair with `v-model:open`. Omit it and the collapse\n * owns its own state, seeded by `defaultOpen`.\n */\n open?: boolean | undefined;\n /** The uncontrolled starting state. */\n defaultOpen?: boolean;\n /** Unavailable rather than hidden: the trigger stays put and inert. */\n disabled?: boolean;\n }>(),\n {\n // `undefined` is the uncontrolled state, and only an explicit default\n // keeps it reachable past Vue's Boolean casting.\n open: undefined,\n defaultOpen: false,\n disabled: false,\n },\n);\n\ndefineEmits<{ \"update:open\": [value: boolean] }>();\n\n// Reka leaves the trigger's aria-controls pointing at an id its Content only\n// registers non-reactively, so a closed-first render ships an empty IDREF.\n// Both halves get explicit ids here instead; fallthrough attributes merge\n// after Reka's own, which is the same mechanism AlertDialog uses to clear\n// its dangling describedby.\nconst triggerId = useId();\nconst contentId = useId();\n</script>\n\n<template>\n <CollapsibleRoot\n v-slot=\"{ open: state }\"\n v-bind=\"optional({ open })\"\n :default-open=\"defaultOpen\"\n :disabled=\"disabled\"\n :unmount-on-hide=\"false\"\n @update:open=\"$emit('update:open', $event)\"\n >\n <CollapsibleTrigger\n :id=\"triggerId\"\n :aria-controls=\"contentId\"\n :class=\"\n cn(\n 'group flex w-full items-center justify-between gap-3 rounded-md px-4 py-3 text-left text-sm font-medium',\n 'transition-colors duration-fast ease-out hover:bg-subtle',\n 'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo',\n 'disabled:pointer-events-none disabled:bg-transparent disabled:text-muted-foreground',\n )\n \"\n >\n <!-- @slot The control's visible face, inside a real `<button>` carrying\n aria-expanded/aria-controls and the canonical focus ring. `open`\n mirrors the real state, controlled or not. -->\n <slot name=\"trigger\" :open=\"state\" />\n <ChevronDown\n class=\"h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-fast ease-out group-data-[state=open]:rotate-180\"\n aria-hidden=\"true\"\n />\n </CollapsibleTrigger>\n <!-- Mounted even while closed: the region id in aria-controls has to\n resolve for a screen reader before the first expansion, and a\n persistent element is what lets the collapse animation actually play\n instead of being unmounted mid-frame. Reka conceals the closed\n region with hidden=\"until-found\". -->\n <CollapsibleContent\n data-loom-collapse=\"\"\n class=\"overflow-hidden data-[state=open]:animate-expand data-[state=closed]:animate-collapse\"\n >\n <!-- The animated box carries no padding of its own: interpolating\n height around fixed padding pops at both ends of the film. This\n inner div also carries the id the trigger references: Reka writes\n its own content id after any we pass, so the reference lands here\n where it actually resolves. -->\n <div :id=\"contentId\" class=\"px-4 pb-4\">\n <!-- @slot The region's content. Nested collapses work unchanged —\n each owns its own region. -->\n <slot />\n </div>\n </CollapsibleContent>\n </CollapsibleRoot>\n</template>\n"],"mappings":""}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collapse — one disclosure: a trigger that shows and hides a single region
|
|
3
|
+
* of content. The generic primitive under Accordion's row-group; the state
|
|
4
|
+
* machinery is Reka's Collapsible, so controlled and uncontrolled use,
|
|
5
|
+
* aria-expanded/aria-controls wiring and rapid open/close are all owned
|
|
6
|
+
* there rather than reimplemented here.
|
|
7
|
+
*
|
|
8
|
+
* Expansion animates on the shared height pair (`--animate-expand` /
|
|
9
|
+
* `--animate-collapse`) keyed to Reka's measured
|
|
10
|
+
* `--reka-collapsible-content-height`; padding lives on an inner wrapper
|
|
11
|
+
* because a padded outer box would pop at both ends of the animation while
|
|
12
|
+
* its height interpolated.
|
|
13
|
+
*/
|
|
14
|
+
declare const _default: typeof __VLS_export;
|
|
15
|
+
export default _default;
|
|
16
|
+
declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
|
|
17
|
+
/**
|
|
18
|
+
* Controls visibility; pair with `v-model:open`. Omit it and the collapse
|
|
19
|
+
* owns its own state, seeded by `defaultOpen`.
|
|
20
|
+
*/
|
|
21
|
+
open?: boolean | undefined;
|
|
22
|
+
/** The uncontrolled starting state. */
|
|
23
|
+
defaultOpen?: boolean;
|
|
24
|
+
/** Unavailable rather than hidden: the trigger stays put and inert. */
|
|
25
|
+
disabled?: boolean;
|
|
26
|
+
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
27
|
+
"update:open": (value: boolean) => any;
|
|
28
|
+
}, string, import("vue").PublicProps, Readonly<{
|
|
29
|
+
/**
|
|
30
|
+
* Controls visibility; pair with `v-model:open`. Omit it and the collapse
|
|
31
|
+
* owns its own state, seeded by `defaultOpen`.
|
|
32
|
+
*/
|
|
33
|
+
open?: boolean | undefined;
|
|
34
|
+
/** The uncontrolled starting state. */
|
|
35
|
+
defaultOpen?: boolean;
|
|
36
|
+
/** Unavailable rather than hidden: the trigger stays put and inert. */
|
|
37
|
+
disabled?: boolean;
|
|
38
|
+
}> & Readonly<{
|
|
39
|
+
"onUpdate:open"?: (value: boolean) => any;
|
|
40
|
+
}>, {
|
|
41
|
+
disabled: boolean;
|
|
42
|
+
open: boolean | undefined;
|
|
43
|
+
defaultOpen: boolean;
|
|
44
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
|
|
45
|
+
trigger?: (props: {
|
|
46
|
+
open: boolean;
|
|
47
|
+
}) => any;
|
|
48
|
+
} & {
|
|
49
|
+
default?: (props: {}) => any;
|
|
50
|
+
}>;
|
|
51
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
52
|
+
new (): {
|
|
53
|
+
$slots: S;
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=Collapse.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Collapse.vue.d.ts","sourceRoot":"","sources":["../../../../../packages/primitives/collapse/src/Collapse.vue"],"names":[],"mappings":"AAmHA;;;;;;;;;;;;GAYG;wBACkB,OAAO,YAAY;AAAxC,wBAAyC;AACzC,QAAA,MAAM,YAAY;IAEd;;;OAGG;WACI,OAAO,GAAG,SAAS;IAC1B,uCAAuC;kBACzB,OAAO;IACrB,uEAAuE;eAC5D,OAAO;;;;IARlB;;;OAGG;WACI,OAAO,GAAG,SAAS;IAC1B,uCAAuC;kBACzB,OAAO;IACrB,uEAAuE;eAC5D,OAAO;;;;cAAP,OAAO;UAJX,OAAO,GAAG,SAAS;iBAEZ,OAAO;;cAqLX,CAAC,KAAK;;KAAiB,KAAK,GAAG;;cAC/B,CAAC,KAAK,IAAiB,KAAK,GAAG;EAWzC,CAAC;AAWL,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { cn } from "../../../core/src/cn.js";
|
|
2
|
+
import { optional } from "../../../core/src/props.js";
|
|
3
|
+
import "../../../core/src/index.js";
|
|
4
|
+
import { createBlock, createElementVNode, createVNode, defineComponent, mergeProps, normalizeClass, openBlock, renderSlot, unref, useId, withCtx } from "vue";
|
|
5
|
+
import { ChevronDown } from "@lucide/vue";
|
|
6
|
+
import { CollapsibleContent, CollapsibleRoot, CollapsibleTrigger } from "reka-ui";
|
|
7
|
+
//#region packages/primitives/collapse/src/Collapse.vue?vue&type=script&setup=true&lang.ts
|
|
8
|
+
var _hoisted_1 = ["id"];
|
|
9
|
+
/**
|
|
10
|
+
* Collapse — one disclosure: a trigger that shows and hides a single region
|
|
11
|
+
* of content. The generic primitive under Accordion's row-group; the state
|
|
12
|
+
* machinery is Reka's Collapsible, so controlled and uncontrolled use,
|
|
13
|
+
* aria-expanded/aria-controls wiring and rapid open/close are all owned
|
|
14
|
+
* there rather than reimplemented here.
|
|
15
|
+
*
|
|
16
|
+
* Expansion animates on the shared height pair (`--animate-expand` /
|
|
17
|
+
* `--animate-collapse`) keyed to Reka's measured
|
|
18
|
+
* `--reka-collapsible-content-height`; padding lives on an inner wrapper
|
|
19
|
+
* because a padded outer box would pop at both ends of the animation while
|
|
20
|
+
* its height interpolated.
|
|
21
|
+
*/
|
|
22
|
+
var Collapse_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
23
|
+
__name: "Collapse",
|
|
24
|
+
props: {
|
|
25
|
+
open: {
|
|
26
|
+
type: Boolean,
|
|
27
|
+
default: void 0
|
|
28
|
+
},
|
|
29
|
+
defaultOpen: {
|
|
30
|
+
type: Boolean,
|
|
31
|
+
default: false
|
|
32
|
+
},
|
|
33
|
+
disabled: {
|
|
34
|
+
type: Boolean,
|
|
35
|
+
default: false
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
emits: ["update:open"],
|
|
39
|
+
setup(__props) {
|
|
40
|
+
const triggerId = useId();
|
|
41
|
+
const contentId = useId();
|
|
42
|
+
return (_ctx, _cache) => {
|
|
43
|
+
return openBlock(), createBlock(unref(CollapsibleRoot), mergeProps(unref(optional)({ open: __props.open }), {
|
|
44
|
+
"default-open": __props.defaultOpen,
|
|
45
|
+
disabled: __props.disabled,
|
|
46
|
+
"unmount-on-hide": false,
|
|
47
|
+
"onUpdate:open": _cache[0] || (_cache[0] = ($event) => _ctx.$emit("update:open", $event))
|
|
48
|
+
}), {
|
|
49
|
+
default: withCtx(({ open: state }) => [createVNode(unref(CollapsibleTrigger), {
|
|
50
|
+
id: unref(triggerId),
|
|
51
|
+
"aria-controls": unref(contentId),
|
|
52
|
+
class: normalizeClass(unref(cn)("group flex w-full items-center justify-between gap-3 rounded-md px-4 py-3 text-left text-sm font-medium", "transition-colors duration-fast ease-out hover:bg-subtle", "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo", "disabled:pointer-events-none disabled:bg-transparent disabled:text-muted-foreground"))
|
|
53
|
+
}, {
|
|
54
|
+
default: withCtx(() => [renderSlot(_ctx.$slots, "trigger", { open: state }), createVNode(unref(ChevronDown), {
|
|
55
|
+
class: "h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-fast ease-out group-data-[state=open]:rotate-180",
|
|
56
|
+
"aria-hidden": "true"
|
|
57
|
+
})]),
|
|
58
|
+
_: 2
|
|
59
|
+
}, 1032, [
|
|
60
|
+
"id",
|
|
61
|
+
"aria-controls",
|
|
62
|
+
"class"
|
|
63
|
+
]), createVNode(unref(CollapsibleContent), {
|
|
64
|
+
"data-loom-collapse": "",
|
|
65
|
+
class: "overflow-hidden data-[state=open]:animate-expand data-[state=closed]:animate-collapse"
|
|
66
|
+
}, {
|
|
67
|
+
default: withCtx(() => [createElementVNode("div", {
|
|
68
|
+
id: unref(contentId),
|
|
69
|
+
class: "px-4 pb-4"
|
|
70
|
+
}, [renderSlot(_ctx.$slots, "default")], 8, _hoisted_1)]),
|
|
71
|
+
_: 3
|
|
72
|
+
})]),
|
|
73
|
+
_: 3
|
|
74
|
+
}, 16, ["default-open", "disabled"]);
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
//#endregion
|
|
79
|
+
export { Collapse_vue_vue_type_script_setup_true_lang_default as default };
|
|
80
|
+
|
|
81
|
+
//# sourceMappingURL=Collapse.vue_vue_type_script_setup_true_lang.js.map
|
package/dist/packages/primitives/collapse/src/Collapse.vue_vue_type_script_setup_true_lang.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Collapse.vue_vue_type_script_setup_true_lang.js","names":["$emit"],"sources":["../../../../../packages/primitives/collapse/src/Collapse.vue"],"sourcesContent":["<script lang=\"ts\">\n/**\n * Collapse — one disclosure: a trigger that shows and hides a single region\n * of content. The generic primitive under Accordion's row-group; the state\n * machinery is Reka's Collapsible, so controlled and uncontrolled use,\n * aria-expanded/aria-controls wiring and rapid open/close are all owned\n * there rather than reimplemented here.\n *\n * Expansion animates on the shared height pair (`--animate-expand` /\n * `--animate-collapse`) keyed to Reka's measured\n * `--reka-collapsible-content-height`; padding lives on an inner wrapper\n * because a padded outer box would pop at both ends of the animation while\n * its height interpolated.\n */\n</script>\n\n<script setup lang=\"ts\">\nimport { ChevronDown } from \"@lucide/vue\";\nimport { CollapsibleContent, CollapsibleRoot, CollapsibleTrigger } from \"reka-ui\";\nimport { cn, optional } from \"@ecoma-io/loom-core\";\nimport { useId } from \"vue\";\n\nwithDefaults(\n defineProps<{\n /**\n * Controls visibility; pair with `v-model:open`. Omit it and the collapse\n * owns its own state, seeded by `defaultOpen`.\n */\n open?: boolean | undefined;\n /** The uncontrolled starting state. */\n defaultOpen?: boolean;\n /** Unavailable rather than hidden: the trigger stays put and inert. */\n disabled?: boolean;\n }>(),\n {\n // `undefined` is the uncontrolled state, and only an explicit default\n // keeps it reachable past Vue's Boolean casting.\n open: undefined,\n defaultOpen: false,\n disabled: false,\n },\n);\n\ndefineEmits<{ \"update:open\": [value: boolean] }>();\n\n// Reka leaves the trigger's aria-controls pointing at an id its Content only\n// registers non-reactively, so a closed-first render ships an empty IDREF.\n// Both halves get explicit ids here instead; fallthrough attributes merge\n// after Reka's own, which is the same mechanism AlertDialog uses to clear\n// its dangling describedby.\nconst triggerId = useId();\nconst contentId = useId();\n</script>\n\n<template>\n <CollapsibleRoot\n v-slot=\"{ open: state }\"\n v-bind=\"optional({ open })\"\n :default-open=\"defaultOpen\"\n :disabled=\"disabled\"\n :unmount-on-hide=\"false\"\n @update:open=\"$emit('update:open', $event)\"\n >\n <CollapsibleTrigger\n :id=\"triggerId\"\n :aria-controls=\"contentId\"\n :class=\"\n cn(\n 'group flex w-full items-center justify-between gap-3 rounded-md px-4 py-3 text-left text-sm font-medium',\n 'transition-colors duration-fast ease-out hover:bg-subtle',\n 'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo',\n 'disabled:pointer-events-none disabled:bg-transparent disabled:text-muted-foreground',\n )\n \"\n >\n <!-- @slot The control's visible face, inside a real `<button>` carrying\n aria-expanded/aria-controls and the canonical focus ring. `open`\n mirrors the real state, controlled or not. -->\n <slot name=\"trigger\" :open=\"state\" />\n <ChevronDown\n class=\"h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-fast ease-out group-data-[state=open]:rotate-180\"\n aria-hidden=\"true\"\n />\n </CollapsibleTrigger>\n <!-- Mounted even while closed: the region id in aria-controls has to\n resolve for a screen reader before the first expansion, and a\n persistent element is what lets the collapse animation actually play\n instead of being unmounted mid-frame. Reka conceals the closed\n region with hidden=\"until-found\". -->\n <CollapsibleContent\n data-loom-collapse=\"\"\n class=\"overflow-hidden data-[state=open]:animate-expand data-[state=closed]:animate-collapse\"\n >\n <!-- The animated box carries no padding of its own: interpolating\n height around fixed padding pops at both ends of the film. This\n inner div also carries the id the trigger references: Reka writes\n its own content id after any we pass, so the reference lands here\n where it actually resolves. -->\n <div :id=\"contentId\" class=\"px-4 pb-4\">\n <!-- @slot The region's content. Nested collapses work unchanged —\n each owns its own region. -->\n <slot />\n </div>\n </CollapsibleContent>\n </CollapsibleRoot>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkDA,MAAM,YAAY,MAAM;EACxB,MAAM,YAAY,MAAM;;GAItB,OAAA,UAAA,GAAA,YAiDkB,MAAA,eAAA,GAjDlB,WAEU,MA+CQ,QAAA,CA/CA,CAAA,EAAA,MAAG,QAAA,KAAI,CAAA,GAAA;IACtB,gBAAc,QAAA;IACd,UAAU,QAAA;IACV,mBAAiB;IACjB,iBAAW,OAAA,OAAA,OAAA,MAAA,WAAEA,KAAAA,MAAK,eAAgB,MAAM;;IAEzC,SAAA,SAoBqB,EAAA,MA3BL,YAAK,CAOrB,YAoBqB,MAAA,kBAAA,GAAA;KAnBlB,IAAI,MAAA,SAAA;KACJ,iBAAe,MAAA,SAAA;KACf,OAAK,eAAW,MAAA,EAAA,CAAE,CAAA,2GAAA,4DAAA,+GAAA,qFAAA,CAAA;;KAYnB,SAAA,cAAqC,CAArC,WAAqC,KAAA,QAAA,WAAA,EAAf,MAAM,MAAK,CAAA,GACjC,YAGE,MAAA,WAAA,GAAA;MAFA,OAAM;MACN,eAAY;;;;;;;IAQhB,CAAA,GAAA,YAcqB,MAAA,kBAAA,GAAA;KAbnB,sBAAmB;KACnB,OAAM;;KAON,SAAA,cAIM,CAJN,mBAIM,OAAA;MAJA,IAAI,MAAA,SAAA;MAAW,OAAM;KAGzB,GAAA,CAAA,WAAQ,KAAA,QAAA,SAAA,CAAA,GAAA,GAAA,UAAA,CAAA,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../packages/primitives/collapse/src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC"}
|