@adyen/bento-mcp 0.0.0 → 0.1.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.
Files changed (410) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +21 -0
  3. package/README.md +113 -0
  4. package/dist/assets/components/accordion/accordion.docs.mdx +77 -0
  5. package/dist/assets/components/accordion/accordion.stories.ts +1 -0
  6. package/dist/assets/components/accordion/accordion.vue +1 -0
  7. package/dist/assets/components/accordion/components/accordion-item.stories.ts +1 -0
  8. package/dist/assets/components/accordion/components/accordion-item.vue +1 -0
  9. package/dist/assets/components/action-bar/action-bar.docs.mdx +103 -0
  10. package/dist/assets/components/action-bar/action-bar.stories.ts +1 -0
  11. package/dist/assets/components/action-bar/action-bar.vue +1 -0
  12. package/dist/assets/components/alert/alert.docs.mdx +106 -0
  13. package/dist/assets/components/alert/alert.stories.ts +1 -0
  14. package/dist/assets/components/alert/alert.vue +1 -0
  15. package/dist/assets/components/avatar/avatar.docs.mdx +44 -0
  16. package/dist/assets/components/avatar/avatar.stories.ts +1 -0
  17. package/dist/assets/components/avatar/avatar.vue +1 -0
  18. package/dist/assets/components/avatar/components/avatar-image/avatar-image.vue +1 -0
  19. package/dist/assets/components/button/button.docs.mdx +137 -0
  20. package/dist/assets/components/button/button.stories.ts +1 -0
  21. package/dist/assets/components/button/button.vue +1 -0
  22. package/dist/assets/components/button/components/ai-button/ai-button.docs.mdx +40 -0
  23. package/dist/assets/components/button/components/ai-button/ai-button.stories.ts +1 -0
  24. package/dist/assets/components/button/components/ai-button/ai-button.vue +1 -0
  25. package/dist/assets/components/button/components/button-actions/button-actions.docs.mdx +169 -0
  26. package/dist/assets/components/button/components/button-actions/button-actions.stories.ts +1 -0
  27. package/dist/assets/components/button/components/button-actions/button-actions.vue +1 -0
  28. package/dist/assets/components/button/components/loading-button/loading-button.docs.mdx +106 -0
  29. package/dist/assets/components/button/components/loading-button/loading-button.stories.ts +1 -0
  30. package/dist/assets/components/button/components/loading-button/loading-button.vue +1 -0
  31. package/dist/assets/components/button/components/toggle-button/toggle-button.docs.mdx +81 -0
  32. package/dist/assets/components/button/components/toggle-button/toggle-button.stories.ts +1 -0
  33. package/dist/assets/components/button/components/toggle-button/toggle-button.vue +1 -0
  34. package/dist/assets/components/card/card.docs.mdx +166 -0
  35. package/dist/assets/components/card/card.stories.ts +1 -0
  36. package/dist/assets/components/card/card.vue +1 -0
  37. package/dist/assets/components/checkbox/checkbox.docs.mdx +116 -0
  38. package/dist/assets/components/checkbox/checkbox.stories.ts +1 -0
  39. package/dist/assets/components/checkbox/checkbox.vue +1 -0
  40. package/dist/assets/components/checkbox/components/checkbox-group/checkbox-group.docs.mdx +134 -0
  41. package/dist/assets/components/checkbox/components/checkbox-group/checkbox-group.stories.ts +1 -0
  42. package/dist/assets/components/checkbox/components/checkbox-group/checkbox-group.vue +1 -0
  43. package/dist/assets/components/chip/chip.docs.mdx +51 -0
  44. package/dist/assets/components/chip/chip.stories.ts +1 -0
  45. package/dist/assets/components/chip/chip.vue +1 -0
  46. package/dist/assets/components/click-outside/click-outside.docs.mdx +30 -0
  47. package/dist/assets/components/click-outside/click-outside.stories.ts +1 -0
  48. package/dist/assets/components/code-snippet/code-snippet.docs.mdx +73 -0
  49. package/dist/assets/components/code-snippet/code-snippet.stories.ts +1 -0
  50. package/dist/assets/components/code-snippet/code-snippet.vue +1 -0
  51. package/dist/assets/components/country/country.docs.mdx +61 -0
  52. package/dist/assets/components/country/country.stories.ts +1 -0
  53. package/dist/assets/components/country/country.vue +1 -0
  54. package/dist/assets/components/currency/currency.docs.mdx +38 -0
  55. package/dist/assets/components/currency/currency.stories.ts +1 -0
  56. package/dist/assets/components/currency/currency.vue +1 -0
  57. package/dist/assets/components/dashed-underline/dashed-underline.docs.mdx +57 -0
  58. package/dist/assets/components/dashed-underline/dashed-underline.stories.ts +1 -0
  59. package/dist/assets/components/dashed-underline/dashed-underline.vue +1 -0
  60. package/dist/assets/components/data-grid/components/data-grid-cell/data-grid-cell.vue +1 -0
  61. package/dist/assets/components/data-grid/components/data-grid-cell-actions/data-grid-cell-actions.vue +1 -0
  62. package/dist/assets/components/data-grid/components/data-grid-columns/data-grid-columns.vue +1 -0
  63. package/dist/assets/components/data-grid/components/data-grid-config-settings/components/data-grid-config-settings-column-row/data-grid-config-settings-column-row.vue +1 -0
  64. package/dist/assets/components/data-grid/components/data-grid-config-settings/data-grid-config-settings.vue +1 -0
  65. package/dist/assets/components/data-grid/components/data-grid-lazy-load/data-grid-lazy-load.vue +1 -0
  66. package/dist/assets/components/data-grid/components/data-grid-row/data-grid-row.vue +1 -0
  67. package/dist/assets/components/data-grid/components/data-grid-static-icon/data-grid-static-icon.docs.mdx +61 -0
  68. package/dist/assets/components/data-grid/components/data-grid-static-icon/data-grid-static-icon.stories.ts +1 -0
  69. package/dist/assets/components/data-grid/components/data-grid-static-icon/data-grid-static-icon.vue +1 -0
  70. package/dist/assets/components/data-grid/components/data-grid-summary-cell/data-grid-summary-cell.docs.mdx +56 -0
  71. package/dist/assets/components/data-grid/components/data-grid-summary-cell/data-grid-summary-cell.stories.ts +1 -0
  72. package/dist/assets/components/data-grid/components/data-grid-summary-cell/data-grid-summary-cell.vue +1 -0
  73. package/dist/assets/components/data-grid/components/data-grid-trend-cell/data-grid-trend-cell.docs.mdx +48 -0
  74. package/dist/assets/components/data-grid/components/data-grid-trend-cell/data-grid-trend-cell.stories.ts +1 -0
  75. package/dist/assets/components/data-grid/components/data-grid-trend-cell/data-grid-trend-cell.vue +1 -0
  76. package/dist/assets/components/data-grid/data-grid.docs.mdx +951 -0
  77. package/dist/assets/components/data-grid/data-grid.stories.ts +1 -0
  78. package/dist/assets/components/data-grid/data-grid.vue +1 -0
  79. package/dist/assets/components/date/date.docs.mdx +49 -0
  80. package/dist/assets/components/date/date.stories.ts +1 -0
  81. package/dist/assets/components/date/date.vue +1 -0
  82. package/dist/assets/components/date-picker/date-picker.docs.mdx +69 -0
  83. package/dist/assets/components/date-picker/date-picker.stories.ts +1 -0
  84. package/dist/assets/components/date-picker/date-picker.vue +1 -0
  85. package/dist/assets/components/date-range-picker/components/date-range-picker-calendar/date-range-picker-calendar.vue +1 -0
  86. package/dist/assets/components/date-range-picker/date-range-picker.docs.mdx +168 -0
  87. package/dist/assets/components/date-range-picker/date-range-picker.stories.ts +1 -0
  88. package/dist/assets/components/date-range-picker/date-range-picker.vue +1 -0
  89. package/dist/assets/components/divider/divider.docs.mdx +27 -0
  90. package/dist/assets/components/divider/divider.stories.ts +1 -0
  91. package/dist/assets/components/divider/divider.vue +1 -0
  92. package/dist/assets/components/dropdown/components/dropdown-base-textbox/dropdown-base-textbox.vue +1 -0
  93. package/dist/assets/components/dropdown/components/dropdown-default-textbox/dropdown-default-textbox.vue +1 -0
  94. package/dist/assets/components/dropdown/components/dropdown-options-container/dropdown-options-container.vue +1 -0
  95. package/dist/assets/components/dropdown/components/dropdown-small-textbox/dropdown-small-textbox.vue +1 -0
  96. package/dist/assets/components/dropdown/components/variants/dropdown-avatar/dropdown-avatar.stories.ts +1 -0
  97. package/dist/assets/components/dropdown/components/variants/dropdown-avatar/dropdown-avatar.vue +1 -0
  98. package/dist/assets/components/dropdown/components/variants/dropdown-country/dropdown-country.stories.ts +1 -0
  99. package/dist/assets/components/dropdown/components/variants/dropdown-country/dropdown-country.vue +1 -0
  100. package/dist/assets/components/dropdown/components/variants/dropdown-currency/dropdown-currency.stories.ts +1 -0
  101. package/dist/assets/components/dropdown/components/variants/dropdown-currency/dropdown-currency.vue +1 -0
  102. package/dist/assets/components/dropdown/components/variants/dropdown-payment-method/dropdown-payment-method.stories.ts +1 -0
  103. package/dist/assets/components/dropdown/components/variants/dropdown-payment-method/dropdown-payment-method.vue +1 -0
  104. package/dist/assets/components/dropdown/components/variants/dropdown-tag/dropdown-tag.stories.ts +1 -0
  105. package/dist/assets/components/dropdown/components/variants/dropdown-tag/dropdown-tag.vue +1 -0
  106. package/dist/assets/components/dropdown/dropdown.docs.mdx +420 -0
  107. package/dist/assets/components/dropdown/dropdown.stories.ts +1 -0
  108. package/dist/assets/components/dropdown/dropdown.vue +1 -0
  109. package/dist/assets/components/empty-state/empty-state.docs.mdx +115 -0
  110. package/dist/assets/components/empty-state/empty-state.stories.ts +1 -0
  111. package/dist/assets/components/empty-state/empty-state.vue +1 -0
  112. package/dist/assets/components/file-uploader/components/file-uploader-file-card/components/file-uploader-file-card-animation/file-uploader-file-card-animation.vue +1 -0
  113. package/dist/assets/components/file-uploader/components/file-uploader-file-card/file-uploader-file-card.stories.ts +1 -0
  114. package/dist/assets/components/file-uploader/components/file-uploader-file-card/file-uploader-file-card.vue +1 -0
  115. package/dist/assets/components/file-uploader/components/file-uploader-restrictions/file-uploader-restrictions.stories.ts +1 -0
  116. package/dist/assets/components/file-uploader/components/file-uploader-restrictions/file-uploader-restrictions.vue +1 -0
  117. package/dist/assets/components/file-uploader/file-uploader.docs.mdx +117 -0
  118. package/dist/assets/components/file-uploader/file-uploader.stories.ts +1 -0
  119. package/dist/assets/components/file-uploader/file-uploader.vue +1 -0
  120. package/dist/assets/components/filter-bar/components/all-filters-modal/all-filters-modal.stories.ts +1 -0
  121. package/dist/assets/components/filter-bar/components/all-filters-modal/all-filters-modal.vue +1 -0
  122. package/dist/assets/components/filter-bar/components/all-filters-modal/components/all-filters-modal-button/all-filters-modal-button.vue +1 -0
  123. package/dist/assets/components/filter-bar/components/base-filter/base-filter.docs.mdx +90 -0
  124. package/dist/assets/components/filter-bar/components/base-filter/base-filter.stories.ts +1 -0
  125. package/dist/assets/components/filter-bar/components/base-filter/base-filter.vue +1 -0
  126. package/dist/assets/components/filter-bar/components/base-filter-button/base-filter-button.vue +1 -0
  127. package/dist/assets/components/filter-bar/components/boolean-filter/boolean-filter.vue +1 -0
  128. package/dist/assets/components/filter-bar/components/checkbox-group-filter/checkbox-group-filter.stories.ts +1 -0
  129. package/dist/assets/components/filter-bar/components/checkbox-group-filter/checkbox-group-filter.vue +1 -0
  130. package/dist/assets/components/filter-bar/components/date-filter/date-filter.stories.ts +1 -0
  131. package/dist/assets/components/filter-bar/components/date-filter/date-filter.vue +1 -0
  132. package/dist/assets/components/filter-bar/components/date-range-filter/date-range-filter.stories.ts +1 -0
  133. package/dist/assets/components/filter-bar/components/date-range-filter/date-range-filter.vue +1 -0
  134. package/dist/assets/components/filter-bar/components/filter-bar-button/filter-bar-button.vue +1 -0
  135. package/dist/assets/components/filter-bar/components/filter-bar-button-group/filter-bar-button-group.vue +1 -0
  136. package/dist/assets/components/filter-bar/components/input-filter/input-filter.docs.mdx +18 -0
  137. package/dist/assets/components/filter-bar/components/input-filter/input-filter.stories.ts +1 -0
  138. package/dist/assets/components/filter-bar/components/input-filter/input-filter.vue +1 -0
  139. package/dist/assets/components/filter-bar/components/input-with-dropdown-filter/input-with-dropdown-filter.docs.mdx +18 -0
  140. package/dist/assets/components/filter-bar/components/input-with-dropdown-filter/input-with-dropdown-filter.stories.ts +1 -0
  141. package/dist/assets/components/filter-bar/components/input-with-dropdown-filter/input-with-dropdown-filter.vue +1 -0
  142. package/dist/assets/components/filter-bar/components/radio-group-filter/radio-group-filter.stories.ts +1 -0
  143. package/dist/assets/components/filter-bar/components/radio-group-filter/radio-group-filter.vue +1 -0
  144. package/dist/assets/components/filter-bar/components/range-filter/range-filter.vue +1 -0
  145. package/dist/assets/components/filter-bar/components/select-filter/components/select-filter-button/select-filter-button.vue +1 -0
  146. package/dist/assets/components/filter-bar/components/select-filter/components/select-filter-dropdown/select-filter-dropdown.vue +1 -0
  147. package/dist/assets/components/filter-bar/components/select-filter/select-filter.stories.ts +1 -0
  148. package/dist/assets/components/filter-bar/components/select-filter/select-filter.vue +1 -0
  149. package/dist/assets/components/filter-bar/components/select-filter/variants/select-avatar-filter/select-avatar-filter.stories.ts +1 -0
  150. package/dist/assets/components/filter-bar/components/select-filter/variants/select-avatar-filter/select-avatar-filter.vue +1 -0
  151. package/dist/assets/components/filter-bar/components/select-filter/variants/select-country-filter/select-country-filter.stories.ts +1 -0
  152. package/dist/assets/components/filter-bar/components/select-filter/variants/select-country-filter/select-country-filter.vue +1 -0
  153. package/dist/assets/components/filter-bar/components/select-filter/variants/select-currency-filter/select-currency-filter.stories.ts +1 -0
  154. package/dist/assets/components/filter-bar/components/select-filter/variants/select-currency-filter/select-currency-filter.vue +1 -0
  155. package/dist/assets/components/filter-bar/components/select-filter/variants/select-payment-method-filter/select-payment-method-filter.stories.ts +1 -0
  156. package/dist/assets/components/filter-bar/components/select-filter/variants/select-payment-method-filter/select-payment-method-filter.vue +1 -0
  157. package/dist/assets/components/filter-bar/components/select-filter/variants/select-tag-filter/select-tag-filter.stories.ts +1 -0
  158. package/dist/assets/components/filter-bar/components/select-filter/variants/select-tag-filter/select-tag-filter.vue +1 -0
  159. package/dist/assets/components/filter-bar/components/toggle-filter/toggle-filter.stories.ts +1 -0
  160. package/dist/assets/components/filter-bar/components/toggle-filter/toggle-filter.vue +1 -0
  161. package/dist/assets/components/filter-bar/filter-bar.docs.mdx +1005 -0
  162. package/dist/assets/components/filter-bar/filter-bar.stories.ts +1 -0
  163. package/dist/assets/components/filter-bar/filter-bar.vue +1 -0
  164. package/dist/assets/components/focus-trap/focus-trap.docs.mdx +10 -0
  165. package/dist/assets/components/focus-trap/focus-trap.stories.ts +1 -0
  166. package/dist/assets/components/focus-trap/focus-trap.vue +1 -0
  167. package/dist/assets/components/form-layout/components/form-layout-field-tip/form-layout-field-tip.vue +1 -0
  168. package/dist/assets/components/form-layout/components/form-layout-group/form-layout-group.vue +1 -0
  169. package/dist/assets/components/form-layout/components/form-layout-title/form-layout-title.vue +1 -0
  170. package/dist/assets/components/form-layout/form-layout.docs.mdx +452 -0
  171. package/dist/assets/components/form-layout/form-layout.stories.ts +1 -0
  172. package/dist/assets/components/form-layout/form-layout.vue +1 -0
  173. package/dist/assets/components/header/header.docs.mdx +85 -0
  174. package/dist/assets/components/header/header.stories.ts +1 -0
  175. package/dist/assets/components/header/header.vue +1 -0
  176. package/dist/assets/components/header-with-views/components/header-with-views-all-views/header-with-views-all-views.vue +1 -0
  177. package/dist/assets/components/header-with-views/components/header-with-views-item-edit-form/header-with-views-edit-form.vue +1 -0
  178. package/dist/assets/components/header-with-views/header-with-views.docs.mdx +103 -0
  179. package/dist/assets/components/header-with-views/header-with-views.stories.ts +1 -0
  180. package/dist/assets/components/header-with-views/header-with-views.vue +1 -0
  181. package/dist/assets/components/image/image.docs.mdx +34 -0
  182. package/dist/assets/components/image/image.stories.ts +1 -0
  183. package/dist/assets/components/image/image.vue +1 -0
  184. package/dist/assets/components/info-icon/info-icon.docs.mdx +57 -0
  185. package/dist/assets/components/info-icon/info-icon.stories.ts +1 -0
  186. package/dist/assets/components/info-icon/info-icon.vue +1 -0
  187. package/dist/assets/components/input-field/input-field.docs.mdx +141 -0
  188. package/dist/assets/components/input-field/input-field.stories.ts +1 -0
  189. package/dist/assets/components/input-field/input-field.vue +1 -0
  190. package/dist/assets/components/input-field-password/input-field-password.docs.mdx +292 -0
  191. package/dist/assets/components/input-field-password/input-field-password.stories.ts +1 -0
  192. package/dist/assets/components/input-field-password/input-field-password.vue +1 -0
  193. package/dist/assets/components/input-field-phone-number/input-field-phone-number.docs.mdx +72 -0
  194. package/dist/assets/components/input-field-phone-number/input-field-phone-number.stories.ts +1 -0
  195. package/dist/assets/components/input-field-phone-number/input-field-phone-number.vue +1 -0
  196. package/dist/assets/components/inspector/inspector.docs.mdx +118 -0
  197. package/dist/assets/components/inspector/inspector.stories.ts +1 -0
  198. package/dist/assets/components/inspector/inspector.vue +1 -0
  199. package/dist/assets/components/internal/base-button/base-button.vue +1 -0
  200. package/dist/assets/components/internal/button-actions-with-menu/button-actions-with-menu.stories.ts +1 -0
  201. package/dist/assets/components/internal/button-actions-with-menu/button-actions-with-menu.vue +1 -0
  202. package/dist/assets/components/internal/calendar/calendar.stories.ts +1 -0
  203. package/dist/assets/components/internal/calendar/calendar.vue +1 -0
  204. package/dist/assets/components/internal/calendar/components/calendar-month/calendar-month.stories.ts +1 -0
  205. package/dist/assets/components/internal/calendar/components/calendar-month/calendar-month.vue +1 -0
  206. package/dist/assets/components/internal/calendar/components/calendar-month/components/calendar-month-day/__test__/calendar-month-day-stub.vue +1 -0
  207. package/dist/assets/components/internal/calendar/components/calendar-month/components/calendar-month-day/calendar-month-day.stories.ts +1 -0
  208. package/dist/assets/components/internal/calendar/components/calendar-month/components/calendar-month-day/calendar-month-day.vue +1 -0
  209. package/dist/assets/components/internal/calendar/components/calendar-year/calendar-year.vue +1 -0
  210. package/dist/assets/components/internal/calendar/components/calendar-year/components/calendar-year-month/__test__/calendar-year-month-stub.vue +1 -0
  211. package/dist/assets/components/internal/calendar/components/calendar-year/components/calendar-year-month/calendar-year-month.vue +1 -0
  212. package/dist/assets/components/internal/checkbox-input/checkbox-input.stories.ts +1 -0
  213. package/dist/assets/components/internal/checkbox-input/checkbox-input.vue +1 -0
  214. package/dist/assets/components/internal/controls-group/controls-group.vue +1 -0
  215. package/dist/assets/components/internal/copy/copy.docs.mdx +43 -0
  216. package/dist/assets/components/internal/copy/copy.stories.ts +1 -0
  217. package/dist/assets/components/internal/copy/copy.vue +1 -0
  218. package/dist/assets/components/internal/decorative-checkbox/decorative-checkbox.vue +1 -0
  219. package/dist/assets/components/internal/dialog-page/dialog-page.vue +1 -0
  220. package/dist/assets/components/internal/error-message/error-message.vue +1 -0
  221. package/dist/assets/components/internal/field-label/field-label.vue +1 -0
  222. package/dist/assets/components/internal/fixed-scroller/fixed-scroller.docs.mdx +21 -0
  223. package/dist/assets/components/internal/fixed-scroller/fixed-scroller.stories.ts +1 -0
  224. package/dist/assets/components/internal/fixed-scroller/fixed-scroller.vue +1 -0
  225. package/dist/assets/components/internal/footer-actions/footer-actions.vue +1 -0
  226. package/dist/assets/components/internal/grid-layout/grid-layout.vue +1 -0
  227. package/dist/assets/components/internal/header-meta/header-meta.vue +1 -0
  228. package/dist/assets/components/internal/info-icon-with-popover/info-icon-with-popover.vue +1 -0
  229. 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 +1 -0
  230. package/dist/assets/components/internal/listbox/components/listbox-multi-select/components/listbox-multi-select-options/listbox-multi-select-options.vue +1 -0
  231. package/dist/assets/components/internal/listbox/components/listbox-multi-select/listbox-multi-select.vue +1 -0
  232. package/dist/assets/components/internal/listbox/components/listbox-option/listbox-option.vue +1 -0
  233. package/dist/assets/components/internal/listbox/components/listbox-option-tag/listbox-option-tag.vue +1 -0
  234. package/dist/assets/components/internal/listbox/components/listbox-single-select/components/listbox-single-select-category/listbox-single-select-category.vue +1 -0
  235. package/dist/assets/components/internal/listbox/components/listbox-single-select/listbox-single-select.vue +1 -0
  236. package/dist/assets/components/internal/listbox/components/listbox-single-select-option/listbox-single-select-option.vue +1 -0
  237. package/dist/assets/components/internal/listbox/listbox.stories.ts +1 -0
  238. package/dist/assets/components/internal/listbox/listbox.vue +1 -0
  239. package/dist/assets/components/internal/radio-input/radio-input.stories.ts +1 -0
  240. package/dist/assets/components/internal/radio-input/radio-input.vue +1 -0
  241. package/dist/assets/components/internal/teleport/teleport.vue +1 -0
  242. package/dist/assets/components/internal/tooltip/tooltip.test.vue +1 -0
  243. package/dist/assets/components/internal/tooltip/tooltip.vue +1 -0
  244. package/dist/assets/components/layout/components/layout-row/layout-row.stories.ts +1 -0
  245. package/dist/assets/components/layout/components/layout-row/layout-row.vue +1 -0
  246. package/dist/assets/components/layout/layout.docs.mdx +108 -0
  247. package/dist/assets/components/layout/layout.stories.ts +1 -0
  248. package/dist/assets/components/layout/layout.vue +1 -0
  249. package/dist/assets/components/link/link.docs.mdx +119 -0
  250. package/dist/assets/components/link/link.stories.ts +1 -0
  251. package/dist/assets/components/link/link.vue +1 -0
  252. package/dist/assets/components/loading-indicator/loading-indicator.docs.mdx +65 -0
  253. package/dist/assets/components/loading-indicator/loading-indicator.stories.ts +1 -0
  254. package/dist/assets/components/loading-indicator/loading-indicator.vue +1 -0
  255. package/dist/assets/components/menu/components/menu-item/menu-item.vue +1 -0
  256. package/dist/assets/components/menu/components/menu-item-list/menu-item-list.vue +1 -0
  257. package/dist/assets/components/menu/components/menu-list-container/menu-list-container.vue +1 -0
  258. package/dist/assets/components/menu/components/sub-menu-item-list/sub-menu-item-list.vue +1 -0
  259. package/dist/assets/components/menu/menu.docs.mdx +121 -0
  260. package/dist/assets/components/menu/menu.stories.ts +1 -0
  261. package/dist/assets/components/menu/menu.vue +1 -0
  262. package/dist/assets/components/modal/components/base-modal/base-modal.vue +1 -0
  263. package/dist/assets/components/modal/components/modal-default/modal-default.vue +1 -0
  264. package/dist/assets/components/modal/components/modal-dialog/modal-dialog.vue +1 -0
  265. package/dist/assets/components/modal/components/modal-page/modal-page.vue +1 -0
  266. package/dist/assets/components/modal/modal.docs.mdx +215 -0
  267. package/dist/assets/components/modal/modal.stories.ts +1 -0
  268. package/dist/assets/components/modal/modal.vue +1 -0
  269. package/dist/assets/components/modal-fullscreen/components/modal-fullscreen-page.vue +1 -0
  270. package/dist/assets/components/modal-fullscreen/modal-fullscreen.docs.mdx +217 -0
  271. package/dist/assets/components/modal-fullscreen/modal-fullscreen.stories.ts +1 -0
  272. package/dist/assets/components/modal-fullscreen/modal-fullscreen.vue +1 -0
  273. package/dist/assets/components/pagination/components/pagination-context/pagination-context.vue +1 -0
  274. package/dist/assets/components/pagination/components/pagination-controls/pagination-controls.vue +1 -0
  275. package/dist/assets/components/pagination/components/pagination-results-per-page/pagination-results-per-page.vue +1 -0
  276. package/dist/assets/components/pagination/pagination.docs.mdx +116 -0
  277. package/dist/assets/components/pagination/pagination.stories.ts +1 -0
  278. package/dist/assets/components/pagination/pagination.vue +1 -0
  279. package/dist/assets/components/payment-method/payment-method.docs.mdx +34 -0
  280. package/dist/assets/components/payment-method/payment-method.stories.ts +1 -0
  281. package/dist/assets/components/payment-method/payment-method.vue +1 -0
  282. package/dist/assets/components/popover/components/popover-dismiss-button/popover-dismiss-button.vue +1 -0
  283. package/dist/assets/components/popover/components/popover-image/popover-image.vue +1 -0
  284. package/dist/assets/components/popover/components/popover-title/popover-title.vue +1 -0
  285. package/dist/assets/components/popover/popover.docs.mdx +89 -0
  286. package/dist/assets/components/popover/popover.stories.ts +1 -0
  287. package/dist/assets/components/popover/popover.vue +1 -0
  288. package/dist/assets/components/popper-container/popper-container.docs.mdx +11 -0
  289. package/dist/assets/components/popper-container/popper-container.stories.ts +1 -0
  290. package/dist/assets/components/popper-container/popper-container.vue +1 -0
  291. package/dist/assets/components/promo-banner/promo-banner.docs.mdx +94 -0
  292. package/dist/assets/components/promo-banner/promo-banner.stories.ts +1 -0
  293. package/dist/assets/components/promo-banner/promo-banner.vue +1 -0
  294. package/dist/assets/components/radio-group/components/radio-button/radio-button.docs.mdx +76 -0
  295. package/dist/assets/components/radio-group/components/radio-button/radio-button.stories.ts +1 -0
  296. package/dist/assets/components/radio-group/components/radio-button/radio-button.vue +1 -0
  297. package/dist/assets/components/radio-group/radio-group.docs.mdx +154 -0
  298. package/dist/assets/components/radio-group/radio-group.stories.ts +1 -0
  299. package/dist/assets/components/radio-group/radio-group.vue +1 -0
  300. package/dist/assets/components/rich-text-editor/components/rich-text-editor-content-area/rich-text-editor-content-area.vue +1 -0
  301. package/dist/assets/components/rich-text-editor/components/rich-text-editor-renderer/components/rich-text-editor-code-block/rich-text-editor-code-block.vue +1 -0
  302. package/dist/assets/components/rich-text-editor/components/rich-text-editor-renderer/components/rich-text-editor-inline-code/rich-text-editor-inline-code.vue +1 -0
  303. package/dist/assets/components/rich-text-editor/components/rich-text-editor-renderer/components/rich-text-editor-link/rich-text-editor-link.vue +1 -0
  304. package/dist/assets/components/rich-text-editor/components/rich-text-editor-renderer/components/rich-text-editor-list-item/rich-text-editor-list-item.vue +1 -0
  305. package/dist/assets/components/rich-text-editor/components/rich-text-editor-renderer/components/rich-text-editor-table/rich-text-editor-table.vue +1 -0
  306. package/dist/assets/components/rich-text-editor/rich-text-editor.docs.mdx +52 -0
  307. package/dist/assets/components/rich-text-editor/rich-text-editor.stories.ts +1 -0
  308. package/dist/assets/components/rich-text-editor/rich-text-editor.vue +1 -0
  309. package/dist/assets/components/search-bar/search-bar.docs.mdx +117 -0
  310. package/dist/assets/components/search-bar/search-bar.stories.ts +1 -0
  311. package/dist/assets/components/search-bar/search-bar.vue +1 -0
  312. package/dist/assets/components/secondary-nav/components/secondary-nav-category/secondary-nav-category.vue +1 -0
  313. package/dist/assets/components/secondary-nav/components/secondary-nav-item/secondary-nav-item.vue +1 -0
  314. package/dist/assets/components/secondary-nav/secondary-nav.docs.mdx +40 -0
  315. package/dist/assets/components/secondary-nav/secondary-nav.stories.ts +1 -0
  316. package/dist/assets/components/secondary-nav/secondary-nav.vue +1 -0
  317. package/dist/assets/components/segmented-control/components/segmented-control-button/segmented-control-button.vue +1 -0
  318. package/dist/assets/components/segmented-control/segmented-control.docs.mdx +90 -0
  319. package/dist/assets/components/segmented-control/segmented-control.stories.ts +1 -0
  320. package/dist/assets/components/segmented-control/segmented-control.vue +1 -0
  321. package/dist/assets/components/selection-card/components/selection-card-group/selection-card-group.docs.mdx +119 -0
  322. package/dist/assets/components/selection-card/components/selection-card-group/selection-card-group.stories.ts +1 -0
  323. package/dist/assets/components/selection-card/components/selection-card-group/selection-card-group.vue +1 -0
  324. package/dist/assets/components/selection-card/selection-card.docs.mdx +108 -0
  325. package/dist/assets/components/selection-card/selection-card.stories.ts +1 -0
  326. package/dist/assets/components/selection-card/selection-card.vue +1 -0
  327. package/dist/assets/components/sidepanel/components/sidepanel-page.stories.ts +1 -0
  328. package/dist/assets/components/sidepanel/components/sidepanel-page.vue +1 -0
  329. package/dist/assets/components/sidepanel/sidepanel.docs.mdx +129 -0
  330. package/dist/assets/components/sidepanel/sidepanel.stories.ts +1 -0
  331. package/dist/assets/components/sidepanel/sidepanel.vue +1 -0
  332. package/dist/assets/components/status/status.docs.mdx +62 -0
  333. package/dist/assets/components/status/status.stories.ts +1 -0
  334. package/dist/assets/components/status/status.vue +1 -0
  335. package/dist/assets/components/stepper/components/step/step.stories.ts +1 -0
  336. package/dist/assets/components/stepper/components/step/step.vue +1 -0
  337. package/dist/assets/components/stepper/stepper.docs.mdx +127 -0
  338. package/dist/assets/components/stepper/stepper.stories.ts +1 -0
  339. package/dist/assets/components/stepper/stepper.vue +1 -0
  340. package/dist/assets/components/structured-list/components/structered-list-item/structured-list-item.stories.ts +1 -0
  341. package/dist/assets/components/structured-list/components/structered-list-item/structured-list-item.vue +1 -0
  342. package/dist/assets/components/structured-list/structured-list.docs.mdx +68 -0
  343. package/dist/assets/components/structured-list/structured-list.stories.ts +1 -0
  344. package/dist/assets/components/structured-list/structured-list.vue +1 -0
  345. package/dist/assets/components/summary-grid/components/summary-grid-item/summary-grid-item.vue +1 -0
  346. package/dist/assets/components/summary-grid/components/summary-grid-item-amount/summary-grid-item-amount.stories.ts +1 -0
  347. package/dist/assets/components/summary-grid/components/summary-grid-item-amount/summary-grid-item-amount.vue +1 -0
  348. package/dist/assets/components/summary-grid/components/summary-grid-item-custom/summary-grid-item-custom.stories.ts +1 -0
  349. package/dist/assets/components/summary-grid/components/summary-grid-item-custom/summary-grid-item-custom.vue +1 -0
  350. package/dist/assets/components/summary-grid/components/summary-grid-item-numeric/summary-grid-item-numeric.stories.ts +1 -0
  351. package/dist/assets/components/summary-grid/components/summary-grid-item-numeric/summary-grid-item-numeric.vue +1 -0
  352. package/dist/assets/components/summary-grid/components/summary-grid-item-percentage/summary-grid-item-percentage.stories.ts +1 -0
  353. package/dist/assets/components/summary-grid/components/summary-grid-item-percentage/summary-grid-item-percentage.vue +1 -0
  354. package/dist/assets/components/summary-grid/components/summary-grid-item-text/summary-grid-item-text.stories.ts +1 -0
  355. package/dist/assets/components/summary-grid/components/summary-grid-item-text/summary-grid-item-text.vue +1 -0
  356. package/dist/assets/components/summary-grid/summary-grid.docs.mdx +152 -0
  357. package/dist/assets/components/summary-grid/summary-grid.stories.ts +1 -0
  358. package/dist/assets/components/summary-grid/summary-grid.vue +1 -0
  359. package/dist/assets/components/tabs/components/tab.vue +1 -0
  360. package/dist/assets/components/tabs/tabs.docs.mdx +98 -0
  361. package/dist/assets/components/tabs/tabs.stories.ts +1 -0
  362. package/dist/assets/components/tabs/tabs.vue +1 -0
  363. package/dist/assets/components/tag/tag.docs.mdx +137 -0
  364. package/dist/assets/components/tag/tag.stories.ts +1 -0
  365. package/dist/assets/components/tag/tag.vue +1 -0
  366. package/dist/assets/components/textarea/textarea.docs.mdx +53 -0
  367. package/dist/assets/components/textarea/textarea.stories.ts +1 -0
  368. package/dist/assets/components/textarea/textarea.vue +1 -0
  369. package/dist/assets/components/timeline/components/timeline-item/timeline-item.stories.ts +1 -0
  370. package/dist/assets/components/timeline/components/timeline-item/timeline-item.vue +1 -0
  371. package/dist/assets/components/timeline/timeline.docs.mdx +131 -0
  372. package/dist/assets/components/timeline/timeline.stories.ts +1 -0
  373. package/dist/assets/components/timeline/timeline.vue +1 -0
  374. package/dist/assets/components/toast/components/toast-item/toast-item.vue +1 -0
  375. package/dist/assets/components/toast/components/toast-shadow-stack/toast-shadow-stack.vue +1 -0
  376. package/dist/assets/components/toast/toast.docs.mdx +151 -0
  377. package/dist/assets/components/toast/toast.stories.ts +1 -0
  378. package/dist/assets/components/toast/toast.vue +1 -0
  379. package/dist/assets/components/toggle/toggle.docs.mdx +81 -0
  380. package/dist/assets/components/toggle/toggle.stories.ts +1 -0
  381. package/dist/assets/components/toggle/toggle.vue +1 -0
  382. package/dist/assets/components/tutorial/components/tutorial-popover/components/tutorial-indicator/tutorial-indicator.vue +1 -0
  383. package/dist/assets/components/tutorial/components/tutorial-popover/tutorial-popover.vue +1 -0
  384. package/dist/assets/components/tutorial/tutorial.docs.mdx +85 -0
  385. package/dist/assets/components/tutorial/tutorial.stories.ts +1 -0
  386. package/dist/assets/components/tutorial/tutorial.vue +1 -0
  387. package/dist/assets/components/typography/typography.docs.mdx +124 -0
  388. package/dist/assets/components/typography/typography.stories.ts +1 -0
  389. package/dist/assets/components/typography/typography.vue +1 -0
  390. package/dist/assets/composables/use-bento-currency/use-bento-currency.docs.mdx +28 -0
  391. package/dist/assets/composables/use-bento-currency/use-bento-currency.stories.ts +1 -0
  392. package/dist/assets/composables/use-click-outside/use-click-outside.docs.mdx +10 -0
  393. package/dist/assets/composables/use-click-outside/use-click-outside.stories.ts +1 -0
  394. package/dist/assets/directives/highlight/highlight.docs.mdx +64 -0
  395. package/dist/assets/directives/highlight/highlight.stories.ts +1 -0
  396. package/dist/assets/directives/keyboard-navigation/keyboard-navigation.docs.mdx +41 -0
  397. package/dist/assets/directives/keyboard-navigation/keyboard-navigation.stories.ts +1 -0
  398. package/dist/assets/directives/tooltip/tooltip.docs.mdx +86 -0
  399. package/dist/assets/directives/tooltip/tooltip.stories.ts +1 -0
  400. package/dist/assets/index.ts +1 -0
  401. package/dist/assets/templates/dashboard-template/components/dashboard-section/dashboard-section.stories.ts +1 -0
  402. package/dist/assets/templates/dashboard-template/components/dashboard-section/dashboard-section.vue +1 -0
  403. package/dist/assets/templates/dashboard-template/dashboard-template.docs.mdx +148 -0
  404. package/dist/assets/templates/dashboard-template/dashboard-template.stories.ts +1 -0
  405. package/dist/assets/templates/dashboard-template/dashboard-template.vue +1 -0
  406. package/dist/assets/templates/data-grid-template/data-grid-template.docs.mdx +117 -0
  407. package/dist/assets/templates/data-grid-template/data-grid-template.stories.ts +1 -0
  408. package/dist/assets/templates/data-grid-template/data-grid-template.vue +1 -0
  409. package/dist/main.js +30031 -0
  410. package/package.json +45 -3
