@rotki/ui-library 2.25.1 → 2.26.1

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 (72) hide show
  1. package/dist/components/date-time-picker/RuiDateTimePicker.js.map +1 -1
  2. package/dist/components/date-time-picker/RuiDateTimePicker.vue_vue_type_script_setup_true_lang.js +1 -0
  3. package/dist/components/date-time-picker/RuiDateTimePicker.vue_vue_type_script_setup_true_lang.js.map +1 -1
  4. package/dist/components/date-time-picker/date-time-picker-styles.d.ts +6 -0
  5. package/dist/components/forms/auto-complete/RuiAutoComplete.js.map +1 -1
  6. package/dist/components/forms/auto-complete/RuiAutoComplete.vue_vue_type_script_setup_true_lang.js +1 -0
  7. package/dist/components/forms/auto-complete/RuiAutoComplete.vue_vue_type_script_setup_true_lang.js.map +1 -1
  8. package/dist/components/forms/auto-complete/auto-complete-styles.d.ts +6 -0
  9. package/dist/components/forms/category-picker/category-picker-styles.d.ts +6 -0
  10. package/dist/components/forms/select/RuiMenuSelect.js.map +1 -1
  11. package/dist/components/forms/select/RuiMenuSelect.vue_vue_type_script_setup_true_lang.js +1 -0
  12. package/dist/components/forms/select/RuiMenuSelect.vue_vue_type_script_setup_true_lang.js.map +1 -1
  13. package/dist/components/forms/select/menu-select-styles.d.ts +6 -0
  14. package/dist/components/forms/text-input-styles.d.ts +3 -0
  15. package/dist/components/forms/text-input-styles.js +3 -1
  16. package/dist/components/forms/text-input-styles.js.map +1 -1
  17. package/dist/components/index.js +1 -1
  18. package/dist/components/overlays/menu/RuiMenu.js.map +1 -1
  19. package/dist/components/overlays/menu/RuiMenu.vue_vue_type_script_setup_true_lang.js +8 -2
  20. package/dist/components/overlays/menu/RuiMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  21. package/dist/components/tables/RuiDataTable.js.map +1 -1
  22. package/dist/components/tables/RuiDataTable.vue.d.ts +12 -1
  23. package/dist/components/tables/RuiDataTable.vue_vue_type_script_setup_true_lang.js +71 -17
  24. package/dist/components/tables/RuiDataTable.vue_vue_type_script_setup_true_lang.js.map +1 -1
  25. package/dist/components/tables/RuiTable.vue_vue_type_script_setup_true_lang.js +1 -1
  26. package/dist/components/tables/RuiTableHead.js.map +1 -1
  27. package/dist/components/tables/RuiTableHead.vue_vue_type_script_setup_true_lang.js +38 -13
  28. package/dist/components/tables/RuiTableHead.vue_vue_type_script_setup_true_lang.js.map +1 -1
  29. package/dist/components/tables/RuiTablePagination.js.map +1 -1
  30. package/dist/components/tables/RuiTablePagination.vue.d.ts +3 -3
  31. package/dist/components/tables/RuiTablePagination.vue_vue_type_script_setup_true_lang.js +65 -14
  32. package/dist/components/tables/RuiTablePagination.vue_vue_type_script_setup_true_lang.js.map +1 -1
  33. package/dist/components/tables/data-table/RuiDataTableBody.js.map +1 -1
  34. package/dist/components/tables/data-table/RuiDataTableBody.vue.d.ts +7 -1
  35. package/dist/components/tables/data-table/RuiDataTableBody.vue_vue_type_script_setup_true_lang.js +45 -9
  36. package/dist/components/tables/data-table/RuiDataTableBody.vue_vue_type_script_setup_true_lang.js.map +1 -1
  37. package/dist/components/tables/data-table/RuiDataTableCell.js.map +1 -1
  38. package/dist/components/tables/data-table/RuiDataTableCell.vue_vue_type_script_setup_true_lang.js +13 -2
  39. package/dist/components/tables/data-table/RuiDataTableCell.vue_vue_type_script_setup_true_lang.js.map +1 -1
  40. package/dist/components/tables/data-table/RuiDataTableErrorRow.js +7 -0
  41. package/dist/components/tables/data-table/RuiDataTableErrorRow.js.map +1 -0
  42. package/dist/components/tables/data-table/RuiDataTableErrorRow.vue.d.ts +21 -0
  43. package/dist/components/tables/data-table/RuiDataTableErrorRow.vue_vue_type_script_setup_true_lang.js +40 -0
  44. package/dist/components/tables/data-table/RuiDataTableErrorRow.vue_vue_type_script_setup_true_lang.js.map +1 -0
  45. package/dist/components/tables/data-table/RuiDataTableExpandedRow.js.map +1 -1
  46. package/dist/components/tables/data-table/RuiDataTableExpandedRow.vue_vue_type_script_setup_true_lang.js +12 -1
  47. package/dist/components/tables/data-table/RuiDataTableExpandedRow.vue_vue_type_script_setup_true_lang.js.map +1 -1
  48. package/dist/components/tables/data-table/RuiDataTableGroupRow.js.map +1 -1
  49. package/dist/components/tables/data-table/RuiDataTableGroupRow.vue_vue_type_script_setup_true_lang.js +52 -20
  50. package/dist/components/tables/data-table/RuiDataTableGroupRow.vue_vue_type_script_setup_true_lang.js.map +1 -1
  51. package/dist/components/tables/data-table/RuiDataTableRow.js.map +1 -1
  52. package/dist/components/tables/data-table/RuiDataTableRow.vue_vue_type_script_setup_true_lang.js +29 -14
  53. package/dist/components/tables/data-table/RuiDataTableRow.vue_vue_type_script_setup_true_lang.js.map +1 -1
  54. package/dist/components/tables/data-table/context.d.ts +10 -0
  55. package/dist/components/tables/data-table/context.js +2 -0
  56. package/dist/components/tables/data-table/context.js.map +1 -1
  57. package/dist/components/tables/data-table-styles.d.ts +15 -9
  58. package/dist/components/tables/data-table-styles.js +29 -6
  59. package/dist/components/tables/data-table-styles.js.map +1 -1
  60. package/dist/components/tables/table-props.d.ts +1 -2
  61. package/dist/components/tables/table-props.js +2 -10
  62. package/dist/components/tables/table-props.js.map +1 -1
  63. package/dist/components/tables/use-pagination-navigation.js +1 -1
  64. package/dist/components/tables/use-pagination-navigation.js.map +1 -1
  65. package/dist/composables/sticky-header.d.ts +0 -1
  66. package/dist/composables/sticky-header.js +56 -8
  67. package/dist/composables/sticky-header.js.map +1 -1
  68. package/dist/index.js +1 -1
  69. package/dist/library-icons.json +1 -1
  70. package/dist/style.css +115 -38
  71. package/dist/web-types.json +139 -3
  72. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"RuiTableHead.js","names":[],"sources":["../../../src/components/tables/RuiTableHead.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"T extends object, K extends keyof T = keyof T\">\nimport RuiButton from '@/components/buttons/button/RuiButton.vue';\nimport RuiCheckbox from '@/components/forms/checkbox/RuiCheckbox.vue';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport RuiBadge from '@/components/overlays/badge/RuiBadge.vue';\nimport RuiProgress from '@/components/progress/RuiProgress.vue';\nimport { getAlignClass, getSortButtonAlignClass, SortDirection, TableAlign } from '@/components/tables/table-props';\nimport { tv } from '@/utils/tv';\n\n/**\n * A sortable column name for `T`, the type of the data in the column: the name\n * must be a key of that object type.\n */\nexport type TableRowKey<T> = keyof T extends string ? keyof T : never;\n\nexport interface BaseTableColumn<T> {\n key: TableRowKey<T> | string;\n sortable?: boolean;\n direction?: SortDirection;\n align?: TableAlign;\n class?: string;\n cellClass?: string;\n tdClass?: string;\n colspan?: string | number;\n rowspan?: string | number;\n /**\n * hide this column when the table is rendered in its stacked mobile layout\n */\n mobileHidden?: boolean;\n /**\n * pin this column to the card header (top row) in the stacked mobile layout\n * instead of rendering it as a label/value pair. Intended for action columns\n * such as an overflow menu. Ignored on non-mobile layouts.\n */\n mobileHeader?: boolean;\n\n [key: string]: any;\n}\n\n/**\n * A sortable table column, which ties `sortable: true` to a key that is a real\n * property of `T`, the type of data in the table.\n */\nexport interface SortableTableColumn<T> extends BaseTableColumn<T> {\n sortable: true;\n}\n\n/**\n * An interface representing a column in a table that cannot be sorted.\n * This can be mapped to an actual property of the object or to a virtual column.\n *\n * @typeparam T - The type of data in the table.\n */\nexport interface NoneSortableTableColumn<T> extends BaseTableColumn<T> {\n sortable?: false;\n}\n\nexport type TableColumn<T> = SortableTableColumn<T> | NoneSortableTableColumn<T>;\n\nexport interface SortColumn<T> {\n column?: TableRowKey<T>;\n direction: SortDirection;\n}\n\nexport type TableSortData<T> = SortColumn<T> | SortColumn<T>[] | undefined;\n\nexport type TableRowKeyData<T> = TableRowKey<T> | TableRowKey<T>[] | undefined;\n\nexport interface GroupData<T> {\n key: string;\n value?: Partial<T>;\n}\n\nexport type GroupKey<T> = TableColumn<T>['key'];\n\nexport type GroupKeys<T> = GroupKey<T> | GroupKey<T>[] | undefined;\n\nexport interface Props<T> {\n loading?: boolean;\n stickyHeader?: boolean;\n stick?: boolean;\n selectable?: boolean;\n disableCheckAll?: boolean;\n colspan?: number;\n columns?: TableColumn<T>[];\n capitalizeHeaders?: boolean;\n isAllSelected?: boolean;\n indeterminate?: boolean;\n dense?: boolean;\n sortedMap?: Partial<Record<TableRowKey<T>, SortColumn<T>>>;\n sortData?: TableSortData<T>;\n columnAttr?: keyof TableColumn<T>;\n dataId?: string;\n}\n\nconst {\n loading = false,\n stickyHeader = false,\n stick = false,\n selectable = false,\n disableCheckAll = false,\n colspan = 0,\n columns,\n capitalizeHeaders = false,\n isAllSelected = false,\n indeterminate = false,\n dense = false,\n sortedMap = {} as Partial<Record<TableRowKey<T>, SortColumn<T>>>,\n columnAttr = 'label',\n sortData,\n dataId = 'head-main',\n} = defineProps<Props<T>>();\n\nconst emit = defineEmits<{\n 'select:all': [value: boolean];\n 'sort': [value: { key: TableRowKey<T>; direction?: SortDirection }];\n}>();\n\nconst tableHeadStyles = tv({\n slots: {\n thead: 'divide-y divide-black/[0.12] dark:divide-white/[0.12]',\n checkbox: 'px-2 w-[3.625rem] max-w-[3.625rem] [&_label]:ml-0',\n th: 'p-4',\n columnText: 'text-rui-text dark:text-white font-medium text-[0.875rem] leading-6',\n sortButton: 'inline-flex group/sort',\n sortIcon: 'transition opacity-0 rotate-180 group-hover/sort:opacity-60',\n loaderRow: 'border-none',\n progress: 'p-0 h-0',\n progressWrapper: 'h-0 -mt-1',\n },\n variants: {\n position: {\n default: {},\n sticky: { thead: 'top-0 z-10 absolute' },\n fixed: {\n thead: 'top-0 z-10 fixed',\n th: 'bg-white dark:bg-[#121212] border-b border-b-black/[0.12] dark:border-b-white/[0.12]',\n },\n },\n dense: {\n true: { th: 'py-[0.38rem]' },\n },\n },\n defaultVariants: {\n position: 'default',\n },\n});\n\nconst headerPosition = computed<'default' | 'sticky' | 'fixed'>(() => {\n if (stickyHeader && stick)\n return 'fixed';\n if (stickyHeader)\n return 'sticky';\n return 'default';\n});\n\nconst ui = computed<ReturnType<typeof tableHeadStyles>>(() => tableHeadStyles({\n position: get(headerPosition),\n dense,\n}));\n\nfunction getSortIconClass(key: TableColumn<T>['key']): string | undefined {\n if (!isSortedBy(key))\n return undefined;\n const direction = getSortDirection(key);\n return `!opacity-100 ${direction === SortDirection.asc ? '!rotate-180' : '!rotate-0'}`;\n}\n\nfunction onSort({ key, direction }: TableColumn<T>): void {\n return emit('sort', {\n key: key as TableRowKey<T>,\n direction: direction ?? SortDirection.asc,\n });\n}\n\nfunction onToggleAll(checked: boolean) {\n return emit('select:all', checked);\n}\n\nfunction isSortedBy(key: TableColumn<T>['key']): boolean {\n return key in sortedMap;\n}\n\nfunction getSortIndex(key: TableColumn<T>['key']): number {\n if (!sortData || !Array.isArray(sortData) || !isSortedBy(key))\n return -1;\n\n return sortData.findIndex(sort => sort.column === key);\n}\n\nfunction getSortDirection(key: TableColumn<T>['key']): SortDirection | undefined {\n return sortedMap[key]?.direction;\n}\n</script>\n\n<template>\n <thead\n :data-id=\"dataId\"\n :class=\"ui.thead()\"\n >\n <tr>\n <th\n v-if=\"selectable\"\n :class=\"ui.checkbox()\"\n scope=\"col\"\n colspan=\"1\"\n rowspan=\"1\"\n >\n <RuiCheckbox\n :disabled=\"disableCheckAll\"\n :indeterminate=\"indeterminate\"\n :model-value=\"isAllSelected\"\n :size=\"dense ? 'sm' : undefined\"\n color=\"primary\"\n data-id=\"table-toggle-check-all\"\n hide-details\n @update:model-value=\"onToggleAll($event)\"\n />\n </th>\n\n <th\n v-for=\"column in columns\"\n :key=\"column.key\"\n :class=\"[\n ui.th({ class: getAlignClass(column.align, column.sortable) }),\n column.class,\n { capitalize: !capitalizeHeaders },\n ]\"\n scope=\"col\"\n :colspan=\"column.colspan ?? 1\"\n :rowspan=\"column.rowspan ?? 1\"\n :data-id=\"column.sortable ? 'column-sortable' : undefined\"\n >\n <slot\n :column=\"column\"\n :name=\"`header.${column.key.toString()}`\"\n >\n <RuiBadge\n v-if=\"column.sortable\"\n :model-value=\"getSortIndex(column.key) >= 0\"\n :text=\"`${getSortIndex(column.key) + 1}`\"\n :offset-y=\"dense ? 8 : 0\"\n color=\"secondary\"\n size=\"sm\"\n >\n <RuiButton\n :class=\"ui.sortButton({ class: getSortButtonAlignClass(column.align) })\"\n :data-sorted=\"isSortedBy(column.key) || undefined\"\n :data-direction=\"getSortDirection(column.key)\"\n size=\"sm\"\n variant=\"text\"\n @click=\"onSort(column)\"\n >\n <span\n :class=\"ui.columnText()\"\n data-id=\"column-text\"\n >\n <slot\n :name=\"`header.text.${column.key.toString()}`\"\n :column=\"column\"\n >\n {{ column[columnAttr] }}\n </slot>\n </span>\n\n <template\n v-if=\"column.align === TableAlign.end\"\n #prepend\n >\n <RuiIcon\n :class=\"ui.sortIcon({ class: getSortIconClass(column.key) })\"\n name=\"lu-arrow-down\"\n size=\"18\"\n />\n </template>\n\n <template #append>\n <RuiIcon\n v-if=\"column.align !== TableAlign.end\"\n :class=\"ui.sortIcon({ class: getSortIconClass(column.key) })\"\n name=\"lu-arrow-down\"\n size=\"18\"\n />\n </template>\n </RuiButton>\n </RuiBadge>\n <span\n v-else\n :class=\"ui.columnText()\"\n data-id=\"column-text\"\n >\n <slot\n :name=\"`header.text.${column.key.toString()}`\"\n :column=\"column\"\n >\n {{ column[columnAttr] }}\n </slot>\n </span>\n </slot>\n </th>\n </tr>\n <tr\n v-if=\"loading\"\n :class=\"ui.loaderRow()\"\n data-id=\"thead-loader\"\n >\n <th\n :class=\"ui.progress()\"\n :colspan=\"colspan\"\n scope=\"col\"\n >\n <div :class=\"ui.progressWrapper()\">\n <RuiProgress\n color=\"primary\"\n variant=\"indeterminate\"\n />\n </div>\n </th>\n </tr>\n </thead>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"RuiTableHead.js","names":[],"sources":["../../../src/components/tables/RuiTableHead.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"T extends object, K extends keyof T = keyof T\">\nimport type { RuiIcons } from '@/icons';\nimport RuiButton from '@/components/buttons/button/RuiButton.vue';\nimport RuiCheckbox from '@/components/forms/checkbox/RuiCheckbox.vue';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport RuiBadge from '@/components/overlays/badge/RuiBadge.vue';\nimport RuiProgress from '@/components/progress/RuiProgress.vue';\nimport { getAlignClass, getSortButtonAlignClass, SortDirection, TableAlign } from '@/components/tables/table-props';\nimport { tv } from '@/utils/tv';\n\n/**\n * A sortable column name for `T`, the type of the data in the column: the name\n * must be a key of that object type.\n */\nexport type TableRowKey<T> = keyof T extends string ? keyof T : never;\n\nexport interface BaseTableColumn<T> {\n key: TableRowKey<T> | string;\n sortable?: boolean;\n direction?: SortDirection;\n align?: TableAlign;\n class?: string;\n cellClass?: string;\n tdClass?: string;\n colspan?: string | number;\n rowspan?: string | number;\n /**\n * hide this column when the table is rendered in its stacked mobile layout\n */\n mobileHidden?: boolean;\n /**\n * pin this column to the card header (top row) in the stacked mobile layout\n * instead of rendering it as a label/value pair. Intended for action columns\n * such as an overflow menu. Ignored on non-mobile layouts.\n */\n mobileHeader?: boolean;\n\n [key: string]: any;\n}\n\n/**\n * A sortable table column, which ties `sortable: true` to a key that is a real\n * property of `T`, the type of data in the table.\n */\nexport interface SortableTableColumn<T> extends BaseTableColumn<T> {\n sortable: true;\n}\n\n/**\n * An interface representing a column in a table that cannot be sorted.\n * This can be mapped to an actual property of the object or to a virtual column.\n *\n * @typeparam T - The type of data in the table.\n */\nexport interface NoneSortableTableColumn<T> extends BaseTableColumn<T> {\n sortable?: false;\n}\n\nexport type TableColumn<T> = SortableTableColumn<T> | NoneSortableTableColumn<T>;\n\nexport interface SortColumn<T> {\n column?: TableRowKey<T>;\n direction: SortDirection;\n}\n\nexport type TableSortData<T> = SortColumn<T> | SortColumn<T>[] | undefined;\n\nexport type TableRowKeyData<T> = TableRowKey<T> | TableRowKey<T>[] | undefined;\n\nexport interface GroupData<T> {\n key: string;\n value?: Partial<T>;\n}\n\nexport type GroupKey<T> = TableColumn<T>['key'];\n\nexport type GroupKeys<T> = GroupKey<T> | GroupKey<T>[] | undefined;\n\nexport interface Props<T> {\n loading?: boolean;\n stickyHeader?: boolean;\n stick?: boolean;\n selectable?: boolean;\n disableCheckAll?: boolean;\n colspan?: number;\n columns?: TableColumn<T>[];\n capitalizeHeaders?: boolean;\n isAllSelected?: boolean;\n indeterminate?: boolean;\n dense?: boolean;\n sortedMap?: Partial<Record<TableRowKey<T>, SortColumn<T>>>;\n sortData?: TableSortData<T>;\n columnAttr?: keyof TableColumn<T>;\n dataId?: string;\n}\n\nconst {\n loading = false,\n stickyHeader = false,\n stick = false,\n selectable = false,\n disableCheckAll = false,\n colspan = 0,\n columns,\n capitalizeHeaders = false,\n isAllSelected = false,\n indeterminate = false,\n dense = false,\n sortedMap = {} as Partial<Record<TableRowKey<T>, SortColumn<T>>>,\n columnAttr = 'label',\n sortData,\n dataId = 'head-main',\n} = defineProps<Props<T>>();\n\nconst emit = defineEmits<{\n 'select:all': [value: boolean];\n 'sort': [value: { key: TableRowKey<T>; direction?: SortDirection }];\n}>();\n\nconst tableHeadStyles = tv({\n slots: {\n thead: 'divide-y divide-black/[0.12] dark:divide-white/[0.12]',\n checkbox: 'px-2 w-[3.625rem] max-w-[3.625rem] [&_label]:ml-0',\n th: '[:where(&)]:px-4',\n // labels read as secondary to the data; the sorted column's label steps up to primary\n columnText: 'text-rui-text-secondary font-medium text-[0.875rem] leading-6',\n // the negative margin cancels the button's own padding so the label lines up with the cells below\n sortButton: 'inline-flex group/sort -mx-1.5',\n // a faint resting icon marks sortable columns for keyboard and touch users\n sortIcon: 'transition opacity-30 group-hover/sort:opacity-60 group-focus-visible/sort:opacity-60',\n loaderRow: 'border-none',\n progress: 'p-0 h-0',\n progressWrapper: 'h-0 -mt-1',\n },\n variants: {\n position: {\n default: {},\n sticky: { thead: 'top-0 z-10 absolute' },\n fixed: {\n thead: 'top-0 z-10 fixed',\n th: 'bg-white dark:bg-[#121212] border-b border-b-black/[0.12] dark:border-b-white/[0.12]',\n },\n },\n // a fixed height evens out rows with and without sort buttons; `:where()` lets a column's `class` win\n dense: {\n true: { th: '[:where(&)]:py-1 [:where(&)]:h-10' },\n false: { th: '[:where(&)]:py-3 [:where(&)]:h-14' },\n },\n },\n defaultVariants: {\n position: 'default',\n dense: false,\n },\n});\n\nconst headerPosition = computed<'default' | 'sticky' | 'fixed'>(() => {\n if (stickyHeader && stick)\n return 'fixed';\n if (stickyHeader)\n return 'sticky';\n return 'default';\n});\n\nconst ui = computed<ReturnType<typeof tableHeadStyles>>(() => tableHeadStyles({\n position: get(headerPosition),\n dense,\n}));\n\nfunction getSortIconClass(key: TableColumn<T>['key']): string | undefined {\n if (!isSortedBy(key))\n return undefined;\n const direction = getSortDirection(key);\n return `!opacity-100 ${direction === SortDirection.asc ? 'rotate-180' : 'rotate-0'}`;\n}\n\n/**\n * An unsorted column shows a neutral up-down icon, so nothing reads as sorted\n * until it is; the sorted column shows its direction.\n */\nfunction getSortIconName(key: TableColumn<T>['key']): RuiIcons {\n return isSortedBy(key) ? 'lu-arrow-down' : 'lu-arrow-up-down';\n}\n\nfunction getColumnTextClass(key: TableColumn<T>['key']): string {\n return get(ui).columnText({ class: isSortedBy(key) ? 'text-rui-text' : undefined });\n}\n\nfunction onSort({ key, direction }: TableColumn<T>): void {\n return emit('sort', {\n key: key as TableRowKey<T>,\n direction: direction ?? SortDirection.asc,\n });\n}\n\nfunction onToggleAll(checked: boolean) {\n return emit('select:all', checked);\n}\n\nfunction isSortedBy(key: TableColumn<T>['key']): boolean {\n return key in sortedMap;\n}\n\nfunction getSortIndex(key: TableColumn<T>['key']): number {\n if (!sortData || !Array.isArray(sortData) || !isSortedBy(key))\n return -1;\n\n return sortData.findIndex(sort => sort.column === key);\n}\n\nfunction getSortDirection(key: TableColumn<T>['key']): SortDirection | undefined {\n return sortedMap[key]?.direction;\n}\n\nfunction getAriaSort(column: TableColumn<T>): 'ascending' | 'descending' | 'none' | undefined {\n if (!column.sortable)\n return undefined;\n const direction = getSortDirection(column.key);\n if (direction === SortDirection.asc)\n return 'ascending';\n if (direction === SortDirection.desc)\n return 'descending';\n return 'none';\n}\n</script>\n\n<template>\n <thead\n :data-id=\"dataId\"\n :class=\"ui.thead()\"\n >\n <tr>\n <th\n v-if=\"selectable\"\n :class=\"ui.checkbox()\"\n scope=\"col\"\n colspan=\"1\"\n rowspan=\"1\"\n >\n <RuiCheckbox\n :disabled=\"disableCheckAll\"\n :indeterminate=\"indeterminate\"\n :model-value=\"isAllSelected\"\n :size=\"dense ? 'sm' : undefined\"\n color=\"primary\"\n data-id=\"table-toggle-check-all\"\n hide-details\n @update:model-value=\"onToggleAll($event)\"\n />\n </th>\n\n <th\n v-for=\"column in columns\"\n :key=\"column.key\"\n :class=\"[\n ui.th({ class: getAlignClass(column.align) }),\n column.class,\n { capitalize: !capitalizeHeaders },\n ]\"\n scope=\"col\"\n :colspan=\"column.colspan ?? 1\"\n :rowspan=\"column.rowspan ?? 1\"\n :aria-sort=\"getAriaSort(column)\"\n :data-id=\"column.sortable ? 'column-sortable' : undefined\"\n >\n <slot\n :column=\"column\"\n :name=\"`header.${column.key.toString()}`\"\n >\n <RuiBadge\n v-if=\"column.sortable\"\n :model-value=\"getSortIndex(column.key) >= 0\"\n :text=\"`${getSortIndex(column.key) + 1}`\"\n :offset-y=\"dense ? 8 : 0\"\n color=\"secondary\"\n size=\"sm\"\n >\n <RuiButton\n :class=\"ui.sortButton({ class: getSortButtonAlignClass(column.align) })\"\n :data-sorted=\"isSortedBy(column.key) || undefined\"\n :data-direction=\"getSortDirection(column.key)\"\n size=\"sm\"\n variant=\"text\"\n @click=\"onSort(column)\"\n >\n <span\n :class=\"getColumnTextClass(column.key)\"\n data-id=\"column-text\"\n >\n <slot\n :name=\"`header.text.${column.key.toString()}`\"\n :column=\"column\"\n >\n {{ column[columnAttr] }}\n </slot>\n </span>\n\n <template\n v-if=\"column.align === TableAlign.end\"\n #prepend\n >\n <RuiIcon\n :class=\"ui.sortIcon({ class: getSortIconClass(column.key) })\"\n :name=\"getSortIconName(column.key)\"\n size=\"18\"\n />\n </template>\n\n <template #append>\n <RuiIcon\n v-if=\"column.align !== TableAlign.end\"\n :class=\"ui.sortIcon({ class: getSortIconClass(column.key) })\"\n :name=\"getSortIconName(column.key)\"\n size=\"18\"\n />\n </template>\n </RuiButton>\n </RuiBadge>\n <span\n v-else\n :class=\"ui.columnText()\"\n data-id=\"column-text\"\n >\n <slot\n :name=\"`header.text.${column.key.toString()}`\"\n :column=\"column\"\n >\n {{ column[columnAttr] }}\n </slot>\n </span>\n </slot>\n </th>\n </tr>\n <tr\n v-if=\"loading\"\n :class=\"ui.loaderRow()\"\n data-id=\"thead-loader\"\n >\n <th\n :class=\"ui.progress()\"\n :colspan=\"colspan\"\n scope=\"col\"\n >\n <div :class=\"ui.progressWrapper()\">\n <RuiProgress\n color=\"primary\"\n variant=\"indeterminate\"\n />\n </div>\n </th>\n </tr>\n </thead>\n</template>\n"],"mappings":""}
