diff --git a/.changeset/itchy-moments-live.md b/.changeset/itchy-moments-live.md new file mode 100644 index 0000000000..a3f55ff431 --- /dev/null +++ b/.changeset/itchy-moments-live.md @@ -0,0 +1,5 @@ +--- +"sit-onyx": minor +--- + +Implemented basic expandable row feature for DataGrid diff --git a/packages/sit-onyx/src/components/OnyxDataGrid/examples/ExpandableRowExample.vue b/packages/sit-onyx/src/components/OnyxDataGrid/examples/ExpandableRowExample.vue new file mode 100644 index 0000000000..3239573f77 --- /dev/null +++ b/packages/sit-onyx/src/components/OnyxDataGrid/examples/ExpandableRowExample.vue @@ -0,0 +1,34 @@ + + + diff --git a/packages/sit-onyx/src/components/OnyxDataGrid/examples/OnyxDataGridFeatureExamples.stories.ts b/packages/sit-onyx/src/components/OnyxDataGrid/examples/OnyxDataGridFeatureExamples.stories.ts index 011d77672e..2d41ca89a3 100644 --- a/packages/sit-onyx/src/components/OnyxDataGrid/examples/OnyxDataGridFeatureExamples.stories.ts +++ b/packages/sit-onyx/src/components/OnyxDataGrid/examples/OnyxDataGridFeatureExamples.stories.ts @@ -28,6 +28,10 @@ export const Editing: Story = { ...createAdvancedStoryExample("OnyxDataGrid", "EditingExample"), }; +export const ExpandableRow: Story = { + ...createAdvancedStoryExample("OnyxDataGrid", "ExpandableRowExample"), +}; + export const LazyLoading: Story = { ...createAdvancedStoryExample("OnyxDataGrid", "LazyLoadingExample"), }; diff --git a/packages/sit-onyx/src/components/OnyxDataGrid/features/all.ts b/packages/sit-onyx/src/components/OnyxDataGrid/features/all.ts index d7da80f05e..437a287388 100644 --- a/packages/sit-onyx/src/components/OnyxDataGrid/features/all.ts +++ b/packages/sit-onyx/src/components/OnyxDataGrid/features/all.ts @@ -1,3 +1,4 @@ +export * from "./expandableRows/types.js"; export * from "./filtering/types.js"; export * from "./hideColumns/types.js"; export * from "./pagination/types.js"; @@ -7,6 +8,7 @@ export * from "./sorting/types.js"; export * from "./stickyColumns/types.js"; export { useEditing } from "./editing/editing.js"; +export { useExpandableRows } from "./expandableRows/expandableRows.js"; export { useFiltering } from "./filtering/filtering.js"; export { useHideColumns } from "./hideColumns/hideColumns.js"; export { usePagination } from "./pagination/pagination.js"; diff --git a/packages/sit-onyx/src/components/OnyxDataGrid/features/expandableRows/expandableRows.ts b/packages/sit-onyx/src/components/OnyxDataGrid/features/expandableRows/expandableRows.ts new file mode 100644 index 0000000000..8d26bc91a1 --- /dev/null +++ b/packages/sit-onyx/src/components/OnyxDataGrid/features/expandableRows/expandableRows.ts @@ -0,0 +1,120 @@ +import { iconChevronDown, iconChevronUp } from "@sit-onyx/icons"; +import { h, ref } from "vue"; +import { DataGridFeatures } from "../../../../index.js"; +import OnyxSystemButton from "../../../OnyxSystemButton/OnyxSystemButton.vue"; +import { DataGridRowOptionsSymbol, type DataGridEntry } from "../../types.js"; +import { createFeature, type DataGridFeature, type InternalColumnConfig } from "../index.js"; +import type { UseExpandableRowsOptions } from "./types.js"; + +const EXPANDABLE_ROWS_FEATURE = Symbol("ExpandableRowsFeature"); +const EXPAND_BUTTON_COLUMN = Symbol("ExpandButtonColumn"); +const EXPAND_BUTTON_RENDERER = Symbol("ExpandButtonRenderer"); +const DETAILS_COLUMN = Symbol("DetailsColumn"); +const DETAILS_RENDERER = Symbol("DetailsRenderer"); + +export const useExpandableRows = ( + options: UseExpandableRowsOptions, +) => + createFeature(() => { + const columnConfig = ref[]>>([]); + + const expandedRows = ref>(new Set()); + const toggleExpanded = (id: PropertyKey) => { + if (expandedRows.value.has(id)) { + expandedRows.value.delete(id); + } else { + expandedRows.value.add(id); + } + }; + + /** + * Adds the detail row for expandedd columns + */ + const mapRow = (row: TEntry) => { + if (!expandedRows.value.has(row.id)) + return [ + // don't change anything + { + ...row, + [DataGridRowOptionsSymbol]: { + columns: columnConfig.value, + }, + }, + ]; + else + return [ + { + ...row, + [DataGridRowOptionsSymbol]: { + columns: columnConfig.value, + }, + }, + // add hidden row to keep striped pattern + { + [DataGridRowOptionsSymbol]: { + trAttributes: { + style: { display: "none" }, + }, + }, + }, + // add row for details + { + ...row, + id: row.id, + [DataGridRowOptionsSymbol]: { + columns: [{ key: DETAILS_COLUMN, type: { name: DETAILS_RENDERER } }], + }, + }, + ]; + }; + + return { + name: EXPANDABLE_ROWS_FEATURE, + watch: [expandedRows, columnConfig], + modifyColumns: { + func: (cols) => { + const config = [ + { + key: EXPAND_BUTTON_COLUMN, + label: "", + type: { name: EXPAND_BUTTON_RENDERER }, + width: "min-content", + }, + ...cols, + ] as InternalColumnConfig[]; + + // Store the column configuration with column for expand button for later reference + columnConfig.value = config; + return config; + }, + }, + mutation: { + func: (rows) => { + return rows.flatMap(mapRow); + }, + }, + + typeRenderer: { + [EXPAND_BUTTON_RENDERER]: DataGridFeatures.createTypeRenderer({ + cell: { + component: ({ row }) => + h(OnyxSystemButton, { + icon: expandedRows.value.has(row.id) ? iconChevronUp : iconChevronDown, + label: "", + onClick: () => { + toggleExpanded(row.id); + }, + }), + }, + }), + [DETAILS_RENDERER]: DataGridFeatures.createTypeRenderer({ + cell: { + tdAttributes: { + colspan: 99, + }, + component: ({ row }) => options.detailsComponent(row), + }, + }), + }, + }; + }) as DataGridFeature; diff --git a/packages/sit-onyx/src/components/OnyxDataGrid/features/expandableRows/types.ts b/packages/sit-onyx/src/components/OnyxDataGrid/features/expandableRows/types.ts new file mode 100644 index 0000000000..b598119325 --- /dev/null +++ b/packages/sit-onyx/src/components/OnyxDataGrid/features/expandableRows/types.ts @@ -0,0 +1,11 @@ +import type { Component } from "vue"; + +/** + * Options for rendering detail content for expanded rows. + */ +export type UseExpandableRowsOptions = { + /** + * Function to render the detail content. + */ + detailsComponent: (row: TEntry) => Component; +};