@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":"Button.js","names":[],"sources":["../../../../../packages/primitives/button/src/Button.vue"],"sourcesContent":["<script lang=\"ts\">\nimport { cva } from \"class-variance-authority\";\n\n/**\n * The six weights, and the choice between them is about emphasis rather than\n * colour: `primary` is the one action a screen is asking for, `destructive` is\n * the one it is warning about, and the four between them step down.\n */\nexport type ButtonVariant =\n \"primary\" | \"secondary\" | \"subtle\" | \"outline\" | \"ghost\" | \"destructive\";\n\nexport type ButtonSize = \"sm\" | \"md\" | \"lg\" | \"icon\" | \"icon-sm\";\n\n/**\n * One row per rest-state variant, naming the fill and the label token the\n * variant paints. Kept separate from the `cva` call because it is also the\n * contract `Button.contrast.test.ts` reads: that file derives every variant's\n * pairing from here, so a variant added to the map is measured on arrival\n * rather than when someone remembers to add a row to a test. The two halves\n * — this map and the `cva` call consuming it — cannot drift apart, because\n * `cva` is handed this very object.\n *\n * `satisfies` is what keeps this map and the union honest. The union above is\n * the name a consumer imports and the documentation prints; the map below is\n * the classes. Adding a member to one and not the other stops compiling, so\n * neither can be the stale copy of the other.\n */\nexport const buttonVariantClasses = {\n primary: \"bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm\",\n secondary: \"bg-secondary text-secondary-foreground hover:bg-secondary/70\",\n subtle: \"bg-transparent text-foreground hover:bg-subtle\",\n outline: \"border border-input bg-transparent text-foreground hover:bg-subtle\",\n ghost: \"bg-transparent text-muted-foreground hover:bg-subtle hover:text-foreground\",\n destructive: \"bg-destructive text-destructive-foreground hover:bg-destructive/90 shadow-sm\",\n} satisfies Record<ButtonVariant, string>;\n\n/**\n * Button — the workhorse, and the primitive that encodes Loom's press\n * language:\n *\n * • hover lifts by fill, not by shadow\n * • active presses down (scale 0.97) — physical, causal feedback\n * • focus blooms the primary ring\n * • loading plays a kinetic swap under a shimmer sweep: the label rolls up\n * and out, a progress arc springs in, the loading text rises last — and\n * both layers share one grid cell, so the width never jumps mid-film\n *\n * Unavailable is painted in colours rather than dimmed, and it is keyed off the\n * `disabled` prop rather than off the DOM disabled state — a loading button is\n * also DOM-disabled yet has to read as \"working\", not \"unavailable\". See the\n * drained treatment on the root element for the measurements that removed the\n * dim.\n */\nexport const buttonVariants = cva(\n [\n \"relative inline-flex items-center justify-center gap-2 whitespace-nowrap select-none\",\n \"rounded-md text-sm font-medium\",\n \"[transition:transform_var(--duration-fast)_var(--ease-spring),background-color_var(--duration-fast)_var(--ease-out),color_var(--duration-fast)_var(--ease-out),box-shadow_var(--duration-fast)_var(--ease-out)]\",\n \"active:scale-press focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo\",\n \"disabled:pointer-events-none\",\n ],\n {\n variants: {\n variant: buttonVariantClasses,\n size: {\n sm: \"h-8 px-3 text-xs\",\n md: \"h-9 px-4\",\n lg: \"h-11 px-6 text-base\",\n icon: \"h-9 w-9 p-0\",\n // A square icon button for dense rows, matching `sm`'s height so a row\n // action lines up with that row's text controls. Still 32px — well\n // over the 24px minimum target size (WCAG 2.2 SC 2.5.8) — so density\n // here costs no reachability. It exists because consumers were\n // reaching for `size=\"icon\"` and then hand-overriding `class=\"h-8 w-8\"`,\n // which silently re-litigates the target-size question at every call\n // site.\n \"icon-sm\": \"h-8 w-8 p-0\",\n } satisfies Record<ButtonSize, string>,\n },\n defaultVariants: { variant: \"primary\", size: \"md\" },\n },\n);\n</script>\n\n<script setup lang=\"ts\">\nimport { cn } from \"@ecoma-io/loom-core\";\n\nwithDefaults(\n defineProps<{\n /** How much emphasis the action carries. */\n variant?: ButtonVariant;\n /** Control height. `icon` and `icon-sm` are square, for a glyph alone. */\n size?: ButtonSize;\n /** Locks the button and plays the kinetic swap (label → arc + loadingText). */\n loading?: boolean;\n /** Text shown beside the progress arc while loading, e.g. \"Saving…\". */\n loadingText?: string;\n /** Unavailable rather than busy — drains the button to the neutral well and blocks pointer events. */\n disabled?: boolean;\n /** The native button type. Defaults to `button`, never an accidental submit. */\n type?: \"button\" | \"submit\" | \"reset\";\n }>(),\n { variant: \"primary\", size: \"md\", loading: false, disabled: false, type: \"button\" },\n);\n</script>\n\n<template>\n <button\n :type=\"type\"\n :disabled=\"disabled || loading\"\n :data-loading=\"loading || undefined\"\n :aria-busy=\"loading || undefined\"\n :class=\"\n cn(\n buttonVariants({ variant, size }),\n 'group',\n // Drained, not dimmed. `opacity-50` here was the library's most visible\n // instance of the mistake, because a button is nothing but a label on a\n // fill and the dim took both together: measured on the built site, a\n // disabled `primary` label came out at 1.62:1 against its own faded fill\n // and a disabled `outline` at 3.14:1 on the page ground. Neither is\n // reachable by recolouring the label, since the fade multiplied whatever\n // colour it was given.\n //\n // One neutral treatment for all six variants rather than six drained\n // pairs: the fill is what carries a variant's emphasis, and an\n // unavailable button has no emphasis to carry. It is the same well Chip\n // and Stepper drain to, so an unavailable control looks the same\n // wherever it appears. Every variant still changes visibly on the way\n // in — the two filled ones give up their hue, the three transparent ones\n // gain a fill — and the native `disabled` attribute carries the fact to\n // assistive tech, so none of it rests on colour alone.\n //\n // `border-border` and not the strong weight, which is what `outline`\n // used to take here. `theme.css` reserves the strong hairline for a\n // block that must *assert* its edge, and an unavailable control is the\n // one thing that must not; slackening from `input` to the lighter\n // `border` is also the same step every other unavailable control in the\n // library now makes, so a disabled Button beside a disabled TextField\n // wears the same rim rather than a heavier one.\n disabled && 'border-border bg-muted text-muted-foreground shadow-none',\n )\n \"\n >\n <!-- The shimmer sweep, while loading. -->\n <span\n v-if=\"loading\"\n aria-hidden=\"true\"\n class=\"pointer-events-none absolute inset-0 rounded-md bg-[linear-gradient(110deg,transparent_35%,--alpha(var(--color-primary)/25%)_50%,transparent_65%)] bg-[length:220%_100%] animate-shimmer\"\n />\n <!-- The kinetic swap. Both layers live in the same grid cell, so the\n button keeps the width of the wider one and nothing jumps mid-swap.\n Only the layer for the current state is exposed to assistive\n technology; the other is aria-hidden, because opacity alone does not\n hide anything from a screen reader. -->\n <span class=\"inline-grid place-items-center\">\n <!-- Beat 1 — the label rolls up and out. -->\n <span\n :aria-hidden=\"loading || undefined\"\n class=\"[grid-area:1/1] inline-flex items-center gap-2 transition-[opacity,transform] duration-fast ease-out group-data-[loading]:-translate-y-1 group-data-[loading]:opacity-0\"\n >\n <slot />\n </span>\n <span\n :aria-hidden=\"!loading || undefined\"\n class=\"pointer-events-none [grid-area:1/1] inline-flex items-center gap-2\"\n >\n <!-- Beat 2 — the progress arc springs in from below. The spinning svg\n sits inside a wrapper because `animate-spin` owns `transform`: the\n entry scale and translate have to live on a separate element, or\n the spin keyframe would overwrite them. It is the same arc the\n standalone Spinner draws, inlined and aria-hidden — Spinner's\n role=\"status\" contract is wrong inside an aria-busy button, where\n it would announce the state twice. -->\n <span\n class=\"transition-[opacity,transform] duration-fast ease-spring opacity-0 scale-50 translate-y-1 group-data-[loading]:translate-y-0 group-data-[loading]:scale-100 group-data-[loading]:opacity-100\"\n >\n <svg class=\"h-4 w-4 animate-spin\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <circle class=\"stroke-current opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke-width=\"4\" />\n <path\n class=\"fill-current opacity-75\"\n d=\"M4 12a8 8 0 0 1 8-8V0C5.373 0 0 5.373 0 12h4z\"\n />\n </svg>\n </span>\n <!-- Beat 3 — the loading text rises last. The stagger delay applies on\n entry only (exit keeps delay 0, so the film cuts out clean), and\n it is cancelled under reduced motion so the instant swap is not\n lagged by a delay nobody asked for. -->\n <span\n v-if=\"loadingText\"\n class=\"transition-[opacity,transform] duration-100 ease-out opacity-0 translate-y-1 group-data-[loading]:translate-y-0 group-data-[loading]:opacity-100 group-data-[loading]:delay-75 motion-reduce:group-data-[loading]:delay-0\"\n >\n {{ loadingText }}\n </span>\n </span>\n </span>\n </button>\n</template>\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"Button.js","names":[],"sources":["../../../../../packages/primitives/button/src/Button.vue"],"sourcesContent":["<script lang=\"ts\">\nimport { cva } from \"class-variance-authority\";\n\n/**\n * The six weights, and the choice between them is about emphasis rather than\n * colour: `primary` is the one action a screen is asking for, `destructive` is\n * the one it is warning about, and the four between them step down.\n */\nexport type ButtonVariant =\n \"primary\" | \"secondary\" | \"subtle\" | \"outline\" | \"ghost\" | \"destructive\";\n\nexport type ButtonSize = \"sm\" | \"md\" | \"lg\" | \"icon\" | \"icon-sm\";\n\n/**\n * One row per rest-state variant, naming the fill and the label token the\n * variant paints. Kept separate from the `cva` call because it is also the\n * contract `Button.contrast.test.ts` reads: that file derives every variant's\n * pairing from here, so a variant added to the map is measured on arrival\n * rather than when someone remembers to add a row to a test. The two halves\n * — this map and the `cva` call consuming it — cannot drift apart, because\n * `cva` is handed this very object.\n *\n * `satisfies` is what keeps this map and the union honest. The union above is\n * the name a consumer imports and the documentation prints; the map below is\n * the classes. Adding a member to one and not the other stops compiling, so\n * neither can be the stale copy of the other.\n */\nexport const buttonVariantClasses = {\n primary: \"bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm\",\n secondary: \"bg-secondary text-secondary-foreground hover:bg-secondary/70\",\n subtle: \"bg-transparent text-foreground hover:bg-subtle\",\n outline: \"border border-input bg-transparent text-foreground hover:bg-subtle\",\n ghost: \"bg-transparent text-muted-foreground hover:bg-subtle hover:text-foreground\",\n destructive: \"bg-destructive text-destructive-foreground hover:bg-destructive/90 shadow-sm\",\n} satisfies Record<ButtonVariant, string>;\n\n/**\n * Button — the workhorse, and the primitive that encodes Loom's press\n * language:\n *\n * • hover lifts by fill, not by shadow\n * • active presses down (scale 0.97) — physical, causal feedback\n * • focus blooms the primary ring\n * • loading plays a kinetic swap under a shimmer sweep: the label rolls up\n * and out, a progress arc springs in, the loading text rises last — and\n * both layers share one grid cell, so the width never jumps mid-film\n *\n * Unavailable is painted in colours rather than dimmed, and it is keyed off the\n * `disabled` prop rather than off the DOM disabled state — a loading button is\n * also DOM-disabled yet has to read as \"working\", not \"unavailable\". See the\n * drained treatment on the root element for the measurements that removed the\n * dim.\n */\nexport const buttonVariants = cva(\n [\n \"relative inline-flex items-center justify-center gap-2 whitespace-nowrap select-none\",\n \"rounded-md text-sm font-medium\",\n \"[transition:transform_var(--duration-fast)_var(--ease-spring),background-color_var(--duration-fast)_var(--ease-out),color_var(--duration-fast)_var(--ease-out),box-shadow_var(--duration-fast)_var(--ease-out)]\",\n \"active:scale-press focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo\",\n \"disabled:pointer-events-none\",\n ],\n {\n variants: {\n variant: buttonVariantClasses,\n size: {\n sm: \"h-8 px-3 text-xs\",\n md: \"h-9 px-4\",\n lg: \"h-11 px-6 text-base\",\n icon: \"h-9 w-9 p-0\",\n // A square icon button for dense rows, matching `sm`'s height so a row\n // action lines up with that row's text controls. Still 32px — well\n // over the 24px minimum target size (WCAG 2.2 SC 2.5.8) — so density\n // here costs no reachability. It exists because consumers were\n // reaching for `size=\"icon\"` and then hand-overriding `class=\"h-8 w-8\"`,\n // which silently re-litigates the target-size question at every call\n // site.\n \"icon-sm\": \"h-8 w-8 p-0\",\n } satisfies Record<ButtonSize, string>,\n },\n defaultVariants: { variant: \"primary\", size: \"md\" },\n },\n);\n</script>\n\n<script setup lang=\"ts\">\nimport { cn } from \"@ecoma-io/loom-core\";\n\nwithDefaults(\n defineProps<{\n /** How much emphasis the action carries. */\n variant?: ButtonVariant;\n /** Control height. `icon` and `icon-sm` are square, for a glyph alone. */\n size?: ButtonSize;\n /** Locks the button and plays the kinetic swap (label → arc + loadingText). */\n loading?: boolean;\n /** Text shown beside the progress arc while loading, e.g. \"Saving…\". */\n loadingText?: string;\n /** Unavailable rather than busy — drains the button to the neutral well and blocks pointer events. */\n disabled?: boolean;\n /** The native button type. Defaults to `button`, never an accidental submit. */\n type?: \"button\" | \"submit\" | \"reset\";\n }>(),\n { variant: \"primary\", size: \"md\", loading: false, disabled: false, type: \"button\" },\n);\n</script>\n\n<template>\n <button\n :type=\"type\"\n :disabled=\"disabled || loading\"\n :data-loading=\"loading || undefined\"\n :aria-busy=\"loading || undefined\"\n :class=\"\n cn(\n buttonVariants({ variant, size }),\n 'group',\n // Drained, not dimmed. `opacity-50` here was the library's most visible\n // instance of the mistake, because a button is nothing but a label on a\n // fill and the dim took both together: measured on the built site, a\n // disabled `primary` label came out at 1.62:1 against its own faded fill\n // and a disabled `outline` at 3.14:1 on the page ground. Neither is\n // reachable by recolouring the label, since the fade multiplied whatever\n // colour it was given.\n //\n // One neutral treatment for all six variants rather than six drained\n // pairs: the fill is what carries a variant's emphasis, and an\n // unavailable button has no emphasis to carry. It is the same well Chip\n // and Stepper drain to, so an unavailable control looks the same\n // wherever it appears. Every variant still changes visibly on the way\n // in — the two filled ones give up their hue, the three transparent ones\n // gain a fill — and the native `disabled` attribute carries the fact to\n // assistive tech, so none of it rests on colour alone.\n //\n // `border-border` and not the strong weight, which is what `outline`\n // used to take here. `theme.css` reserves the strong hairline for a\n // block that must *assert* its edge, and an unavailable control is the\n // one thing that must not; slackening from `input` to the lighter\n // `border` is also the same step every other unavailable control in the\n // library now makes, so a disabled Button beside a disabled TextField\n // wears the same rim rather than a heavier one.\n disabled && 'border-border bg-muted text-muted-foreground shadow-none',\n )\n \"\n >\n <!-- The shimmer sweep, while loading. -->\n <span\n v-if=\"loading\"\n aria-hidden=\"true\"\n class=\"pointer-events-none absolute inset-0 rounded-md bg-[linear-gradient(110deg,transparent_35%,--alpha(var(--color-primary)/25%)_50%,transparent_65%)] bg-[length:220%_100%] animate-shimmer\"\n />\n <!-- The kinetic swap. Both layers live in the same grid cell, so the\n button keeps the width of the wider one and nothing jumps mid-swap.\n Only the layer for the current state is exposed to assistive\n technology; the other is aria-hidden, because opacity alone does not\n hide anything from a screen reader. -->\n <span class=\"inline-grid place-items-center\">\n <!-- Beat 1 — the label rolls up and out. -->\n <span\n :aria-hidden=\"loading || undefined\"\n class=\"[grid-area:1/1] inline-flex items-center gap-2 transition-[opacity,transform] duration-fast ease-out group-data-[loading]:-translate-y-1 group-data-[loading]:opacity-0\"\n >\n <slot />\n </span>\n <span\n :aria-hidden=\"!loading || undefined\"\n class=\"pointer-events-none [grid-area:1/1] inline-flex items-center gap-2\"\n >\n <!-- Beat 2 — the progress arc springs in from below. The spinning svg\n sits inside a wrapper because `animate-spin` owns `transform`: the\n entry scale and translate have to live on a separate element, or\n the spin keyframe would overwrite them. It is the same arc the\n standalone Spinner draws, inlined and aria-hidden — Spinner's\n role=\"status\" contract is wrong inside an aria-busy button, where\n it would announce the state twice. -->\n <span\n class=\"transition-[opacity,transform] duration-fast ease-spring opacity-0 scale-50 translate-y-1 group-data-[loading]:translate-y-0 group-data-[loading]:scale-100 group-data-[loading]:opacity-100\"\n >\n <svg class=\"h-4 w-4 animate-spin\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <circle class=\"stroke-current opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke-width=\"4\" />\n <path\n class=\"fill-current opacity-75\"\n d=\"M4 12a8 8 0 0 1 8-8V0C5.373 0 0 5.373 0 12h4z\"\n />\n </svg>\n </span>\n <!-- Beat 3 — the loading text rises last. The stagger delay applies on\n entry only (exit keeps delay 0, so the film cuts out clean), and\n it is cancelled under reduced motion so the instant swap is not\n lagged by a delay nobody asked for.\n\n The raw 100ms/75ms pair is deliberate: the motion tokens bracket\n this beat without landing on it — `fast` (140ms) is beat 1's own\n exit duration, so sharing it would flatten the stagger into one\n simultaneous move, and `instant` (80ms) reads as a flicker. The\n choreography only makes sense between the two rungs, which no\n token expresses; if a delay token ever lands in theme.css, this\n is the first candidate to move onto it. -->\n <span\n v-if=\"loadingText\"\n class=\"transition-[opacity,transform] duration-100 ease-out opacity-0 translate-y-1 group-data-[loading]:translate-y-0 group-data-[loading]:opacity-100 group-data-[loading]:delay-75 motion-reduce:group-data-[loading]:delay-0\"\n >\n {{ loadingText }}\n </span>\n </span>\n </span>\n </button>\n</template>\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.vue.d.ts","sourceRoot":"","sources":["../../../../../packages/primitives/button/src/Button.vue"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Button.vue.d.ts","sourceRoot":"","sources":["../../../../../packages/primitives/button/src/Button.vue"],"names":[],"mappings":"AAuNA;;;;GAIG;AACH,MAAM,MAAM,aAAa,GACvB,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,aAAa,CAAC;AAE3E,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAAC;AAEjE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,oBAAoB;;;;;;;CAOQ,CAAC;AAE1C;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,cAAc;;;8EA4B1B,CAAC;wBACmB,OAAO,YAAY;AAAxC,wBAAyC;AACzC,QAAA,MAAM,YAAY;IAEd,4CAA4C;cAClC,aAAa;IACvB,0EAA0E;WACnE,UAAU;IACjB,+EAA+E;cACrE,OAAO;IACjB,wEAAwE;kBAC1D,MAAM;IACpB,sGAAsG;eAC3F,OAAO;IAClB,gFAAgF;WACzE,QAAQ,GAAG,QAAQ,GAAG,OAAO;;IAXpC,4CAA4C;cAClC,aAAa;IACvB,0EAA0E;WACnE,UAAU;IACjB,+EAA+E;cACrE,OAAO;IACjB,wEAAwE;kBAC1D,MAAM;IACpB,sGAAsG;eAC3F,OAAO;IAClB,gFAAgF;WACzE,QAAQ,GAAG,QAAQ,GAAG,OAAO;;UAR7B,UAAU;UAQV,QAAQ,GAAG,QAAQ,GAAG,OAAO;cAFzB,OAAO;aARR,aAAa;aAIb,OAAO;;cAqKP,CAAC,KAAK,IAAgB,KAAK,GAAG;EAUxC,CAAC;AAWL,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
|
package/dist/packages/primitives/button/src/Button.vue_vue_type_script_setup_true_lang.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Button.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../../../packages/primitives/button/src/Button.vue"],"sourcesContent":["<script lang=\"ts\">\nimport { cva } from \"class-variance-authority\";\n\n/**\n * The six weights, and the choice between them is about emphasis rather than\n * colour: `primary` is the one action a screen is asking for, `destructive` is\n * the one it is warning about, and the four between them step down.\n */\nexport type ButtonVariant =\n \"primary\" | \"secondary\" | \"subtle\" | \"outline\" | \"ghost\" | \"destructive\";\n\nexport type ButtonSize = \"sm\" | \"md\" | \"lg\" | \"icon\" | \"icon-sm\";\n\n/**\n * One row per rest-state variant, naming the fill and the label token the\n * variant paints. Kept separate from the `cva` call because it is also the\n * contract `Button.contrast.test.ts` reads: that file derives every variant's\n * pairing from here, so a variant added to the map is measured on arrival\n * rather than when someone remembers to add a row to a test. The two halves\n * — this map and the `cva` call consuming it — cannot drift apart, because\n * `cva` is handed this very object.\n *\n * `satisfies` is what keeps this map and the union honest. The union above is\n * the name a consumer imports and the documentation prints; the map below is\n * the classes. Adding a member to one and not the other stops compiling, so\n * neither can be the stale copy of the other.\n */\nexport const buttonVariantClasses = {\n primary: \"bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm\",\n secondary: \"bg-secondary text-secondary-foreground hover:bg-secondary/70\",\n subtle: \"bg-transparent text-foreground hover:bg-subtle\",\n outline: \"border border-input bg-transparent text-foreground hover:bg-subtle\",\n ghost: \"bg-transparent text-muted-foreground hover:bg-subtle hover:text-foreground\",\n destructive: \"bg-destructive text-destructive-foreground hover:bg-destructive/90 shadow-sm\",\n} satisfies Record<ButtonVariant, string>;\n\n/**\n * Button — the workhorse, and the primitive that encodes Loom's press\n * language:\n *\n * • hover lifts by fill, not by shadow\n * • active presses down (scale 0.97) — physical, causal feedback\n * • focus blooms the primary ring\n * • loading plays a kinetic swap under a shimmer sweep: the label rolls up\n * and out, a progress arc springs in, the loading text rises last — and\n * both layers share one grid cell, so the width never jumps mid-film\n *\n * Unavailable is painted in colours rather than dimmed, and it is keyed off the\n * `disabled` prop rather than off the DOM disabled state — a loading button is\n * also DOM-disabled yet has to read as \"working\", not \"unavailable\". See the\n * drained treatment on the root element for the measurements that removed the\n * dim.\n */\nexport const buttonVariants = cva(\n [\n \"relative inline-flex items-center justify-center gap-2 whitespace-nowrap select-none\",\n \"rounded-md text-sm font-medium\",\n \"[transition:transform_var(--duration-fast)_var(--ease-spring),background-color_var(--duration-fast)_var(--ease-out),color_var(--duration-fast)_var(--ease-out),box-shadow_var(--duration-fast)_var(--ease-out)]\",\n \"active:scale-press focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo\",\n \"disabled:pointer-events-none\",\n ],\n {\n variants: {\n variant: buttonVariantClasses,\n size: {\n sm: \"h-8 px-3 text-xs\",\n md: \"h-9 px-4\",\n lg: \"h-11 px-6 text-base\",\n icon: \"h-9 w-9 p-0\",\n // A square icon button for dense rows, matching `sm`'s height so a row\n // action lines up with that row's text controls. Still 32px — well\n // over the 24px minimum target size (WCAG 2.2 SC 2.5.8) — so density\n // here costs no reachability. It exists because consumers were\n // reaching for `size=\"icon\"` and then hand-overriding `class=\"h-8 w-8\"`,\n // which silently re-litigates the target-size question at every call\n // site.\n \"icon-sm\": \"h-8 w-8 p-0\",\n } satisfies Record<ButtonSize, string>,\n },\n defaultVariants: { variant: \"primary\", size: \"md\" },\n },\n);\n</script>\n\n<script setup lang=\"ts\">\nimport { cn } from \"@ecoma-io/loom-core\";\n\nwithDefaults(\n defineProps<{\n /** How much emphasis the action carries. */\n variant?: ButtonVariant;\n /** Control height. `icon` and `icon-sm` are square, for a glyph alone. */\n size?: ButtonSize;\n /** Locks the button and plays the kinetic swap (label → arc + loadingText). */\n loading?: boolean;\n /** Text shown beside the progress arc while loading, e.g. \"Saving…\". */\n loadingText?: string;\n /** Unavailable rather than busy — drains the button to the neutral well and blocks pointer events. */\n disabled?: boolean;\n /** The native button type. Defaults to `button`, never an accidental submit. */\n type?: \"button\" | \"submit\" | \"reset\";\n }>(),\n { variant: \"primary\", size: \"md\", loading: false, disabled: false, type: \"button\" },\n);\n</script>\n\n<template>\n <button\n :type=\"type\"\n :disabled=\"disabled || loading\"\n :data-loading=\"loading || undefined\"\n :aria-busy=\"loading || undefined\"\n :class=\"\n cn(\n buttonVariants({ variant, size }),\n 'group',\n // Drained, not dimmed. `opacity-50` here was the library's most visible\n // instance of the mistake, because a button is nothing but a label on a\n // fill and the dim took both together: measured on the built site, a\n // disabled `primary` label came out at 1.62:1 against its own faded fill\n // and a disabled `outline` at 3.14:1 on the page ground. Neither is\n // reachable by recolouring the label, since the fade multiplied whatever\n // colour it was given.\n //\n // One neutral treatment for all six variants rather than six drained\n // pairs: the fill is what carries a variant's emphasis, and an\n // unavailable button has no emphasis to carry. It is the same well Chip\n // and Stepper drain to, so an unavailable control looks the same\n // wherever it appears. Every variant still changes visibly on the way\n // in — the two filled ones give up their hue, the three transparent ones\n // gain a fill — and the native `disabled` attribute carries the fact to\n // assistive tech, so none of it rests on colour alone.\n //\n // `border-border` and not the strong weight, which is what `outline`\n // used to take here. `theme.css` reserves the strong hairline for a\n // block that must *assert* its edge, and an unavailable control is the\n // one thing that must not; slackening from `input` to the lighter\n // `border` is also the same step every other unavailable control in the\n // library now makes, so a disabled Button beside a disabled TextField\n // wears the same rim rather than a heavier one.\n disabled && 'border-border bg-muted text-muted-foreground shadow-none',\n )\n \"\n >\n <!-- The shimmer sweep, while loading. -->\n <span\n v-if=\"loading\"\n aria-hidden=\"true\"\n class=\"pointer-events-none absolute inset-0 rounded-md bg-[linear-gradient(110deg,transparent_35%,--alpha(var(--color-primary)/25%)_50%,transparent_65%)] bg-[length:220%_100%] animate-shimmer\"\n />\n <!-- The kinetic swap. Both layers live in the same grid cell, so the\n button keeps the width of the wider one and nothing jumps mid-swap.\n Only the layer for the current state is exposed to assistive\n technology; the other is aria-hidden, because opacity alone does not\n hide anything from a screen reader. -->\n <span class=\"inline-grid place-items-center\">\n <!-- Beat 1 — the label rolls up and out. -->\n <span\n :aria-hidden=\"loading || undefined\"\n class=\"[grid-area:1/1] inline-flex items-center gap-2 transition-[opacity,transform] duration-fast ease-out group-data-[loading]:-translate-y-1 group-data-[loading]:opacity-0\"\n >\n <slot />\n </span>\n <span\n :aria-hidden=\"!loading || undefined\"\n class=\"pointer-events-none [grid-area:1/1] inline-flex items-center gap-2\"\n >\n <!-- Beat 2 — the progress arc springs in from below. The spinning svg\n sits inside a wrapper because `animate-spin` owns `transform`: the\n entry scale and translate have to live on a separate element, or\n the spin keyframe would overwrite them. It is the same arc the\n standalone Spinner draws, inlined and aria-hidden — Spinner's\n role=\"status\" contract is wrong inside an aria-busy button, where\n it would announce the state twice. -->\n <span\n class=\"transition-[opacity,transform] duration-fast ease-spring opacity-0 scale-50 translate-y-1 group-data-[loading]:translate-y-0 group-data-[loading]:scale-100 group-data-[loading]:opacity-100\"\n >\n <svg class=\"h-4 w-4 animate-spin\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <circle class=\"stroke-current opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke-width=\"4\" />\n <path\n class=\"fill-current opacity-75\"\n d=\"M4 12a8 8 0 0 1 8-8V0C5.373 0 0 5.373 0 12h4z\"\n />\n </svg>\n </span>\n <!-- Beat 3 — the loading text rises last. The stagger delay applies on\n entry only (exit keeps delay 0, so the film cuts out clean), and\n it is cancelled under reduced motion so the instant swap is not\n lagged by a delay nobody asked for. -->\n <span\n v-if=\"loadingText\"\n class=\"transition-[opacity,transform] duration-100 ease-out opacity-0 translate-y-1 group-data-[loading]:translate-y-0 group-data-[loading]:opacity-100 group-data-[loading]:delay-75 motion-reduce:group-data-[loading]:delay-0\"\n >\n {{ loadingText }}\n </span>\n </span>\n </span>\n </button>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,IAAa,uBAAuB;CAClC,SAAS;CACT,WAAW;CACX,QAAQ;CACR,SAAS;CACT,OAAO;CACP,aAAa;AACf;;;;;;;;;;;;;;;;;;AAmBA,IAAa,iBAAiB,IAC5B;CACE;CACA;CACA;CACA;CACA;AACF,GACA;CACE,UAAU;EACR,SAAS;EACT,MAAM;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,MAAM;GAQN,WAAW;EACb;CACF;CACA,iBAAiB;EAAE,SAAS;EAAW,MAAM;CAAK;AACpD,CACF;;;;;;;;;;;;;;;;;;;GA0BE,OAAA,UAAA,GAAA,mBA0FS,UAAA;IAzFN,MAAM,QAAA;IACN,UAAU,QAAA,YAAY,QAAA;IACtB,gBAAc,QAAA,WAAW,KAAA;IACzB,aAAW,QAAA,WAAW,KAAA;IACtB,OAAK,eAAS,MAAA,EAAA,CAAE,CAAU,MAAA,cAAA,CAAc,CAAA;KAAA,SAAG,QAAA;KAAO,MAAE,QAAA;IAAI,CAAA,GAAA,SAAuqD,QAAA,YAAQ,0DAAA,CAAA;GAkChuD,GAAA,CAAA,QAAA,WADR,UAAA,GAAA,mBAIE,QAJF,UAIE,KAAA,mBAAA,IAAA,IAAA,GAMF,mBAyCO,QAzCP,YAyCO,CAvCL,mBAKO,QAAA;IAJJ,eAAa,QAAA,WAAW,KAAA;IACzB,OAAM;GAEN,GAAA,CAAA,WAAQ,KAAA,QAAA,SAAA,CAAA,GAAA,GAAA,UAAA,GAEV,mBAgCO,QAAA;IA/BJ,eAAW,CAAG,QAAA,WAAW,KAAA;IAC1B,OAAM;GASN,GAAA,CAAA,OAAA,OAAA,OAAA,KAAA,mBAUO,QAAA,EATL,OAAM,+LAA8L,GAAA,CAEpM,mBAMM,OAAA;IAND,OAAM;IAAuB,SAAQ;IAAY,MAAK;IAAO,eAAY;GAC5E,GAAA,CAAA,mBAAoF,UAAA;IAA5E,OAAM;IAA4B,IAAG;IAAK,IAAG;IAAK,GAAE;IAAK,gBAAa;GAC9E,CAAA,GAAA,mBAGE,QAAA;IAFA,OAAM;IACN,GAAE;GASA,CAAA,CAAA,CAAA,CAAA,GAAA,EAAA,IAAA,QAAA,eADR,UAAA,GAAA,mBAKO,QALP,YAKO,gBADF,QAAA,WAAW,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,GAAA,GAAA,UAAA,CAAA,CAAA,CAAA,GAAA,IAAA,UAAA"}
|
|
1
|
+
{"version":3,"file":"Button.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../../../packages/primitives/button/src/Button.vue"],"sourcesContent":["<script lang=\"ts\">\nimport { cva } from \"class-variance-authority\";\n\n/**\n * The six weights, and the choice between them is about emphasis rather than\n * colour: `primary` is the one action a screen is asking for, `destructive` is\n * the one it is warning about, and the four between them step down.\n */\nexport type ButtonVariant =\n \"primary\" | \"secondary\" | \"subtle\" | \"outline\" | \"ghost\" | \"destructive\";\n\nexport type ButtonSize = \"sm\" | \"md\" | \"lg\" | \"icon\" | \"icon-sm\";\n\n/**\n * One row per rest-state variant, naming the fill and the label token the\n * variant paints. Kept separate from the `cva` call because it is also the\n * contract `Button.contrast.test.ts` reads: that file derives every variant's\n * pairing from here, so a variant added to the map is measured on arrival\n * rather than when someone remembers to add a row to a test. The two halves\n * — this map and the `cva` call consuming it — cannot drift apart, because\n * `cva` is handed this very object.\n *\n * `satisfies` is what keeps this map and the union honest. The union above is\n * the name a consumer imports and the documentation prints; the map below is\n * the classes. Adding a member to one and not the other stops compiling, so\n * neither can be the stale copy of the other.\n */\nexport const buttonVariantClasses = {\n primary: \"bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm\",\n secondary: \"bg-secondary text-secondary-foreground hover:bg-secondary/70\",\n subtle: \"bg-transparent text-foreground hover:bg-subtle\",\n outline: \"border border-input bg-transparent text-foreground hover:bg-subtle\",\n ghost: \"bg-transparent text-muted-foreground hover:bg-subtle hover:text-foreground\",\n destructive: \"bg-destructive text-destructive-foreground hover:bg-destructive/90 shadow-sm\",\n} satisfies Record<ButtonVariant, string>;\n\n/**\n * Button — the workhorse, and the primitive that encodes Loom's press\n * language:\n *\n * • hover lifts by fill, not by shadow\n * • active presses down (scale 0.97) — physical, causal feedback\n * • focus blooms the primary ring\n * • loading plays a kinetic swap under a shimmer sweep: the label rolls up\n * and out, a progress arc springs in, the loading text rises last — and\n * both layers share one grid cell, so the width never jumps mid-film\n *\n * Unavailable is painted in colours rather than dimmed, and it is keyed off the\n * `disabled` prop rather than off the DOM disabled state — a loading button is\n * also DOM-disabled yet has to read as \"working\", not \"unavailable\". See the\n * drained treatment on the root element for the measurements that removed the\n * dim.\n */\nexport const buttonVariants = cva(\n [\n \"relative inline-flex items-center justify-center gap-2 whitespace-nowrap select-none\",\n \"rounded-md text-sm font-medium\",\n \"[transition:transform_var(--duration-fast)_var(--ease-spring),background-color_var(--duration-fast)_var(--ease-out),color_var(--duration-fast)_var(--ease-out),box-shadow_var(--duration-fast)_var(--ease-out)]\",\n \"active:scale-press focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo\",\n \"disabled:pointer-events-none\",\n ],\n {\n variants: {\n variant: buttonVariantClasses,\n size: {\n sm: \"h-8 px-3 text-xs\",\n md: \"h-9 px-4\",\n lg: \"h-11 px-6 text-base\",\n icon: \"h-9 w-9 p-0\",\n // A square icon button for dense rows, matching `sm`'s height so a row\n // action lines up with that row's text controls. Still 32px — well\n // over the 24px minimum target size (WCAG 2.2 SC 2.5.8) — so density\n // here costs no reachability. It exists because consumers were\n // reaching for `size=\"icon\"` and then hand-overriding `class=\"h-8 w-8\"`,\n // which silently re-litigates the target-size question at every call\n // site.\n \"icon-sm\": \"h-8 w-8 p-0\",\n } satisfies Record<ButtonSize, string>,\n },\n defaultVariants: { variant: \"primary\", size: \"md\" },\n },\n);\n</script>\n\n<script setup lang=\"ts\">\nimport { cn } from \"@ecoma-io/loom-core\";\n\nwithDefaults(\n defineProps<{\n /** How much emphasis the action carries. */\n variant?: ButtonVariant;\n /** Control height. `icon` and `icon-sm` are square, for a glyph alone. */\n size?: ButtonSize;\n /** Locks the button and plays the kinetic swap (label → arc + loadingText). */\n loading?: boolean;\n /** Text shown beside the progress arc while loading, e.g. \"Saving…\". */\n loadingText?: string;\n /** Unavailable rather than busy — drains the button to the neutral well and blocks pointer events. */\n disabled?: boolean;\n /** The native button type. Defaults to `button`, never an accidental submit. */\n type?: \"button\" | \"submit\" | \"reset\";\n }>(),\n { variant: \"primary\", size: \"md\", loading: false, disabled: false, type: \"button\" },\n);\n</script>\n\n<template>\n <button\n :type=\"type\"\n :disabled=\"disabled || loading\"\n :data-loading=\"loading || undefined\"\n :aria-busy=\"loading || undefined\"\n :class=\"\n cn(\n buttonVariants({ variant, size }),\n 'group',\n // Drained, not dimmed. `opacity-50` here was the library's most visible\n // instance of the mistake, because a button is nothing but a label on a\n // fill and the dim took both together: measured on the built site, a\n // disabled `primary` label came out at 1.62:1 against its own faded fill\n // and a disabled `outline` at 3.14:1 on the page ground. Neither is\n // reachable by recolouring the label, since the fade multiplied whatever\n // colour it was given.\n //\n // One neutral treatment for all six variants rather than six drained\n // pairs: the fill is what carries a variant's emphasis, and an\n // unavailable button has no emphasis to carry. It is the same well Chip\n // and Stepper drain to, so an unavailable control looks the same\n // wherever it appears. Every variant still changes visibly on the way\n // in — the two filled ones give up their hue, the three transparent ones\n // gain a fill — and the native `disabled` attribute carries the fact to\n // assistive tech, so none of it rests on colour alone.\n //\n // `border-border` and not the strong weight, which is what `outline`\n // used to take here. `theme.css` reserves the strong hairline for a\n // block that must *assert* its edge, and an unavailable control is the\n // one thing that must not; slackening from `input` to the lighter\n // `border` is also the same step every other unavailable control in the\n // library now makes, so a disabled Button beside a disabled TextField\n // wears the same rim rather than a heavier one.\n disabled && 'border-border bg-muted text-muted-foreground shadow-none',\n )\n \"\n >\n <!-- The shimmer sweep, while loading. -->\n <span\n v-if=\"loading\"\n aria-hidden=\"true\"\n class=\"pointer-events-none absolute inset-0 rounded-md bg-[linear-gradient(110deg,transparent_35%,--alpha(var(--color-primary)/25%)_50%,transparent_65%)] bg-[length:220%_100%] animate-shimmer\"\n />\n <!-- The kinetic swap. Both layers live in the same grid cell, so the\n button keeps the width of the wider one and nothing jumps mid-swap.\n Only the layer for the current state is exposed to assistive\n technology; the other is aria-hidden, because opacity alone does not\n hide anything from a screen reader. -->\n <span class=\"inline-grid place-items-center\">\n <!-- Beat 1 — the label rolls up and out. -->\n <span\n :aria-hidden=\"loading || undefined\"\n class=\"[grid-area:1/1] inline-flex items-center gap-2 transition-[opacity,transform] duration-fast ease-out group-data-[loading]:-translate-y-1 group-data-[loading]:opacity-0\"\n >\n <slot />\n </span>\n <span\n :aria-hidden=\"!loading || undefined\"\n class=\"pointer-events-none [grid-area:1/1] inline-flex items-center gap-2\"\n >\n <!-- Beat 2 — the progress arc springs in from below. The spinning svg\n sits inside a wrapper because `animate-spin` owns `transform`: the\n entry scale and translate have to live on a separate element, or\n the spin keyframe would overwrite them. It is the same arc the\n standalone Spinner draws, inlined and aria-hidden — Spinner's\n role=\"status\" contract is wrong inside an aria-busy button, where\n it would announce the state twice. -->\n <span\n class=\"transition-[opacity,transform] duration-fast ease-spring opacity-0 scale-50 translate-y-1 group-data-[loading]:translate-y-0 group-data-[loading]:scale-100 group-data-[loading]:opacity-100\"\n >\n <svg class=\"h-4 w-4 animate-spin\" viewBox=\"0 0 24 24\" fill=\"none\" aria-hidden=\"true\">\n <circle class=\"stroke-current opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke-width=\"4\" />\n <path\n class=\"fill-current opacity-75\"\n d=\"M4 12a8 8 0 0 1 8-8V0C5.373 0 0 5.373 0 12h4z\"\n />\n </svg>\n </span>\n <!-- Beat 3 — the loading text rises last. The stagger delay applies on\n entry only (exit keeps delay 0, so the film cuts out clean), and\n it is cancelled under reduced motion so the instant swap is not\n lagged by a delay nobody asked for.\n\n The raw 100ms/75ms pair is deliberate: the motion tokens bracket\n this beat without landing on it — `fast` (140ms) is beat 1's own\n exit duration, so sharing it would flatten the stagger into one\n simultaneous move, and `instant` (80ms) reads as a flicker. The\n choreography only makes sense between the two rungs, which no\n token expresses; if a delay token ever lands in theme.css, this\n is the first candidate to move onto it. -->\n <span\n v-if=\"loadingText\"\n class=\"transition-[opacity,transform] duration-100 ease-out opacity-0 translate-y-1 group-data-[loading]:translate-y-0 group-data-[loading]:opacity-100 group-data-[loading]:delay-75 motion-reduce:group-data-[loading]:delay-0\"\n >\n {{ loadingText }}\n </span>\n </span>\n </span>\n </button>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,IAAa,uBAAuB;CAClC,SAAS;CACT,WAAW;CACX,QAAQ;CACR,SAAS;CACT,OAAO;CACP,aAAa;AACf;;;;;;;;;;;;;;;;;;AAmBA,IAAa,iBAAiB,IAC5B;CACE;CACA;CACA;CACA;CACA;AACF,GACA;CACE,UAAU;EACR,SAAS;EACT,MAAM;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,MAAM;GAQN,WAAW;EACb;CACF;CACA,iBAAiB;EAAE,SAAS;EAAW,MAAM;CAAK;AACpD,CACF;;;;;;;;;;;;;;;;;;;GA0BE,OAAA,UAAA,GAAA,mBAkGS,UAAA;IAjGN,MAAM,QAAA;IACN,UAAU,QAAA,YAAY,QAAA;IACtB,gBAAc,QAAA,WAAW,KAAA;IACzB,aAAW,QAAA,WAAW,KAAA;IACtB,OAAK,eAAS,MAAA,EAAA,CAAE,CAAU,MAAA,cAAA,CAAc,CAAA;KAAA,SAAG,QAAA;KAAO,MAAE,QAAA;IAAI,CAAA,GAAA,SAAuqD,QAAA,YAAQ,0DAAA,CAAA;GAkChuD,GAAA,CAAA,QAAA,WADR,UAAA,GAAA,mBAIE,QAJF,UAIE,KAAA,mBAAA,IAAA,IAAA,GAMF,mBAiDO,QAjDP,YAiDO,CA/CL,mBAKO,QAAA;IAJJ,eAAa,QAAA,WAAW,KAAA;IACzB,OAAM;GAEN,GAAA,CAAA,WAAQ,KAAA,QAAA,SAAA,CAAA,GAAA,GAAA,UAAA,GAEV,mBAwCO,QAAA;IAvCJ,eAAW,CAAG,QAAA,WAAW,KAAA;IAC1B,OAAM;GASN,GAAA,CAAA,OAAA,OAAA,OAAA,KAAA,mBAUO,QAAA,EATL,OAAM,+LAA8L,GAAA,CAEpM,mBAMM,OAAA;IAND,OAAM;IAAuB,SAAQ;IAAY,MAAK;IAAO,eAAY;GAC5E,GAAA,CAAA,mBAAoF,UAAA;IAA5E,OAAM;IAA4B,IAAG;IAAK,IAAG;IAAK,GAAE;IAAK,gBAAa;GAC9E,CAAA,GAAA,mBAGE,QAAA;IAFA,OAAM;IACN,GAAE;GAiBA,CAAA,CAAA,CAAA,CAAA,GAAA,EAAA,IAAA,QAAA,eADR,UAAA,GAAA,mBAKO,QALP,YAKO,gBADF,QAAA,WAAW,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,GAAA,GAAA,UAAA,CAAA,CAAA,CAAA,GAAA,IAAA,UAAA"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { buttonVariants } from "./Button.vue_vue_type_script_setup_true_lang.js";
|
|
2
2
|
import Button_default from "./Button.js";
|
|
3
|
-
export {
|
|
3
|
+
export { buttonVariants, Button_default as default };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import Card_vue_vue_type_script_setup_true_lang_default from "./Card.vue_vue_type_script_setup_true_lang.js";
|
|
2
|
+
//#region packages/primitives/card/src/Card.vue
|
|
3
|
+
var Card_default = Card_vue_vue_type_script_setup_true_lang_default;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { Card_default as default };
|
|
6
|
+
|
|
7
|
+
//# sourceMappingURL=Card.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Card.js","names":[],"sources":["../../../../../packages/primitives/card/src/Card.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { SurfacePad } from \"@ecoma-io/loom-surface\";\n\n/**\n * Card — a compositional surface. The box styling is Surface's own `card`\n * variant consumed as data (`surfaceVariants`), so a card and a bare Surface\n * can never drift apart on radius, hairline or ground, while Card owns what\n * Surface deliberately does not: the section rhythm, the title/description\n * contract and the two ways a card becomes interactive.\n *\n * Interactive comes in exactly two shapes, because a third would be a lie:\n *\n * • `href` — the whole card IS one link. One tab stop, one action, real\n * anchor semantics, the content itself for an accessible name — and one\n * rule: nothing interactive may be placed inside, because nested\n * controls inside an anchor are unreadable to assistive tech.\n * • `interactive` — hover/press language only. The host owns the role,\n * tabindex and handler, exactly as `Surface.interactive` documents; a\n * component cannot invent semantics for somebody else's behaviour.\n */\nexport type { SurfacePad as CardPad };\n</script>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { surfaceVariants } from \"@ecoma-io/loom-surface\";\n\nconst props = defineProps<{\n /** The card's heading. Omit for a purely visual composition. */\n title?: string;\n /** Supporting line under the title. */\n description?: string;\n /**\n * Renders the whole card as one link. The strongest card-link semantics —\n * a real anchor, one Tab stop — and therefore a rule: nothing interactive\n * may be placed inside.\n */\n href?: string;\n /**\n * Hover/press language for a clickable card whose behaviour belongs to the\n * host. Like `Surface.interactive`, this paints intent only — attach the\n * role, `tabindex` and handler yourself, or reach for `href` when the card\n * genuinely navigates.\n */\n interactive?: boolean;\n}>();\n\n/** Section rhythm: one inset owned once, so every card padds alike. */\nconst CARD_SECTIONS = {\n header: \"px-4 pt-4\",\n body: \"px-4 py-4\",\n // A recessed strip under a hairline: the footer reads as chrome for the\n // content above it, not as more content.\n footer: \"border-t border-border bg-muted/40 px-4 py-3\",\n} as const;\n\nconst rootClasses = computed(() =>\n cn(\n // Box styling stays Surface's decision, consumed as data so the two\n // cannot drift; `pad: none` because the sections carry their own rhythm.\n // The interactive language rides the same map for both shapes — an anchor\n // is simply always interactive.\n surfaceVariants({\n variant: \"card\",\n pad: \"none\",\n interactive: Boolean(props.href) || props.interactive,\n }),\n \"overflow-hidden\",\n props.href &&\n // Focus and press join the library's languages only when there is\n // something to act on. The transform composite mirrors Button's:\n // scale on spring, colour steady on ease-out.\n \"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo [transition:transform_var(--duration-fast)_var(--ease-spring),background-color_var(--duration-fast)_var(--ease-out)] active:scale-press\",\n ),\n);\n</script>\n\n<template>\n <component :is=\"href ? 'a' : 'div'\" :href=\"href\" :class=\"rootClasses\">\n <!-- @slot Edge-to-edge media above every section; the card clips its corners. -->\n <div v-if=\"$slots.media\" class=\"[&>img]:block [&>img]:w-full\">\n <slot name=\"media\" />\n </div>\n\n <div v-if=\"$slots.header || title || description\" :class=\"cn(CARD_SECTIONS.header)\">\n <!-- @slot Replaces the title/description pair wholesale for custom header layouts. -->\n <slot name=\"header\">\n <p v-if=\"title\" class=\"text-sm font-medium\">{{ title }}</p>\n <p v-if=\"description\" class=\"mt-1 text-small text-muted-foreground\">{{ description }}</p>\n </slot>\n </div>\n\n <!-- @slot The card's substance. -->\n <div :class=\"cn(CARD_SECTIONS.body)\"><slot /></div>\n\n <!-- @slot Actions or metadata, recessed under a hairline. -->\n <div v-if=\"$slots.footer\" :class=\"cn(CARD_SECTIONS.footer)\">\n <slot name=\"footer\" />\n </div>\n </component>\n</template>\n"],"mappings":""}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { SurfacePad } from "../../surface/src/index";
|
|
2
|
+
/**
|
|
3
|
+
* Card — a compositional surface. The box styling is Surface's own `card`
|
|
4
|
+
* variant consumed as data (`surfaceVariants`), so a card and a bare Surface
|
|
5
|
+
* can never drift apart on radius, hairline or ground, while Card owns what
|
|
6
|
+
* Surface deliberately does not: the section rhythm, the title/description
|
|
7
|
+
* contract and the two ways a card becomes interactive.
|
|
8
|
+
*
|
|
9
|
+
* Interactive comes in exactly two shapes, because a third would be a lie:
|
|
10
|
+
*
|
|
11
|
+
* • `href` — the whole card IS one link. One tab stop, one action, real
|
|
12
|
+
* anchor semantics, the content itself for an accessible name — and one
|
|
13
|
+
* rule: nothing interactive may be placed inside, because nested
|
|
14
|
+
* controls inside an anchor are unreadable to assistive tech.
|
|
15
|
+
* • `interactive` — hover/press language only. The host owns the role,
|
|
16
|
+
* tabindex and handler, exactly as `Surface.interactive` documents; a
|
|
17
|
+
* component cannot invent semantics for somebody else's behaviour.
|
|
18
|
+
*/
|
|
19
|
+
export type { SurfacePad as CardPad };
|
|
20
|
+
declare const _default: typeof __VLS_export;
|
|
21
|
+
export default _default;
|
|
22
|
+
declare const __VLS_export: __VLS_WithSlots<import("vue").DefineComponent<{
|
|
23
|
+
/** The card's heading. Omit for a purely visual composition. */
|
|
24
|
+
title?: string;
|
|
25
|
+
/** Supporting line under the title. */
|
|
26
|
+
description?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Renders the whole card as one link. The strongest card-link semantics —
|
|
29
|
+
* a real anchor, one Tab stop — and therefore a rule: nothing interactive
|
|
30
|
+
* may be placed inside.
|
|
31
|
+
*/
|
|
32
|
+
href?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Hover/press language for a clickable card whose behaviour belongs to the
|
|
35
|
+
* host. Like `Surface.interactive`, this paints intent only — attach the
|
|
36
|
+
* role, `tabindex` and handler yourself, or reach for `href` when the card
|
|
37
|
+
* genuinely navigates.
|
|
38
|
+
*/
|
|
39
|
+
interactive?: boolean;
|
|
40
|
+
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{
|
|
41
|
+
/** The card's heading. Omit for a purely visual composition. */
|
|
42
|
+
title?: string;
|
|
43
|
+
/** Supporting line under the title. */
|
|
44
|
+
description?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Renders the whole card as one link. The strongest card-link semantics —
|
|
47
|
+
* a real anchor, one Tab stop — and therefore a rule: nothing interactive
|
|
48
|
+
* may be placed inside.
|
|
49
|
+
*/
|
|
50
|
+
href?: string;
|
|
51
|
+
/**
|
|
52
|
+
* Hover/press language for a clickable card whose behaviour belongs to the
|
|
53
|
+
* host. Like `Surface.interactive`, this paints intent only — attach the
|
|
54
|
+
* role, `tabindex` and handler yourself, or reach for `href` when the card
|
|
55
|
+
* genuinely navigates.
|
|
56
|
+
*/
|
|
57
|
+
interactive?: boolean;
|
|
58
|
+
}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
|
|
59
|
+
media?: (props: {}) => any;
|
|
60
|
+
} & {
|
|
61
|
+
header?: (props: {}) => any;
|
|
62
|
+
} & {
|
|
63
|
+
default?: (props: {}) => any;
|
|
64
|
+
} & {
|
|
65
|
+
footer?: (props: {}) => any;
|
|
66
|
+
}>;
|
|
67
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
68
|
+
new (): {
|
|
69
|
+
$slots: S;
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=Card.vue.d.ts.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { cn } from "../../../core/src/cn.js";
|
|
2
|
+
import "../../../core/src/index.js";
|
|
3
|
+
import { surfaceVariants } from "../../surface/src/Surface.vue_vue_type_script_setup_true_lang.js";
|
|
4
|
+
import "../../surface/src/index.js";
|
|
5
|
+
import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, defineComponent, normalizeClass, openBlock, renderSlot, resolveDynamicComponent, toDisplayString, unref, withCtx } from "vue";
|
|
6
|
+
//#region packages/primitives/card/src/Card.vue?vue&type=script&setup=true&lang.ts
|
|
7
|
+
var _hoisted_1 = {
|
|
8
|
+
key: 0,
|
|
9
|
+
class: "[&>img]:block [&>img]:w-full"
|
|
10
|
+
};
|
|
11
|
+
var _hoisted_2 = {
|
|
12
|
+
key: 0,
|
|
13
|
+
class: "text-sm font-medium"
|
|
14
|
+
};
|
|
15
|
+
var _hoisted_3 = {
|
|
16
|
+
key: 1,
|
|
17
|
+
class: "mt-1 text-small text-muted-foreground"
|
|
18
|
+
};
|
|
19
|
+
var Card_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
20
|
+
__name: "Card",
|
|
21
|
+
props: {
|
|
22
|
+
title: {},
|
|
23
|
+
description: {},
|
|
24
|
+
href: {},
|
|
25
|
+
interactive: { type: Boolean }
|
|
26
|
+
},
|
|
27
|
+
setup(__props) {
|
|
28
|
+
const props = __props;
|
|
29
|
+
/** Section rhythm: one inset owned once, so every card padds alike. */
|
|
30
|
+
const CARD_SECTIONS = {
|
|
31
|
+
header: "px-4 pt-4",
|
|
32
|
+
body: "px-4 py-4",
|
|
33
|
+
footer: "border-t border-border bg-muted/40 px-4 py-3"
|
|
34
|
+
};
|
|
35
|
+
const rootClasses = computed(() => cn(surfaceVariants({
|
|
36
|
+
variant: "card",
|
|
37
|
+
pad: "none",
|
|
38
|
+
interactive: Boolean(props.href) || props.interactive
|
|
39
|
+
}), "overflow-hidden", props.href && "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo [transition:transform_var(--duration-fast)_var(--ease-spring),background-color_var(--duration-fast)_var(--ease-out)] active:scale-press"));
|
|
40
|
+
return (_ctx, _cache) => {
|
|
41
|
+
return openBlock(), createBlock(resolveDynamicComponent(__props.href ? "a" : "div"), {
|
|
42
|
+
href: __props.href,
|
|
43
|
+
class: normalizeClass(rootClasses.value)
|
|
44
|
+
}, {
|
|
45
|
+
default: withCtx(() => [
|
|
46
|
+
_ctx.$slots.media ? (openBlock(), createElementBlock("div", _hoisted_1, [renderSlot(_ctx.$slots, "media")])) : createCommentVNode("", true),
|
|
47
|
+
_ctx.$slots.header || __props.title || __props.description ? (openBlock(), createElementBlock("div", {
|
|
48
|
+
key: 1,
|
|
49
|
+
class: normalizeClass(unref(cn)(CARD_SECTIONS.header))
|
|
50
|
+
}, [renderSlot(_ctx.$slots, "header", {}, () => [__props.title ? (openBlock(), createElementBlock("p", _hoisted_2, toDisplayString(__props.title), 1)) : createCommentVNode("", true), __props.description ? (openBlock(), createElementBlock("p", _hoisted_3, toDisplayString(__props.description), 1)) : createCommentVNode("", true)])], 2)) : createCommentVNode("", true),
|
|
51
|
+
createElementVNode("div", { class: normalizeClass(unref(cn)(CARD_SECTIONS.body)) }, [renderSlot(_ctx.$slots, "default")], 2),
|
|
52
|
+
_ctx.$slots.footer ? (openBlock(), createElementBlock("div", {
|
|
53
|
+
key: 2,
|
|
54
|
+
class: normalizeClass(unref(cn)(CARD_SECTIONS.footer))
|
|
55
|
+
}, [renderSlot(_ctx.$slots, "footer")], 2)) : createCommentVNode("", true)
|
|
56
|
+
]),
|
|
57
|
+
_: 3
|
|
58
|
+
}, 8, ["href", "class"]);
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
//#endregion
|
|
63
|
+
export { Card_vue_vue_type_script_setup_true_lang_default as default };
|
|
64
|
+
|
|
65
|
+
//# sourceMappingURL=Card.vue_vue_type_script_setup_true_lang.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Card.vue_vue_type_script_setup_true_lang.js","names":["$slots"],"sources":["../../../../../packages/primitives/card/src/Card.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { SurfacePad } from \"@ecoma-io/loom-surface\";\n\n/**\n * Card — a compositional surface. The box styling is Surface's own `card`\n * variant consumed as data (`surfaceVariants`), so a card and a bare Surface\n * can never drift apart on radius, hairline or ground, while Card owns what\n * Surface deliberately does not: the section rhythm, the title/description\n * contract and the two ways a card becomes interactive.\n *\n * Interactive comes in exactly two shapes, because a third would be a lie:\n *\n * • `href` — the whole card IS one link. One tab stop, one action, real\n * anchor semantics, the content itself for an accessible name — and one\n * rule: nothing interactive may be placed inside, because nested\n * controls inside an anchor are unreadable to assistive tech.\n * • `interactive` — hover/press language only. The host owns the role,\n * tabindex and handler, exactly as `Surface.interactive` documents; a\n * component cannot invent semantics for somebody else's behaviour.\n */\nexport type { SurfacePad as CardPad };\n</script>\n\n<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { cn } from \"@ecoma-io/loom-core\";\nimport { surfaceVariants } from \"@ecoma-io/loom-surface\";\n\nconst props = defineProps<{\n /** The card's heading. Omit for a purely visual composition. */\n title?: string;\n /** Supporting line under the title. */\n description?: string;\n /**\n * Renders the whole card as one link. The strongest card-link semantics —\n * a real anchor, one Tab stop — and therefore a rule: nothing interactive\n * may be placed inside.\n */\n href?: string;\n /**\n * Hover/press language for a clickable card whose behaviour belongs to the\n * host. Like `Surface.interactive`, this paints intent only — attach the\n * role, `tabindex` and handler yourself, or reach for `href` when the card\n * genuinely navigates.\n */\n interactive?: boolean;\n}>();\n\n/** Section rhythm: one inset owned once, so every card padds alike. */\nconst CARD_SECTIONS = {\n header: \"px-4 pt-4\",\n body: \"px-4 py-4\",\n // A recessed strip under a hairline: the footer reads as chrome for the\n // content above it, not as more content.\n footer: \"border-t border-border bg-muted/40 px-4 py-3\",\n} as const;\n\nconst rootClasses = computed(() =>\n cn(\n // Box styling stays Surface's decision, consumed as data so the two\n // cannot drift; `pad: none` because the sections carry their own rhythm.\n // The interactive language rides the same map for both shapes — an anchor\n // is simply always interactive.\n surfaceVariants({\n variant: \"card\",\n pad: \"none\",\n interactive: Boolean(props.href) || props.interactive,\n }),\n \"overflow-hidden\",\n props.href &&\n // Focus and press join the library's languages only when there is\n // something to act on. The transform composite mirrors Button's:\n // scale on spring, colour steady on ease-out.\n \"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo [transition:transform_var(--duration-fast)_var(--ease-spring),background-color_var(--duration-fast)_var(--ease-out)] active:scale-press\",\n ),\n);\n</script>\n\n<template>\n <component :is=\"href ? 'a' : 'div'\" :href=\"href\" :class=\"rootClasses\">\n <!-- @slot Edge-to-edge media above every section; the card clips its corners. -->\n <div v-if=\"$slots.media\" class=\"[&>img]:block [&>img]:w-full\">\n <slot name=\"media\" />\n </div>\n\n <div v-if=\"$slots.header || title || description\" :class=\"cn(CARD_SECTIONS.header)\">\n <!-- @slot Replaces the title/description pair wholesale for custom header layouts. -->\n <slot name=\"header\">\n <p v-if=\"title\" class=\"text-sm font-medium\">{{ title }}</p>\n <p v-if=\"description\" class=\"mt-1 text-small text-muted-foreground\">{{ description }}</p>\n </slot>\n </div>\n\n <!-- @slot The card's substance. -->\n <div :class=\"cn(CARD_SECTIONS.body)\"><slot /></div>\n\n <!-- @slot Actions or metadata, recessed under a hairline. -->\n <div v-if=\"$slots.footer\" :class=\"cn(CARD_SECTIONS.footer)\">\n <slot name=\"footer\" />\n </div>\n </component>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;EA4BA,MAAM,QAAQ;;EAqBd,MAAM,gBAAgB;GACpB,QAAQ;GACR,MAAM;GAGN,QAAQ;EACV;EAEA,MAAM,cAAc,eAClB,GAKE,gBAAgB;GACd,SAAS;GACT,KAAK;GACL,aAAa,QAAQ,MAAM,IAAI,KAAK,MAAM;EAC5C,CAAC,GACD,mBACA,MAAM,QAIJ,qPACJ,CACF;;GAIE,OAAA,UAAA,GAAA,YAqBY,wBArBI,QAAA,OAAI,MAAA,KAAA,GAAA;IAAiB,MAAM,QAAA;IAAO,OAAK,eAAE,YAAA,KAAW;;IAElE,SAAA,cAEM;KAFKA,KAAAA,OAAO,SAAlB,UAAA,GAAA,mBAEM,OAFN,YAEM,CADJ,WAAqB,KAAA,QAAA,OAAA,CAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;KAGZA,KAAAA,OAAO,UAAU,QAAA,SAAS,QAAA,eAArC,UAAA,GAAA,mBAMM,OAAA;;MAN6C,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,cAAc,MAAM,CAAA;KAE/E,GAAA,CAAA,WAGO,KAAA,QAAA,UAAA,CAAA,SAAA,CAFI,QAAA,SAAT,UAAA,GAAA,mBAA2D,KAA3D,YAA2D,gBAAZ,QAAA,KAAK,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,GAC3C,QAAA,eAAT,UAAA,GAAA,mBAAyF,KAAzF,YAAyF,gBAAlB,QAAA,WAAW,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,CAAA,CAAA,CAAA,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA;KAKtF,mBAAmD,OAAA,EAA7C,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,cAAc,IAAI,CAAA,EAAA,GAAA,CAAG,WAAQ,KAAA,QAAA,SAAA,CAAA,GAAA,CAAA;KAGlCA,KAAAA,OAAO,UAAlB,UAAA,GAAA,mBAEM,OAAA;;MAFqB,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,cAAc,MAAM,CAAA;KACvD,GAAA,CAAA,WAAsB,KAAA,QAAA,QAAA,CAAA,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../packages/primitives/card/src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import Carousel_vue_vue_type_script_setup_true_lang_default, { CAROUSEL_LABELS } from "./Carousel.vue_vue_type_script_setup_true_lang.js";
|
|
2
|
+
//#region packages/primitives/carousel/src/Carousel.vue
|
|
3
|
+
var Carousel_default = Carousel_vue_vue_type_script_setup_true_lang_default;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { CAROUSEL_LABELS, Carousel_default as default };
|
|
6
|
+
|
|
7
|
+
//# sourceMappingURL=Carousel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Carousel.js","names":[],"sources":["../../../../../packages/primitives/carousel/src/Carousel.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { CarouselLabels } from \"@ecoma-io/loom-labels\";\nimport type { VNode } from \"vue\";\n\n/**\n * Loom's English, co-located with the component so it tree-shakes with it,\n * and exported so a host can build a partial vocabulary against the real\n * thing rather than a transcription of it. `slide` receives the raw numbers\n * and returns the finished sentence — pluralisation and digit shaping stay\n * in the consumer's locale code, per the labels package's contract.\n */\nexport const CAROUSEL_LABELS: CarouselLabels = {\n region: \"Carousel\",\n previous: \"Previous slide\",\n next: \"Next slide\",\n slide: ({ position, total }) => `Slide ${String(position)} of ${String(total)}`,\n};\n\n/**\n * One slide per child of the default slot; a fragment child (a bare `v-for`\n * on the host) contributes its own children as further slides.\n */\nfunction flatten(vnodes: VNode[]): VNode[] {\n return vnodes.flatMap((vnode) =>\n typeof vnode.type === \"symbol\" && Array.isArray(vnode.children)\n ? (vnode.children as VNode[])\n : [vnode],\n );\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref } from \"vue\";\nimport { useSlots } from \"vue\";\nimport { ChevronLeft, ChevronRight } from \"@lucide/vue\";\nimport IconButton from \"@ecoma-io/loom-icon-button\";\nimport { useLabels, type LabelOverrides } from \"@ecoma-io/loom-labels\";\nimport { cn, smoothScrollBehavior } from \"@ecoma-io/loom-core\";\n\nconst props = withDefaults(\n defineProps<{\n /** Names the whole region for assistive technology. */\n labels?: LabelOverrides<CarouselLabels>;\n /**\n * Wraps at both ends instead of stopping. Off by default: a carousel\n * that silently jumps from last back to first reads as a glitch unless\n * the product actually means it.\n */\n loop?: boolean;\n }>(),\n { loop: false },\n);\n\n// `text`, not `labels`: the prop of that name is one of the three sources\n// this resolves, and a template reading the raw prop would be reading the\n// overrides rather than the answer.\nconst text = useLabels(\"carousel\", CAROUSEL_LABELS, () => props.labels);\n\n// Invoked during render, never cached: a computed holding slot vnodes would\n// freeze the list against a host that adds or removes slides reactively\n// (Vue does not track dependencies used inside a slot from outside it).\nconst slots = useSlots();\n\n// The slot function runs here on every render — fresh vnodes each time; only\n// the slot lookup itself is captured in setup.\nfunction slideVnodes(): VNode[] {\n return flatten(slots.default?.() ?? []);\n}\n\nfunction totalCount(): number {\n return slideVnodes().length;\n}\nconst last = computed(() => totalCount() - 1);\n\nconst track = ref<HTMLElement | null>(null);\nconst index = ref(0);\n\nfunction goTo(next: number): void {\n const el = track.value;\n const total = totalCount();\n if (!el || !total) return;\n let target = next;\n // Modulo of zero is NaN — an empty strip has nowhere to go.\n if (props.loop) target = ((next % total) + total) % total;\n else target = Math.max(0, Math.min(total - 1, target));\n index.value = target;\n el.scrollTo({ left: target * el.clientWidth, behavior: smoothScrollBehavior() });\n}\n\nconst atStart = computed(() => !props.loop && index.value <= 0);\nconst atEnd = computed(() => !props.loop && index.value >= last.value);\n\n/** Arrows walk pages while the strip itself holds focus; Home/End jump. */\nfunction onKeydown(event: KeyboardEvent): void {\n if (event.key === \"ArrowLeft\" || event.key === \"ArrowRight\") {\n event.preventDefault();\n goTo(index.value + (event.key === \"ArrowRight\" ? 1 : -1));\n } else if (event.key === \"Home\") {\n event.preventDefault();\n goTo(0);\n } else if (event.key === \"End\") {\n event.preventDefault();\n goTo(last.value);\n }\n}\n\n// Touch swipes land here too: native scroll moves the strip and the reader's\n// finger — not the component — decides where it settles. Work happens once\n// per frame rather than on every scroll tick.\nlet frame = 0;\nfunction onScroll(): void {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(() => {\n const el = track.value;\n if (!el || !el.clientWidth) return;\n index.value = Math.max(0, Math.min(last.value, Math.round(el.scrollLeft / el.clientWidth)));\n });\n}\n\nonMounted(() => track.value?.addEventListener(\"scroll\", onScroll, { passive: true }));\nonBeforeUnmount(() => {\n track.value?.removeEventListener(\"scroll\", onScroll);\n cancelAnimationFrame(frame);\n});\n\nconst trackClasses = cn(\n \"flex snap-x snap-mandatory overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden rounded-md\",\n \"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo\",\n);\n\n// The controls float above media, so they carry their own ground instead of\n// inheriting whatever slide sits beneath them.\nconst controlClasses =\n \"absolute top-1/2 z-raised -translate-y-1/2 bg-background/80 shadow-sm hover:bg-background\";\n</script>\n\n<template>\n <div role=\"region\" aria-roledescription=\"carousel\" :aria-label=\"text.region\" class=\"relative\">\n <!-- @slot One element per slide. Each becomes a full-width,\n snap-aligned page; give every child the same intrinsic height or pad\n it yourself. The slides are wrapped here rather than rendered bare so\n each can carry its own slide semantics. -->\n <!-- A named, focusable scroll container whose arrow keys page the\n strip: a genuine interaction on a genuinely interactive element.\n The accessibility rule cannot see that combination from a plain\n div, so it is answered here rather than by inventing a role the\n element does not have. -->\n <!-- eslint-disable-next-line vuejs-accessibility/no-static-element-interactions -->\n <div ref=\"track\" :class=\"trackClasses\" tabindex=\"0\" @keydown=\"onKeydown\">\n <div\n v-for=\"(slide, i) in slideVnodes()\"\n :key=\"i\"\n role=\"group\"\n aria-roledescription=\"slide\"\n :aria-label=\"text.slide({ position: i + 1, total: totalCount() })\"\n class=\"w-full shrink-0 snap-start\"\n >\n <component :is=\"slide\" />\n </div>\n </div>\n\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n :label=\"text.previous\"\n :disabled=\"atStart\"\n :class=\"cn(controlClasses, 'left-2')\"\n @click=\"goTo(index - 1)\"\n >\n <ChevronLeft class=\"h-4 w-4\" aria-hidden=\"true\" />\n </IconButton>\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n :label=\"text.next\"\n :disabled=\"atEnd\"\n :class=\"cn(controlClasses, 'right-2')\"\n @click=\"goTo(index + 1)\"\n >\n <ChevronRight class=\"h-4 w-4\" aria-hidden=\"true\" />\n </IconButton>\n\n <!-- Present from the first render: a live region mounted in the same\n moment as its text is a region assistive tech was not yet listening\n to. Position changes announce politely; nothing announces on load. -->\n <p class=\"sr-only\" aria-live=\"polite\">\n {{ text.slide({ position: index + 1, total: totalCount() }) }}\n </p>\n </div>\n</template>\n"],"mappings":""}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type LabelOverrides } from "../../../labels/src/index";
|
|
2
|
+
import type { CarouselLabels } from "../../../labels/src/index";
|
|
3
|
+
/**
|
|
4
|
+
* Loom's English, co-located with the component so it tree-shakes with it,
|
|
5
|
+
* and exported so a host can build a partial vocabulary against the real
|
|
6
|
+
* thing rather than a transcription of it. `slide` receives the raw numbers
|
|
7
|
+
* and returns the finished sentence — pluralisation and digit shaping stay
|
|
8
|
+
* in the consumer's locale code, per the labels package's contract.
|
|
9
|
+
*/
|
|
10
|
+
export declare const CAROUSEL_LABELS: CarouselLabels;
|
|
11
|
+
declare const _default: typeof __VLS_export;
|
|
12
|
+
export default _default;
|
|
13
|
+
declare const __VLS_export: import("vue").DefineComponent<{
|
|
14
|
+
/** Names the whole region for assistive technology. */
|
|
15
|
+
labels?: LabelOverrides<CarouselLabels>;
|
|
16
|
+
/**
|
|
17
|
+
* Wraps at both ends instead of stopping. Off by default: a carousel
|
|
18
|
+
* that silently jumps from last back to first reads as a glitch unless
|
|
19
|
+
* the product actually means it.
|
|
20
|
+
*/
|
|
21
|
+
loop?: boolean;
|
|
22
|
+
}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{
|
|
23
|
+
/** Names the whole region for assistive technology. */
|
|
24
|
+
labels?: LabelOverrides<CarouselLabels>;
|
|
25
|
+
/**
|
|
26
|
+
* Wraps at both ends instead of stopping. Off by default: a carousel
|
|
27
|
+
* that silently jumps from last back to first reads as a glitch unless
|
|
28
|
+
* the product actually means it.
|
|
29
|
+
*/
|
|
30
|
+
loop?: boolean;
|
|
31
|
+
}> & Readonly<{}>, {
|
|
32
|
+
loop: boolean;
|
|
33
|
+
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
34
|
+
//# sourceMappingURL=Carousel.vue.d.ts.map
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { cn } from "../../../core/src/cn.js";
|
|
2
|
+
import { smoothScrollBehavior } from "../../../core/src/motion.js";
|
|
3
|
+
import "../../../core/src/index.js";
|
|
4
|
+
import { useLabels } from "../../../labels/src/labels.js";
|
|
5
|
+
import "../../../labels/src/index.js";
|
|
6
|
+
import IconButton_default from "../../icon-button/src/IconButton.js";
|
|
7
|
+
import "../../icon-button/src/index.js";
|
|
8
|
+
import { Fragment, computed, createBlock, createElementBlock, createElementVNode, createVNode, defineComponent, normalizeClass, onBeforeUnmount, onMounted, openBlock, ref, renderList, resolveDynamicComponent, toDisplayString, unref, useSlots, withCtx } from "vue";
|
|
9
|
+
import { ChevronLeft, ChevronRight } from "@lucide/vue";
|
|
10
|
+
//#region packages/primitives/carousel/src/Carousel.vue?vue&type=script&setup=true&lang.ts
|
|
11
|
+
var _hoisted_1 = ["aria-label"];
|
|
12
|
+
var _hoisted_2 = ["aria-label"];
|
|
13
|
+
var _hoisted_3 = {
|
|
14
|
+
class: "sr-only",
|
|
15
|
+
"aria-live": "polite"
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Loom's English, co-located with the component so it tree-shakes with it,
|
|
19
|
+
* and exported so a host can build a partial vocabulary against the real
|
|
20
|
+
* thing rather than a transcription of it. `slide` receives the raw numbers
|
|
21
|
+
* and returns the finished sentence — pluralisation and digit shaping stay
|
|
22
|
+
* in the consumer's locale code, per the labels package's contract.
|
|
23
|
+
*/
|
|
24
|
+
var CAROUSEL_LABELS = {
|
|
25
|
+
region: "Carousel",
|
|
26
|
+
previous: "Previous slide",
|
|
27
|
+
next: "Next slide",
|
|
28
|
+
slide: ({ position, total }) => `Slide ${String(position)} of ${String(total)}`
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* One slide per child of the default slot; a fragment child (a bare `v-for`
|
|
32
|
+
* on the host) contributes its own children as further slides.
|
|
33
|
+
*/
|
|
34
|
+
function flatten(vnodes) {
|
|
35
|
+
return vnodes.flatMap((vnode) => typeof vnode.type === "symbol" && Array.isArray(vnode.children) ? vnode.children : [vnode]);
|
|
36
|
+
}
|
|
37
|
+
var Carousel_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
38
|
+
__name: "Carousel",
|
|
39
|
+
props: {
|
|
40
|
+
labels: {},
|
|
41
|
+
loop: {
|
|
42
|
+
type: Boolean,
|
|
43
|
+
default: false
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
setup(__props) {
|
|
47
|
+
const props = __props;
|
|
48
|
+
const text = useLabels("carousel", CAROUSEL_LABELS, () => props.labels);
|
|
49
|
+
const slots = useSlots();
|
|
50
|
+
function slideVnodes() {
|
|
51
|
+
return flatten(slots.default?.() ?? []);
|
|
52
|
+
}
|
|
53
|
+
function totalCount() {
|
|
54
|
+
return slideVnodes().length;
|
|
55
|
+
}
|
|
56
|
+
const last = computed(() => totalCount() - 1);
|
|
57
|
+
const track = ref(null);
|
|
58
|
+
const index = ref(0);
|
|
59
|
+
function goTo(next) {
|
|
60
|
+
const el = track.value;
|
|
61
|
+
const total = totalCount();
|
|
62
|
+
if (!el || !total) return;
|
|
63
|
+
let target = next;
|
|
64
|
+
if (props.loop) target = (next % total + total) % total;
|
|
65
|
+
else target = Math.max(0, Math.min(total - 1, target));
|
|
66
|
+
index.value = target;
|
|
67
|
+
el.scrollTo({
|
|
68
|
+
left: target * el.clientWidth,
|
|
69
|
+
behavior: smoothScrollBehavior()
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
const atStart = computed(() => !props.loop && index.value <= 0);
|
|
73
|
+
const atEnd = computed(() => !props.loop && index.value >= last.value);
|
|
74
|
+
/** Arrows walk pages while the strip itself holds focus; Home/End jump. */
|
|
75
|
+
function onKeydown(event) {
|
|
76
|
+
if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
|
|
77
|
+
event.preventDefault();
|
|
78
|
+
goTo(index.value + (event.key === "ArrowRight" ? 1 : -1));
|
|
79
|
+
} else if (event.key === "Home") {
|
|
80
|
+
event.preventDefault();
|
|
81
|
+
goTo(0);
|
|
82
|
+
} else if (event.key === "End") {
|
|
83
|
+
event.preventDefault();
|
|
84
|
+
goTo(last.value);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
let frame = 0;
|
|
88
|
+
function onScroll() {
|
|
89
|
+
cancelAnimationFrame(frame);
|
|
90
|
+
frame = requestAnimationFrame(() => {
|
|
91
|
+
const el = track.value;
|
|
92
|
+
if (!el || !el.clientWidth) return;
|
|
93
|
+
index.value = Math.max(0, Math.min(last.value, Math.round(el.scrollLeft / el.clientWidth)));
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
onMounted(() => track.value?.addEventListener("scroll", onScroll, { passive: true }));
|
|
97
|
+
onBeforeUnmount(() => {
|
|
98
|
+
track.value?.removeEventListener("scroll", onScroll);
|
|
99
|
+
cancelAnimationFrame(frame);
|
|
100
|
+
});
|
|
101
|
+
const trackClasses = cn("flex snap-x snap-mandatory overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden rounded-md", "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo");
|
|
102
|
+
const controlClasses = "absolute top-1/2 z-raised -translate-y-1/2 bg-background/80 shadow-sm hover:bg-background";
|
|
103
|
+
return (_ctx, _cache) => {
|
|
104
|
+
return openBlock(), createElementBlock("div", {
|
|
105
|
+
role: "region",
|
|
106
|
+
"aria-roledescription": "carousel",
|
|
107
|
+
"aria-label": unref(text).region,
|
|
108
|
+
class: "relative"
|
|
109
|
+
}, [
|
|
110
|
+
createElementVNode("div", {
|
|
111
|
+
ref_key: "track",
|
|
112
|
+
ref: track,
|
|
113
|
+
class: normalizeClass(unref(trackClasses)),
|
|
114
|
+
tabindex: "0",
|
|
115
|
+
onKeydown
|
|
116
|
+
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(slideVnodes(), (slide, i) => {
|
|
117
|
+
return openBlock(), createElementBlock("div", {
|
|
118
|
+
key: i,
|
|
119
|
+
role: "group",
|
|
120
|
+
"aria-roledescription": "slide",
|
|
121
|
+
"aria-label": unref(text).slide({
|
|
122
|
+
position: i + 1,
|
|
123
|
+
total: totalCount()
|
|
124
|
+
}),
|
|
125
|
+
class: "w-full shrink-0 snap-start"
|
|
126
|
+
}, [(openBlock(), createBlock(resolveDynamicComponent(slide)))], 8, _hoisted_2);
|
|
127
|
+
}), 128))], 34),
|
|
128
|
+
createVNode(unref(IconButton_default), {
|
|
129
|
+
variant: "ghost",
|
|
130
|
+
size: "sm",
|
|
131
|
+
label: unref(text).previous,
|
|
132
|
+
disabled: atStart.value,
|
|
133
|
+
class: normalizeClass(unref(cn)(controlClasses, "left-2")),
|
|
134
|
+
onClick: _cache[0] || (_cache[0] = ($event) => goTo(index.value - 1))
|
|
135
|
+
}, {
|
|
136
|
+
default: withCtx(() => [createVNode(unref(ChevronLeft), {
|
|
137
|
+
class: "h-4 w-4",
|
|
138
|
+
"aria-hidden": "true"
|
|
139
|
+
})]),
|
|
140
|
+
_: 1
|
|
141
|
+
}, 8, [
|
|
142
|
+
"label",
|
|
143
|
+
"disabled",
|
|
144
|
+
"class"
|
|
145
|
+
]),
|
|
146
|
+
createVNode(unref(IconButton_default), {
|
|
147
|
+
variant: "ghost",
|
|
148
|
+
size: "sm",
|
|
149
|
+
label: unref(text).next,
|
|
150
|
+
disabled: atEnd.value,
|
|
151
|
+
class: normalizeClass(unref(cn)(controlClasses, "right-2")),
|
|
152
|
+
onClick: _cache[1] || (_cache[1] = ($event) => goTo(index.value + 1))
|
|
153
|
+
}, {
|
|
154
|
+
default: withCtx(() => [createVNode(unref(ChevronRight), {
|
|
155
|
+
class: "h-4 w-4",
|
|
156
|
+
"aria-hidden": "true"
|
|
157
|
+
})]),
|
|
158
|
+
_: 1
|
|
159
|
+
}, 8, [
|
|
160
|
+
"label",
|
|
161
|
+
"disabled",
|
|
162
|
+
"class"
|
|
163
|
+
]),
|
|
164
|
+
createElementVNode("p", _hoisted_3, toDisplayString(unref(text).slide({
|
|
165
|
+
position: index.value + 1,
|
|
166
|
+
total: totalCount()
|
|
167
|
+
})), 1)
|
|
168
|
+
], 8, _hoisted_1);
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
//#endregion
|
|
173
|
+
export { CAROUSEL_LABELS, Carousel_vue_vue_type_script_setup_true_lang_default as default };
|
|
174
|
+
|
|
175
|
+
//# sourceMappingURL=Carousel.vue_vue_type_script_setup_true_lang.js.map
|
package/dist/packages/primitives/carousel/src/Carousel.vue_vue_type_script_setup_true_lang.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Carousel.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../../../packages/primitives/carousel/src/Carousel.vue"],"sourcesContent":["<script lang=\"ts\">\nimport type { CarouselLabels } from \"@ecoma-io/loom-labels\";\nimport type { VNode } from \"vue\";\n\n/**\n * Loom's English, co-located with the component so it tree-shakes with it,\n * and exported so a host can build a partial vocabulary against the real\n * thing rather than a transcription of it. `slide` receives the raw numbers\n * and returns the finished sentence — pluralisation and digit shaping stay\n * in the consumer's locale code, per the labels package's contract.\n */\nexport const CAROUSEL_LABELS: CarouselLabels = {\n region: \"Carousel\",\n previous: \"Previous slide\",\n next: \"Next slide\",\n slide: ({ position, total }) => `Slide ${String(position)} of ${String(total)}`,\n};\n\n/**\n * One slide per child of the default slot; a fragment child (a bare `v-for`\n * on the host) contributes its own children as further slides.\n */\nfunction flatten(vnodes: VNode[]): VNode[] {\n return vnodes.flatMap((vnode) =>\n typeof vnode.type === \"symbol\" && Array.isArray(vnode.children)\n ? (vnode.children as VNode[])\n : [vnode],\n );\n}\n</script>\n\n<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, onMounted, ref } from \"vue\";\nimport { useSlots } from \"vue\";\nimport { ChevronLeft, ChevronRight } from \"@lucide/vue\";\nimport IconButton from \"@ecoma-io/loom-icon-button\";\nimport { useLabels, type LabelOverrides } from \"@ecoma-io/loom-labels\";\nimport { cn, smoothScrollBehavior } from \"@ecoma-io/loom-core\";\n\nconst props = withDefaults(\n defineProps<{\n /** Names the whole region for assistive technology. */\n labels?: LabelOverrides<CarouselLabels>;\n /**\n * Wraps at both ends instead of stopping. Off by default: a carousel\n * that silently jumps from last back to first reads as a glitch unless\n * the product actually means it.\n */\n loop?: boolean;\n }>(),\n { loop: false },\n);\n\n// `text`, not `labels`: the prop of that name is one of the three sources\n// this resolves, and a template reading the raw prop would be reading the\n// overrides rather than the answer.\nconst text = useLabels(\"carousel\", CAROUSEL_LABELS, () => props.labels);\n\n// Invoked during render, never cached: a computed holding slot vnodes would\n// freeze the list against a host that adds or removes slides reactively\n// (Vue does not track dependencies used inside a slot from outside it).\nconst slots = useSlots();\n\n// The slot function runs here on every render — fresh vnodes each time; only\n// the slot lookup itself is captured in setup.\nfunction slideVnodes(): VNode[] {\n return flatten(slots.default?.() ?? []);\n}\n\nfunction totalCount(): number {\n return slideVnodes().length;\n}\nconst last = computed(() => totalCount() - 1);\n\nconst track = ref<HTMLElement | null>(null);\nconst index = ref(0);\n\nfunction goTo(next: number): void {\n const el = track.value;\n const total = totalCount();\n if (!el || !total) return;\n let target = next;\n // Modulo of zero is NaN — an empty strip has nowhere to go.\n if (props.loop) target = ((next % total) + total) % total;\n else target = Math.max(0, Math.min(total - 1, target));\n index.value = target;\n el.scrollTo({ left: target * el.clientWidth, behavior: smoothScrollBehavior() });\n}\n\nconst atStart = computed(() => !props.loop && index.value <= 0);\nconst atEnd = computed(() => !props.loop && index.value >= last.value);\n\n/** Arrows walk pages while the strip itself holds focus; Home/End jump. */\nfunction onKeydown(event: KeyboardEvent): void {\n if (event.key === \"ArrowLeft\" || event.key === \"ArrowRight\") {\n event.preventDefault();\n goTo(index.value + (event.key === \"ArrowRight\" ? 1 : -1));\n } else if (event.key === \"Home\") {\n event.preventDefault();\n goTo(0);\n } else if (event.key === \"End\") {\n event.preventDefault();\n goTo(last.value);\n }\n}\n\n// Touch swipes land here too: native scroll moves the strip and the reader's\n// finger — not the component — decides where it settles. Work happens once\n// per frame rather than on every scroll tick.\nlet frame = 0;\nfunction onScroll(): void {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(() => {\n const el = track.value;\n if (!el || !el.clientWidth) return;\n index.value = Math.max(0, Math.min(last.value, Math.round(el.scrollLeft / el.clientWidth)));\n });\n}\n\nonMounted(() => track.value?.addEventListener(\"scroll\", onScroll, { passive: true }));\nonBeforeUnmount(() => {\n track.value?.removeEventListener(\"scroll\", onScroll);\n cancelAnimationFrame(frame);\n});\n\nconst trackClasses = cn(\n \"flex snap-x snap-mandatory overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden rounded-md\",\n \"focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring focus-visible:shadow-halo\",\n);\n\n// The controls float above media, so they carry their own ground instead of\n// inheriting whatever slide sits beneath them.\nconst controlClasses =\n \"absolute top-1/2 z-raised -translate-y-1/2 bg-background/80 shadow-sm hover:bg-background\";\n</script>\n\n<template>\n <div role=\"region\" aria-roledescription=\"carousel\" :aria-label=\"text.region\" class=\"relative\">\n <!-- @slot One element per slide. Each becomes a full-width,\n snap-aligned page; give every child the same intrinsic height or pad\n it yourself. The slides are wrapped here rather than rendered bare so\n each can carry its own slide semantics. -->\n <!-- A named, focusable scroll container whose arrow keys page the\n strip: a genuine interaction on a genuinely interactive element.\n The accessibility rule cannot see that combination from a plain\n div, so it is answered here rather than by inventing a role the\n element does not have. -->\n <!-- eslint-disable-next-line vuejs-accessibility/no-static-element-interactions -->\n <div ref=\"track\" :class=\"trackClasses\" tabindex=\"0\" @keydown=\"onKeydown\">\n <div\n v-for=\"(slide, i) in slideVnodes()\"\n :key=\"i\"\n role=\"group\"\n aria-roledescription=\"slide\"\n :aria-label=\"text.slide({ position: i + 1, total: totalCount() })\"\n class=\"w-full shrink-0 snap-start\"\n >\n <component :is=\"slide\" />\n </div>\n </div>\n\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n :label=\"text.previous\"\n :disabled=\"atStart\"\n :class=\"cn(controlClasses, 'left-2')\"\n @click=\"goTo(index - 1)\"\n >\n <ChevronLeft class=\"h-4 w-4\" aria-hidden=\"true\" />\n </IconButton>\n <IconButton\n variant=\"ghost\"\n size=\"sm\"\n :label=\"text.next\"\n :disabled=\"atEnd\"\n :class=\"cn(controlClasses, 'right-2')\"\n @click=\"goTo(index + 1)\"\n >\n <ChevronRight class=\"h-4 w-4\" aria-hidden=\"true\" />\n </IconButton>\n\n <!-- Present from the first render: a live region mounted in the same\n moment as its text is a region assistive tech was not yet listening\n to. Position changes announce politely; nothing announces on load. -->\n <p class=\"sr-only\" aria-live=\"polite\">\n {{ text.slide({ position: index + 1, total: totalCount() }) }}\n </p>\n </div>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAWA,IAAa,kBAAkC;CAC7C,QAAQ;CACR,UAAU;CACV,MAAM;CACN,QAAQ,EAAE,UAAU,YAAY,SAAS,OAAO,QAAQ,EAAE,MAAM,OAAO,KAAK;AAC9E;;;;;AAMA,SAAS,QAAQ,QAA0B;CACzC,OAAO,OAAO,SAAS,UACrB,OAAO,MAAM,SAAS,YAAY,MAAM,QAAQ,MAAM,QAAQ,IACzD,MAAM,WACP,CAAC,KAAK,CACZ;AACF;;;;;;;;;;;EAWA,MAAM,QAAQ;EAiBd,MAAM,OAAO,UAAU,YAAY,uBAAuB,MAAM,MAAM;EAKtE,MAAM,QAAQ,SAAS;EAIvB,SAAS,cAAuB;GAC9B,OAAO,QAAQ,MAAM,UAAU,KAAK,CAAC,CAAC;EACxC;EAEA,SAAS,aAAqB;GAC5B,OAAO,YAAY,CAAC,CAAC;EACvB;EACA,MAAM,OAAO,eAAe,WAAW,IAAI,CAAC;EAE5C,MAAM,QAAQ,IAAwB,IAAI;EAC1C,MAAM,QAAQ,IAAI,CAAC;EAEnB,SAAS,KAAK,MAAoB;GAChC,MAAM,KAAK,MAAM;GACjB,MAAM,QAAQ,WAAW;GACzB,IAAI,CAAC,MAAM,CAAC,OAAO;GACnB,IAAI,SAAS;GAEb,IAAI,MAAM,MAAM,UAAW,OAAO,QAAS,SAAS;QAC/C,SAAS,KAAK,IAAI,GAAG,KAAK,IAAI,QAAQ,GAAG,MAAM,CAAC;GACrD,MAAM,QAAQ;GACd,GAAG,SAAS;IAAE,MAAM,SAAS,GAAG;IAAa,UAAU,qBAAqB;GAAE,CAAC;EACjF;EAEA,MAAM,UAAU,eAAe,CAAC,MAAM,QAAQ,MAAM,SAAS,CAAC;EAC9D,MAAM,QAAQ,eAAe,CAAC,MAAM,QAAQ,MAAM,SAAS,KAAK,KAAK;;EAGrE,SAAS,UAAU,OAA4B;GAC7C,IAAI,MAAM,QAAQ,eAAe,MAAM,QAAQ,cAAc;IAC3D,MAAM,eAAe;IACrB,KAAK,MAAM,SAAS,MAAM,QAAQ,eAAe,IAAI,GAAG;GAC1D,OAAO,IAAI,MAAM,QAAQ,QAAQ;IAC/B,MAAM,eAAe;IACrB,KAAK,CAAC;GACR,OAAO,IAAI,MAAM,QAAQ,OAAO;IAC9B,MAAM,eAAe;IACrB,KAAK,KAAK,KAAK;GACjB;EACF;EAKA,IAAI,QAAQ;EACZ,SAAS,WAAiB;GACxB,qBAAqB,KAAK;GAC1B,QAAQ,4BAA4B;IAClC,MAAM,KAAK,MAAM;IACjB,IAAI,CAAC,MAAM,CAAC,GAAG,aAAa;IAC5B,MAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,OAAO,KAAK,MAAM,GAAG,aAAa,GAAG,WAAW,CAAC,CAAC;GAC5F,CAAC;EACH;EAEA,gBAAgB,MAAM,OAAO,iBAAiB,UAAU,UAAU,EAAE,SAAS,KAAK,CAAC,CAAC;EACpF,sBAAsB;GACpB,MAAM,OAAO,oBAAoB,UAAU,QAAQ;GACnD,qBAAqB,KAAK;EAC5B,CAAC;EAED,MAAM,eAAe,GACnB,8GACA,6GACF;EAIA,MAAM,iBACJ;;GAIA,OAAA,UAAA,GAAA,mBAmDM,OAAA;IAnDD,MAAK;IAAS,wBAAqB;IAAY,cAAY,MAAA,IAAA,CAAI,CAAC;IAAQ,OAAM;;IAWjF,mBAWM,OAAA;KAXG,SAAA;KAAJ,KAAI;KAAS,OAAK,eAAE,MAAA,YAAA,CAAY;KAAE,UAAS;KAAc;IAC5D,GAAA,EAAA,UAAA,IAAA,GAAA,mBASM,UAAA,MAAA,WARiB,YAAW,IAAxB,OAAO,MAAC;KADlB,OAAA,UAAA,GAAA,mBASM,OAAA;MAPH,KAAK;MACN,MAAK;MACL,wBAAqB;MACpB,cAAY,MAAA,IAAA,CAAI,CAAC,MAAK;OAAA,UAAa,IAAC;OAAA,OAAa,WAAU;MAAA,CAAA;MAC5D,OAAM;KAEN,GAAA,EAAA,UAAA,GAAA,YAAyB,wBAAT,KAAK,CAAA,EAAA,GAAA,GAAA,UAAA;;IAIzB,YASa,MAAA,kBAAA,GAAA;KARX,SAAQ;KACR,MAAK;KACJ,OAAO,MAAA,IAAA,CAAI,CAAC;KACZ,UAAU,QAAA;KACV,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,gBAAc,QAAA,CAAA;KACxB,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAK,MAAA,QAAK,CAAA;;KAElB,SAAA,cAAkD,CAAlD,YAAkD,MAAA,WAAA,GAAA;MAArC,OAAM;MAAU,eAAY;;;;;;;;IAE3C,YASa,MAAA,kBAAA,GAAA;KARX,SAAQ;KACR,MAAK;KACJ,OAAO,MAAA,IAAA,CAAI,CAAC;KACZ,UAAU,MAAA;KACV,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,gBAAc,SAAA,CAAA;KACxB,SAAK,OAAA,OAAA,OAAA,MAAA,WAAE,KAAK,MAAA,QAAK,CAAA;;KAElB,SAAA,cAAmD,CAAnD,YAAmD,MAAA,YAAA,GAAA;MAArC,OAAM;MAAU,eAAY;;;;;;;;IAM5C,mBAEI,KAFJ,YAEI,gBADC,MAAA,IAAA,CAAI,CAAC,MAAK;KAAA,UAAa,MAAA,QAAK;KAAA,OAAa,WAAU;IAAA,CAAA,CAAA,GAAA,CAAA"}
|