Skip to content

Commit

Permalink
fix(table): prevent TableColumn function props trigger recursive reac…
Browse files Browse the repository at this point in the history
…tivity (#848)
  • Loading branch information
mlmoravek committed Mar 9, 2024
1 parent 578d854 commit 8f83b71
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 75 deletions.
4 changes: 2 additions & 2 deletions packages/docs/components/Table.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,8 @@ title: Table
| sortable | Enable column sortability | boolean | - | <code style='white-space: nowrap; padding: 0;'>false</code> |
| sticky | Whether the column is sticky or not | boolean | - | <code style='white-space: nowrap; padding: 0;'>false</code> |
| subheading | Define a column sub heading | string | - | |
| tdAttrs | Adds native attributes to td | (row: unknown, column: Column) =&gt; object | - | Default function (see source code) |
| thAttrs | Adds native attributes to th | (column: Column) =&gt; object | - | Default function (see source code) |
| tdAttrs | Adds native attributes to td | (row: unknown, column: typeof props) =&gt; object | - | Default function (see source code) |
| thAttrs | Adds native attributes to th | (column: typeof props) =&gt; object | - | Default function (see source code) |
| visible | Define whether the column is visible or not | boolean | - | <code style='white-space: nowrap; padding: 0;'>true</code> |
| width | Column fixed width | number\|string | - | |

Expand Down
20 changes: 13 additions & 7 deletions packages/oruga/src/components/table/Table.vue
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,8 @@ const tableColumns = computed<TableColumn[]>(() =>
index: column.index,
identifier: column.identifier,
...toValue(column.data),
thAttrsData: {},
tdAttrsData: [],
})),
);
Expand Down Expand Up @@ -793,17 +795,19 @@ const visibleRows = computed(() => {
});
const visibleColumns = computed(() => {
if (!tableColumns.value) return tableColumns.value;
return tableColumns.value.filter((column) => {
return column.visible || column.visible === undefined;
});
if (!tableColumns.value) return [];
return tableColumns.value.filter(
(column) => column.visible || column.visible === undefined,
);
});
watch([() => visibleRows.value, () => visibleColumns.value], () => {
// process tdAttrs when row or columns got changed
/** process thAttrs & tdAttrs when row or columns got changed */
watch([visibleRows, visibleColumns], () => {
if (visibleColumns.value.length && visibleRows.value.length) {
for (let i = 0; i < visibleColumns.value.length; i++) {
const col = visibleColumns.value[i];
col.thAttrsData =
typeof col.thAttrs === "function" ? col.thAttrs(col) : {};
col.tdAttrsData = visibleRows.value.map((data) =>
typeof col.tdAttrs === "function" ? col.tdAttrs(data, col) : {},
);
Expand Down Expand Up @@ -1808,7 +1812,9 @@ function tdClasses(row: unknown, column: TableColumnComponent): ClassBind[] {
tag="span"
:props="{ column, index }" />
</template>
<template v-else>{{ column.subheading }}</template>
<template v-else>
{{ column.subheading }}
</template>
</th>
<th v-if="checkable && checkboxPosition === 'right'" />
</tr>
Expand Down
33 changes: 8 additions & 25 deletions packages/oruga/src/components/table/TableColumn.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,10 @@
<script setup lang="ts">
import {
computed,
onBeforeMount,
ref,
useSlots,
getCurrentInstance,
type PropType,
} from "vue";
import { toRaw, computed, getCurrentInstance, type PropType } from "vue";
import { useProviderChild } from "@/composables";
import { toCssDimension } from "@/utils/helpers";
import type { TableColumnComponent, Column } from "./types";
import type { TableColumnComponent } from "./types";
/**
* @displayName Table Column
Expand Down Expand Up @@ -72,19 +65,18 @@ const props = defineProps({
headerSelectable: { type: Boolean, default: false },
/** Adds native attributes to th */
thAttrs: {
type: Function as PropType<(column: Column) => object>,
type: Function as PropType<(column: typeof props) => object>,
default: () => ({}),
},
/** Adds native attributes to td */
tdAttrs: {
type: Function as PropType<(row: unknown, column: Column) => object>,
type: Function as PropType<
(row: unknown, column: typeof props) => object
>,
default: () => ({}),
},
});
const thAttrsData = ref({});
const tdAttrsData = ref([]);
const style = computed(() => ({
width: toCssDimension(props.width),
}));
Expand All @@ -94,25 +86,16 @@ const isHeaderUnselectable = computed(
);
const vm = getCurrentInstance();
const slots = useSlots();
const providedData = computed<TableColumnComponent>(() => ({
...props,
...toRaw(props),
$el: vm.proxy,
$slots: slots,
$slots: vm.slots,
style: style.value,
thAttrsData: thAttrsData.value,
tdAttrsData: tdAttrsData.value,
isHeaderUnselectable: isHeaderUnselectable.value,
}));
const { item } = useProviderChild({ data: providedData });
onBeforeMount(() => {
if (typeof props.thAttrs !== "undefined") {
thAttrsData.value = props.thAttrs(props);
}
});
</script>

<template>
Expand Down
51 changes: 10 additions & 41 deletions packages/oruga/src/components/table/types.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,24 @@
import type {
ComponentProps,
ComponentSlots,
} from "vue-component-type-helpers";
import type { ComponentPublicInstance, StyleValue } from "vue";
import TableColumn from "./TableColumn.vue";
import type { ProviderItem } from "@/composables";
import type { ComponentSlots } from "vue-component-type-helpers";

export type Column = Partial<{
/** Define the column label */
label: string;
/** Define the object property key */
field: string;
/** Define a column sub label */
subheading: string;
/** Enable column sortability */
sortable: boolean;
/** Enable an additional searchbar below the column header */
searchable: boolean;
/** Add addtional meta information for the column */
meta: unknown;
/** Column fixed width */
width: number | string;
/** Define column value as number */
numeric: boolean;
/**
* Position of the column content
* @values left, centered, right
*/
position: string;
/** Define whether the column is visible or not - default true */
visible: boolean;
/** Define a custom sort function */
customSort: (a: Column, b: Column, isAsc: boolean) => number;
/** Define a custom funtion for the filter search */
customSearch: (row: unknown, filter: string) => boolean;
/** Whether the column is sticky or not */
sticky: boolean;
/** Make header selectable */
headerSelectable;
/** Adds native attributes to th :th-attrs="(column)" => ({})" */
thAttrs: (column: Column) => object;
/** Adds native attributes to td :td-attrs="(row, column)" => ({})" */
tdAttrs: (row: unknown, column: Column) => object;
}>;
export type Column = ComponentProps<typeof TableColumn>;

export type TableColumnSlots = ComponentSlots<typeof TableColumn>;

export type TableColumnComponent = Column & {
$el: ComponentPublicInstance;
$slots: TableColumnSlots;
style: StyleValue;
thAttrsData: object;
tdAttrsData: Array<object>;
isHeaderUnselectable: boolean;
};

export type TableColumn = Omit<ProviderItem, "data"> & TableColumnComponent;
export type TableColumn = Omit<ProviderItem, "data"> &
TableColumnComponent & {
thAttrsData: object;
tdAttrsData: Array<object>;
};

0 comments on commit 8f83b71

Please sign in to comment.