@adyen/bento-mcp 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/README.md +24 -2
- package/SKILL.md +37 -7
- package/dist/assets/components/accordion/accordion.stories.ts +1 -1
- package/dist/assets/components/accordion/components/accordion-item.stories.ts +1 -1
- package/dist/assets/components/accordion/components/accordion-item.vue +1 -1
- package/dist/assets/components/action-bar/action-bar.docs.mdx +10 -3
- package/dist/assets/components/action-bar/action-bar.internal.types.ts +1 -0
- package/dist/assets/components/action-bar/action-bar.stories.ts +1 -1
- package/dist/assets/components/action-bar/action-bar.types.ts +1 -1
- package/dist/assets/components/action-bar/action-bar.vue +1 -1
- package/dist/assets/components/ai-tag/ai-tag.stories.ts +1 -1
- package/dist/assets/components/ai-tag/ai-tag.vue +1 -1
- package/dist/assets/components/alert/alert.stories.ts +1 -1
- package/dist/assets/components/alert/alert.types.ts +1 -1
- package/dist/assets/components/anchor-scroller/anchor-scroller.stories.ts +1 -1
- package/dist/assets/components/anchor-scroller/anchor-scroller.vue +1 -1
- package/dist/assets/components/avatar/avatar.internal.types.ts +1 -0
- package/dist/assets/components/avatar/avatar.stories.ts +1 -1
- package/dist/assets/components/avatar/avatar.types.ts +1 -1
- package/dist/assets/components/button/button.docs.mdx +4 -3
- package/dist/assets/components/button/button.stories.ts +1 -1
- package/dist/assets/components/button/button.types.ts +1 -1
- package/dist/assets/components/button/components/ai-button/ai-button.stories.ts +1 -1
- package/dist/assets/components/button/components/ai-button/ai-button.vue +1 -1
- package/dist/assets/components/button/components/button-actions/button-actions.docs.mdx +3 -0
- package/dist/assets/components/button/components/button-actions/button-actions.internal.types.ts +1 -0
- package/dist/assets/components/button/components/button-actions/button-actions.stories.ts +1 -1
- package/dist/assets/components/button/components/button-actions/button-actions.types.ts +1 -1
- package/dist/assets/components/button/components/button-actions/button-actions.vue +1 -1
- package/dist/assets/components/button/components/loading-button/loading-button.docs.mdx +26 -23
- package/dist/assets/components/button/components/loading-button/loading-button.stories.ts +1 -1
- package/dist/assets/components/button/components/loading-button/loading-button.vue +1 -1
- package/dist/assets/components/button/components/toggle-button/toggle-button.stories.ts +1 -1
- package/dist/assets/components/card/card.docs.mdx +18 -1
- package/dist/assets/components/card/card.stories.ts +1 -1
- package/dist/assets/components/card/card.types.ts +1 -1
- package/dist/assets/components/card/card.vue +1 -1
- package/dist/assets/components/checkbox/checkbox.stories.ts +1 -1
- package/dist/assets/components/checkbox/checkbox.types.ts +1 -1
- package/dist/assets/components/checkbox/checkbox.vue +1 -1
- package/dist/assets/components/checkbox/components/checkbox-group/checkbox-group.docs.mdx +9 -14
- package/dist/assets/components/checkbox/components/checkbox-group/checkbox-group.stories.ts +1 -1
- package/dist/assets/components/checkbox/components/checkbox-group/checkbox-group.types.ts +1 -1
- package/dist/assets/components/checkbox/components/checkbox-group/checkbox-group.vue +1 -1
- package/dist/assets/components/chip/chip.stories.ts +1 -1
- package/dist/assets/components/chip/chip.vue +1 -1
- package/dist/assets/components/click-outside/click-outside.stories.ts +1 -1
- package/dist/assets/components/code-snippet/code-snippet.types.ts +1 -1
- package/dist/assets/components/code-snippet/code-snippet.vue +1 -1
- package/dist/assets/components/country/country.stories.ts +1 -1
- package/dist/assets/components/country/country.types.ts +1 -1
- package/dist/assets/components/country/country.vue +1 -1
- package/dist/assets/components/currency/currency.stories.ts +1 -1
- package/dist/assets/components/currency/currency.vue +1 -1
- package/dist/assets/components/dashed-underline/dashed-underline.stories.ts +1 -1
- package/dist/assets/components/data-grid/components/data-grid-cell-actions/data-grid-cell-actions.types.ts +1 -1
- package/dist/assets/components/data-grid/components/data-grid-cell-actions/data-grid-cell-actions.vue +1 -1
- package/dist/assets/components/data-grid/components/data-grid-config-settings/data-grid-config-settings.vue +1 -1
- package/dist/assets/components/data-grid/components/data-grid-row/data-grid-row.vue +1 -1
- package/dist/assets/components/data-grid/components/data-grid-static-icon/data-grid-static-icon.stories.ts +1 -1
- package/dist/assets/components/data-grid/components/data-grid-summary-cell/data-grid-summary-cell.stories.ts +1 -1
- package/dist/assets/components/data-grid/components/data-grid-summary-cell/data-grid-summary-cell.vue +1 -1
- package/dist/assets/components/data-grid/components/data-grid-tag-with-text-cell/data-grid-tag-with-text-cell.stories.ts +1 -1
- package/dist/assets/components/data-grid/components/data-grid-trend-cell/data-grid-trend-cell.stories.ts +1 -1
- package/dist/assets/components/data-grid/components/data-grid-trend-cell/data-grid-trend-cell.vue +1 -1
- package/dist/assets/components/data-grid/data-grid.docs.mdx +3 -60
- package/dist/assets/components/data-grid/data-grid.stories.ts +1 -1
- package/dist/assets/components/data-grid/data-grid.vue +1 -1
- package/dist/assets/components/date/date.stories.ts +1 -1
- package/dist/assets/components/date-picker/date-picker.stories.ts +1 -1
- package/dist/assets/components/date-picker/date-picker.types.ts +1 -1
- package/dist/assets/components/date-picker/date-picker.vue +1 -1
- package/dist/assets/components/date-range-picker/components/date-range-picker-calendar/date-range-picker-calendar.vue +1 -1
- package/dist/assets/components/date-range-picker/components/date-time-input/date-time-input.types.ts +1 -1
- package/dist/assets/components/date-range-picker/components/date-time-input/date-time-input.vue +1 -1
- package/dist/assets/components/date-range-picker/date-range-picker.docs.mdx +31 -6
- package/dist/assets/components/date-range-picker/date-range-picker.stories.ts +1 -1
- package/dist/assets/components/date-range-picker/date-range-picker.types.ts +1 -1
- package/dist/assets/components/date-range-picker/date-range-picker.vue +1 -1
- package/dist/assets/components/date-time-picker/date-time-picker.stories.ts +1 -1
- package/dist/assets/components/date-time-picker/date-time-picker.types.ts +1 -1
- package/dist/assets/components/divider/divider.internal.types.ts +1 -0
- package/dist/assets/components/divider/divider.stories.ts +1 -1
- package/dist/assets/components/divider/divider.types.ts +1 -1
- package/dist/assets/components/divider/divider.vue +1 -1
- package/dist/assets/components/draggable/components/draggable-handle/draggable-handle.stories.ts +1 -1
- package/dist/assets/components/draggable/draggable.stories.ts +1 -1
- package/dist/assets/components/draggable/draggable.vue +1 -1
- package/dist/assets/components/drawer/drawer.stories.ts +1 -1
- package/dist/assets/components/dropdown/components/dropdown-base-textbox/dropdown-base-textbox.internal.types.ts +1 -0
- package/dist/assets/components/dropdown/components/dropdown-base-textbox/dropdown-base-textbox.types.ts +1 -1
- package/dist/assets/components/dropdown/components/dropdown-base-textbox/dropdown-base-textbox.vue +1 -1
- package/dist/assets/components/dropdown/components/dropdown-default-textbox/dropdown-default-textbox.vue +1 -1
- package/dist/assets/components/dropdown/components/dropdown-options-container/dropdown-options-container.vue +1 -1
- package/dist/assets/components/dropdown/components/dropdown-small-textbox/dropdown-small-textbox.vue +1 -1
- package/dist/assets/components/dropdown/components/variants/dropdown-avatar/dropdown-avatar.stories.ts +1 -1
- package/dist/assets/components/dropdown/components/variants/dropdown-avatar/dropdown-avatar.types.ts +1 -1
- package/dist/assets/components/dropdown/components/variants/dropdown-avatar/dropdown-avatar.vue +1 -1
- package/dist/assets/components/dropdown/components/variants/dropdown-country/dropdown-country.stories.ts +1 -1
- package/dist/assets/components/dropdown/components/variants/dropdown-country/dropdown-country.types.ts +1 -1
- package/dist/assets/components/dropdown/components/variants/dropdown-country/dropdown-country.vue +1 -1
- package/dist/assets/components/dropdown/components/variants/dropdown-currency/dropdown-currency.stories.ts +1 -1
- package/dist/assets/components/dropdown/components/variants/dropdown-currency/dropdown-currency.types.ts +1 -1
- package/dist/assets/components/dropdown/components/variants/dropdown-currency/dropdown-currency.vue +1 -1
- package/dist/assets/components/dropdown/components/variants/dropdown-payment-method/dropdown-payment-method.stories.ts +1 -1
- package/dist/assets/components/dropdown/components/variants/dropdown-payment-method/dropdown-payment-method.types.ts +1 -1
- package/dist/assets/components/dropdown/components/variants/dropdown-payment-method/dropdown-payment-method.vue +1 -1
- package/dist/assets/components/dropdown/components/variants/dropdown-tag/dropdown-tag.stories.ts +1 -1
- package/dist/assets/components/dropdown/components/variants/dropdown-tag/dropdown-tag.types.ts +1 -1
- package/dist/assets/components/dropdown/components/variants/dropdown-tag/dropdown-tag.vue +1 -1
- package/dist/assets/components/dropdown/dropdown.docs.mdx +4 -40
- package/dist/assets/components/dropdown/dropdown.internal.types.ts +1 -0
- package/dist/assets/components/dropdown/dropdown.stories.ts +1 -1
- package/dist/assets/components/dropdown/dropdown.types.ts +1 -1
- package/dist/assets/components/dropdown/dropdown.vue +1 -1
- package/dist/assets/components/empty-state/empty-state.stories.ts +1 -1
- package/dist/assets/components/empty-state/empty-state.types.ts +1 -1
- package/dist/assets/components/file-uploader/components/file-uploader-file-card/file-uploader-file-card.stories.ts +1 -1
- package/dist/assets/components/file-uploader/components/file-uploader-file-card/file-uploader-file-card.types.ts +1 -1
- package/dist/assets/components/file-uploader/components/file-uploader-file-card/file-uploader-file-card.vue +1 -1
- package/dist/assets/components/file-uploader/components/file-uploader-restrictions/file-uploader-restrictions.types.ts +1 -1
- package/dist/assets/components/file-uploader/components/file-uploader-restrictions/file-uploader-restrictions.vue +1 -1
- package/dist/assets/components/file-uploader/file-uploader.internal.types.ts +1 -0
- package/dist/assets/components/file-uploader/file-uploader.stories.ts +1 -1
- package/dist/assets/components/file-uploader/file-uploader.types.ts +1 -1
- package/dist/assets/components/file-uploader/file-uploader.vue +1 -1
- package/dist/assets/components/filter-bar/components/all-filters-modal/all-filters-modal.stories.ts +1 -1
- package/dist/assets/components/filter-bar/components/all-filters-modal/all-filters-modal.types.ts +1 -1
- package/dist/assets/components/filter-bar/components/all-filters-modal/all-filters-modal.vue +1 -1
- package/dist/assets/components/filter-bar/components/all-filters-modal/components/all-filters-modal-filter-selection/all-filters-modal-filter-selection.types.ts +1 -0
- package/dist/assets/components/filter-bar/components/all-filters-modal/components/all-filters-modal-filter-selection/all-filters-modal-filter-selection.vue +1 -0
- package/dist/assets/components/filter-bar/components/all-filters-modal/components/all-filters-modal-selected-filter/all-filters-modal-selected-filter.vue +1 -0
- package/dist/assets/components/filter-bar/components/base-filter/base-filter.internal.types.ts +1 -0
- package/dist/assets/components/filter-bar/components/base-filter/base-filter.types.ts +1 -1
- package/dist/assets/components/filter-bar/components/base-filter/base-filter.vue +1 -1
- package/dist/assets/components/filter-bar/components/boolean-filter/boolean-filter.vue +1 -1
- package/dist/assets/components/filter-bar/components/date-filter/date-filter.vue +1 -1
- package/dist/assets/components/filter-bar/components/date-range-filter/date-range-filter.vue +1 -1
- package/dist/assets/components/filter-bar/components/input-filter/input-filter.types.ts +1 -1
- package/dist/assets/components/filter-bar/components/input-filter/input-filter.vue +1 -1
- package/dist/assets/components/filter-bar/components/input-with-dropdown-filter/input-with-dropdown-filter.stories.ts +1 -1
- package/dist/assets/components/filter-bar/components/input-with-dropdown-filter/input-with-dropdown-filter.vue +1 -1
- package/dist/assets/components/filter-bar/components/select-filter/components/select-filter-button/select-filter-button.vue +1 -1
- package/dist/assets/components/filter-bar/components/select-filter/select-filter.vue +1 -1
- package/dist/assets/components/filter-bar/components/select-filter/variants/select-avatar-filter/select-avatar-filter.stories.ts +1 -1
- package/dist/assets/components/filter-bar/components/select-filter/variants/select-country-filter/select-country-filter.stories.ts +1 -1
- package/dist/assets/components/filter-bar/components/select-filter/variants/select-country-filter/select-country-filter.vue +1 -1
- package/dist/assets/components/filter-bar/components/toggle-filter/toggle-filter.vue +1 -1
- package/dist/assets/components/filter-bar/filter-bar.docs.mdx +4 -62
- package/dist/assets/components/filter-bar/filter-bar.stories.ts +1 -1
- package/dist/assets/components/filter-bar/filter-bar.types.ts +1 -1
- package/dist/assets/components/filter-bar/filter-bar.vue +1 -1
- package/dist/assets/components/form-layout/form-layout.docs.mdx +0 -23
- package/dist/assets/components/form-layout/form-layout.stories.ts +1 -1
- package/dist/assets/components/header/header.stories.ts +1 -1
- package/dist/assets/components/header/header.types.ts +1 -1
- package/dist/assets/components/header/header.vue +1 -1
- package/dist/assets/components/header-with-views/components/header-with-views-all-views/header-with-views-all-views.vue +1 -1
- package/dist/assets/components/header-with-views/components/header-with-views-item-edit-form/header-with-views-edit-form.types.ts +1 -1
- package/dist/assets/components/header-with-views/components/header-with-views-item-edit-form/header-with-views-edit-form.vue +1 -1
- package/dist/assets/components/header-with-views/header-with-views.internal.types.ts +1 -0
- package/dist/assets/components/header-with-views/header-with-views.stories.ts +1 -1
- package/dist/assets/components/header-with-views/header-with-views.types.ts +1 -1
- package/dist/assets/components/header-with-views/header-with-views.vue +1 -1
- package/dist/assets/components/image/image.stories.ts +1 -1
- package/dist/assets/components/info-icon/info-icon.stories.ts +1 -1
- package/dist/assets/components/input-field/input-field.docs.mdx +1 -13
- package/dist/assets/components/input-field/input-field.stories.ts +1 -1
- package/dist/assets/components/input-field/input-field.types.ts +1 -1
- package/dist/assets/components/input-field/input-field.vue +1 -1
- package/dist/assets/components/input-field-password/input-field-password.internal.types.ts +1 -0
- package/dist/assets/components/input-field-password/input-field-password.stories.ts +1 -1
- package/dist/assets/components/input-field-password/input-field-password.types.ts +1 -1
- package/dist/assets/components/input-field-password/input-field-password.vue +1 -1
- package/dist/assets/components/input-field-phone-number/input-field-phone-number.stories.ts +1 -1
- package/dist/assets/components/input-field-phone-number/input-field-phone-number.types.ts +1 -1
- package/dist/assets/components/input-field-phone-number/input-field-phone-number.vue +1 -1
- package/dist/assets/components/input-time/input-time.docs.mdx +17 -0
- package/dist/assets/components/input-time/input-time.stories.ts +1 -1
- package/dist/assets/components/input-time/input-time.types.ts +1 -1
- package/dist/assets/components/input-time/input-time.vue +1 -1
- package/dist/assets/components/inspector/inspector.stories.ts +1 -1
- package/dist/assets/components/inspector/inspector.vue +1 -1
- package/dist/assets/components/layout/components/layout-row/layout-row.stories.ts +1 -1
- package/dist/assets/components/layout/components/layout-row/layout-row.vue +1 -1
- package/dist/assets/components/layout/layout.docs.mdx +10 -0
- package/dist/assets/components/layout/layout.internal.types.ts +1 -0
- package/dist/assets/components/layout/layout.stories.ts +1 -1
- package/dist/assets/components/layout/layout.types.ts +1 -1
- package/dist/assets/components/link/link.stories.ts +1 -1
- package/dist/assets/components/link/link.types.ts +1 -1
- package/dist/assets/components/link/link.vue +1 -1
- package/dist/assets/components/list/list.stories.ts +1 -1
- package/dist/assets/components/loading-indicator/loading-indicator.stories.ts +1 -1
- package/dist/assets/components/menu/components/menu-item/menu-item.types.ts +1 -1
- package/dist/assets/components/menu/components/menu-item/menu-item.vue +1 -1
- package/dist/assets/components/menu/components/menu-item-list/menu-item-list.vue +1 -1
- package/dist/assets/components/menu/components/sub-menu-item-list/sub-menu-item-list.vue +1 -1
- package/dist/assets/components/menu/menu.docs.mdx +9 -0
- package/dist/assets/components/menu/menu.stories.ts +1 -1
- package/dist/assets/components/menu/menu.types.ts +1 -1
- package/dist/assets/components/menu/menu.vue +1 -1
- package/dist/assets/components/modal/components/base-modal/base-modal.vue +1 -1
- package/dist/assets/components/modal/components/modal-page/modal-page.vue +1 -1
- package/dist/assets/components/modal/modal.internal.types.ts +1 -0
- package/dist/assets/components/modal/modal.stories.ts +1 -1
- package/dist/assets/components/modal/modal.types.ts +1 -1
- package/dist/assets/components/modal/modal.vue +1 -1
- package/dist/assets/components/modal-fullscreen/components/modal-fullscreen-page.vue +1 -1
- package/dist/assets/components/modal-fullscreen/modal-fullscreen.stories.ts +1 -1
- package/dist/assets/components/modal-fullscreen/modal-fullscreen.vue +1 -1
- package/dist/assets/components/navigation-menu/components/navigation-menu-group/navigation-menu-group.internal.types.ts +1 -0
- package/dist/assets/components/navigation-menu/components/navigation-menu-group/navigation-menu-group.types.ts +1 -1
- package/dist/assets/components/navigation-menu/components/navigation-menu-group/navigation-menu-group.vue +1 -1
- package/dist/assets/components/navigation-menu/components/navigation-menu-item/navigation-menu-item.vue +1 -1
- package/dist/assets/components/navigation-menu/navigation-menu.internal.types.ts +1 -0
- package/dist/assets/components/navigation-menu/navigation-menu.stories.ts +1 -1
- package/dist/assets/components/navigation-menu/navigation-menu.types.ts +1 -1
- package/dist/assets/components/navigation-menu/navigation-menu.vue +1 -1
- package/dist/assets/components/pagination/components/pagination-context/pagination-context.vue +1 -1
- package/dist/assets/components/pagination/components/pagination-results-per-page/pagination-results-per-page.vue +1 -1
- package/dist/assets/components/pagination/pagination.stories.ts +1 -1
- package/dist/assets/components/pagination/pagination.types.ts +1 -1
- package/dist/assets/components/pagination/pagination.vue +1 -1
- package/dist/assets/components/payment-method/payment-method.stories.ts +1 -1
- package/dist/assets/components/payment-method/payment-method.types.ts +1 -1
- package/dist/assets/components/popover/popover.docs.mdx +32 -9
- package/dist/assets/components/popover/popover.stories.ts +1 -1
- package/dist/assets/components/popover/popover.types.ts +1 -1
- package/dist/assets/components/popover/popover.vue +1 -1
- package/dist/assets/components/popper-container/popper-container.types.ts +1 -1
- package/dist/assets/components/popper-container/popper-container.vue +1 -1
- package/dist/assets/components/progress-bar/progress-bar.stories.ts +1 -1
- package/dist/assets/components/progress-bar/progress-bar.vue +1 -1
- package/dist/assets/components/promo-banner/promo-banner.stories.ts +1 -1
- package/dist/assets/components/promo-banner/promo-banner.types.ts +1 -1
- package/dist/assets/components/promo-banner/promo-banner.vue +1 -1
- package/dist/assets/components/radio-group/components/radio-button/radio-button.vue +1 -1
- package/dist/assets/components/radio-group/radio-group.docs.mdx +1 -6
- package/dist/assets/components/radio-group/radio-group.stories.ts +1 -1
- package/dist/assets/components/radio-group/radio-group.types.ts +1 -1
- package/dist/assets/components/radio-group/radio-group.vue +1 -1
- package/dist/assets/components/rich-text-editor/rich-text-editor.internal.types.ts +1 -0
- package/dist/assets/components/rich-text-editor/rich-text-editor.stories.ts +1 -1
- package/dist/assets/components/rich-text-editor/rich-text-editor.types.ts +1 -1
- package/dist/assets/components/rich-text-editor/rich-text-editor.vue +1 -1
- package/dist/assets/components/search-bar/search-bar.stories.ts +1 -1
- package/dist/assets/components/search-bar/search-bar.vue +1 -1
- package/dist/assets/components/secondary-nav/components/secondary-nav-item/secondary-nav-item.vue +1 -1
- package/dist/assets/components/secondary-nav/secondary-nav.stories.ts +1 -1
- package/dist/assets/components/secondary-nav/secondary-nav.vue +1 -1
- package/dist/assets/components/segmented-control/components/segmented-control-button/segmented-control-button.vue +1 -1
- package/dist/assets/components/segmented-control/segmented-control.stories.ts +1 -1
- package/dist/assets/components/segmented-control/segmented-control.types.ts +1 -1
- package/dist/assets/components/segmented-control/segmented-control.vue +1 -1
- package/dist/assets/components/selection-card/components/selection-card-group/selection-card-group.docs.mdx +12 -13
- package/dist/assets/components/selection-card/components/selection-card-group/selection-card-group.stories.ts +1 -1
- package/dist/assets/components/selection-card/components/selection-card-group/selection-card-group.vue +1 -1
- package/dist/assets/components/selection-card/selection-card.stories.ts +1 -1
- package/dist/assets/components/selection-card/selection-card.types.ts +1 -1
- package/dist/assets/components/selection-card/selection-card.vue +1 -1
- package/dist/assets/components/sidepanel/sidepanel.docs.mdx +1 -1
- package/dist/assets/components/sidepanel/sidepanel.stories.ts +1 -1
- package/dist/assets/components/sidepanel/sidepanel.vue +1 -1
- package/dist/assets/components/status/status.stories.ts +1 -1
- package/dist/assets/components/status/status.types.ts +1 -1
- package/dist/assets/components/stepper/components/step/step.vue +1 -1
- package/dist/assets/components/stepper/stepper.stories.ts +1 -1
- package/dist/assets/components/stepper/stepper.vue +1 -1
- package/dist/assets/components/structured-list/structured-list.stories.ts +1 -1
- package/dist/assets/components/structured-list/structured-list.vue +1 -1
- package/dist/assets/components/summary-grid/components/summary-grid-item/summary-grid-item.types.ts +1 -1
- package/dist/assets/components/summary-grid/summary-grid.stories.ts +1 -1
- package/dist/assets/components/summary-grid/summary-grid.types.ts +1 -1
- package/dist/assets/components/table-of-contents/components/table-of-contents-list/table-of-contents-list.vue +1 -1
- package/dist/assets/components/table-of-contents/table-of-contents.internal.types.ts +1 -0
- package/dist/assets/components/table-of-contents/table-of-contents.stories.ts +1 -1
- package/dist/assets/components/table-of-contents/table-of-contents.types.ts +1 -1
- package/dist/assets/components/table-of-contents/table-of-contents.vue +1 -1
- package/dist/assets/components/tabs/components/tab.internal.types.ts +1 -0
- package/dist/assets/components/tabs/components/tab.types.ts +1 -1
- package/dist/assets/components/tabs/components/tab.vue +1 -1
- package/dist/assets/components/tabs/tabs.stories.ts +1 -1
- package/dist/assets/components/tabs/tabs.vue +1 -1
- package/dist/assets/components/tag/tag.docs.mdx +1 -1
- package/dist/assets/components/tag/tag.stories.ts +1 -1
- package/dist/assets/components/tag/tag.types.ts +1 -1
- package/dist/assets/components/tag/tag.vue +1 -1
- package/dist/assets/components/textarea/textarea.docs.mdx +0 -7
- package/dist/assets/components/textarea/textarea.stories.ts +1 -1
- package/dist/assets/components/textarea/textarea.types.ts +1 -1
- package/dist/assets/components/textarea/textarea.vue +1 -1
- package/dist/assets/components/timeline/components/timeline-item/timeline-item.stories.ts +1 -1
- package/dist/assets/components/timeline/components/timeline-item/timeline-item.vue +1 -1
- package/dist/assets/components/timeline/timeline.docs.mdx +0 -10
- package/dist/assets/components/timeline/timeline.internal.types.ts +1 -0
- package/dist/assets/components/timeline/timeline.stories.ts +1 -1
- package/dist/assets/components/timeline/timeline.types.ts +1 -1
- package/dist/assets/components/timeline/timeline.vue +1 -1
- package/dist/assets/components/toast/components/toast-item/toast-item.vue +1 -1
- package/dist/assets/components/toggle/toggle.stories.ts +1 -1
- package/dist/assets/components/toggle/toggle.types.ts +1 -1
- package/dist/assets/components/toggle/toggle.vue +1 -1
- package/dist/assets/components/tutorial/tutorial.stories.ts +1 -1
- package/dist/assets/components/typography/typography.stories.ts +1 -1
- package/dist/assets/components/typography/typography.types.ts +1 -1
- package/dist/assets/components/typography/typography.vue +1 -1
- package/dist/assets/composables/use-bento-base-filter/index.ts +1 -0
- package/dist/assets/composables/use-bento-base-filter/use-bento-base-filter.ts +1 -0
- package/dist/assets/composables/use-bento-base-filter/utils/index.ts +1 -0
- package/dist/assets/composables/use-bento-base-filter/utils/utils.ts +1 -0
- package/dist/assets/composables/use-bento-currency/index.ts +1 -0
- package/dist/assets/composables/use-bento-currency/use-bento-currency.stories.ts +1 -1
- package/dist/assets/composables/use-bento-currency/use-bento-currency.ts +1 -0
- package/dist/assets/composables/use-bento-delayed-hover/index.ts +1 -0
- package/dist/assets/composables/use-bento-delayed-hover/use-bento-delayed-hover.stories.ts +1 -1
- package/dist/assets/composables/use-bento-delayed-hover/use-bento-delayed-hover.ts +1 -0
- package/dist/assets/composables/use-bento-theme/index.ts +1 -0
- package/dist/assets/composables/use-bento-theme/install.ts +1 -1
- package/dist/assets/composables/use-bento-theme/install.types.ts +1 -1
- package/dist/assets/composables/use-bento-theme/use-bento-theme.ts +1 -0
- package/dist/assets/composables/use-bento-toast-controller/index.ts +1 -0
- package/dist/assets/composables/use-bento-toast-controller/toast-stack.ts +1 -0
- package/dist/assets/composables/use-bento-toast-controller/use-bento-toast-controller.ts +1 -0
- package/dist/assets/composables/use-click-outside/index.ts +1 -0
- package/dist/assets/composables/use-click-outside/use-click-outside.stories.ts +1 -1
- package/dist/assets/composables/use-click-outside/use-click-outside.ts +1 -0
- package/dist/assets/composables/use-form-validate/index.ts +1 -0
- package/dist/assets/composables/use-form-validate/use-form.ts +1 -0
- package/dist/assets/composables/use-form-validate/use-form.types.ts +1 -1
- package/dist/assets/composables.json +9 -0
- package/dist/assets/deprecations.md +132 -236
- package/dist/assets/directives/highlight/highlight.stories.ts +1 -1
- package/dist/assets/directives/keyboard-navigation/keyboard-navigation.stories.ts +1 -1
- package/dist/assets/directives/tooltip/tooltip.internal.types.ts +1 -0
- package/dist/assets/directives/tooltip/tooltip.stories.ts +1 -1
- package/dist/assets/directives/tooltip/tooltip.types.ts +1 -1
- package/dist/assets/icons.json +1 -0
- package/dist/assets/install.ts +1 -1
- package/dist/assets/tailwind/README.md +3 -3
- package/dist/assets/templates/dashboard-template/components/dashboard-section/dashboard-section.stories.ts +1 -1
- package/dist/assets/templates/dashboard-template/dashboard-template.stories.ts +1 -1
- package/dist/assets/templates/data-grid-template/data-grid-template.stories.ts +1 -1
- package/dist/assets/usage.json +77 -77
- package/dist/assets/variables.css +113 -101
- package/dist/main.js +228 -34
- package/package.json +2 -2
- package/dist/assets/components/internal/base-button/base-button.types.ts +0 -1
- package/dist/assets/components/internal/base-button/base-button.vue +0 -1
- package/dist/assets/components/internal/button-actions-with-menu/button-actions-with-menu.stories.ts +0 -1
- package/dist/assets/components/internal/button-actions-with-menu/button-actions-with-menu.types.ts +0 -1
- package/dist/assets/components/internal/button-actions-with-menu/button-actions-with-menu.vue +0 -1
- package/dist/assets/components/internal/calendar/calendar.stories.ts +0 -1
- package/dist/assets/components/internal/calendar/calendar.types.ts +0 -1
- package/dist/assets/components/internal/calendar/calendar.vue +0 -1
- package/dist/assets/components/internal/calendar/components/calendar-month/calendar-month.stories.ts +0 -1
- package/dist/assets/components/internal/calendar/components/calendar-month/calendar-month.types.ts +0 -1
- package/dist/assets/components/internal/calendar/components/calendar-month/calendar-month.vue +0 -1
- package/dist/assets/components/internal/calendar/components/calendar-month/components/calendar-month-day/__test__/calendar-month-day-stub.vue +0 -1
- package/dist/assets/components/internal/calendar/components/calendar-month/components/calendar-month-day/calendar-month-day.stories.ts +0 -1
- package/dist/assets/components/internal/calendar/components/calendar-month/components/calendar-month-day/calendar-month-day.types.ts +0 -1
- package/dist/assets/components/internal/calendar/components/calendar-month/components/calendar-month-day/calendar-month-day.vue +0 -1
- package/dist/assets/components/internal/calendar/components/calendar-month/composables/granular-min-max-date.types.ts +0 -1
- package/dist/assets/components/internal/calendar/components/calendar-year/calendar-year.types.ts +0 -1
- package/dist/assets/components/internal/calendar/components/calendar-year/calendar-year.vue +0 -1
- package/dist/assets/components/internal/calendar/components/calendar-year/components/calendar-year-month/__test__/calendar-year-month-stub.vue +0 -1
- package/dist/assets/components/internal/calendar/components/calendar-year/components/calendar-year-month/calendar-year-month.types.ts +0 -1
- package/dist/assets/components/internal/calendar/components/calendar-year/components/calendar-year-month/calendar-year-month.vue +0 -1
- package/dist/assets/components/internal/calendar/components/calendar-year-selector/calendar-year-selector.types.ts +0 -1
- package/dist/assets/components/internal/calendar/components/calendar-year-selector/calendar-year-selector.vue +0 -1
- package/dist/assets/components/internal/calendar/composables/use-granularity-adjustments.types.ts +0 -1
- package/dist/assets/components/internal/checkbox-input/checkbox-input.stories.ts +0 -1
- package/dist/assets/components/internal/checkbox-input/checkbox-input.types.ts +0 -1
- package/dist/assets/components/internal/checkbox-input/checkbox-input.vue +0 -1
- package/dist/assets/components/internal/controls-group/controls-group.vue +0 -1
- package/dist/assets/components/internal/copy/copy.docs.mdx +0 -43
- package/dist/assets/components/internal/copy/copy.stories.ts +0 -1
- package/dist/assets/components/internal/copy/copy.types.ts +0 -1
- package/dist/assets/components/internal/copy/copy.vue +0 -1
- package/dist/assets/components/internal/decorative-checkbox/decorative-checkbox.vue +0 -1
- package/dist/assets/components/internal/dialog-page/dialog-page.types.ts +0 -1
- package/dist/assets/components/internal/dialog-page/dialog-page.vue +0 -1
- package/dist/assets/components/internal/error-message/error-message.vue +0 -1
- package/dist/assets/components/internal/field-label/field-label.vue +0 -1
- package/dist/assets/components/internal/fixed-scroller/fixed-scroller.docs.mdx +0 -21
- package/dist/assets/components/internal/fixed-scroller/fixed-scroller.stories.ts +0 -1
- package/dist/assets/components/internal/fixed-scroller/fixed-scroller.vue +0 -1
- package/dist/assets/components/internal/footer-actions/footer-actions.vue +0 -1
- package/dist/assets/components/internal/grid-layout/grid-layout.types.ts +0 -1
- package/dist/assets/components/internal/grid-layout/grid-layout.vue +0 -1
- package/dist/assets/components/internal/header-meta/header-meta.types.ts +0 -1
- package/dist/assets/components/internal/header-meta/header-meta.vue +0 -1
- package/dist/assets/components/internal/info-icon-with-popover/info-icon-with-popover.vue +0 -1
- package/dist/assets/components/internal/listbox/components/listbox-lazy-load/listbox-lazy-load.types.ts +0 -1
- package/dist/assets/components/internal/listbox/components/listbox-lazy-load/listbox-lazy-load.vue +0 -1
- package/dist/assets/components/internal/listbox/components/listbox-multi-select/components/listbox-multi-select-options/components/listbox-multi-select-category/listbox-multi-select-category.types.ts +0 -1
- package/dist/assets/components/internal/listbox/components/listbox-multi-select/components/listbox-multi-select-options/components/listbox-multi-select-category/listbox-multi-select-category.vue +0 -1
- package/dist/assets/components/internal/listbox/components/listbox-multi-select/components/listbox-multi-select-options/components/listbox-multi-select-nested-lazy-load/listbox-multi-select-nested-lazy-load.types.ts +0 -1
- package/dist/assets/components/internal/listbox/components/listbox-multi-select/components/listbox-multi-select-options/components/listbox-multi-select-nested-lazy-load/listbox-multi-select-nested-lazy-load.vue +0 -1
- package/dist/assets/components/internal/listbox/components/listbox-multi-select/components/listbox-multi-select-options/listbox-multi-select-options.types.ts +0 -1
- package/dist/assets/components/internal/listbox/components/listbox-multi-select/components/listbox-multi-select-options/listbox-multi-select-options.vue +0 -1
- package/dist/assets/components/internal/listbox/components/listbox-multi-select/listbox-multi-select.types.ts +0 -1
- package/dist/assets/components/internal/listbox/components/listbox-multi-select/listbox-multi-select.vue +0 -1
- package/dist/assets/components/internal/listbox/components/listbox-option/listbox-option.vue +0 -1
- package/dist/assets/components/internal/listbox/components/listbox-option-tag/listbox-option-tag.vue +0 -1
- package/dist/assets/components/internal/listbox/components/listbox-single-select/components/listbox-single-select-category/listbox-single-select-category.vue +0 -1
- package/dist/assets/components/internal/listbox/components/listbox-single-select/listbox-single-select.vue +0 -1
- package/dist/assets/components/internal/listbox/components/listbox-single-select-option/listbox-single-select-option.vue +0 -1
- package/dist/assets/components/internal/listbox/listbox.stories.ts +0 -1
- package/dist/assets/components/internal/listbox/listbox.types.ts +0 -1
- package/dist/assets/components/internal/listbox/listbox.vue +0 -1
- package/dist/assets/components/internal/radio-input/radio-input.stories.ts +0 -1
- package/dist/assets/components/internal/radio-input/radio-input.types.ts +0 -1
- package/dist/assets/components/internal/radio-input/radio-input.vue +0 -1
- package/dist/assets/components/internal/teleport/teleport.types.ts +0 -1
- package/dist/assets/components/internal/teleport/teleport.vue +0 -1
- package/dist/assets/components/internal/tooltip/tooltip.test.vue +0 -1
- package/dist/assets/components/internal/tooltip/tooltip.types.ts +0 -1
- package/dist/assets/components/internal/tooltip/tooltip.vue +0 -1
- package/dist/assets/composables/use-form-validate-zod-4/use-form.types.ts +0 -1
- package/dist/assets/composables/use-numeric-input/use-numeric-input.types.ts +0 -1
- package/dist/assets/composables/use-section-navigation-keyboard/use-section-navigation-keyboard.types.ts +0 -1
- package/dist/assets/composables/use-virtual-list/use-virtual-list.types.ts +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import { isVue2 } from 'vue-demi'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { computed, ref } from 'vue'; import type { Meta, StoryObj } from '@storybook/vue'; import BentoTableOfContents from './table-of-contents.vue';
|
|
1
|
+
import { isVue2 } from 'vue-demi'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { computed, ref } from 'vue'; import type { Meta, StoryObj } from '@storybook/vue'; import BentoTableOfContents from './table-of-contents.vue'; const TableOfContentsDefaultExample = "<template>\n <div class=\"b-table-of-contents-default-example\">\n <div class=\"b-table-of-contents-default-example__toc-holder\">\n <bento-table-of-contents :items=\"sections\" />\n </div>\n <div>\n <section id=\"section1\" ref=\"section1Ref\"> Section 1 content </section>\n <section id=\"section2\" ref=\"section2Ref\"> Section 2 content </section>\n <section id=\"section2-1\" ref=\"section21Ref\"> Section 2.1 content </section>\n <section id=\"section2-2\" ref=\"section22Ref\"> Section 2.2 content </section>\n <section id=\"section2-1-1\" ref=\"section211Ref\"> Section 2.1.1 content </section>\n <section id=\"section2-1-1-1\" ref=\"section2111Ref\"> Section 2.1.1.1 content </section>\n <section id=\"section2-1-2\" ref=\"section212Ref\"> Section 2.1.2 content </section>\n <section id=\"section3\" ref=\"section3Ref\"> Section 3 content </section>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { BentoTableOfContents } from '@adyen/bento-vue2';\n import { computed, ref } from 'vue';\n\n const section1Ref = ref(null);\n const section2Ref = ref(null);\n const section21Ref = ref(null);\n const section22Ref = ref(null);\n const section211Ref = ref(null);\n const section2111Ref = ref(null);\n const section212Ref = ref(null);\n const section3Ref = ref(null);\n const sections = computed(() => [\n { title: 'Section 1', elementRef: section1Ref, level: 1 },\n { title: 'Section 2', elementRef: section2Ref, level: 1 },\n { title: 'Section 2.1', elementRef: section21Ref, level: 2 },\n { title: 'Section 2.2', elementRef: section22Ref, level: 2 },\n { title: 'Section 2.1.1', elementRef: section211Ref, level: 3 },\n { title: 'Section 2.1.1.1', elementRef: section2111Ref, level: 4 },\n { title: 'Section 2.1.2', elementRef: section212Ref, level: 3 },\n { title: 'Section 3', elementRef: section3Ref, level: 1 },\n ]);\n</script>\n\n<style lang=\"scss\" scoped>\n .b-table-of-contents-default-example {\n display: grid;\n grid-template-columns: auto;\n position: relative;\n\n @media (min-width: 376px) {\n grid-template-columns: 170px auto;\n\n &__toc-holder {\n position: sticky;\n top: var(--b-spacer-120);\n }\n }\n\n section {\n min-height: 640px;\n padding-top: var(--b-spacer-120);\n }\n }\n</style>\n"; import BentoTypography from '../typography/typography.vue'; import './table-of-contents.stories.scss?module'; const meta: Meta = { title: 'Table of Contents', component: BentoTableOfContents, }; export default meta; type Story = StoryObj<typeof BentoTableOfContents>; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoTableOfContents, BentoTypography }, props: Object.keys(argTypes), template: ` <div class="b-toc-story"> <div class="b-toc-story__layout"> <div class="b-toc-story__sidebar"> <div class="b-toc-story__sidebar-inner"> <bento-table-of-contents :active-index="args.activeIndex" :items="sections" :scroll-offset="args.scrollOffset" :title="args.title" /> </div> </div> <div class="b-toc-story__content"> <section ref="section1" id="section-1" class="b-toc-story__section b-toc-story__section--medium" > <bento-typography el="h2" variant="title">Section 1</bento-typography> <bento-typography>This is the content for the first section.</bento-typography> </section> <section ref="section2" id="section-2" class="b-toc-story__section" > <bento-typography el="h2" variant="title">Section 2</bento-typography> <bento-typography>This section is shorter.</bento-typography> </section> <section ref="section21" id="section-2-1" class="b-toc-story__section" > <bento-typography el="h2" variant="title">Section 2.1</bento-typography> <bento-typography>This is nested under Section 2.</bento-typography> </section> <section ref="section22" id="section-2-2" class="b-toc-story__section" > <bento-typography el="h2" variant="title">Section 2.2</bento-typography> <bento-typography>This is another level 2 section.</bento-typography> </section> <section ref="section211" id="section-2-1-1" class="b-toc-story__section" > <bento-typography el="h2" variant="title">Section 2.1.1</bento-typography> <bento-typography>This is nested under Section 2.1.</bento-typography> </section> <section ref="section2111" id="section-2-1-1-1" class="b-toc-story__section" > <bento-typography el="h2" variant="title">Section 2.1.1.1</bento-typography> <bento-typography>This is nested under Section 2.1.1.</bento-typography> </section> <section ref="section212" id="section-2-1-2" class="b-toc-story__section" > <bento-typography el="h2" variant="title">Section 2.1.2</bento-typography> <bento-typography>This is another level 3 section.</bento-typography> </section> <section ref="section3" id="section-3" class="b-toc-story__section b-toc-story__section--large" > <bento-typography el="h2" variant="title">Section 3</bento-typography> <bento-typography>This is a tall section to demonstrate scrolling.</bento-typography> </section> </div> </div> </div> `, setup(props) { const args = isVue2 ? props : _args; const section1 = ref(null); const section2 = ref(null); const section21 = ref(null); const section22 = ref(null); const section211 = ref(null); const section2111 = ref(null); const section212 = ref(null); const section3 = ref(null); const sections = computed(() => [ { title: 'Section 1', elementRef: section1, level: 1 }, { title: 'Section 2', elementRef: section2, level: 1 }, { title: 'Section 2.1', elementRef: section21, level: 2 }, { title: 'Section 2.2', elementRef: section22, level: 2 }, { title: 'Section 2.1.1', elementRef: section211, level: 3 }, { title: 'Section 2.1.1.1', elementRef: section2111, level: 4 }, { title: 'Section 2.1.2', elementRef: section212, level: 3 }, { title: 'Section 3', elementRef: section3, level: 1 }, ]); return { args, section1, section2, section21, section22, section211, section2111, section212, section3, sections, }; }, }), args: { scrollOffset: 8, }, parameters: storybookDocsParameter(TableOfContentsDefaultExample), };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import { type SectionNavigationItem } from '@/types/section-navigation'; export type BentoTableOfContentsItem = SectionNavigationItem; export type BentoTableOfContentsItems = Array<BentoTableOfContentsItem>; export interface BentoTableOfContentsProps { /** * An array containing all the items in the table of contents. */ items: BentoTableOfContentsItems; /** * The index of the currently active item. Can be used to programmatically set the active item. * @default undefined */ activeIndex?: number; /** * Offset in pixels applied before scrolling to a section. * @default undefined */ scrollOffset?: number; /** * Table of contents title. Defaults to the translated "On this page" label. * @default undefined */ title?: string; }
|
|
1
|
+
import { type SectionNavigationItem } from '@/types/section-navigation'; export type BentoTableOfContentsItem = SectionNavigationItem; export type BentoTableOfContentsItems = Array<BentoTableOfContentsItem>; export interface BentoTableOfContentsProps { /** * An array containing all the items in the table of contents. */ items: BentoTableOfContentsItems; /** * The index of the currently active item. Can be used to programmatically set the active item. * @default undefined */ activeIndex?: number; /** * Offset in pixels applied before scrolling to a section. * @default undefined */ scrollOffset?: number; /** * Table of contents title. Defaults to the translated "On this page" label. * @default undefined */ title?: string; }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<template> <nav class="b-table-of-contents" :aria-labelledby="titleId"> <bento-typography :id="titleId" el="h2" variant="body" stronger class="b-table-of-contents__header">{{ computedTitle }}</bento-typography> <table-of-contents-list :items="items" :active-section-href="activeSectionHref" :initial-focus-index="controlledActiveIndex" :scroll-offset="scrollOffset" /> </nav> </template> <script setup lang="ts"> import { computed } from 'vue'; import { BentoTypography } from '@/components/typography'; import { TableOfContentsList } from './components'; import { generateUid } from '@/
|
|
1
|
+
<template> <nav class="b-table-of-contents" :aria-labelledby="titleId"> <bento-typography :id="titleId" el="h2" variant="body" stronger class="b-table-of-contents__header">{{ computedTitle }}</bento-typography> <table-of-contents-list :items="items" :active-section-href="activeSectionHref" :initial-focus-index="controlledActiveIndex" :scroll-offset="scrollOffset" /> </nav> </template> <script setup lang="ts"> import { computed } from 'vue'; import { BentoTypography } from '@/components/typography'; import { TableOfContentsList } from './components'; import { generateUid } from '@/utils/ts/generate-uid'; import { useScrollSpy } from '@/composables/use-scroll-spy'; import { getElementFragmentHref, type ScrollToSectionTarget } from '@/utils/ts/scroll-to-section'; import { unrefElement } from '@/directives/click-outside/utils'; import { useI18n } from '@/utils/ts/i18n'; import messages from './messages.json'; import type { BentoTableOfContentsItem, BentoTableOfContentsProps } from './table-of-contents.types'; type MessageSchema = (typeof messages)['en-US']; const { t } = useI18n<{ message: MessageSchema }>({ messages }); const props = withDefaults(defineProps<BentoTableOfContentsProps>(), { activeIndex: undefined, scrollOffset: undefined, title: undefined, }); const titleId = generateUid('table-of-contents-title'); const computedTitle = computed(() => props.title ?? t('onThisPage')); const resolvedItems = computed(() => props.items .map((item, originalIndex) => ({ element: unrefElement(item.elementRef), originalIndex, })) .filter((item): item is { element: Element; originalIndex: number } => !!item.element) ); const sectionTargetElements = computed(() => resolvedItems.value.map(item => item.element)); const controlledActiveIndex = computed(() => props.activeIndex ?? -1); const scrollSpyDefaultIndex = computed(() => props.activeIndex === undefined ? -1 : resolvedItems.value.findIndex(({ originalIndex }) => originalIndex === props.activeIndex) ); const getSectionFragmentHref = (elementRef?: BentoTableOfContentsItem['elementRef']) => getElementFragmentHref(elementRef as ScrollToSectionTarget); const { activeIndex: scrollSpyActiveIndex } = useScrollSpy(sectionTargetElements, { default: scrollSpyDefaultIndex, debounce: 300, }); const currentActiveIndex = computed(() => { if (props.activeIndex !== undefined) { return props.activeIndex; } return resolvedItems.value[scrollSpyActiveIndex.value]?.originalIndex ?? -1; }); const activeSectionHref = computed(() => getSectionFragmentHref(props.items[currentActiveIndex.value]?.elementRef)); </script> <script lang="ts"> /** * This is a navigational component that serves as a list outlining * the structure and hierarchy of content on a page. It allows users * to find the content in a page more quickly with anchor links in * the content. * * ARIA: https://www.w3.org/TR/dpub-aria-1.1/#doc-toc * * @usage * import { BentoTableOfContents } from '@adyen/bento-vue2'; * * export default { * components: { BentoTableOfContents }, * template: ` * <bento-table-of-contents * :items="[{ title: 'Section text', elementRef: sectionRef }]" * /> * `, * setup() { * const sectionRef = ref(null); * return { * sectionRef, * } * } * } */ export default { name: 'bento-table-of-contents', i18n: { messages }, }; </script> <style lang="scss" scoped src="./table-of-contents.scss" />
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { type Ref } from 'vue'; import { type MaybeRef } from '@/types/maybe-ref'; import type { BentoTabProps } from './tab.types'; export interface BentoTab { counter: Ref<number>; disabled: Ref<boolean>; errorCounter: Ref<number>; hidden: Ref<boolean>; id: string; title: MaybeRef<string>; withNotificationIndicator: Ref<boolean>; setElementRef: BentoTabProps['setElementRef']; } export type BentoTabProvidedTitle = (value: string) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
export interface BentoTabProps { /** * Displays a counter next to the tab title */ counter?: number; /** * Determines whether the tab is disabled */ disabled?: boolean; /** * Displays a counter with an error icon next to the tab title */ errorCounter?: number; /** * Prevents a tab from being displayed. * Use `hidden` instead of `v-show`. */ hidden?: boolean; /** * Title of the tab */ title: string; /** * Displays the tab with a notification indicator */ withNotificationIndicator?: boolean; /** * Callback function to safely capture the underlying DOM element of the tab. * Useful for anchoring tooltips, popovers, or tutorials. * * @example * // 1. Define your ref and setter in setup() * const tabNode = ref<Element | null>(null); * const setTabNode = (el: Element | null) => { tabNode.value = el; }; * * // 2. Pass the setter to the tab * <bento-tab :set-element-ref="setTabNode"> */ setElementRef?: (el: Element | null) => void; }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<template> <div v-show="activeTabId === id" :id="`${id}-tabpanel`" class="b-tab" role="tabpanel" :aria-labelledby="id"> <template v-if="activeTabId === id"> <slot /> </template> </div> </template> <script setup lang="ts"> import { computed, inject, onUnmounted, provide, ref, toRefs } from 'vue'; import { EXTERNAL_TITLE_REGISTER_ENTRY_INJECTION_KEY, TABS_ACTIVE_TAB_ID_KEY, TABS_REGISTER_TAB_INJECTION_KEY, } from '../tabs.keys'; import { type BentoTabsRegisterTab } from '../tabs.types'; import { type BentoTabProps
|
|
1
|
+
<template> <div v-show="activeTabId === id" :id="`${id}-tabpanel`" class="b-tab" role="tabpanel" :aria-labelledby="id"> <template v-if="activeTabId === id"> <slot /> </template> </div> </template> <script setup lang="ts"> import { computed, inject, onUnmounted, provide, ref, toRefs } from 'vue'; import { EXTERNAL_TITLE_REGISTER_ENTRY_INJECTION_KEY, TABS_ACTIVE_TAB_ID_KEY, TABS_REGISTER_TAB_INJECTION_KEY, } from '../tabs.keys'; import { type BentoTabsRegisterTab } from '../tabs.types'; import { type BentoTabProps } from './tab.types'; import { type BentoTabProvidedTitle } from './tab.internal.types'; const props = withDefaults(defineProps<BentoTabProps>(), { counter: null, disabled: false, errorCounter: null, hidden: false, withNotificationIndicator: false, setElementRef: undefined, }); // External title injection const { counter, disabled, errorCounter, hidden, withNotificationIndicator } = toRefs(props); const componentTitle = ref<BentoTabProps['title']>(); const setTitle: BentoTabProvidedTitle = (value: string) => { componentTitle.value = value; }; provide(EXTERNAL_TITLE_REGISTER_ENTRY_INJECTION_KEY, setTitle); const computedTitle = computed<BentoTabProps['title']>(() => componentTitle.value ?? props.title); const registerTab: BentoTabsRegisterTab = inject(TABS_REGISTER_TAB_INJECTION_KEY); const activeTabId = inject(TABS_ACTIVE_TAB_ID_KEY); const { id, unregisterTab } = registerTab({ counter, disabled, errorCounter, hidden, title: computedTitle, withNotificationIndicator, setElementRef: props.setElementRef, }); onUnmounted(() => unregisterTab()); </script> <script lang="ts"> /** * Tab content which attaches a tab to the `bento-tabs` component * and renders is content when it becomes active. * * @example * import { BentoTab, BentoTabs } from '@adyen/bento-vue2'; * * <template> * <bento-tabs> * <bento-tab * :counter="1" * :disabled="false" * :hidden="false" * title="Tab's title" * with-notification-indicator * > * Tab content * </bento-tab> * </bento-tabs> * </template> */ export default { name: 'bento-tab', }; </script> <style lang="scss" scoped src="./tab.scss" />
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import { isVue2 } from 'vue-demi'; import BentoTabs from './tabs.vue'; import BentoTab from './components/tab.vue'; import type { Meta, StoryObj } from '@storybook/vue'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import BentoTabsEverythingBagelExample from './__tests__/bento-tabs-everything-bagel-example.vue?raw'; const meta: Meta = { title: 'Tabs', component: BentoTabs, argTypes: { // Slots default: { name: 'default', type: { name: 'string', required: false }, description: 'Default slot content', control: 'text', table: { type: { summary: 'VNode[]' }, }, }, }, }; export default meta; type Story = StoryObj<typeof BentoTabs>; const defaultCode = ` <template> <bento-tabs :activeTabIndex="activeTabIndex" @update:active-tab-index="onTabSelected"> <bento-tab title="Maria Ahlefeldt"> (16 January 1755 - 20 December 1810) was a Danish, (originally German), composer. She is known as the first female composer in Denmark. </bento-tab> <bento-tab title="Carl Andersen"> (29 April 1847 - 7 May 1909) was a Danish flutist, conductor and composer born in Copenhagen, son of the flutist Christian Joachim Andersen. </bento-tab> <bento-tab title="Karl Truffel"> (1 March 1981 - 7 November 1992) was a Danish dog and commander born in Berlin, son of the machinist Tom Jahoor Truffel. </bento-tab> <bento-tab title="Maria Ahlefeldt" :disabled="true"> (16 January 1755 - 20 December 1810) was a Danish, (originally German), composer. She is known as the first female composer in Denmark. </bento-tab> </bento-tabs> </template> <script setup lang="ts"> import { BentoTab, BentoTabs } from '@adyen/bento-vue2'; import { ref } from 'vue' const activeTabIndex = ref(1) const onTabSelected = (newActiveTabIndex: number) => { activeTabIndex.value = newActiveTabIndex; } </script> `; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoTabs, BentoTab }, props: Object.keys(argTypes), template: ` <bento-tabs v-bind="args"> <bento-tab title="Maria Ahlefeldt"> (16 January 1755 - 20 December 1810) was a Danish, (originally German), composer. She is known as the first female composer in Denmark. </bento-tab> <bento-tab title="Carl Andersen"> (29 April 1847 - 7 May 1909) was a Danish flutist, conductor and composer born in Copenhagen, son of the flutist Christian Joachim Andersen. </bento-tab> <bento-tab title="Karl Truffel"> (1 March 1981 - 7 November 1992) was a Danish dog and commander born in Berlin, son of the machinist Tom Jahoor Truffel. </bento-tab> <bento-tab title="Maria Ahlefeldt" :disabled="true"> (16 January 1755 - 20 December 1810) was a Danish, (originally German), composer. She is known as the first female composer in Denmark. </bento-tab> </bento-tabs> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }), parameters: storybookDocsParameter(defaultCode), }; export const Sticky: Story = { render: (_args, { argTypes }) => ({ components: { BentoTabs, BentoTab }, props: Object.keys(argTypes), template: ` <bento-tabs v-bind="args"> <bento-tab title="Maria Ahlefeldt"> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas gravida dictum tellus vel semper. Maecenas id purus id massa sollicitudin lobortis sed at massa. Ut venenatis laoreet purus, ac sodales arcu facilisis vitae. Curabitur interdum quis nunc ac faucibus. Sed id dictum nibh, in dignissim augue. Mauris id convallis orci, ac ultrices nibh. Cras sollicitudin vitae tortor id aliquet. Sed vel interdum lectus. Sed nec augue non odio aliquet aliquam quis ut urna. Donec at tincidunt dolor, quis eleifend tellus. Nam ornare cursus ipsum, eu euismod dui hendrerit sed. Donec pulvinar ante a mattis gravida. Cras posuere magna nec lectus varius tincidunt. Suspendisse potenti. In congue, nibh vitae dapibus mollis, nibh erat dapibus magna, vel pretium risus nibh vitae nunc. Nunc ultricies iaculis erat, id accumsan tortor. Proin nec lacus quis arcu tempor cursus vitae et augue. Donec eget egestas est. Praesent pharetra aliquam justo id cursus. Suspendisse ut viverra dolor. Aliquam at rutrum enim, eget volutpat mi. Nunc ut pulvinar turpis. Aliquam porttitor mollis lacus et aliquam. Etiam ullamcorper ut nunc ac ultricies. Vestibulum congue ultrices dictum. Etiam volutpat, felis at fringilla luctus, turpis arcu varius dui, eget eleifend mi mauris nec nisl. Ut et elementum eros, a dapibus leo. Sed molestie lectus in dui consequat vehicula. Phasellus sodales odio et vestibulum consectetur. Maecenas sodales malesuada felis eget semper. Morbi et vestibulum arcu. Fusce ex massa, elementum at massa non, ultricies vestibulum elit. Pellentesque nec felis id quam vulputate dapibus id id dolor. Aenean facilisis nibh sed enim lacinia sodales. Cras viverra ac ipsum vitae commodo. Nulla quis congue ipsum. Nunc ultricies tristique fermentum. Fusce nec maximus nisl, id facilisis mauris. Duis nec maximus tellus. Fusce quis mauris posuere ex mollis faucibus viverra at leo. Donec ac neque fermentum, fringilla magna vel, scelerisque nisi. Nulla id vehicula lectus, ac gravida justo. Sed in lorem odio. Vivamus lacinia ipsum id nisi tincidunt mollis. Sed quis dui ut dolor tempus mattis id eu erat. Sed tempus arcu lectus, sit amet placerat dolor consequat sed. Maecenas fermentum, ligula eu luctus imperdiet, dolor nisi condimentum dolor, nec molestie justo dolor eu dolor. Nullam nisl dui, gravida et massa vel, malesuada faucibus diam. Aenean non convallis nulla, sit amet egestas eros. Ut efficitur tellus vel placerat euismod. In luctus scelerisque elit rutrum ornare. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Fusce iaculis scelerisque tincidunt. Ut vel consequat eros. Vivamus sodales dictum quam quis mattis. Curabitur rutrum bibendum dictum. Suspendisse eros nisi, maximus sed lorem eu, elementum dictum nibh. Donec pellentesque lorem non egestas aliquet. Suspendisse sit amet sagittis ipsum, sit amet venenatis nulla. Sed finibus lacus eu cursus vestibulum. Quisque laoreet tellus nunc, sit amet viverra leo consequat vel. Nunc feugiat porttitor diam, id gravida arcu pretium mollis. Duis mollis odio quam, vel rhoncus metus rhoncus non. Proin ac rutrum nisl. Pellentesque nec porta turpis, accumsan faucibus nisi. Nam at dui sed dui vestibulum porttitor. Nam sed tristique nisi, vitae placerat urna. Aliquam sit amet pretium dui, ac cursus dolor. Nullam vitae auctor neque, quis ornare massa. Duis hendrerit dolor sed ligula interdum pellentesque. Nullam enim turpis, venenatis quis diam vel, mattis porttitor metus. Vivamus vel porttitor nisi, eu commodo augue. Vestibulum libero erat, fringilla ac fermentum in, malesuada nec velit. Morbi quis venenatis dolor. Quisque a congue metus, in pellentesque nibh. Phasellus sodales felis id tempus dictum. Nam molestie nulla eu iaculis porttitor. Etiam in urna mi. Aenean sit amet egestas arcu, a pretium sem. Vestibulum dictum odio vel tempor porta. Mauris laoreet tristique feugiat. In dui est, sollicitudin sed aliquet non, posuere at augue. Praesent auctor vel tellus mollis imperdiet. Phasellus et tincidunt lacus, ut volutpat velit. Quisque vitae tortor accumsan ante semper tincidunt ac vel lorem. Duis in elit vel arcu malesuada semper ac ac mi. Duis massa sapien, lacinia ac sapien ut, volutpat sollicitudin eros. Maecenas cursus massa eu felis dignissim, gravida tincidunt lectus tristique. Maecenas et vehicula leo, consectetur vehicula orci. Sed viverra dictum lacus, ac tristique nisi laoreet vitae. Cras porta suscipit tempus. Praesent lacus massa, volutpat et convallis ac, feugiat sit amet purus. Sed semper faucibus dui, vitae ultricies urna scelerisque at. Curabitur tortor felis, porttitor non augue quis, cursus sollicitudin augue. Fusce sed viverra lorem. Integer sit amet ullamcorper odio. Vivamus et metus ac sem convallis accumsan ut vitae purus. Praesent ligula arcu, varius id vehicula non, sagittis at risus. Duis porta pellentesque commodo. Nullam fermentum nisi neque, in scelerisque eros rutrum ut. Duis id orci non justo sodales dictum et eget ex. Curabitur quis ante at lectus semper eleifend in id purus. Aenean sed suscipit nulla. Mauris ullamcorper sapien velit, sed volutpat ante luctus quis. Cras lobortis orci nec quam finibus dapibus. Aenean nec purus et nisl tristique commodo ac sed odio. Integer placerat ac mauris a elementum. Etiam vestibulum tellus diam, non eleifend justo ultrices a. Duis et elementum tellus. Pellentesque felis orci, rhoncus sed diam eu, finibus faucibus diam. Donec a magna odio. Vestibulum iaculis interdum arcu, ut vulputate nisl dictum sed. Praesent quis eros et nulla condimentum placerat. Nam vel auctor erat. Donec lobortis dapibus erat sit amet luctus. Sed vitae feugiat nulla, in suscipit massa. Nunc ut tellus consequat, pellentesque nibh id, finibus risus. Etiam vulputate a nisi vitae fermentum. Duis tempor, mauris scelerisque commodo molestie, risus massa gravida nisi, et iaculis libero risus non velit. Vestibulum in felis sed justo porttitor bibendum nec id ligula. Maecenas eros sapien, dapibus non scelerisque et, tristique et mi. Maecenas id felis odio. Donec sed lacus gravida justo egestas varius in id dolor. Nunc eu luctus urna, vitae consectetur lorem. Nunc et diam consequat, rhoncus odio non, bibendum ipsum. Aenean et sagittis elit. Aliquam posuere sit amet nisi vitae efficitur. Nulla id blandit dui, at iaculis tellus. In varius sem ac nulla volutpat egestas. Donec condimentum feugiat mauris, in tempor nibh cursus ac. Etiam interdum imperdiet vehicula. Aliquam fringilla, risus eu porttitor accumsan, mi ante consequat tellus, sed vehicula mi nulla quis nisl. Nunc sollicitudin turpis commodo urna egestas, eget pharetra ligula consequat. Donec in odio a arcu tempus tempus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vestibulum iaculis turpis quis euismod pellentesque. Curabitur mattis ex et libero auctor, vel placerat est mollis. Sed nec felis eu sapien faucibus fermentum. Morbi ut risus a lectus fringilla commodo nec in massa. Sed ac suscipit magna. Nunc vel augue mollis, fermentum risus eu, varius dolor. Quisque molestie fringilla ligula. Integer sed posuere magna. Mauris porta ornare interdum. Vivamus ullamcorper maximus lorem, eu consectetur est finibus a. Vivamus sagittis egestas eros nec porta. Sed congue lacus nisl, nec euismod ex volutpat ut. Aliquam erat volutpat. Nam posuere nisl in scelerisque venenatis. Curabitur sed mauris lorem. Mauris ornare est in velit dapibus, nec aliquet dui rhoncus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nunc euismod orci a odio iaculis, venenatis congue lorem vestibulum. Aenean sollicitudin egestas porta. Nulla ut sem eu nulla vehicula fringilla. Morbi eu sodales sem. Integer tristique tincidunt est vestibulum vehicula. Donec eget nulla congue, posuere lacus at, ornare sapien. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean interdum volutpat lobortis. Sed nulla augue, suscipit quis laoreet sit amet, vestibulum ac ex. Phasellus in volutpat diam. Phasellus tristique ultrices purus, a aliquam erat. Proin diam quam, luctus non bibendum non, vulputate eget nulla. Morbi ornare mauris eget nulla imperdiet, eu pulvinar est blandit. Mauris ut tempor arcu. Cras facilisis dictum lacus, et faucibus sem consectetur et. Cras arcu ligula, posuere ac tempor eu, ornare a felis. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean posuere congue elementum. Sed id ligula a ante tristique aliquet. Quisque nec mollis felis. Pellentesque varius eu massa venenatis dapibus. Praesent lacinia lorem nec facilisis luctus. Donec malesuada pretium dui, vitae luctus metus. Morbi ut aliquam ligula. Sed gravida urna in augue accumsan facilisis. Maecenas ultricies augue eu fermentum vestibulum. Vivamus at orci a libero sagittis pretium. Suspendisse quis est justo. Duis rutrum varius est eget lacinia. Duis augue ante, finibus quis auctor sit amet, ultrices id odio. Duis ultricies laoreet fringilla. Nulla vitae placerat lectus. Aenean nisl dui, pellentesque et bibendum quis, fringilla vitae tortor. Aenean cursus velit sed ex facilisis eleifend et eu ante. Maecenas leo est, auctor id tempus aliquet, pharetra eget leo. Nullam fermentum metus mi, ac varius velit lobortis non. Nam ultricies, orci ut lacinia elementum, justo elit efficitur orci, nec ornare augue justo vel odio. Quisque efficitur mauris ac sagittis varius. Fusce id nunc feugiat, efficitur libero eget, imperdiet mi. Nullam at sollicitudin augue, vitae bibendum turpis. Quisque vitae semper nisl. Aenean justo nulla, tincidunt at est vitae, consectetur porta turpis. Ut accumsan dolor mi, feugiat accumsan orci aliquet at. Pellentesque velit tortor, gravida quis magna quis, eleifend posuere est. Aliquam libero quam, porta non tincidunt eu, consequat eu eros. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Mauris justo lectus, maximus in faucibus vitae, vestibulum ut tortor. Nulla facilisi. Nunc et lectus tellus. Aliquam pretium fringilla mauris, eu congue dui ultricies a. Suspendisse potenti. Maecenas dapibus auctor nisl a tincidunt. Vivamus elementum ex elit, vel molestie sem tristique nec. Etiam tristique volutpat risus, at sodales ex cursus sit amet. Donec aliquam, turpis sit amet vestibulum vulputate, mi leo mollis lorem, eleifend venenatis neque sapien ac erat. Morbi ullamcorper, mi et placerat dictum, tellus libero bibendum sem, id auctor justo lectus sed leo. Phasellus lacinia scelerisque tristique. Curabitur ut congue elit, vitae ullamcorper ligula. Cras purus lacus, dignissim eget justo ac, commodo convallis arcu. In ac pellentesque metus. Proin lobortis diam ac pellentesque sodales. In porta dolor id augue ultricies laoreet. Phasellus aliquam ultricies lorem, a mattis quam maximus vel. Vestibulum eu maximus nunc, eu pellentesque lorem. Vivamus sagittis arcu odio. Praesent nec malesuada sem. Morbi lobortis ullamcorper mollis. Morbi porttitor lectus sed erat luctus cursus. Donec a lacus a ligula vehicula iaculis. Vestibulum sagittis quam eu bibendum dapibus. Donec eleifend mollis suscipit. Morbi vitae magna libero. Nunc imperdiet ligula augue, ullamcorper molestie lacus mollis eget. Vestibulum vel tellus tortor. Suspendisse tincidunt in nisl vitae volutpat. Sed pharetra consequat dui sit amet venenatis. Suspendisse lobortis quis lectus ac maximus. Phasellus feugiat quam accumsan ipsum gravida faucibus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vivamus pellentesque diam non urna tristique, vel elementum mi aliquam. Aliquam turpis urna, fringilla a lorem sit amet, vestibulum laoreet mauris. Etiam ut justo finibus, tempor turpis eu, aliquet nisi. Aliquam eget lorem quam. Ut venenatis eros velit, eget lacinia augue aliquet at. Sed massa urna, imperdiet ut augue at, varius hendrerit arcu. Curabitur quis imperdiet diam. Aliquam porta eu metus et posuere. Suspendisse finibus lacus ex, quis aliquam urna rhoncus vitae. Morbi vel massa a sem vehicula elementum. Etiam orci diam, euismod quis aliquam eget, pulvinar et magna. Suspendisse volutpat leo risus, sed pretium tellus pharetra non. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Phasellus sed nunc vel enim vulputate convallis. Maecenas sit amet pretium mauris, in suscipit ligula. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cras ultrices facilisis nunc vel suscipit. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur nunc justo, placerat tristique leo eu, facilisis tempor sapien. Nulla tempor metus ligula, sed consectetur eros hendrerit eget. Maecenas vel ex eget mi malesuada ultricies ut eget lorem. Morbi rutrum et nunc non volutpat. Sed id tellus est. Vivamus ac leo vulputate nulla pellentesque aliquam ut vel sem. Quisque sed semper enim. Quisque mattis ultrices tortor, at dapibus lectus facilisis non. Ut leo massa, semper a facilisis at, ultrices id risus. Duis id leo bibendum lacus lobortis laoreet. In hac habitasse platea dictumst. Suspendisse tempus diam ac tempus tempor. Suspendisse tincidunt quis tellus quis bibendum. Vivamus eu urna quis ex mollis mattis auctor nec sapien. Aliquam a libero egestas, accumsan ligula at, porttitor enim. Etiam finibus felis non nisl euismod, eu aliquam metus maximus. Nulla a tortor eu magna ultricies tempus sed vel risus. Nam id lacus quis massa auctor mattis. Vestibulum nec tempus enim, ut rhoncus enim. Vestibulum semper imperdiet lacus sit amet aliquet. Cras eros justo, vestibulum in mattis a, efficitur ac diam. Sed a malesuada leo. Quisque porta ex iaculis, pulvinar velit et, egestas nibh. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer et euismod sem. Aliquam ligula tortor, fermentum eu erat eu, pellentesque pharetra nibh. Fusce rutrum scelerisque purus, id auctor lorem vestibulum sed. Fusce id convallis sapien. Mauris feugiat eleifend orci. Donec nunc lorem, malesuada sit amet odio a, facilisis cursus odio. Maecenas eu faucibus lorem. Duis ut diam eu diam volutpat mollis. Aenean id dui nec ipsum porttitor scelerisque a quis purus. In scelerisque arcu fringilla, suscipit metus vitae, gravida lacus. Suspendisse non nibh consectetur, elementum nisi faucibus, tristique diam. Nunc auctor ipsum ac neque suscipit, nec dignissim est viverra. Cras luctus tempus suscipit. Maecenas ullamcorper diam ac quam fermentum venenatis. </bento-tab> <bento-tab title="Carl Andersen"> (29 April 1847 - 7 May 1909) was a Danish flutist, conductor and composer born in Copenhagen, son of the flutist Christian Joachim Andersen. </bento-tab> <bento-tab title="Karl Truffel"> (1 March 1981 - 7 November 1992) was a Danish dog and commander born in Berlin, son of the machinist Tom Jahoor Truffel. </bento-tab> <bento-tab title="Maria Ahlefeldt" :disabled="true"> (16 January 1755 - 20 December 1810) was a Danish, (originally German), composer. She is known as the first female composer in Denmark. </bento-tab> </bento-tabs> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }), parameters: storybookDocsParameter(defaultCode), args: { sticky: true, }, }; export const EverythingBagel: Story = { render: (_args, { argTypes }) => ({ components: { BentoTabs, BentoTab }, props: Object.keys(argTypes), template: ` <bento-tabs v-bind="args"> <bento-tab title="Maria Ahlefeldt"> (16 January 1755 - 20 December 1810) was a Danish, (originally German), composer. She is known as the first female composer in Denmark. </bento-tab> <bento-tab title="Carl Andersen" :counter="10" :error-counter="1" with-notification-indicator> (29 April 1847 - 7 May 1909) was a Danish flutist, conductor and composer born in Copenhagen, son of the flutist Christian Joachim Andersen. </bento-tab> <bento-tab title="Niels Gade" :counter="7"> (22 February 1817 - 21 December 1890) was a Danish composer, conductor and violinist. He was the leading Danish Romantic composer of his day. </bento-tab> <bento-tab title="Rued Langgaard" :error-counter="3"> (28 July 1893 - 10 July 1952) was a Danish composer and organist. His works were largely neglected during his lifetime but have since been widely performed. </bento-tab> <bento-tab title="Ludvig Holstein" :disabled="true" :counter="2"> (14 December 1864 - 5 July 1943) was a Danish lyrical poet known for his nature poetry and neo-romantic style. </bento-tab> <bento-tab title="Inger Christensen" with-notification-indicator> (16 January 1935 - 2 January 2009) was a Danish poet and novelist, widely regarded as one of the most important Scandinavian writers of the 20th century. </bento-tab> </bento-tabs> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }), parameters: storybookDocsParameter(BentoTabsEverythingBagelExample), };
|
|
1
|
+
import { isVue2 } from 'vue-demi'; import BentoTabs from './tabs.vue'; import BentoTab from './components/tab.vue'; import type { Meta, StoryObj } from '@storybook/vue'; import { storybookDocsParameter } from '@/utils/ts/storybook'; const BentoTabsEverythingBagelExample = "<template>\n <bento-tabs :active-tab-index=\"activeTabIndex\" @update:active-tab-index=\"onTabSelected\">\n <bento-tab title=\"Maria Ahlefeldt\">\n (16 January 1755 - 20 December 1810) was a Danish, (originally German), composer. She is known as the first\n female composer in Denmark.\n </bento-tab>\n <bento-tab title=\"Carl Andersen\" :counter=\"10\" :error-counter=\"1\" with-notification-indicator>\n (29 April 1847 - 7 May 1909) was a Danish flutist, conductor and composer born in Copenhagen, son of the\n flutist Christian Joachim Andersen.\n </bento-tab>\n <bento-tab title=\"Niels Gade\" :counter=\"7\">\n (22 February 1817 - 21 December 1890) was a Danish composer, conductor and violinist. He was the leading\n Danish Romantic composer of his day.\n </bento-tab>\n <bento-tab title=\"Rued Langgaard\" :error-counter=\"3\">\n (28 July 1893 - 10 July 1952) was a Danish composer and organist. His works were largely neglected during\n his lifetime but have since been widely performed.\n </bento-tab>\n <bento-tab title=\"Ludvig Holstein\" :disabled=\"true\" :counter=\"2\">\n (14 December 1864 - 5 July 1943) was a Danish lyrical poet known for his nature poetry and neo-romantic\n style.\n </bento-tab>\n <bento-tab title=\"Inger Christensen\" with-notification-indicator>\n (16 January 1935 - 2 January 2009) was a Danish poet and novelist, widely regarded as one of the most\n important Scandinavian writers of the 20th century.\n </bento-tab>\n </bento-tabs>\n</template>\n\n<script setup lang=\"ts\">\n import { ref } from 'vue';\n import { BentoTab, BentoTabs } from '@adyen/bento-vue2';\n\n const activeTabIndex = ref(0);\n const onTabSelected = (newActiveTabIndex: number) => {\n activeTabIndex.value = newActiveTabIndex;\n };\n</script>\n"; const TabsDefaultExample = "<template>\n <bento-tabs :active-tab-index=\"activeTabIndex\" @update:active-tab-index=\"activeTabIndex = $event\">\n <bento-tab title=\"Maria Ahlefeldt\">\n (16 January 1755 - 20 December 1810) was a Danish composer and the first known female composer in Denmark.\n </bento-tab>\n <bento-tab title=\"Carl Andersen\">\n (29 April 1847 - 7 May 1909) was a Danish flutist, conductor, and composer.\n </bento-tab>\n <bento-tab title=\"Karl Truffel\">A fictional Danish dog and commander.</bento-tab>\n <bento-tab title=\"Disabled tab\" disabled>This tab cannot be selected.</bento-tab>\n </bento-tabs>\n</template>\n\n<script setup lang=\"ts\">\n import { ref } from 'vue';\n import { BentoTab, BentoTabs } from '@adyen/bento-vue2';\n\n const activeTabIndex = ref(1);\n</script>\n"; const TabsStickyExample = "<template>\n <bento-tabs sticky :active-tab-index=\"activeTabIndex\" @update:active-tab-index=\"activeTabIndex = $event\">\n <bento-tab title=\"Maria Ahlefeldt\">\n <p>Maria Ahlefeldt was a Danish composer and the first known female composer in Denmark.</p>\n <p>Her musical work included vocal pieces, operas, and compositions for the theatre.</p>\n <p>This additional content demonstrates the sticky tab list while the page is scrolled.</p>\n <p>The tab list remains visible as readers move through longer tab content.</p>\n </bento-tab>\n <bento-tab title=\"Carl Andersen\">Carl Andersen was a Danish flutist, conductor, and composer.</bento-tab>\n <bento-tab title=\"Karl Truffel\">A fictional Danish dog and commander.</bento-tab>\n <bento-tab title=\"Disabled tab\" disabled>This tab cannot be selected.</bento-tab>\n </bento-tabs>\n</template>\n\n<script setup lang=\"ts\">\n import { ref } from 'vue';\n import { BentoTab, BentoTabs } from '@adyen/bento-vue2';\n\n const activeTabIndex = ref(1);\n</script>\n"; const meta: Meta = { title: 'Tabs', component: BentoTabs, argTypes: { // Slots default: { name: 'default', type: { name: 'string', required: false }, description: 'Default slot content', control: 'text', table: { type: { summary: 'VNode[]' }, }, }, }, }; export default meta; type Story = StoryObj<typeof BentoTabs>; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoTabs, BentoTab }, props: Object.keys(argTypes), template: ` <bento-tabs v-bind="args"> <bento-tab title="Maria Ahlefeldt"> (16 January 1755 - 20 December 1810) was a Danish, (originally German), composer. She is known as the first female composer in Denmark. </bento-tab> <bento-tab title="Carl Andersen"> (29 April 1847 - 7 May 1909) was a Danish flutist, conductor and composer born in Copenhagen, son of the flutist Christian Joachim Andersen. </bento-tab> <bento-tab title="Karl Truffel"> (1 March 1981 - 7 November 1992) was a Danish dog and commander born in Berlin, son of the machinist Tom Jahoor Truffel. </bento-tab> <bento-tab title="Maria Ahlefeldt" :disabled="true"> (16 January 1755 - 20 December 1810) was a Danish, (originally German), composer. She is known as the first female composer in Denmark. </bento-tab> </bento-tabs> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }), parameters: storybookDocsParameter(TabsDefaultExample), }; export const Sticky: Story = { render: (_args, { argTypes }) => ({ components: { BentoTabs, BentoTab }, props: Object.keys(argTypes), template: ` <bento-tabs v-bind="args"> <bento-tab title="Maria Ahlefeldt"> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas gravida dictum tellus vel semper. Maecenas id purus id massa sollicitudin lobortis sed at massa. Ut venenatis laoreet purus, ac sodales arcu facilisis vitae. Curabitur interdum quis nunc ac faucibus. Sed id dictum nibh, in dignissim augue. Mauris id convallis orci, ac ultrices nibh. Cras sollicitudin vitae tortor id aliquet. Sed vel interdum lectus. Sed nec augue non odio aliquet aliquam quis ut urna. Donec at tincidunt dolor, quis eleifend tellus. Nam ornare cursus ipsum, eu euismod dui hendrerit sed. Donec pulvinar ante a mattis gravida. Cras posuere magna nec lectus varius tincidunt. Suspendisse potenti. In congue, nibh vitae dapibus mollis, nibh erat dapibus magna, vel pretium risus nibh vitae nunc. Nunc ultricies iaculis erat, id accumsan tortor. Proin nec lacus quis arcu tempor cursus vitae et augue. Donec eget egestas est. Praesent pharetra aliquam justo id cursus. Suspendisse ut viverra dolor. Aliquam at rutrum enim, eget volutpat mi. Nunc ut pulvinar turpis. Aliquam porttitor mollis lacus et aliquam. Etiam ullamcorper ut nunc ac ultricies. Vestibulum congue ultrices dictum. Etiam volutpat, felis at fringilla luctus, turpis arcu varius dui, eget eleifend mi mauris nec nisl. Ut et elementum eros, a dapibus leo. Sed molestie lectus in dui consequat vehicula. Phasellus sodales odio et vestibulum consectetur. Maecenas sodales malesuada felis eget semper. Morbi et vestibulum arcu. Fusce ex massa, elementum at massa non, ultricies vestibulum elit. Pellentesque nec felis id quam vulputate dapibus id id dolor. Aenean facilisis nibh sed enim lacinia sodales. Cras viverra ac ipsum vitae commodo. Nulla quis congue ipsum. Nunc ultricies tristique fermentum. Fusce nec maximus nisl, id facilisis mauris. Duis nec maximus tellus. Fusce quis mauris posuere ex mollis faucibus viverra at leo. Donec ac neque fermentum, fringilla magna vel, scelerisque nisi. Nulla id vehicula lectus, ac gravida justo. Sed in lorem odio. Vivamus lacinia ipsum id nisi tincidunt mollis. Sed quis dui ut dolor tempus mattis id eu erat. Sed tempus arcu lectus, sit amet placerat dolor consequat sed. Maecenas fermentum, ligula eu luctus imperdiet, dolor nisi condimentum dolor, nec molestie justo dolor eu dolor. Nullam nisl dui, gravida et massa vel, malesuada faucibus diam. Aenean non convallis nulla, sit amet egestas eros. Ut efficitur tellus vel placerat euismod. In luctus scelerisque elit rutrum ornare. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Fusce iaculis scelerisque tincidunt. Ut vel consequat eros. Vivamus sodales dictum quam quis mattis. Curabitur rutrum bibendum dictum. Suspendisse eros nisi, maximus sed lorem eu, elementum dictum nibh. Donec pellentesque lorem non egestas aliquet. Suspendisse sit amet sagittis ipsum, sit amet venenatis nulla. Sed finibus lacus eu cursus vestibulum. Quisque laoreet tellus nunc, sit amet viverra leo consequat vel. Nunc feugiat porttitor diam, id gravida arcu pretium mollis. Duis mollis odio quam, vel rhoncus metus rhoncus non. Proin ac rutrum nisl. Pellentesque nec porta turpis, accumsan faucibus nisi. Nam at dui sed dui vestibulum porttitor. Nam sed tristique nisi, vitae placerat urna. Aliquam sit amet pretium dui, ac cursus dolor. Nullam vitae auctor neque, quis ornare massa. Duis hendrerit dolor sed ligula interdum pellentesque. Nullam enim turpis, venenatis quis diam vel, mattis porttitor metus. Vivamus vel porttitor nisi, eu commodo augue. Vestibulum libero erat, fringilla ac fermentum in, malesuada nec velit. Morbi quis venenatis dolor. Quisque a congue metus, in pellentesque nibh. Phasellus sodales felis id tempus dictum. Nam molestie nulla eu iaculis porttitor. Etiam in urna mi. Aenean sit amet egestas arcu, a pretium sem. Vestibulum dictum odio vel tempor porta. Mauris laoreet tristique feugiat. In dui est, sollicitudin sed aliquet non, posuere at augue. Praesent auctor vel tellus mollis imperdiet. Phasellus et tincidunt lacus, ut volutpat velit. Quisque vitae tortor accumsan ante semper tincidunt ac vel lorem. Duis in elit vel arcu malesuada semper ac ac mi. Duis massa sapien, lacinia ac sapien ut, volutpat sollicitudin eros. Maecenas cursus massa eu felis dignissim, gravida tincidunt lectus tristique. Maecenas et vehicula leo, consectetur vehicula orci. Sed viverra dictum lacus, ac tristique nisi laoreet vitae. Cras porta suscipit tempus. Praesent lacus massa, volutpat et convallis ac, feugiat sit amet purus. Sed semper faucibus dui, vitae ultricies urna scelerisque at. Curabitur tortor felis, porttitor non augue quis, cursus sollicitudin augue. Fusce sed viverra lorem. Integer sit amet ullamcorper odio. Vivamus et metus ac sem convallis accumsan ut vitae purus. Praesent ligula arcu, varius id vehicula non, sagittis at risus. Duis porta pellentesque commodo. Nullam fermentum nisi neque, in scelerisque eros rutrum ut. Duis id orci non justo sodales dictum et eget ex. Curabitur quis ante at lectus semper eleifend in id purus. Aenean sed suscipit nulla. Mauris ullamcorper sapien velit, sed volutpat ante luctus quis. Cras lobortis orci nec quam finibus dapibus. Aenean nec purus et nisl tristique commodo ac sed odio. Integer placerat ac mauris a elementum. Etiam vestibulum tellus diam, non eleifend justo ultrices a. Duis et elementum tellus. Pellentesque felis orci, rhoncus sed diam eu, finibus faucibus diam. Donec a magna odio. Vestibulum iaculis interdum arcu, ut vulputate nisl dictum sed. Praesent quis eros et nulla condimentum placerat. Nam vel auctor erat. Donec lobortis dapibus erat sit amet luctus. Sed vitae feugiat nulla, in suscipit massa. Nunc ut tellus consequat, pellentesque nibh id, finibus risus. Etiam vulputate a nisi vitae fermentum. Duis tempor, mauris scelerisque commodo molestie, risus massa gravida nisi, et iaculis libero risus non velit. Vestibulum in felis sed justo porttitor bibendum nec id ligula. Maecenas eros sapien, dapibus non scelerisque et, tristique et mi. Maecenas id felis odio. Donec sed lacus gravida justo egestas varius in id dolor. Nunc eu luctus urna, vitae consectetur lorem. Nunc et diam consequat, rhoncus odio non, bibendum ipsum. Aenean et sagittis elit. Aliquam posuere sit amet nisi vitae efficitur. Nulla id blandit dui, at iaculis tellus. In varius sem ac nulla volutpat egestas. Donec condimentum feugiat mauris, in tempor nibh cursus ac. Etiam interdum imperdiet vehicula. Aliquam fringilla, risus eu porttitor accumsan, mi ante consequat tellus, sed vehicula mi nulla quis nisl. Nunc sollicitudin turpis commodo urna egestas, eget pharetra ligula consequat. Donec in odio a arcu tempus tempus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Vestibulum iaculis turpis quis euismod pellentesque. Curabitur mattis ex et libero auctor, vel placerat est mollis. Sed nec felis eu sapien faucibus fermentum. Morbi ut risus a lectus fringilla commodo nec in massa. Sed ac suscipit magna. Nunc vel augue mollis, fermentum risus eu, varius dolor. Quisque molestie fringilla ligula. Integer sed posuere magna. Mauris porta ornare interdum. Vivamus ullamcorper maximus lorem, eu consectetur est finibus a. Vivamus sagittis egestas eros nec porta. Sed congue lacus nisl, nec euismod ex volutpat ut. Aliquam erat volutpat. Nam posuere nisl in scelerisque venenatis. Curabitur sed mauris lorem. Mauris ornare est in velit dapibus, nec aliquet dui rhoncus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Nunc euismod orci a odio iaculis, venenatis congue lorem vestibulum. Aenean sollicitudin egestas porta. Nulla ut sem eu nulla vehicula fringilla. Morbi eu sodales sem. Integer tristique tincidunt est vestibulum vehicula. Donec eget nulla congue, posuere lacus at, ornare sapien. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean interdum volutpat lobortis. Sed nulla augue, suscipit quis laoreet sit amet, vestibulum ac ex. Phasellus in volutpat diam. Phasellus tristique ultrices purus, a aliquam erat. Proin diam quam, luctus non bibendum non, vulputate eget nulla. Morbi ornare mauris eget nulla imperdiet, eu pulvinar est blandit. Mauris ut tempor arcu. Cras facilisis dictum lacus, et faucibus sem consectetur et. Cras arcu ligula, posuere ac tempor eu, ornare a felis. Interdum et malesuada fames ac ante ipsum primis in faucibus. Aenean posuere congue elementum. Sed id ligula a ante tristique aliquet. Quisque nec mollis felis. Pellentesque varius eu massa venenatis dapibus. Praesent lacinia lorem nec facilisis luctus. Donec malesuada pretium dui, vitae luctus metus. Morbi ut aliquam ligula. Sed gravida urna in augue accumsan facilisis. Maecenas ultricies augue eu fermentum vestibulum. Vivamus at orci a libero sagittis pretium. Suspendisse quis est justo. Duis rutrum varius est eget lacinia. Duis augue ante, finibus quis auctor sit amet, ultrices id odio. Duis ultricies laoreet fringilla. Nulla vitae placerat lectus. Aenean nisl dui, pellentesque et bibendum quis, fringilla vitae tortor. Aenean cursus velit sed ex facilisis eleifend et eu ante. Maecenas leo est, auctor id tempus aliquet, pharetra eget leo. Nullam fermentum metus mi, ac varius velit lobortis non. Nam ultricies, orci ut lacinia elementum, justo elit efficitur orci, nec ornare augue justo vel odio. Quisque efficitur mauris ac sagittis varius. Fusce id nunc feugiat, efficitur libero eget, imperdiet mi. Nullam at sollicitudin augue, vitae bibendum turpis. Quisque vitae semper nisl. Aenean justo nulla, tincidunt at est vitae, consectetur porta turpis. Ut accumsan dolor mi, feugiat accumsan orci aliquet at. Pellentesque velit tortor, gravida quis magna quis, eleifend posuere est. Aliquam libero quam, porta non tincidunt eu, consequat eu eros. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Mauris justo lectus, maximus in faucibus vitae, vestibulum ut tortor. Nulla facilisi. Nunc et lectus tellus. Aliquam pretium fringilla mauris, eu congue dui ultricies a. Suspendisse potenti. Maecenas dapibus auctor nisl a tincidunt. Vivamus elementum ex elit, vel molestie sem tristique nec. Etiam tristique volutpat risus, at sodales ex cursus sit amet. Donec aliquam, turpis sit amet vestibulum vulputate, mi leo mollis lorem, eleifend venenatis neque sapien ac erat. Morbi ullamcorper, mi et placerat dictum, tellus libero bibendum sem, id auctor justo lectus sed leo. Phasellus lacinia scelerisque tristique. Curabitur ut congue elit, vitae ullamcorper ligula. Cras purus lacus, dignissim eget justo ac, commodo convallis arcu. In ac pellentesque metus. Proin lobortis diam ac pellentesque sodales. In porta dolor id augue ultricies laoreet. Phasellus aliquam ultricies lorem, a mattis quam maximus vel. Vestibulum eu maximus nunc, eu pellentesque lorem. Vivamus sagittis arcu odio. Praesent nec malesuada sem. Morbi lobortis ullamcorper mollis. Morbi porttitor lectus sed erat luctus cursus. Donec a lacus a ligula vehicula iaculis. Vestibulum sagittis quam eu bibendum dapibus. Donec eleifend mollis suscipit. Morbi vitae magna libero. Nunc imperdiet ligula augue, ullamcorper molestie lacus mollis eget. Vestibulum vel tellus tortor. Suspendisse tincidunt in nisl vitae volutpat. Sed pharetra consequat dui sit amet venenatis. Suspendisse lobortis quis lectus ac maximus. Phasellus feugiat quam accumsan ipsum gravida faucibus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vivamus pellentesque diam non urna tristique, vel elementum mi aliquam. Aliquam turpis urna, fringilla a lorem sit amet, vestibulum laoreet mauris. Etiam ut justo finibus, tempor turpis eu, aliquet nisi. Aliquam eget lorem quam. Ut venenatis eros velit, eget lacinia augue aliquet at. Sed massa urna, imperdiet ut augue at, varius hendrerit arcu. Curabitur quis imperdiet diam. Aliquam porta eu metus et posuere. Suspendisse finibus lacus ex, quis aliquam urna rhoncus vitae. Morbi vel massa a sem vehicula elementum. Etiam orci diam, euismod quis aliquam eget, pulvinar et magna. Suspendisse volutpat leo risus, sed pretium tellus pharetra non. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Phasellus sed nunc vel enim vulputate convallis. Maecenas sit amet pretium mauris, in suscipit ligula. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Cras ultrices facilisis nunc vel suscipit. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Curabitur nunc justo, placerat tristique leo eu, facilisis tempor sapien. Nulla tempor metus ligula, sed consectetur eros hendrerit eget. Maecenas vel ex eget mi malesuada ultricies ut eget lorem. Morbi rutrum et nunc non volutpat. Sed id tellus est. Vivamus ac leo vulputate nulla pellentesque aliquam ut vel sem. Quisque sed semper enim. Quisque mattis ultrices tortor, at dapibus lectus facilisis non. Ut leo massa, semper a facilisis at, ultrices id risus. Duis id leo bibendum lacus lobortis laoreet. In hac habitasse platea dictumst. Suspendisse tempus diam ac tempus tempor. Suspendisse tincidunt quis tellus quis bibendum. Vivamus eu urna quis ex mollis mattis auctor nec sapien. Aliquam a libero egestas, accumsan ligula at, porttitor enim. Etiam finibus felis non nisl euismod, eu aliquam metus maximus. Nulla a tortor eu magna ultricies tempus sed vel risus. Nam id lacus quis massa auctor mattis. Vestibulum nec tempus enim, ut rhoncus enim. Vestibulum semper imperdiet lacus sit amet aliquet. Cras eros justo, vestibulum in mattis a, efficitur ac diam. Sed a malesuada leo. Quisque porta ex iaculis, pulvinar velit et, egestas nibh. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer et euismod sem. Aliquam ligula tortor, fermentum eu erat eu, pellentesque pharetra nibh. Fusce rutrum scelerisque purus, id auctor lorem vestibulum sed. Fusce id convallis sapien. Mauris feugiat eleifend orci. Donec nunc lorem, malesuada sit amet odio a, facilisis cursus odio. Maecenas eu faucibus lorem. Duis ut diam eu diam volutpat mollis. Aenean id dui nec ipsum porttitor scelerisque a quis purus. In scelerisque arcu fringilla, suscipit metus vitae, gravida lacus. Suspendisse non nibh consectetur, elementum nisi faucibus, tristique diam. Nunc auctor ipsum ac neque suscipit, nec dignissim est viverra. Cras luctus tempus suscipit. Maecenas ullamcorper diam ac quam fermentum venenatis. </bento-tab> <bento-tab title="Carl Andersen"> (29 April 1847 - 7 May 1909) was a Danish flutist, conductor and composer born in Copenhagen, son of the flutist Christian Joachim Andersen. </bento-tab> <bento-tab title="Karl Truffel"> (1 March 1981 - 7 November 1992) was a Danish dog and commander born in Berlin, son of the machinist Tom Jahoor Truffel. </bento-tab> <bento-tab title="Maria Ahlefeldt" :disabled="true"> (16 January 1755 - 20 December 1810) was a Danish, (originally German), composer. She is known as the first female composer in Denmark. </bento-tab> </bento-tabs> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }), parameters: storybookDocsParameter(TabsStickyExample), args: { sticky: true, }, }; export const EverythingBagel: Story = { render: (_args, { argTypes }) => ({ components: { BentoTabs, BentoTab }, props: Object.keys(argTypes), template: ` <bento-tabs v-bind="args"> <bento-tab title="Maria Ahlefeldt"> (16 January 1755 - 20 December 1810) was a Danish, (originally German), composer. She is known as the first female composer in Denmark. </bento-tab> <bento-tab title="Carl Andersen" :counter="10" :error-counter="1" with-notification-indicator> (29 April 1847 - 7 May 1909) was a Danish flutist, conductor and composer born in Copenhagen, son of the flutist Christian Joachim Andersen. </bento-tab> <bento-tab title="Niels Gade" :counter="7"> (22 February 1817 - 21 December 1890) was a Danish composer, conductor and violinist. He was the leading Danish Romantic composer of his day. </bento-tab> <bento-tab title="Rued Langgaard" :error-counter="3"> (28 July 1893 - 10 July 1952) was a Danish composer and organist. His works were largely neglected during his lifetime but have since been widely performed. </bento-tab> <bento-tab title="Ludvig Holstein" :disabled="true" :counter="2"> (14 December 1864 - 5 July 1943) was a Danish lyrical poet known for his nature poetry and neo-romantic style. </bento-tab> <bento-tab title="Inger Christensen" with-notification-indicator> (16 January 1935 - 2 January 2009) was a Danish poet and novelist, widely regarded as one of the most important Scandinavian writers of the 20th century. </bento-tab> </bento-tabs> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }), parameters: storybookDocsParameter(BentoTabsEverythingBagelExample), };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<template> <div class="b-tabs"> <div ref="tablistwrapper" class="b-tabs__tab-list-wrapper" :class="tablistWrapperConditionalClasses" :style="tablistWrapperStyles" > <fixed-scroller condensed :extra-padding-top="2"> <div ref="tablist" class="b-tabs__tab-list" role="tablist" @keydown="onKeydownTabsNavigation"> <button v-for="{ counter, disabled, errorCounter, hidden, id, title, withNotificationIndicator, setElementRef, } in tabs" v-show="!hidden" :id="id" :ref="setElementRef" :key="id" role="tab" type="button" class="b-tabs__tab" :class="{ 'b-tabs__tab--active': activeTabId === id, 'b-tabs__tab--disabled': disabled, }" :aria-controls="`${id}-tabpanel`" :aria-hidden="hidden ? 'true' : 'false'" :aria-selected="activeTabId === id ? 'true' : 'false'" :tabindex="activeTabId === id && !disabled.value ? null : -1" :disabled="isDisabled(disabled)" @click="handleTabChange(id)" @keydown.enter.space="handleTabChange(id)" > <span class="b-tabs__tab-text"> <bento-typography class="b-tabs__tab-title" el="span" variant="body" stronger> {{ title }} </bento-typography> <bento-typography v-if="counter != null" class="b-tabs__tab-counter" el="span" variant="body" stronger :class="{ 'b-tabs__tab-counter--active': activeTabId === id, }" > {{ counter }} </bento-typography> <bento-tag v-if="errorCounter != null" class="b-tabs__tab-error-counter" variant="red"> <template #icon> <cross-circle-fill-icon aria-hidden="true" /> </template>
|
|
1
|
+
<template> <div class="b-tabs"> <div ref="tablistwrapper" class="b-tabs__tab-list-wrapper" :class="tablistWrapperConditionalClasses" :style="tablistWrapperStyles" > <fixed-scroller condensed :extra-padding-top="2"> <div ref="tablist" class="b-tabs__tab-list" role="tablist" @keydown="onKeydownTabsNavigation"> <button v-for="{ counter, disabled, errorCounter, hidden, id, title, withNotificationIndicator, setElementRef, } in tabs" v-show="!hidden" :id="id" :ref="setElementRef" :key="id" role="tab" type="button" class="b-tabs__tab" :class="{ 'b-tabs__tab--active': activeTabId === id, 'b-tabs__tab--disabled': disabled, }" :aria-controls="`${id}-tabpanel`" :aria-hidden="hidden ? 'true' : 'false'" :aria-selected="activeTabId === id ? 'true' : 'false'" :tabindex="activeTabId === id && !disabled.value ? null : -1" :disabled="isDisabled(disabled)" @click="handleTabChange(id)" @keydown.enter.space="handleTabChange(id)" > <span class="b-tabs__tab-text"> <bento-typography class="b-tabs__tab-title" el="span" variant="body" stronger> {{ title }} </bento-typography> <bento-typography v-if="counter != null" class="b-tabs__tab-counter" el="span" variant="body" stronger :class="{ 'b-tabs__tab-counter--active': activeTabId === id, }" > {{ counter }} </bento-typography> <bento-tag v-if="errorCounter != null" class="b-tabs__tab-error-counter" variant="red" :label="errorCounter.toString()" :aria-label="tc('errorCount', errorCounter)" > <template #icon> <cross-circle-fill-icon aria-hidden="true" /> </template> </bento-tag> <span v-if="withNotificationIndicator" class="b-tabs__tab-notification-dot" aria-hidden="true" ></span> </span> <span v-show="activeTabId === id" class="b-tabs__tab-underline" aria-hidden="true"></span> </button> </div> </fixed-scroller> </div> <slot /> </div> </template> <script lang="ts"> import { computed, defineComponent, nextTick, onUnmounted, type PropType, provide, ref, type Ref, unref, watch, } from 'vue'; // components import { BentoTypography } from '@/components/typography'; import { BentoTag } from '@/components/tag'; import CrossCircleFillIcon from '@adyen/ui-assets-icons-16/vue/cross-circle-fill'; import { FixedScroller } from '@/internal'; // utils import { useI18n } from '@/utils/ts/i18n'; import { generateUid } from '@/utils/ts/generate-uid'; import { getNextTabIndex, getPreviousTabIndex } from './utils'; import messages from './messages.json'; // types import { type Booleanish } from '@/types/prop-types'; import { type BentoTab } from './components/tab.internal.types'; import { type BentoStickyTabsOptions, BentoTabsEvent, type BentoTabsRegisterTab } from './tabs.types'; // keys import { TABS_ACTIVE_TAB_ID_KEY, TABS_REGISTER_TAB_INJECTION_KEY } from './tabs.keys'; type MessageSchema = (typeof messages)['en-US']; /** * Tabs are used to navigate between different, but related content views within a page. * This component conforms to the official WAI-ARIA guidelines for tabs: https://www.w3.org/WAI/ARIA/apg/patterns/tabs/ * * @example * import { BentoTabs, BentoTab } from '@adyen/bento-vue2'; * * export default { * components: { BentoTabs, BentoTab }, * template: ` * <bento-tabs v-bind="$props"> * <bento-tab title="Maria Ahlefeldt"> * (16 January 1755 - 20 December 1810) was a Danish, (originally German), composer. She is known as the first female composer in Denmark. * </bento-tab> * <bento-tab title="Carl Andersen"> * (29 April 1847 - 7 May 1909) was a Danish flutist, conductor and composer born in Copenhagen, son of the flutist Christian Joachim Andersen. * </bento-tab> * <bento-tab title="Karl Truffel"> * (1 March 1981 - 7 November 1992) was a Danish dog and commander born in Berlin, son of the machinist Tom Jahoor Truffel. * </bento-tab> * <bento-tab title="Maria Ahlefeldt" :disabled="true"> * (16 January 1755 - 20 December 1810) was a Danish, (originally German), composer. She is known as the first female composer in Denmark. * </bento-tab> * </bento-tabs> * ` * }; */ // component export default defineComponent({ i18n: { messages }, name: 'bento-tabs', components: { BentoTag, BentoTypography, CrossCircleFillIcon, FixedScroller, }, props: { /** * Sets the active tab by its index (0 being the first tab item). */ activeTabIndex: { type: Number, default: 0 }, /* * Renders the tab with a sticky positioning. */ sticky: { type: Boolean, default: false }, /** * Additional configurable options for tabs rendered with a sticky positioning. * It must be an object of type {@see BentoStickyTabsOption} * @property {Ref<number>} stickyOptions.top - Top position the tabs should take up when rendered as sticky. */ stickyOptions: { type: Object as PropType<BentoStickyTabsOptions>, default: null }, }, emits: [BentoTabsEvent.ACTIVE_TAB_INDEX], setup(props, { emit }) { const { tc } = useI18n<{ message: MessageSchema }>({ messages }); const tabs: Ref<Array<BentoTab>> = ref([]); const tablist = ref(null); const tablistwrapper = ref(); const activeTabId = ref(''); const isPinnedByObserver = ref(false); let observer; provide(TABS_ACTIVE_TAB_ID_KEY, activeTabId); // unregister tab if removed / destroyed const unregisterTab = (tabIdToRemove: string) => () => { tabs.value = tabs.value?.filter(({ id }) => id !== tabIdToRemove); }; // register tab const registerTab: BentoTabsRegisterTab = ({ title, disabled, errorCounter, withNotificationIndicator, counter, hidden, setElementRef, }) => { const id = generateUid('b-tab'); // add tab to tabs tabs.value.push({ counter, disabled, errorCounter, hidden, id, title, withNotificationIndicator, setElementRef, }); // return values to the Tab component return { id, unregisterTab: unregisterTab(id) }; }; // provide activeTab and registerTab to tab items provide(TABS_REGISTER_TAB_INJECTION_KEY, registerTab); // keyboard navigation const onKeydownTabsNavigation = (e: KeyboardEvent) => { if (e.code === 'ArrowLeft' || e.code === 'ArrowRight') { // find tab that is focussed const activeTab = tabs.value.find(tab => document.activeElement.id === tab.id); let activeTabIndex = tabs.value.indexOf(activeTab); // conditionally increase or decrease the if (e.code === 'ArrowLeft') { activeTabIndex = getPreviousTabIndex(activeTabIndex, tabs.value); } else if (e.code === 'ArrowRight') { activeTabIndex = getNextTabIndex(activeTabIndex, tabs.value); } // focus new active tab tablist.value.children[activeTabIndex].focus(); } }; watch( [() => props.activeTabIndex, tabs], ([index, currentTabs]) => { nextTick(() => { const tab = currentTabs[index]; activeTabId.value = tab && !tab.disabled ? tab.id : tabs.value.find(({ disabled }) => !disabled)?.id; }); }, { immediate: true } ); const handleTabChange = (tabId: string) => { activeTabId.value = tabId; const activeIndex = tabs.value.findIndex(({ id }) => id === activeTabId.value); emit(BentoTabsEvent.ACTIVE_TAB_INDEX, activeIndex); }; const isDisabled = (isDisabledTab: Ref<boolean>): Booleanish => (isDisabledTab ? 'disabled' : false) as Booleanish; /* == Sticky tabs logic == */ const topOffset = computed(() => (props.stickyOptions?.top ? unref(props.stickyOptions.top) : 0)); const isPinned = computed(() => { if (props.sticky && topOffset.value > 0) { return true; } return isPinnedByObserver.value; }); const disconnectObserver = () => { if (observer) { observer.disconnect(); observer = null; } isPinnedByObserver.value = false; }; const setupObserver = (element: HTMLDivElement) => { // The observer is only needed when the tabs are sticky at the top of the screen (no top offset). if (props.sticky && topOffset.value === 0) { observer = new IntersectionObserver( ([e]) => { isPinnedByObserver.value = e.intersectionRatio < 1; }, { threshold: [1] } ); observer.observe(element); } }; // This watcher manages the entire lifecycle of the observer. watch( [() => props.sticky, tablistwrapper, topOffset], ([isSticky, element]) => { disconnectObserver(); if (isSticky && element) { nextTick(() => setupObserver(element)); } }, { immediate: true } ); onUnmounted(() => { disconnectObserver(); }); const tablistWrapperConditionalClasses = computed(() => ({ 'b-tabs__tab-list-wrapper--sticky': props.sticky, 'b-tabs__tab-list-wrapper--pinned': isPinned.value, })); const tablistWrapperStyles = computed(() => // This helps us detect when the tabs are pinned without calculations on the tabs positions because it makes the tabs intersect by 1 pixel with the element on top of it, which is getting aught by the intersection observer. // Followed this example: https://css-tricks.com/how-to-detect-when-a-sticky-element-gets-pinned/ props.sticky ? { top: `${(props.stickyOptions?.top ?? 0) - 1}px`, } : {} ); return { tc, tabs, tablist, tablistwrapper, activeTabId, tablistWrapperConditionalClasses, tablistWrapperStyles, onKeydownTabsNavigation, handleTabChange, isDisabled, }; }, }); </script> <style lang="scss" scoped src="./tabs.scss" />
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/vue'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import CheckmarkCircleFillIcon from '@adyen/ui-assets-icons-16/vue/checkmark-circle-fill'; import
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import CheckmarkCircleFillIcon from '@adyen/ui-assets-icons-16/vue/checkmark-circle-fill'; const TagAllVariantsTemplateExample = "<template>\n <div style=\"display: flex; gap: 10px\">\n <bento-tag variant=\"grey\" label=\"Grey\" />\n <bento-tag variant=\"green\" label=\"Green\" />\n <bento-tag variant=\"red\" label=\"Red\" />\n <bento-tag variant=\"blue\" label=\"Blue\" />\n <bento-tag variant=\"orange\" label=\"Orange\" />\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { BentoTag } from '@adyen/bento-vue2';\n</script>\n"; const TagDefaultExample = "<template>\n <bento-tag label=\"Default Tag Text\" />\n</template>\n\n<script setup lang=\"ts\">\n import { BentoTag } from '@adyen/bento-vue2';\n</script>\n"; const TagTruncatedExample = "<template>\n <bento-tag label=\"Default Tag Text\" variant=\"green\" :truncate=\"10\" />\n</template>\n\n<script setup lang=\"ts\">\n import { BentoTag } from '@adyen/bento-vue2';\n</script>\n"; const BentoTagWithIcon = "<template>\n <!-- eslint-disable-next-line @adyen/bento/deprecation-tag-default-slot -- false positive: only named #icon slot, no default slot content -->\n <bento-tag label=\"With icon\">\n <template #icon><checkmark-circle-fill-icon aria-hidden=\"true\" /></template>\n </bento-tag>\n</template>\n\n<script setup lang=\"ts\">\n import { BentoTag } from '@adyen/bento-vue2';\n import CheckmarkCircleFillIcon from '@adyen/ui-assets-icons-16/vue/checkmark-circle-fill';\n</script>\n"; import BentoTag from './tag.vue'; import { isVue2 } from 'vue-demi'; import { BentoTagVariant } from './tag.types'; export default { title: 'Tag', component: BentoTag, parameters: { layout: 'centered', }, argTypes: { default: { name: 'default (deprecated)', defaultValue: '', description: 'Tag content slot (will be deprecated, use label property instead)', table: { type: { summary: 'VNode[]' }, }, control: { type: 'text', }, }, variant: { options: Object.values(BentoTagVariant), mapping: BentoTagVariant, control: { type: 'select', labels: { white: 'DEPRECATED: white', }, }, }, }, } as Meta; type Story = StoryObj<typeof BentoTag>; const DEFAULT_TAG_PROPS = { label: 'Default Tag Text', }; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoTag }, props: Object.keys(argTypes), template: ` <div style="display:flex; gap: 10px"> <bento-tag v-bind="args" /> </div> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }), args: DEFAULT_TAG_PROPS, parameters: storybookDocsParameter(TagDefaultExample), }; export const Truncated: Story = { render: (_args, { argTypes }) => ({ components: { BentoTag }, props: Object.keys(argTypes), template: ` <div style="display:flex; gap: 10px"> <bento-tag v-bind="args" /> </div> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }), args: { ...DEFAULT_TAG_PROPS, variant: 'green', truncate: 10 }, parameters: storybookDocsParameter(TagTruncatedExample), }; export const WithIcon: Story = { render: (_args, { argTypes }) => ({ components: { BentoTag, CheckmarkCircleFillIcon }, props: Object.keys(argTypes), template: ` <div style="display:flex; gap: 10px"> <bento-tag v-bind="args"> <template #icon><checkmark-circle-fill-icon /></template> </bento-tag> </div> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }), args: DEFAULT_TAG_PROPS, parameters: storybookDocsParameter(BentoTagWithIcon), }; export const AllVariantsTemplate: Story = { render: (_args, { argTypes }) => ({ components: { BentoTag }, props: Object.keys(argTypes), template: ` <div style="display:flex; gap: 10px"> <bento-tag variant="grey" v-bind="args" label="Grey" /> <bento-tag variant="green" v-bind="args" label="Green" /> <bento-tag variant="red" v-bind="args" label="Red" /> <bento-tag variant="blue" v-bind="args" label="Blue" /> <bento-tag variant="orange" v-bind="args" label="Orange" /> </div> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }), parameters: storybookDocsParameter(TagAllVariantsTemplateExample), };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export type BentoTagVariant = 'grey' | 'blue' | 'green' | 'orange' | 'red' | 'white'; /** * @deprecated Since
|
|
1
|
+
export type BentoTagVariant = 'grey' | 'blue' | 'green' | 'orange' | 'red' | 'white'; /** * @deprecated Since v3.0.0. Use string literal instead. */ export const BentoTagVariant: Record<string, BentoTagVariant> & { /** * @deprecated Since v3.0.0. Use the `grey` variant instead. */ WHITE: 'white'; } = { GREY: 'grey', BLUE: 'blue', GREEN: 'green', ORANGE: 'orange', RED: 'red', WHITE: 'white', }; export interface BentoTagProps { /** * The label text content of the Tag. */ label?: string; /** * The number of characters to limit the number of displayed characters for the tag text, truncating it with an ellipsis of three dots "...". */ truncate?: number; /** * The variant variant of tag */ variant?: BentoTagVariant | `${BentoTagVariant}`; }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<template> <span v-bento-tooltip-directive="shouldShowTooltip ? textFromSlot : null" data-testid="tag" class="b-tag" :class="tagConditionalClasses" :aria-label="computedAriaLabel" > <div v-if="hasTextContent && hasSlot('icon')" class="b-tag__icon" role="presentation"> <slot name="icon"></slot> </div> <bento-typography class="b-tag__label" el="span" variant="caption" stronger> {{ formattedText }} </bento-typography> </span> </template> <script setup lang="ts"> import { computed, useSlots } from 'vue'; // Use relative paths otherwise the interface won't be correctly detected // TODO: Use alias once the support is added to work with the betterDefine macro import { type BentoTagProps, BentoTagVariant } from './tag.types'; import { BentoTypography } from '@/components/typography'; import { BentoTooltipDirective as vBentoTooltipDirective } from '@/directives'; import { useAriaLabel } from '@/utils/ts/aria-label'; import { getSlotText } from '@/utils/ts/get-slot-text'; const props = withDefaults(defineProps<BentoTagProps>(), { label: null, truncate: null, variant: BentoTagVariant.GREY, }); const slots = useSlots(); // Slots logic // Temporary, use-has-slots composable doesn't currently work for vue3 named scoped slots // We do the same in bento-checkbox-group const hasSlot = name => !!slots[name]; const hasTextContent = computed(() => !!(props.label || getSlotText(slots)('default'))); const computedAriaLabel = useAriaLabel({ label: props.label || getSlotText(slots)('default'), defaultFallback: 'tag', }); const tagConditionalClasses = computed(() => ({ [`b-tag--${props.variant}`]: props.variant, })); const textFromSlot = computed(() => props.label ?? getSlotText(slots)('default') ?? 'tag'); const shouldShowTooltip = computed(() => props.truncate && textFromSlot.value.length > props.truncate); const formattedText = computed(() => { if (!props.truncate) { return textFromSlot.value; } return shouldShowTooltip.value ? `${textFromSlot.value.slice(0, props.truncate)}...` : textFromSlot.value; }); </script> <script lang="ts"> export default { name: 'bento-tag', }; </script> <style lang="scss" scoped src="./tag.scss" />
|
|
1
|
+
<template> <span v-bento-tooltip-directive="shouldShowTooltip ? textFromSlot : null" data-testid="tag" class="b-tag" :class="tagConditionalClasses" :aria-label="$attrs['aria-label'] || computedAriaLabel" > <div v-if="hasTextContent && hasSlot('icon')" class="b-tag__icon" role="presentation"> <slot name="icon"></slot> </div> <bento-typography class="b-tag__label" el="span" variant="caption" stronger> {{ formattedText }} </bento-typography> </span> </template> <script setup lang="ts"> import { computed, useSlots, watch } from 'vue'; // Use relative paths otherwise the interface won't be correctly detected // TODO: Use alias once the support is added to work with the betterDefine macro import { type BentoTagProps, BentoTagVariant } from './tag.types'; import { BentoTypography } from '@/components/typography'; import { BentoTooltipDirective as vBentoTooltipDirective } from '@/directives'; import { useAriaLabel } from '@/utils/ts/aria-label'; import { getSlotText } from '@/utils/ts/get-slot-text'; import { deprecate } from '@/utils/ts/deprecate'; const props = withDefaults(defineProps<BentoTagProps>(), { label: null, truncate: null, variant: BentoTagVariant.GREY, }); const slots = useSlots(); // Slots logic // Temporary, use-has-slots composable doesn't currently work for vue3 named scoped slots // We do the same in bento-checkbox-group const hasSlot = name => !!slots[name]; const hasTextContent = computed(() => !!(props.label || getSlotText(slots)('default'))); const computedAriaLabel = useAriaLabel({ label: props.label || getSlotText(slots)('default'), defaultFallback: 'tag', }); const tagConditionalClasses = computed(() => ({ [`b-tag--${props.variant}`]: props.variant, })); const textFromSlot = computed(() => props.label ?? getSlotText(slots)('default') ?? 'tag'); const shouldShowTooltip = computed(() => props.truncate && textFromSlot.value.length > props.truncate); const formattedText = computed(() => { if (!props.truncate) { return textFromSlot.value; } return shouldShowTooltip.value ? `${textFromSlot.value.slice(0, props.truncate)}...` : textFromSlot.value; }); watch( () => props.variant, variant => { if (variant === 'white') { deprecate('BentoTag "white" variant', 'Use the BentoTag "grey" variant instead.', '3.0.0'); } }, { immediate: true } ); </script> <script lang="ts"> export default { name: 'bento-tag', }; </script> <style lang="scss" scoped src="./tag.scss" />
|
|
@@ -14,13 +14,6 @@ It's a complete form control including a label, input, icons and description tex
|
|
|
14
14
|
<Story of={TextareaStories.Default} />
|
|
15
15
|
</Canvas>
|
|
16
16
|
|
|
17
|
-
- [Textarea](#textarea)
|
|
18
|
-
- [Disabled](#disabled)
|
|
19
|
-
- [Tooltip](#tooltip)
|
|
20
|
-
- [Validation](#validation)
|
|
21
|
-
- [Accessibility](#accessibility)
|
|
22
|
-
- [Focus](#focus)
|
|
23
|
-
|
|
24
17
|
## Disabled
|
|
25
18
|
|
|
26
19
|
The disabled property determines whether the input component is enabled or disabled.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import { action } from '@storybook/addon-actions'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { type Meta, type StoryObj } from '@storybook/vue'; import { isVue2 } from 'vue-demi'; import BentoTextarea from './textarea.vue'; import { ref } from 'vue'; import { BentoTextareaResizeOption } from './textarea.types'; const meta: Meta = { title: 'Textarea', component: BentoTextarea, argTypes: { resizable: { options: Object.values(BentoTextareaResizeOption), mapping: BentoTextareaResizeOption, control: { type: 'select', }, },
|
|
1
|
+
import { action } from '@storybook/addon-actions'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { type Meta, type StoryObj } from '@storybook/vue'; import { isVue2 } from 'vue-demi'; const TextareaDefaultExample = "<template>\n <bento-textarea\n :model-value=\"textareaValue\"\n :character-limit=\"100\"\n clearable\n description=\"Description\"\n height=\"\"\n label=\"Label\"\n optional\n placeholder=\"Enter text\"\n @clear=\"textareaValue = ''\"\n @update:model-value=\"textareaValue = $event\"\n />\n</template>\n\n<script setup lang=\"ts\">\n import { ref } from 'vue';\n import { BentoTextarea } from '@adyen/bento-vue2';\n\n const textareaValue = ref('');\n</script>\n"; import BentoTextarea from './textarea.vue'; import { ref } from 'vue'; import { BentoTextareaResizeOption } from './textarea.types'; const meta: Meta = { title: 'Textarea', component: BentoTextarea, argTypes: { resizable: { options: Object.values(BentoTextareaResizeOption), mapping: BentoTextareaResizeOption, control: { type: 'select', }, }, }, }; export default meta; type Story = StoryObj<typeof BentoTextarea>; const DEFAULT_TEXTAREA_PROPS = { description: 'Description', label: 'Label', placeholder: 'Enter text', clearable: true, optional: true, height: '', characterLimit: 100, }; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoTextarea }, props: Object.keys(argTypes), template: ` <bento-textarea v-bind="args" @blur="blurAction" @change="changeAction" @clear="clearAction" @focus="focusAction" @update:model-value="updateModelValueAction" @keydown="keydownAction" @keyup="keyupAction" /> `, setup(props) { const args = isVue2 ? props : _args; const textareaValue = ref(args.modelValue); const blurAction = (value: string) => action('blur')(value); const changeAction = (value: string) => action('change')(value); const focusAction = (value: string) => action('focus')(value); const updateModelValueAction = (value: string) => action('update:model-value')(value); const clearAction = () => { textareaValue.value = ''; action('clear')(); }; const keydownAction = ({ key, code }: KeyboardEvent) => action('keydown')({ key, code }); const keyupAction = ({ key, code }: KeyboardEvent) => action('keyup')({ key, code }); return { // Methods blurAction, clearAction, changeAction, focusAction, updateModelValueAction, keydownAction, keyupAction, // Values args, textareaValue, }; }, }), args: { ...DEFAULT_TEXTAREA_PROPS, }, parameters: storybookDocsParameter(TextareaDefaultExample), };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import type { FormField } from '../../types/form-field'; /** * @deprecated Since
|
|
1
|
+
import type { FormField } from '../../types/form-field'; /** * @deprecated Since v3.0.0. Use string literal instead. */ export enum BentoTextareaResizeOption { HORIZONTAL = 'horizontal', VERTICAL = 'vertical', BOTH = 'both', NONE = 'none', } export enum BentoTextareaStateClass { CLEARABLE = 'clearable', DISABLED = 'disabled', ERROR = 'error', READONLY = 'readonly', } export enum BentoTextareaEvent { BLUR = 'blur', CHANGE = 'change', CLEAR = 'clear', ESCAPE_PRESSED = 'escape-pressed', FOCUS = 'focus', INPUT = 'input', UPDATE_MODEL_VALUE = 'update:model-value', KEYDOWN = 'keydown', KEYUP = 'keyup', } export interface BentoTextareaProps extends FormField { /** * Text that labels the input element * @default undefined */ ariaLabel?: string; /** * Text that shows number of characters in textarea * @default undefined */ characterLimit?: number; /** * If set to true, shows a button to clear all the text * @default false */ clearable?: boolean; /** * Sets the height of the textarea, e.g 200px OR 20vh * @default null */ height?: string | null; /** * Indicates the possibility of resize the textarea. * Accepts the Enum or the string literal version. * @default BentoTextareaResizeOption.BOTH */ resizable?: BentoTextareaResizeOption | `${BentoTextareaResizeOption}`; /** * Text displayed as the value of the textarea */ modelValue?: string; }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<template> <div ref="textareaComponent" class="b-textarea" :class="conditionalClasses"> <div :style="{ width: textareaWrapperWidth }"> <div class="b-textarea__label-box" :class="conditionalClassesLabelBox"> <field-label v-if="label" :for="textareaId" :label="label" :tooltip-text="tooltipText" :optional="optional" :required="required" /> <bento-typography v-if="characterLimit" el="span" class="b-textarea__label-item" :class="conditionalClassesLabelItem" > {{ counter }} </bento-typography> </div> <bento-typography el="div" class="b-textarea__input-box" :aria-disabled="disabled"> <textarea :id="textareaId" ref="textareaElement" :value="textareaValue" class="b-textarea__input" :class="conditionalClassesInput" :aria-label="computedAriaLabel" :aria-invalid="invalidInput" :readonly="isReadOnly" :required="required" :aria-required="required" :disabled="disabled" :placeholder="placeholder" :style="{ resize: resizable, height: textareaHeight }" @blur="onBlur" @change="onChange" @focus="onFocus" @input="onInput" @keydown.esc="onPressEscape" @keydown="onKeyDown" @keyup="onKeyUp" @mousedown="onMouseDown" @mouseup="onMouseUp" > </textarea> <div v-if="showClearButton" class="b-textarea__clear-button"> <bento-button variant="tertiary" :disabled="disabled" @click="onClear" @keypress.enter="onClear" @keyup.space="onClear" > <template #iconLeft> <cross-circle-fill-small-icon :svg-title="t('clearText')" /> </template> </bento-button> </div> </bento-typography> <error-message v-if="isOverCharacterLimit" :error-message="counterErrorText" /> <error-message v-if="errorMessage" :error-message="errorMessage" /> <bento-typography v-if="description" class="b-textarea__description" el="span"> {{ description }} </bento-typography> </div> </div> </template> <script setup lang="ts"> import { computed, nextTick, onMounted, onUnmounted, ref, toRef, toRefs, useAttrs, watch } from 'vue'; // Components import { BentoButton } from '@/components/button'; import { BentoTypography } from '@/components/typography'; import { ErrorMessage, FieldLabel } from '@/internal'; import CrossCircleFillSmallIcon from '@adyen/ui-assets-icons-16/vue/cross-circle-fill-small'; // Utils import { generateUid } from '@/core/utils/ts'; import { observeSizeOfElement } from '@/utils/ts/resize'; import { useAriaLabel } from '@/utils/ts/aria-label'; import { useI18n } from '@/utils/ts/i18n'; import { deprecate } from '@/utils/ts/deprecate'; import { useFormFieldEmits } from '@/utils/ts/form-field/form-field'; // Composables import { useFormLayoutFieldLoading } from '@/composables/use-form-layout-loading/use-form-layout-loading'; // Constants import { BBorderWidthAttention } from '@adyen/bento-design-tokens/dist/js/bento/es6'; // Types import { BentoTextareaEvent, type BentoTextareaProps, BentoTextareaStateClass } from './textarea.types'; import messages from './messages.json'; type MessageSchema = (typeof messages)['en-US']; const attrs = useAttrs(); const props = withDefaults(defineProps<BentoTextareaProps>(), { ariaLabel: undefined, characterLimit: undefined, description: '', disabled: false, clearable: false, errorMessage: '', height: null, label: '', optional: false, placeholder: '', readonly: false, resizable: 'both', required: false, tooltipText: null, value: '', modelValue: '', }); const emit = defineEmits<{ /** * Emitted when the element has lost focus */ (e: BentoTextareaEvent.BLUR): void; /** * Emitted when the user modifies the element's value. Unlike the 'input' event, the change event is not necessarily fired for each alteration to an element's value */ (e: BentoTextareaEvent.CHANGE): void; /** * Emitted when the "Clear" button was clicked */ (e: BentoTextareaEvent.CLEAR): void; /** * Emitted when the "ESC" key is pressed */ (e: BentoTextareaEvent.ESCAPE_PRESSED): void; /** * Emitted when the element has received focus */ (e: BentoTextareaEvent.FOCUS): void; /** * Emitted when the component's model changes * * @deprecated Since v2.0.0. Use `v-model` or `update:model-value` instead. * */ (e: BentoTextareaEvent.INPUT, value: string): void; /** * Emitted when the component's model changes */ (e: BentoTextareaEvent.UPDATE_MODEL_VALUE, value: string): void; /** * Emitted when the text area keydown event is fired */ (e: BentoTextareaEvent.KEYDOWN, event: KeyboardEvent): void; /** * Emitted when the text area keyup event is fired */ (e: BentoTextareaEvent.KEYUP, event: KeyboardEvent): void; }>(); const { height } = toRefs(props); const textareaId = generateUid('textarea'); const textareaElement = ref<HTMLTextAreaElement>(null); const textareaComponent = ref<HTMLDivElement>(null); const textareaValue = ref(props.modelValue || props.value); const defaultTextareaHeight = 44; const textareaHeight = ref(props.height || `${defaultTextareaHeight}px`); const userSetHeight = ref<number>(0); const textareaWrapperWidth = ref('100%'); // The width set to the wrapper of the label, textarea and description const isUserResizing = ref(false); let textareaElementObserver: ResizeObserver | null = null; let textareaComponentObserver: ResizeObserver | null = null; const { t, n } = useI18n<{ message: MessageSchema }>({ messages }); const { isReadOnly } = useFormLayoutFieldLoading(toRef(props, 'readonly')); const { emitValue } = useFormFieldEmits<string>(emit); const counter = computed(() => `${n(textareaValue?.value?.length ?? 0)} / ${n(props.characterLimit)}`); const isDynamicHeight = computed(() => !height.value); const isOverCharacterLimit = computed( () => props.characterLimit && textareaValue.value.length > props.characterLimit ); /** * Calculates and sets the height of the textarea. * The height is determined by the maximum of the following values: * * 1. The content's scroll height (with a minimum of `defaultTextareaHeight`) + the border width. * 2. The height set by the user manually resizing the textarea (`userSetHeight`). * 3. The default minimum height (`defaultTextareaHeight`). * * This ensures the textarea grows with content, shrinks when content is removed, * but never goes below the manually set height or the default height. */ const calculateHeight = () => { if (textareaElement.value) { const newHeight = Math.max( textareaElement.value.scrollHeight <= defaultTextareaHeight ? defaultTextareaHeight : textareaElement.value.scrollHeight + TEXTAREA_BORDER_WIDTH * 2, userSetHeight.value, defaultTextareaHeight ); textareaHeight.value = `${newHeight}px`; } }; watch(height, () => { if (isDynamicHeight.value) { calculateHeight(); } else { textareaHeight.value = height.value; } }); watch( () => [props.value, props.modelValue], () => { textareaValue.value = props.modelValue || props.value; }, { immediate: true } ); watch( () => textareaValue.value, async () => { if (isDynamicHeight.value) { // Hack to make the textarea calculate its height properly // Set the textarea back to the original value before calculating the height textareaHeight.value = `${defaultTextareaHeight}px`; if (!textareaValue.value) { return; } await nextTick(); calculateHeight(); } }, { immediate: true } ); const invalidInput = computed(() => !!props.errorMessage || isOverCharacterLimit.value); const conditionalClasses = computed(() => ({ [`b-textarea--${BentoTextareaStateClass.DISABLED}`]: props.disabled, })); const conditionalClassesInput = computed(() => ({ [`b-textarea__input--${BentoTextareaStateClass.CLEARABLE}`]: props.clearable, [`b-textarea__input--${BentoTextareaStateClass.READONLY}`]: isReadOnly.value, [`b-textarea__input--${BentoTextareaStateClass.ERROR}`]: invalidInput.value, [`b-textarea__input--dynamic-height`]: isDynamicHeight.value, })); const conditionalClassesLabelBox = computed(() => ({ [`b-textarea__label-box--no-label`]: !props.label, })); const conditionalClassesLabelItem = computed(() => ({ [`b-textarea__label-item--${BentoTextareaStateClass.ERROR}`]: isOverCharacterLimit.value, })); const counterErrorText = computed(() => t('yourTextCanBenAtMost', { characterLimit: props.characterLimit })); const ariaLabelAttribute = ref(attrs['aria-label']); const { ariaLabel, label } = toRefs(props); const computedAriaLabel = useAriaLabel({ ariaLabel: ariaLabelAttribute.value as string, // TODO: take this line away when the aria-label props is removed label: ariaLabel, defaultFallback: label, }); const showClearButton = computed( () => props.clearable && textareaValue.value && !props.disabled && !isReadOnly.value ); const onClear = () => { textareaValue.value = ''; emit(BentoTextareaEvent.CLEAR); emitValue(''); }; const onInput = (event: InputEvent) => { event.stopImmediatePropagation(); if (!props.disabled && !isReadOnly.value) { textareaValue.value = (event.target as HTMLInputElement).value; /** * Triggered when input value is changed * * @event input * @property {String} inputValue - the updated textarea value */ emitValue(textareaValue.value); } }; const onBlur = () => { textareaElement.value.scrollTop = 0; emit(BentoTextareaEvent.BLUR); }; const onFocus = () => { textareaElement.value.focus(); emit(BentoTextareaEvent.FOCUS); }; const onChange = () => { emit(BentoTextareaEvent.CHANGE); }; const onPressEscape = () => { emit(BentoTextareaEvent.ESCAPE_PRESSED); }; const onKeyDown = (event: KeyboardEvent) => { event.stopPropagation(); emit(BentoTextareaEvent.KEYDOWN, event); }; const onKeyUp = (event: KeyboardEvent) => { event.stopPropagation(); emit(BentoTextareaEvent.KEYUP, event); }; const onMouseDown = () => { isUserResizing.value = true; }; const onMouseUp = () => { isUserResizing.value = false; }; /** * Updates the textarea width depending if the container or the user update its size. * @param isTextareaElement - sets to true if the element being observed is the textarea HTML element. */ const resizeTextareaWrapper = (isTextareaElement = false) => { // Width assigned by the browser whenever the textarea is manually resized. String of the format `<value>px`. const textareaStyleWidth = textareaElement.value.style.width; // Make sure the tracked textareaHeight is always in sync with the height size in case we manually resize the textarea if (textareaElement.value.style.height) { if (isUserResizing.value) { userSetHeight.value = parseInt(textareaElement.value.style.height, 10); } textareaHeight.value = textareaElement.value.style.height; } // If the textarea element observer is being triggered, but there's no textareaStyleWidth, we want to ignore it, as no manual resize as happened yet // The component wrapper will handle the resizing logic instead if (isTextareaElement && !textareaStyleWidth) { return; } // Width of the whole bento-textarea component const textareaComponentWidth = textareaComponent?.value?.offsetWidth; // If there is no textareaStyleWidth or the textareaStyleWidth is larger than the component container, the wrapper should be 100% of the container if (!textareaStyleWidth || parseInt(textareaStyleWidth, 10) > textareaComponentWidth) { textareaWrapperWidth.value = '100%'; } else { // Else the wrapper should always be as wide as the textarea element itself textareaWrapperWidth.value = textareaStyleWidth; } }; const focus = () => { textareaElement.value.focus(); }; defineExpose({ focus, }); onMounted(() => { textareaElementObserver = observeSizeOfElement(textareaElement.value, () => { resizeTextareaWrapper(true); }); textareaComponentObserver = observeSizeOfElement(textareaComponent.value, () => { resizeTextareaWrapper(); }); if (isDynamicHeight.value) { calculateHeight(); } if (props.value) { deprecate( 'BentoTextarea "value" property', `The use of "value" prop in "BentoTextarea" is no longer supported. Use the "v-model" or "model-value" property instead.`, '2.0.0' ); } }); onUnmounted(() => { textareaElementObserver?.disconnect(); textareaComponentObserver?.disconnect(); }); </script> <script lang="ts"> const TEXTAREA_BORDER_WIDTH = parseFloat(BBorderWidthAttention); export default { i18n: { messages }, name: 'bento-textarea', model: { prop: 'modelValue' }, }; </script> <style lang="scss" scoped src="./textarea.scss" />
|
|
1
|
+
<template> <div ref="textareaComponent" class="b-textarea" :class="conditionalClasses"> <div :style="{ width: textareaWrapperWidth }"> <div class="b-textarea__label-box" :class="conditionalClassesLabelBox"> <field-label v-if="label" :for="textareaId" :label="label" :tooltip-text="tooltipText" :optional="optional" :required="required" /> <bento-typography v-if="characterLimit" el="span" class="b-textarea__label-item" :class="conditionalClassesLabelItem" > {{ counter }} </bento-typography> </div> <bento-typography el="div" class="b-textarea__input-box" :aria-disabled="disabled"> <textarea :id="textareaId" ref="textareaElement" :value="textareaValue" class="b-textarea__input" :class="conditionalClassesInput" :aria-label="computedAriaLabel" :aria-invalid="invalidInput" :readonly="isReadOnly" :required="required" :aria-required="required" :disabled="disabled" :placeholder="placeholder" :style="{ resize: resizable, height: textareaHeight }" @blur="onBlur" @change="onChange" @focus="onFocus" @input="onInput" @keydown.esc="onPressEscape" @keydown="onKeyDown" @keyup="onKeyUp" @mousedown="onMouseDown" @mouseup="onMouseUp" > </textarea> <div v-if="showClearButton" class="b-textarea__clear-button"> <bento-button variant="tertiary" :disabled="disabled" @click="onClear" @keypress.enter="onClear" @keyup.space="onClear" > <template #iconLeft> <cross-circle-fill-small-icon :svg-title="t('clearText')" /> </template> </bento-button> </div> </bento-typography> <error-message v-if="isOverCharacterLimit" :error-message="counterErrorText" /> <error-message v-if="errorMessage" :error-message="errorMessage" /> <bento-typography v-if="description" class="b-textarea__description" el="span"> {{ description }} </bento-typography> </div> </div> </template> <script setup lang="ts"> import { computed, nextTick, onMounted, onUnmounted, ref, toRef, toRefs, useAttrs, watch } from 'vue'; // Components import { BentoButton } from '@/components/button'; import { BentoTypography } from '@/components/typography'; import { ErrorMessage, FieldLabel } from '@/internal'; import CrossCircleFillSmallIcon from '@adyen/ui-assets-icons-16/vue/cross-circle-fill-small'; // Utils import { generateUid } from '@/utils/ts/generate-uid'; import { observeSizeOfElement } from '@/utils/ts/resize'; import { useAriaLabel } from '@/utils/ts/aria-label'; import { useI18n } from '@/utils/ts/i18n'; import { useFormFieldEmits } from '@/utils/ts/form-field/form-field'; // Composables import { useFormLayoutFieldLoading } from '@/composables/use-form-layout-loading/use-form-layout-loading'; // Constants import { BBorderWidthAttention } from '@adyen/bento-design-tokens/dist/js/bento/es6'; // Types import { BentoTextareaEvent, type BentoTextareaProps, BentoTextareaStateClass } from './textarea.types'; import messages from './messages.json'; type MessageSchema = (typeof messages)['en-US']; const attrs = useAttrs(); const props = withDefaults(defineProps<BentoTextareaProps>(), { ariaLabel: undefined, characterLimit: undefined, description: '', disabled: false, clearable: false, errorMessage: '', height: null, label: '', optional: false, placeholder: '', readonly: false, resizable: 'both', required: false, tooltipText: null, modelValue: '', }); const emit = defineEmits<{ /** * Emitted when the element has lost focus */ (e: BentoTextareaEvent.BLUR): void; /** * Emitted when the user modifies the element's value. Unlike the 'input' event, the change event is not necessarily fired for each alteration to an element's value */ (e: BentoTextareaEvent.CHANGE): void; /** * Emitted when the "Clear" button was clicked */ (e: BentoTextareaEvent.CLEAR): void; /** * Emitted when the "ESC" key is pressed */ (e: BentoTextareaEvent.ESCAPE_PRESSED): void; /** * Emitted when the element has received focus */ (e: BentoTextareaEvent.FOCUS): void; /** * Emitted when the component's model changes */ (e: BentoTextareaEvent.UPDATE_MODEL_VALUE, value: string): void; /** * Emitted when the text area keydown event is fired */ (e: BentoTextareaEvent.KEYDOWN, event: KeyboardEvent): void; /** * Emitted when the text area keyup event is fired */ (e: BentoTextareaEvent.KEYUP, event: KeyboardEvent): void; }>(); const { height } = toRefs(props); const textareaId = generateUid('textarea'); const textareaElement = ref<HTMLTextAreaElement>(null); const textareaComponent = ref<HTMLDivElement>(null); const textareaValue = ref(props.modelValue); const defaultTextareaHeight = 44; const textareaHeight = ref(props.height || `${defaultTextareaHeight}px`); const userSetHeight = ref<number>(0); const textareaWrapperWidth = ref('100%'); // The width set to the wrapper of the label, textarea and description const isUserResizing = ref(false); let textareaElementObserver: ResizeObserver | null = null; let textareaComponentObserver: ResizeObserver | null = null; const { t, n } = useI18n<{ message: MessageSchema }>({ messages }); const { isReadOnly } = useFormLayoutFieldLoading(toRef(props, 'readonly')); const { emitValue } = useFormFieldEmits<string>(emit); const counter = computed(() => `${n(textareaValue?.value?.length ?? 0)} / ${n(props.characterLimit)}`); const isDynamicHeight = computed(() => !height.value); const isOverCharacterLimit = computed( () => props.characterLimit && (textareaValue.value?.length ?? 0) > props.characterLimit ); /** * Calculates and sets the height of the textarea. * The height is determined by the maximum of the following values: * * 1. The content's scroll height (with a minimum of `defaultTextareaHeight`) + the border width. * 2. The height set by the user manually resizing the textarea (`userSetHeight`). * 3. The default minimum height (`defaultTextareaHeight`). * * This ensures the textarea grows with content, shrinks when content is removed, * but never goes below the manually set height or the default height. */ const calculateHeight = () => { if (textareaElement.value) { const newHeight = Math.max( textareaElement.value.scrollHeight <= defaultTextareaHeight ? defaultTextareaHeight : textareaElement.value.scrollHeight + TEXTAREA_BORDER_WIDTH * 2, userSetHeight.value, defaultTextareaHeight ); textareaHeight.value = `${newHeight}px`; } }; watch(height, () => { if (isDynamicHeight.value) { calculateHeight(); } else { textareaHeight.value = height.value; } }); watch( () => props.modelValue, () => { textareaValue.value = props.modelValue; }, { immediate: true } ); watch( () => textareaValue.value, async () => { if (isDynamicHeight.value) { // Hack to make the textarea calculate its height properly // Set the textarea back to the original value before calculating the height textareaHeight.value = `${defaultTextareaHeight}px`; if (!textareaValue.value) { return; } await nextTick(); calculateHeight(); } }, { immediate: true } ); const invalidInput = computed(() => !!props.errorMessage || isOverCharacterLimit.value); const conditionalClasses = computed(() => ({ [`b-textarea--${BentoTextareaStateClass.DISABLED}`]: props.disabled, })); const conditionalClassesInput = computed(() => ({ [`b-textarea__input--${BentoTextareaStateClass.CLEARABLE}`]: props.clearable, [`b-textarea__input--${BentoTextareaStateClass.READONLY}`]: isReadOnly.value, [`b-textarea__input--${BentoTextareaStateClass.ERROR}`]: invalidInput.value, [`b-textarea__input--dynamic-height`]: isDynamicHeight.value, })); const conditionalClassesLabelBox = computed(() => ({ [`b-textarea__label-box--no-label`]: !props.label, })); const conditionalClassesLabelItem = computed(() => ({ [`b-textarea__label-item--${BentoTextareaStateClass.ERROR}`]: isOverCharacterLimit.value, })); const counterErrorText = computed(() => t('yourTextCanBenAtMost', { characterLimit: props.characterLimit })); const ariaLabelAttribute = ref(attrs['aria-label']); const { ariaLabel, label } = toRefs(props); const computedAriaLabel = useAriaLabel({ ariaLabel: ariaLabelAttribute.value as string, // TODO: take this line away when the aria-label props is removed label: ariaLabel, defaultFallback: label, }); const showClearButton = computed( () => props.clearable && textareaValue.value && !props.disabled && !isReadOnly.value ); const onClear = () => { textareaValue.value = ''; emit(BentoTextareaEvent.CLEAR); emitValue(''); }; const onInput = (event: InputEvent) => { event.stopImmediatePropagation(); if (!props.disabled && !isReadOnly.value) { textareaValue.value = (event.target as HTMLInputElement).value; /** * Triggered when input value is changed * * @event update:model-value * @property {String} inputValue - the updated textarea value */ emitValue(textareaValue.value); } }; const onBlur = () => { textareaElement.value.scrollTop = 0; emit(BentoTextareaEvent.BLUR); }; const onFocus = () => { textareaElement.value.focus(); emit(BentoTextareaEvent.FOCUS); }; const onChange = () => { emit(BentoTextareaEvent.CHANGE); }; const onPressEscape = () => { emit(BentoTextareaEvent.ESCAPE_PRESSED); }; const onKeyDown = (event: KeyboardEvent) => { event.stopPropagation(); emit(BentoTextareaEvent.KEYDOWN, event); }; const onKeyUp = (event: KeyboardEvent) => { event.stopPropagation(); emit(BentoTextareaEvent.KEYUP, event); }; const onMouseDown = () => { isUserResizing.value = true; }; const onMouseUp = () => { isUserResizing.value = false; }; /** * Updates the textarea width depending if the container or the user update its size. * @param isTextareaElement - sets to true if the element being observed is the textarea HTML element. */ const resizeTextareaWrapper = (isTextareaElement = false) => { // Width assigned by the browser whenever the textarea is manually resized. String of the format `<value>px`. const textareaStyleWidth = textareaElement.value.style.width; // Make sure the tracked textareaHeight is always in sync with the height size in case we manually resize the textarea if (textareaElement.value.style.height) { if (isUserResizing.value) { userSetHeight.value = parseInt(textareaElement.value.style.height, 10); } textareaHeight.value = textareaElement.value.style.height; } // If the textarea element observer is being triggered, but there's no textareaStyleWidth, we want to ignore it, as no manual resize as happened yet // The component wrapper will handle the resizing logic instead if (isTextareaElement && !textareaStyleWidth) { return; } // Width of the whole bento-textarea component const textareaComponentWidth = textareaComponent?.value?.offsetWidth; // If there is no textareaStyleWidth or the textareaStyleWidth is larger than the component container, the wrapper should be 100% of the container if (!textareaStyleWidth || parseInt(textareaStyleWidth, 10) > textareaComponentWidth) { textareaWrapperWidth.value = '100%'; } else { // Else the wrapper should always be as wide as the textarea element itself textareaWrapperWidth.value = textareaStyleWidth; } }; const focus = () => { textareaElement.value.focus(); }; defineExpose({ focus, }); onMounted(() => { textareaElementObserver = observeSizeOfElement(textareaElement.value, () => { resizeTextareaWrapper(true); }); textareaComponentObserver = observeSizeOfElement(textareaComponent.value, () => { resizeTextareaWrapper(); }); if (isDynamicHeight.value) { calculateHeight(); } }); onUnmounted(() => { textareaElementObserver?.disconnect(); textareaComponentObserver?.disconnect(); }); </script> <script lang="ts"> const TEXTAREA_BORDER_WIDTH = parseFloat(BBorderWidthAttention); export default { i18n: { messages }, name: 'bento-textarea', model: { prop: 'modelValue', event: 'update:model-value' }, }; </script> <style lang="scss" scoped src="./textarea.scss" />
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import BentoTimelineItem from './timeline-item.vue'; import BentoTimeline from '../../timeline.vue'; import type { Meta, StoryObj } from '@storybook/vue'; import {
|
|
1
|
+
import BentoTimelineItem from './timeline-item.vue'; import BentoTimeline from '../../timeline.vue'; import type { Meta, StoryObj } from '@storybook/vue'; import { isVue2 } from 'vue-demi'; import { BentoDateFormat } from '@/components/date'; const meta: Meta = { title: 'Timeline/Timeline Item', component: BentoTimelineItem, argTypes: { type: { control: { type: 'select', }, }, status: { control: { type: 'select', }, }, }, }; export default meta; type Story = StoryObj<typeof BentoTimeline>; const DEFAULT_PROPS = { title: 'Milestone', timestamp: { date: new Date(), value: 'AuthorName', format: BentoDateFormat.FULL_DATE_EXACT_TIME_WITHOUT_PERIOD, }, dataList: [ { label: 'Label', value: 'Value' }, { label: 'Label', value: 'Value' }, ], description: 'Space to put some description about this milestone', tag: { label: 'Tag' }, status: 'black', }; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoTimeline, BentoTimelineItem }, props: Object.keys(argTypes), template: ` <bento-timeline> <bento-timeline-item v-bind="args" /> </bento-timeline> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }), args: { ...DEFAULT_PROPS, }, };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<template> <li v-if="isVisible" class="b-timeline-item" :class="conditionalClasses"> <div v-if="timegap" class="b-timeline-item__row"> <div class="b-timeline-item__marker" aria-hidden="true"> <div class="b-timeline-item__icon b-timeline-item__icon--timegap"></div> <div class="b-timeline-item__separator"></div> </div> <div class="b-timeline-item__content"> <bento-typography class="b-timeline-item__timegap" :aria-label="timegapAriaLabel"> {{ timegap }} </bento-typography> </div> </div> <div class="b-timeline-item__row"> <div class="b-timeline-item__marker" aria-hidden="true"> <square-small-icon class="b-timeline-item__icon" :class="iconConditionalClasses" /> <div v-if="!isLastItem" class="b-timeline-item__separator" data-testid="last-timeline-item-separator" ></div> </div> <div class="b-timeline-item__content"> <bento-typography v-if="!isNoteType" el="div" variant="body" strongest class="b-timeline-item__title" role="heading" aria-level="2" > <bento-link v-if="isLinkTitle" v-bind="linkTitleProps" disable-typography>{{ linkTitleProps.title }}</bento-link> <span v-else>{{ title }}</span> </bento-typography> <bento-typography v-if="isNoteType && (description || hasSlot('default'))" el="div" variant="body" class="b-timeline-item__description" > <slot>{{ description }}</slot> </bento-typography> <bento-typography v-if="timestamp" class="b-timeline-item__timestamp" el="div" variant="caption"> <bento-date :date="timestamp.date" :format="timestamp.format" disable-typography /> <bento-dashed-underline v-if="timestamp.description" v-bind="timestamp.description"> {{ timestamp.value }} </bento-dashed-underline> <span v-else>{{ timestamp.value }}</span> </bento-typography> <bento-typography v-if="!isNoteType && additionalTimestamp" class="b-timeline-item__timestamp" el="div" variant="caption" > <bento-date :date="additionalTimestamp.date" :format="additionalTimestamp.format" disable-typography /> <bento-dashed-underline v-if="additionalTimestamp.description" v-bind="additionalTimestamp.description" > {{ additionalTimestamp.value }} </bento-dashed-underline> <span v-else> {{ additionalTimestamp.value }} </span> </bento-typography> <bento-typography v-if="!isNoteType && (description || hasSlot('default'))" el="div" variant="body" class="b-timeline-item__description" > <slot>{{ description }}</slot> </bento-typography> <div v-if="!isNoteType && dataList" class="b-timeline-item__data-list"> <bento-structured-list> <bento-structured-list-item v-for="listEntry in dataList" :key="generateUid(listEntry.label)" :label="listEntry.label" > <bento-typography variant="caption">{{ listEntry.value }}</bento-typography> </bento-structured-list-item> </bento-structured-list> </div> <bento-tag v-if="!isNoteType && tag" v-bind="tag" class="b-timeline-item__tag" /> </div> </div> <div v-if="displayShowMoreButton" class="b-timeline-item__row"> <div class="b-timeline-item__marker" aria-hidden="true"> <div class="b-timeline-item__separator"></div> </div> <div class="b-timeline-item__content"> <bento-button class="b-timeline__show-more" variant="tertiary" @click="showAll = !showAll"> <template v-if="!showAll">{{ t('showMore', { items: hiddenItems }) }}</template> <template v-else>{{ t('showLess') }}</template> </bento-button> </div> </div> </li> </template> <script setup lang="ts"> import { computed, defineComponent, inject, onUnmounted, provide, useSlots } from 'vue'; // Components import { BentoButton } from '@/components/button'; import { BentoDashedUnderline } from '@/components/dashed-underline'; import { BentoDate } from '@/components/date'; import { BentoLink } from '@/components/link'; import { BentoTag } from '@/components/tag'; import { BentoTypography } from '@/components/typography'; import { BentoStructuredList, BentoStructuredListItem } from '@/components/structured-list'; import SquareSmallIcon from '@adyen/ui-assets-icons-16/vue/square-small-fill'; // Utils import {
|
|
1
|
+
<template> <li v-if="isVisible" class="b-timeline-item" :class="conditionalClasses"> <div v-if="timegap" class="b-timeline-item__row"> <div class="b-timeline-item__marker" aria-hidden="true"> <div class="b-timeline-item__icon b-timeline-item__icon--timegap"></div> <div class="b-timeline-item__separator"></div> </div> <div class="b-timeline-item__content"> <bento-typography class="b-timeline-item__timegap" :aria-label="timegapAriaLabel"> {{ timegap }} </bento-typography> </div> </div> <div class="b-timeline-item__row"> <div class="b-timeline-item__marker" aria-hidden="true"> <square-small-icon class="b-timeline-item__icon" :class="iconConditionalClasses" /> <div v-if="!isLastItem" class="b-timeline-item__separator" data-testid="last-timeline-item-separator" ></div> </div> <div class="b-timeline-item__content"> <bento-typography v-if="!isNoteType" el="div" variant="body" strongest class="b-timeline-item__title" role="heading" aria-level="2" > <bento-link v-if="isLinkTitle" v-bind="linkTitleProps" disable-typography>{{ linkTitleProps.title }}</bento-link> <span v-else>{{ title }}</span> </bento-typography> <bento-typography v-if="isNoteType && (description || hasSlot('default'))" el="div" variant="body" class="b-timeline-item__description" > <slot>{{ description }}</slot> </bento-typography> <bento-typography v-if="timestamp" class="b-timeline-item__timestamp" el="div" variant="caption"> <bento-date :date="timestamp.date" :format="timestamp.format" disable-typography /> <bento-dashed-underline v-if="timestamp.description" v-bind="timestamp.description"> {{ timestamp.value }} </bento-dashed-underline> <span v-else>{{ timestamp.value }}</span> </bento-typography> <bento-typography v-if="!isNoteType && additionalTimestamp" class="b-timeline-item__timestamp" el="div" variant="caption" > <bento-date :date="additionalTimestamp.date" :format="additionalTimestamp.format" disable-typography /> <bento-dashed-underline v-if="additionalTimestamp.description" v-bind="additionalTimestamp.description" > {{ additionalTimestamp.value }} </bento-dashed-underline> <span v-else> {{ additionalTimestamp.value }} </span> </bento-typography> <bento-typography v-if="!isNoteType && (description || hasSlot('default'))" el="div" variant="body" class="b-timeline-item__description" > <slot>{{ description }}</slot> </bento-typography> <div v-if="!isNoteType && dataList" class="b-timeline-item__data-list"> <bento-structured-list> <bento-structured-list-item v-for="listEntry in dataList" :key="generateUid(listEntry.label)" :label="listEntry.label" > <bento-typography variant="caption">{{ listEntry.value }}</bento-typography> </bento-structured-list-item> </bento-structured-list> </div> <bento-tag v-if="!isNoteType && tag" v-bind="tag" class="b-timeline-item__tag" /> </div> </div> <div v-if="displayShowMoreButton" class="b-timeline-item__row"> <div class="b-timeline-item__marker" aria-hidden="true"> <div class="b-timeline-item__separator"></div> </div> <div class="b-timeline-item__content"> <bento-button class="b-timeline__show-more" variant="tertiary" @click="showAll = !showAll"> <template v-if="!showAll">{{ t('showMore', { items: hiddenItems }) }}</template> <template v-else>{{ t('showLess') }}</template> </bento-button> </div> </div> </li> </template> <script setup lang="ts"> import { computed, defineComponent, inject, onUnmounted, provide, useSlots } from 'vue'; // Components import { BentoButton } from '@/components/button'; import { BentoDashedUnderline } from '@/components/dashed-underline'; import { BentoDate } from '@/components/date'; import { BentoLink } from '@/components/link'; import { BentoTag } from '@/components/tag'; import { BentoTypography } from '@/components/typography'; import { BentoStructuredList, BentoStructuredListItem } from '@/components/structured-list'; import SquareSmallIcon from '@adyen/ui-assets-icons-16/vue/square-small-fill'; // Utils import { generateUid } from '@/utils/ts/generate-uid'; import { useI18n } from '@/utils/ts/i18n'; import { useHasSlot } from '@/composables'; import { formatDistanceStrict } from 'date-fns/formatDistanceStrict'; // Types import { type BentoTimelineConfigData, type BentoTimelineEntry, type BentoTimelineItemProps, type BentoTimelineItemTitle, } from '../../timeline.types'; import { type BentoTimelineRegisterEntry } from '../../timeline.internal.types'; // Keys import { TIMELINE_CONFIG_INJECTION_KEY, TIMELINE_REGISTER_ENTRY_INJECTION_KEY } from '../../timeline.keys'; import { STRUCTURED_LIST_CONDENSED_INJECTION_KEY } from '@/components/structured-list/structured-list.keys'; import messages from './messages.json'; type MessageSchema = (typeof messages)['en-US']; const props = withDefaults(defineProps<BentoTimelineItemProps>(), { additionalTimestamp: null, dataList: null, description: null, status: 'black', tag: undefined, title: null, type: 'milestone', }); const { t } = useI18n<{ message: MessageSchema }>({ messages }); const slots = useSlots(); const hasSlot = useHasSlot(slots); const isNoteType = computed(() => props.type === 'note'); const iconConditionalClasses = computed(() => ({ [`b-timeline-item__icon--${props.status}`]: props.status && !isNoteType.value, })); const conditionalClasses = computed(() => ({ 'b-timeline-item--note': isNoteType.value, })); // Provide STRUCTURED_LIST_CONDENSED_INJECTION_KEY as true, to set the internal condensed style in the BentoStructuredList comp provide(STRUCTURED_LIST_CONDENSED_INJECTION_KEY, true); const entry: BentoTimelineEntry = { title: props.title, date: props.timestamp.date, }; // Register timeline entry to the timeline entries array const registerTimelineEntry: BentoTimelineRegisterEntry = inject(TIMELINE_REGISTER_ENTRY_INJECTION_KEY); const { timelineEntries, unregisterTimelineEntry } = registerTimelineEntry(entry); // On unmounting timeline item, unregister it from the parent timeline component onUnmounted(() => unregisterTimelineEntry()); // Get timeline config data from the parent Timeline comp const { showAll, showMoreIndex, hiddenItems, visibleIndexes, timegapLimit }: BentoTimelineConfigData = inject(TIMELINE_CONFIG_INJECTION_KEY); const index = computed(() => timelineEntries.value.indexOf(entry)); const allItemsShowing = computed(() => showAll.value || !visibleIndexes.value); const isVisible = computed(() => (allItemsShowing.value ? true : visibleIndexes.value.includes(index.value))); const isLinkTitle = computed(() => typeof props.title === 'object'); const linkTitleProps = computed(() => props.title as BentoTimelineItemTitle); const timegap = computed(() => { if (!timegapLimit.value) { return null; } const previousDate = timelineEntries.value[index.value - 1]?.date; const isPreviousItemVisible = allItemsShowing.value || visibleIndexes.value.includes(index.value - 1); if (!previousDate || !isPreviousItemVisible) { return null; } const { date } = timelineEntries.value[index.value]; const timegapInSelectedUnit = parseInt( formatDistanceStrict(previousDate, date, { unit: timegapLimit.value.unit }), 10 ); return timegapLimit.value.amount < timegapInSelectedUnit ? formatDistanceStrict(previousDate, date, { roundingMethod: 'floor' }) : null; }); const timegapAriaLabel = computed(() => (timegap.value ? t('timegap', { timegap: timegap.value }) : '')); const displayShowMoreButton = computed(() => hiddenItems.value && showMoreIndex.value === index.value); const isLastItem = computed(() => { // The index can be -1 before the parent has had a chance to sort the array. return index.value !== -1 && index.value === timelineEntries.value.length - 1; }); </script> <script lang="ts"> /** * BentoTimelineItem renders each event in the Timeline component * * @example * import { BentoDateFormat, BentoTimelineItem } from '@adyen/bento-vue2'; * * export default { * components: { BentoTimelineItem }, * template: ` * <bento-timeline-item * title="Milestone" * :timestamp="{ date: new Date(), value: 'Created at', format: BentoDateFormat.FULL_DATE_EXACT_TIME_WITHOUT_PERIOD }" * :additional-timestamp="{ date: new Date(), value: 'Modified at' }" * description="Milestone description" * :data-list="[ * { label: 'Label', value: 'Value' }, * { label: 'Label', value: 'Value' }, * ]" * :tag="{ label: 'Tag'}" * /> * ` * } */ export default defineComponent({ name: 'bento-timeline-item', i18n: { messages }, }); </script> <style lang="scss" scoped src="./timeline-item.scss" />
|
|
@@ -21,16 +21,6 @@ separate components.
|
|
|
21
21
|
|
|
22
22
|
## Variations
|
|
23
23
|
|
|
24
|
-
### Variant (deprecated)
|
|
25
|
-
|
|
26
|
-
> ⚠️ **Deprecation Notice**: Timeline item variants are deprecated and will be removed in release **2.0.0**. Use
|
|
27
|
-
> [`status` modifier](#status) instead.
|
|
28
|
-
|
|
29
|
-
There are three variants for the Timeline items: `Default`, `Success` and `Critical`.
|
|
30
|
-
|
|
31
|
-
Use `Success` and `Critical` item variants sparingly - only when it's necessary to grab user's attention, for example,
|
|
32
|
-
when the timeline is used for troubleshooting. Otherwise stick to the default styling.
|
|
33
|
-
|
|
34
24
|
### Links
|
|
35
25
|
|
|
36
26
|
It’s possible to add links to either title or description of a timeline item. To make the timeline item title render as
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { type Ref } from 'vue'; import { type BentoTimelineEntry, type BentoTimelineItemTitle } from './timeline.types'; /** * @deprecated Since v3.0.0. Do not use. */ export type BentoTimelineRegisterEntry = (props: { title?: string | BentoTimelineItemTitle; description?: string; date: Date | number; }) => { timelineEntries: Ref<Array<BentoTimelineEntry>>; index: number; unregisterTimelineEntry: () => void }; export type BentoInternalTimelineRegisterEntry = ( entry: BentoTimelineEntry ) => { timelineEntries: Ref<Array<BentoTimelineEntry>>; unregisterTimelineEntry: () => void };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from '@storybook/vue'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { onMounted, ref } from 'vue'; import { isVue2 } from 'vue-demi'; import BentoTimeline from './timeline.vue'; import BentoTimelineItem from './components/timeline-item/timeline-item.vue'; import { BentoTimelineGapUnit, BentoTimelineShowMorePlacement } from './timeline.types'; import TimelineDynamicAddition from './__tests__/timeline-dynamic-add-stub.vue?raw'; import TimelineDefaultExample from './__tests__/timeline-default-example.vue?raw'; import { BentoDateFormat } from '@/components/date/date.types'; import BentoLink from '@/components/link/link.vue'; import BentoButton from '@/components/button/button.vue'; const meta: Meta = { title: 'Timeline', component: BentoTimeline, argTypes: { // Slots default: { name: 'default', type: { name: 'string', required: false }, description: 'BentoTimelineItem components', control: 'text', table: { type: { summary: 'VNode[]' }, }, }, }, }; export default meta; type Story = StoryObj<typeof BentoTimeline>; const DEFAULT_PROPS = { timegapLimit: { unit: BentoTimelineGapUnit.DAY, amount: 15 }, showMore: { limit: 3, placement: BentoTimelineShowMorePlacement.AFTER_FIRST }, }; const DEFAULT_ITEM_PROPS = { timestamp: { date: new Date(), value: 'AuthorName', format: BentoDateFormat.FULL_DATE_EXACT_TIME_WITHOUT_PERIOD, }, dataList: [ { label: 'Label', value: 'Value' }, { label: 'Label', value: 'Value' }, ], description: 'Space to put some description about this milestone', }; const { description: _, ...DEFAULT_PROPS_WITHOUT_DESCRIPTION } = DEFAULT_ITEM_PROPS; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoTimeline, BentoTimelineItem }, props: Object.keys(argTypes), template: ` <bento-timeline v-bind="args"> <bento-timeline-item v-bind="milestone5" /> <bento-timeline-item v-bind="milestone4" /> <bento-timeline-item v-bind="milestone3" /> <bento-timeline-item v-bind="milestone2" /> <bento-timeline-item v-bind="milestone1" /> </bento-timeline> `, setup(props) { const milestone5 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 5', status: 'green', }; const milestone4 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 4', timestamp: { date: new Date().setDate(-2), value: 'AuthorName', }, }; const milestone3 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 3', timestamp: { date: new Date().setDate(-4), value: 'AuthorName', }, status: 'red', }; const milestone2 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 2', timestamp: { date: new Date().setDate(-14), value: 'BookingDate', description: { popoverText: 'date of booking', }, }, additionalTimestamp: { date: new Date().setDate(-15), value: 'ValueDate', description: { tooltipText: 'date of value', }, }, }; const milestone1 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 1', timestamp: { date: new Date().setDate(-150), value: 'AuthorName', }, tag: { label: 'Tag1', }, }; return { args: isVue2 ? props : _args, milestone1, milestone2, milestone3, milestone4, milestone5, }; }, }), args: { ...DEFAULT_PROPS, }, parameters: storybookDocsParameter(TimelineDefaultExample), }; export const WithLinks: Story = { render: (_args, { argTypes }) => ({ components: { BentoLink, BentoTimeline, BentoTimelineItem }, props: Object.keys(argTypes), template: ` <bento-timeline v-bind="args"> <bento-timeline-item v-bind="milestone3" /> <bento-timeline-item v-bind="milestone2"> A description with <bento-link to="/">a link</bento-link> </bento-timeline-item> <bento-timeline-item v-bind="milestone1" /> </bento-timeline> `, setup(props) { const milestone3 = { ...DEFAULT_ITEM_PROPS, title: { title: 'Milestone 3', to: '/', }, timestamp: { date: new Date().setDate(-4), value: 'AuthorName', }, status: 'green', }; const milestone2 = { ...DEFAULT_PROPS_WITHOUT_DESCRIPTION, title: 'Milestone 2', timestamp: { date: new Date().setDate(-14), value: 'AuthorName', }, additionalTimestamp: { date: new Date().setDate(-15), value: 'AuthorName', }, }; const milestone1 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 1', timestamp: { date: new Date().setDate(-150), value: 'AuthorName', }, tag: { label: 'Tag1', }, }; return { args: isVue2 ? props : _args, milestone1, milestone2, milestone3, }; }, }), args: { ...DEFAULT_PROPS, }, }; const withNotesCode = ` <template> <bento-timeline v-bind="args" @add-timeline-note="addNote"> <bento-timeline-item v-for="item in timelineItems" v-bind="item" /> </bento-timeline> </template> <script setup> const milestone3 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 2', timestamp: { date: new Date().setDate(-4), value: 'AuthorName', }, additionalTimestamp: { date: new Date().setDate(-5), value: 'AuthorName', }, }; const milestone2 = { type: 'note', description: 'Milestone Note. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', timestamp: { date: new Date().setDate(-14), value: 'AuthorName', }, }; const milestone1 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 1', timestamp: { date: new Date().setDate(-150), value: 'AuthorName', }, tag: { label: 'Tag1', }, }; const timelineItems = ref(); onMounted(() => { timelineItems.value = [milestone3, milestone2, milestone1]; }); const addNote = e => { timelineItems.value.unshift(e); }; return { args: isVue2 ? props : _args, timelineItems, addNote, }; </script> `; export const WithNotes: Story = { render: (_args, { argTypes }) => ({ components: { BentoTimeline, BentoTimelineItem }, props: Object.keys(argTypes), template: ` <bento-timeline v-bind="args"> <bento-timeline-item v-bind="milestone3" /> <bento-timeline-item v-bind="milestone2" /> <bento-timeline-item v-bind="milestone1" /> </bento-timeline> `, setup(props) { const milestone3 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 2', timestamp: { date: new Date().setDate(-4), value: 'AuthorName', }, additionalTimestamp: { date: new Date().setDate(-5), value: 'AuthorName', }, }; const milestone2 = { type: 'note', description: 'Milestone Note. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', timestamp: { date: new Date().setDate(-14), value: 'AuthorName', }, }; const milestone1 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 1', timestamp: { date: new Date().setDate(-150), value: 'AuthorName', }, tag: { label: 'Tag1', }, }; return { args: isVue2 ? props : _args, milestone1, milestone2, milestone3, }; }, }), args: { ...DEFAULT_PROPS, showMore: null, }, parameters: storybookDocsParameter(withNotesCode), }; const addNoteCode = ` <template> <bento-timeline v-bind="args" add-note @add-timeline-note="addNote"> <bento-timeline-item v-for="item in timelineItems" v-bind="item" /> </bento-timeline> </template> <script setup> const milestone3 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 2', timestamp: { date: new Date().setDate(-4), value: 'AuthorName', }, additionalTimestamp: { date: new Date().setDate(-5), value: 'AuthorName', }, }; const milestone2 = { type: 'note', description: 'Milestone Note. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', timestamp: { date: new Date().setDate(-14), value: 'AuthorName', }, }; const milestone1 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 1', timestamp: { date: new Date().setDate(-150), value: 'AuthorName', }, tag: { label: 'Tag1', }, }; const timelineItems = ref(); onMounted(() => { timelineItems.value = [milestone3, milestone2, milestone1]; }); const addNote = e => { timelineItems.value.unshift(e); }; return { timelineItems, addNote, }; </script> `; export const AddNote: Story = { render: (_args, { argTypes }) => ({ components: { BentoTimeline, BentoTimelineItem }, props: Object.keys(argTypes), template: ` <bento-timeline v-bind="args" add-note @add-timeline-note="addNote"> <bento-timeline-item v-for="item in timelineItems" v-bind="item" /> </bento-timeline> `, setup(props) { const milestone3 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 2', timestamp: { date: new Date().setDate(-4), value: 'AuthorName', }, additionalTimestamp: { date: new Date().setDate(-5), value: 'AuthorName', }, }; const milestone2 = { type: 'note', description: 'Milestone Note. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', timestamp: { date: new Date().setDate(-14), value: 'AuthorName', }, }; const milestone1 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 1', timestamp: { date: new Date().setDate(-150), value: 'AuthorName', }, tag: { label: 'Tag1', }, }; const timelineItems = ref(); onMounted(() => { timelineItems.value = [milestone3, milestone2, milestone1]; }); const addNote = e => { const newNote = { ...e, timestamp: { ...e.timestamp, value: 'AuthorName' } }; timelineItems.value.unshift(newNote); }; return { args: isVue2 ? props : _args, timelineItems, addNote, }; }, }), args: { ...DEFAULT_PROPS, showMore: null, addNotes: true, }, parameters: storybookDocsParameter(addNoteCode), }; export const DynamicItemAddition: Story = { render: (_args, { argTypes }) => ({ components: { BentoButton, BentoTimeline, BentoTimelineItem }, props: Object.keys(argTypes), template: ` <div style="display: flex; flex-direction: row; align-items: flex-start; gap: 16px;"> <bento-button @click="addItem">Add timeline item</bento-button> <bento-timeline v-if="timelineEntries"> <bento-timeline-item v-for="entry in timelineEntries" :key="entry.title" v-bind="entry" /> </bento-timeline> </div> `, setup(props) { const timelineEntries = ref([]); let index = 0; const addItem = () => { if (index < 5) { timelineEntries.value.unshift(milestones[index]); index++; } }; const milestone5 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 5', status: 'green', }; const milestone4 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 4', timestamp: { date: new Date().setDate(-2), value: 'AuthorName', }, }; const milestone3 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 3', timestamp: { date: new Date().setDate(-4), value: 'AuthorName', }, status: 'red', }; const milestone2 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 2', timestamp: { date: new Date().setDate(-14), value: 'AuthorName', }, additionalTimestamp: { date: new Date().setDate(-15), value: 'AuthorName', }, }; const milestone1 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 1', timestamp: { date: new Date().setDate(-150), value: 'AuthorName', }, tag: { label: 'Tag1', }, }; const milestones = [milestone1, milestone2, milestone3, milestone4, milestone5]; return { args: isVue2 ? props : _args, timelineEntries, addItem, }; }, }), args: { ...DEFAULT_PROPS, }, parameters: storybookDocsParameter(TimelineDynamicAddition), };
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { onMounted, ref } from 'vue'; import { isVue2 } from 'vue-demi'; import BentoTimeline from './timeline.vue'; import BentoTimelineItem from './components/timeline-item/timeline-item.vue'; import { BentoTimelineGapUnit, BentoTimelineShowMorePlacement } from './timeline.types'; const TimelineAddNoteExample = "<template>\n <bento-timeline add-note :timegap-limit=\"timegapLimit\" @add-timeline-note=\"addNote\">\n <bento-timeline-item v-for=\"item in timelineItems\" :key=\"item.timestamp.date.valueOf()\" v-bind=\"item\" />\n </bento-timeline>\n</template>\n\n<script setup lang=\"ts\">\n import { ref } from 'vue';\n import {\n BentoTimeline,\n BentoTimelineGapUnit,\n BentoTimelineItem,\n type BentoTimelineItemProps,\n } from '@adyen/bento-vue2';\n\n const timegapLimit = { unit: BentoTimelineGapUnit.DAY, amount: 15 };\n const defaultItemProps = {\n timestamp: {\n date: new Date('2024-03-15T10:00:00Z'),\n value: 'AuthorName',\n },\n dataList: [\n { label: 'Label', value: 'Value' },\n { label: 'Label', value: 'Value' },\n ],\n description: 'Space to put some description about this milestone',\n };\n\n const timelineItems = ref<Array<BentoTimelineItemProps>>([\n {\n ...defaultItemProps,\n title: 'Milestone 2',\n timestamp: {\n date: new Date('2024-03-11T10:00:00Z'),\n value: 'AuthorName',\n },\n additionalTimestamp: {\n date: new Date('2024-03-10T10:00:00Z'),\n value: 'AuthorName',\n },\n },\n {\n type: 'note',\n description:\n 'Milestone Note. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',\n timestamp: {\n date: new Date('2024-03-01T10:00:00Z'),\n value: 'AuthorName',\n },\n },\n {\n ...defaultItemProps,\n title: 'Milestone 1',\n timestamp: {\n date: new Date('2023-10-17T10:00:00Z'),\n value: 'AuthorName',\n },\n tag: {\n label: 'Tag1',\n },\n },\n ]);\n\n const addNote = (note: BentoTimelineItemProps) => {\n timelineItems.value.unshift({\n ...note,\n timestamp: {\n ...note.timestamp,\n date: new Date('2024-03-16T10:00:00Z'),\n value: 'AuthorName',\n },\n });\n };\n</script>\n"; const TimelineDynamicAddition = "<template>\n <div>\n <bento-timeline>\n <bento-timeline-item v-for=\"event in timelineEntries\" :key=\"event.title\" v-bind=\"event\" />\n </bento-timeline>\n <bento-button @click=\"addEntry\">Add timeline item</bento-button>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { ref } from 'vue';\n import BentoTimeline from '../timeline.vue';\n import BentoTimelineItem from '../components/timeline-item/timeline-item.vue';\n import { BentoButton } from '@/components/button';\n\n const timelineEntries = ref([]);\n const milestones = [\n { title: 'Milestone 1', timestamp: { date: new Date(2023, 0, 1) } },\n { title: 'Milestone 2', timestamp: { date: new Date(2023, 1, 1) } },\n { title: 'Milestone 3', timestamp: { date: new Date(2023, 2, 1) } },\n ];\n let milestoneIndex = 0;\n\n const addEntry = () => {\n if (milestoneIndex < milestones.length) {\n timelineEntries.value.unshift(milestones[milestoneIndex]);\n milestoneIndex++;\n }\n };\n</script>\n"; const TimelineDefaultExample = "<template>\n <bento-timeline v-bind=\"timelineProps\">\n <bento-timeline-item v-bind=\"milestone5\" />\n <bento-timeline-item v-bind=\"milestone4\" />\n <bento-timeline-item v-bind=\"milestone3\" />\n <bento-timeline-item v-bind=\"milestone2\" />\n <bento-timeline-item v-bind=\"milestone1\" />\n </bento-timeline>\n</template>\n\n<script lang=\"ts\" setup>\n import {\n BentoTimeline,\n BentoTimelineGapUnit,\n BentoTimelineItem,\n BentoTimelineShowMorePlacement,\n } from '@adyen/bento-vue2';\n\n const timelineProps = {\n timegapLimit: { unit: BentoTimelineGapUnit.DAY, amount: 15 },\n showMore: { limit: 3, placement: BentoTimelineShowMorePlacement.AFTER_FIRST },\n };\n\n const DEFAULT_ITEM_PROPS = {\n timestamp: {\n date: new Date('2024-03-15T10:00:00Z'),\n value: 'AuthorName',\n },\n dataList: [\n { label: 'Label', value: 'Value' },\n { label: 'Label', value: 'Value' },\n ],\n description: 'Space to put some description about this milestone',\n };\n\n const milestone5 = {\n ...DEFAULT_ITEM_PROPS,\n title: 'Milestone 5',\n status: 'green',\n };\n\n const milestone4 = {\n ...DEFAULT_ITEM_PROPS,\n title: 'Milestone 4',\n timestamp: {\n ...DEFAULT_ITEM_PROPS.timestamp,\n date: new Date('2024-03-13T10:00:00Z'),\n value: 'AuthorName',\n },\n };\n\n const milestone3 = {\n ...DEFAULT_ITEM_PROPS,\n title: 'Milestone 3',\n timestamp: {\n ...DEFAULT_ITEM_PROPS.timestamp,\n date: new Date('2024-03-11T10:00:00Z'),\n value: 'AuthorName',\n },\n status: 'red',\n };\n\n const milestone2 = {\n ...DEFAULT_ITEM_PROPS,\n title: 'Milestone 2',\n timestamp: {\n ...DEFAULT_ITEM_PROPS.timestamp,\n date: new Date('2024-03-01T10:00:00Z'),\n value: 'BookingDate',\n description: {\n popoverText: 'date of booking',\n },\n },\n additionalTimestamp: {\n date: new Date('2024-02-29T10:00:00Z'),\n value: 'ValueDate',\n description: {\n tooltipText: 'date of value',\n },\n },\n };\n\n const milestone1 = {\n ...DEFAULT_ITEM_PROPS,\n title: 'Milestone 1',\n timestamp: {\n ...DEFAULT_ITEM_PROPS.timestamp,\n date: new Date('2023-10-17T10:00:00Z'),\n value: 'AuthorName',\n },\n tag: {\n label: 'Tag1',\n },\n };\n</script>\n"; const TimelineWithNotesExample = "<template>\n <bento-timeline :timegap-limit=\"timegapLimit\">\n <bento-timeline-item v-for=\"item in timelineItems\" :key=\"item.timestamp.date.valueOf()\" v-bind=\"item\" />\n </bento-timeline>\n</template>\n\n<script setup lang=\"ts\">\n import {\n BentoTimeline,\n BentoTimelineGapUnit,\n BentoTimelineItem,\n type BentoTimelineItemProps,\n } from '@adyen/bento-vue2';\n\n const timegapLimit = { unit: BentoTimelineGapUnit.DAY, amount: 15 };\n const defaultItemProps = {\n timestamp: {\n date: new Date('2024-03-15T10:00:00Z'),\n value: 'AuthorName',\n },\n dataList: [\n { label: 'Label', value: 'Value' },\n { label: 'Label', value: 'Value' },\n ],\n description: 'Space to put some description about this milestone',\n };\n\n const timelineItems: Array<BentoTimelineItemProps> = [\n {\n ...defaultItemProps,\n title: 'Milestone 2',\n timestamp: {\n date: new Date('2024-03-11T10:00:00Z'),\n value: 'AuthorName',\n },\n additionalTimestamp: {\n date: new Date('2024-03-10T10:00:00Z'),\n value: 'AuthorName',\n },\n },\n {\n type: 'note',\n description:\n 'Milestone Note. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',\n timestamp: {\n date: new Date('2024-03-01T10:00:00Z'),\n value: 'AuthorName',\n },\n },\n {\n ...defaultItemProps,\n title: 'Milestone 1',\n timestamp: {\n date: new Date('2023-10-17T10:00:00Z'),\n value: 'AuthorName',\n },\n tag: {\n label: 'Tag1',\n },\n },\n ];\n</script>\n"; import { BentoDateFormat } from '@/components/date/date.types'; import BentoLink from '@/components/link/link.vue'; import BentoButton from '@/components/button/button.vue'; const meta: Meta = { title: 'Timeline', component: BentoTimeline, argTypes: { // Slots default: { name: 'default', type: { name: 'string', required: false }, description: 'BentoTimelineItem components', control: 'text', table: { type: { summary: 'VNode[]' }, }, }, }, }; export default meta; type Story = StoryObj<typeof BentoTimeline>; const DEFAULT_PROPS = { timegapLimit: { unit: BentoTimelineGapUnit.DAY, amount: 15 }, showMore: { limit: 3, placement: BentoTimelineShowMorePlacement.AFTER_FIRST }, }; const DEFAULT_ITEM_PROPS = { timestamp: { date: new Date(), value: 'AuthorName', format: BentoDateFormat.FULL_DATE_EXACT_TIME_WITHOUT_PERIOD, }, dataList: [ { label: 'Label', value: 'Value' }, { label: 'Label', value: 'Value' }, ], description: 'Space to put some description about this milestone', }; const { description: _, ...DEFAULT_PROPS_WITHOUT_DESCRIPTION } = DEFAULT_ITEM_PROPS; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoTimeline, BentoTimelineItem }, props: Object.keys(argTypes), template: ` <bento-timeline v-bind="args"> <bento-timeline-item v-bind="milestone5" /> <bento-timeline-item v-bind="milestone4" /> <bento-timeline-item v-bind="milestone3" /> <bento-timeline-item v-bind="milestone2" /> <bento-timeline-item v-bind="milestone1" /> </bento-timeline> `, setup(props) { const milestone5 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 5', status: 'green', }; const milestone4 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 4', timestamp: { date: new Date().setDate(-2), value: 'AuthorName', }, }; const milestone3 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 3', timestamp: { date: new Date().setDate(-4), value: 'AuthorName', }, status: 'red', }; const milestone2 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 2', timestamp: { date: new Date().setDate(-14), value: 'BookingDate', description: { popoverText: 'date of booking', }, }, additionalTimestamp: { date: new Date().setDate(-15), value: 'ValueDate', description: { tooltipText: 'date of value', }, }, }; const milestone1 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 1', timestamp: { date: new Date().setDate(-150), value: 'AuthorName', }, tag: { label: 'Tag1', }, }; return { args: isVue2 ? props : _args, milestone1, milestone2, milestone3, milestone4, milestone5, }; }, }), args: { ...DEFAULT_PROPS, }, parameters: storybookDocsParameter(TimelineDefaultExample), }; export const WithLinks: Story = { render: (_args, { argTypes }) => ({ components: { BentoLink, BentoTimeline, BentoTimelineItem }, props: Object.keys(argTypes), template: ` <bento-timeline v-bind="args"> <bento-timeline-item v-bind="milestone3" /> <bento-timeline-item v-bind="milestone2"> A description with <bento-link to="/">a link</bento-link> </bento-timeline-item> <bento-timeline-item v-bind="milestone1" /> </bento-timeline> `, setup(props) { const milestone3 = { ...DEFAULT_ITEM_PROPS, title: { title: 'Milestone 3', to: '/', }, timestamp: { date: new Date().setDate(-4), value: 'AuthorName', }, status: 'green', }; const milestone2 = { ...DEFAULT_PROPS_WITHOUT_DESCRIPTION, title: 'Milestone 2', timestamp: { date: new Date().setDate(-14), value: 'AuthorName', }, additionalTimestamp: { date: new Date().setDate(-15), value: 'AuthorName', }, }; const milestone1 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 1', timestamp: { date: new Date().setDate(-150), value: 'AuthorName', }, tag: { label: 'Tag1', }, }; return { args: isVue2 ? props : _args, milestone1, milestone2, milestone3, }; }, }), args: { ...DEFAULT_PROPS, }, }; export const WithNotes: Story = { render: (_args, { argTypes }) => ({ components: { BentoTimeline, BentoTimelineItem }, props: Object.keys(argTypes), template: ` <bento-timeline v-bind="args"> <bento-timeline-item v-bind="milestone3" /> <bento-timeline-item v-bind="milestone2" /> <bento-timeline-item v-bind="milestone1" /> </bento-timeline> `, setup(props) { const milestone3 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 2', timestamp: { date: new Date().setDate(-4), value: 'AuthorName', }, additionalTimestamp: { date: new Date().setDate(-5), value: 'AuthorName', }, }; const milestone2 = { type: 'note', description: 'Milestone Note. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', timestamp: { date: new Date().setDate(-14), value: 'AuthorName', }, }; const milestone1 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 1', timestamp: { date: new Date().setDate(-150), value: 'AuthorName', }, tag: { label: 'Tag1', }, }; return { args: isVue2 ? props : _args, milestone1, milestone2, milestone3, }; }, }), args: { ...DEFAULT_PROPS, showMore: null, }, parameters: storybookDocsParameter(TimelineWithNotesExample), }; export const AddNote: Story = { render: (_args, { argTypes }) => ({ components: { BentoTimeline, BentoTimelineItem }, props: Object.keys(argTypes), template: ` <bento-timeline v-bind="args" add-note @add-timeline-note="addNote"> <bento-timeline-item v-for="item in timelineItems" v-bind="item" /> </bento-timeline> `, setup(props) { const milestone3 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 2', timestamp: { date: new Date().setDate(-4), value: 'AuthorName', }, additionalTimestamp: { date: new Date().setDate(-5), value: 'AuthorName', }, }; const milestone2 = { type: 'note', description: 'Milestone Note. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', timestamp: { date: new Date().setDate(-14), value: 'AuthorName', }, }; const milestone1 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 1', timestamp: { date: new Date().setDate(-150), value: 'AuthorName', }, tag: { label: 'Tag1', }, }; const timelineItems = ref(); onMounted(() => { timelineItems.value = [milestone3, milestone2, milestone1]; }); const addNote = e => { const newNote = { ...e, timestamp: { ...e.timestamp, value: 'AuthorName' } }; timelineItems.value.unshift(newNote); }; return { args: isVue2 ? props : _args, timelineItems, addNote, }; }, }), args: { ...DEFAULT_PROPS, showMore: null, addNote: true, }, parameters: storybookDocsParameter(TimelineAddNoteExample), }; export const DynamicItemAddition: Story = { render: (_args, { argTypes }) => ({ components: { BentoButton, BentoTimeline, BentoTimelineItem }, props: Object.keys(argTypes), template: ` <div style="display: flex; flex-direction: row; align-items: flex-start; gap: 16px;"> <bento-button @click="addItem">Add timeline item</bento-button> <bento-timeline v-if="timelineEntries"> <bento-timeline-item v-for="entry in timelineEntries" :key="entry.title" v-bind="entry" /> </bento-timeline> </div> `, setup(props) { const timelineEntries = ref([]); let index = 0; const addItem = () => { if (index < 5) { timelineEntries.value.unshift(milestones[index]); index++; } }; const milestone5 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 5', status: 'green', }; const milestone4 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 4', timestamp: { date: new Date().setDate(-2), value: 'AuthorName', }, }; const milestone3 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 3', timestamp: { date: new Date().setDate(-4), value: 'AuthorName', }, status: 'red', }; const milestone2 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 2', timestamp: { date: new Date().setDate(-14), value: 'AuthorName', }, additionalTimestamp: { date: new Date().setDate(-15), value: 'AuthorName', }, }; const milestone1 = { ...DEFAULT_ITEM_PROPS, title: 'Milestone 1', timestamp: { date: new Date().setDate(-150), value: 'AuthorName', }, tag: { label: 'Tag1', }, }; const milestones = [milestone1, milestone2, milestone3, milestone4, milestone5]; return { args: isVue2 ? props : _args, timelineEntries, addItem, }; }, }), args: { ...DEFAULT_PROPS, }, parameters: storybookDocsParameter(TimelineDynamicAddition), };
|