Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/modern-pumas-live.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"sit-onyx": minor
---

feat(OnyxDataGrid): Extended feature API with new 'enhanceCells' and 'enhanceRow' hooks

These allow modifying render details per cell/row.
The provided function is called for each cell/row, after the matching typeRenderer was applied.
5 changes: 5 additions & 0 deletions .changeset/silly-areas-judge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"sit-onyx": minor
---

feat(OnyxDataGrid): Added base row type 'select'
35 changes: 35 additions & 0 deletions .changeset/slick-years-tickle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
"sit-onyx": minor
---

feat(OnyxDataGrid): Implemented basic inline editing feature.

For all base column `typeRenderers` editing is supported.
To enable editing with custom types, you will need to implement two things:

1. Check for the `editable` prop in the metadata to decide if the cell should render in "display" or "edit" mode.
2. Implement or call the `onUpdate:modelValue` when the value is supposed to change through an edit.

Example

```ts
export const MY_CUSTOM_RENDERER = DataGridFeatures.createTypeRenderer({
header: { component: HeaderCell },
cell: {
component: ({ column, row, metadata, modelValue, ...rest }) =>
metadata?.editable
? h(EditingComponent, {
...rest,
label: `${column} with id ${row.id}`,
modelValue,
})
: h(DisplayComponent, { modelValue, ...metadata?.typeOptions }),
},
});
```

**Caveats:**

- Currently only basic editing is supported.
- Managing `editable` for individual cells or columns state must currently performed in the application. Per default all cells and rows are either editable or not editable.
- Filtering and Sorting features are always using the original value, not the edited value.
5 changes: 5 additions & 0 deletions .changeset/slow-cities-obey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"sit-onyx": patch
---

fix(OnyxDataGrid): Fix increased row height for columnType "boolean" caused by icon
38 changes: 20 additions & 18 deletions packages/headless/src/composables/dataGrid/createDataGrid.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ const LazyResolverFactory = ({
});

type CellIdentifier = {
rowId: string;
colKey: string;
rowId: PropertyKey;
colKey: PropertyKey;
};

export type LazyOptions<Lazy extends boolean> = Lazy extends true
Expand Down Expand Up @@ -122,7 +122,7 @@ export type CreateDataGridOptions<Lazy extends boolean> = {
*/
multiselectable?: boolean;
loading?: MaybeRefOrGetter<boolean>;
selectedCell?: Ref<CellIdentifier>;
selectedCell?: Ref<CellIdentifier | undefined>;
} & LazyOptions<Lazy>;

