UNPKG

bootstrap-vue-next

Version:

Seamless integration of Vue 3, Bootstrap 5, and TypeScript for modern, type-safe UI development

1,432 lines 63.2 kB
import { S as tableKeyboardNavigationKey } from "./keys-CQKrwmvN.mjs"; import { L as useToNumber } from "./dist-Dn5blevd.mjs"; import { i as getSafeDocument } from "./dom-AhkaSoh8.mjs"; import { t as useId$1 } from "./useId-CCwnEmGh.mjs"; import { t as useDefaults } from "./useDefaults-CCWS15M8.mjs"; import { a as pick, o as set, t as deepEqual } from "./object-B9ZW1_9f.mjs"; import "./constants-CQ8ucyPI.mjs"; import { n as startCase, r as titleCase } from "./stringUtils-BP8rZgjn.mjs"; import "./src/types/index.mjs"; import { t as useDebounceFn } from "./debounce-BIVtU5ek.mjs"; import { t as BTableSimple_default } from "./BTableSimple-eebTf2L5.mjs"; import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createSlots, createTextVNode, createVNode, defineComponent, guardReactiveProps, inject, mergeModels, mergeProps, normalizeClass, normalizeProps, onMounted, openBlock, provide, readonly, renderList, renderSlot, resolveDynamicComponent, toDisplayString, toRef, toValue, unref, useModel, useSlots, vShow, watch, withCtx, withDirectives } from "vue"; //#region src/types/TableTypes.ts var isTableItem = (value) => typeof value === "object" && value !== null; var isTableField = (value) => typeof value === "object" && value !== null && "key" in value; //#endregion //#region src/components/BTable/BTbody.vue var BTbody_default = /* @__PURE__ */ defineComponent({ __name: "BTbody", props: { variant: { default: null } }, setup(__props) { const props = useDefaults(__props, "BTbody"); const computedClasses = computed(() => ({ [`thead-${props.variant}`]: props.variant !== null })); return (_ctx, _cache) => { return openBlock(), createElementBlock("tbody", { class: normalizeClass(computedClasses.value) }, [renderSlot(_ctx.$slots, "default")], 2); }; } }); //#endregion //#region src/components/BTable/BTd.vue?vue&type=script&setup=true&lang.ts var _hoisted_1$3 = [ "colspan", "rowspan", "data-label" ]; var _hoisted_2$3 = { key: 0 }; //#endregion //#region src/components/BTable/BTd.vue var BTd_default = /* @__PURE__ */ defineComponent({ __name: "BTd", props: { colspan: { default: void 0 }, rowspan: { default: void 0 }, stackedHeading: { default: void 0 }, stickyColumn: { type: Boolean, default: false }, variant: { default: null } }, setup(__props) { const props = useDefaults(__props, "BTd"); const computedClasses = computed(() => ({ [`table-${props.variant}`]: props.variant !== null, "b-table-sticky-column": props.stickyColumn, "table-b-table-default": props.stickyColumn && props.variant === null })); return (_ctx, _cache) => { return openBlock(), createElementBlock("td", { class: normalizeClass(computedClasses.value), colspan: unref(props).colspan, rowspan: unref(props).rowspan, "data-label": unref(props).stackedHeading }, [unref(props).stackedHeading ? (openBlock(), createElementBlock("div", _hoisted_2$3, [renderSlot(_ctx.$slots, "default")])) : renderSlot(_ctx.$slots, "default", { key: 1 })], 10, _hoisted_1$3); }; } }); //#endregion //#region src/components/BTable/BTfoot.vue var BTfoot_default = /* @__PURE__ */ defineComponent({ __name: "BTfoot", props: { variant: { default: null } }, setup(__props) { const props = useDefaults(__props, "BTfoot"); const computedClasses = computed(() => ({ [`table-${props.variant}`]: props.variant !== null })); return (_ctx, _cache) => { return openBlock(), createElementBlock("tfoot", { class: normalizeClass(computedClasses.value) }, [renderSlot(_ctx.$slots, "default")], 2); }; } }); //#endregion //#region src/components/BTable/BTh.vue?vue&type=script&setup=true&lang.ts var _hoisted_1$2 = [ "scope", "colspan", "rowspan", "data-label" ]; var _hoisted_2$2 = { key: 0 }; //#endregion //#region src/components/BTable/BTh.vue var BTh_default = /* @__PURE__ */ defineComponent({ __name: "BTh", props: { colspan: { default: void 0 }, rowspan: { default: void 0 }, stackedHeading: { default: void 0 }, stickyColumn: { type: Boolean, default: false }, variant: { default: null }, scope: { default: void 0 } }, setup(__props) { const props = useDefaults(__props, "BTh"); const computedClasses = computed(() => ({ [`table-${props.variant}`]: props.variant !== null, "b-table-sticky-column": props.stickyColumn, "table-b-table-default": props.stickyColumn && props.variant === null })); const localScope = computed(() => props.scope ? props.scope : props.colspan ? "colgroup" : props.rowspan ? "rowgroup" : "col"); return (_ctx, _cache) => { return openBlock(), createElementBlock("th", { scope: localScope.value, class: normalizeClass(computedClasses.value), colspan: unref(props).colspan, rowspan: unref(props).rowspan, "data-label": unref(props).stackedHeading }, [unref(props).stackedHeading !== void 0 ? (openBlock(), createElementBlock("div", _hoisted_2$2, [renderSlot(_ctx.$slots, "default")])) : renderSlot(_ctx.$slots, "default", { key: 1 })], 10, _hoisted_1$2); }; } }); //#endregion //#region src/components/BTable/BThead.vue var BThead_default = /* @__PURE__ */ defineComponent({ __name: "BThead", props: { variant: { default: null } }, setup(__props) { const props = useDefaults(__props, "BThead"); const computedClasses = computed(() => ({ [`table-${props.variant}`]: props.variant !== null })); return (_ctx, _cache) => { return openBlock(), createElementBlock("thead", { class: normalizeClass(computedClasses.value) }, [renderSlot(_ctx.$slots, "default")], 2); }; } }); //#endregion //#region src/components/BTable/BTr.vue var BTr_default = /* @__PURE__ */ defineComponent({ __name: "BTr", props: { variant: { default: null } }, setup(__props) { const props = useDefaults(__props, "BTr"); const computedClasses = computed(() => ({ [`table-${props.variant}`]: props.variant !== null })); return (_ctx, _cache) => { return openBlock(), createElementBlock("tr", { class: normalizeClass(computedClasses.value) }, [renderSlot(_ctx.$slots, "default")], 2); }; } }); //#endregion //#region src/utils/tableUtils.ts var getTableFieldHeadLabel = (field) => typeof field === "string" ? titleCase(field) : field.label !== void 0 ? field.label : titleCase(field.key); var getWithGetter = (item, key) => { if (typeof key === "function") return key(item); return item[key]; }; var getByFieldKey = (item, field) => typeof item === "object" && item !== null ? getWithGetter(item, field.accessor ?? field.key) : item; var formatItem = (item, field) => { const val = getByFieldKey(item, field); return typeof field.formatter === "function" ? field.formatter({ value: val, key: field.key, item }) : val; }; var bTableSimpleProps = Object.freeze(Object.keys({ bordered: 0, borderless: 0, borderVariant: 0, captionTop: 0, dark: 0, fixed: 0, hover: 0, id: 0, noBorderCollapse: 0, outlined: 0, responsive: 0, small: 0, stacked: 0, stickyHeader: 0, striped: 0, stripedColumns: 0, variant: 0, tableAttrs: 0, tableClass: 0 })); var bTableLiteProps = Object.freeze(Object.keys({ align: 0, caption: 0, detailsTdClass: 0, fieldColumnClass: 0, fields: 0, footClone: 0, footRowVariant: 0, footVariant: 0, headRowVariant: 0, headVariant: 0, items: 0, labelStacked: 0, modelValue: 0, primaryKey: 0, tbodyClass: 0, tbodyTrAttrs: 0, tbodyTrClass: 0, tfootClass: 0, expandedItems: 0, tfootTrClass: 0, theadClass: 0, theadTrClass: 0 })); var getDataLabelAttr = (props, label) => props.stacked && props.labelStacked !== true ? { "data-label": label } : void 0; //#endregion //#region src/utils/filterEvent.ts var TABLE_TAG_NAMES = [ "TD", "TH", "TR" ]; var eventFilter = [ "a", "a *", "button", "button *", "input:not(.disabled):not([disabled])", "select:not(.disabled):not([disabled])", "textarea:not(.disabled):not([disabled])", "[role=\"link\"]", "[role=\"link\"] *", "[role=\"button\"]", "[role=\"button\"] *", "[tabindex]:not(.disabled):not([disabled])" ].join(","); var filterEvent = (event) => { if (!event || !event.target) return false; const el = event.target; if ("disabled" in el && el.disabled || TABLE_TAG_NAMES.indexOf(el.tagName) !== -1) return false; if (el.closest(".dropdown-menu")) return true; const label = el.tagName === "LABEL" ? el : el.closest("label"); if (label) { const doc = getSafeDocument(); const labelFor = label.getAttribute("for"); const input = labelFor ? doc?.getElementById(labelFor) : label.querySelector("input, select, textarea"); if (input && !input.disabled) return true; } return el.matches(eventFilter); }; //#endregion //#region src/composables/useTableLiteHelpers.ts var useTableFieldsMapper = ({ fields, items, stackedProps }) => { const computedFields = computed(() => { const fieldsValue = toValue(fields); const itemsValue = toValue(items); const stacked = { stacked: toValue(stackedProps.stacked), labelStacked: toValue(stackedProps.labelStacked) }; if (!fieldsValue.length && itemsValue.length) { const [firstItem] = itemsValue; if (firstItem && (isTableItem(firstItem) || Array.isArray(firstItem))) return { items: Object.keys(firstItem).map((k) => { const label = startCase(k); return { key: k, label, tdAttr: getDataLabelAttr(stacked, label) }; }) }; return { items: [{ key: "" }], opts: { noHeader: true } }; } return { items: fieldsValue.map((f) => { if (isTableField(f)) { const dataLabelAttr = getDataLabelAttr(stacked, f.label ?? startCase(f.key)); const tdAttr = typeof f.tdAttr === "function" ? (obj) => ({ ...dataLabelAttr, ...f.tdAttr(obj) }) : dataLabelAttr || f.tdAttr ? { ...dataLabelAttr, ...f.tdAttr } : void 0; return { ...f, tdAttr }; } const label = startCase(f); return { key: f, label, tdAttr: getDataLabelAttr(stacked, label) }; }) }; }); const total = computed(() => computedFields.value.items.length); return { total, showHeaders: computed(() => !(total.value > 0 && computedFields.value.opts?.noHeader === true)), fields: computed(() => computedFields.value.items) }; }; var useItemTracker = ({ allItems, selectedItems, primaryKey }) => { const isActivated = computed(() => selectedItems.value.length > 0); const pKey = readonly(toRef(primaryKey)); const get = (item) => { if (typeof item === "object" && item !== null && pKey.value) return getWithGetter(item, pKey.value); return item; }; const add = (item) => { const value = get(item); selectedItems.value = [...selectedItems.value, value]; }; const set = (items) => { selectedItems.value = pKey.value ? items.map(get) : items; }; const setAll = () => set([...toValue(allItems)]); const remove = (item) => { const value = get(item); selectedItems.value = selectedItems.value.filter((i) => i !== value); }; const clear = () => { selectedItems.value = []; }; const has = (item) => { const value = get(item); return selectedItems.value.includes(value); }; watch(pKey, () => { clear(); }); const objectsMap = computed(() => { if (!pKey.value) return /* @__PURE__ */ new Map(); return new Map(toValue(allItems).map((item) => [get(item), item])); }); const getFromPrimaryKey = (value) => { if (!pKey.value) return void 0; return objectsMap.value.get(value); }; return { getFromPrimaryKey, resolvedItems: computed(() => { if (!pKey.value) return selectedItems.value; const arr = []; selectedItems.value.forEach((item) => { const resolved = getFromPrimaryKey(item); if (resolved !== void 0) arr.push(resolved); }); return arr; }), isActivated, get, add, remove, clear, set, has, setAll }; }; var useItemExpansion = ({ allItems, primaryKey, expandedItems }) => { const utils = useItemTracker({ primaryKey, allItems, selectedItems: expandedItems }); const toggle = (item) => { if (utils.has(item)) utils.remove(item); else utils.add(item); }; return { ...utils, toggle }; }; var useTableKeyboardNavigation = ({ items, id }, events) => { const keyboardNavigation = inject(tableKeyboardNavigationKey, null); const shouldHeaderBeFocusable = (field) => !!(keyboardNavigation?.headerNavigation.value && field.sortable === true); const shouldRowBeFocusable = computed(() => !!(keyboardNavigation?.rowNavigation.value && toValue(items).length > 0)); const headerClicked = (field, event, isFooter = false) => { events.onHeadClicked({ key: field.key, field, event, isFooter }); }; const handleHeaderKeydown = (field, event, isFooter = false) => { const { target, code } = event; if (target && target.tagName !== "TH" && getSafeDocument()?.activeElement === target) return; if (code === "Enter" || code === "NumpadEnter" || code === "Space") { event.preventDefault(); headerClicked(field, event, isFooter); } }; const handleRowKeydown = (item, itemIndex, event) => { const { target, code, shiftKey } = event; if (target && target.tagName !== "TR" && getSafeDocument()?.activeElement === target) return; if (code === "Enter" || code === "NumpadEnter" || code === "Space") { event.preventDefault(); events.onRowClicked({ item, index: itemIndex, event }); return; } if (code === "ArrowDown" || code === "ArrowUp" || code === "Home" || code === "End") { event.preventDefault(); handleRowNavigation(code, shiftKey, itemIndex); } }; const handleRowNavigation = (code, shiftKey, currentIndex) => { const doc = getSafeDocument(); const rows = doc === null ? [] : Array.from(doc.querySelectorAll(`#${toValue(id)} tbody tr[tabindex]`)); if (rows.length === 0) return; let targetIndex = currentIndex; if (code === "ArrowDown") if (shiftKey) targetIndex = rows.length - 1; else targetIndex = Math.min(currentIndex + 1, rows.length - 1); else if (code === "ArrowUp") if (shiftKey) targetIndex = 0; else targetIndex = Math.max(currentIndex - 1, 0); else if (code === "End") targetIndex = rows.length - 1; else if (code === "Home") targetIndex = 0; if (targetIndex !== currentIndex && rows[targetIndex]) rows[targetIndex]?.focus(); }; const handleMiddleClick = (item, itemIndex, event) => { if (event.button === 1 && !filterEvent(event)) events.onRowMiddleClicked({ item, index: itemIndex, event }); }; return { shouldHeaderBeFocusable, headerClicked, handleHeaderKeydown, shouldRowBeFocusable, handleRowKeydown, handleMiddleClick }; }; //#endregion //#region src/components/BTable/BTableLite.vue?vue&type=script&setup=true&lang.ts var _hoisted_1$1 = { key: 0 }; var _hoisted_2$1 = { key: 0, class: "b-table-stacked-label" }; var _hoisted_3 = { class: "d-inline-flex flex-nowrap align-items-center gap-1" }; var _hoisted_4 = { key: 3 }; //#endregion //#region src/components/BTable/BTableLite.vue var BTableLite_default = /* @__PURE__ */ defineComponent({ __name: "BTableLite", props: /* @__PURE__ */ mergeModels({ align: { default: void 0 }, caption: { default: void 0 }, detailsTdClass: { default: void 0 }, fieldColumnClass: { type: [ Function, String, Object, Array ], default: void 0 }, fields: { default: () => [] }, footClone: { type: Boolean, default: false }, footRowVariant: { default: void 0 }, footVariant: { default: void 0 }, headRowVariant: { default: void 0 }, headVariant: { default: void 0 }, items: { default: () => [] }, labelStacked: { type: Boolean, default: false }, modelValue: { default: void 0 }, primaryKey: { type: [String, Function], default: void 0 }, tbodyClass: { default: void 0 }, tbodyTrAttrs: { type: [Function, Object], default: void 0 }, tbodyTrClass: { type: [ Function, String, Array, Object ], default: void 0 }, tfootClass: { default: void 0 }, tfootTrClass: { default: void 0 }, theadClass: { default: void 0 }, theadTrClass: { default: void 0 }, bordered: { type: Boolean, default: void 0 }, borderless: { type: Boolean, default: void 0 }, borderVariant: { default: void 0 }, captionTop: { type: Boolean, default: void 0 }, dark: { type: Boolean, default: void 0 }, fixed: { type: Boolean, default: void 0 }, hover: { type: Boolean, default: void 0 }, id: { default: void 0 }, noBorderCollapse: { type: Boolean, default: void 0 }, outlined: { type: Boolean, default: void 0 }, responsive: { type: [Boolean, String], default: void 0 }, small: { type: Boolean, default: void 0 }, stacked: { type: [Boolean, String], default: void 0 }, stickyHeader: { type: [ Boolean, String, Number ], default: void 0 }, striped: { type: Boolean, default: void 0 }, stripedColumns: { type: Boolean, default: void 0 }, variant: { default: void 0 }, tableAttrs: {}, tableClass: { default: void 0 } }, { "expandedItems": { default: () => [] }, "expandedItemsModifiers": {} }), emits: /* @__PURE__ */ mergeModels([ "head-clicked", "row-clicked", "row-dblclicked", "row-contextmenu", "row-hovered", "row-unhovered", "row-middle-clicked" ], ["update:expandedItems"]), setup(__props, { expose: __expose, emit: __emit }) { const props = useDefaults(__props, "BTableLite"); const emit = __emit; const slots = useSlots(); const expandedItems = useModel(__props, "expandedItems"); const computedId = useId$1(() => props.id); const expandedItemsController = useItemExpansion({ allItems: () => props.items, primaryKey: toRef(() => props.primaryKey), expandedItems }); const keyboardController = useTableKeyboardNavigation({ items: () => props.items, id: computedId }, { onHeadClicked: (obj) => { emit("head-clicked", obj); }, onRowClicked: (obj) => { emit("row-clicked", obj); }, onRowMiddleClicked: (obj) => { emit("row-middle-clicked", obj); } }); const { fields: computedFields, total: computedFieldsTotal, showHeaders: showComputedHeaders } = useTableFieldsMapper({ fields: () => props.fields, items: () => props.items, stackedProps: { labelStacked: () => props.labelStacked, stacked: () => props.stacked } }); const calculatedFooterSlot = (key) => slots[`foot(${key})`] ? `foot(${key})` : slots["foot()"] ? "foot()" : slots[`head(${key})`] ? `head(${key})` : "head()"; const computedTableClasses = computed(() => [props.tableClass, { [`align-${props.align}`]: props.align !== void 0 }]); const getFieldColumnClasses = (field) => [ field.class, field.thClass, { "b-table-sticky-column": field.stickyColumn }, props.fieldColumnClass ? typeof props.fieldColumnClass === "function" ? props.fieldColumnClass(field) : props.fieldColumnClass : null ]; const getFieldRowClasses = (field, tr) => [ field.class, typeof field.tdClass === "function" ? field.tdClass(getByFieldKey(tr, field), field.key, tr) : field.tdClass, (isTableItem(tr) ? tr._cellVariants?.[field.key] : false) ? `table-${tr._cellVariants?.[field.key]}` : null, { "b-table-sticky-column": field.stickyColumn } ]; const getRowClasses = (item, type) => props.tbodyTrClass ? typeof props.tbodyTrClass === "function" ? props.tbodyTrClass(item, type) : props.tbodyTrClass : null; const itemAttributes = (item, field) => field.tdAttr && typeof field.tdAttr === "function" ? field.tdAttr({ value: getByFieldKey(item, field), key: field.key, item }) : field.tdAttr; const callThAttr = (item, field, type) => field.thAttr && typeof field.thAttr === "function" ? field.thAttr({ value: getByFieldKey(item, field), key: field.key, item, type }) : field.thAttr; const callTbodyTrAttrs = (item, type) => props.tbodyTrAttrs ? typeof props.tbodyTrAttrs === "function" ? props.tbodyTrAttrs(item, type) : props.tbodyTrAttrs : null; const generateTableRowId = (primaryKeyValue) => `${computedId.value}__row_${primaryKeyValue}`; const getCellComponent = (field) => { if (field?.isRowHeader) return BTh_default; return BTd_default; }; const footerProps = computed(() => ({ variant: props.footVariant ?? props.headVariant, class: props.tfootClass ?? props.theadClass })); const computedSimpleProps = computed(() => ({ ...pick(props, bTableSimpleProps), tableClass: computedTableClasses.value, id: computedId.value })); __expose({ expansion: { ...expandedItemsController, expandedItems: readonly(expandedItems) } }); return (_ctx, _cache) => { return openBlock(), createBlock(BTableSimple_default, normalizeProps(guardReactiveProps(computedSimpleProps.value)), { default: withCtx(() => [ slots["table-colgroup"] ? (openBlock(), createElementBlock("colgroup", _hoisted_1$1, [renderSlot(_ctx.$slots, "table-colgroup", { fields: unref(computedFields) })])) : createCommentVNode("", true), withDirectives(createVNode(BThead_default, { variant: unref(props).headVariant, class: normalizeClass(unref(props).theadClass) }, { default: withCtx(() => [ renderSlot(_ctx.$slots, "thead-top", { columns: unref(computedFieldsTotal), fields: unref(computedFields) }), createVNode(BTr_default, { variant: unref(props).headRowVariant, class: normalizeClass(unref(props).theadTrClass) }, { default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(computedFields), (field) => { return openBlock(), createBlock(BTh_default, mergeProps({ key: field.key, scope: field.scope, class: getFieldColumnClasses(field), title: field.headerTitle, variant: field.variant, abbr: field.headerAbbr, style: field.thStyle, tabindex: unref(keyboardController).shouldHeaderBeFocusable(field) ? "0" : void 0 }, { ref_for: true }, callThAttr(null, field, "top"), { onClick: ($event) => unref(keyboardController).headerClicked(field, $event), onKeydown: ($event) => unref(keyboardController).handleHeaderKeydown(field, $event) }), { default: withCtx(() => [renderSlot(_ctx.$slots, slots[`head(${field.key})`] ? `head(${field.key})` : "head()", { label: field.label, column: field.key, field, isFoot: false }, () => [createTextVNode(toDisplayString(unref(getTableFieldHeadLabel)(field)), 1)])]), _: 2 }, 1040, [ "scope", "class", "title", "variant", "abbr", "style", "tabindex", "onClick", "onKeydown" ]); }), 128))]), _: 3 }, 8, ["variant", "class"]), slots["thead-sub"] ? (openBlock(), createBlock(BTr_default, { key: 0 }, { default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(computedFields), (field) => { return openBlock(), createBlock(BTd_default, { key: field.key, scope: "col", variant: field.variant, class: normalizeClass([field.class, field.thClass]) }, { default: withCtx(() => [renderSlot(_ctx.$slots, "thead-sub", { items: unref(props).items, fields: unref(computedFields), field }, () => [createTextVNode(toDisplayString(field.label), 1)])]), _: 2 }, 1032, ["variant", "class"]); }), 128))]), _: 3 })) : createCommentVNode("", true) ]), _: 3 }, 8, ["variant", "class"]), [[vShow, unref(showComputedHeaders)]]), createVNode(BTbody_default, { class: normalizeClass(unref(props).tbodyClass) }, { default: withCtx(() => [renderSlot(_ctx.$slots, "custom-body", { fields: unref(computedFields), items: unref(props).items, columns: unref(computedFieldsTotal) }, () => [ !unref(props).stacked && slots["top-row"] ? (openBlock(), createBlock(BTr_default, mergeProps({ key: 0, class: getRowClasses(null, "row-top") }, callTbodyTrAttrs(null, "row-top")), { default: withCtx(() => [renderSlot(_ctx.$slots, "top-row", { columns: unref(computedFieldsTotal), fields: unref(computedFields) })]), _: 3 }, 16, ["class"])) : createCommentVNode("", true), (openBlock(true), createElementBlock(Fragment, null, renderList(unref(props).items, (item, itemIndex) => { return openBlock(), createElementBlock(Fragment, { key: unref(props).primaryKey && unref(isTableItem)(item) && unref(getWithGetter)(item, unref(props).primaryKey) ? unref(getWithGetter)(item, unref(props).primaryKey) : itemIndex }, [createVNode(BTr_default, mergeProps({ id: unref(props).primaryKey && unref(isTableItem)(item) && unref(getWithGetter)(item, unref(props).primaryKey) ? generateTableRowId(unref(getWithGetter)(item, unref(props).primaryKey)) : void 0, class: getRowClasses(item, "row"), variant: unref(isTableItem)(item) ? item._rowVariant : void 0, tabindex: unref(keyboardController).shouldRowBeFocusable.value ? "0" : void 0 }, { ref_for: true }, callTbodyTrAttrs(item, "row"), { onClick: ($event) => !unref(filterEvent)($event) && emit("row-clicked", { item, index: itemIndex, event: $event }), onDblclick: ($event) => !unref(filterEvent)($event) && emit("row-dblclicked", { item, index: itemIndex, event: $event }), onContextmenu: ($event) => !unref(filterEvent)($event) && emit("row-contextmenu", { item, index: itemIndex, event: $event }), onMouseenter: ($event) => !unref(filterEvent)($event) && emit("row-hovered", { item, index: itemIndex, event: $event }), onMouseleave: ($event) => !unref(filterEvent)($event) && emit("row-unhovered", { item, index: itemIndex, event: $event }), onMousedown: ($event) => unref(keyboardController).handleMiddleClick(item, itemIndex, $event), onKeydown: ($event) => unref(keyboardController).handleRowKeydown(item, itemIndex, $event) }), { default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(computedFields), (field) => { return openBlock(), createBlock(resolveDynamicComponent(getCellComponent(field)), mergeProps({ key: field.key, variant: (unref(isTableItem)(item) ? item._cellVariants?.[field.key] : false) ? null : field.variant, class: getFieldRowClasses(field, item) }, { ref_for: true }, itemAttributes(item, field)), { default: withCtx(() => [unref(props).stacked && unref(props).labelStacked ? (openBlock(), createElementBlock("label", _hoisted_2$1, toDisplayString(unref(getTableFieldHeadLabel)(field)), 1)) : createCommentVNode("", true), renderSlot(_ctx.$slots, slots[`cell(${field.key})`] ? `cell(${field.key})` : "cell()", { value: unref(formatItem)(item, field), unformatted: unref(getByFieldKey)(item, field), index: itemIndex, item, field, items: unref(props).items, toggleExpansion: () => unref(expandedItemsController).toggle(item), expansionShowing: unref(expandedItemsController).has(item) }, () => [!slots[`cell(${field.key})`] && !slots["cell()"] ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createTextVNode(toDisplayString(unref(formatItem)(item, field)), 1)], 64)) : createCommentVNode("", true)])]), _: 2 }, 1040, ["variant", "class"]); }), 128))]), _: 2 }, 1040, [ "id", "class", "variant", "tabindex", "onClick", "onDblclick", "onContextmenu", "onMouseenter", "onMouseleave", "onMousedown", "onKeydown" ]), unref(expandedItemsController).has(item) && slots["row-expansion"] ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createVNode(BTr_default, { "aria-hidden": "true", role: "presentation", class: "d-none" }), createVNode(BTr_default, mergeProps({ class: getRowClasses(item, "row-expansion"), variant: unref(isTableItem)(item) ? item._rowVariant : void 0 }, { ref_for: true }, callTbodyTrAttrs(item, "row-expansion")), { default: withCtx(() => [createVNode(BTd_default, { colspan: unref(computedFieldsTotal), class: normalizeClass(__props.detailsTdClass) }, { default: withCtx(() => [renderSlot(_ctx.$slots, "row-expansion", { item, toggleExpansion: () => unref(expandedItemsController).toggle(item), fields: unref(computedFields), index: itemIndex })]), _: 2 }, 1032, ["colspan", "class"])]), _: 2 }, 1040, ["class", "variant"])], 64)) : createCommentVNode("", true)], 64); }), 128)), !unref(props).stacked && slots["bottom-row"] ? (openBlock(), createBlock(BTr_default, mergeProps({ key: 1, class: ["bottom-row", getRowClasses(null, "row-bottom")] }, callTbodyTrAttrs(null, "row-bottom")), { default: withCtx(() => [renderSlot(_ctx.$slots, "bottom-row", { columns: unref(computedFieldsTotal), fields: unref(computedFields) })]), _: 3 }, 16, ["class"])) : createCommentVNode("", true) ])]), _: 3 }, 8, ["class"]), unref(props).footClone ? (openBlock(), createBlock(BTfoot_default, normalizeProps(mergeProps({ key: 1 }, footerProps.value)), { default: withCtx(() => [createVNode(BTr_default, { variant: unref(props).footRowVariant ?? unref(props).headRowVariant, class: normalizeClass(unref(props).tfootTrClass ?? unref(props).theadTrClass) }, { default: withCtx(() => [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(computedFields), (field) => { return openBlock(), createBlock(BTh_default, mergeProps({ key: field.key, scope: "col", class: getFieldColumnClasses(field), title: field.headerTitle, abbr: field.headerAbbr, style: field.thStyle, variant: field.variant, tabindex: unref(keyboardController).shouldHeaderBeFocusable(field) ? "0" : void 0 }, { ref_for: true }, callThAttr(null, field, "bottom"), { onClick: ($event) => unref(keyboardController).headerClicked(field, $event, true), onKeydown: ($event) => unref(keyboardController).handleHeaderKeydown(field, $event, true) }), { default: withCtx(() => [createElementVNode("div", _hoisted_3, [createElementVNode("div", null, [renderSlot(_ctx.$slots, calculatedFooterSlot(field.key), { label: field.label, column: field.key, field, isFoot: true }, () => [createTextVNode(toDisplayString(unref(getTableFieldHeadLabel)(field)), 1)])])])]), _: 2 }, 1040, [ "class", "title", "abbr", "style", "variant", "tabindex", "onClick", "onKeydown" ]); }), 128))]), _: 3 }, 8, ["variant", "class"])]), _: 3 }, 16)) : slots["custom-foot"] ? (openBlock(), createBlock(BTfoot_default, normalizeProps(mergeProps({ key: 2 }, footerProps.value)), { default: withCtx(() => [renderSlot(_ctx.$slots, "custom-foot", { fields: unref(computedFields), items: unref(props).items, columns: unref(computedFieldsTotal) })]), _: 3 }, 16)) : createCommentVNode("", true), slots["table-caption"] || unref(props).caption ? (openBlock(), createElementBlock("caption", _hoisted_4, [renderSlot(_ctx.$slots, "table-caption", {}, () => [createTextVNode(toDisplayString(unref(props).caption), 1)])])) : createCommentVNode("", true) ]), _: 3 }, 16); }; } }); //#endregion //#region src/composables/useTableHelpers.ts var useTableMapper = ({ fields, items, stackedProps, provider, events, pagination }) => { const sortByModelResolved = readonly(toRef(pagination.sort.by)); const isSortableResolved = readonly(toRef(pagination.sort.isSortable)); const filterResolved = readonly(toRef(pagination.filter.filter)); const usesProviderResolved = readonly(toRef(provider.usesProvider)); const isFilterableTable = computed(() => filterResolved.value !== void 0); const computedFields = computed(() => toValue(fields).map((el) => { if (!isTableField(el)) { const label = startCase(el); return { key: el, label, tdAttr: getDataLabelAttr({ stacked: toValue(stackedProps.stacked), labelStacked: toValue(stackedProps.labelStacked) }, label) }; } const value = sortByModelResolved.value?.find((sb) => el.key === sb.key); const sortValue = !el.sortable || isSortableResolved.value === false ? void 0 : value === void 0 ? "none" : value.order === "desc" ? "descending" : value.order === "asc" ? "ascending" : "none"; return { ...el, thAttr: { "aria-sort": sortValue, ...el.thAttr }, thClass: [el.thClass, { "b-table-sort-icon-left": toValue(pagination.sort.iconLeft) && sortValue !== void 0 }] }; })); const getFormatter = (value) => typeof value.sortByFormatted === "function" ? value.sortByFormatted : value.formatter; const getStringValue = (ob, key) => { if (!isTableItem(ob)) return String(ob); const sortField = computedFields.value.find((el) => { if (isTableField(el)) return el.key === key; return false; }); const val = isTableField(sortField) && sortField.accessor ? getWithGetter(ob, sortField.accessor) : getWithGetter(ob, key); if (isTableField(sortField) && !!sortField.sortByFormatted) { const formatter = getFormatter(sortField); if (formatter) return String(formatItem(ob, { ...sortField, formatter })); } return typeof val === "object" && val !== null ? JSON.stringify(val) : val?.toString() ?? ""; }; const fieldByKey = computed(() => { const map = /* @__PURE__ */ new Map(); for (const f of computedFields.value) if (isTableField(f)) map.set(f.key, f); return map; }); const computedItems = computed(() => { const filterableValue = toValue(pagination.filter.filterable); const filterFunctionValue = unref(pagination.filter.filterFunction); const itemsValue = toValue(items); const sortByItems = sortByModelResolved.value?.filter((el) => !!el.order); const mapItem = (item) => { if (typeof item === "object" && item !== null && Object.keys(item).some((key) => key.includes("."))) { let newItem = {}; for (const key in item) if (key.includes(".")) newItem = set(newItem, key, item[key]); else newItem[key] = item[key]; return newItem; } return item; }; const filterItem = (item) => { if (!isTableItem(item)) return true; return Object.entries(item).some(([key, val]) => { if (val === null || val === void 0 || key[0] === "_" || !filterableValue?.includes(key) && !!filterableValue?.length) return false; if (typeof filterFunctionValue === "function") return filterFunctionValue(item, filterResolved.value); const realVal = () => { const filterField = computedFields.value.find((el) => { if (isTableField(el)) return el.key === key; return false; }); if (isTableField(filterField) && !!filterField.filterByFormatted) { const formatter = getFormatter(filterField); if (formatter) return String(formatter({ value: val, key: String(filterField.key), item })); } return typeof val === "object" ? JSON.stringify(Object.values(val)) : val.toString(); }; return realVal().toLowerCase().includes(filterResolved.value?.toLowerCase() ?? ""); }); }; const noProviderFilteringValue = toValue(provider.noProviderFiltering); const mappedItems = itemsValue.reduce((acc, val) => { const item = mapItem(val); if (!(isFilterableTable.value && (!usesProviderResolved.value || noProviderFilteringValue)) || filterItem(item)) acc.push(item); return acc; }, []); if (sortByItems?.length && (isSortableResolved.value === true && !usesProviderResolved.value && !toValue(pagination.sort.noLocalSorting) || isSortableResolved.value === true && usesProviderResolved.value && toValue(provider.noProviderSorting))) { const sortCompareValue = unref(pagination.sort.sortCompare); return mappedItems.sort((a, b) => { for (let i = 0; i < sortByItems.length; i++) { const value = sortByItems[i]; if (value === void 0) continue; const { key, order } = value; const comparer = fieldByKey.value.get(key)?.sortCompare || sortCompareValue; const comparison = comparer ? comparer(a, b, key) : getStringValue(a, key).localeCompare(getStringValue(b, key), void 0, { numeric: true }); if (comparison !== 0) return order === "asc" ? comparison : -comparison; } return 0; }); } return mappedItems; }); const computedDisplayItems = computed(() => { const perPageNumber = toValue(pagination.perPage); const currentPageNumber = toValue(pagination.currentPage); if (Number.isNaN(perPageNumber) || usesProviderResolved.value && !toValue(provider.noProviderPaging)) return computedItems.value; return computedItems.value.slice((currentPageNumber - 1) * (perPageNumber || Number.POSITIVE_INFINITY), currentPageNumber * (perPageNumber || Number.POSITIVE_INFINITY)); }); watch(filterResolved, () => { pagination.currentPage.value = 1; }); watch(computedDisplayItems, (v) => { events.onChange([...v]); }); return { items: computedItems, displayItems: computedDisplayItems, getStringValue, fields: computedFields, isFilterableTable }; }; var useTableKeyboardNavigationInjector = ({ isSortable, selectable, noSelectOnClick }) => { provide(tableKeyboardNavigationKey, { rowNavigation: computed(() => !!(toValue(selectable) && !toValue(noSelectOnClick))), headerNavigation: computed(() => !!toValue(isSortable)) }); }; var useTableSelectedItems = ({ selectable, selectMode, selectedItems, events, primaryKey, allItems }) => { const selectableResolved = readonly(toRef(selectable)); const selectModeResolved = readonly(toRef(selectMode)); const allItemsResolved = computed(() => toValue(allItems)); const utils = useItemTracker({ allItems: allItemsResolved, primaryKey, selectedItems }); watch(selectedItems, (newValue, oldValue) => { Array.from(oldValue).filter((item) => !newValue.includes(item)).forEach((item) => { events.onRowUnselected(item); }); Array.from(newValue).filter((item) => !oldValue.includes(item)).forEach((item) => { events.onRowSelected(item); }); }); const handleRowSelection = ({ item, index, shiftClicked = false, ctrlClicked = false, metaClicked = false }) => { if (!selectableResolved.value) return; if (selectModeResolved.value === "single" || selectModeResolved.value === "multi") { if (shiftClicked || ctrlClicked) return; if (utils.has(item)) utils.remove(item); else if (selectModeResolved.value === "single") utils.set([item]); else utils.add(item); } else if (ctrlClicked || metaClicked) if (utils.has(item)) utils.remove(item); else utils.add(item); else if (shiftClicked) { const lastSelectedItem = selectedItems.value[selectedItems.value.length - 1]; const lastSelectedIndex = allItemsResolved.value.findIndex((i) => utils.get(i) === lastSelectedItem); if (lastSelectedIndex === -1) { utils.set([item]); return; } const selectStartIndex = Math.min(lastSelectedIndex, index); const selectEndIndex = Math.max(lastSelectedIndex, index); const items = allItemsResolved.value.slice(selectStartIndex, selectEndIndex + 1); utils.set(items); } else utils.set([item]); }; return { ...utils, handleRowSelection, clear: () => { if (!selectableResolved.value) return; utils.clear(); }, setAll: () => { if (!selectableResolved.value || selectModeResolved.value === "single") return; utils.setAll(); }, add: (item) => { if (!selectableResolved.value || utils.has(item)) return; if (selectModeResolved.value === "single") utils.set([item]); else utils.add(item); }, remove: (item) => { if (!selectableResolved.value) return; utils.remove(item); }, has: (item) => { if (!selectableResolved.value) return false; return utils.has(item); } }; }; var useTableProvider = ({ items, provider, busy, currentPage, debounce, perPage, noProvider, noProviderFiltering, noProviderPaging, noProviderSorting, filter, sortBy, events }) => { const providerResolved = readonly(toRef(provider)); const currentPageResolved = readonly(toRef(currentPage)); const perPageResolved = readonly(toRef(perPage)); const sortByResolved = readonly(toRef(sortBy)); const filterResolved = readonly(toRef(filter)); const usesProvider = computed(() => providerResolved.value !== void 0); let abortController = null; const callItemsProvider = async () => { if (!usesProvider.value || providerResolved.value === void 0) return; if (abortController) abortController.abort(); abortController = new AbortController(); const { signal } = abortController; busy.value = true; try { const response = providerResolved.value({ currentPage: currentPageResolved.value, filter: filterResolved.value, sortBy: sortByResolved.value, perPage: perPageResolved.value, signal }); const returnValue = response instanceof Promise ? await response : response; if (signal.aborted) return; if (returnValue === void 0) return; items.value = returnValue; } catch (error) { if (error instanceof Error && error.name === "AbortError") return; throw error; } finally { if (!signal.aborted) busy.value = false; } }; const debouncedCallItemsProvider = useDebounceFn(callItemsProvider, debounce.wait, { maxWait: debounce.maxWait }); const providerPropsWatch = async (prop, val, oldVal) => { if (deepEqual(val, oldVal)) return; const inNoProvider = (key) => toValue(noProvider)?.includes(key) === true; const noProvideWhenPaging = (prop === "currentPage" || prop === "perPage") && (inNoProvider("paging") || toValue(noProviderPaging) === true); const noProvideWhenFiltering = prop === "filter" && (inNoProvider("filtering") || toValue(noProviderFiltering) === true); const noProvideWhenSorting = (prop === "sortBy" || prop === "sortDesc") && (inNoProvider("sorting") || toValue(noProviderSorting) === true); if (noProvideWhenPaging || noProvideWhenFiltering || noProvideWhenSorting) return; if (usesProvider.value === true) await debouncedCallItemsProvider(); if (!(prop === "currentPage" || prop === "perPage")) events.onFiltered(); }; watch(filterResolved, async (filter, oldFilter) => { await providerPropsWatch("filter", filter, oldFilter); }); watch(currentPageResolved, async (val, oldVal) => { await providerPropsWatch("currentPage", val, oldVal); }); watch(perPageResolved, async (val, oldVal) => { await providerPropsWatch("perPage", val, oldVal); }); watch(sortByResolved, async (val, oldVal) => { await providerPropsWatch("sortBy", val, oldVal); }, { deep: true }); watch(providerResolved, async (newValue) => { if (newValue === void 0) { items.value = []; return; } await callItemsProvider(); }); onMounted(async () => { await callItemsProvider(); }); return { usesProvider, callItemsProvider }; }; var useTableSort = ({ fields, sortBy, initialSortDirection, multisort, mustSort, events }) => { const isSortable = computed(() => sortBy.value !== void 0 || toValue(fields).some((field) => typeof field === "object" && field !== null && field.sortable === true)); const handleFieldSorting = (field) => { if (!isSortable.value) return; const fieldKey = typeof field === "object" && field !== null ? field.key : field; const fieldSortable = typeof field === "object" && field !== null ? field.sortable : false; if (!(isSortable.value === true && fieldSortable === true)) return; const getLastSortDirection = () => { return [...sortBy.value ?? []].reverse().find((sort) => sort.order !== void 0 && sort.key !== fieldKey)?.order ?? "asc"; }; const getInitialSortDirection = () => { if (typeof field === "object" && field !== null && field.initialSortDirection) { if (field.initialSortDirection === "last") return getLastSortDirection(); return field.initialSortDirection; } const initialSortDirectionValue = toValue(initialSortDirection); if (initialSortDirectionValue) { if (initialSortDirectionValue === "last") return getLastSortDirection(); return initialSortDirectionValue; } return "asc"; }; const resolveOrder = (val) => { const mustSortValue = toValue(mustSort); if (val === void 0) return getInitialSortDirection(); const initial = getInitialSortDirection(); const must = mustSortValue === true || !!mustSortValue && mustSortValue.includes(fieldKey); if (val === "asc") { if (initial === "desc") return must ? "desc" : void 0; return "desc"; } if (val === "desc") { if (initial === "desc") return "asc"; return must ? "asc" : void 0; } }; const index = sortBy.value?.findIndex((el) => el.key === fieldKey) ?? -1; const originalValue = sortBy.value?.[index]; const updatedValue = index === -1 || !originalValue ? { key: fieldKey, order: getInitialSortDirection() } : { ...originalValue }; /** * @returns the updated value to emit for sorted */ const handleMultiSort = () => { const tmp = [...sortBy.value ?? []]; const val = updatedValue; if (index === -1) tmp.push(val); else { const order = resolveOrder(val.order); if (order) { val.order = order; tmp.splice(index, 1, val); } else { val.order = void 0; tmp.splice(index, 1); } } sortBy.value = tmp; return val; }; /** * @returns the updated value to emit for sorted */ const handleSingleSort = () => { const order = index === -1 ? updatedValue.order : resolveOrder(updatedValue.order); const val = { ...updatedValue, order }; sortBy.value = order ? [val] : []; return val; }; events.onSorted(toValue(multisort) === true ? handleMultiSort() : handleSingleSort()); }; return { isSortable, handleFieldSorting }; }; //#endregion //#region src/components/BTable/BTable.vue?vue&type=script&setup=true&lang.ts var _hoisted_1 = { role: "alert", "aria-live": "polite" }; var _hoisted_2 = { class: "text-center my-2" }; //#endregion //#region src/components/BTable/BTable.vue var BTable_default = /* @__PURE__ */ defineComponent({ __name: "BTable", props: /* @__PURE__ */ mergeModels({ provider: { type: Function, default: void 0 }, noProvider: { default: void 0 }, noProviderPaging: { type: Boolean, default: false }, noProviderSorting: { type: Boolean, default: false }, noProviderFiltering: { type: Boolean, default: false }, sortCompare: { type: Function, default: void 0 }, mustSort: { type: [Boolean, Array], default: false }, initialSortDirection: { default: "asc" }, selectable: { type: Boolean, default: false }, multisort: { type: Boolean, default: false }, stickySelect: { type: Boolean, default: false }, selectHead: { type: [Boolean, String], default: true }, selectMode: { default: "multi" }, selectionVariant: { default: "primary" }, perPage: { default: Number.POSITIVE_INFINITY }, filter: { default: void 0 }, filterFunction: { type: Function, default: void 0 }, filterable: { default: void 0 }, noLocalSorting: { type: Boolean, default: false }, noSelectOnClick: { type: Boolean, default: false }, noSortableIcon: { type: Boolean, default: false }, sortIconLeft: { type: Boolean, default: false }, emptyFilteredText: { default: "There are no records matching your request" }, emptyText: { default: "There are no records to show" }, showEmpty: { type: Boolean, default: false }, align: { default: void 0 }, caption: { default: void 0 }, detailsTdClass: { default: void 0 }, fieldColumnClass: { type: [ Function, String, Object, Array ], default: void 0 }, fields: { default: () => [] }, footClone: { type: Boolean, default: void 0 }, footRowVariant: { default: void 0 }, footVariant: { default: void 0 }, headRowVariant: { default: void 0 }, headVariant: { default: void 0 }, labelStacked: { type: Boolean, default: void 0 }, modelValue: { default: void 0 }, expandedItems: {}, primaryKey: { type: [String, Function], default: void 0 }, tbodyClass: { default: void 0 }, tbodyTrAttrs: {}, tbodyTrClass: { type: [ Function, String, Array, Object ], default: void 0 }, tfootClass: { default: void 0 }, tfootTrClass: { default: void 0 }, theadClass: { default: void 0 }, theadTrClass: { default: void 0 }, bordered: { type: Boolean, default: void 0 }, borderless: { type: Boolean, default: void 0 }, borderVariant: { default: void 0 }, captionTop: { type: Boolean, default: void 0 }, dark: { type: Boolean, default: void 0 }, fixed: { type: Boolean, default: void 0 }, hover: { type: Boolean, default: void 0 }, id: { default: void 0 }, noBorderCollapse: { type: Boolean, default: void 0 }, outlined: { type: Boolean, default: void 0 }, responsive: { type: [Boolean, String], default: void 0 }, small: { t