bootstrap-vue-next
Version:
Seamless integration of Vue 3, Bootstrap 5, and TypeScript for modern, type-safe UI development
1 lines • 149 kB
Source Map (JSON)
{"version":3,"file":"BTable-C__GSYX2.mjs","names":[],"sources":["../src/types/TableTypes.ts","../src/components/BTable/BTbody.vue","../src/components/BTable/BTbody.vue","../src/components/BTable/BTd.vue","../src/components/BTable/BTd.vue","../src/components/BTable/BTfoot.vue","../src/components/BTable/BTfoot.vue","../src/components/BTable/BTh.vue","../src/components/BTable/BTh.vue","../src/components/BTable/BThead.vue","../src/components/BTable/BThead.vue","../src/components/BTable/BTr.vue","../src/components/BTable/BTr.vue","../src/utils/tableUtils.ts","../src/utils/filterEvent.ts","../src/composables/useTableLiteHelpers.ts","../src/components/BTable/BTableLite.vue","../src/components/BTable/BTableLite.vue","../src/composables/useTableHelpers.ts","../src/components/BTable/BTable.vue","../src/components/BTable/BTable.vue"],"sourcesContent":["import type {ComputedRef, StyleValue} from 'vue'\nimport type {ColorVariant} from './ColorTypes'\nimport type {MaybePromise} from './MaybePromise'\nimport type {LiteralUnion} from './LiteralUnion'\nimport type {AttrsValue, ClassValue} from './AnyValuedAttributes'\n\nexport type TableHeadClickedEventObject<Item, E extends UIEvent = Readonly<MouseEvent>> = {\n key: string\n field: TableField<Item>\n event: E\n isFooter: boolean\n}\nexport type TableRowEventObject<T, E extends UIEvent = Readonly<MouseEvent>> = {\n item: T\n index: number\n event: E\n}\nexport type TableRowEvent<T, E extends UIEvent = Readonly<MouseEvent>> = [\n object: TableRowEventObject<T, E>,\n]\n\nexport type TableItem<T = Record<string, unknown>> = T & {\n _rowVariant?: ColorVariant | null\n _cellVariants?: Partial<Record<keyof T, ColorVariant>>\n _showDetails?: boolean\n}\n\nexport const isTableItem = (value: unknown): value is TableItem =>\n typeof value === 'object' && value !== null\n\n/**\n * `undefined` means it's not sorting this column. It is set to undefined rather than removed from the array because\n * we don't want to make updates that remove the comparer function from the value.\n */\nexport type BTableSortByOrder = 'desc' | 'asc' | undefined\n\nexport type BTableSelectMode = 'single' | 'multi' | 'range'\n\n/**\n * Initial sort direction for table fields. Includes 'last' option to maintain the direction of the previously sorted column.\n */\nexport type BTableInitialSortDirection = 'desc' | 'asc' | 'last'\n\nexport type BTableSortByComparerFunction<T> = (a: T, b: T, key: string) => number\n\nexport type BTableFilterFunction<T> = (item: Readonly<T>, filter: string | undefined) => boolean\n\nexport type BTableSortBy = {\n order: BTableSortByOrder\n key: string\n}\n\nexport type BTableProviderContext = {\n sortBy: readonly BTableSortBy[] | undefined\n filter: string | undefined\n currentPage: number\n perPage: number\n signal: AbortSignal\n}\n\nexport type BTableProvider<T> = (\n context: Readonly<BTableProviderContext>\n) => MaybePromise<T[] | undefined>\n\nexport type TableFieldFormatterObject<T> = {value: unknown; key: string; item: T}\n\nexport type TableFieldFormatter<T> = (obj: TableFieldFormatterObject<T>) => string\n\nexport type TableRowType = 'row' | 'row-expansion' | 'row-top' | 'row-bottom' | 'table-busy'\nexport type TableRowThead = 'top' | 'bottom'\n\nexport type TableStrictClassValue = string | readonly unknown[] | Record<string, boolean>\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type TableField<T = any> = {\n class?: ClassValue\n filterByFormatted?: boolean | TableFieldFormatter<T>\n formatter?: TableFieldFormatter<T>\n isRowHeader?: boolean\n /**\n * Unique identifier for the column.\n *\n * Used for:\n * - generating slot names (e.g., `head(<key>)`, `cell(<key>)`)\n * - tracking column state (sorting, visibility, focus, etc.)\n *\n * This value must be stable and unique across all columns.\n * It is **not** used directly to read data from items or as the label.\n */\n key: string\n /**\n * Text displayed in the table header for this column.\n *\n * If omitted, a default label may be generated from the column `key`.\n * This value is purely presentational and does not affect column identity or data access.\n * For rich headers, use the `head(<key>)` slot instead.\n */\n label?: string\n /**\n * How to read the value from each row item.\n *\n * - Can be a string representing a root-level property name (e.g., `'email'` for `const users = [{email: 'abc'}]`).\n * - Can be a function that receives the row item and returns the value (recommended for type safety and nested or computed values).\n * - If omitted, defaults to using the column `key`.\n */\n accessor?: string | ((item: T) => unknown)\n headerTitle?: string\n headerAbbr?: string\n sortable?: boolean\n sortDirection?: string\n sortByFormatted?: boolean | TableFieldFormatter<T>\n sortCompare?: BTableSortByComparerFunction<T>\n stickyColumn?: boolean\n scope?: TableThScope\n initialSortDirection?: BTableInitialSortDirection\n tdClass?:\n | TableStrictClassValue\n | ((value: unknown, key: string, item: T) => TableStrictClassValue)\n thClass?: ClassValue\n thStyle?: StyleValue\n tdAttr?: AttrsValue | ((obj: {value: unknown; key: string; item: T}) => AttrsValue)\n thAttr?:\n | AttrsValue\n | ((obj: {value: unknown; key: string; item: T | null; type: TableRowThead}) => AttrsValue)\n variant?: ColorVariant | null\n}\n\nexport type TableFieldRaw<T = unknown> = T extends object\n ? LiteralUnion<keyof T> | TableField<T>\n : string | TableField\n\nexport const isTableField = (value: unknown): value is TableField =>\n typeof value === 'object' && value !== null && 'key' in value\n\nexport const isTableFieldRaw = (value: unknown): value is TableFieldRaw =>\n typeof value === 'string' || isTableField(value)\n\nexport type NoProviderTypes = 'paging' | 'sorting' | 'filtering'\n\nexport type TableThScope = 'row' | 'col' | 'rowgroup' | 'colgroup'\n\nexport type ItemTrackerReturn<Item> = {\n /**\n * Gets the item from the items prop, using the primary key if provided\n *\n * @param item\n * @returns\n */\n get: (item: Item) => unknown\n /**\n * Gets the item from the internal \"computed items\" representation when using the primary key.\n * This is used to get the object reference of the item from the items when using the primary key, since the items are stored as the primary key value rather than the item itself.\n * If no `primaryKey` is used, it will return undefined\n *\n * @param primaryKey\n */\n getFromPrimaryKey: (primaryKey: unknown) => unknown\n /**\n * A computed array of items resolved from their primary key values\n * against the table’s internal computed dataset.\n *\n * When a `primaryKey` is configured, selection and tracking mechanisms\n * may store only the primary key values. This property provides the\n * corresponding full item objects by resolving those keys against the\n * current internal items representation.\n *\n * If no `primaryKey` is used, this will mirror the selectedItems array.\n *\n * Note:\n * - Only items currently present in the internal computed dataset\n * can be resolved. Keys that do not match an available item\n * will be ignored.\n * - This property is readonly and represents a derived projection,\n * not a source of truth.\n */\n resolvedItems: ComputedRef<readonly unknown[]>\n /**\n * Adds an item to the selected items\n *\n * @param item\n * @returns\n */\n add: (item: Item) => void\n /**\n * Sets the selected items to the provided items\n *\n * @param items\n * @returns\n */\n set: (items: readonly Item[]) => void\n /**\n * Sets the selected items to all items\n *\n * @returns\n */\n setAll: () => void\n /**\n * Deletes an item from the selected items\n *\n * @param item\n * @returns\n */\n remove: (item: Item) => void\n /**\n * Clears all selected items\n *\n * @returns\n */\n clear: () => void\n /**\n * Checks if an item is in the selected items\n *\n * @param item\n * @returns\n */\n has: (item: Item) => boolean\n /**\n * Whether the tracker is activated (has any selected items)\n */\n isActivated: ComputedRef<boolean>\n}\nexport type TableDetailsReturn<Item> = ItemTrackerReturn<Item> & {\n /**\n * Toggles the details for the given item\n *\n * @param item\n * @returns\n */\n toggle: (item: Item) => void\n}\nexport type TableSelectedReturn<Item> = ItemTrackerReturn<Item> & {\n handleRowSelection: (obj: {\n item: Item\n index: number\n shiftClicked?: boolean\n ctrlClicked?: boolean\n metaClicked?: boolean\n }) => void\n}\n\nexport type MaybeGetter<Obj extends object, Key extends PropertyKey, Output = unknown> =\n | Key\n | ((obj: Obj) => Output)\n\n// export type MaybeGetter<Obj, Key extends PropertyKey, Output = unknown> = Obj extends object\n// ? Key | ((obj: Obj) => Output)\n// : Key\n\nexport type TablePrimaryKey<Item> = string | ((item: Item) => string)\n// export type TablePrimaryKey<Item> = Item extends object\n// ? MaybeGetter<Item, keyof Item & string, string>\n// : MaybeGetter<Item, string>\n","<template>\n <tbody :class=\"computedClasses\">\n <slot />\n </tbody>\n</template>\n\n<script setup lang=\"ts\">\nimport type {BTbodySlots} from '../../types'\nimport {useDefaults} from '../../composables/useDefaults'\nimport type {BTbodyProps} from '../../types/ComponentProps'\nimport {computed} from 'vue'\n\nconst _props = withDefaults(defineProps<BTbodyProps>(), {\n variant: null,\n})\nconst props = useDefaults(_props, 'BTbody')\ndefineSlots<BTbodySlots>()\n\nconst computedClasses = computed(() => ({\n [`thead-${props.variant}`]: props.variant !== null,\n}))\n</script>\n","<template>\n <tbody :class=\"computedClasses\">\n <slot />\n </tbody>\n</template>\n\n<script setup lang=\"ts\">\nimport type {BTbodySlots} from '../../types'\nimport {useDefaults} from '../../composables/useDefaults'\nimport type {BTbodyProps} from '../../types/ComponentProps'\nimport {computed} from 'vue'\n\nconst _props = withDefaults(defineProps<BTbodyProps>(), {\n variant: null,\n})\nconst props = useDefaults(_props, 'BTbody')\ndefineSlots<BTbodySlots>()\n\nconst computedClasses = computed(() => ({\n [`thead-${props.variant}`]: props.variant !== null,\n}))\n</script>\n","<template>\n <td\n :class=\"computedClasses\"\n :colspan=\"props.colspan\"\n :rowspan=\"props.rowspan\"\n :data-label=\"props.stackedHeading\"\n >\n <div v-if=\"props.stackedHeading\">\n <slot />\n </div>\n <slot v-else />\n </td>\n</template>\n\n<script setup lang=\"ts\">\nimport type {BTdSlots} from '../../types'\nimport {useDefaults} from '../../composables/useDefaults'\nimport type {BTdProps} from '../../types/ComponentProps'\nimport {computed} from 'vue'\n\nconst _props = withDefaults(defineProps<BTdProps>(), {\n colspan: undefined,\n rowspan: undefined,\n stackedHeading: undefined,\n stickyColumn: false,\n variant: null,\n})\nconst props = useDefaults(_props, 'BTd')\ndefineSlots<BTdSlots>()\n\nconst computedClasses = computed(() => ({\n [`table-${props.variant}`]: props.variant !== null,\n 'b-table-sticky-column': props.stickyColumn,\n 'table-b-table-default': props.stickyColumn && props.variant === null,\n}))\n</script>\n","<template>\n <td\n :class=\"computedClasses\"\n :colspan=\"props.colspan\"\n :rowspan=\"props.rowspan\"\n :data-label=\"props.stackedHeading\"\n >\n <div v-if=\"props.stackedHeading\">\n <slot />\n </div>\n <slot v-else />\n </td>\n</template>\n\n<script setup lang=\"ts\">\nimport type {BTdSlots} from '../../types'\nimport {useDefaults} from '../../composables/useDefaults'\nimport type {BTdProps} from '../../types/ComponentProps'\nimport {computed} from 'vue'\n\nconst _props = withDefaults(defineProps<BTdProps>(), {\n colspan: undefined,\n rowspan: undefined,\n stackedHeading: undefined,\n stickyColumn: false,\n variant: null,\n})\nconst props = useDefaults(_props, 'BTd')\ndefineSlots<BTdSlots>()\n\nconst computedClasses = computed(() => ({\n [`table-${props.variant}`]: props.variant !== null,\n 'b-table-sticky-column': props.stickyColumn,\n 'table-b-table-default': props.stickyColumn && props.variant === null,\n}))\n</script>\n","<template>\n <tfoot :class=\"computedClasses\">\n <slot />\n </tfoot>\n</template>\n\n<script setup lang=\"ts\">\nimport {useDefaults} from '../../composables/useDefaults'\nimport type {BTfootProps} from '../../types/ComponentProps'\nimport {computed} from 'vue'\nimport type {BTfootSlots} from '../../types/ComponentSlots'\n\nconst _props = withDefaults(defineProps<BTfootProps>(), {\n variant: null,\n})\nconst props = useDefaults(_props, 'BTfoot')\ndefineSlots<BTfootSlots>()\n\nconst computedClasses = computed(() => ({\n [`table-${props.variant}`]: props.variant !== null,\n}))\n</script>\n","<template>\n <tfoot :class=\"computedClasses\">\n <slot />\n </tfoot>\n</template>\n\n<script setup lang=\"ts\">\nimport {useDefaults} from '../../composables/useDefaults'\nimport type {BTfootProps} from '../../types/ComponentProps'\nimport {computed} from 'vue'\nimport type {BTfootSlots} from '../../types/ComponentSlots'\n\nconst _props = withDefaults(defineProps<BTfootProps>(), {\n variant: null,\n})\nconst props = useDefaults(_props, 'BTfoot')\ndefineSlots<BTfootSlots>()\n\nconst computedClasses = computed(() => ({\n [`table-${props.variant}`]: props.variant !== null,\n}))\n</script>\n","<template>\n <th\n :scope=\"localScope\"\n :class=\"computedClasses\"\n :colspan=\"props.colspan\"\n :rowspan=\"props.rowspan\"\n :data-label=\"props.stackedHeading\"\n >\n <div v-if=\"props.stackedHeading !== undefined\">\n <slot />\n </div>\n <slot v-else />\n </th>\n</template>\n\n<script setup lang=\"ts\">\nimport {useDefaults} from '../../composables/useDefaults'\nimport type {BThProps} from '../../types/ComponentProps'\nimport {computed} from 'vue'\nimport type {BThSlots} from '../../types/ComponentSlots'\n\nconst _props = withDefaults(defineProps<BThProps>(), {\n colspan: undefined,\n rowspan: undefined,\n stackedHeading: undefined,\n stickyColumn: false,\n variant: null,\n scope: undefined,\n})\nconst props = useDefaults(_props, 'BTh')\ndefineSlots<BThSlots>()\n\nconst computedClasses = computed(() => ({\n [`table-${props.variant}`]: props.variant !== null,\n 'b-table-sticky-column': props.stickyColumn,\n 'table-b-table-default': props.stickyColumn && props.variant === null,\n}))\n\nconst localScope = computed(() =>\n props.scope ? props.scope : props.colspan ? 'colgroup' : props.rowspan ? 'rowgroup' : 'col'\n)\n</script>\n","<template>\n <th\n :scope=\"localScope\"\n :class=\"computedClasses\"\n :colspan=\"props.colspan\"\n :rowspan=\"props.rowspan\"\n :data-label=\"props.stackedHeading\"\n >\n <div v-if=\"props.stackedHeading !== undefined\">\n <slot />\n </div>\n <slot v-else />\n </th>\n</template>\n\n<script setup lang=\"ts\">\nimport {useDefaults} from '../../composables/useDefaults'\nimport type {BThProps} from '../../types/ComponentProps'\nimport {computed} from 'vue'\nimport type {BThSlots} from '../../types/ComponentSlots'\n\nconst _props = withDefaults(defineProps<BThProps>(), {\n colspan: undefined,\n rowspan: undefined,\n stackedHeading: undefined,\n stickyColumn: false,\n variant: null,\n scope: undefined,\n})\nconst props = useDefaults(_props, 'BTh')\ndefineSlots<BThSlots>()\n\nconst computedClasses = computed(() => ({\n [`table-${props.variant}`]: props.variant !== null,\n 'b-table-sticky-column': props.stickyColumn,\n 'table-b-table-default': props.stickyColumn && props.variant === null,\n}))\n\nconst localScope = computed(() =>\n props.scope ? props.scope : props.colspan ? 'colgroup' : props.rowspan ? 'rowgroup' : 'col'\n)\n</script>\n","<template>\n <thead :class=\"computedClasses\">\n <slot />\n </thead>\n</template>\n\n<script setup lang=\"ts\">\nimport type {BTheadSlots} from '../../types'\nimport {useDefaults} from '../../composables/useDefaults'\nimport type {BTheadProps} from '../../types/ComponentProps'\nimport {computed} from 'vue'\n\nconst _props = withDefaults(defineProps<BTheadProps>(), {\n variant: null,\n})\nconst props = useDefaults(_props, 'BThead')\ndefineSlots<BTheadSlots>()\n\nconst computedClasses = computed(() => ({\n [`table-${props.variant}`]: props.variant !== null,\n}))\n</script>\n","<template>\n <thead :class=\"computedClasses\">\n <slot />\n </thead>\n</template>\n\n<script setup lang=\"ts\">\nimport type {BTheadSlots} from '../../types'\nimport {useDefaults} from '../../composables/useDefaults'\nimport type {BTheadProps} from '../../types/ComponentProps'\nimport {computed} from 'vue'\n\nconst _props = withDefaults(defineProps<BTheadProps>(), {\n variant: null,\n})\nconst props = useDefaults(_props, 'BThead')\ndefineSlots<BTheadSlots>()\n\nconst computedClasses = computed(() => ({\n [`table-${props.variant}`]: props.variant !== null,\n}))\n</script>\n","<template>\n <tr :class=\"computedClasses\">\n <slot />\n </tr>\n</template>\n\n<script setup lang=\"ts\">\nimport {useDefaults} from '../../composables/useDefaults'\nimport type {BTrProps} from '../../types/ComponentProps'\nimport {computed} from 'vue'\nimport type {BTrSlots} from '../../types/ComponentSlots'\n\nconst _props = withDefaults(defineProps<BTrProps>(), {\n variant: null,\n})\nconst props = useDefaults(_props, 'BTr')\ndefineSlots<BTrSlots>()\n\nconst computedClasses = computed(() => ({\n [`table-${props.variant}`]: props.variant !== null,\n}))\n</script>\n","<template>\n <tr :class=\"computedClasses\">\n <slot />\n </tr>\n</template>\n\n<script setup lang=\"ts\">\nimport {useDefaults} from '../../composables/useDefaults'\nimport type {BTrProps} from '../../types/ComponentProps'\nimport {computed} from 'vue'\nimport type {BTrSlots} from '../../types/ComponentSlots'\n\nconst _props = withDefaults(defineProps<BTrProps>(), {\n variant: null,\n})\nconst props = useDefaults(_props, 'BTr')\ndefineSlots<BTrSlots>()\n\nconst computedClasses = computed(() => ({\n [`table-${props.variant}`]: props.variant !== null,\n}))\n</script>\n","import {titleCase} from './stringUtils'\nimport {type TableFieldRaw} from '../types/TableTypes'\nimport type {Breakpoint, BTableLiteProps, BTableSimpleProps, TableField} from '../types'\n\nexport const getTableFieldHeadLabel = (field: Readonly<TableFieldRaw<unknown>>) =>\n typeof field === 'string'\n ? titleCase(field)\n : field.label !== undefined\n ? field.label\n : titleCase(field.key)\n\nexport const getWithGetter = <Obj extends object>(\n item: Obj,\n key: string | ((item: Obj) => unknown)\n): unknown => {\n if (typeof key === 'function') {\n return key(item)\n }\n return item[key as unknown as keyof Obj]\n}\n\nexport const getByFieldKey = (item: unknown, field: TableField) =>\n typeof item === 'object' && item !== null\n ? getWithGetter(item, field.accessor ?? field.key)\n : item\n\nexport const formatItem = (item: unknown, field: TableField) => {\n const val = getByFieldKey(item, field)\n\n return typeof field.formatter === 'function'\n ? field.formatter({value: val, key: field.key, item})\n : val\n}\n\nexport const bTableSimpleProps = Object.freeze(\n Object.keys({\n bordered: 0,\n borderless: 0,\n borderVariant: 0,\n captionTop: 0,\n dark: 0,\n fixed: 0,\n hover: 0,\n id: 0,\n noBorderCollapse: 0,\n outlined: 0,\n responsive: 0,\n small: 0,\n stacked: 0,\n stickyHeader: 0,\n striped: 0,\n stripedColumns: 0,\n variant: 0,\n tableAttrs: 0,\n tableClass: 0,\n } satisfies Record<keyof BTableSimpleProps, 0>)\n) as readonly (keyof BTableSimpleProps)[]\n\nexport const bTableLiteProps = Object.freeze(\n Object.keys({\n align: 0,\n caption: 0,\n detailsTdClass: 0,\n fieldColumnClass: 0,\n fields: 0,\n footClone: 0,\n footRowVariant: 0,\n footVariant: 0,\n headRowVariant: 0,\n headVariant: 0,\n items: 0,\n labelStacked: 0,\n modelValue: 0,\n primaryKey: 0,\n tbodyClass: 0,\n tbodyTrAttrs: 0,\n tbodyTrClass: 0,\n tfootClass: 0,\n expandedItems: 0,\n tfootTrClass: 0,\n theadClass: 0,\n theadTrClass: 0,\n } satisfies Record<keyof Omit<BTableLiteProps<unknown>, keyof BTableSimpleProps>, 0>)\n) as readonly (keyof Omit<BTableLiteProps<unknown>, keyof BTableSimpleProps>)[]\n\nexport type StackedProps = {\n stacked: boolean | Breakpoint | undefined\n labelStacked: boolean | undefined\n}\n\nexport const getDataLabelAttr = (props: StackedProps, label: string) =>\n props.stacked && props.labelStacked !== true ? {'data-label': label} : undefined\n","import {getSafeDocument} from './dom'\n\nconst TABLE_TAG_NAMES = ['TD', 'TH', 'TR']\n\n// Filter CSS selector for click/dblclick/etc. events\n// If any of these selectors match the clicked element, we ignore the event\nconst eventFilter = [\n 'a',\n 'a *', // Include content inside links\n 'button',\n 'button *', // Include content inside buttons\n 'input:not(.disabled):not([disabled])',\n 'select:not(.disabled):not([disabled])',\n 'textarea:not(.disabled):not([disabled])',\n '[role=\"link\"]',\n '[role=\"link\"] *',\n '[role=\"button\"]',\n '[role=\"button\"] *',\n '[tabindex]:not(.disabled):not([disabled])',\n].join(',')\n\n// Returns `true` if we should ignore the click/double-click/keypress event\n// Avoids having the user need to use `@click.stop` on the form control\nexport const filterEvent = (event: Readonly<Event>) => {\n // Exit early when we don't have a target element\n if (!event || !event.target) {\n return false\n }\n const el = event.target as HTMLElement\n // Exit early when element is disabled or a table element\n if (('disabled' in el && el.disabled) || TABLE_TAG_NAMES.indexOf(el.tagName) !== -1) {\n return false\n }\n // Ignore the click when it was inside a dropdown menu\n if (el.closest('.dropdown-menu')) return true\n\n const label = el.tagName === 'LABEL' ? el : el.closest('label')\n // If the label's form control is not disabled then we don't propagate event\n // Modern browsers have `label.control` that references the associated input, but IE 11\n // does not have this property on the label element, so we resort to DOM lookups\n if (label) {\n const doc = getSafeDocument()\n const labelFor = label.getAttribute('for')\n const input = labelFor\n ? doc?.getElementById(labelFor)\n : label.querySelector('input, select, textarea')\n if (input && !(input as HTMLInputElement).disabled) {\n return true\n }\n }\n // Otherwise check if the event target matches one of the selectors in the\n // event filter (i.e. anchors, non disabled inputs, etc.)\n // Return `true` if we should ignore the event\n return el.matches(eventFilter)\n}\n","import {\n computed,\n inject,\n type MaybeRef,\n type MaybeRefOrGetter,\n readonly,\n type Ref,\n toRef,\n toValue,\n watch,\n} from 'vue'\nimport {\n isTableField,\n isTableItem,\n type ItemTrackerReturn,\n type TableDetailsReturn,\n type TableField,\n type TableFieldRaw,\n type TableHeadClickedEventObject,\n type TablePrimaryKey,\n type TableRowEventObject,\n} from '../types/TableTypes'\nimport {getDataLabelAttr, getWithGetter, type StackedProps} from '../utils/tableUtils'\nimport {startCase} from '../utils/stringUtils'\nimport {CODE_DOWN, CODE_END, CODE_HOME, CODE_UP} from '../utils/constants'\nimport {tableKeyboardNavigationKey} from '../utils/keys'\nimport {filterEvent} from '../utils/filterEvent'\nimport type {AttrsValue} from '../types/AnyValuedAttributes'\nimport {getSafeDocument} from '../utils/dom'\n\nexport const useTableFieldsMapper = <Item>({\n fields,\n items,\n stackedProps,\n}: {\n fields: MaybeRefOrGetter<readonly TableFieldRaw<Item>[]>\n items: MaybeRefOrGetter<readonly Item[]>\n stackedProps: {\n stacked: MaybeRefOrGetter<StackedProps['stacked']>\n labelStacked: MaybeRefOrGetter<StackedProps['labelStacked']>\n }\n}) => {\n const computedFields = computed<{\n items: TableField[]\n opts?: {noHeader: boolean}\n }>(() => {\n const fieldsValue = toValue(fields)\n const itemsValue = toValue(items)\n const stacked = {\n stacked: toValue(stackedProps.stacked),\n labelStacked: toValue(stackedProps.labelStacked),\n }\n\n if (!fieldsValue.length && itemsValue.length) {\n const [firstItem] = itemsValue\n if (firstItem && (isTableItem(firstItem) || Array.isArray(firstItem))) {\n return {\n items: Object.keys(firstItem).map((k) => {\n const label = startCase(k)\n return {\n key: k,\n label,\n tdAttr: getDataLabelAttr(stacked, label),\n }\n }),\n }\n }\n // The items are primitives, so we just return a single empty field\n // No header will be shown, as we don't know what to show\n return {items: [{key: ''}], opts: {noHeader: true}}\n }\n\n return {\n items: fieldsValue.map((f) => {\n if (isTableField(f)) {\n const label = f.label ?? startCase(f.key as string)\n const dataLabelAttr = getDataLabelAttr(stacked, label)\n const tdAttr =\n typeof f.tdAttr === 'function'\n ? (obj: unknown) => ({\n ...dataLabelAttr,\n ...(f.tdAttr as (obj: unknown) => AttrsValue)(obj),\n })\n : dataLabelAttr || f.tdAttr\n ? {...dataLabelAttr, ...f.tdAttr}\n : undefined\n return {\n ...(f as TableField),\n tdAttr,\n }\n }\n const label = startCase(f as string)\n return {\n key: f as string,\n label,\n tdAttr: getDataLabelAttr(stacked, label),\n }\n }),\n }\n })\n const total = computed(() => computedFields.value.items.length)\n const showHeaders = computed(\n () =>\n // We only hide the header if all fields have _noHeader set to true. Which would be our doing\n // This usually happens under a circumstance of displaying an array of primitives\n // Under any other circumstance, I'm not sure how this would apply\n !(total.value > 0 && computedFields.value.opts?.noHeader === true)\n )\n\n return {\n total,\n showHeaders,\n fields: computed(() => computedFields.value.items),\n }\n}\n\nexport const useItemTracker = <Item>({\n allItems,\n selectedItems,\n primaryKey,\n}: {\n allItems: MaybeRefOrGetter<readonly Item[]>\n selectedItems: Ref<readonly unknown[]>\n primaryKey?: MaybeRef<TablePrimaryKey<Item> | undefined>\n}): ItemTrackerReturn<Item> => {\n const isActivated = computed(() => selectedItems.value.length > 0)\n const pKey = readonly(toRef(primaryKey))\n\n const get = (item: Item) => {\n if (typeof item === 'object' && item !== null && pKey.value) {\n return getWithGetter(item, pKey.value)\n }\n return item\n }\n\n const add = (item: Item) => {\n const value = get(item)\n\n selectedItems.value = [...selectedItems.value, value]\n }\n const set = (items: readonly Item[]) => {\n selectedItems.value = pKey.value ? items.map(get) : items\n }\n const setAll = () => set([...toValue(allItems)])\n const remove = (item: Item) => {\n const value = get(item)\n\n selectedItems.value = selectedItems.value.filter((i) => i !== value)\n }\n const clear = () => {\n selectedItems.value = []\n }\n\n const has = (item: Item) => {\n const value = get(item)\n\n return selectedItems.value.includes(value)\n }\n\n watch(pKey, () => {\n // Clear selected items if primary key changes\n clear()\n })\n\n const objectsMap = computed(() => {\n if (!pKey.value) return new Map()\n\n return new Map<unknown, Item>(toValue(allItems).map((item) => [get(item), item]))\n })\n const getFromPrimaryKey = (value: unknown): Item | undefined => {\n if (!pKey.value) return undefined\n\n return objectsMap.value.get(value)\n }\n const resolvedItems = computed(() => {\n if (!pKey.value) return selectedItems.value\n\n const arr: unknown[] = []\n selectedItems.value.forEach((item) => {\n const resolved = getFromPrimaryKey(item)\n if (resolved !== undefined) {\n arr.push(resolved)\n }\n })\n return arr\n })\n\n return {\n getFromPrimaryKey,\n resolvedItems,\n isActivated,\n get,\n add,\n remove,\n clear,\n set,\n has,\n setAll,\n }\n}\n\nexport const useItemExpansion = <Item>({\n allItems,\n primaryKey,\n expandedItems,\n}: {\n allItems: MaybeRefOrGetter<readonly Item[]>\n primaryKey: MaybeRef<TablePrimaryKey<Item> | undefined>\n expandedItems: Ref<readonly unknown[]>\n}): TableDetailsReturn<Item> => {\n const utils = useItemTracker({\n primaryKey,\n allItems,\n selectedItems: expandedItems,\n })\n\n const toggle = (item: Item) => {\n if (utils.has(item)) {\n utils.remove(item)\n } else {\n utils.add(item)\n }\n }\n\n return {\n ...utils,\n toggle,\n }\n}\n\nexport const useTableKeyboardNavigation = <Item>(\n {\n items,\n id,\n }: {\n items: MaybeRefOrGetter<readonly Item[]>\n id: MaybeRefOrGetter<string>\n },\n events: {\n onHeadClicked: (\n obj: TableHeadClickedEventObject<Item, Readonly<KeyboardEvent> | Readonly<MouseEvent>>\n ) => void\n onRowClicked: (\n obj: TableRowEventObject<Item, Readonly<KeyboardEvent> | Readonly<MouseEvent>>\n ) => void\n onRowMiddleClicked: (obj: TableRowEventObject<Item>) => void\n }\n) => {\n // Inject keyboard navigation state from parent BTable\n const keyboardNavigation = inject(tableKeyboardNavigationKey, null)\n // Keyboard navigation support\n const shouldHeaderBeFocusable = (field: TableField) =>\n !!(keyboardNavigation?.headerNavigation.value && field.sortable === true)\n\n const shouldRowBeFocusable = computed(\n () => !!(keyboardNavigation?.rowNavigation.value && toValue(items).length > 0)\n )\n\n const headerClicked = (\n field: TableField,\n event: Readonly<MouseEvent> | Readonly<KeyboardEvent>,\n isFooter = false\n ) => {\n events.onHeadClicked({key: field.key as string, field, event, isFooter})\n }\n\n const handleHeaderKeydown = (field: TableField, event: KeyboardEvent, isFooter = false) => {\n const {target, code} = event\n\n if (\n target &&\n (target as Element).tagName !== 'TH' &&\n getSafeDocument()?.activeElement === target\n )\n return\n\n if (code === 'Enter' || code === 'NumpadEnter' || code === 'Space') {\n event.preventDefault()\n headerClicked(field, event, isFooter)\n }\n }\n\n const handleRowKeydown = (item: Item, itemIndex: number, event: KeyboardEvent) => {\n const {target, code, shiftKey} = event\n\n if (\n target &&\n (target as Element).tagName !== 'TR' &&\n getSafeDocument()?.activeElement === target\n )\n return\n\n if (code === 'Enter' || code === 'NumpadEnter' || code === 'Space') {\n event.preventDefault()\n events.onRowClicked({item, index: itemIndex, event})\n return\n }\n\n if (code === CODE_DOWN || code === CODE_UP || code === CODE_HOME || code === CODE_END) {\n event.preventDefault()\n handleRowNavigation(code, shiftKey, itemIndex)\n }\n }\n\n const handleRowNavigation = (code: string, shiftKey: boolean, currentIndex: number) => {\n const doc = getSafeDocument()\n const rows =\n doc === null\n ? []\n : (Array.from(\n doc.querySelectorAll(`#${toValue(id)} tbody tr[tabindex]`)\n ) as HTMLTableRowElement[])\n\n if (rows.length === 0) return\n\n let targetIndex = currentIndex\n\n if (code === CODE_DOWN) {\n if (shiftKey) {\n targetIndex = rows.length - 1 // Go to last row\n } else {\n targetIndex = Math.min(currentIndex + 1, rows.length - 1) // Go to next row\n }\n } else if (code === CODE_UP) {\n if (shiftKey) {\n targetIndex = 0 // Go to first row\n } else {\n targetIndex = Math.max(currentIndex - 1, 0) // Go to previous row\n }\n } else if (code === CODE_END) {\n targetIndex = rows.length - 1 // Go to last row\n } else if (code === CODE_HOME) {\n targetIndex = 0 // Go to first row\n }\n\n if (targetIndex !== currentIndex && rows[targetIndex]) {\n rows[targetIndex]?.focus()\n }\n }\n\n const handleMiddleClick = (item: Item, itemIndex: number, event: MouseEvent) => {\n if (event.button === 1 && !filterEvent(event)) {\n events.onRowMiddleClicked({item, index: itemIndex, event})\n }\n }\n\n return {\n shouldHeaderBeFocusable,\n headerClicked,\n handleHeaderKeydown,\n shouldRowBeFocusable,\n handleRowKeydown,\n handleMiddleClick,\n }\n}\n","<template>\n <BTableSimple v-bind=\"computedSimpleProps\">\n <colgroup v-if=\"slots['table-colgroup']\">\n <slot name=\"table-colgroup\" :fields=\"computedFields\" />\n </colgroup>\n <BThead v-show=\"showComputedHeaders\" :variant=\"props.headVariant\" :class=\"props.theadClass\">\n <slot name=\"thead-top\" :columns=\"computedFieldsTotal\" :fields=\"computedFields\" />\n <BTr :variant=\"props.headRowVariant\" :class=\"props.theadTrClass\">\n <BTh\n v-for=\"field in computedFields\"\n :key=\"field.key\"\n :scope=\"field.scope\"\n :class=\"getFieldColumnClasses(field)\"\n :title=\"field.headerTitle\"\n :variant=\"field.variant\"\n :abbr=\"field.headerAbbr\"\n :style=\"field.thStyle\"\n :tabindex=\"keyboardController.shouldHeaderBeFocusable(field) ? '0' : undefined\"\n v-bind=\"callThAttr(null, field, 'top')\"\n @click=\"keyboardController.headerClicked(field, $event)\"\n @keydown=\"keyboardController.handleHeaderKeydown(field, $event)\"\n >\n <!-- eslint-disable prettier/prettier -->\n <slot\n :name=\"slots[`head(${field.key})`] ? (`head(${field.key})` as 'head()') : 'head()'\"\n :label=\"field.label\"\n :column=\"field.key\"\n :field\n :is-foot=\"false\"\n >\n <!-- eslint-enable prettier/prettier -->\n {{ getTableFieldHeadLabel(field) }}\n </slot>\n </BTh>\n </BTr>\n <BTr v-if=\"slots['thead-sub']\">\n <BTd\n v-for=\"field in computedFields\"\n :key=\"field.key\"\n scope=\"col\"\n :variant=\"field.variant\"\n :class=\"[field.class, field.thClass]\"\n >\n <slot name=\"thead-sub\" :items=\"props.items\" :fields=\"computedFields\" :field>\n {{ field.label }}\n </slot>\n </BTd>\n </BTr>\n </BThead>\n <BTbody :class=\"props.tbodyClass\">\n <slot\n name=\"custom-body\"\n :fields=\"computedFields\"\n :items=\"props.items\"\n :columns=\"computedFieldsTotal\"\n >\n <BTr\n v-if=\"!props.stacked && slots['top-row']\"\n :class=\"getRowClasses(null, 'row-top')\"\n v-bind=\"callTbodyTrAttrs(null, 'row-top')\"\n >\n <slot name=\"top-row\" :columns=\"computedFieldsTotal\" :fields=\"computedFields\" />\n </BTr>\n\n <template\n v-for=\"(item, itemIndex) in props.items\"\n :key=\"\n props.primaryKey && isTableItem(item) && getWithGetter(item, props.primaryKey)\n ? getWithGetter(item, props.primaryKey)\n : itemIndex\n \"\n >\n <BTr\n :id=\"\n props.primaryKey && isTableItem(item) && getWithGetter(item, props.primaryKey)\n ? generateTableRowId(getWithGetter(item, props.primaryKey) as string)\n : undefined\n \"\n :class=\"getRowClasses(item, 'row')\"\n :variant=\"isTableItem(item) ? item._rowVariant : undefined\"\n :tabindex=\"keyboardController.shouldRowBeFocusable.value ? '0' : undefined\"\n v-bind=\"callTbodyTrAttrs(item, 'row')\"\n @click=\"\n !filterEvent($event) && emit('row-clicked', {item, index: itemIndex, event: $event})\n \"\n @dblclick=\"\n !filterEvent($event) &&\n emit('row-dblclicked', {item, index: itemIndex, event: $event})\n \"\n @contextmenu=\"\n !filterEvent($event) &&\n emit('row-contextmenu', {item, index: itemIndex, event: $event})\n \"\n @mouseenter=\"\n !filterEvent($event) && emit('row-hovered', {item, index: itemIndex, event: $event})\n \"\n @mouseleave=\"\n !filterEvent($event) && emit('row-unhovered', {item, index: itemIndex, event: $event})\n \"\n @mousedown=\"keyboardController.handleMiddleClick(item, itemIndex, $event)\"\n @keydown=\"keyboardController.handleRowKeydown(item, itemIndex, $event)\"\n >\n <component\n :is=\"getCellComponent(field)\"\n v-for=\"field in computedFields\"\n :key=\"field.key\"\n :variant=\"\n (isTableItem(item) ? item._cellVariants?.[field.key] : false) ? null : field.variant\n \"\n :class=\"getFieldRowClasses(field, item)\"\n v-bind=\"itemAttributes(item, field)\"\n >\n <label v-if=\"props.stacked && props.labelStacked\" class=\"b-table-stacked-label\">\n {{ getTableFieldHeadLabel(field) }}\n </label>\n <slot\n :name=\"slots[`cell(${field.key})`] ? (`cell(${field.key})` as 'cell()') : 'cell()'\"\n :value=\"formatItem(item, field)\"\n :unformatted=\"getByFieldKey(item, field)\"\n :index=\"itemIndex\"\n :item=\"item\"\n :field=\"field\"\n :items=\"props.items\"\n :toggle-expansion=\"() => expandedItemsController.toggle(item)\"\n :expansion-showing=\"expandedItemsController.has(item)\"\n >\n <template v-if=\"!slots[`cell(${field.key})`] && !slots['cell()']\">\n {{ formatItem(item, field) }}\n </template>\n </slot>\n </component>\n </BTr>\n\n <template v-if=\"expandedItemsController.has(item) && slots['row-expansion']\">\n <BTr aria-hidden=\"true\" role=\"presentation\" class=\"d-none\" />\n <BTr\n :class=\"getRowClasses(item, 'row-expansion')\"\n :variant=\"isTableItem(item) ? item._rowVariant : undefined\"\n v-bind=\"callTbodyTrAttrs(item, 'row-expansion')\"\n >\n <BTd :colspan=\"computedFieldsTotal\" :class=\"detailsTdClass\">\n <slot\n name=\"row-expansion\"\n :item=\"item\"\n :toggle-expansion=\"() => expandedItemsController.toggle(item)\"\n :fields=\"computedFields\"\n :index=\"itemIndex\"\n />\n </BTd>\n </BTr>\n </template>\n </template>\n <!-- This class is for specific targetting of this slot element -->\n <BTr\n v-if=\"!props.stacked && slots['bottom-row']\"\n class=\"bottom-row\"\n :class=\"getRowClasses(null, 'row-bottom')\"\n v-bind=\"callTbodyTrAttrs(null, 'row-bottom')\"\n >\n <slot name=\"bottom-row\" :columns=\"computedFieldsTotal\" :fields=\"computedFields\" />\n </BTr>\n </slot>\n </BTbody>\n <BTfoot v-if=\"props.footClone\" v-bind=\"footerProps\">\n <BTr\n :variant=\"props.footRowVariant ?? props.headRowVariant\"\n :class=\"props.tfootTrClass ?? props.theadTrClass\"\n >\n <BTh\n v-for=\"field in computedFields\"\n :key=\"field.key\"\n scope=\"col\"\n :class=\"getFieldColumnClasses(field)\"\n :title=\"field.headerTitle\"\n :abbr=\"field.headerAbbr\"\n :style=\"field.thStyle\"\n :variant=\"field.variant\"\n :tabindex=\"keyboardController.shouldHeaderBeFocusable(field) ? '0' : undefined\"\n v-bind=\"callThAttr(null, field, 'bottom')\"\n @click=\"keyboardController.headerClicked(field, $event, true)\"\n @keydown=\"keyboardController.handleHeaderKeydown(field, $event, true)\"\n >\n <div class=\"d-inline-flex flex-nowrap align-items-center gap-1\">\n <div>\n <!-- eslint-disable prettier/prettier -->\n <!-- @vue-expect-error - typescript is generating 2322 errors for all properties after name, which is wrong -->\n <slot\n :name=\"calculatedFooterSlot(field.key)\"\n :label=\"field.label\"\n :column=\"field.key\"\n :field=\"field\"\n :is-foot=\"true\"\n >\n <!-- eslint-enable prettier/prettier -->\n {{ getTableFieldHeadLabel(field) }}\n </slot>\n </div>\n </div>\n </BTh>\n </BTr>\n </BTfoot>\n <BTfoot v-else-if=\"slots['custom-foot']\" v-bind=\"footerProps\">\n <slot\n name=\"custom-foot\"\n :fields=\"computedFields\"\n :items=\"props.items\"\n :columns=\"computedFieldsTotal\"\n />\n </BTfoot>\n <caption v-if=\"slots['table-caption'] || props.caption\">\n <slot name=\"table-caption\">\n {{ props.caption }}\n </slot>\n </caption>\n </BTableSimple>\n</template>\n\n<script setup lang=\"ts\" generic=\"Item\">\nimport {computed, readonly, toRef} from 'vue'\nimport type {BTableLiteProps} from '../../types/ComponentProps'\nimport {\n isTableItem,\n type TableField,\n type TableItem,\n type TableRowThead,\n type TableRowType,\n} from '../../types/TableTypes'\nimport BTableSimple from './BTableSimple.vue'\nimport BTbody from './BTbody.vue'\nimport BTd from './BTd.vue'\nimport BTfoot from './BTfoot.vue'\nimport BTh from './BTh.vue'\nimport BThead from './BThead.vue'\nimport BTr from './BTr.vue'\nimport {useDefaults} from '../../composables/useDefaults'\nimport {pick} from '../../utils/object'\nimport {\n bTableSimpleProps,\n formatItem,\n getByFieldKey,\n getTableFieldHeadLabel,\n getWithGetter,\n} from '../../utils/tableUtils'\nimport {filterEvent} from '../../utils/filterEvent'\nimport {useId} from '../../composables/useId'\nimport type {BTableLiteEmits} from '../../types/ComponentEmits'\nimport type {BTableLiteSlots} from '../../types'\nimport {\n useItemExpansion,\n useTableFieldsMapper,\n useTableKeyboardNavigation,\n} from '../../composables/useTableLiteHelpers'\n\nconst _props = withDefaults(defineProps<Omit<BTableLiteProps<Item>, 'expandedItems'>>(), {\n caption: undefined,\n align: undefined,\n fields: () => [],\n footClone: false,\n items: () => [],\n labelStacked: false,\n fieldColumnClass: undefined,\n tbodyTrClass: undefined,\n detailsTdClass: undefined,\n headVariant: undefined,\n headRowVariant: undefined,\n footRowVariant: undefined,\n footVariant: undefined,\n modelValue: undefined,\n primaryKey: undefined,\n tbodyClass: undefined,\n tbodyTrAttrs: undefined,\n tfootClass: undefined,\n tfootTrClass: undefined,\n theadClass: undefined,\n theadTrClass: undefined,\n // BTableSimpleProps props\n borderVariant: undefined,\n tableClass: undefined,\n variant: undefined,\n bordered: undefined,\n borderless: undefined,\n captionTop: undefined,\n dark: undefined,\n hover: undefined,\n id: undefined,\n noBorderCollapse: undefined,\n outlined: undefined,\n fixed: undefined,\n responsive: undefined,\n stacked: undefined,\n striped: undefined,\n stripedColumns: undefined,\n small: undefined,\n stickyHeader: undefined,\n // End BTableSimpleProps props\n})\nconst props = useDefaults(_props, 'BTableLite')\nconst emit = defineEmits<BTableLiteEmits<Item>>()\nconst slots = defineSlots<BTableLiteSlots<Item>>()\n\nconst expandedItems = defineModel<Exclude<BTableLiteProps<Item>['expandedItems'], undefined>>(\n 'expandedItems',\n {\n default: () => [],\n }\n)\n\nconst computedId = useId(() => props.id)\nconst expandedItemsController = useItemExpansion({\n allItems: () => props.items,\n primaryKey: toRef(() => props.primaryKey),\n expandedItems,\n})\nconst keyboardController = useTableKeyboardNavigation(\n {\n items: () => props.items,\n id: computedId,\n },\n {\n onHeadClicked: (obj) => {\n emit('head-clicked', obj)\n },\n onRowClicked: (obj) => {\n emit('row-clicked', obj)\n },\n onRowMiddleClicked: (obj) => {\n emit('row-middle-clicked', obj)\n },\n }\n)\nconst {\n fields: computedFields,\n total: computedFieldsTotal,\n showHeaders: showComputedHeaders,\n} = useTableFieldsMapper({\n fields: () => props.fields,\n items: () => props.items,\n stackedProps: {\n labelStacked: () => props.labelStacked,\n stacked: () => props.stacked,\n },\n})\n\nconst calculatedFooterSlot = (key: string): keyof typeof slots =>\n slots[`foot(${key})`]\n ? `foot(${key})`\n : slots['foot()']\n ? 'foot()'\n : slots[`head(${key})`]\n ? `head(${key})`\n : 'head()'\n\nconst computedTableClasses = computed(() => [\n props.tableClass,\n {\n [`align-${props.align}`]: props.align !== undefined,\n },\n])\nconst getFieldColumnClasses = (field: TableField) => [\n field.class,\n field.thClass,\n {\n 'b-table-sticky-column': field.stickyColumn,\n },\n props.fieldColumnClass\n ? typeof props.fieldColumnClass === 'function'\n ? props.fieldColumnClass(field)\n : props.fieldColumnClass\n : null,\n]\nconst getFieldRowClasses = (field: Readonly<TableField>, tr: Item) => [\n field.class,\n typeof field.tdClass === 'function'\n ? field.tdClass(getByFieldKey(tr, field), field.key, tr)\n : field.tdClass,\n (isTableItem(tr) ? tr._cellVariants?.[field.key] : false)\n ? `table-${(tr as TableItem)._cellVariants?.[field.key]}`\n : null,\n {\n 'b-table-sticky-column': field.stickyColumn,\n },\n]\nconst getRowClasses = (item: Item | null, type: TableRowType) =>\n props.tbodyTrClass\n ? typeof props.tbodyTrClass === 'function'\n ? props.tbodyTrClass(item, type)\n : props.tbodyTrClass\n : null\n\nconst itemAttributes = (item: Item, field: TableField) =>\n field.tdAttr && typeof field.tdAttr === 'function'\n ? field.tdAttr({value: getByFieldKey(item, field), key: field.key, item})\n : field.tdAttr\nconst callThAttr = (item: Item | null, field: TableField<Item>, type: TableRowThead) =>\n field.thAttr && typeof field.thAttr === 'function'\n ? field.thAttr({value: getByFieldKey(item, field), key: field.key, item, type})\n : field.thAttr\nconst callTbodyTrAttrs = (item: Item | null, type: TableRowType) =>\n props.tbodyTrAttrs\n ? typeof props.tbodyTrAttrs === 'function'\n ? props.tbodyTrAttrs(item, type)\n : props.tbodyTrAttrs\n : null\n\nconst generateTableRowId = (primaryKeyValue: string) =>\n `${computedId.value}__row_${primaryKeyValue}`\n\nconst getCellComponent = (field: Readonly<TableField>) => {\n if (field?.isRowHeader) {\n return BTh\n }\n return BTd\n}\n\nconst footerProps = computed(() => ({\n variant: props.footVariant ?? props.headVariant,\n class: props.tfootClass ?? props.theadClass,\n}))\n\nconst computedSimpleProps = computed(() => ({\n ...pick(props, bTableSimpleProps),\n tableClass: computedTableClasses.value,\n id: computedId.value,\n}))\n\ndefineExpose({\n expansion: {\n ...expandedItemsController,\n expandedItems: readonly(expandedItems),\n },\n})\n</script>\n","<template>\n <BTableSimple v-bind=\"computedSimpleProps\">\n <colgroup v-if=\"slots['table-colgroup']\">\n <slot name=\"table-colgroup\" :fields=\"computedFields\" />\n </colgroup>\n <BThead v-show=\"showComputedHeaders\" :variant=\"props.headVariant\" :class=\"props.theadClass\">\n <slot name=\"thead-top\" :columns=\"computedFieldsTotal\" :fields=\"computedFields\" />\n <BTr :variant=\"props.headRowVariant\" :class=\"props.theadTrClass\">\n <BTh\n v-for=\"field in computedFields\"\n :key=\"field.key\"\n :scope=\"field.scope\"\n :class=\"getFieldColumnClasses(field)\"\n :title=\"field.headerTitle\"\n :variant=\"field.variant\"\n :abbr=\"field.headerAbbr\"\n :style=\"field.thStyle\"\n :tabindex=\"keyboardController.shouldHeaderBeFocusable(field) ? '0' : undefined\"\n v-bind=\"callThAttr(null, field, 'top')\"\n @click=\"keyboardController.headerClicked(field, $event)\"\n @keydown=\"keyboardController.handleHeaderKeydown(field, $event)\"\n >\n <!-- eslint-disable prettier/prettier -->\n <slot\n :name=\"slots[`head(${field.key})`] ? (`head(${field.key})` as 'head()') : 'head()'\"\n :label=\"field.label\"\n :column=\"field.key\"\n :field\n :is-foot=\"false\"\n >\n <!-- eslint-enable prettier/prettier -->\n {{ getTableFieldHeadLabel(field) }}\n </slot>\n </BTh>\n </BTr>\n <BTr v-if=\"slots['thead-sub']\">\n <BTd\n v-for=\"field in computedFields\"\n :key=\"field.key\"\n scope=\"col\"\n :variant=\"field.variant\"\n :class=\"[field.class, fie