export type TrOptions<Lazy extends boolean> = {
Expand All @@ -147,7 +147,7 @@ export const createDataGrid = createBuilder(
: StaticResolver;

const labelId = useId();
const selectedCell = ref<CellIdentifier>();
const selectedCell = options.selectedCell || ref<CellIdentifier>();
const selectedCellEl = createElRef<HTMLElement>();

/**
Expand All @@ -156,7 +156,6 @@ export const createDataGrid = createBuilder(
const focusQueue = useLastSettled<Nullable<HTMLElement>>((success, cell) => {
if (success) {
cell?.focus();
cell?.scrollIntoView();
}
});

Expand All @@ -166,7 +165,7 @@ export const createDataGrid = createBuilder(
const busySet = useAllSettled();

const findFirstCell = () =>
tableElement.value?.querySelector<HTMLElement>(
tableElement.value?.querySelector?.<HTMLElement>(
`[${ROW_ID_DATA_ATTR}] [${COL_KEY_DATA_ATTR}]`,
);

Expand All @@ -191,7 +190,7 @@ export const createDataGrid = createBuilder(
}
};

let mutationObserver: MutationObserver;
let mutationObserver: Nullable<MutationObserver>;

onMounted(() => {
ensureTabTarget();
Expand All @@ -201,22 +200,23 @@ export const createDataGrid = createBuilder(
watch(
tableElement,
() => {
mutationObserver.disconnect();
if (tableElement.value) {
mutationObserver.observe(tableElement.value, {
childList: true,
attributes: true,
subtree: true,
attributeFilter: ["value"],
});
if (!tableElement.value) {
return;
}
mutationObserver?.disconnect();
mutationObserver?.observe(tableElement.value, {
childList: true,
attributes: true,
subtree: true,
attributeFilter: ["value"],
});
},
{ immediate: true },
);
});

onBeforeUnmount(() => {
mutationObserver.disconnect();
mutationObserver?.disconnect();
});

/**
Expand Down Expand Up @@ -267,6 +267,7 @@ export const createDataGrid = createBuilder(
} else {
return;
}
event.preventDefault();

// apply bounds
const maxRows = totalRows === "unknown" ? Infinity : totalRows - 1;
Expand Down Expand Up @@ -306,11 +307,12 @@ export const createDataGrid = createBuilder(
}),
td: computed(() => ({ rowId, colKey, colIndex }: TdOptions<Lazy>) => {
const isSelected =
colKey === selectedCell.value?.colKey && rowId === selectedCell.value?.rowId;
colKey.toString() === selectedCell.value?.colKey.toString() &&
rowId.toString() === selectedCell.value?.rowId.toString();
return {
tabindex: isSelected ? "0" : "-1",
ref: isSelected ? selectedCellEl : undefined,
[COL_KEY_DATA_ATTR]: colKey,
[COL_KEY_DATA_ATTR]: colKey.toString(), // TODO: handle symbols
"aria-colindex": colIndex == undefined ? undefined : colIndex + 1,
role: "cell",
};
Expand Down
1 change: 1 addition & 0 deletions packages/headless/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from "./composables/calendar/createCalendar.js";
export * from "./composables/comboBox/createComboBox.js";
export * from "./composables/dataGrid/createDataGrid.js";
export * from "./composables/helpers/useGlobalListener.js";
export * from "./composables/helpers/useOutsideClick.js";
export * from "./composables/listbox/createListbox.js";
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ import {
useSkeletonContext,
} from "../../composables/useSkeletonState.js";
import { injectI18n } from "../../i18n/index.js";
import type { DataGridScrollContainerAttributes, InternalDataGridSlots } from "../../index.js";
import type {
DataGridScrollContainerAttributes,
DataGridTableAttributes,
InternalDataGridSlots,
} from "../../index.js";
import { mergeVueProps } from "../../utils/attrs.js";
import { useForwardProps } from "../../utils/props.js";
import type { OnyxTableSlots, TableColumnGroup } from "../OnyxTable/types.js";
Expand Down Expand Up @@ -58,6 +62,7 @@ const renderColumns = shallowRef<DataGridRendererColumn<TEntry>[]>([]);
const renderRows = shallowRef<DataGridRendererRow<TEntry, DataGridMetadata>[]>([]);
const rendererColumnGroups = shallowRef<TableColumnGroup[]>();
const rendererScrollContainerAttributes = shallowRef<DataGridScrollContainerAttributes>();
const rendererTableAttributes = shallowRef<DataGridTableAttributes>();
const rendererSlots = shallowRef<InternalDataGridSlots>();

const { columns: columnConfig, data, features, columnGroups, async } = toRefs(props);
Expand All @@ -72,6 +77,7 @@ const createFeatureBuilderWatcher = ({
watchSources,
createRendererColumnGroups,
createScrollContainerAttributes,
createTableAttributes,
createSlots,
}: ReturnType<
typeof useDataGridFeatures<TEntry, TFeatureName, TTypeRenderer, TColumnGroup, TTypes, TFeatures>
Expand All @@ -83,6 +89,7 @@ const createFeatureBuilderWatcher = ({
renderRows.value = createRendererRows(data.value);
rendererColumnGroups.value = createRendererColumnGroups();
rendererScrollContainerAttributes.value = createScrollContainerAttributes();
rendererTableAttributes.value = createTableAttributes();
rendererSlots.value = createSlots();
},
{ immediate: true, deep: true },
Expand Down Expand Up @@ -115,6 +122,7 @@ watch(
:column-groups="rendererColumnGroups"
:columns="renderColumns"
:rows="renderRows"
:table-attrs="rendererTableAttributes"
:scroll-container-attrs="rendererScrollContainerAttributes"
>
<template v-for="(slot, slotName) in rendererSlots" :key="slotName" #[slotName]>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,12 @@ function getDummyColumn(columnNumber: number): DataGridRendererColumn<DataGridEn
*/
function getDummyCell(
id: string,
columnKey: keyof DataGridEntry,
column: keyof DataGridEntry,
): DataGridRendererCell<DataGridEntry> {
return {
component: (props) => h("span", props.row.id.toString()),
props: {
key: columnKey,
column,
row: {
id,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ export type DataGridRendererColumn<TEntry extends DataGridEntry, TProps = any> =
thAttributes?: ThHTMLAttributes;
};

export type DataGridRendererRowCells<
TEntry extends DataGridEntry,
TMetadata extends DataGridRendererCellMetadata = DataGridRendererCellMetadata,
> = Partial<Record<keyof TEntry, DataGridRendererCell<TEntry, TMetadata>>>;

/**
* Describes how a specific row is rendered in the data grid.
*/
Expand All @@ -72,7 +77,7 @@ export type DataGridRendererRow<
* Describes how a cell in a specific row is rendered in the data grid.
* Only cells that are defined in the columns will be rendered in the defined order.
*/
cells: Partial<Record<keyof TEntry, DataGridRendererCell<TEntry, TMetadata>>>;
cells: DataGridRendererRowCells<TEntry, TMetadata>;
/**
* Attributes that are bound directly to the `<tr>` element of the row.
*/
Expand Down Expand Up @@ -122,7 +127,7 @@ export type DataGridRendererCellComponentProps<
/**
* The key of the column for which the cell is rendered.
*/
key: keyof TEntry;
column: keyof TEntry;
/**
* Complete row data.
*/
Expand Down
Loading
Loading