From 1020ad372e4c8ce8e7cb5b9f70600a3307005f4e Mon Sep 17 00:00:00 2001 From: anserwaseem Date: Tue, 23 Sep 2025 16:34:33 +0500 Subject: [PATCH 1/2] enhance DataGrid with rowHoverStyle and hideSelectAll props for improved styling and functionality --- .changeset/big-kangaroos-enjoy.md | 5 +++++ .../runtime/src/widgets/DataGrid/DataGrid.tsx | 20 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 .changeset/big-kangaroos-enjoy.md diff --git a/.changeset/big-kangaroos-enjoy.md b/.changeset/big-kangaroos-enjoy.md new file mode 100644 index 000000000..17afe1b48 --- /dev/null +++ b/.changeset/big-kangaroos-enjoy.md @@ -0,0 +1,5 @@ +--- +"@ensembleui/react-runtime": patch +--- + +enhance DataGrid with rowHoverStyle and hideSelectAll props for improved styling and functionality diff --git a/packages/runtime/src/widgets/DataGrid/DataGrid.tsx b/packages/runtime/src/widgets/DataGrid/DataGrid.tsx index ce90bc336..77ccd5384 100644 --- a/packages/runtime/src/widgets/DataGrid/DataGrid.tsx +++ b/packages/runtime/src/widgets/DataGrid/DataGrid.tsx @@ -40,6 +40,7 @@ import type { EnsembleWidgetStyles, HasItemTemplate, } from "../../shared/types"; +import { getComponentStyles } from "../../shared/styles"; import { useEnsembleAction } from "../../runtime/hooks/useEnsembleAction"; import { EnsembleRuntime } from "../../runtime"; import { DataCell } from "./DataCell"; @@ -84,6 +85,7 @@ export interface DataGridRowTemplate { onTap?: EnsembleAction; children?: EnsembleWidget[]; styles?: EnsembleWidgetStyles; + rowHoverStyle?: EnsembleWidgetStyles; } & HasItemTemplate; } @@ -115,6 +117,7 @@ export type GridProps = { totalRows?: number; curPage?: number; virtual?: boolean; + hideSelectAll?: boolean; } & EnsembleWidgetProps; function djb2Hash(str: string): number { @@ -364,6 +367,7 @@ export const DataGrid: React.FC = (props) => { pageSize, curPage, widgetName, + rowHoverStyle: itemTemplate.template.properties.rowHoverStyle, }, props.id, { @@ -375,6 +379,7 @@ export const DataGrid: React.FC = (props) => { }, ); const headerStyle = values?.styles?.headerStyle; + const rowHoverStyle = values?.rowHoverStyle; useEffect(() => { setPageSize(values?.pageSize || 10); @@ -559,6 +564,7 @@ export const DataGrid: React.FC = (props) => { }, defaultSelectedRowKeys: values?.defaultSelectedRowKeys, selectedRowKeys: rowsKey.length ? rowsKey : undefined, + hideSelectAll: values?.hideSelectAll, } : undefined } @@ -702,6 +708,20 @@ export const DataGrid: React.FC = (props) => { !headerStyle?.borderBottom ? "border-bottom: none !important" : "" } } + + ${ + rowHoverStyle + ? `#${resolvedWidgetId} .ant-table-tbody > tr > td { + transition: all 0.2s ease-in-out; + } + #${resolvedWidgetId} .ant-table-tbody > tr:hover > td { + ${getComponentStyles("", rowHoverStyle, true, true)} + } + #${resolvedWidgetId} .ant-table-tbody > tr.ant-table-row:hover > td { + ${getComponentStyles("", rowHoverStyle, true, true)} + }` + : "" + } `} From 1e9847a9eb42eebe481036aa2130f560ff36ca8b Mon Sep 17 00:00:00 2001 From: anserwaseem Date: Tue, 23 Sep 2025 22:48:24 +0500 Subject: [PATCH 2/2] use ConfigProvider instead --- .changeset/big-kangaroos-enjoy.md | 2 +- .../src/ensemble/screens/widgets.yaml | 5 + packages/runtime/src/widgets/Collapsible.tsx | 6 + .../runtime/src/widgets/DataGrid/DataGrid.tsx | 308 +++++++++--------- packages/runtime/src/widgets/Switch.tsx | 3 +- 5 files changed, 171 insertions(+), 153 deletions(-) diff --git a/.changeset/big-kangaroos-enjoy.md b/.changeset/big-kangaroos-enjoy.md index 17afe1b48..00e777956 100644 --- a/.changeset/big-kangaroos-enjoy.md +++ b/.changeset/big-kangaroos-enjoy.md @@ -2,4 +2,4 @@ "@ensembleui/react-runtime": patch --- -enhance DataGrid with rowHoverStyle and hideSelectAll props for improved styling and functionality +enhance DataGrid with rowHoverBg and rowSelectedHoverBg styles diff --git a/apps/kitchen-sink/src/ensemble/screens/widgets.yaml b/apps/kitchen-sink/src/ensemble/screens/widgets.yaml index 5946ea866..f1fe97ae0 100644 --- a/apps/kitchen-sink/src/ensemble/screens/widgets.yaml +++ b/apps/kitchen-sink/src/ensemble/screens/widgets.yaml @@ -540,6 +540,9 @@ View: name: dummyData template: DataRow: + styles: + backgroundColor: lime + rowHoverBg: yellow children: - Text: id: ${'x_'+index} @@ -1131,6 +1134,8 @@ View: size: small value: true leadingText: Set to true and requred + htmlAttributes: + title: tooltip text - Card: styles: width: unset diff --git a/packages/runtime/src/widgets/Collapsible.tsx b/packages/runtime/src/widgets/Collapsible.tsx index 30b3843bf..19ce3913e 100644 --- a/packages/runtime/src/widgets/Collapsible.tsx +++ b/packages/runtime/src/widgets/Collapsible.tsx @@ -233,6 +233,12 @@ export const Collapsible: React.FC = (props) => { expandIconPosition={props.expandIconPosition} items={[...collapsibleItems, ...templateItems]} onChange={handleCollapsibleChange} + style={{ + ...values?.styles, + ...(values?.styles?.visible === false + ? { display: "none" } + : undefined), + }} /> ); diff --git a/packages/runtime/src/widgets/DataGrid/DataGrid.tsx b/packages/runtime/src/widgets/DataGrid/DataGrid.tsx index 77ccd5384..49cbef07c 100644 --- a/packages/runtime/src/widgets/DataGrid/DataGrid.tsx +++ b/packages/runtime/src/widgets/DataGrid/DataGrid.tsx @@ -1,4 +1,4 @@ -import { Table, type TableProps } from "antd"; +import { Table, type TableProps, ConfigProvider } from "antd"; import type { SorterResult } from "antd/es/table/interface"; import isEqual from "react-fast-compare"; import { @@ -65,7 +65,13 @@ interface DataColumn { visible?: boolean; } -export interface DataGridStyles extends Partial { +export interface DataGridStyles + extends Partial< + EnsembleWidgetStyles & { + rowHoverBg?: Expression; + rowSelectedHoverBg?: Expression; + } + > { headerStyle?: { backgroundColor?: Expression; fontSize?: Expression; @@ -85,7 +91,6 @@ export interface DataGridRowTemplate { onTap?: EnsembleAction; children?: EnsembleWidget[]; styles?: EnsembleWidgetStyles; - rowHoverStyle?: EnsembleWidgetStyles; } & HasItemTemplate; } @@ -117,7 +122,6 @@ export type GridProps = { totalRows?: number; curPage?: number; virtual?: boolean; - hideSelectAll?: boolean; } & EnsembleWidgetProps; function djb2Hash(str: string): number { @@ -367,7 +371,6 @@ export const DataGrid: React.FC = (props) => { pageSize, curPage, widgetName, - rowHoverStyle: itemTemplate.template.properties.rowHoverStyle, }, props.id, { @@ -379,7 +382,6 @@ export const DataGrid: React.FC = (props) => { }, ); const headerStyle = values?.styles?.headerStyle; - const rowHoverStyle = values?.rowHoverStyle; useEffect(() => { setPageSize(values?.pageSize || 10); @@ -517,140 +519,158 @@ export const DataGrid: React.FC = (props) => { return (
- ({ - "data-index": recordIndex, - "data-record": record, - "data-styles": itemTemplate.template.properties.styles, - onClick: itemTemplate.template.properties.onTap - ? (): unknown => onTapActionCallback(record, recordIndex) - : undefined, - })} - pagination={paginationObject} - rowKey={(data: unknown) => { - const identifier: string = evaluate( - defaultScreenContext, - itemTemplate.key, - { - [itemTemplate.name]: get(data, itemTemplate.name) as unknown, + { - setRowsKey(selectedRowKeys); - setRowsSelected(selectedRows); - if (rest.onRowsSelected) { - onRowsSelectedCallback(selectedRowKeys, selectedRows); - } - }, - getCheckboxProps: (record) => { - return { - disabled: handleRowSelectableOrNot(record), - }; - }, - defaultSelectedRowKeys: values?.defaultSelectedRowKeys, - selectedRowKeys: rowsKey.length ? rowsKey : undefined, - hideSelectAll: values?.hideSelectAll, - } - : undefined - } - scroll={ - values?.scroll - ? { - y: values.scroll.scrollHeight, - x: values.scroll.scrollWidth || "max-content", - } - : undefined - } - style={{ - width: "100%", - ...values?.styles, - ...(values?.styles?.visible === false - ? { display: "none" } - : undefined), + }, }} - tableLayout="auto" - virtual={values?.virtual} > - {dataColumns.map((col, colIndex) => { - return ( - ({ - text: label, - value, - }))} - hidden={col.visible === false} - key={colIndex} - minWidth={col.width ?? 100} - onFilter={ - col.filter?.onFilter - ? (value, record): boolean => - Boolean( - evaluate(defaultScreenContext, col.filter?.onFilter, { - value, - record, - [itemTemplate.name]: get( - record, - itemTemplate.name, - ) as unknown, - }), - ) - : undefined - } - onHeaderCell={ - values?.allowResizableColumns - ? () => ({ - width: col.width, - onResize: handleResize( - colIndex, - ) as ReactEventHandler, - }) - : undefined - } - render={(_, record, rowIndex): ReactElement => { - return ( - - ); - }} - shouldCellUpdate={(record, prev) => !isEqual(record, prev)} - sorter={ - col.sort?.compareFn - ? (a, b): number => - Number( - evaluate(defaultScreenContext, col.sort?.compareFn, { - a, - b, - }), - ) - : undefined - } - title={col.label as string | React.ReactNode} - width={col.width} - /> - ); - })} -
+ ({ + "data-index": recordIndex, + "data-record": record, + "data-styles": itemTemplate.template.properties.styles, + onClick: itemTemplate.template.properties.onTap + ? (): unknown => onTapActionCallback(record, recordIndex) + : undefined, + })} + pagination={paginationObject} + rowKey={(data: unknown) => { + const identifier: string = evaluate( + defaultScreenContext, + itemTemplate.key, + { + [itemTemplate.name]: get(data, itemTemplate.name) as unknown, + }, + ); + if (identifier) { + return identifier; + } + const res = djb2Hash(JSON.stringify(data)); + return String(res); + }} + rowSelection={ + allowSelection + ? { + columnWidth: values?.selectionColWidth, + type: selectionType, + onChange: (selectedRowKeys, selectedRows): void => { + setRowsKey(selectedRowKeys); + setRowsSelected(selectedRows); + if (rest.onRowsSelected) { + onRowsSelectedCallback(selectedRowKeys, selectedRows); + } + }, + getCheckboxProps: (record) => { + return { + disabled: handleRowSelectableOrNot(record), + }; + }, + defaultSelectedRowKeys: values?.defaultSelectedRowKeys, + selectedRowKeys: rowsKey.length ? rowsKey : undefined, + } + : undefined + } + scroll={ + values?.scroll + ? { + y: values.scroll.scrollHeight, + x: values.scroll.scrollWidth || "max-content", + } + : undefined + } + style={{ + width: "100%", + ...values?.styles, + ...(values?.styles?.visible === false + ? { display: "none" } + : undefined), + }} + tableLayout="auto" + virtual={values?.virtual} + > + {dataColumns.map((col, colIndex) => { + return ( + ({ + text: label, + value, + }))} + hidden={col.visible === false} + key={colIndex} + minWidth={col.width ?? 100} + onFilter={ + col.filter?.onFilter + ? (value, record): boolean => + Boolean( + evaluate( + defaultScreenContext, + col.filter?.onFilter, + { + value, + record, + [itemTemplate.name]: get( + record, + itemTemplate.name, + ) as unknown, + }, + ), + ) + : undefined + } + onHeaderCell={ + values?.allowResizableColumns + ? () => ({ + width: col.width, + onResize: handleResize( + colIndex, + ) as ReactEventHandler, + }) + : undefined + } + render={(_, record, rowIndex): ReactElement => { + return ( + + ); + }} + shouldCellUpdate={(record, prev) => !isEqual(record, prev)} + sorter={ + col.sort?.compareFn + ? (a, b): number => + Number( + evaluate( + defaultScreenContext, + col.sort?.compareFn, + { + a, + b, + }, + ), + ) + : undefined + } + title={col.label as string | React.ReactNode} + width={col.width} + /> + ); + })} +
+
diff --git a/packages/runtime/src/widgets/Switch.tsx b/packages/runtime/src/widgets/Switch.tsx index 39621b08e..a9ec41664 100644 --- a/packages/runtime/src/widgets/Switch.tsx +++ b/packages/runtime/src/widgets/Switch.tsx @@ -36,7 +36,7 @@ export type SwitchProps = { export const SwitchWidget: React.FC = (props) => { const [value, setValue] = useState(props.value); - const { values } = useRegisterBindings( + const { values, rootRef } = useRegisterBindings( { ...props, value, widgetName }, props.id, { @@ -110,6 +110,7 @@ export const SwitchWidget: React.FC = (props) => { loading={values?.loading} onChange={handleChange} size={values?.size} + ref={rootRef} /> {trailingContent}