@@ -12,6 +12,7 @@ var _hoisted_1 = ["data-id"];
12
12
  var _hoisted_2 = [
13
13
  "colspan",
14
14
  "rowspan",
15
+ "aria-sort",
15
16
  "data-id"
16
17
  ];
17
18
  var _hoisted_3 = ["colspan"];
@@ -68,10 +69,10 @@ var RuiTableHead_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ def
68
69
  slots: {
69
70
  thead: "divide-y divide-black/[0.12] dark:divide-white/[0.12]",
70
71
  checkbox: "px-2 w-[3.625rem] max-w-[3.625rem] [&_label]:ml-0",
71
- th: "p-4",
72
- columnText: "text-rui-text dark:text-white font-medium text-[0.875rem] leading-6",
73
- sortButton: "inline-flex group/sort",
74
- sortIcon: "transition opacity-0 rotate-180 group-hover/sort:opacity-60",
72
+ th: "[:where(&)]:px-4",
73
+ columnText: "text-rui-text-secondary font-medium text-[0.875rem] leading-6",
74
+ sortButton: "inline-flex group/sort -mx-1.5",
75
+ sortIcon: "transition opacity-30 group-hover/sort:opacity-60 group-focus-visible/sort:opacity-60",
75
76
  loaderRow: "border-none",
76
77
  progress: "p-0 h-0",
77
78
  progressWrapper: "h-0 -mt-1"
@@ -85,9 +86,15 @@ var RuiTableHead_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ def
85
86
  th: "bg-white dark:bg-[#121212] border-b border-b-black/[0.12] dark:border-b-white/[0.12]"
86
87
  }
