@adyen/bento-mcp 0.10.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 +36 -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 +17 -7
- 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.docs.mdx +102 -0
- package/dist/assets/components/progress-bar/progress-bar.stories.ts +1 -0
- package/dist/assets/components/progress-bar/progress-bar.types.ts +1 -0
- package/dist/assets/components/progress-bar/progress-bar.vue +1 -0
- 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/components.json +1 -0
- 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 +139 -243
- 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/index.ts +1 -1
- 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 +81 -80
- package/dist/assets/variables.css +152 -140
- 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/calendar/composables/use-range-pane.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 { BentoButtonActions } from '@/components/button'; import { BentoHeaderVariant } from './header.types'; import { reactive, ref, watch } from 'vue'; import { isVue2 } from 'vue-demi'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { BentoLink } from '@/components/link'; import type { Meta, StoryObj } from '@storybook/vue'; import BentoHeader from './header.vue'; import PlusIcon from '@adyen/ui-assets-icons-16/vue/plus'; const meta: Meta = { title: 'Header', component: BentoHeader, argTypes: { variant: { options: Object.values(BentoHeaderVariant), mapping: BentoHeaderVariant, defaultValue: BentoHeaderVariant.PAGE, control: { type: 'radio', }, }, lastUpdated: { control: { type: 'date', }, }, withReloadButton: { control: { type: 'boolean', }, }, unsetDate: { table: { category: 'Storybook controls', }, description: 'Control to hide date, because date control cannot be unset in Storybook. Not a prop.', control: { type: 'boolean', }, }, // Slots description: { control: { disable: true }, description: 'Slot for the description, displayed below the title. Use this slot for rich content like links or formatted text.', table: { type: { summary: 'VNode[]' }, }, }, }, }; const HEADER_DESCRIPTION = "This page shows how Adyen's AI boosts your payment performance and revenue. Monitor your authorization rate uplift and discover data-driven opportunities for further optimization."; const DEFAULT_HEADER_PROPS = { title: 'Title', variant: 'page', }; const EVERYTHING_HEADER_PROPS = { ...DEFAULT_HEADER_PROPS, description: HEADER_DESCRIPTION, actions: [ { title: 'Primary action', event: () => undefined }, { title: 'Secondary action', icon: PlusIcon, event: () => undefined }, { title: 'Tertiary action', icon: PlusIcon, event: () => undefined }, { title: 'Another action', event: () => undefined }, ], lastUpdated: new Date(), infoIconTooltipText: 'Title Tooltip Text', }; export default meta; type Story = StoryObj<typeof BentoHeader>; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoHeader, BentoButtonActions }, props: Object.keys(argTypes), setup(props) { const date = ref(null); const reloadButtonText = ref('Reload'); const reloadButtonState = ref('start'); const reloadButton = reactive({ title: reloadButtonText, state: reloadButtonState, event: async () => { if (reloadButtonState.value === 'start') { reloadButtonText.value = 'Loading'; reloadButtonState.value = 'loading'; setTimeout(() => { reloadButtonText.value = 'Reload'; reloadButtonState.value = 'start'; }, 2000); } }, }); watch( () => props.unsetDate, (val: boolean) => { date.value = val ? null : props.lastUpdated; }, { immediate: true } ); watch( () => props.lastUpdated, val => { date.value = props.unsetDate !== true && val; }, { immediate: true } ); return { args: isVue2 ? props : _args, date, reloadButton, }; }, template: ` <bento-header v-bind="args" :reloadButton="reloadButton" :last-updated="date" /> `, }), args: DEFAULT_HEADER_PROPS, argTypes: { icon: { table: { disable: true } } }, };
|
|
1
|
+
import { BentoButtonActions } from '@/components/button'; import { BentoHeaderVariant } from './header.types'; import { reactive, ref, watch } from 'vue'; import { isVue2 } from 'vue-demi'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { BentoLink } from '@/components/link'; import type { Meta, StoryObj } from '@storybook/vue'; import BentoHeader from './header.vue'; import PlusIcon from '@adyen/ui-assets-icons-16/vue/plus'; const HeaderEverythingBagel = "<template>\n <bento-header\n :actions=\"actions\"\n :description=\"description\"\n info-icon-tooltip-text=\"Title Tooltip Text\"\n :last-updated=\"lastUpdated\"\n :reload-button=\"reloadButton\"\n title=\"Title\"\n />\n</template>\n\n<script setup lang=\"ts\">\n import { type BentoButtonActionsList, BentoHeader, type BentoHeaderReloadButton } from '@adyen/bento-vue2';\n import PlusIcon from '@adyen/ui-assets-icons-16/vue/plus';\n\n const actions: BentoButtonActionsList = [\n { title: 'Primary action', event: () => undefined },\n { title: 'Secondary action', icon: PlusIcon, event: () => undefined },\n { title: 'Tertiary action', icon: PlusIcon, event: () => undefined },\n { title: 'Another action', event: () => undefined },\n ];\n const description =\n \"This page shows how Adyen's AI boosts your payment performance and revenue. Monitor your authorization rate uplift and discover data-driven opportunities for further optimization.\";\n const lastUpdated = new Date();\n const reloadButton: BentoHeaderReloadButton = {\n title: 'Reload',\n state: 'start',\n event: async () => {\n // Add your loading button logic here.\n },\n };\n</script>\n"; const HeaderWithInfoIconSlot = "<template>\n <bento-header :last-updated=\"lastUpdated\" title=\"Title\">\n <template #infoIconTooltipContent> Description and/or <bento-link to=\"/\">link</bento-link> </template>\n </bento-header>\n</template>\n\n<script setup lang=\"ts\">\n import { BentoHeader, BentoLink } from '@adyen/bento-vue2';\n\n const lastUpdated = new Date();\n</script>\n"; const meta: Meta = { title: 'Header', component: BentoHeader, argTypes: { variant: { options: Object.values(BentoHeaderVariant), mapping: BentoHeaderVariant, defaultValue: BentoHeaderVariant.PAGE, control: { type: 'radio', }, }, lastUpdated: { control: { type: 'date', }, }, withReloadButton: { control: { type: 'boolean', }, }, unsetDate: { table: { category: 'Storybook controls', }, description: 'Control to hide date, because date control cannot be unset in Storybook. Not a prop.', control: { type: 'boolean', }, }, // Slots description: { control: { disable: true }, description: 'Slot for the description, displayed below the title. Use this slot for rich content like links or formatted text.', table: { type: { summary: 'VNode[]' }, }, }, }, }; const HEADER_DESCRIPTION = "This page shows how Adyen's AI boosts your payment performance and revenue. Monitor your authorization rate uplift and discover data-driven opportunities for further optimization."; const DEFAULT_HEADER_PROPS = { title: 'Title', variant: 'page', }; const EVERYTHING_HEADER_PROPS = { ...DEFAULT_HEADER_PROPS, description: HEADER_DESCRIPTION, actions: [ { title: 'Primary action', event: () => undefined }, { title: 'Secondary action', icon: PlusIcon, event: () => undefined }, { title: 'Tertiary action', icon: PlusIcon, event: () => undefined }, { title: 'Another action', event: () => undefined }, ], lastUpdated: new Date(), infoIconTooltipText: 'Title Tooltip Text', }; export default meta; type Story = StoryObj<typeof BentoHeader>; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoHeader, BentoButtonActions }, props: Object.keys(argTypes), setup(props) { const date = ref(null); const reloadButtonText = ref('Reload'); const reloadButtonState = ref('start'); const reloadButton = reactive({ title: reloadButtonText, state: reloadButtonState, event: async () => { if (reloadButtonState.value === 'start') { reloadButtonText.value = 'Loading'; reloadButtonState.value = 'loading'; setTimeout(() => { reloadButtonText.value = 'Reload'; reloadButtonState.value = 'start'; }, 2000); } }, }); watch( () => props.unsetDate, (val: boolean) => { date.value = val ? null : props.lastUpdated; }, { immediate: true } ); watch( () => props.lastUpdated, val => { date.value = props.unsetDate !== true && val; }, { immediate: true } ); return { args: isVue2 ? props : _args, date, reloadButton, }; }, template: ` <bento-header v-bind="args" :reloadButton="reloadButton" :last-updated="date" /> `, }), args: DEFAULT_HEADER_PROPS, argTypes: { icon: { table: { disable: true } } }, }; export const EverythingBagel: Story = { ...Default, args: EVERYTHING_HEADER_PROPS, parameters: storybookDocsParameter(HeaderEverythingBagel), }; export const WithInfoIconSlot: Story = { ...Default, render: (_args, { argTypes }) => ({ components: { BentoHeader, BentoButtonActions, BentoLink }, props: Object.keys(argTypes), setup(props) { const date = new Date(); return { args: isVue2 ? props : _args, date, }; }, template: ` <bento-header v-bind="args" :last-updated="date"> <template #infoIconTooltipContent> Description and/or <bento-link to="/">link</bento-link> </template> </bento-header> `, }), args: { ...Default.args, }, parameters: storybookDocsParameter(HeaderWithInfoIconSlot), };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type TitleDetailsReloadButton } from '../internal/title-details/title-details.types'; /** * @deprecated Since v3.0.0. Use string literal instead. */ export enum BentoHeaderVariant { PAGE = 'page', COMPONENT = 'component', } export type BentoHeaderReloadButton = TitleDetailsReloadButton;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<template> <header class="b-header" :class="conditionalClasses"> <div class="b-header__wrapper"> <div class="b-header__title"> <div> <bento-typography v-bind="typography" class="b-header__text">{{ title }}</bento-typography> <info-icon-with-popover v-if="infoIconTooltipText || hasSlot('infoIconTooltipContent')" class="b-header__tooltip" :popover-text="infoIconTooltipText" > <template v-if="hasSlot('infoIconTooltipContent')" #default ><slot name="infoIconTooltipContent" /></template> </info-icon-with-popover> </div> </div> <
|
|
1
|
+
<template> <header class="b-header" :class="conditionalClasses"> <div class="b-header__wrapper"> <div class="b-header__title"> <div> <bento-typography v-bind="typography" class="b-header__text">{{ title }}</bento-typography> <info-icon-with-popover v-if="infoIconTooltipText || hasSlot('infoIconTooltipContent')" class="b-header__tooltip" :popover-text="infoIconTooltipText" > <template v-if="hasSlot('infoIconTooltipContent')" #default ><slot name="infoIconTooltipContent" /></template> </info-icon-with-popover> </div> </div> <title-details v-if="description || hasSlot('description') || lastUpdated" class="b-header__meta-container" :description="description" :last-updated="lastUpdated" :reload-button="reloadButton" > <template v-if="hasSlot('description')" #description><slot name="description" /></template> </title-details> <button-actions-with-menu v-if="actions" :actions="actions" :displayed-actions="2" class="b-header__actions" /> </div> </header> </template> <script lang="ts"> import { computed, defineComponent, type PropType } from 'vue'; import { InfoIconWithPopover } from '@/internal'; import { type BentoButtonActionsList } from '@/components/button'; import { useHasSlot } from '@/composables'; import { type BentoHeaderReloadButton, BentoHeaderVariant } from './header.types'; import { BentoTypography, BentoTypographyElement, BentoTypographyVariant } from '@/components/typography'; import { TitleDetails } from '@/internal/title-details'; import ButtonActionsWithMenu from '../internal/button-actions-with-menu/button-actions-with-menu.vue'; export default defineComponent({ name: 'bento-header', components: { BentoTypography, ButtonActionsWithMenu, InfoIconWithPopover, TitleDetails, }, props: { /** * Array of BentoButtonActionsList to show to the right of the title */ actions: { type: Array as PropType<BentoButtonActionsList>, default: null, }, /** * Text showed under the title */ description: { type: String, default: null }, /** * Text showed in the info-icon tooltip on hover */ infoIconTooltipText: { type: InfoIconWithPopover.props.popoverText.type, default: null }, /** * Popover position of the info icon text. */ infoIconPopoverPosition: { type: InfoIconWithPopover.props.popoverPosition.type, default: InfoIconWithPopover.props.popoverPosition.default, validator: InfoIconWithPopover.props.popoverPosition.validator, }, /** * JavaScript "Date" object for when the content was last updated */ lastUpdated: { type: [Date, Number], default: null }, /** * Variant represents type of Header * @values page, component */ variant: { type: String as PropType<BentoHeaderVariant | `${BentoHeaderVariant}`>, default: BentoHeaderVariant.PAGE, validator: (value: BentoHeaderVariant) => Object.values(BentoHeaderVariant).includes(value), }, /** * Text used as a title */ title: { type: String, required: true }, /** * Reload button which shows next to the header's timestamp * {@see BentoHeaderReloadButton} */ reloadButton: { type: Object as PropType<BentoHeaderReloadButton>, default: null, }, }, emits: ['reload'], setup(props, { slots }) { const conditionalClasses = computed(() => ({ 'b-header--component': props.variant === BentoHeaderVariant.COMPONENT, })); const typography = computed(() => ({ el: props.variant === BentoHeaderVariant.PAGE ? BentoTypographyElement.H1 : BentoTypographyElement.H2, variant: BentoTypographyVariant.TITLE, medium: props.variant === BentoHeaderVariant.COMPONENT, large: props.variant === BentoHeaderVariant.PAGE, })); return { hasSlot: useHasSlot(slots), conditionalClasses, typography, }; }, }); </script> <style lang="scss" scoped src="./header.scss" />
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<template> <div ref="allViewsRef" class="b-header-with-views-all-views" data-testid="header-with-views-all-views" @keydown.esc.stop.prevent="closeMenu" > <bento-toggle-button ref="allViewsMenuToggleRef" aria-haspopup="true" :aria-expanded="showMenu" :aria-controls="showMenu ? menuPopoverId : null" variant="tertiary" :toggled="showMenu" @click="toggleShowMenu" > <template #iconLeft> <menu-icon :svg-title="t('toggleAllViews')" /> </template> </bento-toggle-button> <bento-popover v-if="allViewsMenuToggleRef" :id="menuPopoverId" ref="allViewsMenuPopoverRef" class="b-header-with-views-all-views__popover" :open="showMenu" :target-element="allViewsMenuToggleRef" :fixed-positioning="true" :overflow-visible="true" position="bottom-end" without-space > <output v-if="draggedViewIndex !== null && draggingOverViewIndex !== null" class="b-header-with-views-all-views__popover-status" > {{ t('movingDraggedToPosition', { dragged: menuItems[draggedViewIndex].label, draggingOver: menuItems[draggingOverViewIndex].label, }) }} </output> <ul ref="menuRef" class="b-header-with-views-all-views__popover-menu"> <li v-for="(item, index) in menuItems" ref="menuItemRefs" :key="index" data-drag-item class="b-header-with-views-all-views__popover-menu-item" :class="conditionalDraggableItemClass(index)" @pointerdown="handleDragStart(index, $event)" > <div class="b-header-with-views-all-views__popover-menu-item-content"> <bento-button data-drag-trigger class="b-header-with-views-all-views__popover-menu-item-button b-header-with-views-all-views__popover-menu-item-button-reorder" variant="tertiary" :aria-pressed="isItemDragged(index)" @keydown.enter.native="handleDragKeyboardToggle(index, $event)" @keydown.space.native="handleDragKeyboardToggle(index, $event)" @keydown.down.native="handleDragKeyboardMove(UseDragAndDropViewsDirectionEnum.DOWN, $event)" @keydown.up.native="handleDragKeyboardMove(UseDragAndDropViewsDirectionEnum.UP, $event)" @keydown.esc.native="handleDragStop" > <template #iconLeft> <grab-icon :svg-title="t('reorderView', { label: item.label })" /> </template> </bento-button> <bento-button data-view-select class="b-header-with-views-all-views__popover-menu-item-button b-header-with-views-all-views__popover-menu-item-button-select" variant="tertiary" @click="emit('select:view', index)" > <template #iconLeft> <span class="b-header-with-views-all-views__popover-menu-item-button-icon" :class="`b-header-with-views-all-views__popover-menu-item-button-icon--${item.iconColor}`" aria-hidden="true" > <component :is="BentoHeaderWithViewsItemIconElement[item.icon]" /> </span> </template> <bento-typography el="span" variant="body" class="b-header-with-views-all-views__popover-menu-item-button-text" > {{ item.label }} </bento-typography> </bento-button> <bento-menu ref="editViewMenuRefs" data-view-menu class="b-header-with-views-all-views__popover-menu-item-button" :button="{ variant: 'tertiary', condensed: true }" :close-on-click-outside-options="menuCloseOnClickOutsideOptions" :data="menuItemsActions" menu-position="right-start" @open="emit('open:view-menu', index)" @close="emit('close:view-menu', index)" @keydown.tab.native.capture="closeViewMenuNatively(index)" > <template #iconLeft> <options-horizontal-icon :
|
|
1
|
+
<template> <div ref="allViewsRef" class="b-header-with-views-all-views" data-testid="header-with-views-all-views" @keydown.esc.stop.prevent="closeMenu" > <bento-toggle-button ref="allViewsMenuToggleRef" aria-haspopup="true" :aria-expanded="showMenu" :aria-controls="showMenu ? menuPopoverId : null" variant="tertiary" :toggled="showMenu" @click="toggleShowMenu" > <template #iconLeft> <menu-icon :svg-title="t('toggleAllViews')" /> </template> </bento-toggle-button> <bento-popover v-if="allViewsMenuToggleRef" :id="menuPopoverId" ref="allViewsMenuPopoverRef" class="b-header-with-views-all-views__popover" :open="showMenu" :target-element="allViewsMenuToggleRef" :fixed-positioning="true" :overflow-visible="true" position="bottom-end" without-space > <output v-if="draggedViewIndex !== null && draggingOverViewIndex !== null" class="b-header-with-views-all-views__popover-status" > {{ t('movingDraggedToPosition', { dragged: menuItems[draggedViewIndex].label, draggingOver: menuItems[draggingOverViewIndex].label, }) }} </output> <ul ref="menuRef" class="b-header-with-views-all-views__popover-menu"> <li v-for="(item, index) in menuItems" ref="menuItemRefs" :key="index" data-drag-item class="b-header-with-views-all-views__popover-menu-item" :class="conditionalDraggableItemClass(index)" @pointerdown="handleDragStart(index, $event)" > <div class="b-header-with-views-all-views__popover-menu-item-content"> <bento-button data-drag-trigger class="b-header-with-views-all-views__popover-menu-item-button b-header-with-views-all-views__popover-menu-item-button-reorder" variant="tertiary" :aria-pressed="isItemDragged(index)" @keydown.enter.native="handleDragKeyboardToggle(index, $event)" @keydown.space.native="handleDragKeyboardToggle(index, $event)" @keydown.down.native="handleDragKeyboardMove(UseDragAndDropViewsDirectionEnum.DOWN, $event)" @keydown.up.native="handleDragKeyboardMove(UseDragAndDropViewsDirectionEnum.UP, $event)" @keydown.esc.native="handleDragStop" > <template #iconLeft> <grab-icon :svg-title="t('reorderView', { label: item.label })" /> </template> </bento-button> <bento-button data-view-select class="b-header-with-views-all-views__popover-menu-item-button b-header-with-views-all-views__popover-menu-item-button-select" variant="tertiary" @click="emit('select:view', index)" > <template #iconLeft> <span class="b-header-with-views-all-views__popover-menu-item-button-icon" :class="`b-header-with-views-all-views__popover-menu-item-button-icon--${item.iconColor}`" aria-hidden="true" > <component :is="BentoHeaderWithViewsItemIconElement[item.icon]" /> </span> </template> <bento-typography el="span" variant="body" class="b-header-with-views-all-views__popover-menu-item-button-text" > {{ item.label }} </bento-typography> </bento-button> <bento-menu ref="editViewMenuRefs" data-view-menu class="b-header-with-views-all-views__popover-menu-item-button" :button="{ variant: 'tertiary', condensed: true, 'aria-label': t('openViewOptions', { label: item.label }), }" :close-on-click-outside-options="menuCloseOnClickOutsideOptions" :data="menuItemsActions" menu-position="right-start" @open="emit('open:view-menu', index)" @close="emit('close:view-menu', index)" @keydown.tab.native.capture="closeViewMenuNatively(index)" > <template #iconLeft> <options-horizontal-icon :aria-hidden="true" /> </template> </bento-menu> </div> </li> </ul> </bento-popover> </div> </template> <script setup lang="ts"> import { BentoButton, BentoToggleButton } from '@/components/button'; import { BentoHeaderWithViewsItemIconElement } from '@/components/header-with-views/header-with-views.internal.types'; import { BentoMenu } from '@/components/menu'; import { BentoPopover } from '@/components/popover'; import { BentoTypography } from '@/components/typography'; import { useClickOutside } from '@/composables'; import { generateUid } from '@/utils/ts/generate-uid'; import { useI18n } from '@/utils/ts/i18n'; import GrabIcon from '@adyen/ui-assets-icons-16/vue/grab'; import MenuIcon from '@adyen/ui-assets-icons-16/vue/menu'; import OptionsHorizontalIcon from '@adyen/ui-assets-icons-16/vue/options-horizontal'; import { nextTick, ref, toRef } from 'vue'; import { useDragAndDropViews, UseDragAndDropViewsDirectionEnum } from '../../composables'; import { type HeaderWithViewsAllViewsProps } from './header-with-views-all-views.types'; import messages from './messages.json'; type MessageSchema = (typeof messages)['en-US']; const props = withDefaults(defineProps<HeaderWithViewsAllViewsProps>(), {}); const emit = defineEmits<{ /** * Emitted when the "all views" menu is requested to be closed. */ (e: 'close:all-views'): void; /** * Emitted when a specific view's action menu is requested to be closed. */ (e: 'close:view-menu', index: number): void; /** * Emitted when the "all views" menu is requested to be opened. */ (e: 'open:all-views'): void; /** * Emitted when a specific view's action menu is requested to be opened. */ (e: 'open:view-menu', index: number): void; /** * Emitted when a view item is selected from the "all views" menu. */ (e: 'select:view', index: number): void; /** * Emitted when the order of views has been changed due to drag and drop. */ (e: 'reorder:view', newViews: HeaderWithViewsAllViewsProps['menuItems']): void; }>(); const showMenu = ref(false); const allViewsMenuToggleRef = ref(null); const allViewsMenuPopoverRef = ref(null); const allViewsRef = ref(null); const menuRef = ref(null); const menuItemRefs = ref<Array<HTMLElement>>(null); const editViewMenuRefs = ref(null); const menuPopoverId = generateUid('menu-popover'); const { t } = useI18n<{ message: MessageSchema }>({ messages }); useClickOutside(allViewsMenuPopoverRef, () => closeMenu(), { ignore: [allViewsMenuToggleRef, ...(props.menuCloseOnClickOutsideOptions?.ignore ?? [])], }); const openMenu = () => { showMenu.value = true; emit('open:all-views'); }; const closeMenu = async () => { if (!showMenu.value) { return; } showMenu.value = false; emit('close:all-views'); await nextTick(); allViewsMenuToggleRef.value.$el.focus(); }; const toggleShowMenu = (e: MouseEvent) => { e.stopPropagation(); if (!showMenu.value) { openMenu(); } else { closeMenu(); } }; const { draggedViewIndex, draggingOverViewIndex, isItemDragged, handleDragKeyboardMove, handleDragKeyboardToggle, handleDragStart, handleDragStop, } = useDragAndDropViews({ items: toRef(props, 'menuItems'), itemRefs: menuItemRefs, onDrop: value => emit('reorder:view', value), }); const conditionalDraggableItemClass = (index: number) => ({ ['b-header-with-views-all-views__popover-menu-item--dragging']: isItemDragged(index), }); const closeViewMenuNatively = (index: number) => { // skip built-in refocus on the menu button when menu is closed // enables tab to flow as intended by HTML spec editViewMenuRefs.value[index].closeMenu(false); }; </script> <script lang="ts"> /** * Internal component to handle the logic for the list of all views available. */ export default { i18n: { messages }, }; </script> <style lang="scss" scoped src="./header-with-views-all-views.scss" />
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import { type BentoHeaderWithViewsItemAdditionalConfig, type
|
|
1
|
+
import { type BentoHeaderWithViewsItemAdditionalConfig, type BentoHeaderWithViewsItemIcon, type BentoHeaderWithViewsItemIconColor, } from '../../header-with-views.types'; import { type BentoHeaderWithViewsItemErrors } from '../../header-with-views.internal.types'; type HeaderWithViewsEditFormContext = `${HeaderWithViewsEditFormContextEnum}`; export enum HeaderWithViewsEditFormContextEnum { EDIT = 'edit', CREATE = 'create', } export interface HeaderWithViewsEditFormProps { additionalFormItem?: BentoHeaderWithViewsItemAdditionalConfig; context?: HeaderWithViewsEditFormContext; disableCustomization?: boolean; errors?: BentoHeaderWithViewsItemErrors; icon?: BentoHeaderWithViewsItemIcon; iconColor?: BentoHeaderWithViewsItemIconColor; label?: string; }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<template> <form class="b-header-with-views-edit-form"> <field-label>{{ t('title') }}</field-label> <div class="b-header-with-views-edit-form__icon-choice-alert" role="alert">{{ computedAlert }}</div> <div class="b-header-with-views-edit-form__icon-selector"> <bento-toggle-button ref="iconSelectorButtonRef" variant="secondary" :aria-expanded="showIconSelectorPopover" :aria-controls="showIconSelectorPopover ? iconPopoverId : null" :disabled="props.disableCustomization" :toggled="showIconSelectorPopover" @click="showIconSelectorPopover = !showIconSelectorPopover" > <template #iconLeft> <component :is="BentoHeaderWithViewsItemIconElement[iconValue]" :class="conditionalIconClass" :svg-title="t('viewIcons')" /> </template> </bento-toggle-button> <bento-popover v-if="iconSelectorButtonRef && !props.disableCustomization" :id="iconPopoverId" ref="iconPopoverRef" class="b-header-with-views-edit-form__icon-selector-popover" :target-element="iconSelectorButtonRef" :open="showIconSelectorPopover" :fallback-position="['bottom-end']" :fixed-positioning="true" :overflow-visible="true" position="bottom-end" @dismiss="showIconSelectorPopover = false" @keydown.escape.native.capture.stop.prevent="showIconSelectorPopover = false" > <div class="b-header-with-views-edit-form__icon-panel"> <div class="b-header-with-views-edit-form__icon-color-options"> <div v-for="(color, index) in iconColorOptions" :key="index" class="b-header-with-views-edit-form__icon-options-wrapper" > <bento-button class="b-header-with-views-edit-form__icon-color-options-button" :class="`b-header-with-views-edit-form__icon-color--${color}`" :aria-selected="iconColorValue === color" variant="tertiary" @click="iconColorValue = color" > <template #iconLeft> <circle-filled-icon :svg-title="t('selectIconColor', { color })" ></circle-filled-icon> </template> </bento-button> </div> </div> <bento-divider /> <div class="b-header-with-views-edit-form__icon-options"> <bento-button v-for="(icon, index) in iconOptions" :key="index" class="b-header-with-views-edit-form__icon-options-button" :aria-selected="iconValue === icon" variant="tertiary" @click="iconValue = icon" > <template #iconLeft> <component :is="BentoHeaderWithViewsItemIconElement[icon]" :svg-title="t('selectIcon', { icon })" /> </template> </bento-button> </div> </div> </bento-popover> <bento-input-field ref="inputFieldRef" class="b-header-with-views-edit-form__icon-selector-input" :aria-label="t('viewLabel')" :error-message="errors.label" :model-value="labelValue" @keydown.enter.prevent="
|
|
1
|
+
<template> <form class="b-header-with-views-edit-form"> <field-label>{{ t('title') }}</field-label> <div class="b-header-with-views-edit-form__icon-choice-alert" role="alert">{{ computedAlert }}</div> <div class="b-header-with-views-edit-form__icon-selector"> <bento-toggle-button ref="iconSelectorButtonRef" variant="secondary" :aria-expanded="showIconSelectorPopover" :aria-controls="showIconSelectorPopover ? iconPopoverId : null" :disabled="props.disableCustomization" :toggled="showIconSelectorPopover" @click="showIconSelectorPopover = !showIconSelectorPopover" > <template #iconLeft> <component :is="BentoHeaderWithViewsItemIconElement[iconValue]" :class="conditionalIconClass" :svg-title="t('viewIcons')" /> </template> </bento-toggle-button> <bento-popover v-if="iconSelectorButtonRef && !props.disableCustomization" :id="iconPopoverId" ref="iconPopoverRef" class="b-header-with-views-edit-form__icon-selector-popover" :target-element="iconSelectorButtonRef" :open="showIconSelectorPopover" :fallback-position="['bottom-end']" :fixed-positioning="true" :overflow-visible="true" position="bottom-end" @dismiss="showIconSelectorPopover = false" @keydown.escape.native.capture.stop.prevent="showIconSelectorPopover = false" > <div class="b-header-with-views-edit-form__icon-panel"> <div class="b-header-with-views-edit-form__icon-color-options"> <div v-for="(color, index) in iconColorOptions" :key="index" class="b-header-with-views-edit-form__icon-options-wrapper" > <bento-button class="b-header-with-views-edit-form__icon-color-options-button" :class="`b-header-with-views-edit-form__icon-color--${color}`" :aria-selected="iconColorValue === color" variant="tertiary" @click="iconColorValue = color" > <template #iconLeft> <circle-filled-icon :svg-title="t('selectIconColor', { color })" ></circle-filled-icon> </template> </bento-button> </div> </div> <bento-divider /> <div class="b-header-with-views-edit-form__icon-options"> <bento-button v-for="(icon, index) in iconOptions" :key="index" class="b-header-with-views-edit-form__icon-options-button" :aria-selected="iconValue === icon" variant="tertiary" @click="iconValue = icon" > <template #iconLeft> <component :is="BentoHeaderWithViewsItemIconElement[icon]" :svg-title="t('selectIcon', { icon })" /> </template> </bento-button> </div> </div> </bento-popover> <bento-input-field ref="inputFieldRef" class="b-header-with-views-edit-form__icon-selector-input" :aria-label="t('viewLabel')" :error-message="errors.label" :model-value="labelValue" @keydown.enter.prevent="handleKeydownEnter" @update:model-value="handleInput" @blur="emit('blur:label', labelValue)" ></bento-input-field> </div> <bento-checkbox v-if="additionalFormItem" :model-value="additionalFormItemValue" class="b-header-with-views-edit-form__additional-config" @update:model-value="handleAdditionalFormItemChange" > {{ additionalFormItem.label }} </bento-checkbox> <bento-button-actions class="b-header-with-views-edit-form__actions" :actions="computedActions" layout="buttons-end" /> </form> </template> <script setup lang="ts"> import { BentoButton, BentoButtonActions, type BentoButtonActionsList, BentoToggleButton, } from '@/components/button'; import { BentoCheckbox } from '@/components/checkbox'; import { BentoDivider } from '@/components/divider'; import { BentoInputField } from '@/components/input-field'; import { BentoPopover } from '@/components/popover'; import { useClickOutside } from '@/composables'; import { generateUid } from '@/utils/ts/generate-uid'; import { FieldLabel } from '@/internal'; import { debounce } from '@/utils/ts/debounce'; import { useI18n } from '@/utils/ts/i18n'; import CircleFilledIcon from '@adyen/ui-assets-icons-16/vue/circle-filled'; import { onKeyStroke } from '@vueuse/core'; import { computed, nextTick, onMounted, ref, watch } from 'vue'; import { BentoHeaderWithViewsItemIconColorEnum, BentoHeaderWithViewsItemIconElement, BentoHeaderWithViewsItemIconEnum, } from '../../header-with-views.internal.types'; import { type BentoHeaderWithViewsItem } from '../../header-with-views.types'; import { HeaderWithViewsEditFormContextEnum, type HeaderWithViewsEditFormProps, } from './header-with-views-edit-form.types'; import messages from './messages.json'; type MessageSchema = (typeof messages)['en-US']; const props = withDefaults(defineProps<HeaderWithViewsEditFormProps>(), { additionalFormItem: null, context: HeaderWithViewsEditFormContextEnum.CREATE, disableCustomization: false, errors: null, label: '', icon: BentoHeaderWithViewsItemIconEnum.STACK, iconColor: BentoHeaderWithViewsItemIconColorEnum.PRIMARY, }); const emit = defineEmits<{ /** * Emitted when form is canceled. */ (e: 'cancel'): void; /** * Emitted when the item is updated. * Typically used to sync changes to the parent component. */ (e: 'update', value: BentoHeaderWithViewsItem): void; /** * Emitted when the item is requested to be deleted. */ (e: 'delete', value: BentoHeaderWithViewsItem): void; /** * Emitted when the label of the item is modified via input. */ (e: 'input:label', value: BentoHeaderWithViewsItem['label']): void; /** * Emitted when the label input loses focus (blur event). */ (e: 'blur:label', value: BentoHeaderWithViewsItem['label']): void; /** * Emitted when the Enter key is pressed while interacting with the item. */ (e: 'keydown:enter', value: BentoHeaderWithViewsItem): void; }>(); // form inputs const labelValue = ref(props.label); const iconValue = ref(props.icon); const iconColorValue = ref(props.iconColor); const additionalFormItemValue = ref<boolean>(props.additionalFormItem?.value ?? false); const isFormDirty = ref(false); // refs const iconSelectorButtonRef = ref(null); const iconPopoverRef = ref(null); const inputFieldRef = ref(null); const iconPopoverId = generateUid('iconPopover'); // view controls const showIconSelectorPopover = ref(false); const iconOptions = computed(() => Object.values(BentoHeaderWithViewsItemIconEnum)); const iconColorOptions = computed(() => Object.values(BentoHeaderWithViewsItemIconColorEnum)); const hasErrors = computed(() => Object.values(props.errors).some(error => error.length > 0)); const computedAlert = computed( () => `${t('selected')}: ${iconValue.value} ${t('icon')}, ${t('color')} ${iconColorValue.value}` ); const computedActions = computed<BentoButtonActionsList>(() => [ ...(props.context === HeaderWithViewsEditFormContextEnum.EDIT ? // edit mode [ { title: t('save'), type: 'submit', event: () => { if (hasErrors.value) { return; } updateView(); }, }, ] : // create mode [ { title: t('create'), type: 'submit', event: () => { if (!isFormDirty.value || hasErrors.value) { return; } updateView(); }, }, ]), { title: t('cancel'), event: resetView, }, ]); const { t } = useI18n<{ message: MessageSchema }>({ messages }); const computedFormValues = computed(() => ({ iconColor: iconColorValue.value, icon: iconValue.value, label: labelValue.value, ...(props.additionalFormItem ? { additionalViewSetting: { label: props.additionalFormItem.label, value: additionalFormItemValue.value, }, } : []), })); const conditionalIconClass = computed(() => props.disableCustomization ? '' : `b-header-with-views-edit-form__icon-color--${iconColorValue.value}` ); const hideIconSelectorPopover = () => { showIconSelectorPopover.value = false; }; const updateView = () => { emit('update', computedFormValues.value); }; const resetView = () => { iconValue.value = props.icon; iconColorValue.value = props.iconColor; labelValue.value = props.label; emit('cancel'); }; const handleInput = debounce((value: string) => { labelValue.value = value; isFormDirty.value = true; }, 200); const handleKeydownEnter = (event: KeyboardEvent) => { labelValue.value = (event.target as HTMLInputElement).value; isFormDirty.value = true; emit('keydown:enter', computedFormValues.value); }; const handleAdditionalFormItemChange = (value: boolean) => { additionalFormItemValue.value = value; isFormDirty.value = true; }; useClickOutside(iconPopoverRef, hideIconSelectorPopover, { ignore: [iconSelectorButtonRef], }); onKeyStroke('Escape', hideIconSelectorPopover, { target: iconPopoverRef, eventName: 'keydown', }); watch(labelValue, value => emit('input:label', value)); onMounted(async () => { // force focus to start from the input field await nextTick(); inputFieldRef.value.$refs.inputFieldElement.focus(); }); </script> <script lang="ts"> /** * Internal component to which presents a form for handling the logic of editing and creating a view. */ export default { i18n: { messages }, }; </script> <style lang="scss" scoped src="./header-with-views-edit-form.scss" />
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import { type VueNodeElement } from '@/types/elements'; import BagIcon from '@adyen/ui-assets-icons-16/vue/bag'; import CarIcon from '@adyen/ui-assets-icons-16/vue/car'; import CashStackIcon from '@adyen/ui-assets-icons-16/vue/cash-stack'; import CloudIcon from '@adyen/ui-assets-icons-16/vue/cloud'; import DiamondIcon from '@adyen/ui-assets-icons-16/vue/diamond'; import EducationIcon from '@adyen/ui-assets-icons-16/vue/education'; import EmoticonHappyIcon from '@adyen/ui-assets-icons-16/vue/emoticon-happy'; import HotelIcon from '@adyen/ui-assets-icons-16/vue/gambling'; import GamingIcon from '@adyen/ui-assets-icons-16/vue/gaming'; import GiftIcon from '@adyen/ui-assets-icons-16/vue/gift'; import HandRockIcon from '@adyen/ui-assets-icons-16/vue/hand-rock'; import HashtagIcon from '@adyen/ui-assets-icons-16/vue/hashtag'; import HeartIcon from '@adyen/ui-assets-icons-16/vue/heart'; import HospitalityIcon from '@adyen/ui-assets-icons-16/vue/hospitality'; import HourglassIcon from '@adyen/ui-assets-icons-16/vue/hourglass'; import LeafIcon from '@adyen/ui-assets-icons-16/vue/leaf'; import LightningIcon from '@adyen/ui-assets-icons-16/vue/lightning'; import LockIcon from '@adyen/ui-assets-icons-16/vue/lock'; import NewsIcon from '@adyen/ui-assets-icons-16/vue/news'; import NotificationIcon from '@adyen/ui-assets-icons-16/vue/notification'; import PrizeIcon from '@adyen/ui-assets-icons-16/vue/prize'; import RetailIcon from '@adyen/ui-assets-icons-16/vue/retail'; import ShoppingCartIcon from '@adyen/ui-assets-icons-16/vue/shopping-cart'; import SleepIcon from '@adyen/ui-assets-icons-16/vue/sleep'; import StackIcon from '@adyen/ui-assets-icons-16/vue/stack'; import StarIcon from '@adyen/ui-assets-icons-16/vue/star'; import SupportIcon from '@adyen/ui-assets-icons-16/vue/support'; import TargetIcon from '@adyen/ui-assets-icons-16/vue/target'; import ThumbsDownIcon from '@adyen/ui-assets-icons-16/vue/thumb-down'; import ThumbsUpIcon from '@adyen/ui-assets-icons-16/vue/thumb-up'; import TravelBagIcon from '@adyen/ui-assets-icons-16/vue/travel-bag'; import WarningDiamondIcon from '@adyen/ui-assets-icons-16/vue/warning-diamond'; import WorldIcon from '@adyen/ui-assets-icons-16/vue/world'; export enum BentoHeaderWithViewsItemIconColorEnum { BLUE = 'blue', GREEN = 'green', ORANGE = 'orange', PRIMARY = 'primary', RED = 'red', YELLOW = 'yellow', } export enum BentoHeaderWithViewsItemIconEnum { STACK = 'stack', LEAF = 'leaf', HAND_ROCK = 'hand-rock', CAR = 'car', EDUCATION = 'education', HOTEL = 'hotel', TRAVEL_BAG = 'travel-bag', LIGHTNING = 'lightning', NEWS = 'news', PRIZE = 'prize', LOCK = 'lock', BAG = 'bag', DIAMOND = 'diamond', TARGET = 'target', GIFT = 'gift', CASH_STACK = 'cash-stack', HASHTAG = 'hashtag', CLOUD = 'cloud', HOSPITALITY = 'hospitality', SHOPPING_CART = 'shopping-cart', SLEEP = 'sleep', NOTIFICATION = 'notification', THUMBS_UP = 'thumb-up', THUMBS_DOWN = 'thumb-down', STAR = 'star', WARNING_DIAMOND = 'warning-diamond', RETAIL = 'retail', EMOTICON_HAPPY = 'emoticon-happy', GAMING = 'gaming', SUPPORT = 'support', HEART = 'heart', HOURGLASS = 'hourglass', WORLD = 'world', } export type BentoHeaderWithViewsItemIconColor = `${BentoHeaderWithViewsItemIconColorEnum}`; export type BentoHeaderWithViewsItemIcon = `${BentoHeaderWithViewsItemIconEnum}`; export interface BentoHeaderWithViewsItemAdditionalConfig { /** * Label for an additional view setting (e.g. a checkbox or toggle). */ label: string; /** * Boolean value indicating the state of the additional view setting (e.g., checked or toggled). */ value: boolean; } export interface BentoHeaderWithViewsItem { /** * Optional icon to display for the view item. */ icon?: BentoHeaderWithViewsItemIcon; /** * Optional color of the icon for the view item. */ iconColor?: BentoHeaderWithViewsItemIconColor; /** * Text label for the view item. */ label: string; /** * Optional boolean indicating if this view item is the default or currently active view. */ isDefault?: boolean; /** * Optional configuration for an additional view setting, such as a filter or option. */ additionalViewSetting?: BentoHeaderWithViewsItemAdditionalConfig; /** * Optional boolean indicating if this view can be edited or deleted. */ isFixed?: boolean; } export interface BentoHeaderWithViewsItemUpdate { newViews: Array<BentoHeaderWithViewsItem>; oldViews: Array<BentoHeaderWithViewsItem>; targetViewLabel: BentoHeaderWithViewsItem['label']; } export const BentoHeaderWithViewsItemIconElement: Record<BentoHeaderWithViewsItemIcon, VueNodeElement> = { [BentoHeaderWithViewsItemIconEnum.STACK]: StackIcon, [BentoHeaderWithViewsItemIconEnum.LEAF]: LeafIcon, [BentoHeaderWithViewsItemIconEnum.HAND_ROCK]: HandRockIcon, [BentoHeaderWithViewsItemIconEnum.CAR]: CarIcon, [BentoHeaderWithViewsItemIconEnum.EDUCATION]: EducationIcon, [BentoHeaderWithViewsItemIconEnum.HOTEL]: HotelIcon, [BentoHeaderWithViewsItemIconEnum.TRAVEL_BAG]: TravelBagIcon, [BentoHeaderWithViewsItemIconEnum.LIGHTNING]: LightningIcon, [BentoHeaderWithViewsItemIconEnum.NEWS]: NewsIcon, [BentoHeaderWithViewsItemIconEnum.PRIZE]: PrizeIcon, [BentoHeaderWithViewsItemIconEnum.LOCK]: LockIcon, [BentoHeaderWithViewsItemIconEnum.BAG]: BagIcon, [BentoHeaderWithViewsItemIconEnum.DIAMOND]: DiamondIcon, [BentoHeaderWithViewsItemIconEnum.TARGET]: TargetIcon, [BentoHeaderWithViewsItemIconEnum.GIFT]: GiftIcon, [BentoHeaderWithViewsItemIconEnum.CASH_STACK]: CashStackIcon, [BentoHeaderWithViewsItemIconEnum.HASHTAG]: HashtagIcon, [BentoHeaderWithViewsItemIconEnum.CLOUD]: CloudIcon, [BentoHeaderWithViewsItemIconEnum.HOSPITALITY]: HospitalityIcon, [BentoHeaderWithViewsItemIconEnum.SHOPPING_CART]: ShoppingCartIcon, [BentoHeaderWithViewsItemIconEnum.SLEEP]: SleepIcon, [BentoHeaderWithViewsItemIconEnum.NOTIFICATION]: NotificationIcon, [BentoHeaderWithViewsItemIconEnum.THUMBS_UP]: ThumbsUpIcon, [BentoHeaderWithViewsItemIconEnum.THUMBS_DOWN]: ThumbsDownIcon, [BentoHeaderWithViewsItemIconEnum.STAR]: StarIcon, [BentoHeaderWithViewsItemIconEnum.WARNING_DIAMOND]: WarningDiamondIcon, [BentoHeaderWithViewsItemIconEnum.RETAIL]: RetailIcon, [BentoHeaderWithViewsItemIconEnum.EMOTICON_HAPPY]: EmoticonHappyIcon, [BentoHeaderWithViewsItemIconEnum.GAMING]: GamingIcon, [BentoHeaderWithViewsItemIconEnum.SUPPORT]: SupportIcon, [BentoHeaderWithViewsItemIconEnum.HEART]: HeartIcon, [BentoHeaderWithViewsItemIconEnum.HOURGLASS]: HourglassIcon, [BentoHeaderWithViewsItemIconEnum.WORLD]: WorldIcon, }; export interface BentoHeaderWithViewsEmits { /** * Emitted when a new view is added to the list of views. */ (e: 'add:view', value: BentoHeaderWithViewsItemUpdate): void; /** * Emitted when a view is deleted from the list of views. */ (e: 'delete:view', value: BentoHeaderWithViewsItemUpdate): void; /** * Emitted when a view is duplicated. */ (e: 'duplicate:view', value: BentoHeaderWithViewsItemUpdate): void; /** * Emitted when the user reorganizes the order of the views. * No `targetViewLabel` is emitted as the views are reordered as a whole. */ (e: 'reorder:view', value: Omit<BentoHeaderWithViewsItemUpdate, 'targetViewLabel'>): void; /** * Emitted when the user requests to restore a previously saved or default view. */ (e: 'restore:view', value: BentoHeaderWithViewsItem['label']): void; /** * Emitted when the user requests to save the current view. */ (e: 'save:view', value: BentoHeaderWithViewsItem['label']): void; /** * Emitted when the user selects a different view from the available options. */ (e: 'select:view', value: BentoHeaderWithViewsItem['label']): void; /** * Emitted when a property of a view is updated. */ (e: 'update:view', value: BentoHeaderWithViewsItemUpdate): void; /** * Emitted when the user selects the prompt checkbox and closes the modal with a valid button action. * Only works when a string is pass into the `skipUnsavedChangesText` prop. */ (e: 'update:skip-unsaved-changes', value: boolean): void; /** * Emitted when the active view index changes. Used for two-way binding with v-model:active-view-index. */ (e: 'update:active-view-index', value: number | null): void; } export type BentoHeaderWithViewsItemErrors = { /** * Object where the keys correspond to the properties of `BentoHeaderWithViewsItem`, * and the values are strings representing any validation errors for that property. */ [K in keyof BentoHeaderWithViewsItem]: string; };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import { storybookDocsParameter, withToastDecorator } from '@/utils/ts/storybook'; import { action } from '@storybook/addon-actions'; import type { Meta, StoryObj } from '@storybook/vue'; import { isVue2 } from 'vue-demi'; import { ref, watch } from 'vue'; import { BentoHeaderWithViewsItemIconColorEnum } from './header-with-views.types'; import BentoHeaderWithViews from './header-with-views.vue'; import { BentoInputField } from '@/components/input-field';
|
|
1
|
+
import { storybookDocsParameter, withToastDecorator } from '@/utils/ts/storybook'; import { action } from '@storybook/addon-actions'; import type { Meta, StoryObj } from '@storybook/vue'; import { isVue2 } from 'vue-demi'; import { ref, watch } from 'vue'; import { BentoHeaderWithViewsItemIconColorEnum } from './header-with-views.internal.types'; import BentoHeaderWithViews from './header-with-views.vue'; import { BentoInputField } from '@/components/input-field'; const HeaderWithViewsControlledActiveViewExample = "<template>\n <div>\n <bento-header-with-views\n :active-view-index=\"activeViewIndex\"\n :views=\"views\"\n :has-changes=\"false\"\n :skip-unsaved-changes-text=\"null\"\n title=\"Page Title\"\n @update:active-view-index=\"activeViewIndex = $event\"\n />\n <div style=\"margin-top: var(--b-spacer-080); max-width: 200px\">\n <bento-input-field v-model=\"activeViewIndex\" type=\"number\" label=\"Active view index\" />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\n import { ref } from 'vue';\n import { BentoHeaderWithViews, type BentoHeaderWithViewsItem, BentoInputField } from '@adyen/bento-vue2';\n\n const activeViewIndex = ref(0);\n\n const views: Array<BentoHeaderWithViewsItem> = [\n { icon: 'stack', iconColor: 'blue', label: 'First View' },\n { icon: 'leaf', iconColor: 'green', label: 'Second View' },\n { icon: 'star', iconColor: 'yellow', label: 'Third View' },\n ];\n</script>\n"; const HeaderWithViewsDefault = "<template>\n <bento-header-with-views\n :actions=\"actions\"\n :additional-view-setting=\"additionalViewSetting\"\n :description=\"description\"\n has-changes\n info-icon-tooltip-text=\"Info icon tooltip text\"\n :last-updated=\"lastUpdated\"\n :reload-button=\"reloadButton\"\n skip-unsaved-changes-text=\"Don’t show this message again\"\n title=\"Page Title\"\n :views=\"views\"\n @add:view=\"handleViews\"\n @delete:view=\"handleViews\"\n @duplicate:view=\"handleViews\"\n @reorder:view=\"handleViews\"\n @restore:view=\"handleView\"\n @save:view=\"handleView\"\n @select:view=\"handleView\"\n @update:active-view-index=\"handleActiveViewIndex\"\n @update:skip-unsaved-changes=\"handleSkipUnsavedChanges\"\n @update:view=\"handleViews\"\n />\n</template>\n\n<script setup lang=\"ts\">\n import {\n type BentoButtonActionsList,\n BentoHeaderWithViews,\n type BentoHeaderWithViewsItem,\n } from '@adyen/bento-vue2';\n\n const actions: BentoButtonActionsList = [\n { title: 'Primary action', event: () => undefined },\n { title: 'Secondary action', event: () => undefined },\n { title: 'Tertiary action', event: () => undefined },\n { title: 'Another action', event: () => undefined },\n ];\n const additionalViewSetting = {\n label: 'Include date filter (relative)',\n value: false,\n };\n const description =\n \"This page shows how Adyen's AI boosts your payment performance and revenue. Monitor your authorization rate uplift and discover data-driven opportunities for further optimization.\";\n const lastUpdated = new Date();\n const reloadButton = {\n title: 'Reload',\n state: 'start' as const,\n event: () => undefined,\n };\n const views: Array<BentoHeaderWithViewsItem> = [\n { icon: 'stack', iconColor: 'primary', label: 'A very long value' },\n { icon: 'stack', iconColor: 'primary', isFixed: true, label: 'Fixed view' },\n { icon: 'stack', iconColor: 'primary', label: 'View' },\n ];\n\n const handleViews = () => undefined;\n const handleView = () => undefined;\n const handleSkipUnsavedChanges = () => undefined;\n const handleActiveViewIndex = () => undefined;\n</script>\n"; const meta: Meta = { title: 'Header with views', component: BentoHeaderWithViews, decorators: [withToastDecorator], argTypes: { // Events 'add:view': { table: { type: { summary: '(value: BentoHeaderWithViewsItemUpdate) => void' }, }, control: { disable: true }, }, 'delete:view': { table: { type: { summary: '(value: BentoHeaderWithViewsItemUpdate) => void' }, }, control: { disable: true }, }, 'duplicate:view': { table: { type: { summary: '(value: BentoHeaderWithViewsItemUpdate) => void' }, }, control: { disable: true }, }, 'reorder:view': { table: { type: { summary: '(value: Omit<BentoHeaderWithViewsItemUpdate, "targetViewLabel">) => void' }, }, control: { disable: true }, }, 'restore:view': { table: { type: { summary: '(value: string) => void' }, }, control: { disable: true }, }, 'save:view': { table: { type: { summary: '(value: string) => void' }, }, control: { disable: true }, }, 'select:view': { table: { type: { summary: '(value: string) => void' }, }, control: { disable: true }, }, 'update:view': { table: { type: { summary: '(value: BentoHeaderWithViewsItemUpdate) => void' }, }, control: { disable: true }, }, 'update:active-view-index': { table: { type: { summary: '(value: number | null) => void' }, }, control: { disable: true }, }, // Slots infoIconTooltipContent: { description: 'Content to be displayed as a tooltip next to the header. This replaces the description property if present', control: 'text', table: { type: { summary: 'VNode[]' }, }, }, }, }; export default meta; type Story = StoryObj<typeof BentoHeaderWithViews>; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoHeaderWithViews }, props: Object.keys(argTypes), template: ` <bento-header-with-views v-bind="args" @add:view="addView" @delete:view="deleteView" @duplicate:view="duplicateView" @reorder:view="reorderView" @restore:view="restoreView" @save:view="saveView" @select:view="selectView" @update:view="updateView" @update:skip-unsaved-changes="updateSkipUnsavedChanges" @update:active-view-index="updateActiveViewIndex" /> `, setup(props) { const addView = value => action('add:view')(value); const deleteView = value => action('delete:view')(value); const duplicateView = value => action('duplicate:view')(value); const reorderView = value => action('reorder:view')(value); const restoreView = value => action('restore:view')(value); const saveView = value => action('save:view')(value); const selectView = value => action('select:view')(value); const updateView = value => action('update:view')(value); const updateSkipUnsavedChanges = value => action('update:skip-unsaved-changes')(value); const updateActiveViewIndex = value => action('update:active-view-index')(value); return { args: isVue2 ? props : _args, addView, deleteView, duplicateView, reorderView, restoreView, saveView, selectView, updateView, updateSkipUnsavedChanges, updateActiveViewIndex, }; }, }), args: { hasChanges: true, skipUnsavedChangesText: 'Don’t show this message again', actions: [ { title: 'Primary action', event: () => undefined }, { title: 'Secondary action', event: () => undefined }, { title: 'Tertiary action', event: () => undefined }, { title: 'Another action', event: () => undefined }, ], additionalViewSetting: { label: 'Include date filter (relative)', value: false, }, title: 'Page Title', views: [ { icon: 'stack', iconColor: BentoHeaderWithViewsItemIconColorEnum.PRIMARY, label: 'A very long value', }, { icon: 'stack', iconColor: BentoHeaderWithViewsItemIconColorEnum.PRIMARY, label: 'Fixed view', isFixed: true, }, { icon: 'stack', iconColor: BentoHeaderWithViewsItemIconColorEnum.PRIMARY, label: 'View', }, ], infoIconTooltipText: 'Info icon tooltip text', lastUpdated: new Date(), description: "This page shows how Adyen's AI boosts your payment performance and revenue. Monitor your authorization rate uplift and discover data-driven opportunities for further optimization.", reloadButton: { title: 'Reload', state: 'start', event: action('reloadButtonEvent'), }, }, parameters: storybookDocsParameter(HeaderWithViewsDefault), }; export const ControlledActiveView: Story = { render: (_args, { argTypes }) => ({ components: { BentoHeaderWithViews, BentoInputField }, props: Object.keys(argTypes), template: ` <div> <bento-header-with-views v-bind="args" :active-view-index="activeViewIndex" @update:active-view-index="onActiveViewIndexChange" /> <div style="margin-top: var(--b-spacer-080); max-width: 200px;"> <bento-input-field :model-value="activeViewIndex" @update:model-value="onInputChange" type="number" label="Active view index" /> </div> </div> `, setup(props) { const activeViewIndex = ref(props.activeViewIndex); const onActiveViewIndexChange = (value: number) => { activeViewIndex.value = value; action('update:active-view-index')(value); }; const onInputChange = (value: string) => { const numValue = Number.parseInt(value, 10); activeViewIndex.value = numValue; }; watch( () => props.activeViewIndex, index => { activeViewIndex.value = index; } ); return { args: isVue2 ? props : _args, activeViewIndex, onActiveViewIndexChange, onInputChange, }; }, }), args: { hasChanges: false, skipUnsavedChangesText: null, title: 'Page Title', views: [ { icon: 'stack', iconColor: BentoHeaderWithViewsItemIconColorEnum.BLUE, label: 'First View', }, { icon: 'leaf', iconColor: BentoHeaderWithViewsItemIconColorEnum.GREEN, label: 'Second View', }, { icon: 'star', iconColor: BentoHeaderWithViewsItemIconColorEnum.YELLOW, label: 'Third View', }, ], }, parameters: storybookDocsParameter(HeaderWithViewsControlledActiveViewExample), };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import { type BentoButtonActionsList } from '@/components/button'; import { type VueNodeElement } from '@/types/elements'; import type { HeaderMetaProps } from '@/internal'; import BagIcon from '@adyen/ui-assets-icons-16/vue/bag'; import CarIcon from '@adyen/ui-assets-icons-16/vue/car'; import CashStackIcon from '@adyen/ui-assets-icons-16/vue/cash-stack'; import CloudIcon from '@adyen/ui-assets-icons-16/vue/cloud'; import DiamondIcon from '@adyen/ui-assets-icons-16/vue/diamond'; import EducationIcon from '@adyen/ui-assets-icons-16/vue/education'; import EmoticonHappyIcon from '@adyen/ui-assets-icons-16/vue/emoticon-happy'; import HotelIcon from '@adyen/ui-assets-icons-16/vue/gambling'; import GamingIcon from '@adyen/ui-assets-icons-16/vue/gaming'; import GiftIcon from '@adyen/ui-assets-icons-16/vue/gift'; import HandRockIcon from '@adyen/ui-assets-icons-16/vue/hand-rock'; import HashtagIcon from '@adyen/ui-assets-icons-16/vue/hashtag'; import HeartIcon from '@adyen/ui-assets-icons-16/vue/heart'; import HospitalityIcon from '@adyen/ui-assets-icons-16/vue/hospitality'; import HourglassIcon from '@adyen/ui-assets-icons-16/vue/hourglass'; import LeafIcon from '@adyen/ui-assets-icons-16/vue/leaf'; import LightningIcon from '@adyen/ui-assets-icons-16/vue/lightning'; import LockIcon from '@adyen/ui-assets-icons-16/vue/lock'; import NewsIcon from '@adyen/ui-assets-icons-16/vue/news'; import NotificationIcon from '@adyen/ui-assets-icons-16/vue/notification'; import PrizeIcon from '@adyen/ui-assets-icons-16/vue/prize'; import RetailIcon from '@adyen/ui-assets-icons-16/vue/retail'; import ShoppingCartIcon from '@adyen/ui-assets-icons-16/vue/shopping-cart'; import SleepIcon from '@adyen/ui-assets-icons-16/vue/sleep'; import StackIcon from '@adyen/ui-assets-icons-16/vue/stack'; import StarIcon from '@adyen/ui-assets-icons-16/vue/star'; import SupportIcon from '@adyen/ui-assets-icons-16/vue/support'; import TargetIcon from '@adyen/ui-assets-icons-16/vue/target'; import ThumbsDownIcon from '@adyen/ui-assets-icons-16/vue/thumb-down'; import ThumbsUpIcon from '@adyen/ui-assets-icons-16/vue/thumb-up'; import TravelBagIcon from '@adyen/ui-assets-icons-16/vue/travel-bag'; import WarningDiamondIcon from '@adyen/ui-assets-icons-16/vue/warning-diamond'; import WorldIcon from '@adyen/ui-assets-icons-16/vue/world'; /* Public */ export type BentoHeaderWithViewsItemIconColor = `${BentoHeaderWithViewsItemIconColorEnum}`; export type BentoHeaderWithViewsItemIcon = `${BentoHeaderWithViewsItemIconEnum}`; export interface BentoHeaderWithViewsItemAdditionalConfig { /** * Label for an additional view setting (e.g. a checkbox or toggle). */ label: string; /** * Boolean value indicating the state of the additional view setting (e.g. checked or toggled). */ value: boolean; } export interface BentoHeaderWithViewsItem { /** * Optional icon to display for the view item. */ icon?: BentoHeaderWithViewsItemIcon; /** * Optional color of the icon for the view item. */ iconColor?: BentoHeaderWithViewsItemIconColor; /** * Text label for the view item. */ label: string; /** * Optional boolean indicating if this view item is the default or currently active view. */ isDefault?: boolean; /** * Optional configuration for an additional view setting, such as a filter or option. */ additionalViewSetting?: BentoHeaderWithViewsItemAdditionalConfig; /** * Optional boolean indicating if this view can be edited or deleted. */ isFixed?: boolean; } export interface BentoHeaderWithViewsProps { /** * Optional list of actions (buttons) to display in the header. * The structure of each action is defined by the `BentoButtonActionsList` type. * * {@link [BentoButtonActionsList](https://adyen-bento.netlify.app/?path=/docs/components-button-button-actions--docs#variations)} */ actions?: BentoButtonActionsList; /** * Main title text for the header. This prop is required. */ title: string; /** * Optional array of view items to display as selectable views or tabs. * * Each view should have the following property: * - `icon` (BentoHeaderWithViewsItemIcon, optional): Optional icon to display. * - `iconColor` (BentoHeaderWithViewsItemIconColor, optional): Optional color of the icon. * - `label` (string): Text label for the view item. * - `isDefault` (boolean, optional): Indicates if this view item is the default or currently active view. * - `additionalViewSetting` (BentoHeaderWithViewsItemAdditionalConfig, optional): Optional configuration for an additional view setting. * - `isFixed` (boolean, optional): Indicates if this view can be edited or deleted. * @see {@link BentoHeaderWithViewsItem} */ views?: Array<BentoHeaderWithViewsItem>; /** * Optional configuration for an additional view setting, such as a filter or option. * Defined by the `BentoHeaderWithViewsItemAdditionalConfig` interface. * * @see {@link BentoHeaderWithViewsItemAdditionalConfig} */ additionalViewSetting?: BentoHeaderWithViewsItemAdditionalConfig; /** * Boolean indicating whether there are unsaved changes on the page. * This might be used to visually indicate to the user that they need to save their work. */ hasChanges: boolean; /** * String indicating whether to display a prompt, reminding the user to save their changes whenever they switch view tabs. * If null, no prompt is shown and changes will always be restored to it's previous settings on view tab change. */ skipUnsavedChangesText: string | null; /** * Boolean indicating whether icon and icon selection is allowed on view creation and edit. */ disableCustomization?: boolean; /** * Text showed in the info-icon tooltip on hover */ infoIconTooltipText?: string; /** * Meta description for the header component */ description?: string; /** * JavaScript "Date" object for when the content was last updated */ lastUpdated?: Date | number; /** * Reload button which shows next to the header's timestamp * @see HeaderMetaProps['reloadButton'] */ reloadButton?: HeaderMetaProps['reloadButton']; /** * Whether or not to automatically select a view on mount */ disableAutoSelect?: boolean; /** * The active view index. Supports two-way binding via v-model:active-view-index. * When provided, this takes precedence over `isDefault` on individual views for initial selection. * Changes from outside the component will update the active view. * Has no effect when `disableAutoSelect` is true. */ activeViewIndex?: number; } export interface BentoHeaderWithViewsItemUpdate { newViews: Array<BentoHeaderWithViewsItem>; oldViews: Array<BentoHeaderWithViewsItem>; targetViewLabel: BentoHeaderWithViewsItem['label']; } /* Private */ export enum BentoHeaderWithViewsItemIconColorEnum { BLUE = 'blue', GREEN = 'green', ORANGE = 'orange', PRIMARY = 'primary', RED = 'red', YELLOW = 'yellow', } export enum BentoHeaderWithViewsItemIconEnum { STACK = 'stack', LEAF = 'leaf', HAND_ROCK = 'hand-rock', CAR = 'car', EDUCATION = 'education', HOTEL = 'hotel', TRAVEL_BAG = 'travel-bag', LIGHTNING = 'lightning', NEWS = 'news', PRIZE = 'prize', LOCK = 'lock', BAG = 'bag', DIAMOND = 'diamond', TARGET = 'target', GIFT = 'gift', CASH_STACK = 'cash-stack', HASHTAG = 'hashtag', CLOUD = 'cloud', HOSPITALITY = 'hospitality', SHOPPING_CART = 'shopping-cart', SLEEP = 'sleep', NOTIFICATION = 'notification', THUMBS_UP = 'thumb-up', THUMBS_DOWN = 'thumb-down', STAR = 'star', WARNING_DIAMOND = 'warning-diamond', RETAIL = 'retail', EMOTICON_HAPPY = 'emoticon-happy', GAMING = 'gaming', SUPPORT = 'support', HEART = 'heart', HOURGLASS = 'hourglass', WORLD = 'world', } export const BentoHeaderWithViewsItemIconElement: Record<BentoHeaderWithViewsItemIcon, VueNodeElement> = { [BentoHeaderWithViewsItemIconEnum.STACK]: StackIcon, [BentoHeaderWithViewsItemIconEnum.LEAF]: LeafIcon, [BentoHeaderWithViewsItemIconEnum.HAND_ROCK]: HandRockIcon, [BentoHeaderWithViewsItemIconEnum.CAR]: CarIcon, [BentoHeaderWithViewsItemIconEnum.EDUCATION]: EducationIcon, [BentoHeaderWithViewsItemIconEnum.HOTEL]: HotelIcon, [BentoHeaderWithViewsItemIconEnum.TRAVEL_BAG]: TravelBagIcon, [BentoHeaderWithViewsItemIconEnum.LIGHTNING]: LightningIcon, [BentoHeaderWithViewsItemIconEnum.NEWS]: NewsIcon, [BentoHeaderWithViewsItemIconEnum.PRIZE]: PrizeIcon, [BentoHeaderWithViewsItemIconEnum.LOCK]: LockIcon, [BentoHeaderWithViewsItemIconEnum.BAG]: BagIcon, [BentoHeaderWithViewsItemIconEnum.DIAMOND]: DiamondIcon, [BentoHeaderWithViewsItemIconEnum.TARGET]: TargetIcon, [BentoHeaderWithViewsItemIconEnum.GIFT]: GiftIcon, [BentoHeaderWithViewsItemIconEnum.CASH_STACK]: CashStackIcon, [BentoHeaderWithViewsItemIconEnum.HASHTAG]: HashtagIcon, [BentoHeaderWithViewsItemIconEnum.CLOUD]: CloudIcon, [BentoHeaderWithViewsItemIconEnum.HOSPITALITY]: HospitalityIcon, [BentoHeaderWithViewsItemIconEnum.SHOPPING_CART]: ShoppingCartIcon, [BentoHeaderWithViewsItemIconEnum.SLEEP]: SleepIcon, [BentoHeaderWithViewsItemIconEnum.NOTIFICATION]: NotificationIcon, [BentoHeaderWithViewsItemIconEnum.THUMBS_UP]: ThumbsUpIcon, [BentoHeaderWithViewsItemIconEnum.THUMBS_DOWN]: ThumbsDownIcon, [BentoHeaderWithViewsItemIconEnum.STAR]: StarIcon, [BentoHeaderWithViewsItemIconEnum.WARNING_DIAMOND]: WarningDiamondIcon, [BentoHeaderWithViewsItemIconEnum.RETAIL]: RetailIcon, [BentoHeaderWithViewsItemIconEnum.EMOTICON_HAPPY]: EmoticonHappyIcon, [BentoHeaderWithViewsItemIconEnum.GAMING]: GamingIcon, [BentoHeaderWithViewsItemIconEnum.SUPPORT]: SupportIcon, [BentoHeaderWithViewsItemIconEnum.HEART]: HeartIcon, [BentoHeaderWithViewsItemIconEnum.HOURGLASS]: HourglassIcon, [BentoHeaderWithViewsItemIconEnum.WORLD]: WorldIcon, }; export interface BentoHeaderWithViewsEmits { /** * Emitted when a new view is added to the list of views. */ (e: 'add:view', value: BentoHeaderWithViewsItemUpdate): void; /** * Emitted when a view is deleted from the list of views. */ (e: 'delete:view', value: BentoHeaderWithViewsItemUpdate): void; /** * Emitted when a view is duplicated. */ (e: 'duplicate:view', value: BentoHeaderWithViewsItemUpdate): void; /** * Emitted when the user reorganizes the order of the views. * No `targetViewLabel` is emitted as the views are reordered as a whole. */ (e: 'reorder:view', value: Omit<BentoHeaderWithViewsItemUpdate, 'targetViewLabel'>): void; /** * Emitted when the user requests to restore a previously saved or default view. */ (e: 'restore:view', value: BentoHeaderWithViewsItem['label']): void; /** * Emitted when the user requests to save the current view. */ (e: 'save:view', value: BentoHeaderWithViewsItem['label']): void; /** * Emitted when the user selects a different view from the available options. */ (e: 'select:view', value: BentoHeaderWithViewsItem['label']): void; /** * Emitted when a property of a view is updated. */ (e: 'update:view', value: BentoHeaderWithViewsItemUpdate): void; /** * Emitted when the user selects the prompt checkbox and closes the modal with a valid button action. * Only works when a string is pass into the `skipUnsavedChangesText` prop. */ (e: 'update:skip-unsaved-changes', value: boolean): void; /** * Emitted when the active view index changes. Used for two-way binding with v-model:active-view-index. */ (e: 'update:active-view-index', value: number | null): void; } export type BentoHeaderWithViewsItemErrors = { /** * Object where the keys correspond to the properties of `BentoHeaderWithViewsItem`, * and the values are strings representing any validation errors for that property. */ [K in keyof BentoHeaderWithViewsItem]: string; };
|
|
1
|
+
import { type BentoButtonActionsList } from '@/components/button/components/button-actions/button-actions.types'; import type { TitleDetailsProps } from '@/internal/title-details/title-details.types'; import type { BentoHeaderWithViewsItem, BentoHeaderWithViewsItemAdditionalConfig, BentoHeaderWithViewsItemIcon, BentoHeaderWithViewsItemIconColor, BentoHeaderWithViewsItemUpdate, } from './header-with-views.internal.types'; export { BentoHeaderWithViewsItem, BentoHeaderWithViewsItemAdditionalConfig, BentoHeaderWithViewsItemIcon, BentoHeaderWithViewsItemIconColor, BentoHeaderWithViewsItemUpdate, }; export interface BentoHeaderWithViewsProps { /** * Optional list of actions (buttons) to display in the header. * The structure of each action is defined by the `BentoButtonActionsList` type. * * {@link [BentoButtonActionsList](https://adyen-bento.netlify.app/?path=/docs/components-button-button-actions--docs#variations)} */ actions?: BentoButtonActionsList; /** * Main title text for the header. This prop is required. */ title: string; /** * Optional array of view items to display as selectable views or tabs. * * Each view should have the following property: * - `icon` (BentoHeaderWithViewsItemIcon, optional): Optional icon to display. * - `iconColor` (BentoHeaderWithViewsItemIconColor, optional): Optional color of the icon. * - `label` (string): Text label for the view item. * - `isDefault` (boolean, optional): Indicates if this view item is the default or currently active view. * - `additionalViewSetting` (BentoHeaderWithViewsItemAdditionalConfig, optional): Optional configuration for an additional view setting. * - `isFixed` (boolean, optional): Indicates if this view can be edited or deleted. * @see {@link BentoHeaderWithViewsItem} */ views?: Array<BentoHeaderWithViewsItem>; /** * Optional configuration for an additional view setting, such as a filter or option. * Defined by the `BentoHeaderWithViewsItemAdditionalConfig` interface. * * @see {@link BentoHeaderWithViewsItemAdditionalConfig} */ additionalViewSetting?: BentoHeaderWithViewsItemAdditionalConfig; /** * Boolean indicating whether there are unsaved changes on the page. * This might be used to visually indicate to the user that they need to save their work. */ hasChanges: boolean; /** * String indicating whether to display a prompt, reminding the user to save their changes whenever they switch view tabs. * If null, no prompt is shown and changes will always be restored to it's previous settings on view tab change. */ skipUnsavedChangesText: string | null; /** * Boolean indicating whether icon and icon selection is allowed on view creation and edit. */ disableCustomization?: boolean; /** * Text showed in the info-icon tooltip on hover */ infoIconTooltipText?: string; /** * Meta description for the header component */ description?: string; /** * JavaScript "Date" object for when the content was last updated */ lastUpdated?: Date | number; /** * Reload button which shows next to the header's timestamp * @see TitleDetailsProps['reloadButton'] */ reloadButton?: TitleDetailsProps['reloadButton']; /** * Whether or not to automatically select a view on mount */ disableAutoSelect?: boolean; /** * The active view index. Supports two-way binding via v-model:active-view-index. * When provided, this takes precedence over `isDefault` on individual views for initial selection. * Changes from outside the component will update the active view. * Has no effect when `disableAutoSelect` is true. */ activeViewIndex?: number; }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<template> <div ref="hostRef" class="b-header-with-views"> <div class="b-header-with-views__container" :class="conditionalContainerClass"> <div class="b-header-with-views__title"> <bento-typography el="h2" variant="title">{{ title }}</bento-typography> <info-icon-with-popover v-if="infoIconTooltipText || hasSlot('infoIconTooltipContent')" :popover-text="infoIconTooltipText" class="b-header-with-views__title-icon" > <template v-if="hasSlot('infoIconTooltipContent')" #default> <slot name="infoIconTooltipContent" /> </template> </info-icon-with-popover> </div> <fieldset ref="viewsRef" class="b-header-with-views__bar" :aria-label="t('viewListLabel')"> <div v-for="(view, index) in computedViews" :key="index" ref="viewRefs" class="b-header-with-views__bar-tab" :class="isViewActive(index) ? 'b-header-with-views__bar-tab--active' : null" > <bento-button class="b-header-with-views__bar-tab-button" variant="tertiary-with-background" :aria-current="currentActiveView === index ? 'true' : 'false'" @click="handleViewSelect(index)" @keydown.enter.native="handleViewSelect(index)" @keydown.space.native="handleViewSelect(index)" > <template #iconLeft> <span class="b-header-with-views__bar-tab-button-icon" :class="`b-header-with-views__bar-tab-button-icon--${view.iconColor}`" aria-hidden="true" > <component :is="BentoHeaderWithViewsItemIconElement[view.icon]" /> </span> </template> <bento-typography el="span" variant="body" stronger class="b-header-with-views__bar-tab-button-text" > {{ view.label }} </bento-typography> <template v-if="isViewUnsaved(index)" #iconRight> <span class="b-header-with-views__bar-tab-button--unsaved"> <dot-icon :svg-title="t('withUnsavedChanges')" /> </span> </template> </bento-button> <bento-menu v-if="currentActiveView === index" ref="editViewMenuRefs" class="b-header-with-views__bar-tab-menu" :button="{ variant: 'tertiary' }" :data="getViewMenuData(index)" :close-on-click-outside-options="{ ignore: [`#${editViewPopoverId}`], }" :menu-fixed-positioning="true" @open="showViewMenuPopover(index)" @close="hideViewMenuPopover()" @keydown.tab.native.capture="handleTabEditViewPopover" > <template #iconLeft> <options-horizontal-icon :svg-title="t('openViewOptions', { label: view.label })" /> </template> </bento-menu> </div> <div ref="viewControlRef" class="b-header-with-views__bar-control"> <!-- All views --> <header-with-views-all-views v-if="computedViews.length" ref="viewControlAllViewsRef" :menu-items="computedViews" :menu-items-actions="getViewMenuData(activeViewMenuPopover)" :menu-close-on-click-outside-options="{ ignore: [`#${editViewPopoverId}`] }" @close:view-menu="hideViewMenuPopover()" @close:all-views="showAllViewsPopover = false" @open:all-views="showAllViewsPopover = true" @open:view-menu="index => showViewMenuPopover(index)" @reorder:view="views => handleReorder(views)" @select:view="index => handleViewSelect(index)" /> <bento-toggle-button ref="createViewButtonRef" :aria-expanded="showCreateViewPopover" :aria-controls="showCreateViewPopover ? createViewPopoverId : null" :toggled="showCreateViewPopover" variant="tertiary" @click="showCreateViewPopover = !showCreateViewPopover" > <template #iconLeft> <plus-icon :svg-title="t('createView')" :aria-hidden="computedViews.length === 0" /> </template> <template v-if="computedViews.length === 0" #default>{{ t('createView') }}</template> </bento-toggle-button> </div> </fieldset> <header-meta v-if="hasMetaSection" class="b-header-with-views__meta-container" :description="description" :last-updated="lastUpdated" :reload-button="reloadButton" > <template v-if="hasSlot('description')" #description><slot name="description" /></template> </header-meta> <button-actions-with-menu v-if="actions" :actions="actions" :actions-layout="isCompact ? 'buttons-start' : 'buttons-end'" :displayed-actions="2" :disable-responsive-behavior="true" :hide-displayed-actions-breakpoint="STACKED_LAYOUT_BREAKPOINT" class="b-header-with-views__actions" /> </div> <!-- Create view --> <bento-popover v-if="activeViewMenuPopover === null && createViewButtonRef" :id="createViewPopoverId" ref="createViewPopoverRef" :dismissible="true" :fixed-positioning="true" :target-element="createViewButtonRef" :title="t('createView')" :open="showCreateViewPopover" :overflow-visible="true" position="bottom-start" @dismiss="hideCreateViewPopover" @keydown.esc.native.capture.stop.prevent="hideCreateViewPopover" > <header-with-views-edit-form context="create" :errors="errors" :additional-form-item="additionalViewSetting" :disable-customization="disableCustomization" @blur:label="value => validateLabel(value, activeViewMenuPopover)" @input:label="value => validateLabel(value, activeViewMenuPopover)" @keydown:enter="handleKeydownEnterViewItem" @update="handleCreateViewItem" @cancel="hideCreateViewPopover" > </header-with-views-edit-form> </bento-popover> <!-- Edit view --> <bento-popover v-if="activeViewMenuPopover !== null && editViewTargetElementRef" :id="editViewPopoverId" ref="editViewPopoverRef" :disable-focus-trap="false" :dismissible="true" :fixed-positioning="true" :target-element="editViewTargetElementRef" :title="t('editDetails')" :open="activeEditViewPopover !== null" :overflow-visible="true" position="right-start" @dismiss="setActiveEditViewPopover()" @keydown.esc.native.capture.stop.prevent="setActiveEditViewPopover()" > <header-with-views-edit-form v-if="computedViews[activeEditViewPopover]" context="edit" :additional-form-item="computedViews[activeEditViewPopover].additionalViewSetting" :errors="errors" :icon="computedViews[activeEditViewPopover].icon" :icon-color="computedViews[activeEditViewPopover].iconColor" :label="computedViews[activeEditViewPopover].label" :disable-customization="disableCustomization" @blur:label="value => validateLabel(value, activeViewMenuPopover)" @input:label="value => validateLabel(value, activeViewMenuPopover)" @keydown:enter="handleKeydownEnterViewItem" @update="handleUpdateViewItem" @cancel="hideEditViewPopover" > </header-with-views-edit-form> </bento-popover> <!-- Unsaved views prompt --> <bento-modal v-if="showUnsavedModalWarning" :actions="computedUnsavedViewModalActions" :is-dismissible="false" :is-open="showUnsavedModalWarning" @close-modal="showUnsavedModalWarning = false" > <template #default>{{ t('unsavedChangesTitle', { label: computedViews[currentActiveView].label }) }}</template> <template #content> <div class="b-header-with-views__modal-description"> <bento-typography>{{ t('unsavedChangesDescription') }}</bento-typography> <bento-checkbox v-if="skipUnsavedChangesText" :model-value="skipUnsavedChangesReminder" @update:model-value="value => (skipUnsavedChangesReminder = value)" > {{ skipUnsavedChangesText }} </bento-checkbox> </div> </template> </bento-modal> <!-- Delete views prompt --> <bento-modal v-if="showDeleteModalWarning" :actions="computedDeleteViewModalActions" :is-open="showDeleteModalWarning" :is-dismissible="false" destructive-actions @click.native.capture="handleClickModal" @close-modal="showDeleteModalWarning = false" > <template #default> {{ t('deleteViewTitle', { label: computedViewToBeDeletedLabel }) }} </template> <template #content> <div class="b-header-with-views__modal-description"> <bento-typography>{{ t('deleteViewDescription', { label: computedViewToBeDeletedLabel }) }}</bento-typography> </div> </template> </bento-modal> </div> </template> <script setup lang="ts"> import { BentoButton, BentoToggleButton } from '@/components/button'; import { BentoCheckbox } from '@/components/checkbox'; import { BentoMenu, type BentoMenuItem } from '@/components/menu'; import BentoModal from '@/components/modal/modal.vue'; import { BentoPopover } from '@/components/popover'; import { BentoTypography } from '@/components/typography'; import { HeaderMeta } from '@/internal'; import { useBentoToastController, useClickOutside, useHasSlot } from '@/composables'; import { generateUid } from '@/core/utils/ts'; import ButtonActionsWithMenu from '@/internal/button-actions-with-menu/button-actions-with-menu.vue'; import { InfoIconWithPopover } from '@/internal/info-icon-with-popover'; import { useI18n } from '@/utils/ts/i18n'; import BinIcon from '@adyen/ui-assets-icons-16/vue/bin'; import DotIcon from '@adyen/ui-assets-icons-16/vue/dot'; import DownloadIcon from '@adyen/ui-assets-icons-16/vue/download'; import EditIcon from '@adyen/ui-assets-icons-16/vue/edit-2'; import HeartIcon from '@adyen/ui-assets-icons-16/vue/heart'; import OptionsHorizontalIcon from '@adyen/ui-assets-icons-16/vue/options-horizontal'; import PlusIcon from '@adyen/ui-assets-icons-16/vue/plus'; import PlusMultipleIcon from '@adyen/ui-assets-icons-16/vue/plus-multiple'; import RefreshIcon from '@adyen/ui-assets-icons-16/vue/refresh'; import { useElementSize } from '@vueuse/core'; import { computed, nextTick, onMounted, ref, useSlots, watch } from 'vue'; import { HeaderWithViewsAllViews, HeaderWithViewsEditForm } from './components'; import { useEditViews, useResponsiveViews } from './composables'; import { type BentoHeaderWithViewsEmits, type BentoHeaderWithViewsItem, BentoHeaderWithViewsItemIconElement, type BentoHeaderWithViewsProps, } from './header-with-views.types'; import messages from './messages.json'; type MessageSchema = (typeof messages)['en-US']; const props = withDefaults(defineProps<BentoHeaderWithViewsProps>(), { actions: () => [], additionalViewSetting: null, title: null, description: null, disableCustomization: false, lastUpdated: null, reloadButton: null, infoIconTooltipText: null, views: () => [], disableAutoSelect: false, activeViewIndex: undefined, }); const emit = defineEmits<BentoHeaderWithViewsEmits>(); const STACKED_LAYOUT_BREAKPOINT = 573; const { t } = useI18n<{ message: MessageSchema }>({ messages }); const slots = useSlots(); const hasSlot = useHasSlot(slots); // refs const createViewButtonRef = ref(null); const createViewPopoverRef = ref(null); const editViewPopoverRef = ref(null); const editViewMenuRefs = ref([]); const editViewTargetElementRef = ref(null); // used as a mount for the edit form popover const hostRef = ref(null); const viewsRef = ref<HTMLElement>(null); // container for all views const viewRefs = ref<Array<HTMLDivElement>>([]); // individual view const viewControlRef = ref(null); // all views menu and create view button container const viewControlAllViewsRef = ref(null); // all views menu // controls const showCreateViewPopover = ref<boolean>(false); // control the create view popover const showAllViewsPopover = ref<boolean>(false); // control when menu button is shown const showUnsavedModalWarning = ref<boolean>(false); const showDeleteModalWarning = ref<boolean>(false); const activeEditViewPopover = ref<number>(null); // control the edit view popover const activeViewMenuPopover = ref<number>(null); // control when menu items are shown const internalActiveView = ref<number>(null); // control which view the user is currently on const nextActiveView = ref<number>(null); // control which view is does the user want to navigate to, used for caching unsaved view navigation const viewToBeDeleted = ref<number>(null); // cache which view is marked for deletion // Computed property to handle two-way binding for activeViewIndex const currentActiveView = computed({ get: () => internalActiveView.value, set: (value: number | null) => { internalActiveView.value = value; emit('update:active-view-index', value); }, }); // models const skipUnsavedChangesReminder = ref<boolean>(false); const createViewPopoverId = generateUid('createViewPopover'); const editViewPopoverId = generateUid('editViewPopover'); useResponsiveViews({ container: viewsRef, children: viewRefs, offset: viewControlRef, activeViewIndex: currentActiveView, }); const { computedViews, clearError, duplicateView, errors, setViews, updateView, validateLabel } = useEditViews({ additionalViewSetting: props.additionalViewSetting, views: props.views, }); const hideCreateViewPopover = async (updateFocus = true) => { showCreateViewPopover.value = false; if (updateFocus) { await nextTick(); createViewButtonRef.value.$el.focus(); } clearError(); }; const hideEditViewPopover = () => { const currentMenuRef = showAllViewsPopover.value ? viewControlAllViewsRef.value.$refs.editViewMenuRefs[activeEditViewPopover.value] : editViewMenuRefs.value[0]; currentMenuRef?.closeMenu(); }; useClickOutside(createViewPopoverRef, () => hideCreateViewPopover(false), { ignore: [createViewButtonRef], }); useClickOutside(editViewPopoverRef, () => setActiveEditViewPopover(), { ignore: [editViewTargetElementRef], }); const { width: hostWidth } = useElementSize(hostRef); const { addToast } = useBentoToastController(); const computedUnsavedViewModalActions = computed(() => [ { title: t('saveChanges'), event: handleSaveChanges, }, { title: t('leave'), event: handleRestoreChanges, }, ]); const computedDeleteViewModalActions = computed(() => [ { title: t('deleteViewAction'), event: async () => { showDeleteModalWarning.value = false; const updatedViewObj = updateView(null, viewToBeDeleted.value); emit('delete:view', updatedViewObj); hideViewMenuPopover(); // if a view that is placed higher in the order is deleted, ignore if (currentActiveView.value < viewToBeDeleted.value) { return; } // if a view that is placed lower in the order is deleted, // we need to shift the index to maintain the current view if (currentActiveView.value > viewToBeDeleted.value) { currentActiveView.value -= 1; } else if (currentActiveView.value === viewToBeDeleted.value) { if (updatedViewObj.newViews.length === 0) { // there are no more views currentActiveView.value = null; } else if (currentActiveView.value >= updatedViewObj.newViews.length) { // shift it to the left currentActiveView.value -= 1; } // else, keep it at the same index } viewToBeDeleted.value = null; }, }, { title: t('cancel'), event: () => { showDeleteModalWarning.value = false; }, }, ]); const computedViewToBeDeletedLabel = computed(() => computedViews.value[viewToBeDeleted.value]?.label); const hasMetaSection = computed(() => props.description || hasSlot('description') || props.lastUpdated); const isCompact = computed(() => hostWidth.value <= STACKED_LAYOUT_BREAKPOINT); const conditionalContainerClass = computed(() => ({ 'b-header-with-views__container--is-compact': isCompact.value, 'b-header-with-views__container--has-meta': hasMetaSection.value, })); watch([currentActiveView, computedViews], ([newIndex, newViews], [oldIndex, oldViews]) => { // skip if the labels are the same or no active view const isViewUpdated = newIndex === oldIndex && newViews.length === oldViews.length; const isViewDeletedWithChangedIndex = newViews[newIndex]?.label === oldViews[oldIndex]?.label; if (isViewUpdated || isViewDeletedWithChangedIndex) { return; } // allow empty label // occurs when the user removes all views emit('select:view', newViews[newIndex]?.label ?? null); }); // whenever the view menu popover changes, we reset the edit popover because it is never opened by default watch(activeViewMenuPopover, () => { setActiveEditViewPopover(); }); watch(showAllViewsPopover, value => { if (value) { return; } // if show all view popover is closed, close children as well hideViewMenuPopover(); }); // set the popover to open next to the edit button watch(activeEditViewPopover, value => { const editButtonIndex = isViewUnsaved(value) ? 2 : 0; // EDGE CASE: // Since edit button can be clicked while in all views popover or while it is in the bar, // we will not support the case where edit popover is opened and the screen is resize until the view menu is hidden. // Edit popover will still be displayed but the position does not have to follow the edit button as it is hidden. if (showAllViewsPopover.value) { const menuListItemRef = viewControlAllViewsRef.value.$refs.editViewMenuRefs[activeViewMenuPopover.value]?.$refs ?.menuItemListRef; editViewTargetElementRef.value = viewControlAllViewsRef.value.$refs.editViewMenuRefs[activeViewMenuPopover.value]?.$refs?.menuItemListRef ?.$refs?.menuItemRef?.[editButtonIndex] ?? null; if (!value) { menuListItemRef?.focusItem(); clearError(); } return; } const menuListItemRef = editViewMenuRefs.value.find(ref => ref.$refs.menuItemListRef)?.$refs?.menuItemListRef; editViewTargetElementRef.value = menuListItemRef?.$refs?.menuItemRef?.[editButtonIndex] ?? null; if (!value) { menuListItemRef?.focusItem(); clearError(); } }); // Watch for external changes to the activeViewIndex prop watch( () => props.activeViewIndex, newValue => { if (newValue !== undefined && newValue >= 0 && newValue < computedViews.value.length) { currentActiveView.value = newValue; } } ); onMounted(() => { if ( props.activeViewIndex !== undefined && props.activeViewIndex >= 0 && props.activeViewIndex < computedViews.value.length ) { currentActiveView.value = props.activeViewIndex; } else if (!props.disableAutoSelect) { const defaultView = computedViews.value.findIndex(view => view.isDefault); currentActiveView.value = defaultView >= 0 ? defaultView : 0; } }); const getViewMenuData = (index: number): Array<BentoMenuItem> => [ ...(isViewUnsaved(index) ? [ { text: t('saveChanges'), handler: handleSaveChanges, icon: DownloadIcon, }, { addDivider: true, text: t('restoreChanges'), handler: handleRestoreChanges, icon: RefreshIcon, }, ] : []), ...(!computedViews.value[index]?.isFixed ? [ { text: t('edit'), handler: () => { setActiveEditViewPopover(activeViewMenuPopover.value); }, icon: EditIcon, skipClose: true, }, ] : []), { text: t('setAsDefault'), handler: () => { const updatedViewObj = updateView({ isDefault: true }, activeViewMenuPopover.value); emit('update:view', updatedViewObj); addToast({ text: t('setAsDefaultToast', { label: updatedViewObj.targetViewLabel }), }); hideViewMenuPopover(); }, icon: HeartIcon, }, { text: t('duplicate'), handler: () => { const updatedViewObj = duplicateView(activeViewMenuPopover.value); emit('duplicate:view', updatedViewObj); hideViewMenuPopover(); // switch to newly duplicated view handleViewSelect(updatedViewObj.newViews.length - 1); }, icon: PlusMultipleIcon, }, ...(!computedViews.value[index]?.isFixed ? [ { text: t('delete'), handler: () => { viewToBeDeleted.value = activeViewMenuPopover.value; showDeleteModalWarning.value = true; }, critical: true, icon: BinIcon, }, ] : []), ]; const showViewMenuPopover = (index: number) => { activeViewMenuPopover.value = index; }; const hideViewMenuPopover = () => { activeViewMenuPopover.value = null; setActiveEditViewPopover(); }; const setActiveEditViewPopover = (index?: number) => { if (index === activeEditViewPopover.value) { activeEditViewPopover.value = null; } else { activeEditViewPopover.value = index ?? null; } }; const handleCreateViewItem = (value: BentoHeaderWithViewsItem) => { const updatedViewObj = updateView(value); emit('add:view', updatedViewObj); hideCreateViewPopover(); // switch to new view on add handleViewSelect(updatedViewObj.newViews.findIndex(view => updatedViewObj.targetViewLabel === view.label)); addToast({ text: t('createViewToast', { label: updatedViewObj.targetViewLabel }), }); }; const handleUpdateViewItem = (value: BentoHeaderWithViewsItem) => { emit('update:view', updateView(value, activeEditViewPopover.value)); hideEditViewPopover(); }; const handleKeydownEnterViewItem = (value: BentoHeaderWithViewsItem) => { validateLabel(value.label, activeViewMenuPopover.value); if (Object.values(errors).some(error => error.length > 0)) { return; } if (activeEditViewPopover.value) { handleUpdateViewItem(value); return; } handleCreateViewItem(value); }; const handleViewSelect = (index: number) => { // If there's no currently active view, or if there are no unsaved changes, // just switch to the new view. if (currentActiveView.value === null || !props.hasChanges || props.skipUnsavedChangesText === null) { // If switching from an existing view, emit restore event. if (currentActiveView.value !== null) { emit('restore:view', computedViews.value[currentActiveView.value].label); } currentActiveView.value = index; return; } nextActiveView.value = index; showUnsavedModalWarning.value = true; }; const handleReorder = (reorderedViews: Array<BentoHeaderWithViewsItem>) => { const currentActiveLabel = currentActiveView.value !== null ? computedViews.value[currentActiveView.value]?.label : null; const result = setViews(reorderedViews); if (currentActiveLabel) { const newIndex = result.newViews.findIndex(view => view.label === currentActiveLabel); if (newIndex !== -1) { currentActiveView.value = newIndex; } } emit('reorder:view', result); }; const handleClickModal = (event: MouseEvent) => { if ((event.target as HTMLElement).closest('button')) { return; } event.stopPropagation(); event.preventDefault(); }; const handleTabEditViewPopover = () => { // skip built-in refocus on the menu button when menu is closed // enables tab to flow as intended by HTML spec editViewMenuRefs.value[0].closeMenu(false); }; const handleSaveChanges = () => { const label = computedViews.value[currentActiveView.value].label; emit('save:view', label); if (showUnsavedModalWarning.value) { emit('update:skip-unsaved-changes', skipUnsavedChangesReminder.value); // fired when user saves the view while navigating to a different tab currentActiveView.value = nextActiveView.value; nextActiveView.value = null; showUnsavedModalWarning.value = false; } else { // fired when user saves the view via the menu hideViewMenuPopover(); } addToast({ text: t('saveChangesToast', { label }), action: { handler: () => emit('restore:view', label), text: t('undo'), }, }); }; const handleRestoreChanges = () => { emit('restore:view', computedViews.value[currentActiveView.value].label); if (showUnsavedModalWarning.value) { emit('update:skip-unsaved-changes', skipUnsavedChangesReminder.value); // fired when user restore the view while navigating to a different tab currentActiveView.value = nextActiveView.value; nextActiveView.value = null; showUnsavedModalWarning.value = false; } else { // fired when user restore the view via the menu hideViewMenuPopover(); } }; const isViewActive = (index: number) => currentActiveView.value === index; const isViewUnsaved = (index: number) => isViewActive(index) && props.hasChanges; </script> <script lang="ts"> /** * Header with views allow users to save and quickly retrieve configurations tailored to specific tasks. * For example, managing daily transactions, auditing, or analyzing specific data sets. * It also allows the user to define the columns, filters, and search queries they need. * * @example * import { BentoHeaderWithViews } from '@adyen/bento-vue2'; * * export default { * components: { BentoHeaderWithViews }, * template: ` * <bento-header-with-views * :actions="[ * { title: 'Primary action', event: () => undefined }, * { title: 'Secondary action', event: () => undefined }, * { title: 'Tertiary action', event: () => undefined }, * { title: 'Another action', event: () => undefined } * ]" * description="Page Description" * title="Page Title" * :views="[ * { icon: 'stack', iconColor: 'blue', label: 'A very long value' }, * { icon: 'stack', iconColor: 'primary', label: 'Title 1' }, * { icon: 'stack', iconColor: 'primary', label: 'Title 2' } * ]" * :additionalViewSetting="{ * label: 'Include date filter (relative)', * value: false * }" * hasChanges * /> * ` * }; */ export default { i18n: { messages }, }; </script> <style lang="scss" scoped src="./header-with-views.scss" />
|
|
1
|
+
<template> <div ref="hostRef" class="b-header-with-views"> <div class="b-header-with-views__container" :class="conditionalContainerClass"> <div class="b-header-with-views__title"> <bento-typography el="h2" variant="title">{{ title }}</bento-typography> <info-icon-with-popover v-if="infoIconTooltipText || hasSlot('infoIconTooltipContent')" :popover-text="infoIconTooltipText" class="b-header-with-views__title-icon" > <template v-if="hasSlot('infoIconTooltipContent')" #default> <slot name="infoIconTooltipContent" /> </template> </info-icon-with-popover> </div> <fieldset ref="viewsRef" class="b-header-with-views__bar" :aria-label="t('viewListLabel')"> <div v-for="(view, index) in computedViews" :key="index" ref="viewRefs" class="b-header-with-views__bar-tab" :class="isViewActive(index) ? 'b-header-with-views__bar-tab--active' : null" > <bento-button class="b-header-with-views__bar-tab-button" variant="tertiary-with-background" :aria-current="currentActiveView === index ? 'true' : 'false'" @click="handleViewSelect(index)" @keydown.enter.native="handleViewSelect(index)" @keydown.space.native="handleViewSelect(index)" > <template #iconLeft> <span class="b-header-with-views__bar-tab-button-icon" :class="`b-header-with-views__bar-tab-button-icon--${view.iconColor}`" aria-hidden="true" > <component :is="BentoHeaderWithViewsItemIconElement[view.icon]" /> </span> </template> <bento-typography el="span" variant="body" stronger class="b-header-with-views__bar-tab-button-text" > {{ view.label }} </bento-typography> <template v-if="isViewUnsaved(index)" #iconRight> <span class="b-header-with-views__bar-tab-button--unsaved"> <dot-icon :svg-title="t('withUnsavedChanges')" /> </span> </template> </bento-button> <bento-menu v-if="currentActiveView === index" ref="editViewMenuRefs" class="b-header-with-views__bar-tab-menu" :button="{ variant: 'tertiary', 'aria-label': t('openViewOptions', { label: view.label }) }" :data="getViewMenuData(index)" :close-on-click-outside-options="{ ignore: [`#${editViewPopoverId}`], }" :menu-fixed-positioning="true" @open="showViewMenuPopover(index)" @close="hideViewMenuPopover()" @keydown.tab.native.capture="handleTabEditViewPopover" > <template #iconLeft> <options-horizontal-icon :aria-hidden="true" /> </template> </bento-menu> </div> <div ref="viewControlRef" class="b-header-with-views__bar-control"> <!-- All views --> <header-with-views-all-views v-if="computedViews.length" ref="viewControlAllViewsRef" :menu-items="computedViews" :menu-items-actions="getViewMenuData(activeViewMenuPopover)" :menu-close-on-click-outside-options="{ ignore: [`#${editViewPopoverId}`] }" @close:view-menu="hideViewMenuPopover()" @close:all-views="showAllViewsPopover = false" @open:all-views="showAllViewsPopover = true" @open:view-menu="index => showViewMenuPopover(index)" @reorder:view="views => handleReorder(views)" @select:view="index => handleViewSelect(index)" /> <bento-toggle-button ref="createViewButtonRef" :aria-expanded="showCreateViewPopover" :aria-controls="showCreateViewPopover ? createViewPopoverId : null" :toggled="showCreateViewPopover" variant="tertiary" @click="showCreateViewPopover = !showCreateViewPopover" > <template #iconLeft> <plus-icon :svg-title="t('createView')" :aria-hidden="computedViews.length === 0" /> </template> <template v-if="computedViews.length === 0" #default>{{ t('createView') }}</template> </bento-toggle-button> </div> </fieldset> <title-details v-if="hasMetaSection" class="b-header-with-views__meta-container" :description="description" :last-updated="lastUpdated" :reload-button="reloadButton" > <template v-if="hasSlot('description')" #description><slot name="description" /></template> </title-details> <button-actions-with-menu v-if="actions" :actions="actions" :actions-layout="isCompact ? 'buttons-start' : 'buttons-end'" :displayed-actions="2" :disable-responsive-behavior="true" :hide-displayed-actions-breakpoint="STACKED_LAYOUT_BREAKPOINT" class="b-header-with-views__actions" /> </div> <!-- Create view --> <bento-popover v-if="activeViewMenuPopover === null && createViewButtonRef" :id="createViewPopoverId" ref="createViewPopoverRef" :dismissible="true" :fixed-positioning="true" :target-element="createViewButtonRef" :title="t('createView')" :open="showCreateViewPopover" :overflow-visible="true" position="bottom-start" @dismiss="hideCreateViewPopover" @keydown.esc.native.capture.stop.prevent="hideCreateViewPopover" > <header-with-views-edit-form context="create" :errors="errors" :additional-form-item="additionalViewSetting" :disable-customization="disableCustomization" @blur:label="value => validateLabel(value, activeViewMenuPopover)" @input:label="value => validateLabel(value, activeViewMenuPopover)" @keydown:enter="handleKeydownEnterViewItem" @update="handleCreateViewItem" @cancel="hideCreateViewPopover" > </header-with-views-edit-form> </bento-popover> <!-- Edit view --> <bento-popover v-if="activeViewMenuPopover !== null && editViewTargetElementRef" :id="editViewPopoverId" ref="editViewPopoverRef" :disable-focus-trap="false" :dismissible="true" :fixed-positioning="true" :target-element="editViewTargetElementRef" :title="t('editDetails')" :open="activeEditViewPopover !== null" :overflow-visible="true" position="right-start" @dismiss="setActiveEditViewPopover()" @keydown.esc.native.capture.stop.prevent="setActiveEditViewPopover()" > <header-with-views-edit-form v-if="computedViews[activeEditViewPopover]" context="edit" :additional-form-item="computedViews[activeEditViewPopover].additionalViewSetting" :errors="errors" :icon="computedViews[activeEditViewPopover].icon" :icon-color="computedViews[activeEditViewPopover].iconColor" :label="computedViews[activeEditViewPopover].label" :disable-customization="disableCustomization" @blur:label="value => validateLabel(value, activeViewMenuPopover)" @input:label="value => validateLabel(value, activeViewMenuPopover)" @keydown:enter="handleKeydownEnterViewItem" @update="handleUpdateViewItem" @cancel="hideEditViewPopover" > </header-with-views-edit-form> </bento-popover> <!-- Unsaved views prompt --> <bento-modal :actions="computedUnsavedViewModalActions" :is-dismissible="false" :is-open="showUnsavedModalWarning" @close-modal="showUnsavedModalWarning = false" > <template #default>{{ t('leaveView') }}</template> <template #content> <div class="b-header-with-views__modal-description"> <bento-typography> <component :is="i18nComponent" v-bind="getI18nProps('leaveViewDescription')" tag="span"> <template #label> <bento-typography strongest el="span">{{ computedViews[currentActiveView]?.label }}</bento-typography> </template> </component> </bento-typography> <bento-checkbox v-if="skipUnsavedChangesText" :model-value="skipUnsavedChangesReminder" @update:model-value="value => (skipUnsavedChangesReminder = value)" > {{ skipUnsavedChangesText }} </bento-checkbox> </div> </template> </bento-modal> <!-- Delete views prompt --> <bento-modal :actions="computedDeleteViewModalActions" :is-open="showDeleteModalWarning" :is-dismissible="false" destructive-actions @click.native.capture="handleClickModal" @close-modal="showDeleteModalWarning = false" > <template #default> {{ t('deleteView') }} </template> <template #content> <div class="b-header-with-views__modal-description"> <bento-typography> <component :is="i18nComponent" v-bind="getI18nProps('deleteViewDescription')" tag="span"> <template #label> <bento-typography strongest el="span">{{ computedViewToBeDeletedLabel }}</bento-typography> </template> </component> </bento-typography> </div> </template> </bento-modal> </div> </template> <script setup lang="ts"> import { BentoButton, BentoToggleButton } from '@/components/button'; import { BentoCheckbox } from '@/components/checkbox'; import { BentoMenu, type BentoMenuItem } from '@/components/menu'; import BentoModal from '@/components/modal/modal.vue'; import { BentoPopover } from '@/components/popover'; import { BentoTypography } from '@/components/typography'; import { TitleDetails } from '@/internal'; import { useBentoToastController, useClickOutside, useHasSlot } from '@/composables'; import { generateUid } from '@/utils/ts/generate-uid'; import ButtonActionsWithMenu from '@/internal/button-actions-with-menu/button-actions-with-menu.vue'; import { InfoIconWithPopover } from '@/internal/info-icon-with-popover'; import { getI18nComponent, getI18nProps, useI18n } from '@/utils/ts/i18n'; import BinIcon from '@adyen/ui-assets-icons-16/vue/bin'; import DotIcon from '@adyen/ui-assets-icons-16/vue/dot'; import DownloadIcon from '@adyen/ui-assets-icons-16/vue/download'; import EditIcon from '@adyen/ui-assets-icons-16/vue/edit-2'; import HeartIcon from '@adyen/ui-assets-icons-16/vue/heart'; import OptionsHorizontalIcon from '@adyen/ui-assets-icons-16/vue/options-horizontal'; import PlusIcon from '@adyen/ui-assets-icons-16/vue/plus'; import PlusMultipleIcon from '@adyen/ui-assets-icons-16/vue/plus-multiple'; import RefreshIcon from '@adyen/ui-assets-icons-16/vue/refresh'; import { useElementSize } from '@vueuse/core'; import { computed, nextTick, onMounted, ref, useSlots, watch } from 'vue'; import { HeaderWithViewsAllViews, HeaderWithViewsEditForm } from './components'; import { useEditViews, useResponsiveViews } from './composables'; import { type BentoHeaderWithViewsEmits, BentoHeaderWithViewsItemIconElement, } from './header-with-views.internal.types'; import { type BentoHeaderWithViewsItem, type BentoHeaderWithViewsProps } from './header-with-views.types'; import messages from './messages.json'; type MessageSchema = (typeof messages)['en-US']; const props = withDefaults(defineProps<BentoHeaderWithViewsProps>(), { actions: () => [], additionalViewSetting: null, title: null, description: null, disableCustomization: false, lastUpdated: null, reloadButton: null, infoIconTooltipText: null, views: () => [], disableAutoSelect: false, activeViewIndex: undefined, }); const emit = defineEmits<BentoHeaderWithViewsEmits>(); const STACKED_LAYOUT_BREAKPOINT = 573; const i18nComponent = getI18nComponent(); const { t } = useI18n<{ message: MessageSchema }>({ messages }); const slots = useSlots(); const hasSlot = useHasSlot(slots); // refs const createViewButtonRef = ref(null); const createViewPopoverRef = ref(null); const editViewPopoverRef = ref(null); const editViewMenuRefs = ref([]); const editViewTargetElementRef = ref(null); // used as a mount for the edit form popover const hostRef = ref(null); const viewsRef = ref<HTMLElement>(null); // container for all views const viewRefs = ref<Array<HTMLDivElement>>([]); // individual view const viewControlRef = ref(null); // all views menu and create view button container const viewControlAllViewsRef = ref(null); // all views menu // controls const showCreateViewPopover = ref<boolean>(false); // control the create view popover const showAllViewsPopover = ref<boolean>(false); // control when menu button is shown const showUnsavedModalWarning = ref<boolean>(false); const showDeleteModalWarning = ref<boolean>(false); const activeEditViewPopover = ref<number>(null); // control the edit view popover const activeViewMenuPopover = ref<number>(null); // control when menu items are shown const internalActiveView = ref<number>(null); // control which view the user is currently on const nextActiveView = ref<number>(null); // control which view is does the user want to navigate to, used for caching unsaved view navigation const viewToBeDeleted = ref<number>(null); // cache which view is marked for deletion // Computed property to handle two-way binding for activeViewIndex const currentActiveView = computed({ get: () => internalActiveView.value, set: (value: number | null) => { internalActiveView.value = value; emit('update:active-view-index', value); }, }); // models const skipUnsavedChangesReminder = ref<boolean>(false); const createViewPopoverId = generateUid('createViewPopover'); const editViewPopoverId = generateUid('editViewPopover'); useResponsiveViews({ container: viewsRef, children: viewRefs, offset: viewControlRef, activeViewIndex: currentActiveView, }); const { computedViews, clearError, duplicateView, errors, setViews, updateView, validateLabel } = useEditViews({ additionalViewSetting: props.additionalViewSetting, views: props.views, }); const hideCreateViewPopover = async (updateFocus = true) => { showCreateViewPopover.value = false; if (updateFocus) { await nextTick(); createViewButtonRef.value.$el.focus(); } clearError(); }; const hideEditViewPopover = () => { const currentMenuRef = showAllViewsPopover.value ? viewControlAllViewsRef.value.$refs.editViewMenuRefs[activeEditViewPopover.value] : editViewMenuRefs.value[0]; currentMenuRef?.closeMenu(); }; useClickOutside(createViewPopoverRef, () => hideCreateViewPopover(false), { ignore: [createViewButtonRef], }); useClickOutside(editViewPopoverRef, () => setActiveEditViewPopover(), { ignore: [editViewTargetElementRef], }); const { width: hostWidth } = useElementSize(hostRef); const { addToast } = useBentoToastController(); const computedUnsavedViewModalActions = computed(() => [ { title: t('leave'), event: handleRestoreChanges, }, { title: t('saveChanges'), event: handleSaveChanges, }, ]); const computedDeleteViewModalActions = computed(() => [ { title: t('delete'), event: async () => { showDeleteModalWarning.value = false; const updatedViewObj = updateView(null, viewToBeDeleted.value); emit('delete:view', updatedViewObj); hideViewMenuPopover(); // if a view that is placed higher in the order is deleted, ignore if (currentActiveView.value < viewToBeDeleted.value) { return; } // if a view that is placed lower in the order is deleted, // we need to shift the index to maintain the current view if (currentActiveView.value > viewToBeDeleted.value) { currentActiveView.value -= 1; } else if (currentActiveView.value === viewToBeDeleted.value) { if (updatedViewObj.newViews.length === 0) { // there are no more views currentActiveView.value = null; } else if (currentActiveView.value >= updatedViewObj.newViews.length) { // shift it to the left currentActiveView.value -= 1; } // else, keep it at the same index } viewToBeDeleted.value = null; }, }, { title: t('cancel'), event: () => { showDeleteModalWarning.value = false; }, }, ]); const computedViewToBeDeletedLabel = computed(() => computedViews.value[viewToBeDeleted.value]?.label); const hasMetaSection = computed(() => props.description || hasSlot('description') || props.lastUpdated); const isCompact = computed(() => hostWidth.value <= STACKED_LAYOUT_BREAKPOINT); const conditionalContainerClass = computed(() => ({ 'b-header-with-views__container--is-compact': isCompact.value, 'b-header-with-views__container--has-meta': hasMetaSection.value, })); watch([currentActiveView, computedViews], ([newIndex, newViews], [oldIndex, oldViews]) => { // skip if the labels are the same or no active view const isViewUpdated = newIndex === oldIndex && newViews.length === oldViews.length; const isViewDeletedWithChangedIndex = newViews[newIndex]?.label === oldViews[oldIndex]?.label; if (isViewUpdated || isViewDeletedWithChangedIndex) { return; } // allow empty label // occurs when the user removes all views emit('select:view', newViews[newIndex]?.label ?? null); }); // whenever the view menu popover changes, we reset the edit popover because it is never opened by default watch(activeViewMenuPopover, () => { setActiveEditViewPopover(); }); watch(showAllViewsPopover, value => { if (value) { return; } // if show all view popover is closed, close children as well hideViewMenuPopover(); }); // set the popover to open next to the edit button watch(activeEditViewPopover, value => { const editButtonIndex = isViewUnsaved(value) ? 2 : 0; // EDGE CASE: // Since edit button can be clicked while in all views popover or while it is in the bar, // we will not support the case where edit popover is opened and the screen is resize until the view menu is hidden. // Edit popover will still be displayed but the position does not have to follow the edit button as it is hidden. if (showAllViewsPopover.value) { const menuListItemRef = viewControlAllViewsRef.value.$refs.editViewMenuRefs[activeViewMenuPopover.value]?.$refs ?.menuItemListRef; editViewTargetElementRef.value = viewControlAllViewsRef.value.$refs.editViewMenuRefs[activeViewMenuPopover.value]?.$refs?.menuItemListRef ?.$refs?.menuItemRef?.[editButtonIndex] ?? null; if (!value) { menuListItemRef?.focusItem(); clearError(); } return; } const menuListItemRef = editViewMenuRefs.value.find(ref => ref.$refs.menuItemListRef)?.$refs?.menuItemListRef; editViewTargetElementRef.value = menuListItemRef?.$refs?.menuItemRef?.[editButtonIndex] ?? null; if (!value) { menuListItemRef?.focusItem(); clearError(); } }); // Watch for external changes to the activeViewIndex prop watch( () => props.activeViewIndex, newValue => { if (newValue !== undefined && newValue >= 0 && newValue < computedViews.value.length) { currentActiveView.value = newValue; } } ); onMounted(() => { if ( props.activeViewIndex !== undefined && props.activeViewIndex >= 0 && props.activeViewIndex < computedViews.value.length ) { currentActiveView.value = props.activeViewIndex; } else if (!props.disableAutoSelect) { const defaultView = computedViews.value.findIndex(view => view.isDefault); currentActiveView.value = defaultView >= 0 ? defaultView : 0; } }); const getViewMenuData = (index: number): Array<BentoMenuItem> => [ ...(isViewUnsaved(index) ? [ { text: t('saveChanges'), handler: handleSaveChanges, icon: DownloadIcon, }, { addDivider: true, text: t('restoreChanges'), handler: handleRestoreChanges, icon: RefreshIcon, }, ] : []), ...(!computedViews.value[index]?.isFixed ? [ { text: t('edit'), handler: () => { setActiveEditViewPopover(activeViewMenuPopover.value); }, icon: EditIcon, skipClose: true, }, ] : []), { text: t('setAsDefault'), handler: () => { const updatedViewObj = updateView({ isDefault: true }, activeViewMenuPopover.value); emit('update:view', updatedViewObj); addToast({ text: t('setAsDefaultToast', { label: updatedViewObj.targetViewLabel }), }); hideViewMenuPopover(); }, icon: HeartIcon, }, { text: t('duplicate'), handler: () => { const updatedViewObj = duplicateView(activeViewMenuPopover.value); emit('duplicate:view', updatedViewObj); hideViewMenuPopover(); // switch to newly duplicated view handleViewSelect(updatedViewObj.newViews.length - 1); }, icon: PlusMultipleIcon, }, ...(!computedViews.value[index]?.isFixed ? [ { text: t('delete'), handler: () => { viewToBeDeleted.value = activeViewMenuPopover.value; showDeleteModalWarning.value = true; }, critical: true, icon: BinIcon, }, ] : []), ]; const showViewMenuPopover = (index: number) => { activeViewMenuPopover.value = index; }; const hideViewMenuPopover = () => { activeViewMenuPopover.value = null; setActiveEditViewPopover(); }; const setActiveEditViewPopover = (index?: number) => { if (index === activeEditViewPopover.value) { activeEditViewPopover.value = null; } else { activeEditViewPopover.value = index ?? null; } }; const handleCreateViewItem = (value: BentoHeaderWithViewsItem) => { const updatedViewObj = updateView(value); emit('add:view', updatedViewObj); hideCreateViewPopover(); // switch to new view on add handleViewSelect(updatedViewObj.newViews.findIndex(view => updatedViewObj.targetViewLabel === view.label)); addToast({ text: t('createViewToast', { label: updatedViewObj.targetViewLabel }), }); }; const handleUpdateViewItem = (value: BentoHeaderWithViewsItem) => { emit('update:view', updateView(value, activeEditViewPopover.value)); hideEditViewPopover(); }; const handleKeydownEnterViewItem = (value: BentoHeaderWithViewsItem) => { validateLabel(value.label, activeViewMenuPopover.value); if (Object.values(errors).some(error => error.length > 0)) { return; } if (activeEditViewPopover.value) { handleUpdateViewItem(value); return; } handleCreateViewItem(value); }; const handleViewSelect = (index: number) => { // If there's no currently active view, or if there are no unsaved changes, // just switch to the new view. if (currentActiveView.value === null || !props.hasChanges || props.skipUnsavedChangesText === null) { // If switching from an existing view, emit restore event. if (currentActiveView.value !== null) { emit('restore:view', computedViews.value[currentActiveView.value].label); } currentActiveView.value = index; return; } nextActiveView.value = index; showUnsavedModalWarning.value = true; }; const handleReorder = (reorderedViews: Array<BentoHeaderWithViewsItem>) => { const currentActiveLabel = currentActiveView.value !== null ? computedViews.value[currentActiveView.value]?.label : null; const result = setViews(reorderedViews); if (currentActiveLabel) { const newIndex = result.newViews.findIndex(view => view.label === currentActiveLabel); if (newIndex !== -1) { currentActiveView.value = newIndex; } } emit('reorder:view', result); }; const handleClickModal = (event: MouseEvent) => { if ((event.target as HTMLElement).closest('button')) { return; } event.stopPropagation(); event.preventDefault(); }; const handleTabEditViewPopover = () => { // skip built-in refocus on the menu button when menu is closed // enables tab to flow as intended by HTML spec editViewMenuRefs.value[0].closeMenu(false); }; const handleSaveChanges = () => { const label = computedViews.value[currentActiveView.value].label; emit('save:view', label); if (showUnsavedModalWarning.value) { emit('update:skip-unsaved-changes', skipUnsavedChangesReminder.value); // fired when user saves the view while navigating to a different tab currentActiveView.value = nextActiveView.value; nextActiveView.value = null; showUnsavedModalWarning.value = false; } else { // fired when user saves the view via the menu hideViewMenuPopover(); } addToast({ text: t('saveChangesToast', { label }), action: { handler: () => emit('restore:view', label), text: t('undo'), }, }); }; const handleRestoreChanges = () => { emit('restore:view', computedViews.value[currentActiveView.value].label); if (showUnsavedModalWarning.value) { emit('update:skip-unsaved-changes', skipUnsavedChangesReminder.value); // fired when user restore the view while navigating to a different tab currentActiveView.value = nextActiveView.value; nextActiveView.value = null; showUnsavedModalWarning.value = false; } else { // fired when user restore the view via the menu hideViewMenuPopover(); } }; const isViewActive = (index: number) => currentActiveView.value === index; const isViewUnsaved = (index: number) => isViewActive(index) && props.hasChanges; </script> <script lang="ts"> /** * Header with views allow users to save and quickly retrieve configurations tailored to specific tasks. * For example, managing daily transactions, auditing, or analyzing specific data sets. * It also allows the user to define the columns, filters, and search queries they need. * * @example * import { BentoHeaderWithViews } from '@adyen/bento-vue2'; * * export default { * components: { BentoHeaderWithViews }, * template: ` * <bento-header-with-views * :actions="[ * { title: 'Primary action', event: () => undefined }, * { title: 'Secondary action', event: () => undefined }, * { title: 'Tertiary action', event: () => undefined }, * { title: 'Another action', event: () => undefined } * ]" * description="Page Description" * title="Page Title" * :views="[ * { icon: 'stack', iconColor: 'blue', label: 'A very long value' }, * { icon: 'stack', iconColor: 'primary', label: 'Title 1' }, * { icon: 'stack', iconColor: 'primary', label: 'Title 2' } * ]" * :additionalViewSetting="{ * label: 'Include date filter (relative)', * value: false * }" * hasChanges * /> * ` * }; */ export default { i18n: { messages }, name: 'bento-header-with-views', }; </script> <style lang="scss" scoped src="./header-with-views.scss" />
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import { storybookDocsParameter } from '@/utils/ts/storybook'; import type { StoryObj } from '@storybook/vue'; import BentoImage from './image.vue'; import { mockImage } from './const'; import { BentoImageAspectRatio, BentoImageObjectFit } from './image.types'; import { isVue2 } from 'vue-demi'; export default { title: 'Image', component: BentoImage, argTypes: { aspectRatio: { options: Object.values(BentoImageAspectRatio), mapping: BentoImageAspectRatio, control: { type: 'select', }, }, objectFit: { options: Object.values(BentoImageObjectFit), mapping: BentoImageObjectFit, control: { type: 'select', }, }, }, }; type Story = StoryObj<typeof BentoImage>; const DEFAULT_IMAGE_PROPS = { src: mockImage, alt: 'Mock image', aspectRatio: 'landscape', objectFit: 'cover', }; const DefaultTemplate: Story = { render: (_args, { argTypes }) => ({ components: { BentoImage }, props: Object.keys(argTypes), template: ` <bento-image v-bind="args" style="width:200px" /> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }), };
|
|
1
|
+
import { storybookDocsParameter } from '@/utils/ts/storybook'; import type { StoryObj } from '@storybook/vue'; import BentoImage from './image.vue'; import { mockImage } from './const'; import { BentoImageAspectRatio, BentoImageObjectFit } from './image.types'; import { isVue2 } from 'vue-demi'; const ImageDefault = "<template>\n <bento-image alt=\"Example image\" aspect-ratio=\"landscape\" object-fit=\"cover\" src=\"/images/example.jpg\" />\n</template>\n\n<script setup lang=\"ts\">\n import { BentoImage } from '@adyen/bento-vue2';\n</script>\n"; export default { title: 'Image', component: BentoImage, argTypes: { aspectRatio: { options: Object.values(BentoImageAspectRatio), mapping: BentoImageAspectRatio, control: { type: 'select', }, }, objectFit: { options: Object.values(BentoImageObjectFit), mapping: BentoImageObjectFit, control: { type: 'select', }, }, }, }; type Story = StoryObj<typeof BentoImage>; const DEFAULT_IMAGE_PROPS = { src: mockImage, alt: 'Mock image', aspectRatio: 'landscape', objectFit: 'cover', }; const DefaultTemplate: Story = { render: (_args, { argTypes }) => ({ components: { BentoImage }, props: Object.keys(argTypes), template: ` <bento-image v-bind="args" style="width:200px" /> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }), }; export const Default = { ...DefaultTemplate, args: { ...DEFAULT_IMAGE_PROPS, }, parameters: storybookDocsParameter(ImageDefault), }; Default.argTypes = { icon: { table: { disable: true } } };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import BentoInfoIcon from './info-icon.vue'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { isVue2 } from 'vue-demi'; import { type Meta, type StoryObj } from '@storybook/vue'; import { PopperContainerPosition } from '@/components/popper-container'; export default { title: 'Info Icon', component: BentoInfoIcon, argTypes: { tooltipPosition: { options: Object.values(PopperContainerPosition), mapping: PopperContainerPosition, defaultValue: PopperContainerPosition.AUTO, control: { type: 'select', }, }, }, } as Meta; type Story = StoryObj<typeof BentoInfoIcon>; const DEFAULT_INFO_ICON_PROPS = { tooltipText: 'Default Icon Tooltip Text', };
|
|
1
|
+
import BentoInfoIcon from './info-icon.vue'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { isVue2 } from 'vue-demi'; import { type Meta, type StoryObj } from '@storybook/vue'; import { PopperContainerPosition } from '@/components/popper-container'; const InfoIconDefault = "<template>\n <bento-info-icon tooltip-text=\"Default Icon Tooltip Text\" />\n</template>\n\n<script setup lang=\"ts\">\n import { BentoInfoIcon } from '@adyen/bento-vue2';\n</script>\n"; export default { title: 'Info Icon', component: BentoInfoIcon, argTypes: { tooltipPosition: { options: Object.values(PopperContainerPosition), mapping: PopperContainerPosition, defaultValue: PopperContainerPosition.AUTO, control: { type: 'select', }, }, }, } as Meta; type Story = StoryObj<typeof BentoInfoIcon>; const DEFAULT_INFO_ICON_PROPS = { tooltipText: 'Default Icon Tooltip Text', }; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoInfoIcon }, props: Object.keys(argTypes), template: ` <bento-info-icon v-bind="args"/> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }), args: { ...DEFAULT_INFO_ICON_PROPS }, parameters: storybookDocsParameter(InfoIconDefault), };
|
|
@@ -13,13 +13,6 @@ It's a complete form control including a label, input, icons and description tex
|
|
|
13
13
|
<Story of={InputFieldStories.Default} />
|
|
14
14
|
</Canvas>
|
|
15
15
|
|
|
16
|
-
- [Input field](#input-field)
|
|
17
|
-
- [Variants](#variants)
|
|
18
|
-
- [Icons](#icons)
|
|
19
|
-
- [Modifiers](#modifiers)
|
|
20
|
-
- [Accessibility](#accessibility)
|
|
21
|
-
- [Focus](#focus)
|
|
22
|
-
|
|
23
16
|
## Variants
|
|
24
17
|
|
|
25
18
|
There are available 3 variants, which can be set by passing one of the listed string value to the `variant` prop:
|
|
@@ -110,12 +103,7 @@ copy the data in them.
|
|
|
110
103
|
|
|
111
104
|
### Error
|
|
112
105
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
- `errorMessage`: renders the component in an error state with an error message below the input field
|
|
116
|
-
- `error`: toggles the error state
|
|
117
|
-
|
|
118
|
-
`error` is deprecated and will be removed in the near future, so please use `errorMessage`.
|
|
106
|
+
Set `errorMessage` to render `input-field` in an error state with an error message below the field.
|
|
119
107
|
|
|
120
108
|
### Slashed zero
|
|
121
109
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import { action } from '@storybook/addon-actions'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { type BentoInputDropdownProps, BentoInputFieldElementPosition, BentoInputFieldType, BentoInputFieldVariant, } from './input-field.types'; import { type BentoListboxSelectedValue } from '@/types/listbox'; import { ref, watch } from 'vue'; import type { Meta, StoryObj } from '@storybook/vue'; import { isVue2 } from 'vue-demi'; import BentoInputField from './input-field.vue'; import CheckmarkCircleFill from '@adyen/ui-assets-icons-16/vue/checkmark-circle-fill'; import PaymentMethodTypes from '@adyen/ui-assets-icons-16/vue/payment-method-types'; import './input-field.stories.scss?module'; const meta: Meta = { title: 'Input field', component: BentoInputField, argTypes: { variant: { options: Object.values(BentoInputFieldVariant), mapping: BentoInputFieldVariant, defaultValue: BentoInputFieldVariant.DEFAULT, control: { type: 'select', }, }, type: { options: Object.values(BentoInputFieldType), mapping: BentoInputFieldType, defaultValue: BentoInputFieldType.TEXT, control: { type: 'select', }, }, default: { name: 'default', defaultValue: '', description: 'Label for the input field', table: { type: { summary: 'VNode[]' }, }, control: { type: 'text', }, }, description: { name: 'description', description: 'If set, displays a description text for the input.', table: { category: 'props', type: { summary: 'string' }, }, control: { type: 'text', }, }, 'slot:description': { name: 'description', defaultValue: '', description: 'If set, displays a description text for the input. Overrides the `description` prop when provided.', table: { type: { summary: 'VNode[]' }, category: 'slots', }, control: { type: 'text', }, }, dropdownPosition: { options: Object.values(BentoInputFieldElementPosition), mapping: BentoInputFieldElementPosition, defaultValue: BentoInputFieldElementPosition.START, control: { type: 'select', }, }, staticValue: { name: 'staticValue', defaultValue: '', description: 'Static text that displayed before or after input value', table: { type: { summary: 'VNode[]' }, }, control: { type: 'text', }, }, staticValuePosition: { options: Object.values(BentoInputFieldElementPosition), mapping: BentoInputFieldElementPosition, defaultValue: BentoInputFieldElementPosition.START, control: { type: 'select', }, }, defaultIconBefore: { name: 'defaultIconBefore', defaultValue: '', description: 'Optional `icon` at the left side', table: { type: { summary: 'VNode[]' }, category: 'Deprecated', }, control: { disable: true, }, }, iconBefore: { name: 'iconBefore', defaultValue: '', description: 'Optional `icon` at the left side', table: { type: { summary: 'VNode[]' }, }, control: { disable: true, }, }, iconAfter: { name: 'iconAfter', defaultValue: '', description: 'Optional `icon` at the right side', table: { type: { summary: 'VNode[]' }, }, control: { disable: true, }, }, error: { control: { type: 'boolean' }, table: { category: 'Deprecated' }, }, value: { control: { type: 'text' }, table: { category: 'Deprecated' }, description: 'Deprecated for usage with properties since version 2.0. Use `v-model` or `model-value` property instead', }, // Events input: { table: { type: { summary: '(value: BentoInputFieldValue) => void' }, category: 'Deprecated', }, control: { disable: true }, description: 'Deprecated since version 2.0. Use `v-model` or `update:model-value` instead', }, 'update:model-value': { table: { type: { summary: '(value: BentoInputFieldValue) => void' }, }, control: { disable: true }, }, }, }; export default meta; type Story = StoryObj<typeof BentoInputField>; const INPUT_FIELD_DEFAULT_LABEL = 'Label above'; const INPUT_FIELD_DEFAULT_VALUE = 'Entered Text'; const INPUT_FIELD_DEFAULT_DESCRIPTION = 'Description below'; const DEFAULT_INPUT_FIELD_PROPS = { disabled: false, variant: 'default', description: INPUT_FIELD_DEFAULT_DESCRIPTION, modelValue: INPUT_FIELD_DEFAULT_VALUE, staticValue: 'EUR', }; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoInputField }, props: Object.keys(argTypes), template: ` <bento-input-field class="b-input-field-story" v-bind="args" :model-value="inputValue" @update:model-value="inputAction" @change="changeAction" @focus="focusAction" @blur="blurAction" > <template #default v-if="args.default">{{ args.default }}</template> <template #description v-if="args['slot:description']">{{ args['slot:description'] }}</template> </bento-input-field> `, setup(props) { const args = isVue2 ? props : _args; const inputValue = ref(args.modelValue); const inputAction = (value: string) => { inputValue.value = value; action('input')(value); }; const changeAction = (value: string) => action('change')(value); const focusAction = (value: string) => action('focus')(value); const blurAction = (value: string) => action('blur')(value); watch( () => args.modelValue, () => { inputValue.value = args.modelValue; } ); return { args, inputValue, inputAction, changeAction, focusAction, blurAction, }; }, }), args: { ...DEFAULT_INPUT_FIELD_PROPS, default: INPUT_FIELD_DEFAULT_LABEL, }, parameters: storybookDocsParameter(` <bento-input-field variant="default" :model-value="inputValue" @update:model-value="inputAction" @change="changeAction" @focus="focusAction" @blur="blurAction" > ${INPUT_FIELD_DEFAULT_LABEL} <template #description> ${INPUT_FIELD_DEFAULT_DESCRIPTION} </template> </bento-input-field> `), }; export const DefaultWithIcons = { render: (_args, { argTypes }) => ({ components: { BentoInputField, CheckmarkCircleFill }, props: Object.keys(argTypes), template: ` <bento-input-field class="b-input-field-story" v-bind="args" @update:model-value="inputAction" @change="changeAction" @focus="focusAction" @blur="blurAction" > <template #default v-if="args.default">{{ args.default }}</template> <template #iconBefore> <checkmark-circle-fill svg-title="checkmark-circle-fill" /> </template> <template #iconAfter> <checkmark-circle-fill svg-title="checkmark-circle-fill" /> </template> <template #description v-if="args['slot:description']">{{ args['slot:description'] }}</template> </bento-input-field> `, setup(props) { const inputAction = (value: string) => action('input')(value); const changeAction = (value: string) => action('change')(value); const focusAction = (value: string) => action('focus')(value); const blurAction = (value: string) => action('blur')(value); return { args: isVue2 ? props : _args, inputAction, changeAction, focusAction, blurAction, }; }, }), args: { ...DEFAULT_INPUT_FIELD_PROPS, default: 'With Icons', }, parameters: storybookDocsParameter(` <bento-input-field variant="default" :model-value="inputValue" @update:model-value="inputAction" @change="changeAction" @focus="focusAction" @blur="blurAction" > ${INPUT_FIELD_DEFAULT_LABEL} <template #description> ${INPUT_FIELD_DEFAULT_DESCRIPTION} </template> <template #iconBefore> <checkmark-circle-fill svg-title="checkmark-circle-fill" /> </template> <template #iconAfter> <checkmark-circle-fill svg-title="checkmark-circle-fill" /> </template> </bento-input-field> `), }; export const PaymentsMethod = { render: (_args, { argTypes }) => ({ components: { BentoInputField, CheckmarkCircleFill, PaymentMethodTypes }, props: Object.keys(argTypes), template: ` <bento-input-field class="b-input-field-story" v-bind="args" @update:model-value="inputAction" @change="changeAction" @focus="focusAction" @blur="blurAction" > <template #default v-if="args.default">{{ args.default }}</template> <template #paymentMethod> <payment-method-types svg-title="payment-method-types" /> </template> <template #iconAfter> <checkmark-circle-fill svg-title="checkmark-circle-fill" /> </template> <template #description v-if="args['slot:description']">{{ args['slot:description'] }}</template> <template #staticValue> EUR </template> </bento-input-field> `, setup(props) { const inputAction = (value: string) => action('input')(value); const changeAction = (value: string) => action('change')(value); const focusAction = (value: string) => action('focus')(value); const blurAction = (value: string) => action('blur')(value); return { args: isVue2 ? props : _args, inputAction, changeAction, focusAction, blurAction, }; }, }), args: { ...DEFAULT_INPUT_FIELD_PROPS, default: 'Payment Method Variant', variant: 'payment-method', }, parameters: storybookDocsParameter(` <bento-input-field variant="payment-method" :model-value="inputValue" @update:model-value="inputAction" @change="changeAction" @focus="focusAction" @blur="blurAction" > ${INPUT_FIELD_DEFAULT_LABEL} <template #description> ${INPUT_FIELD_DEFAULT_DESCRIPTION} </template> <template #paymentMethod> <bento-payment-method svg-title="payment-method" /> </template> <template #iconAfter> <checkmark-circle-fill svg-title="checkmark-circle-fill" /> </template> </bento-input-field> `), }; export const StaticValue = { ...PaymentsMethod, args: { ...DEFAULT_INPUT_FIELD_PROPS, default: 'Static Value Variant', variant: 'static-value', }, parameters: storybookDocsParameter(` <bento-input-field variant="static-value" :model-value="inputValue" @update:model-value="inputAction" @change="changeAction" @focus="focusAction" @blur="blurAction" > ${INPUT_FIELD_DEFAULT_LABEL} <template #description> ${INPUT_FIELD_DEFAULT_DESCRIPTION} </template> <template #staticValue> EUR </template> <template #iconAfter> <checkmark-circle-fill svg-title="checkmark-circle-fill" /> </template> </bento-input-field> `), }; const DROPDOWN_DEFAULT_OPTIONS = [ { label: 'EUR', value: 'EUR' }, { label: 'USD', value: 'USD' }, ]; export const Dropdown: Story = { render: (_args, { argTypes }) => ({ components: { BentoInputField, CheckmarkCircleFill, PaymentMethodTypes }, props: Object.keys(argTypes), template: ` <bento-input-field class="b-input-field-story" v-bind="args" :dropdown.sync="dropdownProps" @update:model-value="inputAction" @change="changeAction" @focus="focusAction" @blur="blurAction" > <template #default v-if="args.default">{{ args.default }}</template> <template #paymentMethod> <payment-method-types svg-title="payment-method-types" /> </template> <template #iconAfter> <checkmark-circle-fill svg-title="checkmark-circle-fill" /> </template> <template #description v-if="args['slot:description']">{{ args['slot:description'] }}</template> </bento-input-field> `, setup(props) { const args = isVue2 ? props : _args; const dropdownProps = ref({ ...args?.dropdown }); const inputAction = (value: string) => action('input')(value); const changeAction = (value: string) => action('change')(value); const focusAction = (value: string) => action('focus')(value); const blurAction = (value: string) => action('blur')(value); const dropdownInputAction = (value: BentoListboxSelectedValue) => { action('dropdown-input')(value); }; watch( () => args.dropdown, () => { Object.assign(dropdownProps.value, args.dropdown); } ); return { args, inputAction, changeAction, focusAction, blurAction, dropdownInputAction, dropdownProps, }; }, }), args: { default: 'Dropdown Variant', description: INPUT_FIELD_DEFAULT_DESCRIPTION, modelValue: INPUT_FIELD_DEFAULT_VALUE, variant: 'dropdown', dropdown: { items: DROPDOWN_DEFAULT_OPTIONS, modelValue: DROPDOWN_DEFAULT_OPTIONS[0].value, readonly: false, 'aria-label': 'Dropdown aria label', dynamicFiltering: false, } satisfies BentoInputDropdownProps, }, };
|
|
1
|
+
import { action } from '@storybook/addon-actions'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { type BentoInputDropdownProps, BentoInputFieldElementPosition, BentoInputFieldType, BentoInputFieldVariant, } from './input-field.types'; import { type BentoListboxSelectedValue } from '@/types/listbox'; import { ref, watch } from 'vue'; import type { Meta, StoryObj } from '@storybook/vue'; import { isVue2 } from 'vue-demi'; import BentoInputField from './input-field.vue'; import CheckmarkCircleFill from '@adyen/ui-assets-icons-16/vue/checkmark-circle-fill'; import PaymentMethodTypes from '@adyen/ui-assets-icons-16/vue/payment-method-types'; import './input-field.stories.scss?module'; const InputFieldDefault = "<template>\n <bento-input-field v-model=\"inputValue\" @blur=\"handleBlur\" @change=\"handleChange\" @focus=\"handleFocus\">\n Label above\n <template #description>Description below</template>\n </bento-input-field>\n</template>\n\n<script setup lang=\"ts\">\n import { BentoInputField } from '@adyen/bento-vue2';\n import { ref } from 'vue';\n\n const inputValue = ref('Entered Text');\n const handleBlur = () => undefined;\n const handleChange = () => undefined;\n const handleFocus = () => undefined;\n</script>\n"; const InputFieldPaymentMethod = "<template>\n <bento-input-field\n v-model=\"inputValue\"\n static-value=\"EUR\"\n variant=\"payment-method\"\n @blur=\"handleBlur\"\n @change=\"handleChange\"\n @focus=\"handleFocus\"\n >\n Payment Method Variant\n <template #description>Description below</template>\n <template #paymentMethod>\n <payment-method-types svg-title=\"payment-method-types\" />\n </template>\n <template #iconAfter>\n <checkmark-circle-fill svg-title=\"checkmark-circle-fill\" />\n </template>\n </bento-input-field>\n</template>\n\n<script setup lang=\"ts\">\n import { BentoInputField } from '@adyen/bento-vue2';\n import CheckmarkCircleFill from '@adyen/ui-assets-icons-16/vue/checkmark-circle-fill';\n import PaymentMethodTypes from '@adyen/ui-assets-icons-16/vue/payment-method-types';\n import { ref } from 'vue';\n\n const inputValue = ref('Entered Text');\n const handleBlur = () => undefined;\n const handleChange = () => undefined;\n const handleFocus = () => undefined;\n</script>\n"; const InputFieldStaticValue = "<template>\n <bento-input-field\n v-model=\"inputValue\"\n variant=\"static-value\"\n @blur=\"handleBlur\"\n @change=\"handleChange\"\n @focus=\"handleFocus\"\n >\n Static Value Variant\n <template #description>Description below</template>\n <template #staticValue>EUR</template>\n <template #iconAfter>\n <checkmark-circle-fill svg-title=\"checkmark-circle-fill\" />\n </template>\n </bento-input-field>\n</template>\n\n<script setup lang=\"ts\">\n import { BentoInputField } from '@adyen/bento-vue2';\n import CheckmarkCircleFill from '@adyen/ui-assets-icons-16/vue/checkmark-circle-fill';\n import { ref } from 'vue';\n\n const inputValue = ref('Entered Text');\n const handleBlur = () => undefined;\n const handleChange = () => undefined;\n const handleFocus = () => undefined;\n</script>\n"; const InputFieldWithIcons = "<template>\n <bento-input-field v-model=\"inputValue\" @blur=\"handleBlur\" @change=\"handleChange\" @focus=\"handleFocus\">\n With Icons\n <template #description>Description below</template>\n <template #iconBefore>\n <checkmark-circle-fill svg-title=\"checkmark-circle-fill\" />\n </template>\n <template #iconAfter>\n <checkmark-circle-fill svg-title=\"checkmark-circle-fill\" />\n </template>\n </bento-input-field>\n</template>\n\n<script setup lang=\"ts\">\n import { BentoInputField } from '@adyen/bento-vue2';\n import CheckmarkCircleFill from '@adyen/ui-assets-icons-16/vue/checkmark-circle-fill';\n import { ref } from 'vue';\n\n const inputValue = ref('Entered Text');\n const handleBlur = () => undefined;\n const handleChange = () => undefined;\n const handleFocus = () => undefined;\n</script>\n"; const meta: Meta = { title: 'Input field', component: BentoInputField, argTypes: { variant: { options: Object.values(BentoInputFieldVariant), mapping: BentoInputFieldVariant, defaultValue: BentoInputFieldVariant.DEFAULT, control: { type: 'select', }, }, type: { options: Object.values(BentoInputFieldType), mapping: BentoInputFieldType, defaultValue: BentoInputFieldType.TEXT, control: { type: 'select', }, }, default: { name: 'default', defaultValue: '', description: 'Label for the input field', table: { type: { summary: 'VNode[]' }, }, control: { type: 'text', }, }, description: { name: 'description', description: 'If set, displays a description text for the input.', table: { category: 'props', type: { summary: 'string' }, }, control: { type: 'text', }, }, 'slot:description': { name: 'description', defaultValue: '', description: 'If set, displays a description text for the input. Overrides the `description` prop when provided.', table: { type: { summary: 'VNode[]' }, category: 'slots', }, control: { type: 'text', }, }, dropdownPosition: { options: Object.values(BentoInputFieldElementPosition), mapping: BentoInputFieldElementPosition, defaultValue: BentoInputFieldElementPosition.START, control: { type: 'select', }, }, staticValue: { name: 'staticValue', defaultValue: '', description: 'Static text that displayed before or after input value', table: { type: { summary: 'VNode[]' }, }, control: { type: 'text', }, }, staticValuePosition: { options: Object.values(BentoInputFieldElementPosition), mapping: BentoInputFieldElementPosition, defaultValue: BentoInputFieldElementPosition.START, control: { type: 'select', }, }, defaultIconBefore: { name: 'defaultIconBefore', defaultValue: '', description: 'Optional `icon` at the left side', table: { type: { summary: 'VNode[]' }, category: 'Deprecated', }, control: { disable: true, }, }, iconBefore: { name: 'iconBefore', defaultValue: '', description: 'Optional `icon` at the left side', table: { type: { summary: 'VNode[]' }, }, control: { disable: true, }, }, iconAfter: { name: 'iconAfter', defaultValue: '', description: 'Optional `icon` at the right side', table: { type: { summary: 'VNode[]' }, }, control: { disable: true, }, }, // Events 'update:model-value': { table: { type: { summary: '(value: BentoInputFieldValue) => void' }, }, control: { disable: true }, }, }, }; export default meta; type Story = StoryObj<typeof BentoInputField>; const INPUT_FIELD_DEFAULT_LABEL = 'Label above'; const INPUT_FIELD_DEFAULT_VALUE = 'Entered Text'; const INPUT_FIELD_DEFAULT_DESCRIPTION = 'Description below'; const DEFAULT_INPUT_FIELD_PROPS = { disabled: false, variant: 'default', description: INPUT_FIELD_DEFAULT_DESCRIPTION, modelValue: INPUT_FIELD_DEFAULT_VALUE, staticValue: 'EUR', }; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoInputField }, props: Object.keys(argTypes), template: ` <bento-input-field class="b-input-field-story" v-bind="args" :model-value="inputValue" @update:model-value="inputAction" @change="changeAction" @focus="focusAction" @blur="blurAction" > <template #default v-if="args.default">{{ args.default }}</template> <template #description v-if="args['slot:description']">{{ args['slot:description'] }}</template> </bento-input-field> `, setup(props) { const args = isVue2 ? props : _args; const inputValue = ref(args.modelValue); const inputAction = (value: string) => { inputValue.value = value; action('update:model-value')(value); }; const changeAction = (value: string) => action('change')(value); const focusAction = (value: string) => action('focus')(value); const blurAction = (value: string) => action('blur')(value); watch( () => args.modelValue, () => { inputValue.value = args.modelValue; } ); return { args, inputValue, inputAction, changeAction, focusAction, blurAction, }; }, }), args: { ...DEFAULT_INPUT_FIELD_PROPS, default: INPUT_FIELD_DEFAULT_LABEL, }, parameters: storybookDocsParameter(InputFieldDefault), }; export const DefaultWithIcons = { render: (_args, { argTypes }) => ({ components: { BentoInputField, CheckmarkCircleFill }, props: Object.keys(argTypes), template: ` <bento-input-field class="b-input-field-story" v-bind="args" @update:model-value="inputAction" @change="changeAction" @focus="focusAction" @blur="blurAction" > <template #default v-if="args.default">{{ args.default }}</template> <template #iconBefore> <checkmark-circle-fill svg-title="checkmark-circle-fill" /> </template> <template #iconAfter> <checkmark-circle-fill svg-title="checkmark-circle-fill" /> </template> <template #description v-if="args['slot:description']">{{ args['slot:description'] }}</template> </bento-input-field> `, setup(props) { const inputAction = (value: string) => action('update:model-value')(value); const changeAction = (value: string) => action('change')(value); const focusAction = (value: string) => action('focus')(value); const blurAction = (value: string) => action('blur')(value); return { args: isVue2 ? props : _args, inputAction, changeAction, focusAction, blurAction, }; }, }), args: { ...DEFAULT_INPUT_FIELD_PROPS, default: 'With Icons', }, parameters: storybookDocsParameter(InputFieldWithIcons), }; export const PaymentsMethod = { render: (_args, { argTypes }) => ({ components: { BentoInputField, CheckmarkCircleFill, PaymentMethodTypes }, props: Object.keys(argTypes), template: ` <bento-input-field class="b-input-field-story" v-bind="args" @update:model-value="inputAction" @change="changeAction" @focus="focusAction" @blur="blurAction" > <template #default v-if="args.default">{{ args.default }}</template> <template #paymentMethod> <payment-method-types svg-title="payment-method-types" /> </template> <template #iconAfter> <checkmark-circle-fill svg-title="checkmark-circle-fill" /> </template> <template #description v-if="args['slot:description']">{{ args['slot:description'] }}</template> <template #staticValue> EUR </template> </bento-input-field> `, setup(props) { const inputAction = (value: string) => action('update:model-value')(value); const changeAction = (value: string) => action('change')(value); const focusAction = (value: string) => action('focus')(value); const blurAction = (value: string) => action('blur')(value); return { args: isVue2 ? props : _args, inputAction, changeAction, focusAction, blurAction, }; }, }), args: { ...DEFAULT_INPUT_FIELD_PROPS, default: 'Payment Method Variant', variant: 'payment-method', }, parameters: storybookDocsParameter(InputFieldPaymentMethod), }; export const StaticValue = { ...PaymentsMethod, args: { ...DEFAULT_INPUT_FIELD_PROPS, default: 'Static Value Variant', variant: 'static-value', }, parameters: storybookDocsParameter(InputFieldStaticValue), }; const DROPDOWN_DEFAULT_OPTIONS = [ { label: 'EUR', value: 'EUR' }, { label: 'USD', value: 'USD' }, ]; export const Dropdown: Story = { render: (_args, { argTypes }) => ({ components: { BentoInputField, CheckmarkCircleFill, PaymentMethodTypes }, props: Object.keys(argTypes), template: ` <bento-input-field class="b-input-field-story" v-bind="args" :dropdown.sync="dropdownProps" @update:model-value="inputAction" @change="changeAction" @focus="focusAction" @blur="blurAction" > <template #default v-if="args.default">{{ args.default }}</template> <template #paymentMethod> <payment-method-types svg-title="payment-method-types" /> </template> <template #iconAfter> <checkmark-circle-fill svg-title="checkmark-circle-fill" /> </template> <template #description v-if="args['slot:description']">{{ args['slot:description'] }}</template> </bento-input-field> `, setup(props) { const args = isVue2 ? props : _args; const dropdownProps = ref({ ...args?.dropdown }); const inputAction = (value: string) => action('update:model-value')(value); const changeAction = (value: string) => action('change')(value); const focusAction = (value: string) => action('focus')(value); const blurAction = (value: string) => action('blur')(value); const dropdownInputAction = (value: BentoListboxSelectedValue) => { action('dropdown-input')(value); }; watch( () => args.dropdown, () => { Object.assign(dropdownProps.value, args.dropdown); } ); return { args, inputAction, changeAction, focusAction, blurAction, dropdownInputAction, dropdownProps, }; }, }), args: { default: 'Dropdown Variant', description: INPUT_FIELD_DEFAULT_DESCRIPTION, modelValue: INPUT_FIELD_DEFAULT_VALUE, variant: 'dropdown', dropdown: { items: DROPDOWN_DEFAULT_OPTIONS, modelValue: DROPDOWN_DEFAULT_OPTIONS[0].value, readonly: false, 'aria-label': 'Dropdown aria label', dynamicFiltering: false, } satisfies BentoInputDropdownProps, }, };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import { type BentoDropdownProps } from '../dropdown/dropdown.types'; import { type FormField } from '../../types/form-field'; /** * @deprecated Since
|
|
1
|
+
import { type BentoDropdownProps } from '../dropdown/dropdown.types'; import { type FormField } from '../../types/form-field'; /** * @deprecated Since v3.0.0. Use string literal instead. */ export enum BentoInputFieldVariant { DEFAULT = 'default', PAYMENT_METHOD = 'payment-method', STATIC_VALUE = 'static-value', DROPDOWN = 'dropdown', } /** * @deprecated Since v3.0.0. Use string literal instead. */ export enum BentoInputFieldElementPosition { START = 'start', END = 'end', } export enum BentoInputFieldStateClass { CONDENSED = 'condensed', DISABLED = 'disabled', READONLY = 'readonly', ERROR = 'error', } /** * @deprecated Since v3.0.0. Use string literal instead. */ export enum BentoInputFieldType { SEARCH = 'search', TEXT = 'text', NUMBER = 'number', } export type BentoInputFieldValue = string | number; export interface BentoInputDropdownProps { /** * Text that labels the dropdown element */ 'aria-label'?: string; /** * Indicates that dropdown items are loading (shows a loading skeleton) */ componentLoading?: BentoDropdownProps['componentLoading']; /** * Enables dynamic filtering */ dynamicFiltering?: BentoDropdownProps['dynamicFiltering']; /** * Empty state displayed when no search results are found */ emptyState?: BentoDropdownProps['emptyState']; /** * Enables value/label pair handling for selected values */ enableValueLabelPair?: BentoDropdownProps['enableValueLabelPair']; /** * Indicates whether there are more items to load */ hasMoreItems?: BentoDropdownProps['hasMoreItems']; /** * Function that determines which options are disabled from interaction */ isOptionDisabled?: BentoDropdownProps['isOptionDisabled']; /** * List of available items */ items: BentoDropdownProps['items']; /** * The type of lazy load: "automatic" for infinite scrolling, "button" for a show-more button */ lazyLoadType?: BentoDropdownProps['lazyLoadType']; /** * Indicates if new options are lazy loading */ loading?: BentoDropdownProps['loading']; /** * Selected value of the dropdown */ modelValue?: BentoDropdownProps['modelValue']; /** * Sets the dropdown in read-only mode */ readonly?: BentoDropdownProps['readonly']; /** * Dropdown search options including debounce time and placeholder */ search?: BentoDropdownProps['search']; /** * Displays non-selectable, static categories in the dropdown */ staticCategories?: BentoDropdownProps['staticCategories']; /** * Enables virtual scrolling for large lists of items */ virtualScroll?: BentoDropdownProps['virtualScroll']; } /** * @deprecated Since v3.0.0. Use `@<emit name>` instead. */ export enum BentoInputFieldEvent { INPUT = 'input', ESCAPE_PRESSED = 'escape-pressed', UPDATE_DROPDOWN = 'update:dropdown', DROPDOWN_INPUT = 'dropdown-input', FOCUS = 'focus', CHANGE = 'change', BLUR = 'blur', KEYDOWN = 'keydown', } export interface BentoInputFieldProps extends FormField { /** * Indicates whether the element is exposed to an accessibility API. */ ariaHidden?: boolean; /** * Reduces height of the input */ condensed?: boolean; /** * Dropdown props for the single-select dropdown menu embedded in the input field. * Only a subset of `BentoDropdownProps` is supported (multi-select is not available). * * @see {@link BentoInputDropdownProps} */ dropdown?: BentoInputDropdownProps; /** * Position of the dropdown before or after input value */ dropdownPosition?: BentoInputFieldElementPosition | `${BentoInputFieldElementPosition}`; /** * Text displayed as the value of the input-field */ modelValue?: BentoInputFieldValue; /** * Renders a font numeric style with slashed zeros instead of normal ones. */ slashedZero?: boolean; /** * Static text that displayed before or after input value * Applicable to the Static Value variant */ staticValue?: string; /** * Position of Static Value before or after input value */ staticValuePosition?: BentoInputFieldElementPosition | `${BentoInputFieldElementPosition}`; /** * A string specifying the type of control to render. * All `input` types are available but only `search`, `text` and `number` are fully supported. * * If `type` is `search`, use `bento-search-bar` instead. */ type?: BentoInputFieldType | `${BentoInputFieldType}` | HTMLInputElement['type']; /** * Describes the different variants for the input element */ variant?: BentoInputFieldVariant | `${BentoInputFieldVariant}`; }
|