@@ -0,0 +1,30 @@
1
+ import { Canvas, Meta } from '@storybook/blocks';
2
+ import * as ClickOutsideStories from './click-outside.stories';
3
+
4
+ <Meta of={ClickOutsideStories} />
5
+
6
+ # Click outside
7
+
8
+ Listen for clicks outside of an element. Useful for modal or dropdown.
9
+
10
+ ## Use Cases
11
+
12
+ Here are some examples:
13
+
14
+ - Close a dropdown, modal or popover when another element in the page is clicked
15
+
16
+ ## Variations
17
+
18
+ ### Component
19
+
20
+ Wrapper component that adds a `onClickOutside` listener to the `default` slot allowing to trigger a `handler` event
21
+ every time a click outside of the `default` slot happens.
22
+
23
+ <Canvas of={ClickOutsideStories.ComponentDefault} />
24
+
25
+ ### Directive
26
+
27
+ Directive that adds a `onClickOutside` listener to the element allowing to trigger a `handler` event every time a click
28
+ outside of the element happens.
29
+
30
+ <Canvas of={ClickOutsideStories.Directive} />
@@ -0,0 +1 @@
1
+ import { action } from '@storybook/addon-actions'; import { BentoClickOutside } from '@/components/click-outside'; import { BentoClickOutsideDirective } from '@/directives/click-outside'; import { BentoTypography } from '@/components/typography'; import { BentoCard } from '@/components/card'; import { ref } from 'vue'; import { type Meta, type StoryObj } from '@storybook/vue'; import { isVue2 } from 'vue-demi'; import { storybookDocsParameter } from '@/utils/ts/storybook'; export default { title: 'Click outside', component: BentoClickOutside, argTypes: { // Events handler: { name: 'handler', defaultValue: '', description: 'Callback handler function to be called when a click outside the element is triggered.', table: { type: { summary: '(event: FocusEvent | PointerEvent) => void' } }, control: { disable: true }, }, }, } as Meta; type Story = StoryObj<typeof BentoClickOutside>; const DEFAULT_TITLE = 'This text is inside '; const defaultCode = ` <bento-click-outside @handler="clickOutsideHandlerMethod"> <div>Your content goes in here...</div> </bento-click-outside> `; export const ComponentDefault: Story = { render: (_args, { argTypes }) => ({ components: { BentoCard, BentoClickOutside, BentoTypography, }, props: Object.keys(argTypes), setup(props) { const totalClickOutsideCount = ref(0); const clickedAction = () => { totalClickOutsideCount.value += 1; return action('trigger'); }; return { // Values args: isVue2 ? props : _args, totalClickOutsideCount, // Events clickedAction, }; }, template: ` <div> <bento-click-outside @handler="clickedAction"> <bento-card> {{args.title}} </bento-card> </bento-click-outside> <bento-typography>Total click outside events triggered: {{totalClickOutsideCount}} </bento-typography> </div> `, }), args: { title: DEFAULT_TITLE, }, parameters: storybookDocsParameter(defaultCode), }; const directiveCode = ` <div v-bento-click-outside-directive="clickOutsideHandlerMethod"> <div>Your content goes in here...</div> </div> `; export const Directive: Story = { render: (_args, { argTypes }) => ({ components: { BentoCard, BentoTypography }, directives: { BentoClickOutsideDirective }, props: Object.keys(argTypes), setup(props) { const totalClickOutsideCount = ref(0); const clickedAction = () => { totalClickOutsideCount.value += 1; return action('trigger'); }; return { // Values totalClickOutsideCount, args: isVue2 ? props : _args, // Events clickedAction, }; }, template: ` <div> <div v-bento-click-outside-directive="clickedAction"> <bento-card> {{args.title}} </bento-card> </div> <bento-typography>Total click outside events triggered: {{totalClickOutsideCount}} </bento-typography> </div> `, }), args: { title: DEFAULT_TITLE, }, parameters: storybookDocsParameter(directiveCode), };
@@ -0,0 +1,73 @@
1
+ import { Canvas, Meta } from '@storybook/blocks';
2
+ import * as CodeSnippetStories from './code-snippet.stories';
3
+
4
+ <Meta of={CodeSnippetStories} />
5
+
6
+ # Code Snippet
7
+
8
+ `BentoCodeSnippet` is used for displaying and editing code snippets with syntax highlighting and additional features
9
+ like
10
+
11
+ <Canvas of={CodeSnippetStories.Default} />
12
+
13
+ ## Use Cases
14
+
15
+ - Use when you need to display code. This could be for multi-line, single line and in-line code snippets
16
+
17
+ ### Do not use
18
+
19
+ - Showing elements that are not code and do not need syntax highlighting
20
+
21
+ ## Variations
22
+
23
+ ### Multi-line
24
+
25
+ Without specifying a `type` the `BentoCodeSnippet` will expect and display multi-line content with standard features.
26
+
27
+ <Canvas of={CodeSnippetStories.Default} />
28
+
29
+ ### Single-line
30
+
31
+ The `type="inline"` allows for a more compact version of the `BentoCodeSnippet`, with no line numbers or expansion.
32
+
33
+ <Canvas of={CodeSnippetStories.SingleLine} />
34
+
35
+ ### Inline
36
+
37
+ The `type="inline"` creates an inline text to fit withi text without taking extra space or any including extra features.
38
+
39
+ <Canvas of={CodeSnippetStories.Inline} />
40
+
41
+ ## Modifiers
42
+
43
+ ### Editable
44
+
45
+ `BentoCodeSnippet` can be made editable with real-time syntax highlighting with the `editable` prop. This is recommended
46
+ only when needed and is disabled by default.
47
+
48
+ ### Actionable
49
+
50
+ A button can be added to the `BentoCodeSnippet` action bar with the `actionable` prop. Clicking it will emit the event
51
+ 'action' containing the contents of the code snippet. The text can be customized with `actionableText`
52
+
53
+ ### Expandable
54
+
55
+ WIP
56
+
57
+ ## Accessibility
58
+
59
+ ### Keyboard interaction
60
+
61
+ `BentoCodeSnippet` is reachable through keyboard navigation and follows logical navigation flow of the page. Both its
62
+ button actions and code are interactable via the keyboard as described in the table below.
63
+
64
+ | Keyboard interaction | Output |
65
+ | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
66
+ | `Tab` | Moves focus to the various buttons in action bar and the text contents of the code snippet. |
67
+ | Various | The code snippet content can be interacted with all the keys supported by the system, such as arrows to move, shift+arrows to select, etc. If text manipulation is allowed via the `editable` prop, then typing, backspace to delete and enter for newline are also allowed |
68
+
69
+ ### Roles, states and properties
70
+
71
+ ## Resources
72
+
73
+ - [Figma link](https://www.figma.com/file/uLabwF3243jdMDsNSP7U9I/Bento---Components?type=design&node-id=9496-20847&mode=design&t=xEZ7kBL87KCfxz0n-0)
@@ -0,0 +1 @@
1
+ import BentoCodeSnippet from './code-snippet.vue'; import type { Meta, StoryObj } from '@storybook/vue'; import { isVue2 } from 'vue-demi'; import { BentoCodeSnippetSupportedLanguage, BentoCodeSnippetVariant } from './code-snippet.types'; import { BentoTypography } from '@/components/typography'; const meta: Meta = { title: 'Code snippet', component: BentoCodeSnippet, argTypes: { variant: { options: Object.values(BentoCodeSnippetVariant), mapping: BentoCodeSnippetVariant, control: { type: 'select', }, }, lang: { options: Object.values(BentoCodeSnippetSupportedLanguage), mapping: BentoCodeSnippetSupportedLanguage, control: { type: 'select', }, defaultValue: BentoCodeSnippetSupportedLanguage.JSON, }, }, }; export default meta; type Story = StoryObj<typeof BentoCodeSnippet>; const DEFAULT_TITLE = 'title'; const CODE_EXAMPLE_JSON_MULTI_LINE = `{ "id" : "good-51774", "amount" : 2, "spicy" : true, "condiments" : { "tomato" : true, "mozzarella" : true, "pineapple" : false } }`; const CODE_EXAMPLE_JSON_SINGLE_LINE = '{ "id": 1234 }'; const CODE_EXAMPLE_XML = `<note> <to>Recipient</to> <from>Sender</from> <heading>Reminder</heading> <body>Do not forget to test xml notes.</body> </note>`; const CODE_EXAMPLE_JAVASCRIPT = `let firstName = 'John'; const helloBento = (name) => { console.log('Hello from the bento team, '+ name); } helloBento(firstName); `; const CODE_EXAMPLE_JAVA = `/** * * * {@snippet class="Bento" region="binary"} */ public class HelloFromBento { public void helloBento() { System.out.println("Hello from the Bento team."); } }`; const CODE_EXAMPLE_PYTHON = `/** from lib_workflow.backend.artifactory import FileSystemArtifactory artifactory = FileSystemArtifactory("{{template-this}}") artifactory.list_artifacts() `; const CODE_EXAMPLE_CSV = `name,age,height john doe,30,178 `; const defaultArgs = { title: DEFAULT_TITLE, editable: true, actionable: true, }; export const Default: Story = { render: (_args, { argTypes }) => ({ components: { BentoCodeSnippet }, props: Object.keys(argTypes), template: ` <div> <bento-code-snippet v-bind="args"/> </div> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }), args: { ...defaultArgs, expandable: true, content: CODE_EXAMPLE_JSON_MULTI_LINE, }, }; export const Inline: Story = { render: (_args, { argTypes }) => ({ components: { BentoCodeSnippet, BentoTypography }, props: Object.keys(argTypes), template: ` <bento-typography> Some time ago <bento-code-snippet variant='inline' v-bind="args"/> in there </bento-typography> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }), args: { ...defaultArgs, content: CODE_EXAMPLE_JSON_SINGLE_LINE, }, }; export const SingleLine: Story = { render: (_args, { argTypes }) => ({ components: { BentoCodeSnippet }, props: Object.keys(argTypes), template: ` <bento-code-snippet variant='singleline' v-bind="args"/> `, setup(props) { return { singleLineCode: CODE_EXAMPLE_JSON_SINGLE_LINE, args: isVue2 ? props : _args, }; }, }), args: { ...defaultArgs, content: CODE_EXAMPLE_JSON_SINGLE_LINE, }, }; export const XML: Story = { ...Default, args: { ...Default.args, lang: 'xml', content: CODE_EXAMPLE_XML, }, }; export const Javascript: Story = { ...Default, args: { ...Default.args, lang: 'javascript', content: CODE_EXAMPLE_JAVASCRIPT, }, }; export const Java: Story = { ...Default, args: { ...Default.args, lang: 'java', content: CODE_EXAMPLE_JAVA, }, }; export const Python: Story = { ...Default, args: { ...Default.args, lang: 'python', content: CODE_EXAMPLE_PYTHON, }, }; export const CSV: Story = { ...Default, args: { ...Default.args, lang: 'csv', content: CODE_EXAMPLE_CSV, }, };
@@ -0,0 +1 @@
1
+ <template> <div class="b-code-snippet" :class="conditionalClasses"> <section v-if="!isInlineVariant" class="b-code-snippet__action-bar"> <div class="b-code-snippet__action-bar--start"> <bento-dropdown v-if="availableLanguages.length > 0" v-model="selectedLanguage" v-bind="$props" :disabled="availableLanguages.length === 1" :items="availableLanguages" class="b-code-snippet__language-picker" @input="handleChangeLanguage" /> <bento-typography el="span" stronger> {{ displayedLang }} </bento-typography> </div> <div class="b-code-snippet__action-bar--end"> <bento-loading-indicator v-if="loading" /> <copy v-if="currentContents !== null" class="b-structured-list__icon" :text="currentContents" /> <bento-button v-if="expandable" variant="tertiary" condensed :aria-label="t('expand')" @click="showModal = true" > <template #iconLeft> <expand-icon /> </template> </bento-button> <bento-button v-if="actionable" variant="primary" tertiary condensed @click="onActionClicked"> <template #iconLeft> <bento-typography el="span" class="b-code-snippet__action-button"> {{ runText }} </bento-typography> </template> </bento-button> </div> </section> <code v-if="content || editable" class="b-code-snippet__code-area"> <div ref="container" class="b-code-snippet__code-area-content" :class="codeAreaContentConditionalClasses" /> </code> <section v-else> <slot name="empty-state"> <!-- TODO: switch with empty-state component when ready --> <div class="b-code-snippet__empty-state">{{ t('nothingToDisplay') }}</div> </slot> </section> <base-modal v-if="expandable" class="b-code-snippet__modal" :hide-close-button="true" :custom-padding="'0px'" :is-dismissible="true" :is-open="showModal" size="large" @close-modal="showModal = false" > <template #content> <bento-code-snippet v-bind="{ ...$props, expandable: false }" /> </template> </base-modal> </div> </template> <script setup lang="ts"> import { computed, type HTMLAttributes, onMounted, ref, shallowRef, useAttrs, watch, watchEffect } from 'vue'; import { EditorView, highlightActiveLine, keymap, lineNumbers } from '@codemirror/view'; import { Compartment, EditorState } from '@codemirror/state'; import { history, indentWithTab } from '@codemirror/commands'; import { lightThemeHighlight } from './highlight'; import { getLangLoader, INDENT_SAME_AS_LINE_BEFORE } from './utils'; import { BentoDropdown } from '@/components/dropdown'; import { BentoButton } from '@/components/button'; import { BentoTypography } from '@/components/typography'; import { BentoLoadingIndicator } from '@/components/loading-indicator'; import { useI18n } from '@/utils/ts/i18n'; import { Copy } from '@/internal'; import { BentoCodeSnippetEvent, type BentoCodeSnippetProps, BentoCodeSnippetSupportedLanguage, BentoCodeSnippetVariant, } from './code-snippet.types'; import ExpandIcon from '@adyen/ui-assets-icons-16/vue/expand'; import BaseModal from '@/components/modal/components/base-modal/base-modal.vue'; import messages from './messages.json'; type MessageSchema = (typeof messages)['en-US']; const props = withDefaults(defineProps<BentoCodeSnippetProps>(), { actionable: false, actionableText: null, content: '', editable: false, expandable: false, lang: BentoCodeSnippetSupportedLanguage.JSON, langLabel: null, loading: false, variant: BentoCodeSnippetVariant.MULTILINE, }); const emit = defineEmits([BentoCodeSnippetEvent.ACTION, BentoCodeSnippetEvent.CHANGE]); const attrs = useAttrs(); const { t } = useI18n<{ message: MessageSchema }>({ messages }); // language const selectedLanguage = ref<string | null>(null); const availableLanguages = []; // TODO: implement // eslint-disable-next-line @typescript-eslint/no-empty-function const handleChangeLanguage = () => {}; const displayedLang = computed(() => props.langLabel ?? props.lang.toUpperCase()); const isInlineVariant = computed(() => props.variant === BentoCodeSnippetVariant.INLINE); const conditionalClasses = computed(() => ({ 'b-code-snippet--inline': isInlineVariant.value, 'b-code-snippet--scrollable': !isInlineVariant.value, })); const codeAreaContentConditionalClasses = computed(() => ({ 'b-code-snippet__code-area-content--editable': props.editable, })); const loadLang = getLangLoader(props.lang); const container = shallowRef<HTMLDivElement>(); const editor = shallowRef<EditorView>(); const languageConf = new Compartment(); const editorTheme = new Compartment(); const editorReadOnly = new Compartment(); const editorAttributes = new Compartment(); const currentContents = ref(null); const contentLength = ref(0); const runText = computed(() => props.actionableText || t('run')); onMounted(async () => { const language = await loadLang(); // alongside the updateListener plugin it acts as a watcher of changes to the editor state currentContents.value = props.content; const updateCurrentContents = ({ state }) => { currentContents.value = state.doc.toString(); }; const extensions = [ highlightActiveLine(), keymap.of([indentWithTab, INDENT_SAME_AS_LINE_BEFORE]), languageConf.of(language()), editorTheme.of(lightThemeHighlight(props.lang)), editorReadOnly.of(EditorState.readOnly.of(!props.editable)), editorAttributes.of( EditorView.contentAttributes.of({ 'aria-label': (attrs['aria-label'] as HTMLAttributes['aria-label']) ?? t('codeSnippet'), ...(!isInlineVariant.value && { tabindex: '0' }), }) ), EditorView.updateListener.of(content => updateCurrentContents(content)), ]; if (props.variant === 'multiline') { extensions.push([lineNumbers(), history()]); } const state = EditorState.create({ doc: props.content, extensions: [...extensions], }); contentLength.value = state.doc.length; editor.value = new EditorView({ state, parent: container.value, }); }); watch( () => props.content, () => { if (props.content !== currentContents.value) { const upcomingContentLength = props.content.length; editor.value.dispatch({ changes: { from: 0, to: contentLength.value, insert: props.content, }, }); contentLength.value = upcomingContentLength; } } ); const onActionClicked = () => { emit(BentoCodeSnippetEvent.ACTION, currentContents.value); }; watchEffect(() => { if (currentContents.value !== null) { emit(BentoCodeSnippetEvent.CHANGE, currentContents.value); } }); const showModal = ref(false); </script> <script lang="ts"> /** * A code-snippet is a container to display code in a readable way. * Come with added functionalities like syntax highlighting, editing and actioning the code * * @example * import { BentoCodeSnippet } from '@adyen/bento-vue2'; * * export default { * components: { BentoCodeSnippet }, * template: ` * <bento-code-snippet :variant="BentoCodeSnippetVariant.MULTILINE" content="{ example: 1234 }" /> * ` * } */ export default { i18n: { messages }, name: 'bento-code-snippet', }; </script> <style lang="scss" scoped src="./code-snippet.scss"></style>
@@ -0,0 +1,61 @@
1
+ import { Canvas, Story, Meta } from '@storybook/blocks';
2
+ import * as ComponentStories from './country.stories';
3
+
4
+ <Meta of={ComponentStories} />
5
+
6
+ # Country
7
+
8
+ A Country component to display the country's label. The label comes in four variates: iso code, country name, phone
9
+ number code or capital city.
10
+
11
+ <Canvas of={ComponentStories.Default} />
12
+
13
+ ## Use Cases
14
+
15
+ Use a Country if:
16
+
17
+ - You need to display country information in a visual way.
18
+ - Need to show a list of phone number codes.
19
+ - Need to show a list of capitals.
20
+
21
+ ### Do not use
22
+
23
+ In a filter or dropdown, there are components specific for those usages.
24
+
25
+ ## Code
26
+
27
+ <Canvas of={ComponentStories.Default} />
28
+
29
+ To determine which country the component should display, the `code` property is used. The value passed to this property
30
+ should translate to a two character country code in
31
+ ([ISO 3166-1 alpha-2 format](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)).
32
+
33
+ ## Variations
34
+
35
+ Use the `variant` prop to change the label of the country. This prop can take the following values:
36
+
37
+ - `'iso'`
38
+ - `'name'`
39
+ - `'phone_code'`
40
+ - `'capital'`
41
+
42
+ ### Name (default)
43
+
44
+ Displays the country's name as the label.
45
+
46
+ ### ISO
47
+
48
+ Displays the two character country code ([ISO 3166-1 alpha-2 format](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2))
49
+ as the label.
50
+
51
+ ### Phone code
52
+
53
+ Displays ISO code and phone number code as the label, e.g. NL (+31).
54
+
55
+ ### Capital
56
+
57
+ Displays the country and capital city as the label, e.g. Amsterdam (Netherlands)
58
+
59
+ ## Resources
60
+
61
+ - [Figma link](https://www.figma.com/file/uLabwF3243jdMDsNSP7U9I/Bento---Components?type=design&node-id=2882%3A78027&t=0ACEzlBBN6wJNhg1-1)
@@ -0,0 +1 @@
1
+ import { BentoCountryCode, BentoCountryVariant } from './country.types'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import BentoCountry from './country.vue'; import { isVue2 } from 'vue-demi'; const COUNTRY_CODES_LIST = Object.values(BentoCountryCode); const ALPHABETICALLY_ORDERED_COUNTRY_CODES = COUNTRY_CODES_LIST.sort(); export default { title: 'Country', component: BentoCountry, argTypes: { code: { options: ALPHABETICALLY_ORDERED_COUNTRY_CODES, mapping: BentoCountryCode, control: { type: 'select', }, }, variant: { options: Object.values(BentoCountryVariant), mapping: BentoCountryVariant, control: { type: 'select', labels: { custom: 'DEPRECATED: custom', }, }, }, }, }; export const Default = (_args, { argTypes }) => ({ components: { BentoCountry }, props: Object.keys(argTypes), template: ` <bento-country v-bind="args"/> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }); Default.args = { code: BentoCountryCode.NL, }; Default.parameters = storybookDocsParameter(` <bento-country :code="BentoCountryCode.NL"/> `);
@@ -0,0 +1 @@
1
+ <template> <div class="b-country"> <div class="b-country__label"> <span v-if="isSummaryGridLayout">{{ countryLabel }}</span> <bento-typography v-else el="span">{{ countryLabel }}</bento-typography> </div> </div> </template> <script setup lang="ts"> import { inject, ref, toRefs, watch } from 'vue'; import { BentoTypography } from '@/components/typography'; import { useCountryCapital, useCountryName, useCountryPhoneCode } from './composables'; import { BentoCountryCode, type BentoCountryProps, BentoCountryVariant } from './country.types'; import { useI18n } from '@/utils/ts/i18n'; import { deprecate } from '@/utils/ts/deprecate'; import { type BentoSummaryGridLayout } from '@/components/summary-grid'; import { SUMMARY_GRID_LAYOUT_INJECTION_KEY } from '@/components/summary-grid/summary-grid.keys'; const props = withDefaults(defineProps<BentoCountryProps>(), { code: BentoCountryCode.UNKNOWN, label: undefined, variant: BentoCountryVariant.NAME, }); const { code, variant, label: customLabel } = toRefs(props); const { locale } = useI18n(undefined, true); const countryName = useCountryName(code, locale); const countryLabel = ref(''); const getLabel = () => { switch (variant.value) { case BentoCountryVariant.ISO: return code.value; case BentoCountryVariant.PHONE_CODE: return `${code.value} (${useCountryPhoneCode(code).value})`; case BentoCountryVariant.CAPITAL: // TODO: do we need an API OR get a longer translations list??? // Need to be able TRANSLATE as well as map... return `${useCountryCapital(code, locale).value} (${countryName.value})`; case BentoCountryVariant.CUSTOM: return customLabel.value; case BentoCountryVariant.NAME: default: return countryName.value; } }; // Update label if variant prop changes watch( [variant, locale, code], () => { countryLabel.value = getLabel(); }, { immediate: true } ); if (variant.value === 'custom' || props.label) { deprecate( 'BentoCountry "custom" variant and "label" prop', 'Use any of the other BentoCountry variants instead OR BentoTypography component for custom text', '2.0.0' ); } /* == Summary-grid styles == */ const registerPaymentMethodInSummaryGrid: BentoSummaryGridLayout = inject( SUMMARY_GRID_LAYOUT_INJECTION_KEY, () => ({ isSummaryGridLayout: false, }) ); const { isSummaryGridLayout } = registerPaymentMethodInSummaryGrid(); </script> <script lang="ts"> /** * A component to display a country's label. * The label comes in four variates: iso code, country name, phone number code or capital city. * * @usage * import { BentoCountry, BentoCountryCode } from '@adyen/bento-vue2'; * * export default { * components: { BentoCountry }, * template: ` * <bento-country :code="BentoCountryCode.NL" /> * ` * } */ export default { name: 'bento-country', }; </script> <style lang="scss" scoped src="./country.scss" />
@@ -0,0 +1,38 @@
1
+ import { Canvas, Meta } from '@storybook/blocks';
2
+ import * as CurrencyStories from './currency.stories';
3
+
4
+ <Meta of={CurrencyStories} />
5
+
6
+ # Currency
7
+
8
+ A currency component is used to display a formatted currency value. It renders the formatted currency value with the
9
+ numeric amount and currency ISO code appended.
10
+
11
+ <Canvas of={CurrencyStories.Default} />
12
+
13
+ ## Use Cases
14
+
15
+ Use currency component for:
16
+
17
+ - displaying payment amounts in data-grid
18
+
19
+ ## Variations
20
+
21
+ ### Major Units
22
+
23
+ `bento-currency` component uses minor currency units by default (e.g. cents for Euro).
24
+
25
+ If you want to use major units, then `majorUnits` property should be added to `bento-currency` component
26
+
27
+ <Canvas of={CurrencyStories.MajorUnits} />
28
+
29
+ ## Accessibility
30
+
31
+ ### Keyboard Interaction
32
+
33
+ The currency component is not reachable through keyboard navigation since it's a non-interactive element that displays a
34
+ readonly currency information
35
+
36
+ ## Resources
37
+
38
+ - [Figma link](https://www.figma.com/file/uLabwF3243jdMDsNSP7U9I/Bento---Components?type=design&node-id=2882-78027&t=ZR8lRIGw62b0oKKO-0)
@@ -0,0 +1 @@
1
+ import { storybookDocsParameter } from '@/utils/ts/storybook'; import { BentoCurrencyISOCode, type BentoCurrencyProps } from '@/components/currency/currency.types'; import { isVue2 } from 'vue-demi'; import { type Meta, type StoryObj } from '@storybook/vue'; import BentoCurrency from './currency.vue'; export default { title: 'Currency', component: BentoCurrency, argTypes: { default: { name: 'default (deprecated)', defaultValue: '', description: 'Amount value. Use `value` property instead', table: { type: { summary: 'VNode[]' }, }, control: { type: 'text', disable: true, }, }, currency: { options: Object.values(BentoCurrencyISOCode), mapping: BentoCurrencyISOCode, control: { type: 'select', }, }, currencySign: { options: ['standard', 'accounting'], mapping: BentoCurrencyISOCode, control: { type: 'select', }, }, }, } as Meta; type Story = StoryObj<typeof BentoCurrency>; const DEFAULT_CURRENCY_PROPS: BentoCurrencyProps = { currency: BentoCurrencyISOCode.EUR, currencySign: 'standard', majorUnits: false, value: 123423, }; const DefaultTemplate: Story = { render: (_args, { argTypes }) => ({ components: { BentoCurrency }, props: Object.keys(argTypes), template: ` <bento-currency v-bind="args" /> `, setup(props) { return { args: isVue2 ? props : _args, }; }, }), }; const defaultCode = ` <bento-currency :currency="BentoCurrencyISOCode.EUR" :value="${DEFAULT_CURRENCY_PROPS.value}" :major-units="false" /> `; export const Default = { ...DefaultTemplate, args: { ...DEFAULT_CURRENCY_PROPS, }, parameters: storybookDocsParameter(defaultCode), }; const majorUnitsCode = ` <bento-currency :currency="BentoCurrencyISOCode.EUR" :value="${DEFAULT_CURRENCY_PROPS.value}" major-units /> `; export const MajorUnits = { ...DefaultTemplate, args: { ...DEFAULT_CURRENCY_PROPS, majorUnits: true, }, parameters: storybookDocsParameter(majorUnitsCode), };
@@ -0,0 +1 @@
1
+ <template> <div class="b-currency"> <bento-typography v-if="!disableTypography" class="b-currency__amount" el="span" variant="body" strong> {{ formattedCurrency }} </bento-typography> <template v-else>{{ formattedCurrency }}</template> </div> </template> <script setup lang="ts"> import { computed, onMounted, useSlots } from 'vue'; import { BentoTypography } from '@/components/typography'; import { useBentoCurrency } from '@/composables/use-bento-currency/use-bento-currency'; import { deprecate } from '@/utils/ts/deprecate'; import { getSlotText } from '@/utils/ts/get-slot-text'; import { BentoCurrencyISOCode, type BentoCurrencyProps } from './currency.types'; const slots = useSlots(); const props = withDefaults(defineProps<BentoCurrencyProps>(), { currency: BentoCurrencyISOCode.EUR, disableTypography: false, majorUnits: false, value: undefined, currencySign: 'standard', }); const { formatCurrency } = useBentoCurrency(); const formattedCurrency = computed(() => { const rawCurrencyValue = props.value ?? getSlotText(slots)('default'); return formatCurrency(rawCurrencyValue, { currency: props.currency, currencySign: props.currencySign, majorUnits: props.majorUnits, }); }); onMounted(() => { if (getSlotText(slots)('default')) { deprecate( 'BentoCurrency "default" slot', `The use of "default" slot in "BentoCurrency" is no longer supported as it causes issues with dynamic content. Use the "value" property instead. <bento-currency :value="12345.67" currency="USD" major-units="false" />`, '2.0.0' ); } }); </script> <script lang="ts"> /** * A Currency component is used to display a formatted currency value. * * @example * import { BentoCurrency, BentoCurrencyISOCode } from '@adyen/bento-vue2'; * * export default { * components: { BentoCurrency }, * template: ` * <bento-currency * :currency="BentoCurrencyISOCode.USD" * :value="76.33" * /> * ` * } */ export default { name: 'bento-currency', i18n: {}, // required to enable E2E to run as they do not have vue-i18n set up }; </script> <style lang="scss" scoped src="./currency.scss" />
@@ -0,0 +1,57 @@
1
+ import { Canvas, Meta } from '@storybook/blocks';
2
+ import * as DashedUnderlineStories from './dashed-underline.stories';
3
+
4
+ <Meta of={DashedUnderlineStories} />
5
+
6
+ # Dashed underline
7
+
8
+ The dashed underline serves as a visual cue to indicate that additional information is available for a specific word or
9
+ phrase without adding an icon. When users hover over the underlined text, a tooltip or popover will appear, providing
10
+ further explanation or context related to that specific term.
11
+
12
+ <Canvas of={DashedUnderlineStories.WithTooltip} />
13
+
14
+ ## Use Cases
15
+
16
+ Use a dashed underline if:
17
+
18
+ - You need to offer contextual guidance for one or two words within a sentence, or a single term
19
+ - There is limited space (e.g.: data-grid column headers)
20
+ - There are multiple instances of contextual guidance on a single page, to declutter the page
21
+
22
+ ### Do not use
23
+
24
+ - If there's a single contextual guidance item in the page with enough space - you should use an info icon instead.
25
+
26
+ ## Variations
27
+
28
+ ### With tooltip
29
+
30
+ For more concise and single-purpose extra information, use the dashed underline with a tooltip:
31
+
32
+ <Canvas of={DashedUnderlineStories.WithTooltip} />
33
+
34
+ ### With popover
35
+
36
+ For larger and/or more complex extra information, including links to other pages/sections, use the dashed underline with
37
+ a popover:
38
+
39
+ <Canvas of={DashedUnderlineStories.WithPopover} />
40
+
41
+ In both tooltip and popover variations, you can configure the position of the attached element using the respective
42
+ properties.
43
+
44
+ ## Accessibility
45
+
46
+ ### Keyboard interaction
47
+
48
+ `BentoDashedUnderline` is reachable through keyboard navigation as a focusable element. This allows for triggering the
49
+ attached tooltip/popover/clickable action via keyboard only.
50
+
51
+ | Keyboard interaction | Output |
52
+ | -------------------- | ---------------------------------- |
53
+ | `Tab` | Moves focus to the annotated text. |
54
+
55
+ ## Resources
56
+
57
+ - [Figma link](https://www.figma.com/design/uLabwF3243jdMDsNSP7U9I/Bento---Components?node-id=30676-14818&t=qCTheTgk60Jf4Rfj-4)
@@ -0,0 +1 @@
1
+ import { isVue2 } from 'vue-demi'; import type { Meta, StoryObj } from '@storybook/vue'; import { storybookDocsParameter } from '@/utils/ts/storybook'; import { BentoLink } from '@/components/link'; import { BentoPopoverPositions } from '@/components/popover'; import { BentoTooltipPosition } from '@/directives'; import { BentoTypography } from '@/components/typography'; import BentoDashedUnderline from './dashed-underline.vue'; const TELEPORT_DEFAULT = { disabled: true, }; const meta: Meta = { title: 'Dashed underline', component: BentoDashedUnderline, argTypes: { // Enums tooltipPosition: { options: Object.values(BentoTooltipPosition), mapping: BentoTooltipPosition, control: { type: 'select', }, }, popoverPosition: { options: Object.values(BentoPopoverPositions), mapping: BentoPopoverPositions, control: { type: 'select', }, }, // Slots default: { defaultValue: 'annotated text', description: 'Dashed underline content', table: { type: { summary: 'VNode[]' }, }, control: { type: 'text' }, }, }, }; export default meta; type Story = StoryObj<typeof BentoDashedUnderline>; const withTooltipCode = ` <bento-dashed-underline tooltip-text="Some extra info">annotated text</bento-dashed-underline> `; export const WithTooltip: Story = { render: (_args, { argTypes }) => ({ components: { BentoTypography, BentoDashedUnderline }, props: Object.keys(argTypes), template: ` <bento-typography> This is an example of some <bento-dashed-underline v-bind="args">{{args.default}}</bento-dashed-underline>. </bento-typography> `, setup(props) { return { // Values args: isVue2 ? props : _args, }; }, }), args: { default: 'annotated text with tooltip', tooltipText: 'Some extra info', }, parameters: storybookDocsParameter(withTooltipCode), }; const withPopoverCode = ` <bento-dashed-underline> annotated text <template #popover-content> Some extra info. <bento-link>Learn more here</bento-link> </template> </bento-dashed-underline> `; export const WithPopover: Story = { render: (_args, { argTypes }) => ({ components: { BentoTypography, BentoDashedUnderline, BentoLink }, props: Object.keys(argTypes), template: ` <bento-typography> This is an example of some <bento-dashed-underline v-bind="args"> {{ args.default }} <template v-if="!args.popoverText" #popover-content> Some extra info. <bento-link to="https://www.adyen.com/" external> Learn more here </bento-link> </template> </bento-dashed-underline> </bento-typography> `, setup(props) { return { // Values args: isVue2 ? props : _args, }; }, }), args: { default: 'annotated text with popover', teleport: { ...TELEPORT_DEFAULT }, }, parameters: storybookDocsParameter(withPopoverCode), };
@@ -0,0 +1 @@
1
+ <template> <span ref="element" v-bento-tooltip-directive:[tooltipPosition]="tooltipText" class="b-dashed-underline" tabindex="0" @mouseenter="openPopover" @mouseleave="closePopover" @focus="dashedUnderlineOnFocus" @focusout="dashedUnderlineOnFocusOut" > <span class="b-dashed-underline__content"> <slot></slot> </span> <bento-teleport v-if="element && hasPopover" :disabled="teleport?.disabled" :to="teleport?.to"> <span v-on="computedPopoverContainerEvents"> <bento-popover :open="isOpen" :target-element="element" :position="popoverPosition" role="tooltip" :fit-content="popoverSize === 'fit-content' || undefined" :large="popoverSize === 'large' || undefined" :small="popoverSize === 'small' || undefined" :title="popoverTitle" disable-focus-trap > <slot name="popover-content"> <bento-typography> {{ popoverText }} </bento-typography> </slot> </bento-popover> </span> </bento-teleport> </span> </template> <script lang="ts" setup> import { computed, ref, useSlots, watch } from 'vue'; import { BentoPopover } from '@/components/popover'; import { BentoTooltipDirective as vBentoTooltipDirective } from '@/directives/tooltip/index'; import { BentoTypography } from '@/components/typography'; import { useHasSlot } from '@/composables'; import { BentoTeleport } from '@/internal'; import { type BentoDashedUnderlineProps } from './dashed-underline.types'; const CLOSING_POPOVER_DELAY_MS = 200; const props = withDefaults(defineProps<BentoDashedUnderlineProps>(), { popoverText: null, popoverTitle: null, popoverPosition: 'top', popoverSize: 'fit-content', teleport: () => ({ disabled: true, }), tooltipText: null, tooltipPosition: 'top', }); const slots = useSlots(); const hasSlot = useHasSlot(slots); const element = ref<HTMLSpanElement>(null); const isOpen = ref(false); const closingTimeOutId = ref(); const openPopover = () => { isOpen.value = true; if (closingTimeOutId.value) { clearTimeout(closingTimeOutId.value); } }; const closePopover = () => { // We want to delay closing for a couple of seconds so // it doesn't flicker if a user hovers over the icon and then inside the popover. const timeOutId = setTimeout(() => { isOpen.value = false; }, CLOSING_POPOVER_DELAY_MS); closingTimeOutId.value = timeOutId; }; const dashedUnderlineOnFocus = () => { openPopover(); }; /** * Only close popover on blur if the newly focus element is not a child of this element * @param e - the event that contains the newly focused element */ const dashedUnderlineOnFocusOut = (e: FocusEvent) => { if (!element.value || !(e?.relatedTarget instanceof Element) || !element.value.contains(e.relatedTarget)) { closePopover(); } }; const hasPopover = computed(() => props.popoverText || hasSlot('popover-content')); const shouldShowTooltipPopoverError = computed(() => !props.tooltipText && !hasPopover.value); watch( shouldShowTooltipPopoverError, value => { if (value) { throw new Error( 'BentoDashedUnderline requires either a tooltip or a popover to be configured. Check the docs for more info.' ); } }, { immediate: true } ); /** * When the popover is teleported, it is no longer a DOM descendant of the trigger element. * We add these event listeners to the popover's container to keep the popover open when * the user moves their mouse or focuses out from the trigger to the popover BUT then mouses over * or focuses on the popover content. */ const computedPopoverContainerEvents = computed(() => props.teleport?.disabled === false ? { mouseenter: openPopover, mouseleave: closePopover, focus: dashedUnderlineOnFocus, focusout: dashedUnderlineOnFocusOut, } : {} ); </script> <script lang="ts"> /** * Provides a dashed underline to text, indicating that more information is available on * hover or focus. The additional information is displayed in a tooltip or a popover. * * @example * import { BentoDashedUnderline } from '@adyen/bento-vue2'; * * export default { * components: { BentoDashedUnderline }, * template: ` * <p> * This is some <bento-dashed-underline tooltip-text="A short explanation">annotated text</bento-dashed-underline>. * </p> * ` * } */ export default {}; </script> <style lang="scss" scoped src="./dashed-underline.scss" />
@@ -0,0 +1 @@
1
+ <template> <div :key="`cell-${column.field}`" ref="gridcellRef" :aria-hidden="ariaHidden" :data-testid="`cell-${column.field}`" :data-cellindex="getCellIndex(rowIndex, colIndex, columnSetIndex)" class="b-data-grid-cell" :class="conditionalRowCellClasses(column)" role="gridcell" :tabindex="tabIndex" :aria-colindex="getColIndex(colIndex, columnSetIndex) + 1" :style="{ width: column.autoWidth && isMeasuringColumnWidths ? 'auto' : `${column.width}px`, }" v-on="getGridcellEvents(column)" > <template v-if="column.field === DATAGRID_ACTIONS_COLUMN_FIELD_NAME"> <template v-if="selectable"> <bento-checkbox class="b-data-grid-cell__select-cell" :aria-label="t('selectRow')" :disabled="isDataItemDisabled(rowData)" :indeterminate="selectionIsIndeterminate" :model-value="selectedItems" :value="getDataItemId(rowData)" :tabindex="isFocused ? 0 : -1" @update:model-value="$emit('select-row', $event)" /> </template> <template v-if="nestedContent"> <bento-toggle-button ref="menuButtonRef" aria-haspopup="true" :aria-controls="controlledNestedRowElementIds" condensed variant="tertiary" :toggled="isNestedContentExpanded" @click="toggleNestedContent" > <template #iconLeft> <chevron-up-icon v-if="isNestedContentExpanded" class="b-accordion-item__icon" :svg-title="t('collapseNestedContent')" /> <chevron-down-icon v-else class="b-accordion-item__icon" :svg-title="t('expandNestedContent')" /> </template> </bento-toggle-button> </template> </template> <template v-else> <bento-typography el="div" class="b-data-grid-cell__cell-content" :class="computedCellContentClasses" data-testid="cell-content" > <slot :item="rowData" :row-index="rowIndex" :name="`item-${column.field}`"> <span :title="conditionalTitleAttribute(column, rowData[column.field])">{{ getRowData(rowData[column.field]) }}</span> </slot> </bento-typography> <div v-if="column.cellActions?.length > 0" class="b-data-grid-cell__cell-actions"> <data-grid-cell-actions v-if="(shouldShowCellActions || isTouchDevice) && (hasRowData || hasSlot(`item-${column.field}`))" ref="cellActionsRef" :cell-actions="column.cellActions" :column-key="column.field" :row-data-item="rowData" :has-zebra-background="hasZebraBackground" :is-highlighted="isHighlighted" :is-selected="isSelected" teleport-menu @menu-item-click="onMenuClose" /> </div> </template> </div> </template> <script setup lang="ts"> import { computed, type ComputedRef, inject, nextTick, onMounted, type PropType, provide, type Ref, ref, type UnwrapRef, useSlots, watch, } from 'vue'; import { focusOnInteractiveChild, makeAllInteractiveChildrenFocusable, makeAllInteractiveChildrenUnFocusable, } from '../../utils'; import { DATAGRID_ACTIONS_COLUMN_FIELD_NAME } from '../../composables/use-calculate-column-width'; import { BentoCheckbox } from '@/components/checkbox'; import { BentoTypography } from '@/components/typography'; import { BentoToggleButton } from '@/components/button'; import DataGridCellActions from '../data-grid-cell-actions/data-grid-cell-actions.vue'; import { DATA_GRID_SLASHED_ZERO, DATA_GRID_SMALL_CONTAINER, DATA_GRID_TOUCH_DEVICE } from '../../data-grid.keys'; import ChevronDownIcon from '@adyen/ui-assets-icons-16/vue/chevron-down'; import ChevronUpIcon from '@adyen/ui-assets-icons-16/vue/chevron-up'; import { DATA_GRID_CELL_MENU_REGISTERING_INJECTION_KEY } from '@/components/data-grid/components/data-grid-cell/data-grid-cell.keys'; // Types and enums import { type BentoColumn, BentoColumnOverflow, type BentoDatagridDataItem, type BentoDatagridDataItemId, type BentoDatagridGetDataItemIdFn, type BentoDatagridIsDataItemDisabledFn, } from '@/components/data-grid/data-grid.types'; import { type DataGridCellColumnSet, type DataGridCellDragEventsForColumn, type DataGridCellEvents, type DataGridCellEventsForColumn, DataGridCellPropClass, } from './data-grid-cell.types'; import type { BentoDatagridDraggingColumnState } from '../data-grid-columns/data-grid-columns.types'; import type { DatagridColumnHoveringState } from '@/components/data-grid/composables/use-column-hovering/use-column-hovering.types'; // Utils import { useHasSlot } from '@/composables'; import { useI18n } from '@/utils/ts/i18n'; import { isFocusVisible } from '@/utils/ts/focus-visible'; import { EventKey } from '@/types/utils-events'; // Translations import messages from './messages.json'; const slots = useSlots(); const hasSlot = useHasSlot(slots); type MessageSchema = (typeof messages)['en-US']; const { t } = useI18n<{ message: MessageSchema }>({ messages }); const props = defineProps({ draggingEventsForRowCell: { type: Function as PropType<DataGridCellDragEventsForColumn>, required: true, }, /** * The columns sets including */ columnSets: { type: Array as PropType<Array<DataGridCellColumnSet>>, required: true, }, /** * The columns set we are iterating over */ column: { type: Object as PropType<BentoColumn>, required: true, }, colIndex: { type: Number, required: true, }, columnSetIndex: { type: Number, required: true, }, /** * Sets the row cells to the condensed padding state. */ condensed: { type: Boolean, default: false, }, /** * Columns being dragged, contains dragging column and a target column */ draggingColumns: { type: Object as PropType<BentoDatagridDraggingColumnState>, default: undefined, }, /** * A customisable method to retrieve a unique identifier for a data item in the array of data supplied. * This is used selecting and performance reasons and must exist in each data item. */ getDataItemId: { type: Function as PropType<BentoDatagridGetDataItemIdFn>, default: (item: BentoDatagridDataItem) => item.id, }, /** * Set to true if the cell has a darker background when zebra rows is enabled. */ hasZebraBackground: { type: Boolean, default: false, }, /** * Set to true if the row is selected. */ isSelected: { type: Boolean, default: false, }, /** * Set to true if the row is highlighted. */ isHighlighted: { type: Boolean, default: false, }, /** * The column hovered over state and whether the hovered over column should show a border right or left. */ hoveredOverColumnState: { type: Object as PropType<DatagridColumnHoveringState>, required: true, }, /** * A customisable method to determine if a row is not selectable. * The default is that every row is selectable. */ isDataItemDisabled: { type: Function as PropType<BentoDatagridIsDataItemDisabledFn>, default: () => false, }, isMeasuringColumnWidths: { type: Boolean, default: false, }, /** * Determines if the nested content is visible. */ isNestedContentExpanded: { type: Boolean, default: false, }, /** * Determines if the nest content slot is enabled for this data row. */ nestedContent: { type: Boolean, default: false, }, /** * The IDs of the rows this cell controls to show / hide them. */ nestedRowsElementIds: { type: Array as PropType<Array<string>>, default: () => [], }, /** * The row data object */ rowData: { type: Object as PropType<BentoDatagridDataItem>, required: true, }, rowIndex: { type: Number, required: true, }, /** * The row selection checkbox indeterminate state. */ selectionIsIndeterminate: { type: Boolean, default: false, }, /** * Determines if the row selection is enabled. */ selectable: { type: Boolean, default: false, }, selectedItems: { type: Array as PropType<Array<BentoDatagridDataItemId>>, required: true, }, }); const emit = defineEmits<{ /** * Emitted when an a frozen column cell fires a key up or down event. */ (e: 'on-frozen-columns-key-press', event: Event); /** * Emitted when a row has been selected. */ (e: 'select-row', value: Array<BentoDatagridDataItemId>); /** * Emitted when a row has been selected. */ (e: 'register-row-cell-element', el: Element, field: string, rowIndex: number); /** * Emitted when a cell has been hovered over. This is used to keep the row in a hovered state when hovering over a menu. */ (e: 'cell-hovered', value: number); /** * Emitted when nested content is being toggled. */ (e: 'nested-content-toggled'); /** * Emitted when a cell gains focus. */ ( e: 'cell-focus', value: { rowData: BentoDatagridDataItem; column: BentoColumn; rowIndex: number; colIndex: number } ); }>(); // References const gridcellRef = ref<HTMLDivElement | null>(null); const cellActionsRef = ref<InstanceType<typeof DataGridCellActions>>(null); const shouldShowCellActions = ref(false); const childrenMenuContainerElements = ref<Array<Ref<UnwrapRef<HTMLDivElement>>>>([]); const isFocused = ref(false); const focusableItems = ref<Array<{ node: HTMLElement }>>(); const hasEverBeenFocused = ref(false); // Computed const hasRowData = computed(() => !!props.rowData?.[props.column.field]); const controlledNestedRowElementIds = computed(() => props.column.field === DATAGRID_ACTIONS_COLUMN_FIELD_NAME && props.nestedRowsElementIds?.length > 0 ? props.nestedRowsElementIds.join(' ') : undefined ); const ariaHidden = computed( () => props.column.field === DATAGRID_ACTIONS_COLUMN_FIELD_NAME && !props.selectable && !props.nestedContent ); const tabIndex = computed(() => { if (!ariaHidden.value) { // The first cell of the grid should be tabbable on initial render to allow keyboard navigation into the grid. if (getCellIndex(props.rowIndex, props.colIndex, props.columnSetIndex) === 0 && !hasEverBeenFocused.value) { return '0'; } // A cell becomes part of the tab order only when it is focused. if (isFocused.value) { return '0'; } } // Action cells are not directly tabbable; they are accessed via cell navigation. if (props.column.field === DATAGRID_ACTIONS_COLUMN_FIELD_NAME) { return '-1'; } // By default, cells are not in the tab order. return '-1'; }); // Row data retrieval methods const conditionalTitleAttribute = (column: BentoColumn, content: unknown) => column?.overflow === BentoColumnOverflow.ELLIPSIS ? `${content}` : undefined; const getRowData = (rowData: unknown) => rowData ?? '-'; // Cell and Columns index calculation const sumColumnAmountsInColumnSets = (limitIndex?: number) => props.columnSets.slice(0, limitIndex).reduce((total, { columnSet }) => total + columnSet.length, 0); // Sums the amount of columns on every previous column set to find // the absolute column index (useful for frozen columns use case) const getColIndex = (colIndex: number, colSetIndex: number) => colIndex + sumColumnAmountsInColumnSets(colSetIndex); const getCellIndex = (rowIndex: number, colIndex: number, colSetIndex: number) => sumColumnAmountsInColumnSets() * rowIndex + getColIndex(colIndex, colSetIndex); /** * We check here if we are leaving the cell but mouse into a child menu * If this is the case we don't want to hide the cell actions. * @param relatedTarget */ const isLeavingCellFocus = (relatedTarget: EventTarget) => { if ( relatedTarget === null || (!gridcellRef.value.contains(relatedTarget as Node) && !childrenMenuContainerElements.value ?.map(registeredMenuRef => registeredMenuRef.value) .some(menuElement => menuElement === relatedTarget || menuElement?.contains(relatedTarget as Node))) ) { shouldShowCellActions.value = false; } }; /** * Hides the cell actions when a menu item is clicked. This is necessary as relying solely on * focus-out events is not reliable, especially when the menu is teleported or an action * causes focus to leave the window. */ const onMenuClose = () => { shouldShowCellActions.value = false; }; // Events const hasInteractiveElFocusedInside = ref(false); const getGridcellEvents: DataGridCellEventsForColumn = (column: BentoColumn) => { const defaultEvents: DataGridCellEvents = { ...props.draggingEventsForRowCell(column), // Focus on the first interactive child in the grid cell if one exists focus: async () => { if (!isFocusVisible()) { return; } isFocused.value = true; hasEverBeenFocused.value = true; emit('cell-focus', { rowData: props.rowData, column: props.column, rowIndex: props.rowIndex, colIndex: getColIndex(props.colIndex, props.columnSetIndex), }); makeAllInteractiveChildrenFocusable(focusableItems.value); await nextTick(); const hasFocusedOnInteractiveChild = focusOnInteractiveChild(gridcellRef.value); if (hasFocusedOnInteractiveChild) { hasInteractiveElFocusedInside.value = true; } }, focusout: () => { if (!isFocusVisible()) { return; } hasInteractiveElFocusedInside.value = false; }, keydown: (event: KeyboardEvent) => { if (event.key !== EventKey.TAB) { hasInteractiveElFocusedInside.value = false; isFocused.value = false; makeAllInteractiveChildrenUnFocusable(gridcellRef.value); } }, }; const gridCellEvents: DataGridCellEvents = { ...defaultEvents }; if (column.frozen) { gridCellEvents.keyup = (event: Event) => emit('on-frozen-columns-key-press', event); gridCellEvents.keydown = (event: KeyboardEvent) => { defaultEvents.keydown(event); emit('on-frozen-columns-key-press', event); }; } if (column.cellActions) { gridCellEvents.mouseenter = () => { shouldShowCellActions.value = true; }; gridCellEvents.mouseleave = (e: MouseEvent) => { isLeavingCellFocus(e?.relatedTarget); }; gridCellEvents.focus = async (e: FocusEvent) => { defaultEvents.focus(e); await nextTick(); shouldShowCellActions.value = true; }; gridCellEvents.focusout = (e: FocusEvent) => { defaultEvents.focusout(e); isLeavingCellFocus(e?.relatedTarget); }; } return gridCellEvents; }; onMounted(() => { focusableItems.value = makeAllInteractiveChildrenUnFocusable(gridcellRef.value); }); const toggleNestedContent = () => { emit('nested-content-toggled'); }; // Each cell knows it's on hover state but this state needs to be emitted up // in order for the DataGrid to know which row to set the hovered state on. watch( () => shouldShowCellActions.value, () => { if (shouldShowCellActions.value === true) { emit('cell-hovered', props.rowIndex); } else { emit('cell-hovered', -1); } } ); const registerMenuContainerElement = (menuContainerElement: Ref<UnwrapRef<HTMLDivElement>>) => { childrenMenuContainerElements.value.push(menuContainerElement); }; const menuMouseLeave = (e: MouseEvent) => { if (!gridcellRef.value.contains(e?.relatedTarget as Node)) { shouldShowCellActions.value = false; } }; provide<{ registerMenuContainerElement: (menuContainerElement: Ref<UnwrapRef<HTMLDivElement>>) => void; menuMouseLeave: (e: MouseEvent) => void; }>(DATA_GRID_CELL_MENU_REGISTERING_INJECTION_KEY, { registerMenuContainerElement, menuMouseLeave, }); const hasSlashedZero = inject(DATA_GRID_SLASHED_ZERO, ref(false)); const isSmallContainer: ComputedRef<boolean> = inject( DATA_GRID_SMALL_CONTAINER, computed(() => false) ); const isTouchDevice: Ref<boolean> = inject(DATA_GRID_TOUCH_DEVICE, ref(false)); // Styling const conditionalRowCellClasses = (column: BentoColumn) => ({ [`b-data-grid-cell--${DataGridCellPropClass.COLUMN_HOVERED}`]: column?.field === props.hoveredOverColumnState.hoveredOverColumnField, [`b-data-grid-cell--${DataGridCellPropClass.COLUMN_HOVERED_NO_BORDER_LEFT}`]: column?.field === props.hoveredOverColumnState.hoveredOverColumnField && props.hoveredOverColumnState.noBorderLeft, [`b-data-grid-cell--${DataGridCellPropClass.COLUMN_HOVERED_NO_BORDER_RIGHT}`]: column?.field === props.hoveredOverColumnState.hoveredOverColumnField && props.hoveredOverColumnState.noBorderRight, [`b-data-grid-cell--${DataGridCellPropClass.PADDING_LEFT_UNSET}`]: column?.padding?.left === false, [`b-data-grid-cell--${DataGridCellPropClass.PADDING_RIGHT_UNSET}`]: column?.padding?.right === false, [`b-data-grid-cell--${DataGridCellPropClass.NUMERIC}`]: column?.numeric, [`b-data-grid-cell--${DataGridCellPropClass.CONDENSED}`]: props.condensed, [`b-data-grid-cell--${DataGridCellPropClass.OVERFLOW_ELLIPSIS}`]: column?.overflow === BentoColumnOverflow.ELLIPSIS, [`b-data-grid-cell--${DataGridCellPropClass.IS_COLUMN_REORDERING}`]: props.draggingColumns.dragging?.field === column.field, [`b-data-grid-cell--${DataGridCellPropClass.IS_COLUMN_REORDERING_TARGET_LEFT}`]: props.draggingColumns.target?.field === column.field && props.draggingColumns?.dragToLeft, [`b-data-grid-cell--${DataGridCellPropClass.IS_COLUMN_REORDERING_TARGET_RIGHT}`]: props.draggingColumns.target?.field === column.field && !props.draggingColumns?.dragToLeft, [`b-data-grid-cell--${DataGridCellPropClass.FROZEN}`]: column?.frozen, [`b-data-grid-cell--${DataGridCellPropClass.NESTED_CONTENT_ENABLED}`]: props.nestedContent, [`b-data-grid-cell--${DataGridCellPropClass.ACTION_COL_NESTED_CONTENT_ENABLED}`]: props.nestedContent && !props.selectable && column.field === DATAGRID_ACTIONS_COLUMN_FIELD_NAME, [`b-data-grid-cell--${DataGridCellPropClass.ACTION_COL_NESTED_CONTENT_AND_SELECTION_ENABLED}`]: props.nestedContent && props.selectable && column.field === DATAGRID_ACTIONS_COLUMN_FIELD_NAME, [`b-data-grid-cell--${DataGridCellPropClass.ACTION_COL_SELECTION_ENABLED}`]: column.field === DATAGRID_ACTIONS_COLUMN_FIELD_NAME && props.selectable, [`b-data-grid-cell--${DataGridCellPropClass.SLASHED_ZERO}`]: hasSlashedZero.value, 'b-data-grid-cell--small-container': isSmallContainer.value, }); const computedCellContentClasses = computed(() => ({ 'b-data-grid-cell__cell-content--is-focused-on-inside-interactive-element': hasInteractiveElFocusedInside.value, })); // Exposed refs defineExpose({ gridcellRef, }); </script> <script lang="ts"> /** * Internal component to display the BentoDataGrid cells. * * The component can also render the selecting row checkbox as well as cell actions. * * @example * <data-grid-cell * * /> */ export default { i18n: { messages }, name: 'data-grid-cell', }; </script> <style lang="scss" scoped src="./data-grid-cell.scss" />
@@ -0,0 +1 @@
1
+ <template> <Transition name="b-data-grid-cell-actions__animation" appear> <div class="b-data-grid-cell-actions"> <div v-if="showGradientElement && !isTouchDevice" class="b-data-grid-cell-actions__background-gradient" :class="conditionalCellActionsBackgroundGradientClasses" ></div> <div class="b-data-grid-cell-actions__actions" :class="conditionalCellActionsClasses" role="group"> <div v-for="(button, index) in computedCellActions" :key="index" class="b-data-grid-cell-actions__action" > <bento-menu v-if="button.menuData" :data="transformMenuEvents(button.menuData)" :button="{ condensed: true, variant: 'secondary', disabled: button.disabled, }" :disable-arrow-down-opening-menu="disableArrowDownOpeningMenu" :teleport="teleportMenu" @open="emit('menu-open')" @close="emit('menu-close')" @menu-item-click="emit('menu-item-click')" > <template v-if="shouldShowMenuIconLeft(button)" #iconLeft> <options-vertical-icon v-if="button?.variant === 'more'" :svg-title="getButtonTitle(button, true)" /> <component :is="button.iconLeft" v-else :svg-title="getButtonTitle(button, true)" /> </template> <template v-if="!button?.isShort" #default> {{ getButtonTitle(button) }} </template> </bento-menu> <copy v-else-if="button.variant === 'copy'" :text="getCopyText(button)" :button-text="getButtonTitle(button)" :button="{ condensed: true, variant: 'secondary', }" /> <bento-button v-else condensed variant="secondary" :critical="button.critical" :disabled="button.disabled" :link="button.link" v-on="generateButtonEvents(button)" > <template v-if="button.iconLeft || button.variant === 'edit'" #iconLeft> <edit1-icon v-if="button?.variant === 'edit'" :svg-title="getButtonTitle(button, true)" /> <component :is="button.iconLeft" v-else :svg-title="button.title" /> </template> <template v-if="!button?.isShort" #default> {{ getButtonTitle(button) }} </template> <template v-if="button.iconRight || button.variant === 'open'" #iconRight> <external-link-icon v-if="button?.variant === 'open'" :svg-title="getButtonTitle(button, true)" /> <component :is="button.iconRight" v-else :svg-title="button.title" /> </template> </bento-button> </div> </div> </div> </Transition> </template> <script setup lang="ts"> import { computed, inject, type PropType, ref, type Ref } from 'vue'; import { BentoButton } from '@/components/button'; import { BentoMenu, type BentoMenuItem } from '@/components/menu'; import { Copy } from '@/internal/copy'; import { printDevelopmentWarning } from '@/utils/ts/print-development-warning'; import { type BentoDatagridDataItem } from '@/components/data-grid/data-grid.types'; import { DATA_GRID_TOUCH_DEVICE } from '@/components/data-grid/data-grid.keys'; import Edit1Icon from '@adyen/ui-assets-icons-16/vue/edit-1'; import ExternalLinkIcon from '@adyen/ui-assets-icons-16/vue/external-link'; import OptionsVerticalIcon from '@adyen/ui-assets-icons-16/vue/options-vertical'; // Types import type { BentoColumnCellAction, BentoColumnCellActionMenuItem, BentoColumnCellActions, BentoColumnCellActionsByCellDataFn, BentoColumnCellActionsProp, BentoDataGridRowActionsProp, } from './data-grid-cell-actions.types'; // Translations import messages from './messages.json'; import { useI18n } from '@/utils/ts/i18n'; type MessageSchema = (typeof messages)['en-US']; const { t } = useI18n<{ message: MessageSchema }>({ messages }); const props = defineProps({ /** * Column definitions using 'key' to relate to data object. */ cellActions: { type: [Array, Function] as PropType<BentoColumnCellActionsProp | BentoDataGridRowActionsProp>, required: true, validator: (value: BentoColumnCellActions) => value?.length <= 2, }, /** * The column field key. */ columnKey: { type: String, default: undefined, }, /** * Disables any opening of the cell actions menus using the down arrow key. */ disableArrowDownOpeningMenu: { type: Boolean, default: false, }, /** * Set to true if the datagrid has been scrolled and the right side has overflowing space. */ hasRightOverflow: { type: Boolean, default: false, }, /** * Set to true if the row has a darker background when zebra rows is enabled. */ hasZebraBackground: { type: Boolean, default: false, }, /** * Set to true if the row is selected. */ isSelected: { type: Boolean, default: false, }, /** * Set to true if the row is highlighted. */ isHighlighted: { type: Boolean, default: false, }, /** * Set if set to true will teleport the action menu component */ teleportMenu: { type: Boolean, default: false, }, /** * The row data item object. */ rowDataItem: { type: Object as PropType<BentoDatagridDataItem>, required: true, }, }); const emit = defineEmits<{ /** * Emits when a cell actions menu is opened */ (e: 'menu-open'): void; /** * Emits when a cell actions menu is closed */ (e: 'menu-close'): void; /** * Emits when a cell actions menu button closes due to a menu item click */ (e: 'menu-item-click'): void; }>(); const isTouchDevice: Ref<boolean> = inject(DATA_GRID_TOUCH_DEVICE, ref(false)); // Helper to get text for menu items, always returning a string const getMenuItemText = (action: BentoColumnCellAction | BentoColumnCellActionMenuItem) => { // If it's already a menu item with a text property, use it if ('text' in action && action.text !== undefined) { return action.text; } // Otherwise, derive from variant or title switch ((action as BentoColumnCellAction).variant) { case 'copy': return t('copy'); case 'edit': return t('edit'); case 'open': return t('open'); case 'more': return t('more'); default: return (action as BentoColumnCellAction).title; } }; const getMenuItemIcon = (action: BentoColumnCellAction) => { switch (action.variant) { case 'copy': return Copy; case 'edit': return Edit1Icon; case 'open': return ExternalLinkIcon; default: return undefined; } }; const isRowActions = computed(() => props.columnKey === undefined); const conditionalCellActionsBackgroundGradientClasses = computed(() => ({ 'b-data-grid-cell-actions__background-gradient--with-row-actions-and-overflow': isRowActions.value && props.hasRightOverflow, 'b-data-grid-cell-actions__background-gradient--zebra': props.hasZebraBackground, 'b-data-grid-cell-actions__background-gradient--highlighted': props.isHighlighted, 'b-data-grid-cell-actions__background-gradient--selected': props.isSelected, })); const conditionalCellActionsClasses = computed(() => ({ 'b-data-grid-cell-actions__actions--with-row-actions': isRowActions.value, 'b-data-grid-cell-actions__actions--zebra': props.hasZebraBackground, 'b-data-grid-cell-actions__actions--highlighted': props.isHighlighted, 'b-data-grid-cell-actions__actions--selected': props.isSelected, })); const showGradientElement = computed(() => !isRowActions.value || (isRowActions.value && props.hasRightOverflow)); /** * Checks if the passed object is a cell actions function that returns an cell actions array. * @param cellActions - a cell actions array or function */ function isBentoColumnCellActionsByCellValueDataFn( cellActions: BentoColumnCellActionsProp ): cellActions is BentoColumnCellActionsByCellDataFn { return typeof cellActions === 'function'; } const computedCellActions = computed(() => { const cellActions = isBentoColumnCellActionsByCellValueDataFn(props.cellActions) ? props.cellActions?.(props.rowDataItem, props.columnKey) : props.cellActions; if (isTouchDevice.value && !isRowActions.value && cellActions) { const flattenedMenuItems: Array<BentoColumnCellActionMenuItem> = []; cellActions.forEach(action => { if (action.menuData) { action.menuData.forEach(subMenuItem => { flattenedMenuItems.push(subMenuItem); }); } else { flattenedMenuItems.push({ text: getMenuItemText(action), event: action.event, critical: action.critical, disabled: action.disabled, icon: getMenuItemIcon(action), }); } }); return [ { variant: 'more', isShort: true, iconLeft: OptionsVerticalIcon, menuData: transformMenuEvents(flattenedMenuItems), }, ] as BentoColumnCellActions; } // Do not allow users to provide more than 2 cell action buttons if (!isTouchDevice.value && cellActions?.length > 2) { printDevelopmentWarning( 'WARNING: BentoDataGrid Cell actions can only have a maximum of 2 actions, if you need more consider using the menuData property instead.' ); } return cellActions?.slice(0, 2); }); const getButtonTitle = computed(() => (button: BentoColumnCellAction, isForSvgTitle?: boolean) => { if ((!isForSvgTitle && button.isShort) || (isForSvgTitle && !button.isShort)) { return undefined; } switch (button.variant) { case 'copy': return t('copy'); case 'edit': return t('edit'); case 'open': return t('open'); case 'more': return t('more'); default: return button.title; } }); const generateButtonEvents = (button: BentoColumnCellAction) => button?.event !== undefined ? { click: (event: MouseEvent) => { event.stopPropagation(); button.event(props.rowDataItem, props.columnKey); }, } : {}; const generateMenuEvents = (menuDataItem: BentoColumnCellActionMenuItem): BentoMenuItem => ({ ...menuDataItem, handler: () => { if (menuDataItem?.handler && typeof menuDataItem.handler === 'function') { menuDataItem?.handler(); } if (menuDataItem?.event && typeof menuDataItem.event === 'function') { menuDataItem?.event(props.rowDataItem, props.columnKey); } }, }); const transformMenuEvents = (menuData: Array<BentoColumnCellActionMenuItem>) => menuData.map(menuDataItem => { const transformedMenuData = menuDataItem; if (transformedMenuData?.items) { transformedMenuData.items = transformMenuEvents(transformedMenuData.items); } return { ...generateMenuEvents(transformedMenuData) }; }); const getCopyText = (button: BentoColumnCellAction) => button?.getCopyData !== undefined ? button.getCopyData(props.rowDataItem, props.columnKey) : props.columnKey ? (props.rowDataItem?.[props.columnKey]?.toString() as string) : ''; const shouldShowMenuIconLeft = (button: BentoColumnCellAction) => button.iconLeft || button?.variant === 'more'; </script> <script lang="ts"> /** * Internal component to display the data-grid actions. * There are 4 different pre-defined buttons that can be used by using the `variant` property. * * The component renders buttons or a menu depending on the actions provided. * * @example * <data-grid-cell-actions * :cell-actions="[ * { variant: 'edit', event: () => {} }, * { * variant: 'more', * isShort: true, * menuData: [ * { * text: 'Cancel payment', * handler: () => {}), * critical: true, * }, * { * text: 'Transfer payment', * handler: () => {}, * }, * { * text: 'Delete payment', * handler: () => {}, * disabled: true, * }, * ], * }, * ]" * column-key="payment" * :row-data-item="rowData" * /> */ export default { i18n: { messages }, name: 'data-grid-cell-actions', }; </script> <style lang="scss" scoped src="./data-grid-cell-actions.scss" />
@@ -0,0 +1 @@
1
+ <template> <div ref="allColumnsRef" class="b-data-grid-columns" :class="conditionalColumnsClasses" data-testid="data-grid-columns" > <div v-for="(column, index) in columns" :key="column.field" :ref="el => registerColumnsRefs(el, column.field)" :aria-colindex="index + 1" :aria-label="column.label" :aria-sort="getSortingColumnDirection(column)" :class="conditionalColumnClasses(column, index)" :data-testid="`${column.field}-column-header`" :draggable="hasDraggableColumns && !isResizing && !isActionsColumn(column)" class="b-data-grid-columns__column" role="columnheader" :tabindex="column.sortable || hasColumnPanel || column.infoIconTooltip ? '0' : '-1'" :style="{ width: column.autoWidth && isMeasuringColumnWidths ? 'auto' : `${column.width}px` }" v-on="dataGridColumnEvents(column)" > <div v-if="isActionsColumn(column) && selectable" class="b-data-grid-columns__select-column" :class="conditionalSelectColumnClasses" > <bento-checkbox v-if="!selectableAcrossPages" :aria-label="t('selectAll')" :model-value="allItemsSelectedState.allItemsSelected" data-testid="checkbox-select-all" :indeterminate="allItemsSelectedState.isIndeterminate" value="data-grid-select-all" @update:model-value="onAllRowSelect" /> <template v-if="selectableAcrossPages"> <bento-menu :button="{ variant: 'tertiary', condensed: true, 'aria-label': t('selectAllMenu') }" :data="selectAllMenu" class="b-data-grid-columns__select-column-menu" disable-arrow-down-opening-menu menu-fixed-positioning menu-position="bottom-start" > <template #iconLeft> <div class="b-data-grid-columns__select-column-menu-button"> <decorative-checkbox :indeterminate="allItemsSelectedState.isIndeterminate" :checked="allItemsSelectedState.allItemsSelected" :all-selected="isAllSelectionState" class="b-data-grid-columns__select-column-menu-checkbox" data-testid="checkbox-select-all" /> <triangle-down :svg-title="t('selectionMenu')" aria-hidden="true" /> </div> </template> </bento-menu> </template> </div> <template v-else> <div :aria-label="t('columnActions', { name: column.label })" aria-live="polite" role="region" class="b-data-grid-columns__column-header-and-actions" :class="conditionalColumnHeaderAndActionsClasses(column)" > <bento-typography el="div" strongest class="b-data-grid-columns__column-header" :class="conditionalColumnHeaderClasses" > <slot :column="column" :name="`head-${column.field}`"> <span class="b-data-grid-columns__label">{{ column.label }}</span> </slot> </bento-typography> <div v-if="hasColumnActions(column)" class="b-data-grid-columns__column-actions" :class="conditionalColumnActionsClasses(column)" > <div v-if="column.infoIconTooltip" class="b-data-grid-columns__column-info-icon"> <bento-info-icon :tooltip-text="column.infoIconTooltip" /> </div> <div v-if="column.sortable" class="b-data-grid-columns__column-sort"> <bento-toggle-button condensed :toggled="isSorting(column.field)" variant="tertiary" @click="sortByColumnField(column.field)" > <template #iconLeft> <arrow-up-and-down-sort-icon v-if="!getSortingDirection(column.field)" :svg-title="t('sortColumn', { name: column.label })" /> <sort-ascending-icon v-else-if=" getSortingDirection(column.field) === BentoDatagridSortDirection.ASCENDING " :svg-title="t('sortColumnInDescendingOrder', { name: column.label })" /> <sort-descending-icon v-else-if=" getSortingDirection(column.field) === BentoDatagridSortDirection.DESCENDING " :svg-title="t('sortColumnInAscendingOrder', { name: column.label })" /> </template> </bento-toggle-button> </div> <bento-menu v-if="hasColumnPanel" :ref="el => (contextMenuRef[column.field] = el)" class="b-data-grid-columns__menu" :class="conditionalColumnMenuClasses(column)" disable-arrow-down-opening-menu :data="contextualMenuData(column)" :menu-position="computeMenuPosition(hasFirstColumnPanelRightAligned, index)" :menu-fixed-positioning="true" :button="{ variant: 'tertiary', condensed: true }" @open="$emit(DatagridColumnsEvent.COLUMN_MENU_OPEN)" @close="$emit(DatagridColumnsEvent.COLUMN_MENU_CLOSE)" /> </div> </div> <div :data-testid="`data-grid-column-${column.field}`" class="b-data-grid-columns__column-separator-container" :class="conditionalColumnSeparatorClasses(column)" role="presentation" @mousedown="startResize(hasResizableColumns, column.field, $event)" @click.stop > <div v-if="!isFrozenColumns || (isFrozenColumns && index !== columns.length - 1)" class="b-data-grid-columns__column-separator" ></div> </div> </template> </div> <template v-if="rowActionsMaximumWidth && !isFrozenColumns"> <div role="presentation" class="b-data-grid-columns__row-actions-filler"></div> <div :ref="el => registerColumnsRefs(el, DATAGRID_ROW_ACTIONS_COLUMN_FIELD_NAME)" :aria-label="t('rowActionsColumn')" :aria-colindex="columns.length + 1" class="b-data-grid-columns__column" role="columnheader" tabindex="0" :style="{ minWidth: `${rowActionsMaximumWidth}px` }" ></div> </template> </div> </template> <script lang="ts" setup> import { computed, type ComputedRef, inject, type PropType, type Ref, ref, toRefs } from 'vue'; import { BentoCheckbox } from '@/components/checkbox'; import { type BentoColumn, type BentoColumnWidthOverridesLookup, type BentoDatagridAllItemsSelectedState, BentoDatagridAriaSortValue, type BentoDatagridSortByColumn, BentoDatagridSortDirection, type BentoDataGridVisibleRowsState, } from '../../data-grid.types'; import { BentoTypography } from '@/components/typography'; import { DATAGRID_ACTIONS_COLUMN_FIELD_NAME, DATAGRID_ROW_ACTIONS_COLUMN_FIELD_NAME, useResizer, useSorter, } from '../../composables'; import { focusOnInteractiveChild } from '../../utils'; import { BentoInfoIcon } from '@/components/info-icon'; import { type BentoDatagridColumnSelectionState, type BentoDatagridDraggingColumnState, DatagridColumnsEvent, type DatagridColumnsHeaderReferences, } from './data-grid-columns.types'; import { BentoMenu } from '@/components/menu'; import { BentoToggleButton } from '@/components/button'; import { DecorativeCheckbox } from '@/internal/decorative-checkbox'; import { useI18n } from '@/utils/ts/i18n'; import type { BentoMenuItem } from '@/components/menu/components'; import type { DatagridColumnHoveringState } from '@/components/data-grid/composables/use-column-hovering/use-column-hovering.types'; import { BentoPopoverPositions } from '@/components/popover/popover.types'; import ArrowUpAndDownSortIcon from '@adyen/ui-assets-icons-16/vue/arrow-up-down-sort'; import ArrowLeftIcon from '@adyen/ui-assets-icons-16/vue/arrow-left'; import ArrowRightIcon from '@adyen/ui-assets-icons-16/vue/arrow-right'; import FreezeIcon from '@adyen/ui-assets-icons-16/vue/freeze'; import HideIcon from '@adyen/ui-assets-icons-16/vue/hide'; import SortAscendingIcon from '@adyen/ui-assets-icons-16/vue/sort-ascending'; import SortDescendingIcon from '@adyen/ui-assets-icons-16/vue/sort-descending'; import ShowIcon from '@adyen/ui-assets-icons-16/vue/show'; import TriangleDown from '@adyen/ui-assets-icons-16/vue/triangle-down'; import messages from './messages.json'; import { DATA_GRID_SMALL_CONTAINER, DATA_GRID_TOUCH_DEVICE } from '@/components/data-grid/data-grid.keys'; type MessageSchema = (typeof messages)['en-US']; const props = defineProps({ /** * Column definitions using 'key' to relate to data object. */ columns: { type: Array as PropType<Array<BentoColumn>>, required: true, }, /** * Columns being dragged, contains dragging column and a target column */ draggingColumns: { type: Object as PropType<BentoDatagridDraggingColumnState>, default: undefined, }, /** * Determines the state of the current results: { filtered: boolean, allItemsVisible: boolean } to render the correct select all menu options */ visibleRowsState: { type: Object as PropType<BentoDataGridVisibleRowsState>, default: undefined, }, /** * Toggle the columns draggable events. */ hasDraggableColumns: { type: Boolean, default: true, }, /** * If resize is enabled */ hasResizableColumns: { type: Boolean, default: true, }, /** * Signifies if the column panel is enabled. */ hasColumnPanel: { type: Boolean, default: false, }, /** * If set to true, aligns the first column panel to the right of its button. */ hasFirstColumnPanelRightAligned: { type: Boolean, default: false, }, /** * If the data grid has overflow on the left side */ hasLeftOverflow: { type: Boolean, default: false, }, /** * The column hovered over state and whether the hovered over column should show a border right or left. */ hoveredOverColumnState: { type: Object as PropType<DatagridColumnHoveringState>, required: true, }, /** * Toggled to true when the DataGrid has auto width columns and is measuring the */ isMeasuringColumnWidths: { type: Boolean, default: false, }, /** * Used to disable hiding the last visible column. */ isOnlyOneVisibleColumn: { type: Boolean, default: false, }, /** * If these columns are frozen (pinned) columns */ isFrozenColumns: { type: Boolean, default: false, }, /** * Show frozen columns separator */ showFrozenColumnsSeparator: { type: Boolean, default: false, }, /** * The column all items selected state */ allItemsSelectedState: { type: Object as PropType<BentoDatagridAllItemsSelectedState>, default: undefined, }, /** * Determines if the user has enabled row selection */ selectable: { type: Boolean, default: false, }, /** * Adds an extra option when selecting all, to select items in other pages too */ selectableAcrossPages: { type: Boolean, default: false, }, /** * Determines which state of row selection is currently active. Used to display the "Select all" menu options. */ selectionState: { type: String as PropType<BentoDatagridColumnSelectionState>, default: undefined, }, /** * Sortby handles proper rendering of sort icon in header. Sorting currently is only supported server side. * It does make sure the currently sorted column has a more visible sort icon, and the sort icon points in the right direction. */ sortBy: { type: Array as PropType<Array<BentoDatagridSortByColumn>>, // NOTE: is an array to allow multi-sorting in the future default: () => [], }, /** * Truncates all data grid column headers */ truncateColumnHeaders: { type: Boolean, default: false, }, /** * The maximum width of the row actions in all rows */ rowActionsMaximumWidth: { type: Number, default: 0, }, }); const emit = defineEmits<{ /** * Emitted when a user manually resizes a column width. */ ( e: DatagridColumnsEvent.COLUMN_RESIZE, column: { field: string; width: number; } ); /** * Triggered when a column is resized. */ (e: DatagridColumnsEvent.COLUMN_RESIZING, value: BentoColumnWidthOverridesLookup); /** * Triggered when a column is sorted. */ (e: 'sort', updatedSortBy: Array<BentoDatagridSortByColumn>); /** * Triggered when selecting all rows. */ (e: DatagridColumnsEvent.SELECT_ALL_ROWS, areAllItemsSelected: boolean); /** * Triggered when all rows across all pages are selected. */ (e: DatagridColumnsEvent.SELECT_ALL_ACROSS_PAGES); /** * Triggered when all filtered rows are selected. */ (e: DatagridColumnsEvent.SELECT_ALL_CURRENT_RESULTS); /** * Triggered when deselecting all rows. */ (e: DatagridColumnsEvent.DESELECT_ALL); /** * Triggered when all filtered rows are deselected. */ (e: DatagridColumnsEvent.DESELECT_ALL_CURRENT_RESULTS); /** * Triggered when a columns started to be dragged. */ (e: DatagridColumnsEvent.COLUMN_DRAG_START, column: BentoColumn, event: DragEvent & Event); /** * Triggered when a column is being dragged over another DOM element. */ (e: DatagridColumnsEvent.COLUMN_DRAG_ENTER, column: BentoColumn, event: DragEvent & Event); /** * Triggered when a column is being dragged and is dropped over another DOM element. */ (e: DatagridColumnsEvent.COLUMN_DROP, column: BentoColumn); /** * Triggered when a column drag operation has ended. */ (e: DatagridColumnsEvent.COLUMN_DRAG_END); /** * Triggered when a column is being moved by a certain offset. */ (e: DatagridColumnsEvent.COLUMN_MOVE, movingColumn: BentoColumn, offset: number); /** * Triggered when a column is updated. */ (e: DatagridColumnsEvent.COLUMN_UPDATE, column: BentoColumn); /** * Triggered when the column contextual menu is opened. */ (e: DatagridColumnsEvent.COLUMN_MENU_OPEN); /** * Triggered when the column contextual menu is closed. */ (e: DatagridColumnsEvent.COLUMN_MENU_CLOSE); /** * Emitted when a column header is hovered over. */ (e: 'column-hover', columnKey: BentoColumn); }>(); const CLOSING_CONTEXTUAL_MENU_DELAY_MS = 100; const { t } = useI18n<{ message: MessageSchema }>({ messages }); const { columns, sortBy } = toRefs(props); const allColumnsRef = ref<HTMLDivElement>(); const columnHeaderRefs = ref<DatagridColumnsHeaderReferences>({}); const contextMenuRef = ref({}); const hoveredColumnField = ref(); const closingTimeOutId = ref(); // Selecting const onAllRowSelect = (areAllItemsSelected: boolean) => emit(DatagridColumnsEvent.SELECT_ALL_ROWS, areAllItemsSelected); const isActionsColumn = (column: BentoColumn) => column.field === DATAGRID_ACTIONS_COLUMN_FIELD_NAME; const isAllSelectionState = computed(() => props.selectionState === 'all'); // Select all menu const selectAllMenu = computed(() => [ { text: t('selectPage') as string, handler: () => onAllRowSelect(true), disabled: props.selectionState === 'page' || props.selectionState === 'all', }, props.visibleRowsState?.filtered ? { text: t('selectAllCurrentResults') as string, handler: () => emit(DatagridColumnsEvent.SELECT_ALL_CURRENT_RESULTS), addDivider: props.selectionState !== 'none', disabled: props.selectionState === 'all', } : { text: t('selectAll') as string, handler: () => emit(DatagridColumnsEvent.SELECT_ALL_ACROSS_PAGES), addDivider: props.selectionState !== 'none', disabled: props.selectionState === 'all', }, props.selectionState !== 'none' && { text: t('deselectPage') as string, handler: () => onAllRowSelect(false), addDivider: !props.visibleRowsState?.filtered, // otherwise divider shown in deselect all in current results disabled: props.selectionState === 'unselected-page', }, props.visibleRowsState?.filtered && props.selectionState !== 'none' && { text: t('deselectAllCurrentResults') as string, handler: () => emit(DatagridColumnsEvent.DESELECT_ALL_CURRENT_RESULTS), addDivider: true, disabled: props.selectionState === 'unselected-page' && props.visibleRowsState.allRowsVisible, }, props.selectionState !== 'none' && { text: t('deselectAll') as string, handler: () => emit(DatagridColumnsEvent.DESELECT_ALL), }, ].filter(Boolean) ); // Resizing const { startResize, isResizing, columnHoverEventToEmitAfterResizing } = useResizer(columns, emit); // Sorting const { isSorting, getSortingDirection, sortByColumnField } = useSorter(sortBy, columns, emit); const getSortingColumnDirection = (column: BentoColumn): BentoDatagridAriaSortValue => { if (!column.sortable) { return null; } const sortingDirection = props.sortBy.find(({ field }) => field === column.field)?.direction; if (!sortingDirection) { return BentoDatagridAriaSortValue.NONE; } const ariaSortingDirectionMap: { [key in BentoDatagridSortDirection]: BentoDatagridAriaSortValue } = { [BentoDatagridSortDirection.ASCENDING]: BentoDatagridAriaSortValue.ASCENDING, [BentoDatagridSortDirection.DESCENDING]: BentoDatagridAriaSortValue.DESCENDING, }; return ariaSortingDirectionMap[sortingDirection]; }; // Dragging emit events and methods const draggingEventsForColumn = computed(() => (column: BentoColumn) => { if (!props.hasDraggableColumns) { return {}; } return { dragstart: (e: DragEvent & Event) => { emit(DatagridColumnsEvent.COLUMN_DRAG_START, column, e); }, dragover: (e: DragEvent) => { e.preventDefault(); }, dragenter: (e: DragEvent) => { e.preventDefault(); emit(DatagridColumnsEvent.COLUMN_DRAG_ENTER, column, e); }, drop: () => { emit(DatagridColumnsEvent.COLUMN_DROP, column); }, dragend: () => { emit(DatagridColumnsEvent.COLUMN_DRAG_END); }, }; }); // Contextual menu (moving, visiblity and freezing) const contextualMenuData = computed<(col: BentoColumn) => Array<BentoMenuItem>>( () => (column: BentoColumn) => [ { text: t('moveColumnLeft') as string, handler: () => emit(DatagridColumnsEvent.COLUMN_MOVE, column, -1), icon: ArrowLeftIcon, disabled: props.columns[0].field === column.field, }, { text: t('moveColumnRight') as string, handler: () => emit(DatagridColumnsEvent.COLUMN_MOVE, column, 1), icon: ArrowRightIcon, disabled: props.columns[props.columns.length - 1].field === column.field, }, !isSmallContainer.value && { text: (column?.frozen ? t('unfreezeColumn') : t('freezeColumn')) as string, handler: () => emit(DatagridColumnsEvent.COLUMN_UPDATE, { ...column, frozen: !column?.frozen }), icon: FreezeIcon, }, { text: (column?.visible !== false ? t('hideColumn') : t('showColumn')) as string, handler: () => { emit(DatagridColumnsEvent.COLUMN_UPDATE, { ...column, visible: !(column?.visible !== false), }); }, icon: column?.visible !== false ? HideIcon : ShowIcon, disabled: column?.mandatory || (column?.visible !== false && props.isOnlyOneVisibleColumn), }, ].filter(Boolean) ); const showContextualMenu = (column: BentoColumn) => { hoveredColumnField.value = column.field; if (closingTimeOutId.value) { clearTimeout(closingTimeOutId.value); } }; const hideContextualMenu = (column: BentoColumn) => { // We want to delay closing for a couple of seconds so // it doesn't flicker if a user hovers over the icon and then inside the popover. const timeOutId = setTimeout(() => { if (column.field === hoveredColumnField.value) { hoveredColumnField.value = ''; } }, CLOSING_CONTEXTUAL_MENU_DELAY_MS); closingTimeOutId.value = timeOutId; }; /** * Generates events for a Datagrid Column. * @param column - the BentoColumn we are attaching events onto. */ const dataGridColumnEvents = (column: BentoColumn) => { let events: { [eventName: string]: (e: Event) => void } = { mouseenter: () => { showContextualMenu(column); if (!isResizing.value) { emit('column-hover', column); } else { columnHoverEventToEmitAfterResizing.value = column; } }, mouseleave: () => { hideContextualMenu(column); if (!isResizing.value) { emit('column-hover', null); } else { columnHoverEventToEmitAfterResizing.value = null; } }, focus: () => { showContextualMenu(column); if (isActionsColumn(column)) { focusOnInteractiveChild(columnHeaderRefs.value[column.field]); } }, focusout: (e: FocusEvent) => { if (!allColumnsRef.value?.contains(e?.relatedTarget as Node)) { hideContextualMenu(column); } }, }; if (!isActionsColumn(column)) { events = { ...events, ...draggingEventsForColumn.value(column), }; } return events; }; const hasColumnActions = (column: BentoColumn) => column.infoIconTooltip || column.sortable || props.hasColumnPanel; const registerColumnsRefs = (el: unknown, field: string) => { if (columnHeaderRefs.value?.[field] === undefined) { columnHeaderRefs.value = { ...columnHeaderRefs.value, [field]: null }; } if (el !== null) { columnHeaderRefs.value[field] = el as HTMLDivElement; } }; const isSmallContainer: ComputedRef<boolean> = inject( DATA_GRID_SMALL_CONTAINER, computed(() => false) ); const isTouchDevice: Ref<boolean> = inject(DATA_GRID_TOUCH_DEVICE, ref(false)); // Conditional column styles // Conditional styles const conditionalColumnsClasses = computed(() => { const defaultConditionalClasses = { 'b-data-grid-columns--is-measuring': props.isMeasuringColumnWidths, }; return columns.value[0]?.frozen && !isSmallContainer.value ? { ...defaultConditionalClasses, 'b-data-grid-columns--frozen': true, 'b-data-grid-columns--frozen-separator': props.showFrozenColumnsSeparator, 'b-data-grid-columns--overflow-shadow-left': props.hasLeftOverflow, } : { ...defaultConditionalClasses, 'b-data-grid-columns--unfrozen': true, }; }); const conditionalSelectColumnClasses = computed(() => ({ 'b-data-grid-columns__select-column--all': props.selectableAcrossPages, })); const computeMenuPosition = (hasFirstColumnPanelRightAligned, index) => hasFirstColumnPanelRightAligned && index === 0 ? BentoPopoverPositions.BOTTOM_START : BentoPopoverPositions.BOTTOM_END; const conditionalColumnClasses = (column: BentoColumn, columnIndex: number) => ({ 'b-data-grid-columns__column--column-hovered': column.field === props.hoveredOverColumnState.hoveredOverColumnField, 'b-data-grid-columns__column--column-hovered-no-border-left': column.field === props.hoveredOverColumnState.hoveredOverColumnField && props.hoveredOverColumnState.noBorderLeft, 'b-data-grid-columns__column--column-hovered-no-border-right': column.field === props.hoveredOverColumnState.hoveredOverColumnField && props.hoveredOverColumnState.noBorderRight, 'b-data-grid-columns__column--last-frozen-frozen-column': props.isFrozenColumns && columnIndex === columns.value.length - 1, 'b-data-grid-columns__column--sortable': column.sortable, 'b-data-grid-columns__column--padding-left-unset': column?.padding?.left === false, 'b-data-grid-columns__column--padding-right-unset': column?.padding?.right === false, 'b-data-grid-columns__column--numeric': column?.numeric, 'b-data-grid-columns__column--frozen': column?.frozen, 'b-data-grid-columns__column--is-re-ordering': column?.field === props.draggingColumns?.dragging?.field, 'b-data-grid-columns__column--is-re-ordering-target-left': column?.field === props.draggingColumns?.target?.field && props.draggingColumns?.dragToLeft, 'b-data-grid-columns__column--is-re-ordering-target-right': column?.field === props.draggingColumns?.target?.field && !props.draggingColumns?.dragToLeft, 'b-data-grid-columns__column--with-sorting-or-column-panel': column.sortable || column.infoIconTooltip || props.hasColumnPanel || props.selectableAcrossPages, 'b-data-grid-columns--small-container': isSmallContainer.value, }); const conditionalColumnHeaderAndActionsClasses = (column: BentoColumn) => ({ 'b-data-grid-columns__column-header-and-actions--numeric': column?.numeric, }); const conditionalColumnActionsClasses = (column: BentoColumn) => ({ 'b-data-grid-columns__column-actions--numeric': column?.numeric, }); const conditionalColumnSeparatorClasses = (column: BentoColumn) => ({ 'b-data-grid-columns__column-separator-container--is-resizeable': props.hasResizableColumns, 'b-data-grid-columns__column-separator-container--is-offset': props.hoveredOverColumnState.hoveredOverColumnField === column.field && props.hoveredOverColumnState.isSeparatorOffset, }); const conditionalColumnHeaderClasses = computed(() => ({ 'b-data-grid-columns__column-header--truncated': props.truncateColumnHeaders, })); const conditionalColumnMenuClasses = (column: BentoColumn) => ({ 'b-data-grid-columns__menu--hovered': hoveredColumnField.value === column.field || isTouchDevice.value, }); // Exposed refs defineExpose({ columnHeaderRefs, }); </script> <script lang="ts"> /** * A component to display the BentoDataGrid columns * * @example * * export default { * components: { DataGridColumns }, * template: ` * <data-grid-columns * v-if="columnSet?.length" * :key="`data-grid-columns-${id}`" * :columns="columnSet" * :has-resizable-columns="hasResizableColumns" * :sort-by="sortBy" * :all-items-selected-state="allItemsSelectedState" * :is-frozen-columns="isFrozenColumns" * :show-frozen-columns-separator="showFrozenColumnsSeparator" * :dragging-columns="draggingColumns" * :has-draggable-columns="allowColumnDragAndDrop" * :has-column-panel="columnPanel" * :has-left-overflow="hasLeftOverflow" * :has-first-column-panel-right-aligned="index === computedColumnPanelAlignment" * :is-only-one-visible-column="isOnlyOneVisibleColumn" * :selectable="selectable" * :selectable-across-pages="selectableAcrossPagesComputed" * :truncate-column-headers="truncateColumnHeaders" * v-on="draggingEventsForColumns" * @column-move="onMoveColumnByOffset" * @column-update="onConfigSettingsColumnUpdate" * @column-resize="onColumnResized" * @column-resizing="onColumnWidthOverride" * @select-all-rows="onAllRowSelect" * @select-all-across-pages="emit(BentoDatagridEvent.SELECT_ALL_ACROSS_PAGES)" * @sort="onSort" * > * <template v-for="(_, innerSlot) of slots" #[innerSlot]="scope"> * <slot :name="innerSlot" v-bind="scope" /> * </template> * </data-grid-columns> * ` * } */ export default { i18n: { messages }, name: 'data-grid-columns', }; </script> <style lang="scss" scoped src="./data-grid-columns.scss" />
@@ -0,0 +1 @@
1
+ <template> <div class="b-data-grid-config-settings-column-row" @mouseover="showFreezeIcon" @mouseleave="hideFreezeIcon"> <bento-toggle :aria-label="t('toggleColumnVisibility', { label: column.label })" :disabled="isColumnVisibilityToggleDisabled" label-position="after" :value="isColumnVisible" @input="toggleColumnVisibility" > {{ column.label }} </bento-toggle> <bento-freeze-icon v-show="(isColumnFrozen || isFreezeIconOptionShown) && !isSmallContainer" :aria-label="t('toggleColumnFreeze', { label: column.label })" class="b-data-grid-config-settings-column-row__freeze-icon" :class="freezeIconConditionalClasses" @click.native="toggleColumnFreeze" /> </div> </template> <script lang="ts"> import { computed, type ComputedRef, defineComponent, inject, type PropType, ref } from 'vue'; import { BentoToggle } from '@/components/toggle'; import { useI18n } from '@/utils/ts/i18n'; import { DATA_GRID_SMALL_CONTAINER } from '@/components/data-grid/data-grid.keys'; import type { BentoColumn } from '@/components/data-grid/components/data-grid-columns/data-grid-columns.types'; import BentoFreezeIcon from '@adyen/ui-assets-icons-16/vue/freeze'; import messages from './messages.json'; type MessageSchema = (typeof messages)['en-US']; export default defineComponent({ i18n: { messages }, name: 'bento-data-grid', components: { BentoFreezeIcon, BentoToggle, }, props: { /** * Is column visibitlity toggle disabled */ isColumnVisibilityToggleDisabled: { type: Boolean, required: true, }, /** * Column definition */ column: { type: Object as PropType<BentoColumn>, required: true, }, }, emits: ['visible-column', 'updated-column'], setup(props, { emit }) { const { t } = useI18n<{ message: MessageSchema }>({ messages }); const isFreezeIconOptionShown = ref(false); const isColumnFrozen = computed(() => props.column?.frozen); const isColumnVisible = computed(() => props.column?.visible !== false); const isSmallContainer: ComputedRef<boolean> = inject( DATA_GRID_SMALL_CONTAINER, computed(() => false) ); const toggleColumnVisibility = () => { emit('visible-column', { ...props.column, visible: !isColumnVisible.value }); }; const toggleColumnFreeze = () => { emit('updated-column', { ...props.column, frozen: !isColumnFrozen.value }); }; const showFreezeIcon = () => { if (!isColumnFrozen.value) { isFreezeIconOptionShown.value = true; } }; const hideFreezeIcon = () => { if (!isColumnFrozen.value) { isFreezeIconOptionShown.value = false; } }; const freezeIconConditionalClasses = computed(() => ({ [`b-data-grid-config-settings-column-row__freeze-icon--frozen`]: isColumnFrozen.value, })); return { // Values isFreezeIconOptionShown, isColumnFrozen, freezeIconConditionalClasses, isColumnVisible, isSmallContainer, // Methods toggleColumnVisibility, showFreezeIcon, hideFreezeIcon, toggleColumnFreeze, // Translations t, }; }, }); </script> <style lang="scss" scoped src="./data-grid-config-settings-column-row.scss" />
@@ -0,0 +1 @@
1
+ <!-- TODO: Remove aria props from search-bar as these name clashes with JSX type /vue/types/jsx.d.ts --> <!-- eslint-disable vue/attribute-hyphenation --> <template> <div v-bento-click-outside-directive="onClickOutside" class="b-data-grid-config-settings"> <div class="b-data-grid-config-settings__button-and-separator"> <div ref="configSettingsButtonRef" class="b-data-grid-config-settings__button-container"> <bento-toggle-button :aria-controls="isConfigSettingsPanelOpen ? popoverId : null" variant="tertiary" condensed :toggled="isConfigSettingsPanelOpen" @click="setConfigSettingsPanelVisibility(!isConfigSettingsPanelOpen)" > <template #iconLeft> <bento-settings-icon :svg-title="t('toggleConfigSettingsPanelVisibility')" /> </template> </bento-toggle-button> </div> </div> <bento-popover v-if="configSettingsButtonRef" :id="popoverId" :open="isConfigSettingsPanelOpen" class="b-data-grid-config-settings__panel" :aria-label="t('configSettingsPanel')" :target-element="configSettingsButtonRef" position="bottom-end" > <div class="b-data-grid-config-settings__panel-header"> <bento-typography el="span" strongest> {{ t('displayOptions') }} </bento-typography> <bento-button variant="tertiary" @click="setConfigSettingsPanelVisibility(false)"> <template #iconLeft> <bento-cross-icon :svg-title="t('closeConfigSettingsPanel')" /> </template> </bento-button> </div> <div class="b-data-grid-config-settings__appearance"> <bento-typography class="b-data-grid-config-settings__appearance-title" el="span" stronger> {{ t('appearance') }} </bento-typography> <div class="b-data-grid-config-settings__appearance-button"> <bento-toggle label-position="after" :aria-label="t('condensedToggle')" :value="condensed" @input="emitCondensedToggle" > {{ t('condensed') }} </bento-toggle> </div> <div class="b-data-grid-config-settings__appearance-button"> <bento-toggle label-position="after" :aria-label="t('zebraRowsToggle')" :value="zebraRows" @input="emitZebraRowsToggle" > {{ t('zebraRows') }} </bento-toggle> </div> </div> <div class="b-data-grid-config-settings__column-visibility-settings"> <div class="b-data-grid-config-settings__column-visibility-header"> <bento-typography class="b-data-grid-config-settings__column-visibility-title" el="span" stronger> {{ t('columns') }} </bento-typography> <bento-button v-if="!isColumnVisibilityFilterShown" ref="searchButtonRef" variant="tertiary" @click="toggleColumnFilter" > <template #iconLeft><bento-search-icon /> </template>{{ t('search') }}</bento-button > <bento-button v-else variant="tertiary" @click="toggleColumnFilter"> <template #iconLeft> <bento-cross-icon /> </template> {{ t('closeSearch') }} </bento-button> </div> <div v-if="isColumnVisibilityFilterShown" class="b-data-grid-config-settings__column-visibility-search"> <bento-search-bar ref="searchBarRef" :value="searchTerm" :input-field-aria-label="t('columnVisibilitySearchFilter').toString()" @input="searchTerm = $event" @clear="clearAction" @escape-pressed="toggleColumnFilter" /> </div> <div v-else class="b-data-grid-config-settings__column-visibility-show-all"> <bento-toggle label-position="after" :value="allColumnsVisbilityToggleState" @input="toggleAllColumnVisibility" > {{ t('showAllColumns', { numberOfColumns: columns.length }) }} </bento-toggle> </div> <div class="b-data-grid-config-settings__column-visibility-toggles"> <div v-for="column of filteredColumnVisibilityColumns" :key="column.field"> <bento-data-grid-config-settings-config-row v-if="column.field !== DATAGRID_ACTIONS_COLUMN_FIELD_NAME" :column="column" :is-column-visibility-toggle-disabled="isColumnVisibilityToggleDisabled(column)" @visible-column="emitVisibleColumns" @updated-column="emitFrozenColumns" /> </div> <bento-typography v-if="filteredColumnVisibilityColumns.length === 0" el="span" class="b-data-grid-config-settings__column-visibility-empty-search-message" > {{ t('noColumnsMatch') }} </bento-typography> </div> </div> </bento-popover> </div> </template> <script lang="ts"> import { computed, defineComponent, nextTick, type PropType, ref, toRef } from 'vue'; import { BentoPopover } from '@/components/popover'; import { BentoButton, BentoToggleButton } from '@/components/button'; import { BentoTypography } from '@/components/typography'; import { BentoToggle } from '@/components/toggle'; import { type BentoColumn } from '../../data-grid.types'; import BentoSearchBar from '@/components/search-bar/search-bar.vue'; import { BentoClickOutsideDirective } from '@/directives'; import { DatagridConfigSettingsEvent } from './data-grid-config-settings.types'; import { useSearchBarFilter } from '@/components/search-bar'; import { generateUid } from '@/core/utils/ts'; import { DATAGRID_ACTIONS_COLUMN_FIELD_NAME } from '../../composables'; import { useI18n } from '@/utils/ts/i18n'; import BentoSettingsIcon from '@adyen/ui-assets-icons-16/vue/settings'; import BentoCrossIcon from '@adyen/ui-assets-icons-16/vue/cross'; import BentoSearchIcon from '@adyen/ui-assets-icons-16/vue/search'; import BentoDataGridConfigSettingsConfigRow from './components/data-grid-config-settings-column-row/data-grid-config-settings-column-row.vue'; import messages from './messages.json'; type MessageSchema = (typeof messages)['en-US']; export default defineComponent({ i18n: { messages }, name: 'bento-data-grid', components: { BentoButton, BentoCrossIcon, BentoPopover, BentoSettingsIcon, BentoToggleButton, BentoDataGridConfigSettingsConfigRow, BentoSearchBar, BentoSearchIcon, BentoToggle, BentoTypography, }, directives: { BentoClickOutsideDirective }, props: { /** * Column definitions */ columns: { type: Array as PropType<Array<BentoColumn>>, required: true, }, /** * Sets the row cells to the condensed padding state. */ condensed: { type: Boolean, default: false, }, /** * Sets a different background color for even rows. */ zebraRows: { type: Boolean, default: false, }, }, emits: [ DatagridConfigSettingsEvent.UPDATE_CONDENSED, DatagridConfigSettingsEvent.UPDATE_COLUMNS, DatagridConfigSettingsEvent.UPDATE_ZEBRA_ROWS, ], setup(props, { emit }) { const { t } = useI18n<{ message: MessageSchema }>({ messages }); const configSettingsButtonRef = ref(null); const popoverId = generateUid('data-grid-config-settings-popover'); const searchBarRef = ref(null); const isConfigSettingsPanelOpen = ref<boolean>(false); const allColumnsVisbilityToggleState = ref<boolean>(true); const searchButtonRef = ref(null); const searchTerm = ref<string>(''); const columnsRef = toRef(props, 'columns'); const isColumnVisibilityFilterShown = ref<boolean>(false); // Column Visibility Filtering const filteredColumnVisibilityColumns = useSearchBarFilter<BentoColumn>(searchTerm, columnsRef); const clearAction = () => { searchTerm.value = ''; }; const areAllNonMandatoryColumnsHidden = computed( () => !props.columns.some(col => col.visible === false && !col.mandatory) ); const toggleColumnFilter = () => { clearAction(); isColumnVisibilityFilterShown.value = !isColumnVisibilityFilterShown.value; if (isColumnVisibilityFilterShown.value) { nextTick(() => searchBarRef.value.$refs.bentoInputField?.focusInput()); } else { nextTick(() => searchButtonRef?.value?.$el?.focus()); } allColumnsVisbilityToggleState.value = areAllNonMandatoryColumnsHidden.value; }; // Column visibility const isColumnVisibilityToggleDisabled = (column: BentoColumn) => column.mandatory ?? false; const emitVisibleColumns = (updatedColumn: BentoColumn) => { const updatedColumns = props.columns.map(col => ({ ...col, visible: updatedColumn.field === col.field ? updatedColumn.visible : col.visible, })); emit(DatagridConfigSettingsEvent.UPDATE_COLUMNS, updatedColumns); }; const toggleAllColumnVisibility = (toggleState: boolean) => { if (toggleState) { // Show all columns emit( DatagridConfigSettingsEvent.UPDATE_COLUMNS, props.columns.map(col => ({ ...col, visible: true })) ); } else { // Hide all columns except for mandatory columns emit( DatagridConfigSettingsEvent.UPDATE_COLUMNS, props.columns.map(col => ({ ...col, visible: col?.mandatory === true })) ); } }; // Column freeze // TODO to convert this function to 'emitUpdatedColumns', that will emit both: visible and frozen columns const emitFrozenColumns = (updatedColumn: BentoColumn) => { const updatedColumns = props.columns.map(col => ({ ...col, frozen: updatedColumn.field === col.field ? !!updatedColumn?.frozen : !!col?.frozen, })); emit(DatagridConfigSettingsEvent.UPDATE_COLUMNS, updatedColumns); }; // Panel visibility const setConfigSettingsPanelVisibility = (isVisible: boolean) => { isConfigSettingsPanelOpen.value = isVisible; allColumnsVisbilityToggleState.value = areAllNonMandatoryColumnsHidden.value; }; const onClickOutside = () => setConfigSettingsPanelVisibility(false); // Condensed view const emitCondensedToggle = (isCondensedView: boolean) => emit(DatagridConfigSettingsEvent.UPDATE_CONDENSED, isCondensedView); const emitZebraRowsToggle = (isZebraRows: boolean) => emit(DatagridConfigSettingsEvent.UPDATE_ZEBRA_ROWS, isZebraRows); return { // Values configSettingsButtonRef, popoverId, isConfigSettingsPanelOpen, allColumnsVisbilityToggleState, filteredColumnVisibilityColumns, searchTerm, isColumnVisibilityFilterShown, searchBarRef, searchButtonRef, DATAGRID_ACTIONS_COLUMN_FIELD_NAME, // Methods setConfigSettingsPanelVisibility, emitCondensedToggle, toggleAllColumnVisibility, clearAction, toggleColumnFilter, emitVisibleColumns, emitFrozenColumns, onClickOutside, isColumnVisibilityToggleDisabled, emitZebraRowsToggle, // Translations t, }; }, }); </script> <style lang="scss" scoped src="./data-grid-config-settings.scss" />
@@ -0,0 +1 @@
1
+ <template> <div class="b-data-grid-lazy-load"> <bento-loading-indicator v-if="loading && isInfiniteScroll" /> <bento-loading-button v-if="loading && !isInfiniteScroll" variant="secondary" :state="BentoLoadingButtonState.LOADING" >{{ t('loading') }}</bento-loading-button > <bento-loading-button v-if="!loading && !isInfiniteScroll" :state="BentoLoadingButtonState.START" variant="secondary" @click="$emit(BentoDatagridEvent.UPDATE_SHOW_MORE)" >{{ t('showMore') }}</bento-loading-button > </div> </template> <script setup lang="ts"> import { BentoLoadingButton, BentoLoadingButtonState } from '@/components/button'; import { BentoLoadingIndicator } from '@/components/loading-indicator'; import { BentoDatagridEvent } from '../../data-grid.types'; import { useI18n } from '@/utils/ts/i18n'; import messages from '../../messages.json'; type MessageSchema = (typeof messages)['en-US']; defineProps<{ /** * Indicates whether the component is in a loading state. */ loading: boolean; /** * If set to true, displays a loading indicator. Otherwise displays a loading button. */ isInfiniteScroll: boolean; }>(); const { t } = useI18n<{ message: MessageSchema }>({ messages }); </script> <script lang="ts"> /** * Internal component to display the bento-data-grid's lazy loading. * The component can render a 'Show more' loading button or a loading indicator. */ export default { i18n: { messages }, }; </script> <style lang="scss" scoped src="./data-grid-lazy-load.scss" />