87
88
  },
88
- dense: { true: { th: "py-[0.38rem]" } }
89
+ dense: {
90
+ true: { th: "[:where(&)]:py-1 [:where(&)]:h-10" },
91
+ false: { th: "[:where(&)]:py-3 [:where(&)]:h-14" }
92
+ }
89
93
  },
90
- defaultVariants: { position: "default" }
94
+ defaultVariants: {
95
+ position: "default",
96
+ dense: false
97
+ }
91
98
  });
92
99
  const headerPosition = computed(() => {
93
100
  if (__props.stickyHeader && __props.stick) return "fixed";
@@ -100,7 +107,17 @@ var RuiTableHead_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ def
100
107
  }));
101
108
  function getSortIconClass(key) {
102
109
  if (!isSortedBy(key)) return void 0;
103
- return `!opacity-100 ${getSortDirection(key) === SortDirection.asc ? "!rotate-180" : "!rotate-0"}`;
110
+ return `!opacity-100 ${getSortDirection(key) === SortDirection.asc ? "rotate-180" : "rotate-0"}`;
111
+ }
112
+ /**
113
+ * An unsorted column shows a neutral up-down icon, so nothing reads as sorted
114
+ * until it is; the sorted column shows its direction.
115
+ */
116
+ function getSortIconName(key) {
117
+ return isSortedBy(key) ? "lu-arrow-down" : "lu-arrow-up-down";
118
+ }
119
+ function getColumnTextClass(key) {
120
+ return get(ui).columnText({ class: isSortedBy(key) ? "text-rui-text" : void 0 });
104
121
  }
