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
JavaScript
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