105
122
  function onSort({ key, direction }) {
106
123
  return emit("sort", {
@@ -121,6 +138,13 @@ var RuiTableHead_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ def
121
138
  function getSortDirection(key) {
122
139
  return __props.sortedMap[key]?.direction;
123
140
  }
141
+ function getAriaSort(column) {
142
+ if (!column.sortable) return void 0;
143
+ const direction = getSortDirection(column.key);
144
+ if (direction === SortDirection.asc) return "ascending";
145
+ if (direction === SortDirection.desc) return "descending";
146
+ return "none";
147
+ }
124
148
  return (_ctx, _cache) => {
125
149
  return openBlock(), createElementBlock("thead", {
126
150
  "data-id": __props.dataId,
@@ -149,13 +173,14 @@ var RuiTableHead_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ def
149
173
  return openBlock(), createElementBlock("th", {
150
174
  key: column.key,
151
175
  class: normalizeClass([
152
- unref(ui).th({ class: unref(getAlignClass)(column.align, column.sortable) }),
176
+ unref(ui).th({ class: unref(getAlignClass)(column.align) }),
153
177
  column.class,
154
178
  { capitalize: !__props.capitalizeHeaders }
155
179
  ]),
156
180
  scope: "col",
157
181
  colspan: column.colspan ?? 1,
158
182
  rowspan: column.rowspan ?? 1,
183
+ "aria-sort": getAriaSort(column),
159
184
  "data-id": column.sortable ? "column-sortable" : void 0
160
185
  }, [renderSlot(_ctx.$slots, `header.${column.key.toString()}`, { column }, () => [column.sortable ? (openBlock(), createBlock(RuiBadge_default, {
161
186
  key: 0,
@@ -176,11 +201,11 @@ var RuiTableHead_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ def
176
201
  append: withCtx(() => [column.align !== unref(TableAlign).end ? (openBlock(), createBlock(RuiIcon_default, {
177
202
  key: 0,
178
203
  class: normalizeClass(unref(ui).sortIcon({ class: getSortIconClass(column.key) })),
179
- name: "lu-arrow-down",
204
+ name: getSortIconName(column.key),
180
205
  size: "18"
181
- }, null, 8, ["class"])) : createCommentVNode("", true)]),
206
+ }, null, 8, ["class", "name"])) : createCommentVNode("", true)]),
182
207
  default: withCtx(() => [createElementVNode("span", {
183
- class: normalizeClass(unref(ui).columnText()),
208
+ class: normalizeClass(getColumnTextClass(column.key)),
184
209
  "data-id": "column-text"
185
210
  }, [renderSlot(_ctx.$slots, `header.text.${column.key.toString()}`, { column }, () => [createTextVNode(toDisplayString(column[__props.columnAttr]), 1)])], 2)]),
186
211
  _: 2
@@ -188,9 +213,9 @@ var RuiTableHead_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ def
188
213
  name: "prepend",
189
214
  fn: withCtx(() => [createVNode(RuiIcon_default, {
190
215
  class: normalizeClass(unref(ui).sortIcon({ class: getSortIconClass(column.key) })),
191
- name: "lu-arrow-down",
216
+ name: getSortIconName(column.key),
192
217
  size: "18"
193
- }, null, 8, ["class"])]),
218
+ }, null, 8, ["class", "name"])]),
194
219
  key: "0"
195
220
  } : void 0]), 1032, [
196
221
  "class",
@@ -1 +1 @@
1
- {"version":3,"file":"RuiTableHead.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/tables/RuiTableHead.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"T extends object, K extends keyof T = keyof T\">\nimport RuiButton from '@/components/buttons/button/RuiButton.vue';\nimport RuiCheckbox from '@/components/forms/checkbox/RuiCheckbox.vue';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport RuiBadge from '@/components/overlays/badge/RuiBadge.vue';\nimport RuiProgress from '@/components/progress/RuiProgress.vue';\nimport { getAlignClass, getSortButtonAlignClass, SortDirection, TableAlign } from '@/components/tables/table-props';\nimport { tv } from '@/utils/tv';\n\n/**\n * A sortable column name for `T`, the type of the data in the column: the name\n * must be a key of that object type.\n */\nexport type TableRowKey<T> = keyof T extends string ? keyof T : never;\n\nexport interface BaseTableColumn<T> {\n key: TableRowKey<T> | string;\n sortable?: boolean;\n direction?: SortDirection;\n align?: TableAlign;\n class?: string;\n cellClass?: string;\n tdClass?: string;\n colspan?: string | number;\n rowspan?: string | number;\n /**\n * hide this column when the table is rendered in its stacked mobile layout\n */\n mobileHidden?: boolean;\n /**\n * pin this column to the card header (top row) in the stacked mobile layout\n * instead of rendering it as a label/value pair. Intended for action columns\n * such as an overflow menu. Ignored on non-mobile layouts.\n */\n mobileHeader?: boolean;\n\n [key: string]: any;\n}\n\n/**\n * A sortable table column, which ties `sortable: true` to a key that is a real\n * property of `T`, the type of data in the table.\n */\nexport interface SortableTableColumn<T> extends BaseTableColumn<T> {\n sortable: true;\n}\n\n/**\n * An interface representing a column in a table that cannot be sorted.\n * This can be mapped to an actual property of the object or to a virtual column.\n *\n * @typeparam T - The type of data in the table.\n */\nexport interface NoneSortableTableColumn<T> extends BaseTableColumn<T> {\n sortable?: false;\n}\n\nexport type TableColumn<T> = SortableTableColumn<T> | NoneSortableTableColumn<T>;\n\nexport interface SortColumn<T> {\n column?: TableRowKey<T>;\n direction: SortDirection;\n}\n\nexport type TableSortData<T> = SortColumn<T> | SortColumn<T>[] | undefined;\n\nexport type TableRowKeyData<T> = TableRowKey<T> | TableRowKey<T>[] | undefined;\n\nexport interface GroupData<T> {\n key: string;\n value?: Partial<T>;\n}\n\nexport type GroupKey<T> = TableColumn<T>['key'];\n\nexport type GroupKeys<T> = GroupKey<T> | GroupKey<T>[] | undefined;\n\nexport interface Props<T> {\n loading?: boolean;\n stickyHeader?: boolean;\n stick?: boolean;\n selectable?: boolean;\n disableCheckAll?: boolean;\n colspan?: number;\n columns?: TableColumn<T>[];\n capitalizeHeaders?: boolean;\n isAllSelected?: boolean;\n indeterminate?: boolean;\n dense?: boolean;\n sortedMap?: Partial<Record<TableRowKey<T>, SortColumn<T>>>;\n sortData?: TableSortData<T>;\n columnAttr?: keyof TableColumn<T>;\n dataId?: string;\n}\n\nconst {\n loading = false,\n stickyHeader = false,\n stick = false,\n selectable = false,\n disableCheckAll = false,\n colspan = 0,\n columns,\n capitalizeHeaders = false,\n isAllSelected = false,\n indeterminate = false,\n dense = false,\n sortedMap = {} as Partial<Record<TableRowKey<T>, SortColumn<T>>>,\n columnAttr = 'label',\n sortData,\n dataId = 'head-main',\n} = defineProps<Props<T>>();\n\nconst emit = defineEmits<{\n 'select:all': [value: boolean];\n 'sort': [value: { key: TableRowKey<T>; direction?: SortDirection }];\n}>();\n\nconst tableHeadStyles = tv({\n slots: {\n thead: 'divide-y divide-black/[0.12] dark:divide-white/[0.12]',\n checkbox: 'px-2 w-[3.625rem] max-w-[3.625rem] [&_label]:ml-0',\n th: 'p-4',\n columnText: 'text-rui-text dark:text-white font-medium text-[0.875rem] leading-6',\n sortButton: 'inline-flex group/sort',\n sortIcon: 'transition opacity-0 rotate-180 group-hover/sort:opacity-60',\n loaderRow: 'border-none',\n progress: 'p-0 h-0',\n progressWrapper: 'h-0 -mt-1',\n },\n variants: {\n position: {\n default: {},\n sticky: { thead: 'top-0 z-10 absolute' },\n fixed: {\n thead: 'top-0 z-10 fixed',\n th: 'bg-white dark:bg-[#121212] border-b border-b-black/[0.12] dark:border-b-white/[0.12]',\n },\n },\n dense: {\n true: { th: 'py-[0.38rem]' },\n },\n },\n defaultVariants: {\n position: 'default',\n },\n});\n\nconst headerPosition = computed<'default' | 'sticky' | 'fixed'>(() => {\n if (stickyHeader && stick)\n return 'fixed';\n if (stickyHeader)\n return 'sticky';\n return 'default';\n});\n\nconst ui = computed<ReturnType<typeof tableHeadStyles>>(() => tableHeadStyles({\n position: get(headerPosition),\n dense,\n}));\n\nfunction getSortIconClass(key: TableColumn<T>['key']): string | undefined {\n if (!isSortedBy(key))\n return undefined;\n const direction = getSortDirection(key);\n return `!opacity-100 ${direction === SortDirection.asc ? '!rotate-180' : '!rotate-0'}`;\n}\n\nfunction onSort({ key, direction }: TableColumn<T>): void {\n return emit('sort', {\n key: key as TableRowKey<T>,\n direction: direction ?? SortDirection.asc,\n });\n}\n\nfunction onToggleAll(checked: boolean) {\n return emit('select:all', checked);\n}\n\nfunction isSortedBy(key: TableColumn<T>['key']): boolean {\n return key in sortedMap;\n}\n\nfunction getSortIndex(key: TableColumn<T>['key']): number {\n if (!sortData || !Array.isArray(sortData) || !isSortedBy(key))\n return -1;\n\n return sortData.findIndex(sort => sort.column === key);\n}\n\nfunction getSortDirection(key: TableColumn<T>['key']): SortDirection | undefined {\n return sortedMap[key]?.direction;\n}\n</script>\n\n<template>\n <thead\n :data-id=\"dataId\"\n :class=\"ui.thead()\"\n >\n <tr>\n <th\n v-if=\"selectable\"\n :class=\"ui.checkbox()\"\n scope=\"col\"\n colspan=\"1\"\n rowspan=\"1\"\n >\n <RuiCheckbox\n :disabled=\"disableCheckAll\"\n :indeterminate=\"indeterminate\"\n :model-value=\"isAllSelected\"\n :size=\"dense ? 'sm' : undefined\"\n color=\"primary\"\n data-id=\"table-toggle-check-all\"\n hide-details\n @update:model-value=\"onToggleAll($event)\"\n />\n </th>\n\n <th\n v-for=\"column in columns\"\n :key=\"column.key\"\n :class=\"[\n ui.th({ class: getAlignClass(column.align, column.sortable) }),\n column.class,\n { capitalize: !capitalizeHeaders },\n ]\"\n scope=\"col\"\n :colspan=\"column.colspan ?? 1\"\n :rowspan=\"column.rowspan ?? 1\"\n :data-id=\"column.sortable ? 'column-sortable' : undefined\"\n >\n <slot\n :column=\"column\"\n :name=\"`header.${column.key.toString()}`\"\n >\n <RuiBadge\n v-if=\"column.sortable\"\n :model-value=\"getSortIndex(column.key) >= 0\"\n :text=\"`${getSortIndex(column.key) + 1}`\"\n :offset-y=\"dense ? 8 : 0\"\n color=\"secondary\"\n size=\"sm\"\n >\n <RuiButton\n :class=\"ui.sortButton({ class: getSortButtonAlignClass(column.align) })\"\n :data-sorted=\"isSortedBy(column.key) || undefined\"\n :data-direction=\"getSortDirection(column.key)\"\n size=\"sm\"\n variant=\"text\"\n @click=\"onSort(column)\"\n >\n <span\n :class=\"ui.columnText()\"\n data-id=\"column-text\"\n >\n <slot\n :name=\"`header.text.${column.key.toString()}`\"\n :column=\"column\"\n >\n {{ column[columnAttr] }}\n </slot>\n </span>\n\n <template\n v-if=\"column.align === TableAlign.end\"\n #prepend\n >\n <RuiIcon\n :class=\"ui.sortIcon({ class: getSortIconClass(column.key) })\"\n name=\"lu-arrow-down\"\n size=\"18\"\n />\n </template>\n\n <template #append>\n <RuiIcon\n v-if=\"column.align !== TableAlign.end\"\n :class=\"ui.sortIcon({ class: getSortIconClass(column.key) })\"\n name=\"lu-arrow-down\"\n size=\"18\"\n />\n </template>\n </RuiButton>\n </RuiBadge>\n <span\n v-else\n :class=\"ui.columnText()\"\n data-id=\"column-text\"\n >\n <slot\n :name=\"`header.text.${column.key.toString()}`\"\n :column=\"column\"\n >\n {{ column[columnAttr] }}\n </slot>\n </span>\n </slot>\n </th>\n </tr>\n <tr\n v-if=\"loading\"\n :class=\"ui.loaderRow()\"\n data-id=\"thead-loader\"\n >\n <th\n :class=\"ui.progress()\"\n :colspan=\"colspan\"\n scope=\"col\"\n >\n <div :class=\"ui.progressWrapper()\">\n <RuiProgress\n color=\"primary\"\n variant=\"indeterminate\"\n />\n </div>\n </th>\n </tr>\n </thead>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiHA,MAAM,OAAO;EAKb,MAAM,kBAAkB,GAAG;GACzB,OAAO;IACL,OAAO;IACP,UAAU;IACV,IAAI;IACJ,YAAY;IACZ,YAAY;IACZ,UAAU;IACV,WAAW;IACX,UAAU;IACV,iBAAiB;GACnB;GACA,UAAU;IACR,UAAU;KACR,SAAS,CAAC;KACV,QAAQ,EAAE,OAAO,sBAAsB;KACvC,OAAO;MACL,OAAO;MACP,IAAI;KACN;IACF;IACA,OAAO,EACL,MAAM,EAAE,IAAI,eAAe,EAC7B;GACF;GACA,iBAAiB,EACf,UAAU,UACZ;EACF,CAAC;EAED,MAAM,iBAAiB,eAA+C;GACpE,IAAI,QAAA,gBAAgB,QAAA,OAClB,OAAO;GACT,IAAI,QAAA,cACF,OAAO;GACT,OAAO;EACT,CAAC;EAED,MAAM,KAAK,eAAmD,gBAAgB;GAC5E,UAAU,IAAI,cAAc;GAC5B,OAAI,QAAA;EACN,CAAC,CAAC;EAEF,SAAS,iBAAiB,KAAgD;GACxE,IAAI,CAAC,WAAW,GAAG,GACjB,OAAO,KAAA;GAET,OAAO,gBADW,iBAAiB,GACZ,MAAc,cAAc,MAAM,gBAAgB;EAC3E;EAEA,SAAS,OAAO,EAAE,KAAK,aAAmC;GACxD,OAAO,KAAK,QAAQ;IACb;IACL,WAAW,aAAa,cAAc;GACxC,CAAC;EACH;EAEA,SAAS,YAAY,SAAkB;GACrC,OAAO,KAAK,cAAc,OAAO;EACnC;EAEA,SAAS,WAAW,KAAqC;GACvD,OAAO,OAAO,QAAA;EAChB;EAEA,SAAS,aAAa,KAAoC;GACxD,IAAI,CAAC,QAAA,YAAY,CAAC,MAAM,QAAQ,QAAA,QAAQ,KAAK,CAAC,WAAW,GAAG,GAC1D,OAAO;GAET,OAAO,QAAA,SAAS,WAAU,SAAQ,KAAK,WAAW,GAAG;EACvD;EAEA,SAAS,iBAAiB,KAAuD;GAC/E,OAAO,QAAA,UAAU,IAAI,EAAE;EACzB;;GAIE,OAAA,UAAA,GAAA,mBA2HQ,SAAA;IA1HL,WAAS,QAAA;IACT,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,MAAK,CAAA;GAEhB,GAAA,CAAA,mBAoGK,MAAA,MAAA,CAlGK,QAAA,cADR,UAAA,GAAA,mBAiBK,MAAA;;IAfF,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,SAAQ,CAAA;IACnB,OAAM;IACN,SAAQ;IACR,SAAQ;GAER,GAAA,CAAA,YASE,qBAAA;IARC,UAAU,QAAA;IACV,eAAe,QAAA;IACf,eAAa,QAAA;IACb,MAAM,QAAA,QAAK,OAAU,KAAA;IACtB,OAAM;IACN,WAAQ;IACR,gBAAA;IACC,uBAAkB,OAAA,OAAA,OAAA,MAAA,WAAE,YAAY,MAAM;;;;;;GAI3C,CAAA,CAAA,GAAA,CAAA,KAAA,mBAAA,IAAA,IAAA,IAAA,UAAA,IAAA,GAAA,mBA+EK,UAAA,MAAA,WA9Ec,QAAA,UAAV,WAAM;IADf,OAAA,UAAA,GAAA,mBA+EK,MAAA;KA7EF,KAAK,OAAO;KACZ,OAAK,eAAA;MAAc,MAAA,EAAA,CAAE,CAAC,GAAE,EAAA,OAAU,MAAA,aAAA,CAAa,CAAC,OAAO,OAAO,OAAO,QAAQ,EAAA,CAAA;MAAgB,OAAO;MAAgC,EAAA,YAAA,CAAA,QAAA,kBAAiB;;KAKtJ,OAAM;KACL,SAAS,OAAO,WAAO;KACvB,SAAS,OAAO,WAAO;KACvB,WAAS,OAAO,WAAQ,oBAAuB,KAAA;IAEhD,GAAA,CAAA,WAiEO,KAAA,QAAA,UA/DY,OAAO,IAAI,SAAQ,KAAA,EAD3B,OAAM,SAgEV,CA5DG,OAAO,YADf,UAAA,GAAA,YAgDW,kBAAA;;KA9CR,eAAa,aAAa,OAAO,GAAG,KAAA;KACpC,MAAI,GAAK,aAAa,OAAO,GAAG,IAAA;KAChC,YAAU,QAAA,QAAK,IAAA;KAChB,OAAM;KACN,MAAK;;KAEL,SAAA,cAuCY,CAvCZ,YAuCY,mBAAA;MAtCT,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,WAAU,EAAA,OAAU,MAAA,uBAAA,CAAuB,CAAC,OAAO,KAAK,EAAA,CAAA,CAAA;MAClE,eAAa,WAAW,OAAO,GAAG,KAAK,KAAA;MACvC,kBAAgB,iBAAiB,OAAO,GAAG;MAC5C,MAAK;MACL,SAAQ;MACP,UAAK,WAAE,OAAO,MAAM;;MAyBV,QAAM,cAMb,CAJM,OAAO,UAAU,MAAA,UAAA,CAAU,CAAC,OADpC,UAAA,GAAA,YAKE,iBAAA;;OAHC,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,SAAQ,EAAA,OAAU,iBAAiB,OAAO,GAAG,EAAA,CAAA,CAAA;OACxD,MAAK;OACL,MAAK;;MA5BT,SAAA,cAUO,CAVP,mBAUO,QAAA;OATJ,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,WAAU,CAAA;OACrB,WAAQ;MAER,GAAA,CAAA,WAKO,KAAA,QAAA,eAJiB,OAAO,IAAI,SAAQ,KAAA,EAChC,OAAM,SAGV,CADF,gBAAA,gBAAA,OAAO,QAAA,WAAU,GAAA,CAAA,CAAA,CAAA,CAAA,GAAA,CAAA,CAAA,CAAA;;KAKhB,GAAA,CAAA,OAAO,UAAU,MAAA,UAAA,CAAU,CAAC,MAAA;MACjC,MAAA;MAED,IAAA,cAIE,CAJF,YAIE,iBAAA;OAHC,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,SAAQ,EAAA,OAAU,iBAAiB,OAAO,GAAG,EAAA,CAAA,CAAA;OACxD,MAAK;OACL,MAAK;;;;;;;;;;;;;;IAcb,CAAA,MAAA,UAAA,GAAA,mBAWO,QAAA;;KATJ,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,WAAU,CAAA;KACrB,WAAQ;IAER,GAAA,CAAA,WAKO,KAAA,QAAA,eAJiB,OAAO,IAAI,SAAQ,KAAA,EAChC,OAAM,SAGV,CADF,gBAAA,gBAAA,OAAO,QAAA,WAAU,GAAA,CAAA,CAAA,CAAA,CAAA,GAAA,CAAA,EAAA,CAAA,CAAA,GAAA,IAAA,UAAA;GAOtB,CAAA,GAAA,GAAA,EAAA,CAAA,GAAA,QAAA,WADR,UAAA,GAAA,mBAiBK,MAAA;;IAfF,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,UAAS,CAAA;IACpB,WAAQ;GAER,GAAA,CAAA,mBAWK,MAAA;IAVF,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,SAAQ,CAAA;IAClB,SAAS,QAAA;IACV,OAAM;GAEN,GAAA,CAAA,mBAKM,OAAA,EALA,OAAK,eAAE,MAAA,EAAA,CAAE,CAAC,gBAAe,CAAA,EAAA,GAAA,CAC7B,YAGE,qBAAA;IAFA,OAAM;IACN,SAAQ"}
1
+ {"version":3,"file":"RuiTableHead.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/tables/RuiTableHead.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"T extends object, K extends keyof T = keyof T\">\nimport type { RuiIcons } from '@/icons';\nimport RuiButton from '@/components/buttons/button/RuiButton.vue';\nimport RuiCheckbox from '@/components/forms/checkbox/RuiCheckbox.vue';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport RuiBadge from '@/components/overlays/badge/RuiBadge.vue';\nimport RuiProgress from '@/components/progress/RuiProgress.vue';\nimport { getAlignClass, getSortButtonAlignClass, SortDirection, TableAlign } from '@/components/tables/table-props';\nimport { tv } from '@/utils/tv';\n\n/**\n * A sortable column name for `T`, the type of the data in the column: the name\n * must be a key of that object type.\n */\nexport type TableRowKey<T> = keyof T extends string ? keyof T : never;\n\nexport interface BaseTableColumn<T> {\n key: TableRowKey<T> | string;\n sortable?: boolean;\n direction?: SortDirection;\n align?: TableAlign;\n class?: string;\n cellClass?: string;\n tdClass?: string;\n colspan?: string | number;\n rowspan?: string | number;\n /**\n * hide this column when the table is rendered in its stacked mobile layout\n */\n mobileHidden?: boolean;\n /**\n * pin this column to the card header (top row) in the stacked mobile layout\n * instead of rendering it as a label/value pair. Intended for action columns\n * such as an overflow menu. Ignored on non-mobile layouts.\n */\n mobileHeader?: boolean;\n\n [key: string]: any;\n}\n\n/**\n * A sortable table column, which ties `sortable: true` to a key that is a real\n * property of `T`, the type of data in the table.\n */\nexport interface SortableTableColumn<T> extends BaseTableColumn<T> {\n sortable: true;\n}\n\n/**\n * An interface representing a column in a table that cannot be sorted.\n * This can be mapped to an actual property of the object or to a virtual column.\n *\n * @typeparam T - The type of data in the table.\n */\nexport interface NoneSortableTableColumn<T> extends BaseTableColumn<T> {\n sortable?: false;\n}\n\nexport type TableColumn<T> = SortableTableColumn<T> | NoneSortableTableColumn<T>;\n\nexport interface SortColumn<T> {\n column?: TableRowKey<T>;\n direction: SortDirection;\n}\n\nexport type TableSortData<T> = SortColumn<T> | SortColumn<T>[] | undefined;\n\nexport type TableRowKeyData<T> = TableRowKey<T> | TableRowKey<T>[] | undefined;\n\nexport interface GroupData<T> {\n key: string;\n value?: Partial<T>;\n}\n\nexport type GroupKey<T> = TableColumn<T>['key'];\n\nexport type GroupKeys<T> = GroupKey<T> | GroupKey<T>[] | undefined;\n\nexport interface Props<T> {\n loading?: boolean;\n stickyHeader?: boolean;\n stick?: boolean;\n selectable?: boolean;\n disableCheckAll?: boolean;\n colspan?: number;\n columns?: TableColumn<T>[];\n capitalizeHeaders?: boolean;\n isAllSelected?: boolean;\n indeterminate?: boolean;\n dense?: boolean;\n sortedMap?: Partial<Record<TableRowKey<T>, SortColumn<T>>>;\n sortData?: TableSortData<T>;\n columnAttr?: keyof TableColumn<T>;\n dataId?: string;\n}\n\nconst {\n loading = false,\n stickyHeader = false,\n stick = false,\n selectable = false,\n disableCheckAll = false,\n colspan = 0,\n columns,\n capitalizeHeaders = false,\n isAllSelected = false,\n indeterminate = false,\n dense = false,\n sortedMap = {} as Partial<Record<TableRowKey<T>, SortColumn<T>>>,\n columnAttr = 'label',\n sortData,\n dataId = 'head-main',\n} = defineProps<Props<T>>();\n\nconst emit = defineEmits<{\n 'select:all': [value: boolean];\n 'sort': [value: { key: TableRowKey<T>; direction?: SortDirection }];\n}>();\n\nconst tableHeadStyles = tv({\n slots: {\n thead: 'divide-y divide-black/[0.12] dark:divide-white/[0.12]',\n checkbox: 'px-2 w-[3.625rem] max-w-[3.625rem] [&_label]:ml-0',\n th: '[:where(&)]:px-4',\n // labels read as secondary to the data; the sorted column's label steps up to primary\n columnText: 'text-rui-text-secondary font-medium text-[0.875rem] leading-6',\n // the negative margin cancels the button's own padding so the label lines up with the cells below\n sortButton: 'inline-flex group/sort -mx-1.5',\n // a faint resting icon marks sortable columns for keyboard and touch users\n sortIcon: 'transition opacity-30 group-hover/sort:opacity-60 group-focus-visible/sort:opacity-60',\n loaderRow: 'border-none',\n progress: 'p-0 h-0',\n progressWrapper: 'h-0 -mt-1',\n },\n variants: {\n position: {\n default: {},\n sticky: { thead: 'top-0 z-10 absolute' },\n fixed: {\n thead: 'top-0 z-10 fixed',\n th: 'bg-white dark:bg-[#121212] border-b border-b-black/[0.12] dark:border-b-white/[0.12]',\n },\n },\n // a fixed height evens out rows with and without sort buttons; `:where()` lets a column's `class` win\n dense: {\n true: { th: '[:where(&)]:py-1 [:where(&)]:h-10' },\n false: { th: '[:where(&)]:py-3 [:where(&)]:h-14' },\n },\n },\n defaultVariants: {\n position: 'default',\n dense: false,\n },\n});\n\nconst headerPosition = computed<'default' | 'sticky' | 'fixed'>(() => {\n if (stickyHeader && stick)\n return 'fixed';\n if (stickyHeader)\n return 'sticky';\n return 'default';\n});\n\nconst ui = computed<ReturnType<typeof tableHeadStyles>>(() => tableHeadStyles({\n position: get(headerPosition),\n dense,\n}));\n\nfunction getSortIconClass(key: TableColumn<T>['key']): string | undefined {\n if (!isSortedBy(key))\n return undefined;\n const direction = getSortDirection(key);\n return `!opacity-100 ${direction === SortDirection.asc ? 'rotate-180' : 'rotate-0'}`;\n}\n\n/**\n * An unsorted column shows a neutral up-down icon, so nothing reads as sorted\n * until it is; the sorted column shows its direction.\n */\nfunction getSortIconName(key: TableColumn<T>['key']): RuiIcons {\n return isSortedBy(key) ? 'lu-arrow-down' : 'lu-arrow-up-down';\n}\n\nfunction getColumnTextClass(key: TableColumn<T>['key']): string {\n return get(ui).columnText({ class: isSortedBy(key) ? 'text-rui-text' : undefined });\n}\n\nfunction onSort({ key, direction }: TableColumn<T>): void {\n return emit('sort', {\n key: key as TableRowKey<T>,\n direction: direction ?? SortDirection.asc,\n });\n}\n\nfunction onToggleAll(checked: boolean) {\n return emit('select:all', checked);\n}\n\nfunction isSortedBy(key: TableColumn<T>['key']): boolean {\n return key in sortedMap;\n}\n\nfunction getSortIndex(key: TableColumn<T>['key']): number {\n if (!sortData || !Array.isArray(sortData) || !isSortedBy(key))\n return -1;\n\n return sortData.findIndex(sort => sort.column === key);\n}\n\nfunction getSortDirection(key: TableColumn<T>['key']): SortDirection | undefined {\n return sortedMap[key]?.direction;\n}\n\nfunction getAriaSort(column: TableColumn<T>): 'ascending' | 'descending' | 'none' | undefined {\n if (!column.sortable)\n return undefined;\n const direction = getSortDirection(column.key);\n if (direction === SortDirection.asc)\n return 'ascending';\n if (direction === SortDirection.desc)\n return 'descending';\n return 'none';\n}\n</script>\n\n<template>\n <thead\n :data-id=\"dataId\"\n :class=\"ui.thead()\"\n >\n <tr>\n <th\n v-if=\"selectable\"\n :class=\"ui.checkbox()\"\n scope=\"col\"\n colspan=\"1\"\n rowspan=\"1\"\n >\n <RuiCheckbox\n :disabled=\"disableCheckAll\"\n :indeterminate=\"indeterminate\"\n :model-value=\"isAllSelected\"\n :size=\"dense ? 'sm' : undefined\"\n color=\"primary\"\n data-id=\"table-toggle-check-all\"\n hide-details\n @update:model-value=\"onToggleAll($event)\"\n />\n </th>\n\n <th\n v-for=\"column in columns\"\n :key=\"column.key\"\n :class=\"[\n ui.th({ class: getAlignClass(column.align) }),\n column.class,\n { capitalize: !capitalizeHeaders },\n ]\"\n scope=\"col\"\n :colspan=\"column.colspan ?? 1\"\n :rowspan=\"column.rowspan ?? 1\"\n :aria-sort=\"getAriaSort(column)\"\n :data-id=\"column.sortable ? 'column-sortable' : undefined\"\n >\n <slot\n :column=\"column\"\n :name=\"`header.${column.key.toString()}`\"\n >\n <RuiBadge\n v-if=\"column.sortable\"\n :model-value=\"getSortIndex(column.key) >= 0\"\n :text=\"`${getSortIndex(column.key) + 1}`\"\n :offset-y=\"dense ? 8 : 0\"\n color=\"secondary\"\n size=\"sm\"\n >\n <RuiButton\n :class=\"ui.sortButton({ class: getSortButtonAlignClass(column.align) })\"\n :data-sorted=\"isSortedBy(column.key) || undefined\"\n :data-direction=\"getSortDirection(column.key)\"\n size=\"sm\"\n variant=\"text\"\n @click=\"onSort(column)\"\n >\n <span\n :class=\"getColumnTextClass(column.key)\"\n data-id=\"column-text\"\n >\n <slot\n :name=\"`header.text.${column.key.toString()}`\"\n :column=\"column\"\n >\n {{ column[columnAttr] }}\n </slot>\n </span>\n\n <template\n v-if=\"column.align === TableAlign.end\"\n #prepend\n >\n <RuiIcon\n :class=\"ui.sortIcon({ class: getSortIconClass(column.key) })\"\n :name=\"getSortIconName(column.key)\"\n size=\"18\"\n />\n </template>\n\n <template #append>\n <RuiIcon\n v-if=\"column.align !== TableAlign.end\"\n :class=\"ui.sortIcon({ class: getSortIconClass(column.key) })\"\n :name=\"getSortIconName(column.key)\"\n size=\"18\"\n />\n </template>\n </RuiButton>\n </RuiBadge>\n <span\n v-else\n :class=\"ui.columnText()\"\n data-id=\"column-text\"\n >\n <slot\n :name=\"`header.text.${column.key.toString()}`\"\n :column=\"column\"\n >\n {{ column[columnAttr] }}\n </slot>\n </span>\n </slot>\n </th>\n </tr>\n <tr\n v-if=\"loading\"\n :class=\"ui.loaderRow()\"\n data-id=\"thead-loader\"\n >\n <th\n :class=\"ui.progress()\"\n :colspan=\"colspan\"\n scope=\"col\"\n >\n <div :class=\"ui.progressWrapper()\">\n <RuiProgress\n color=\"primary\"\n variant=\"indeterminate\"\n />\n </div>\n </th>\n </tr>\n </thead>\n</template>\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"RuiTablePagination.js","names":[],"sources":["../../../src/components/tables/RuiTablePagination.vue"],"sourcesContent":["<script lang=\"ts\" setup>\nimport RuiButton from '@/components/buttons/button/RuiButton.vue';\nimport RuiMenuSelect from '@/components/forms/select/RuiMenuSelect.vue';\nimport RuiTextField from '@/components/forms/text-field/RuiTextField.vue';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport { type TablePaginationData, usePaginationNavigation } from '@/components/tables/use-pagination-navigation';\nimport { useTable } from '@/composables/defaults/table';\nimport { tv } from '@/utils/tv';\n\nexport type { TablePaginationData };\n\nexport interface Props {\n dense?: boolean;\n disablePerPage?: boolean;\n loading?: boolean;\n /**\n * Render a compact, touch-friendly layout: the sections spread across the\n * full width and the first/last jump buttons are dropped to save space.\n */\n mobile?: boolean;\n /**\n * Maximum number of pages before the jump-to-page dropdown is replaced\n * with a numeric input. Set to `0` to always use the input, or a very\n * large number to always use the dropdown. Defaults to `500` — past that\n * materialising the full range list stalls the main thread.\n */\n rangesThreshold?: number;\n}\n\nconst modelValue = defineModel<TablePaginationData>({ required: true });\n\nconst {\n dense = false,\n loading = false,\n disablePerPage = false,\n rangesThreshold = 500,\n mobile = false,\n} = defineProps<Props>();\n\nconst paginationStyles = tv({\n slots: {\n wrapper: 'relative flex flex-wrap items-center justify-end gap-x-4 gap-y-0',\n limit: 'flex items-center space-x-2 text-caption',\n ranges: 'flex items-center space-x-2 text-caption pr-2',\n pageInput: 'w-14 [&_input]:text-center',\n sectionLabel: 'text-rui-text-secondary whitespace-nowrap py-3',\n indicator: 'text-rui-text text-caption whitespace-nowrap',\n navigation: 'flex items-center',\n },\n variants: {\n dense: {\n true: {\n wrapper: 'gap-x-2',\n },\n },\n mobile: {\n true: {\n wrapper: 'flex-nowrap justify-between gap-x-2 gap-y-0',\n ranges: 'pr-0',\n sectionLabel: 'hidden',\n },\n },\n },\n});\n\nconst ui = computed<ReturnType<typeof paginationStyles>>(() => paginationStyles({ dense, mobile }));\n\nconst tableDefaults = useTable();\n\nconst {\n limits,\n currentLimit,\n pages,\n ranges,\n indicatorText,\n currentRange,\n useInputJump,\n hasPrev,\n hasNext,\n onPrev,\n onNext,\n onFirst,\n onLast,\n} = usePaginationNavigation(modelValue, tableDefaults, () => rangesThreshold);\n\nconst pageDraft = ref<string>(String(get(currentRange)));\n\nwatch(currentRange, (value) => {\n set(pageDraft, String(value));\n});\n\nfunction commitPageInput(): void {\n const parsed = Number.parseInt(get(pageDraft), 10);\n const maxPage = get(pages);\n if (!Number.isFinite(parsed) || maxPage === 0) {\n set(pageDraft, String(get(currentRange)));\n return;\n }\n const clamped = Math.min(Math.max(parsed, 1), maxPage);\n if (clamped !== get(currentRange))\n set(currentRange, clamped);\n set(pageDraft, String(clamped));\n}\n</script>\n\n<template>\n <div :class=\"ui.wrapper()\">\n <div\n :class=\"ui.limit()\"\n data-id=\"table-pagination-limit-section\"\n >\n <span :class=\"ui.sectionLabel()\">Rows per page:</span>\n <RuiMenuSelect\n v-model=\"currentLimit\"\n :options=\"limits\"\n :disabled=\"loading || disablePerPage\"\n label-class=\"!text-xs !min-h-8\"\n name=\"limit\"\n key-attr=\"limit\"\n text-attr=\"limit\"\n hide-details\n dense\n data-id=\"table-pagination-limit\"\n />\n </div>\n <div\n :class=\"ui.ranges()\"\n data-id=\"table-pagination-ranges-section\"\n >\n <span :class=\"ui.sectionLabel()\">{{ useInputJump ? 'Page' : 'Items #' }}</span>\n <RuiTextField\n v-if=\"useInputJump\"\n v-model=\"pageDraft\"\n :disabled=\"loading\"\n :class=\"[ui.pageInput()]\"\n class=\"[&_input]:!pr-0\"\n type=\"number\"\n min=\"1\"\n :max=\"pages\"\n inputmode=\"numeric\"\n hide-details\n dense\n data-id=\"table-pagination-ranges-input\"\n @blur=\"commitPageInput()\"\n @keydown.enter=\"commitPageInput()\"\n />\n <RuiMenuSelect\n v-else-if=\"ranges.length > 0\"\n v-model=\"currentRange\"\n :options=\"ranges\"\n :disabled=\"loading\"\n label-class=\"!text-xs !min-h-8\"\n name=\"ranges\"\n key-attr=\"page\"\n text-attr=\"text\"\n hide-details\n dense\n data-id=\"table-pagination-ranges\"\n />\n <span :class=\"ui.indicator()\">\n {{ indicatorText }}\n </span>\n </div>\n <div\n :class=\"ui.navigation()\"\n data-id=\"table-pagination-navigation\"\n >\n <RuiButton\n v-if=\"!mobile\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"!hasPrev || loading\"\n variant=\"text\"\n icon\n data-id=\"table-pagination-first\"\n @click=\"onFirst()\"\n >\n <RuiIcon name=\"lu-chevrons-left\" />\n </RuiButton>\n <RuiButton\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"!hasPrev || loading\"\n variant=\"text\"\n icon\n data-id=\"table-pagination-prev\"\n @click=\"onPrev()\"\n >\n <RuiIcon name=\"lu-chevron-left\" />\n </RuiButton>\n <RuiButton\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"!hasNext || loading\"\n variant=\"text\"\n icon\n data-id=\"table-pagination-next\"\n @click=\"onNext()\"\n >\n <RuiIcon name=\"lu-chevron-right\" />\n </RuiButton>\n <RuiButton\n v-if=\"!mobile\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"!hasNext || loading\"\n variant=\"text\"\n icon\n data-id=\"table-pagination-last\"\n @click=\"onLast()\"\n >\n <RuiIcon name=\"lu-chevrons-right\" />\n </RuiButton>\n </div>\n </div>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"RuiTablePagination.js","names":[],"sources":["../../../src/components/tables/RuiTablePagination.vue"],"sourcesContent":["<script lang=\"ts\" setup>\nimport RuiButton from '@/components/buttons/button/RuiButton.vue';\nimport RuiMenuSelect from '@/components/forms/select/RuiMenuSelect.vue';\nimport RuiTextField from '@/components/forms/text-field/RuiTextField.vue';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport { type TablePaginationData, usePaginationNavigation } from '@/components/tables/use-pagination-navigation';\nimport { useTable } from '@/composables/defaults/table';\nimport { tv } from '@/utils/tv';\n\nexport type { TablePaginationData };\n\nexport interface Props {\n dense?: boolean;\n disablePerPage?: boolean;\n loading?: boolean;\n /**\n * Render a compact, touch-friendly layout: the sections spread across the\n * full width and the first/last jump buttons are dropped to save space.\n */\n mobile?: boolean;\n /**\n * Maximum number of pages before the jump-to-page dropdown is replaced\n * with a numeric input. Set to `1` to use the input whenever there is more\n * than one page, or `0` to always use the dropdown. Defaults to `500`, since\n * past that materialising the full range list stalls the main thread.\n */\n rangesThreshold?: number;\n}\n\nconst modelValue = defineModel<TablePaginationData>({ required: true });\n\nconst {\n dense = false,\n loading = false,\n disablePerPage = false,\n rangesThreshold = 500,\n mobile = false,\n} = defineProps<Props>();\n\nconst paginationStyles = tv({\n slots: {\n wrapper: 'relative flex flex-wrap items-center justify-end gap-x-4 gap-y-0',\n limit: 'flex items-center space-x-2 text-caption',\n ranges: 'flex items-center space-x-2 text-caption pr-2',\n pageInput: 'w-14 [&_input]:text-center [&_input]:!px-1 [&_input]:!text-xs [&_input]:!leading-5',\n sectionLabel: 'text-rui-text-secondary whitespace-nowrap py-3',\n select: '!text-xs !pl-3',\n indicator: 'text-rui-text text-caption whitespace-nowrap',\n navigation: 'flex items-center',\n },\n variants: {\n // the selects match the icon buttons, 28px with `size=\"sm\"` and 32px without\n dense: {\n true: {\n select: '!min-h-7',\n pageInput: '[&_input]:!py-1',\n },\n false: {\n select: '!min-h-8',\n pageInput: '[&_input]:!py-1.5',\n },\n },\n mobile: {\n true: {\n wrapper: 'flex-nowrap justify-between gap-x-2 gap-y-0',\n ranges: 'pr-0',\n // sr-only rather than hidden: a display:none label names nothing\n sectionLabel: 'sr-only',\n },\n },\n },\n});\n\nconst ui = computed<ReturnType<typeof paginationStyles>>(() => paginationStyles({ dense, mobile }));\n\nconst tableDefaults = useTable();\n\nconst {\n limits,\n currentLimit,\n pages,\n ranges,\n indicatorText,\n currentRange,\n useInputJump,\n hasPrev,\n hasNext,\n onPrev,\n onNext,\n onFirst,\n onLast,\n} = usePaginationNavigation(modelValue, tableDefaults, () => rangesThreshold);\n\n/**\n * With every row on one page the bar stays in place, as a summary, but the\n * page jump has nowhere to go, so it disables like the arrows beside it.\n */\nconst singlePage = computed<boolean>(() => get(pages) <= 1);\n\n/**\n * The rows-per-page select disables only once every option would fit all the\n * rows. Until then it stays live even on a single page: it is how a user\n * lowers a limit they raised past the row count.\n */\nconst everyLimitFits = computed<boolean>(() => {\n const { total } = get(modelValue);\n return get(singlePage) && get(limits).every(({ limit }) => limit >= total);\n});\n\nconst pageDraft = ref<string>(String(get(currentRange)));\n\nwatch(currentRange, (value) => {\n set(pageDraft, String(value));\n});\n\nfunction commitPageInput(): void {\n const parsed = Number.parseInt(get(pageDraft), 10);\n const maxPage = get(pages);\n if (!Number.isFinite(parsed) || maxPage === 0) {\n set(pageDraft, String(get(currentRange)));\n return;\n }\n const clamped = Math.min(Math.max(parsed, 1), maxPage);\n if (clamped !== get(currentRange))\n set(currentRange, clamped);\n set(pageDraft, String(clamped));\n}\n\nconst limitId = useId();\nconst rangesId = useId();\n</script>\n\n<template>\n <div :class=\"ui.wrapper()\">\n <div\n :class=\"ui.limit()\"\n data-id=\"table-pagination-limit-section\"\n >\n <label\n :for=\"limitId\"\n :class=\"ui.sectionLabel()\"\n >\n Rows per page:\n </label>\n <RuiMenuSelect\n :id=\"limitId\"\n v-model=\"currentLimit\"\n :options=\"limits\"\n :disabled=\"loading || disablePerPage || everyLimitFits\"\n :class-names=\"{ label: ui.select() }\"\n variant=\"outlined\"\n label=\"\"\n name=\"limit\"\n key-attr=\"limit\"\n text-attr=\"limit\"\n hide-details\n dense\n data-id=\"table-pagination-limit\"\n />\n </div>\n <div\n :class=\"ui.ranges()\"\n data-id=\"table-pagination-ranges-section\"\n >\n <label\n :for=\"rangesId\"\n :class=\"ui.sectionLabel({ class: useInputJump ? undefined : 'sr-only' })\"\n >\n Page\n </label>\n <RuiTextField\n v-if=\"useInputJump\"\n :id=\"rangesId\"\n v-model=\"pageDraft\"\n variant=\"outlined\"\n :disabled=\"loading || singlePage\"\n :class=\"[ui.pageInput()]\"\n type=\"number\"\n min=\"1\"\n :max=\"pages\"\n inputmode=\"numeric\"\n hide-details\n dense\n data-id=\"table-pagination-ranges-input\"\n @blur=\"commitPageInput()\"\n @keydown.enter=\"commitPageInput()\"\n />\n <RuiMenuSelect\n v-else-if=\"ranges.length > 0\"\n :id=\"rangesId\"\n v-model=\"currentRange\"\n :options=\"ranges\"\n :disabled=\"loading || singlePage\"\n :class-names=\"{ label: ui.select() }\"\n variant=\"outlined\"\n label=\"\"\n name=\"ranges\"\n key-attr=\"page\"\n text-attr=\"text\"\n hide-details\n dense\n data-id=\"table-pagination-ranges\"\n />\n <span :class=\"ui.indicator()\">\n {{ indicatorText }}\n </span>\n </div>\n <div\n :class=\"ui.navigation()\"\n data-id=\"table-pagination-navigation\"\n >\n <RuiButton\n v-if=\"!mobile\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"!hasPrev || loading\"\n variant=\"text\"\n icon\n aria-label=\"First page\"\n data-id=\"table-pagination-first\"\n @click=\"onFirst()\"\n >\n <RuiIcon name=\"lu-chevrons-left\" />\n </RuiButton>\n <RuiButton\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"!hasPrev || loading\"\n variant=\"text\"\n icon\n aria-label=\"Previous page\"\n data-id=\"table-pagination-prev\"\n @click=\"onPrev()\"\n >\n <RuiIcon name=\"lu-chevron-left\" />\n </RuiButton>\n <RuiButton\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"!hasNext || loading\"\n variant=\"text\"\n icon\n aria-label=\"Next page\"\n data-id=\"table-pagination-next\"\n @click=\"onNext()\"\n >\n <RuiIcon name=\"lu-chevron-right\" />\n </RuiButton>\n <RuiButton\n v-if=\"!mobile\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"!hasNext || loading\"\n variant=\"text\"\n icon\n aria-label=\"Last page\"\n data-id=\"table-pagination-last\"\n @click=\"onLast()\"\n >\n <RuiIcon name=\"lu-chevrons-right\" />\n </RuiButton>\n </div>\n </div>\n</template>\n"],"mappings":""}
@@ -11,9 +11,9 @@ export interface Props {
11
11
  mobile?: boolean;
12
12
  /**
13
13
  * Maximum number of pages before the jump-to-page dropdown is replaced
14
- * with a numeric input. Set to `0` to always use the input, or a very
15
- * large number to always use the dropdown. Defaults to `500` — past that
16
- * materialising the full range list stalls the main thread.
14
+ * with a numeric input. Set to `1` to use the input whenever there is more
15
+ * than one page, or `0` to always use the dropdown. Defaults to `500`, since
16
+ * past that materialising the full range list stalls the main thread.
17
17
  */
18
18
  rangesThreshold?: number;
19
19
  }
@@ -5,9 +5,11 @@ import RuiButton_default from "../buttons/button/RuiButton.js";
5
5
  import RuiTextField_default from "../forms/text-field/RuiTextField.js";
6
6
  import RuiMenuSelect_default from "../forms/select/RuiMenuSelect.js";
7
7
  import { usePaginationNavigation } from "./use-pagination-navigation.js";
8
- import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, isRef, mergeModels, normalizeClass, openBlock, ref, toDisplayString, unref, useModel, watch, withCtx, withKeys } from "vue";
8
+ import { computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, isRef, mergeModels, normalizeClass, openBlock, ref, toDisplayString, unref, useId, useModel, watch, withCtx, withKeys } from "vue";
9
9
  import { get, set } from "@vueuse/shared";
10
10
  //#region src/components/tables/RuiTablePagination.vue?vue&type=script&setup=true&lang.ts
11
+ var _hoisted_1 = ["for"];
12
+ var _hoisted_2 = ["for"];
11
13
  var RuiTablePagination_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
12
14
  __name: "RuiTablePagination",
13
15
  props: /*@__PURE__*/ mergeModels({
@@ -40,17 +42,27 @@ var RuiTablePagination_vue_vue_type_script_setup_true_lang_default = /*@__PURE__
40
42
  wrapper: "relative flex flex-wrap items-center justify-end gap-x-4 gap-y-0",
41
43
  limit: "flex items-center space-x-2 text-caption",
42
44
  ranges: "flex items-center space-x-2 text-caption pr-2",
43
- pageInput: "w-14 [&_input]:text-center",
45
+ pageInput: "w-14 [&_input]:text-center [&_input]:!px-1 [&_input]:!text-xs [&_input]:!leading-5",
44
46
  sectionLabel: "text-rui-text-secondary whitespace-nowrap py-3",
47
+ select: "!text-xs !pl-3",
45
48
  indicator: "text-rui-text text-caption whitespace-nowrap",
46
49
  navigation: "flex items-center"
47
50
  },
48
51
  variants: {
49
- dense: { true: { wrapper: "gap-x-2" } },
52
+ dense: {
53
+ true: {
54
+ select: "!min-h-7",
55
+ pageInput: "[&_input]:!py-1"
56
+ },
57
+ false: {
58
+ select: "!min-h-8",
59
+ pageInput: "[&_input]:!py-1.5"
60
+ }
61
+ },
50
62
  mobile: { true: {
51
63
  wrapper: "flex-nowrap justify-between gap-x-2 gap-y-0",
52
64
  ranges: "pr-0",
53
- sectionLabel: "hidden"
65
+ sectionLabel: "sr-only"
54
66
  } }
55
67
  }
56
68
  });
@@ -60,6 +72,20 @@ var RuiTablePagination_vue_vue_type_script_setup_true_lang_default = /*@__PURE__
60
72
  }));
61
73
  const tableDefaults = useTable();
62
74
  const { limits, currentLimit, pages, ranges, indicatorText, currentRange, useInputJump, hasPrev, hasNext, onPrev, onNext, onFirst, onLast } = usePaginationNavigation(modelValue, tableDefaults, () => __props.rangesThreshold);
75
+ /**
76
+ * With every row on one page the bar stays in place, as a summary, but the
77
+ * page jump has nowhere to go, so it disables like the arrows beside it.
78
+ */
79
+ const singlePage = computed(() => get(pages) <= 1);
80
+ /**
81
+ * The rows-per-page select disables only once every option would fit all the
82
+ * rows. Until then it stays live even on a single page: it is how a user
83
+ * lowers a limit they raised past the row count.
84
+ */
85
+ const everyLimitFits = computed(() => {
86
+ const { total } = get(modelValue);
87
+ return get(singlePage) && get(limits).every(({ limit }) => limit >= total);
88
+ });
63
89
  const pageDraft = ref(String(get(currentRange)));
64
90
  watch(currentRange, (value) => {
65
91
  set(pageDraft, String(value));
@@ -75,17 +101,25 @@ var RuiTablePagination_vue_vue_type_script_setup_true_lang_default = /*@__PURE__
75
101
  if (clamped !== get(currentRange)) set(currentRange, clamped);
76
102
  set(pageDraft, String(clamped));
77
103
  }
104
+ const limitId = useId();
105
+ const rangesId = useId();
78
106
  return (_ctx, _cache) => {
79
107
  return openBlock(), createElementBlock("div", { class: normalizeClass(unref(ui).wrapper()) }, [
80
108
  createElementVNode("div", {
81
109
  class: normalizeClass(unref(ui).limit()),
82
110
  "data-id": "table-pagination-limit-section"
83
- }, [createElementVNode("span", { class: normalizeClass(unref(ui).sectionLabel()) }, "Rows per page:", 2), createVNode(RuiMenuSelect_default, {
111
+ }, [createElementVNode("label", {
112
+ for: unref(limitId),
113
+ class: normalizeClass(unref(ui).sectionLabel())
114
+ }, " Rows per page: ", 10, _hoisted_1), createVNode(RuiMenuSelect_default, {
115
+ id: unref(limitId),
84
116
  modelValue: unref(currentLimit),
85
117
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => isRef(currentLimit) ? currentLimit.value = $event : null),
86
118
  options: unref(limits),
87
- disabled: __props.loading || __props.disablePerPage,
88
- "label-class": "!text-xs !min-h-8",
119
+ disabled: __props.loading || __props.disablePerPage || unref(everyLimitFits),
120
+ "class-names": { label: unref(ui).select() },
121
+ variant: "outlined",
122
+ label: "",
89
123
  name: "limit",
90
124
  "key-attr": "limit",
91
125
  "text-attr": "limit",
@@ -93,21 +127,28 @@ var RuiTablePagination_vue_vue_type_script_setup_true_lang_default = /*@__PURE__
93
127
  dense: "",
94
128
  "data-id": "table-pagination-limit"
95
129
  }, null, 8, [
130
+ "id",
96
131
  "modelValue",
97
132
  "options",
98
- "disabled"
133
+ "disabled",
134
+ "class-names"
99
135
  ])], 2),
100
136
  createElementVNode("div", {
101
137
  class: normalizeClass(unref(ui).ranges()),
102
138
  "data-id": "table-pagination-ranges-section"
103
139
  }, [
104
- createElementVNode("span", { class: normalizeClass(unref(ui).sectionLabel()) }, toDisplayString(unref(useInputJump) ? "Page" : "Items #"), 3),
140
+ createElementVNode("label", {
141
+ for: unref(rangesId),
142
+ class: normalizeClass(unref(ui).sectionLabel({ class: unref(useInputJump) ? void 0 : "sr-only" }))
143
+ }, " Page ", 10, _hoisted_2),
105
144
  unref(useInputJump) ? (openBlock(), createBlock(RuiTextField_default, {
106
145
  key: 0,
146
+ id: unref(rangesId),
107
147
  modelValue: unref(pageDraft),
108
148
  "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => isRef(pageDraft) ? pageDraft.value = $event : null),
109
- disabled: __props.loading,
110
- class: normalizeClass([[unref(ui).pageInput()], "[&_input]:!pr-0"]),
149
+ variant: "outlined",
150
+ disabled: __props.loading || unref(singlePage),
151
+ class: normalizeClass([unref(ui).pageInput()]),
111
152
  type: "number",
112
153
  min: "1",
113
154
  max: unref(pages),
@@ -118,17 +159,21 @@ var RuiTablePagination_vue_vue_type_script_setup_true_lang_default = /*@__PURE__
118
159
  onBlur: _cache[2] || (_cache[2] = ($event) => commitPageInput()),
119
160
  onKeydown: _cache[3] || (_cache[3] = withKeys(($event) => commitPageInput(), ["enter"]))
120
161
  }, null, 8, [
162
+ "id",
121
163
  "modelValue",
122
164
  "disabled",
123
165
  "class",
124
166
  "max"
125
167
  ])) : unref(ranges).length > 0 ? (openBlock(), createBlock(RuiMenuSelect_default, {
126
168
  key: 1,
169
+ id: unref(rangesId),
127
170
  modelValue: unref(currentRange),
128
171
  "onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => isRef(currentRange) ? currentRange.value = $event : null),
129
172
  options: unref(ranges),
130
- disabled: __props.loading,
131
- "label-class": "!text-xs !min-h-8",
173
+ disabled: __props.loading || unref(singlePage),
174
+ "class-names": { label: unref(ui).select() },
175
+ variant: "outlined",
176
+ label: "",
132
177
  name: "ranges",
133
178
  "key-attr": "page",
134
179
  "text-attr": "text",
@@ -136,9 +181,11 @@ var RuiTablePagination_vue_vue_type_script_setup_true_lang_default = /*@__PURE__
136
181
  dense: "",
137
182
  "data-id": "table-pagination-ranges"
138
183
  }, null, 8, [
184
+ "id",
139
185
  "modelValue",
140
186
  "options",
141
- "disabled"
187
+ "disabled",
188
+ "class-names"
142
189
  ])) : createCommentVNode("", true),
143
190
  createElementVNode("span", { class: normalizeClass(unref(ui).indicator()) }, toDisplayString(unref(indicatorText)), 3)
144
191
  ], 2),
@@ -152,6 +199,7 @@ var RuiTablePagination_vue_vue_type_script_setup_true_lang_default = /*@__PURE__
152
199
  disabled: !unref(hasPrev) || __props.loading,
153
200
  variant: "text",
154
201
  icon: "",
202
+ "aria-label": "First page",
155
203
  "data-id": "table-pagination-first",
156
204
  onClick: _cache[5] || (_cache[5] = ($event) => unref(onFirst)())
157
205
  }, {
@@ -163,6 +211,7 @@ var RuiTablePagination_vue_vue_type_script_setup_true_lang_default = /*@__PURE__
163
211
  disabled: !unref(hasPrev) || __props.loading,
164
212
  variant: "text",
165
213
  icon: "",
214
+ "aria-label": "Previous page",
166
215
  "data-id": "table-pagination-prev",
167
216
  onClick: _cache[6] || (_cache[6] = ($event) => unref(onPrev)())
168
217
  }, {
@@ -174,6 +223,7 @@ var RuiTablePagination_vue_vue_type_script_setup_true_lang_default = /*@__PURE__
174
223
  disabled: !unref(hasNext) || __props.loading,
175
224
  variant: "text",
176
225
  icon: "",
226
+ "aria-label": "Next page",
177
227
  "data-id": "table-pagination-next",
178
228
  onClick: _cache[7] || (_cache[7] = ($event) => unref(onNext)())
179
229
  }, {
@@ -186,6 +236,7 @@ var RuiTablePagination_vue_vue_type_script_setup_true_lang_default = /*@__PURE__
186
236
  disabled: !unref(hasNext) || __props.loading,
187
237
  variant: "text",
188
238
  icon: "",
239
+ "aria-label": "Last page",
189
240
  "data-id": "table-pagination-last",
190
241
  onClick: _cache[8] || (_cache[8] = ($event) => unref(onLast)())
191
242
  }, {
@@ -1 +1 @@
1
- {"version":3,"file":"RuiTablePagination.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/tables/RuiTablePagination.vue"],"sourcesContent":["<script lang=\"ts\" setup>\nimport RuiButton from '@/components/buttons/button/RuiButton.vue';\nimport RuiMenuSelect from '@/components/forms/select/RuiMenuSelect.vue';\nimport RuiTextField from '@/components/forms/text-field/RuiTextField.vue';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport { type TablePaginationData, usePaginationNavigation } from '@/components/tables/use-pagination-navigation';\nimport { useTable } from '@/composables/defaults/table';\nimport { tv } from '@/utils/tv';\n\nexport type { TablePaginationData };\n\nexport interface Props {\n dense?: boolean;\n disablePerPage?: boolean;\n loading?: boolean;\n /**\n * Render a compact, touch-friendly layout: the sections spread across the\n * full width and the first/last jump buttons are dropped to save space.\n */\n mobile?: boolean;\n /**\n * Maximum number of pages before the jump-to-page dropdown is replaced\n * with a numeric input. Set to `0` to always use the input, or a very\n * large number to always use the dropdown. Defaults to `500` — past that\n * materialising the full range list stalls the main thread.\n */\n rangesThreshold?: number;\n}\n\nconst modelValue = defineModel<TablePaginationData>({ required: true });\n\nconst {\n dense = false,\n loading = false,\n disablePerPage = false,\n rangesThreshold = 500,\n mobile = false,\n} = defineProps<Props>();\n\nconst paginationStyles = tv({\n slots: {\n wrapper: 'relative flex flex-wrap items-center justify-end gap-x-4 gap-y-0',\n limit: 'flex items-center space-x-2 text-caption',\n ranges: 'flex items-center space-x-2 text-caption pr-2',\n pageInput: 'w-14 [&_input]:text-center',\n sectionLabel: 'text-rui-text-secondary whitespace-nowrap py-3',\n indicator: 'text-rui-text text-caption whitespace-nowrap',\n navigation: 'flex items-center',\n },\n variants: {\n dense: {\n true: {\n wrapper: 'gap-x-2',\n },\n },\n mobile: {\n true: {\n wrapper: 'flex-nowrap justify-between gap-x-2 gap-y-0',\n ranges: 'pr-0',\n sectionLabel: 'hidden',\n },\n },\n },\n});\n\nconst ui = computed<ReturnType<typeof paginationStyles>>(() => paginationStyles({ dense, mobile }));\n\nconst tableDefaults = useTable();\n\nconst {\n limits,\n currentLimit,\n pages,\n ranges,\n indicatorText,\n currentRange,\n useInputJump,\n hasPrev,\n hasNext,\n onPrev,\n onNext,\n onFirst,\n onLast,\n} = usePaginationNavigation(modelValue, tableDefaults, () => rangesThreshold);\n\nconst pageDraft = ref<string>(String(get(currentRange)));\n\nwatch(currentRange, (value) => {\n set(pageDraft, String(value));\n});\n\nfunction commitPageInput(): void {\n const parsed = Number.parseInt(get(pageDraft), 10);\n const maxPage = get(pages);\n if (!Number.isFinite(parsed) || maxPage === 0) {\n set(pageDraft, String(get(currentRange)));\n return;\n }\n const clamped = Math.min(Math.max(parsed, 1), maxPage);\n if (clamped !== get(currentRange))\n set(currentRange, clamped);\n set(pageDraft, String(clamped));\n}\n</script>\n\n<template>\n <div :class=\"ui.wrapper()\">\n <div\n :class=\"ui.limit()\"\n data-id=\"table-pagination-limit-section\"\n >\n <span :class=\"ui.sectionLabel()\">Rows per page:</span>\n <RuiMenuSelect\n v-model=\"currentLimit\"\n :options=\"limits\"\n :disabled=\"loading || disablePerPage\"\n label-class=\"!text-xs !min-h-8\"\n name=\"limit\"\n key-attr=\"limit\"\n text-attr=\"limit\"\n hide-details\n dense\n data-id=\"table-pagination-limit\"\n />\n </div>\n <div\n :class=\"ui.ranges()\"\n data-id=\"table-pagination-ranges-section\"\n >\n <span :class=\"ui.sectionLabel()\">{{ useInputJump ? 'Page' : 'Items #' }}</span>\n <RuiTextField\n v-if=\"useInputJump\"\n v-model=\"pageDraft\"\n :disabled=\"loading\"\n :class=\"[ui.pageInput()]\"\n class=\"[&_input]:!pr-0\"\n type=\"number\"\n min=\"1\"\n :max=\"pages\"\n inputmode=\"numeric\"\n hide-details\n dense\n data-id=\"table-pagination-ranges-input\"\n @blur=\"commitPageInput()\"\n @keydown.enter=\"commitPageInput()\"\n />\n <RuiMenuSelect\n v-else-if=\"ranges.length > 0\"\n v-model=\"currentRange\"\n :options=\"ranges\"\n :disabled=\"loading\"\n label-class=\"!text-xs !min-h-8\"\n name=\"ranges\"\n key-attr=\"page\"\n text-attr=\"text\"\n hide-details\n dense\n data-id=\"table-pagination-ranges\"\n />\n <span :class=\"ui.indicator()\">\n {{ indicatorText }}\n </span>\n </div>\n <div\n :class=\"ui.navigation()\"\n data-id=\"table-pagination-navigation\"\n >\n <RuiButton\n v-if=\"!mobile\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"!hasPrev || loading\"\n variant=\"text\"\n icon\n data-id=\"table-pagination-first\"\n @click=\"onFirst()\"\n >\n <RuiIcon name=\"lu-chevrons-left\" />\n </RuiButton>\n <RuiButton\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"!hasPrev || loading\"\n variant=\"text\"\n icon\n data-id=\"table-pagination-prev\"\n @click=\"onPrev()\"\n >\n <RuiIcon name=\"lu-chevron-left\" />\n </RuiButton>\n <RuiButton\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"!hasNext || loading\"\n variant=\"text\"\n icon\n data-id=\"table-pagination-next\"\n @click=\"onNext()\"\n >\n <RuiIcon name=\"lu-chevron-right\" />\n </RuiButton>\n <RuiButton\n v-if=\"!mobile\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"!hasNext || loading\"\n variant=\"text\"\n icon\n data-id=\"table-pagination-last\"\n @click=\"onLast()\"\n >\n <RuiIcon name=\"lu-chevrons-right\" />\n </RuiButton>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"RuiTablePagination.vue_vue_type_script_setup_true_lang.js","names":[],"sources":["../../../src/components/tables/RuiTablePagination.vue"],"sourcesContent":["<script lang=\"ts\" setup>\nimport RuiButton from '@/components/buttons/button/RuiButton.vue';\nimport RuiMenuSelect from '@/components/forms/select/RuiMenuSelect.vue';\nimport RuiTextField from '@/components/forms/text-field/RuiTextField.vue';\nimport RuiIcon from '@/components/icons/RuiIcon.vue';\nimport { type TablePaginationData, usePaginationNavigation } from '@/components/tables/use-pagination-navigation';\nimport { useTable } from '@/composables/defaults/table';\nimport { tv } from '@/utils/tv';\n\nexport type { TablePaginationData };\n\nexport interface Props {\n dense?: boolean;\n disablePerPage?: boolean;\n loading?: boolean;\n /**\n * Render a compact, touch-friendly layout: the sections spread across the\n * full width and the first/last jump buttons are dropped to save space.\n */\n mobile?: boolean;\n /**\n * Maximum number of pages before the jump-to-page dropdown is replaced\n * with a numeric input. Set to `1` to use the input whenever there is more\n * than one page, or `0` to always use the dropdown. Defaults to `500`, since\n * past that materialising the full range list stalls the main thread.\n */\n rangesThreshold?: number;\n}\n\nconst modelValue = defineModel<TablePaginationData>({ required: true });\n\nconst {\n dense = false,\n loading = false,\n disablePerPage = false,\n rangesThreshold = 500,\n mobile = false,\n} = defineProps<Props>();\n\nconst paginationStyles = tv({\n slots: {\n wrapper: 'relative flex flex-wrap items-center justify-end gap-x-4 gap-y-0',\n limit: 'flex items-center space-x-2 text-caption',\n ranges: 'flex items-center space-x-2 text-caption pr-2',\n pageInput: 'w-14 [&_input]:text-center [&_input]:!px-1 [&_input]:!text-xs [&_input]:!leading-5',\n sectionLabel: 'text-rui-text-secondary whitespace-nowrap py-3',\n select: '!text-xs !pl-3',\n indicator: 'text-rui-text text-caption whitespace-nowrap',\n navigation: 'flex items-center',\n },\n variants: {\n // the selects match the icon buttons, 28px with `size=\"sm\"` and 32px without\n dense: {\n true: {\n select: '!min-h-7',\n pageInput: '[&_input]:!py-1',\n },\n false: {\n select: '!min-h-8',\n pageInput: '[&_input]:!py-1.5',\n },\n },\n mobile: {\n true: {\n wrapper: 'flex-nowrap justify-between gap-x-2 gap-y-0',\n ranges: 'pr-0',\n // sr-only rather than hidden: a display:none label names nothing\n sectionLabel: 'sr-only',\n },\n },\n },\n});\n\nconst ui = computed<ReturnType<typeof paginationStyles>>(() => paginationStyles({ dense, mobile }));\n\nconst tableDefaults = useTable();\n\nconst {\n limits,\n currentLimit,\n pages,\n ranges,\n indicatorText,\n currentRange,\n useInputJump,\n hasPrev,\n hasNext,\n onPrev,\n onNext,\n onFirst,\n onLast,\n} = usePaginationNavigation(modelValue, tableDefaults, () => rangesThreshold);\n\n/**\n * With every row on one page the bar stays in place, as a summary, but the\n * page jump has nowhere to go, so it disables like the arrows beside it.\n */\nconst singlePage = computed<boolean>(() => get(pages) <= 1);\n\n/**\n * The rows-per-page select disables only once every option would fit all the\n * rows. Until then it stays live even on a single page: it is how a user\n * lowers a limit they raised past the row count.\n */\nconst everyLimitFits = computed<boolean>(() => {\n const { total } = get(modelValue);\n return get(singlePage) && get(limits).every(({ limit }) => limit >= total);\n});\n\nconst pageDraft = ref<string>(String(get(currentRange)));\n\nwatch(currentRange, (value) => {\n set(pageDraft, String(value));\n});\n\nfunction commitPageInput(): void {\n const parsed = Number.parseInt(get(pageDraft), 10);\n const maxPage = get(pages);\n if (!Number.isFinite(parsed) || maxPage === 0) {\n set(pageDraft, String(get(currentRange)));\n return;\n }\n const clamped = Math.min(Math.max(parsed, 1), maxPage);\n if (clamped !== get(currentRange))\n set(currentRange, clamped);\n set(pageDraft, String(clamped));\n}\n\nconst limitId = useId();\nconst rangesId = useId();\n</script>\n\n<template>\n <div :class=\"ui.wrapper()\">\n <div\n :class=\"ui.limit()\"\n data-id=\"table-pagination-limit-section\"\n >\n <label\n :for=\"limitId\"\n :class=\"ui.sectionLabel()\"\n >\n Rows per page:\n </label>\n <RuiMenuSelect\n :id=\"limitId\"\n v-model=\"currentLimit\"\n :options=\"limits\"\n :disabled=\"loading || disablePerPage || everyLimitFits\"\n :class-names=\"{ label: ui.select() }\"\n variant=\"outlined\"\n label=\"\"\n name=\"limit\"\n key-attr=\"limit\"\n text-attr=\"limit\"\n hide-details\n dense\n data-id=\"table-pagination-limit\"\n />\n </div>\n <div\n :class=\"ui.ranges()\"\n data-id=\"table-pagination-ranges-section\"\n >\n <label\n :for=\"rangesId\"\n :class=\"ui.sectionLabel({ class: useInputJump ? undefined : 'sr-only' })\"\n >\n Page\n </label>\n <RuiTextField\n v-if=\"useInputJump\"\n :id=\"rangesId\"\n v-model=\"pageDraft\"\n variant=\"outlined\"\n :disabled=\"loading || singlePage\"\n :class=\"[ui.pageInput()]\"\n type=\"number\"\n min=\"1\"\n :max=\"pages\"\n inputmode=\"numeric\"\n hide-details\n dense\n data-id=\"table-pagination-ranges-input\"\n @blur=\"commitPageInput()\"\n @keydown.enter=\"commitPageInput()\"\n />\n <RuiMenuSelect\n v-else-if=\"ranges.length > 0\"\n :id=\"rangesId\"\n v-model=\"currentRange\"\n :options=\"ranges\"\n :disabled=\"loading || singlePage\"\n :class-names=\"{ label: ui.select() }\"\n variant=\"outlined\"\n label=\"\"\n name=\"ranges\"\n key-attr=\"page\"\n text-attr=\"text\"\n hide-details\n dense\n data-id=\"table-pagination-ranges\"\n />\n <span :class=\"ui.indicator()\">\n {{ indicatorText }}\n </span>\n </div>\n <div\n :class=\"ui.navigation()\"\n data-id=\"table-pagination-navigation\"\n >\n <RuiButton\n v-if=\"!mobile\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"!hasPrev || loading\"\n variant=\"text\"\n icon\n aria-label=\"First page\"\n data-id=\"table-pagination-first\"\n @click=\"onFirst()\"\n >\n <RuiIcon name=\"lu-chevrons-left\" />\n </RuiButton>\n <RuiButton\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"!hasPrev || loading\"\n variant=\"text\"\n icon\n aria-label=\"Previous page\"\n data-id=\"table-pagination-prev\"\n @click=\"onPrev()\"\n >\n <RuiIcon name=\"lu-chevron-left\" />\n </RuiButton>\n <RuiButton\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"!hasNext || loading\"\n variant=\"text\"\n icon\n aria-label=\"Next page\"\n data-id=\"table-pagination-next\"\n @click=\"onNext()\"\n >\n <RuiIcon name=\"lu-chevron-right\" />\n </RuiButton>\n <RuiButton\n v-if=\"!mobile\"\n :size=\"dense ? 'sm' : undefined\"\n :disabled=\"!hasNext || loading\"\n variant=\"text\"\n icon\n aria-label=\"Last page\"\n data-id=\"table-pagination-last\"\n @click=\"onLast()\"\n >\n <RuiIcon name=\"lu-chevrons-right\" />\n </RuiButton>\n </div>\n </div>\n</template>\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"RuiDataTableBody.js","names":[],"sources":["../../../../src/components/tables/data-table/RuiDataTableBody.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"T extends object\">\nimport type { TableColumn } from '@/components/tables/RuiTableHead.vue';\nimport { useDataTableColumns, useDataTableRowIdentity, useDataTableStyling } from '@/components/tables/data-table/context';\nimport RuiDataTableEmptyRow from '@/components/tables/data-table/RuiDataTableEmptyRow.vue';\nimport RuiDataTableGroupRow from '@/components/tables/data-table/RuiDataTableGroupRow.vue';\nimport RuiDataTableLoadingRow from '@/components/tables/data-table/RuiDataTableLoadingRow.vue';\nimport RuiDataTableRow from '@/components/tables/data-table/RuiDataTableRow.vue';\nimport { type GroupHeader, isRow } from '@/composables/tables/data-table/types';\n\ndefineProps<{\n filtered: (T | GroupHeader<T>)[];\n loading: boolean;\n noData: boolean;\n empty: { label?: string; description?: string };\n}>();\n\ndefineSlots<Partial<\n Record<`item.${string}`, (props: { column: TableColumn<T>; row: T; index: number }) => any> & {\n 'body.prepend'?: (props: { colspan: number }) => any;\n 'body.append'?: (props: { colspan: number }) => any;\n 'group.header'?: (props: {\n colspan: number;\n header: GroupHeader<T>;\n isOpen: boolean;\n toggle: () => void;\n }) => any;\n 'group.header.content'?: (props: { header: GroupHeader<T>; groupKey: string }) => any;\n 'expanded-item'?: (props: { row: T; index: number }) => any;\n 'no-data'?: () => any;\n 'empty-description'?: () => any;\n }\n>>();\n\nconst { classes, colspan } = useDataTableStyling();\nconst { itemSlotKeys } = useDataTableColumns<T>();\nconst { getRowId } = useDataTableRowIdentity<T>();\n</script>\n\n<template>\n <tbody :class=\"classes.tbody\">\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n v-if=\"$slots['body.prepend']\"\n :colspan=\"colspan\"\n name=\"body.prepend\"\n />\n <template v-for=\"(row, index) in filtered\">\n <RuiDataTableGroupRow\n v-if=\"!isRow(row)\"\n :key=\"`group-${row.identifier}`\"\n :row=\"row\"\n >\n <template\n v-if=\"$slots['group.header']\"\n #group.header=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n name=\"group.header\"\n v-bind=\"slotData\"\n />\n </template>\n <template\n v-if=\"$slots['group.header.content']\"\n #group.header.content=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n name=\"group.header.content\"\n v-bind=\"slotData\"\n />\n </template>\n </RuiDataTableGroupRow>\n <RuiDataTableRow\n v-else\n :key=\"`row-${getRowId(row)}`\"\n :row=\"row\"\n :index=\"index\"\n >\n <template\n v-for=\"key in itemSlotKeys\"\n #[`item.${key}`]=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n :name=\"`item.${key}`\"\n v-bind=\"slotData\"\n />\n </template>\n <template\n v-if=\"$slots['expanded-item']\"\n #expanded-item=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n name=\"expanded-item\"\n v-bind=\"slotData\"\n />\n </template>\n </RuiDataTableRow>\n </template>\n <RuiDataTableLoadingRow v-if=\"loading && noData\" />\n <RuiDataTableEmptyRow\n v-if=\"noData && empty && !loading\"\n :empty=\"empty\"\n >\n <template\n v-if=\"$slots['no-data']\"\n #no-data\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot name=\"no-data\" />\n </template>\n <template\n v-if=\"$slots['empty-description']\"\n #empty-description\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot name=\"empty-description\" />\n </template>\n </RuiDataTableEmptyRow>\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n v-if=\"$slots['body.append']\"\n :colspan=\"colspan\"\n name=\"body.append\"\n />\n </tbody>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"RuiDataTableBody.js","names":[],"sources":["../../../../src/components/tables/data-table/RuiDataTableBody.vue"],"sourcesContent":["<script lang=\"ts\" setup generic=\"T extends object\">\nimport type { TableColumn } from '@/components/tables/RuiTableHead.vue';\nimport { useDataTableColumns, useDataTableRowIdentity, useDataTableStyling } from '@/components/tables/data-table/context';\nimport RuiDataTableEmptyRow from '@/components/tables/data-table/RuiDataTableEmptyRow.vue';\nimport RuiDataTableErrorRow from '@/components/tables/data-table/RuiDataTableErrorRow.vue';\nimport RuiDataTableGroupRow from '@/components/tables/data-table/RuiDataTableGroupRow.vue';\nimport RuiDataTableLoadingRow from '@/components/tables/data-table/RuiDataTableLoadingRow.vue';\nimport RuiDataTableRow from '@/components/tables/data-table/RuiDataTableRow.vue';\nimport { type GroupHeader, isRow } from '@/composables/tables/data-table/types';\n\nconst { error, loading, noData } = defineProps<{\n filtered: (T | GroupHeader<T>)[];\n loading: boolean;\n noData: boolean;\n empty: { label?: string; description?: string };\n error: string;\n errorTitle: string;\n retryText: string;\n}>();\n\nconst emit = defineEmits<{\n retry: [];\n}>();\n\ndefineSlots<Partial<\n Record<`item.${string}`, (props: { column: TableColumn<T>; row: T; index: number }) => any> & {\n 'body.prepend'?: (props: { colspan: number }) => any;\n 'body.append'?: (props: { colspan: number }) => any;\n 'group.header'?: (props: {\n colspan: number;\n header: GroupHeader<T>;\n isOpen: boolean;\n toggle: () => void;\n }) => any;\n 'group.header.content'?: (props: { header: GroupHeader<T>; groupKey: string }) => any;\n 'expanded-item'?: (props: { row: T; index: number }) => any;\n 'no-data'?: () => any;\n 'empty-description'?: () => any;\n 'error'?: () => any;\n }\n>>();\n\nconst { classes, colspan } = useDataTableStyling();\nconst { itemSlotKeys } = useDataTableColumns<T>();\nconst { getRowId } = useDataTableRowIdentity<T>();\n\n/**\n * Loading outranks a failure, which outranks the empty state: rows that did\n * not arrive are not rows that are not there. Rows kept from an earlier read\n * stay below the error.\n */\nconst showError = computed<boolean>(() => !!error && !loading);\n\n/**\n * Rows shown while a read is in flight are the previous result, so they dim\n * until it lands. An empty table shows its loading row instead, undimmed.\n */\nconst stale = computed<boolean>(() => loading && !noData);\n</script>\n\n<template>\n <tbody\n class=\"transition-opacity duration-150\"\n :class=\"[classes.tbody, { 'opacity-50': stale }]\"\n :data-stale=\"stale || undefined\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n v-if=\"$slots['body.prepend']\"\n :colspan=\"colspan\"\n name=\"body.prepend\"\n />\n <RuiDataTableErrorRow\n v-if=\"showError\"\n :message=\"error\"\n :title=\"errorTitle\"\n :retry-text=\"retryText\"\n @retry=\"emit('retry')\"\n >\n <template\n v-if=\"$slots.error\"\n #error\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot name=\"error\" />\n </template>\n </RuiDataTableErrorRow>\n <template v-for=\"(row, index) in filtered\">\n <RuiDataTableGroupRow\n v-if=\"!isRow(row)\"\n :key=\"`group-${row.identifier}`\"\n :row=\"row\"\n >\n <template\n v-if=\"$slots['group.header']\"\n #group.header=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n name=\"group.header\"\n v-bind=\"slotData\"\n />\n </template>\n <template\n v-if=\"$slots['group.header.content']\"\n #group.header.content=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n name=\"group.header.content\"\n v-bind=\"slotData\"\n />\n </template>\n </RuiDataTableGroupRow>\n <RuiDataTableRow\n v-else\n :key=\"`row-${getRowId(row)}`\"\n :row=\"row\"\n :index=\"index\"\n >\n <template\n v-for=\"key in itemSlotKeys\"\n #[`item.${key}`]=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n :name=\"`item.${key}`\"\n v-bind=\"slotData\"\n />\n </template>\n <template\n v-if=\"$slots['expanded-item']\"\n #expanded-item=\"slotData\"\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n name=\"expanded-item\"\n v-bind=\"slotData\"\n />\n </template>\n </RuiDataTableRow>\n </template>\n <RuiDataTableLoadingRow v-if=\"loading && noData\" />\n <RuiDataTableEmptyRow\n v-if=\"noData && empty && !loading && !showError\"\n :empty=\"empty\"\n >\n <template\n v-if=\"$slots['no-data']\"\n #no-data\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot name=\"no-data\" />\n </template>\n <template\n v-if=\"$slots['empty-description']\"\n #empty-description\n >\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot name=\"empty-description\" />\n </template>\n </RuiDataTableEmptyRow>\n <!-- eslint-disable-next-line vue/require-explicit-slots -- defined via Partial<Record<...>> in defineSlots -->\n <slot\n v-if=\"$slots['body.append']\"\n :colspan=\"colspan\"\n name=\"body.append\"\n />\n </tbody>\n</template>\n"],"mappings":""}
@@ -9,6 +9,11 @@ declare const __VLS_export: <T extends object>(__VLS_props: NonNullable<Awaited<
9
9
  label?: string;
10
10
  description?: string;
11
11
  };
12
+ error: string;
13
+ errorTitle: string;
14
+ retryText: string;
15
+ } & {
16
+ onRetry?: (() => any) | undefined;
12
17
  }> & (typeof globalThis extends {
13
18
  __VLS_PROPS_FALLBACK: infer P;
14
19
  } ? P : {});
@@ -41,8 +46,9 @@ declare const __VLS_export: <T extends object>(__VLS_props: NonNullable<Awaited<
41
46
  }) => any;
42
47
  'no-data'?: () => any;
43
48
  'empty-description'?: () => any;
49
+ error?: () => any;
44
50
  }>;
45
- emit: {};
51
+ emit: (evt: "retry") => void;
46
52
  }>) => import("vue").VNode & {
47
53
  __ctx?: NonNullable<Awaited<typeof __VLS_setup>>;
48
54
  };