From 26640829f7d077855af3686010057b10b16a3cc2 Mon Sep 17 00:00:00 2001
From: Faran Javed <faran1997@outlook.com>
Date: Tue, 5 Aug 2025 20:10:56 +0500
Subject: [PATCH 1/7] clone original Table Comp

---
 .../tableLiteComp/column/columnTypeComp.tsx   |  170 +++
 .../column/columnTypeCompBuilder.tsx          |  212 +++
 .../columnTypeComps/ColumnNumberComp.tsx      |  224 ++++
 .../columnTypeComps/columnAvatarsComp.tsx     |  251 ++++
 .../columnTypeComps/columnBooleanComp.tsx     |  201 +++
 .../column/columnTypeComps/columnDateComp.tsx |  283 ++++
 .../columnTypeComps/columnDateTimeComp.tsx    |   90 ++
 .../columnTypeComps/columnDropdownComp.tsx    |  200 +++
 .../column/columnTypeComps/columnImgComp.tsx  |  143 ++
 .../column/columnTypeComps/columnLinkComp.tsx |  145 ++
 .../columnTypeComps/columnLinksComp.tsx       |  139 ++
 .../columnTypeComps/columnMarkdownComp.tsx    |  128 ++
 .../columnTypeComps/columnProgressComp.tsx    |  166 +++
 .../columnTypeComps/columnRatingComp.tsx      |  139 ++
 .../columnTypeComps/columnSelectComp.tsx      |  232 ++++
 .../columnTypeComps/columnStatusComp.tsx      |  192 +++
 .../columnTypeComps/columnSwitchComp.tsx      |  168 +++
 .../column/columnTypeComps/columnTagsComp.tsx |  465 +++++++
 .../column/columnTypeComps/columnTimeComp.tsx |  193 +++
 .../column/columnTypeComps/simpleTextComp.tsx |  121 ++
 .../column/simpleColumnTypeComps.tsx          |  129 ++
 .../tableLiteComp/column/tableColumnComp.tsx  |  488 +++++++
 .../column/tableColumnListComp.tsx            |  202 +++
 .../column/tableSummaryColumnComp.tsx         |  205 +++
 .../comps/tableLiteComp/expansionControl.tsx  |  116 ++
 .../src/comps/comps/tableLiteComp/index.tsx   |    1 +
 .../comps/tableLiteComp/mockTableComp.tsx     |   72 +
 .../comps/tableLiteComp/paginationControl.tsx |   90 ++
 .../comps/tableLiteComp/selectionControl.tsx  |  134 ++
 .../comps/tableLiteComp/tableComp.test.tsx    |  213 +++
 .../comps/comps/tableLiteComp/tableComp.tsx   | 1019 ++++++++++++++
 .../comps/tableLiteComp/tableCompView.tsx     | 1176 +++++++++++++++++
 .../comps/tableLiteComp/tableContext.tsx      |   12 +
 .../tableLiteComp/tableDynamicColumn.test.tsx |  241 ++++
 .../comps/tableLiteComp/tablePropertyView.tsx |  650 +++++++++
 .../comps/tableLiteComp/tableSummaryComp.tsx  |  291 ++++
 .../comps/tableLiteComp/tableToolbarComp.tsx  |  947 +++++++++++++
 .../comps/comps/tableLiteComp/tableTypes.tsx  |  282 ++++
 .../comps/comps/tableLiteComp/tableUtils.tsx  |  492 +++++++
 39 files changed, 10622 insertions(+)
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeCompBuilder.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/ColumnNumberComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnAvatarsComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnBooleanComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnDateComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnDateTimeComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnDropdownComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnImgComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnLinkComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnLinksComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnMarkdownComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnProgressComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnRatingComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnSelectComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnStatusComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnSwitchComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnTagsComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnTimeComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/simpleTextComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/simpleColumnTypeComps.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableColumnComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableColumnListComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableSummaryColumnComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/expansionControl.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/index.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/mockTableComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/paginationControl.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/selectionControl.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.test.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/tableCompView.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/tableContext.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/tableDynamicColumn.test.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/tablePropertyView.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/tableSummaryComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/tableToolbarComp.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/tableTypes.tsx
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/tableUtils.tsx

diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComp.tsx
new file mode 100644
index 0000000000..ff3df44c4e
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComp.tsx
@@ -0,0 +1,170 @@
+import { CellProps } from "components/table/EditableCell";
+import { DateTimeComp } from "comps/comps/tableComp/column/columnTypeComps/columnDateTimeComp";
+import { TimeComp } from "./columnTypeComps/columnTimeComp";
+import { ButtonComp } from "comps/comps/tableComp/column/simpleColumnTypeComps";
+import { withType } from "comps/generators";
+import { trans } from "i18n";
+import { Dropdown } from "lowcoder-design/src/components/Dropdown";
+import { BooleanComp } from "./columnTypeComps/columnBooleanComp";
+import { SwitchComp } from "./columnTypeComps/columnSwitchComp";
+import { DateComp } from "./columnTypeComps/columnDateComp";
+import { ImageComp } from "./columnTypeComps/columnImgComp";
+import { LinkComp } from "./columnTypeComps/columnLinkComp";
+import { ColumnLinksComp } from "./columnTypeComps/columnLinksComp";
+import { ColumnMarkdownComp } from "./columnTypeComps/columnMarkdownComp";
+import { ProgressComp } from "./columnTypeComps/columnProgressComp";
+import { RatingComp } from "./columnTypeComps/columnRatingComp";
+import { BadgeStatusComp } from "./columnTypeComps/columnStatusComp";
+import { ColumnTagsComp } from "./columnTypeComps/columnTagsComp";
+import { ColumnSelectComp } from "./columnTypeComps/columnSelectComp";
+import { SimpleTextComp } from "./columnTypeComps/simpleTextComp";
+import { ColumnNumberComp } from "./columnTypeComps/ColumnNumberComp";
+
+import { ColumnAvatarsComp } from "./columnTypeComps/columnAvatarsComp";
+import { ColumnDropdownComp } from "./columnTypeComps/columnDropdownComp";
+
+const actionOptions = [
+  {
+    label: trans("table.avatars"),
+    value: "avatars",
+  },
+  {
+    label: trans("table.text"),
+    value: "text",
+  },
+  {
+    label: trans("table.number"),
+    value: "number",
+  },
+  {
+    label: trans("table.link"),
+    value: "link",
+  },
+  {
+    label: trans("table.links"),
+    value: "links",
+  },
+  {
+    label: trans("table.tag"),
+    value: "tag",
+  },
+  {
+    label: trans("table.select"),
+    value: "select",
+  },
+  {
+    label: trans("table.dropdown"),
+    value: "dropdown",
+  },
+  {
+    label: trans("table.badgeStatus"),
+    value: "badgeStatus",
+  },
+  {
+    label: trans("table.button"),
+    value: "button",
+  },
+  {
+    label: trans("table.image"),
+    value: "image",
+  },
+  {
+    label: trans("table.time"),
+    value: "time",
+  },
+
+  {
+    label: trans("table.date"),
+    value: "date",
+  },
+  {
+    label: trans("table.dateTime"),
+    value: "dateTime",
+  },
+  {
+    label: "Markdown",
+    value: "markdown",
+  },
+  {
+    label: trans("table.boolean"),
+    value: "boolean",
+  },
+  {
+    label: trans("table.switch"),
+    value: "switch",
+  },
+  {
+    label: trans("table.rating"),
+    value: "rating",
+  },
+  {
+    label: trans("table.progress"),
+    value: "progress",
+  },
+] as const;
+
+export const ColumnTypeCompMap = {
+  avatars: ColumnAvatarsComp,
+  text: SimpleTextComp,
+  number: ColumnNumberComp,
+  button: ButtonComp,
+  badgeStatus: BadgeStatusComp,
+  link: LinkComp,
+  tag: ColumnTagsComp,
+  select: ColumnSelectComp,
+  dropdown: ColumnDropdownComp,
+  links: ColumnLinksComp,
+  image: ImageComp,
+  markdown: ColumnMarkdownComp,
+  dateTime: DateTimeComp,
+  boolean: BooleanComp,
+  switch: SwitchComp,
+  rating: RatingComp,
+  progress: ProgressComp,
+  date: DateComp,
+  time: TimeComp,
+};
+
+type ColumnTypeMapType = typeof ColumnTypeCompMap;
+export type ColumnTypeKeys = keyof ColumnTypeMapType;
+
+const TypedColumnTypeComp = withType(ColumnTypeCompMap, "text");
+
+export class ColumnTypeComp extends TypedColumnTypeComp {
+  override getView() {
+    const childView = this.children.comp.getView();
+    return {
+      view: (cellProps: CellProps) => {
+        return childView(cellProps);
+      },
+      value: this.children.comp.getDisplayValue(),
+    };
+  }
+
+  private handleTypeChange: (value: ColumnTypeKeys) => void = (value) => {
+    // Keep the previous text value, some components do not have text, the default value is currentCell
+    let textRawData = "{{currentCell}}";
+    if (this.children.comp.children.hasOwnProperty("text")) {
+      textRawData = (this.children.comp.children as any).text.toJsonValue();
+    }
+    this.dispatchChangeValueAction({
+      compType: value,
+      comp: { text: textRawData },
+    } as any);
+  }
+
+  override getPropertyView() {
+    return (
+      <>
+        <Dropdown
+          showSearch={true}
+          value={this.children.compType.getView()}
+          options={actionOptions}
+          label={trans("table.columnType")}
+          onChange={this.handleTypeChange}
+        />
+        {this.children.comp.getPropertyView()}
+      </>
+    );
+  }
+}
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeCompBuilder.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeCompBuilder.tsx
new file mode 100644
index 0000000000..b401761d26
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeCompBuilder.tsx
@@ -0,0 +1,212 @@
+import {
+  CellViewReturn,
+  EditableCell,
+  EditViewFn,
+  TABLE_EDITABLE_SWITCH_ON,
+} from "components/table/EditableCell";
+import { stateComp } from "comps/generators";
+import {
+  MultiCompBuilder,
+  PropertyViewFnTypeForComp,
+  ToConstructor,
+  ViewFnTypeForComp,
+} from "comps/generators/multi";
+import _ from "lodash";
+import {
+  CompConstructor,
+  ConstructorToNodeType,
+  fromRecord,
+  NodeToValue,
+  RecordConstructorToComp,
+  withFunction,
+} from "lowcoder-core";
+import { ReactNode } from "react";
+import { JSONValue } from "util/jsonTypes";
+
+export const __COLUMN_DISPLAY_VALUE_FN = "__COLUMN_DISPLAY_VALUE_FN";
+
+type RecordConstructorToNodeValue<T> = {
+  [K in keyof T]: NodeToValue<ConstructorToNodeType<T[K]>>;
+};
+
+type ViewValueFnType<ChildrenCtorMap extends Record<string, CompConstructor<unknown>>> = (
+  nodeValue: RecordConstructorToNodeValue<ChildrenCtorMap>
+) => JSONValue;
+
+type NewChildrenCtorMap<ChildrenCtorMap, T extends JSONValue> = ChildrenCtorMap & {
+  changeValue: ReturnType<typeof stateComp<T | null>>;
+};
+
+export type ColumnTypeViewFn<ChildrenCtroMap, T extends JSONValue, ViewReturn> = ViewFnTypeForComp<
+  ViewReturn,
+  RecordConstructorToComp<NewChildrenCtorMap<ChildrenCtroMap, T>>
+>;
+
+export class ColumnTypeCompBuilder<
+  ChildrenCtorMap extends Record<string, CompConstructor<unknown>>,
+  T extends JSONValue = JSONValue
+> {
+  private childrenMap: NewChildrenCtorMap<ChildrenCtorMap, T>;
+  private propertyViewFn?: PropertyViewFnTypeForComp<
+    RecordConstructorToComp<NewChildrenCtorMap<ChildrenCtorMap, T>>
+  >;
+  private stylePropertyViewFn?: PropertyViewFnTypeForComp<
+    RecordConstructorToComp<NewChildrenCtorMap<ChildrenCtorMap, T>>
+  >;
+  private editViewFn?: EditViewFn<T>;
+  private cleanupFunctions: (() => void)[] = [];
+
+  constructor(
+    childrenMap: ChildrenCtorMap,
+    private viewFn: ColumnTypeViewFn<ChildrenCtorMap, T, ReactNode>,
+    private displayValueFn: ViewValueFnType<ChildrenCtorMap>,
+    private baseValueFn?: ColumnTypeViewFn<ChildrenCtorMap, T, T>
+  ) {
+    this.childrenMap = { ...childrenMap, changeValue: stateComp<T | null>(null) };
+  }
+
+  setEditViewFn(editViewFn: NonNullable<typeof this.editViewFn>) {
+    if (TABLE_EDITABLE_SWITCH_ON) {
+      this.editViewFn = editViewFn;
+    }
+    return this;
+  }
+
+  setPropertyViewFn(
+    propertyViewFn: PropertyViewFnTypeForComp<
+      RecordConstructorToComp<NewChildrenCtorMap<ChildrenCtorMap, T>>
+    >
+  ) {
+    this.propertyViewFn = propertyViewFn;
+    return this;
+  }
+
+  setStylePropertyViewFn(
+    stylePropertyViewFn: PropertyViewFnTypeForComp<
+      RecordConstructorToComp<NewChildrenCtorMap<ChildrenCtorMap, T>>
+    >
+  ) {
+    this.stylePropertyViewFn = stylePropertyViewFn;
+    return this;
+  }
+
+  build() {
+    if (!this.propertyViewFn) {
+      throw new Error("need property view fn");
+    }
+
+    // Memoize the props processing
+    const memoizedViewFn = _.memoize(
+      (props: any, dispatch: any) => {
+          const baseValue = this.baseValueFn?.(props, dispatch);
+          const normalView = this.viewFn(props, dispatch);
+          return (
+            <EditableCell<T>
+              {...props}
+              normalView={normalView}
+              dispatch={dispatch}
+              baseValue={baseValue}
+              changeValue={props.changeValue as any}
+              editViewFn={this.editViewFn}
+            />
+          );
+      },
+      (props) => {
+        let safeOptions = [];
+        let safeAvatars = [];
+        if(props.options) {
+          safeOptions = props.options.map((option: Record<string, any>) => {
+            const {prefixIcon, suffixIcon, ...safeOption} = option;
+            return safeOption;
+          })
+        }
+        if(props.avatars) {
+          safeAvatars = props.avatars.map((avatar: Record<string, any>) => {
+            const {AvatarIcon, ...safeAvatar} = avatar;
+            return safeAvatar;
+          })
+        }
+        const {
+          prefixIcon,
+          suffixIcon,
+          iconFalse,
+          iconTrue,
+          iconNull,
+          tagColors,
+          options,
+          avatars,
+          ...safeProps
+        } = props;
+        return safeProps;
+      }
+    );
+
+    const viewFn: ColumnTypeViewFn<ChildrenCtorMap, T, CellViewReturn> =
+      (props, dispatch): CellViewReturn =>
+      (cellProps) => memoizedViewFn({ ...props, ...cellProps } as any, dispatch);
+
+    const ColumnTypeCompTmp = new MultiCompBuilder(
+      this.childrenMap as ToConstructor<
+        RecordConstructorToComp<NewChildrenCtorMap<ChildrenCtorMap, T>>
+      >,
+      viewFn
+    )
+      .setPropertyViewFn(this.propertyViewFn)
+      .build();
+
+    const displayValueFn = this.displayValueFn;
+    const editViewFn = this.editViewFn;
+
+    return class extends ColumnTypeCompTmp {
+      // table cell data
+      private _displayValue: JSONValue = null;
+      private cleanupFunctions: (() => void)[] = [];
+      constructor(props: any) {
+        super(props);
+        this.cleanupFunctions.push(() => {
+          this._displayValue = null;
+          memoizedViewFn.cache.clear?.();
+        });
+      }
+
+      override extraNode() {
+        return {
+          node: {
+            [__COLUMN_DISPLAY_VALUE_FN]: withFunction(
+              fromRecord(this.childrenNode()),
+              () => displayValueFn
+            ),
+          },
+          updateNodeFields: (value: any) => {
+            const displayValueFunc = value[__COLUMN_DISPLAY_VALUE_FN];
+            this._displayValue = displayValueFunc(value);
+            return { displayValue: this._displayValue };
+          },
+        };
+      }
+
+      /**
+       * Get the data actually displayed by the table cell
+       */
+      getDisplayValue() {
+        return this._displayValue;
+      }
+
+      static canBeEditable() {
+        return !_.isNil(editViewFn);
+      }
+
+      componentWillUnmount() {
+        // Cleanup all registered cleanup functions
+        this.cleanupFunctions.forEach(cleanup => cleanup());
+        this.cleanupFunctions = [];
+      }
+    };
+  }
+
+  // Cleanup method to be called when the builder is no longer needed
+  cleanup() {
+    this.cleanupFunctions.forEach(cleanup => cleanup());
+    this.cleanupFunctions = [];
+  }
+}
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/ColumnNumberComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/ColumnNumberComp.tsx
new file mode 100644
index 0000000000..619b42674f
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/ColumnNumberComp.tsx
@@ -0,0 +1,224 @@
+import React, { useState, useRef, useEffect, useCallback, useMemo, ReactNode } from "react";
+import { default as InputNumber } from "antd/es/input-number";
+import { NumberControl, RangeControl, StringControl } from "comps/controls/codeControl";
+import { BoolControl } from "comps/controls/boolControl";
+import { trans } from "i18n";
+import { ColumnTypeCompBuilder, ColumnTypeViewFn } from "../columnTypeCompBuilder";
+import { ColumnValueTooltip } from "../simpleColumnTypeComps";
+import { withDefault } from "comps/generators";
+import styled from "styled-components";
+import { IconControl } from "comps/controls/iconControl";
+import { hasIcon } from "comps/utils";
+import { clickEvent, eventHandlerControl, doubleClickEvent } from "comps/controls/eventHandlerControl";
+import { useCompClickEventHandler } from "@lowcoder-ee/comps/utils/useCompClickEventHandler";
+
+const InputNumberWrapper = styled.div`
+  .ant-input-number  {
+    width: 100%;
+    border-radius: 0;
+    background: transparent !important;
+    // padding: 0 !important;
+    box-shadow: none;
+
+    input {
+      padding: 0;
+      border-radius: 0;
+    }
+  }
+`;
+
+const NumberViewWrapper = styled.div`
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  gap: 4px;
+`;
+
+const NumberEventOptions = [clickEvent, doubleClickEvent] as const;
+
+const childrenMap = {
+  text: NumberControl,
+  step: withDefault(NumberControl, 1),
+  precision: RangeControl.closed(0, 20, 0),
+  float: BoolControl,
+  prefix: StringControl,
+  prefixIcon: IconControl,
+  suffixIcon: IconControl,
+  suffix: StringControl,
+  onEvent: eventHandlerControl(NumberEventOptions),
+};
+
+const getBaseValue: ColumnTypeViewFn<typeof childrenMap, number, number> = (props) => props.text;
+
+type NumberViewProps = {
+  value: number;
+  prefix: string;
+  suffix: string;
+  prefixIcon: ReactNode;
+  suffixIcon: ReactNode;
+  float: boolean;
+  precision: number;
+  onEvent?: (eventName: string) => void;
+};
+
+type NumberEditProps = {
+  value: number;
+  onChange: (value: number) => void;
+  onChangeEnd: () => void;
+  step: number;
+  precision: number;
+  float: boolean;
+};
+
+const ColumnNumberView = React.memo((props: NumberViewProps) => {
+  const handleClickEvent = useCompClickEventHandler({onEvent: props.onEvent ?? (() => {})})
+  
+  const formattedValue = useMemo(() => {
+    let result = !props.float ? Math.floor(props.value) : props.value;
+    if (props.float) {
+      result = Number(result.toFixed(props.precision + 1));
+    }
+    return result;
+  }, [props.value, props.float, props.precision]);
+
+  const handleClick = useCallback(() => {
+    handleClickEvent()
+  }, [props.onEvent]);
+
+  return (
+    <NumberViewWrapper onClick={handleClick}>
+      {hasIcon(props.prefixIcon) && (
+        <span>{props.prefixIcon}</span>
+      )}
+      <span>{props.prefix + formattedValue + props.suffix}</span>
+      {hasIcon(props.suffixIcon) && (
+        <span>{props.suffixIcon}</span>
+      )}
+    </NumberViewWrapper>
+  );
+});
+
+ColumnNumberView.displayName = 'ColumnNumberView';
+
+
+const ColumnNumberEdit = React.memo((props: NumberEditProps) => {
+  const [currentValue, setCurrentValue] = useState(props.value);
+  const mountedRef = useRef(true);
+
+  // Cleanup on unmount
+  useEffect(() => {
+    return () => {
+      mountedRef.current = false;
+      setCurrentValue(0);
+    };
+  }, []);
+
+  const handleChange = useCallback((value: string | number | null) => {
+    if (!mountedRef.current) return;
+    const newValue = typeof value === 'number' ? value : 0;
+    const finalValue = !props.float ? Math.floor(newValue) : newValue;
+    props.onChange(finalValue);
+    setCurrentValue(finalValue);
+  }, [props.onChange, props.float]);
+
+  const handleBlur = useCallback(() => {
+    if (!mountedRef.current) return;
+    props.onChangeEnd();
+  }, [props.onChangeEnd]);
+
+  const handlePressEnter = useCallback(() => {
+    if (!mountedRef.current) return;
+    props.onChangeEnd();
+  }, [props.onChangeEnd]);
+
+  return (
+    <InputNumberWrapper>
+      <InputNumber
+        step={props.step}
+        value={currentValue}
+        autoFocus
+        variant="borderless"
+        onChange={handleChange}
+        precision={props.float ? props.precision : 0}
+        onBlur={handleBlur}
+        onPressEnter={handlePressEnter}
+      />
+    </InputNumberWrapper>
+  );
+});
+
+ColumnNumberEdit.displayName = 'NumberEdit';
+
+export const ColumnNumberComp = (function () {
+  return new ColumnTypeCompBuilder(
+    childrenMap,
+    (props, dispatch) => {
+      const value = props.changeValue ?? getBaseValue(props, dispatch);
+      return <ColumnNumberView value={value} {...props} />;
+    },
+    (nodeValue) => nodeValue.text.value,
+    getBaseValue
+  )
+    .setEditViewFn((props) => {
+      const { value, onChange, onChangeEnd, otherProps } = props;
+      return (
+        <ColumnNumberEdit
+          value={value}
+          onChange={onChange}
+          onChangeEnd={onChangeEnd}
+          step={otherProps?.step ?? 1}
+          precision={otherProps?.precision ?? 0}
+          float={otherProps?.float ?? false}
+        />
+      );
+    })
+    .setPropertyViewFn((children) => {
+      return (
+        <>
+          {children.text.propertyView({
+            label: trans("table.columnValue"),
+            tooltip: ColumnValueTooltip,
+          })}
+          {children.step.propertyView({
+            label: trans("table.numberStep"),
+            tooltip: trans("table.numberStepTooltip"),
+            onFocus: (focused) => {
+              if (!focused) {
+                const value = children.step.getView();
+                const isFloat = children.float.getView();
+                const newValue = !isFloat ? Math.floor(value) : value;
+                children.step.dispatchChangeValueAction(String(newValue));
+              }
+            }
+          })}
+          {children.float.getView() && (
+            children.precision.propertyView({
+              label: trans("table.precision"),
+            })
+          )}
+          {children.prefix.propertyView({
+            label: trans("table.prefix"),
+          })}
+          {children.prefixIcon.propertyView({
+            label: trans("button.prefixIcon"),
+          })}
+          {children.suffix.propertyView({
+            label: trans("table.suffix"),
+          })}
+          {children.suffixIcon.propertyView({
+            label: trans("button.suffixIcon"),
+          })}
+          {children.float.propertyView({
+            label: trans("table.float"),
+            onChange: (isFloat) => {
+              const value = children.step.getView();
+              const newValue = !isFloat ? Math.floor(value) : value;
+              children.step.dispatchChangeValueAction(String(newValue));
+            }
+          })}
+          {children.onEvent.propertyView()}
+        </>
+      );
+    })
+    .build();
+})();
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnAvatarsComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnAvatarsComp.tsx
new file mode 100644
index 0000000000..f02ee19943
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnAvatarsComp.tsx
@@ -0,0 +1,251 @@
+import { ColumnTypeCompBuilder } from "comps/comps/tableComp/column/columnTypeCompBuilder";
+import { StringControl } from "comps/controls/codeControl";
+import { MultiCompBuilder, stateComp, withDefault } from "comps/generators";
+import { trans } from "i18n";
+import styled from "styled-components";
+import { LightActiveTextColor, PrimaryColor } from "constants/style";
+import { styleControl } from "comps/controls/styleControl";
+import { avatarGroupStyle, AvatarGroupStyleType } from "comps/controls/styleControlConstants";
+import { AlignCenter, AlignLeft, AlignRight } from "lowcoder-design";
+import { NumberControl } from "comps/controls/codeControl";
+import { Avatar, Tooltip } from "antd";
+import { clickEvent, eventHandlerControl, refreshEvent, doubleClickEvent } from "comps/controls/eventHandlerControl";
+import React, { ReactElement, useCallback, useEffect, useRef } from "react";
+import { IconControl } from "comps/controls/iconControl";
+import { ColorControl } from "comps/controls/colorControl";
+import { optionsControl } from "comps/controls/optionsControl";
+import { BoolControl } from "comps/controls/boolControl";
+import { dropdownControl } from "comps/controls/dropdownControl";
+import { JSONObject } from "util/jsonTypes";
+import { useCompClickEventHandler } from "@lowcoder-ee/comps/utils/useCompClickEventHandler";
+
+const MacaroneList = [
+  '#fde68a',
+  '#eecff3',
+  '#a7f3d0',
+  '#bfdbfe',
+  '#bfdbfe',
+  '#c7d2fe',
+  '#fecaca',
+  '#fcd6bb',
+]
+
+const Container = styled.div<{ $style: AvatarGroupStyleType | undefined, alignment: string }>`
+  height: 100%;
+  width: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: ${props => props.alignment};
+  cursor: pointer;
+`;
+
+const AvatarEventOptions = [clickEvent, refreshEvent] as const;
+
+const DropdownOption = new MultiCompBuilder(
+  {
+    src: StringControl,
+    AvatarIcon: IconControl,
+    label: StringControl,
+    color: ColorControl,
+    backgroundColor: ColorControl,
+    Tooltip: StringControl,
+    onEvent: eventHandlerControl(AvatarEventOptions),
+  },
+  (props) => props
+)
+.setPropertyViewFn((children) => {
+  return (
+    <>
+      {children.src.propertyView({ label: trans("avatarComp.src"), placeholder: "", tooltip: trans("avatarComp.avatarCompTooltip") })}
+      {children.label.propertyView({label: trans("avatarComp.title"), tooltip: trans("avatarComp.avatarCompTooltip"),
+      })}
+      {children.AvatarIcon.propertyView({
+        label: trans("avatarComp.icon"),
+        IconType: "All",
+        tooltip: trans("avatarComp.avatarCompTooltip"),
+      })}
+      {children.color.propertyView({ label: trans("style.fill") })}
+      {children.backgroundColor.propertyView({ label: trans("style.background") })}
+      {children.Tooltip.propertyView({ label: trans("badge.tooltip") })}
+      {children.onEvent.propertyView()}
+    </>
+  );
+})
+.build();
+
+const EventOptions = [clickEvent, refreshEvent, doubleClickEvent] as const;
+
+export const alignOptions = [
+  { label: <AlignLeft />, value: "flex-start" },
+  { label: <AlignCenter />, value: "center" },
+  { label: <AlignRight />, value: "flex-end" },
+] as const;
+
+// Memoized Avatar component
+const MemoizedAvatar = React.memo(({ 
+  item, 
+  index, 
+  style, 
+  autoColor, 
+  avatarSize, 
+  onEvent,
+  onItemEvent
+}: { 
+  item: any; 
+  index: number; 
+  style: any; 
+  autoColor: boolean; 
+  avatarSize: number; 
+  onEvent: (event: string) => void;
+  onItemEvent?: (event: string) => void;
+}) => {
+  const mountedRef = useRef(true);
+  const handleClickEvent = useCompClickEventHandler({onEvent})
+
+
+  // Cleanup on unmount
+  useEffect(() => {
+    return () => {
+      mountedRef.current = false;
+    };
+  }, []);
+
+  const handleClick = useCallback(() => {
+    if (!mountedRef.current) return;
+    
+    // Trigger individual avatar event first
+    if (onItemEvent) {
+      onItemEvent("click");
+    }
+    
+    // Then trigger main component event
+    handleClickEvent()
+  }, [onItemEvent, handleClickEvent]);
+
+  return (
+    <Tooltip title={item.Tooltip} key={index}>
+      <Avatar
+        src={item.src ?? undefined}
+        icon={(item.AvatarIcon as ReactElement)?.props.value === '' || item.label.trim() !== '' ? undefined : item.AvatarIcon}
+        style={{
+          color: item.color ? item.color : (style.fill !== '#FFFFFF' ? style.fill : '#FFFFFF'),
+          backgroundColor: item.backgroundColor ? item.backgroundColor : (autoColor ? MacaroneList[index % MacaroneList.length] : style.background),
+          cursor: 'pointer',
+        }}
+        size={avatarSize}
+        onClick={handleClick}
+      >
+        {item.label}
+      </Avatar>
+    </Tooltip>
+  );
+});
+
+MemoizedAvatar.displayName = 'MemoizedAvatar';
+
+// Memoized Avatar Group component
+const MemoizedAvatarGroup = React.memo(({ 
+  avatars, 
+  maxCount, 
+  avatarSize, 
+  style, 
+  autoColor, 
+  onEvent 
+}: { 
+  avatars: any[]; 
+  maxCount: number; 
+  avatarSize: number; 
+  style: any; 
+  autoColor: boolean; 
+  onEvent: (event: string) => void; 
+}) => {
+  const mountedRef = useRef(true);
+
+  // Cleanup on unmount
+  useEffect(() => {
+    return () => {
+      mountedRef.current = false;
+    };
+  }, []);
+
+  return (
+    <Avatar.Group maxCount={maxCount} size={avatarSize}>
+      {avatars.map((item, index) => (
+        <MemoizedAvatar
+          key={index}
+          item={item}
+          index={index}
+          style={style}
+          autoColor={autoColor}
+          avatarSize={avatarSize}
+          onEvent={onEvent}
+          onItemEvent={item.onEvent}
+        />
+      ))}
+    </Avatar.Group>
+  );
+});
+
+MemoizedAvatarGroup.displayName = 'MemoizedAvatarGroup';
+
+export const ColumnAvatarsComp = (function () {
+  const childrenMap = {
+    style: styleControl(avatarGroupStyle),
+    maxCount: withDefault(NumberControl, 3),
+    avatarSize: withDefault(NumberControl, 40),
+    alignment: dropdownControl(alignOptions, "center"),
+    autoColor: BoolControl.DEFAULT_TRUE,
+    onEvent: eventHandlerControl(EventOptions),
+    currentAvatar: stateComp<JSONObject>({}),
+    avatars: optionsControl(DropdownOption, {
+      initOptions: [
+        { src: "https://api.dicebear.com/7.x/miniavs/svg?seed=1", label: String.fromCharCode(65 + Math.ceil(Math.random() * 25)) },
+        { AvatarIcon: "/icon:antd/startwotone" },
+        { label: String.fromCharCode(65 + Math.ceil(Math.random() * 25)) },
+        { label: String.fromCharCode(65 + Math.ceil(Math.random() * 25)) },
+      ],
+    })
+  };
+
+  return new ColumnTypeCompBuilder(
+    childrenMap,
+    (props, dispatch) => {
+      return (
+        <Container
+          $style={props.style}
+          alignment={props.alignment}
+        >
+          <MemoizedAvatarGroup
+            avatars={props.avatars}
+            maxCount={props.maxCount}
+            avatarSize={props.avatarSize}
+            style={props.style}
+            autoColor={props.autoColor}
+            onEvent={props.onEvent}
+          />
+        </Container>
+      );
+    },
+    () => ""
+  )
+    .setPropertyViewFn((children) => (
+      <>
+        {children.avatars.propertyView({})}
+        {children.maxCount.propertyView({
+          label: trans("avatarGroup.maxCount")
+        })}
+        {children.avatarSize.propertyView({
+          label: trans("avatarGroup.avatarSize")
+        })}
+        {children.autoColor.propertyView({
+          label: trans("avatarGroup.autoColor")
+        })}
+        {children.alignment.propertyView({
+          label: trans("table.avatarGroupAlignment"),
+          radioButton: true,
+        })}
+        {children.onEvent.propertyView()}
+      </>
+    ))
+    .build();
+})();
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnBooleanComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnBooleanComp.tsx
new file mode 100644
index 0000000000..d1d530eb6c
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnBooleanComp.tsx
@@ -0,0 +1,201 @@
+import React, { useCallback, useRef, useEffect, useMemo } from "react";
+import { BoolCodeControl } from "comps/controls/codeControl";
+import { trans } from "i18n";
+import { default as Checkbox, CheckboxChangeEvent } from "antd/es/checkbox";
+import { ColumnTypeCompBuilder, ColumnTypeViewFn } from "../columnTypeCompBuilder";
+import { ColumnValueTooltip } from "../simpleColumnTypeComps";
+import { getStyle } from "comps/comps/selectInputComp/checkboxComp";
+import styled from "styled-components";
+import { CheckboxStyle, CheckboxStyleType } from "comps/controls/styleControlConstants";
+import { useStyle } from "comps/controls/styleControl";
+import { dropdownControl } from "comps/controls/dropdownControl";
+import { TableCheckedIcon, TableUnCheckedIcon } from "lowcoder-design";
+import { IconControl } from "comps/controls/iconControl";
+import { hasIcon } from "comps/utils";
+
+const CheckboxStyled = styled(Checkbox)<{ $style: CheckboxStyleType }>`
+  ${(props) => props.$style && getStyle(props.$style)}
+`;
+
+const Wrapper = styled.div`
+  background: transparent !important;
+  padding: 0 8px;
+`;
+
+const IconWrapper = styled.span<{ $style: CheckboxStyleType; $ifChecked: boolean }>`
+  // pointer-events: none;
+  height: 22px;
+  display: inline-block;
+  svg {
+    width: 14px;
+    height: 22px;
+    g {
+      stroke: ${(props) => props.$ifChecked && props.$style.checkedBackground} !important;
+    }
+  }
+`;
+
+const falseValuesOptions = [
+  {
+    label: trans("table.empty"),
+    value: "",
+  },
+  {
+    label: "-",
+    value: "-",
+  },
+  {
+    label: <TableUnCheckedIcon width={10} height={10} />,
+    value: "x",
+  },
+] as const;
+
+const childrenMap = {
+  text: BoolCodeControl,
+  falseValues: dropdownControl(falseValuesOptions, ""),
+  iconTrue: IconControl,
+  iconFalse: IconControl,
+  iconNull: IconControl,
+};
+
+const getBaseValue: ColumnTypeViewFn<typeof childrenMap, boolean, boolean> = (props) => props.text;
+
+type CheckBoxEditPropsType = {
+  value: boolean;
+  onChange: (value: boolean) => void;
+  onChangeEnd: () => void;
+};
+
+// Memoized checkbox edit component
+const CheckBoxEdit = React.memo((props: CheckBoxEditPropsType) => {
+  const mountedRef = useRef(true);
+  const style = useStyle(CheckboxStyle);
+
+  // Cleanup on unmount
+  useEffect(() => {
+    return () => {
+      mountedRef.current = false;
+    };
+  }, []);
+
+  const handleBlur = useCallback(() => {
+    if (!mountedRef.current) return;
+    props.onChangeEnd();
+  }, [props.onChangeEnd]);
+
+  const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
+    if (!mountedRef.current) return;
+    if (e.key === "Enter") {
+      props.onChangeEnd();
+    }
+  }, [props.onChangeEnd]);
+
+  const handleChange = useCallback((e: CheckboxChangeEvent) => {
+    if (!mountedRef.current) return;
+    props.onChange(e.target.checked);
+  }, [props.onChange]);
+
+  return (
+    <Wrapper
+      onBlur={handleBlur}
+      onKeyDown={handleKeyDown}
+    >
+      <CheckboxStyled
+        autoFocus
+        $style={style}
+        defaultChecked={props.value}
+        onChange={handleChange}
+      />
+    </Wrapper>
+  );
+});
+
+CheckBoxEdit.displayName = 'CheckBoxEdit';
+
+// Memoized checkbox view component
+const CheckBoxView = React.memo(({ 
+  value, 
+  iconTrue, 
+  iconFalse, 
+  iconNull, 
+  falseValues 
+}: { 
+  value: boolean;
+  iconTrue: React.ReactNode;
+  iconFalse: React.ReactNode;
+  iconNull: React.ReactNode;
+  falseValues: string;
+}) => {
+  const style = useStyle(CheckboxStyle);
+
+  const content = useMemo(() => {
+    if (value === true) {
+      return hasIcon(iconTrue) ? iconTrue : <TableCheckedIcon />;
+    } else if (value === false) {
+      return hasIcon(iconFalse) ? iconFalse : (falseValues === "x" ? <TableUnCheckedIcon /> : falseValues);
+    } else {
+      return hasIcon(iconNull) ? iconNull : "No Value";
+    }
+  }, [value, iconTrue, iconFalse, iconNull, falseValues]);
+
+  return (
+    <IconWrapper $style={style} $ifChecked={value}>
+      {content}
+    </IconWrapper>
+  );
+});
+
+CheckBoxView.displayName = 'CheckBoxView';
+
+export const BooleanComp = (function () {
+  return new ColumnTypeCompBuilder(
+    childrenMap,
+    (props, dispatch) => {
+      const value = props.changeValue ?? getBaseValue(props, dispatch);
+      return (
+        <CheckBoxView
+          value={value}
+          iconTrue={props.iconTrue}
+          iconFalse={props.iconFalse}
+          iconNull={props.iconNull}
+          falseValues={props.falseValues}
+        />
+      );
+    },
+    (nodeValue) => nodeValue.text.value,
+    getBaseValue
+  )
+    .setEditViewFn((props) => {
+      return (
+        <CheckBoxEdit
+          value={props.value}
+          onChange={props.onChange}
+          onChangeEnd={props.onChangeEnd}
+        />
+      );
+    })
+    .setPropertyViewFn((children) => {
+      return (
+        <>
+          {children.text.propertyView({
+            label: trans("table.columnValue"),
+            tooltip: ColumnValueTooltip,
+          })}
+          {children.falseValues.propertyView({
+            label: trans("table.falseValues"),
+            radioButton: true,
+          })}
+          {children.iconTrue.propertyView({
+            label: trans("table.iconTrue"),
+          })}
+          {children.iconFalse.propertyView({
+            label: trans("table.iconFalse"),
+          })}
+          {children.iconNull.propertyView({
+            label: trans("table.iconNull"),
+          })}
+        </>
+      );
+    })
+    .build();
+})();
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnDateComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnDateComp.tsx
new file mode 100644
index 0000000000..55168b1515
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnDateComp.tsx
@@ -0,0 +1,283 @@
+import { default as DatePicker } from "antd/es/date-picker";
+import {
+  ColumnTypeCompBuilder,
+  ColumnTypeViewFn,
+} from "comps/comps/tableComp/column/columnTypeCompBuilder";
+import { ColumnValueTooltip } from "comps/comps/tableComp/column/simpleColumnTypeComps";
+import { StringControl } from "comps/controls/codeControl";
+import { withDefault } from "comps/generators";
+import { formatPropertyView } from "comps/utils/propertyUtils";
+import { trans } from "i18n";
+import { isNumber } from "lodash";
+import dayjs, { Dayjs } from "dayjs";
+import utc from "dayjs/plugin/utc";
+import { CalendarCompIconSmall, PrevIcon, SuperPrevIcon } from "lowcoder-design";
+import React, { useCallback, useEffect, useRef, useState } from "react";
+import styled from "styled-components";
+import { DateParser, DATE_FORMAT } from "util/dateTimeUtils";
+
+dayjs.extend(utc)
+
+const IconNext = styled(PrevIcon)`
+  transform: rotate(180deg);
+`;
+const IconSuperNext = styled(SuperPrevIcon)`
+  transform: rotate(180deg);
+`;
+
+const DatePickerStyled = styled(DatePicker)<{ $open: boolean }>`
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  top: 0;
+  padding: 0;
+  padding-left: 11px;
+  .ant-picker-input {
+    height: 100%;
+  }
+  input {
+    padding-right: 18px;
+    cursor: pointer;
+  }
+  &.ant-picker-focused .ant-picker-suffix svg g {
+    stroke: ${(props) => props.$open && "#315EFB"};
+  }
+  .ant-picker-suffix {
+    height: calc(100% - 1px);
+    position: absolute;
+    right: 0;
+    top: 0.5px;
+    display: flex;
+    align-items: center;
+    padding: 0 3px;
+  }
+`;
+
+const StylePanel = styled.div`
+  .ant-picker-header {
+    padding: 0 12px;
+    .ant-picker-header-super-prev-btn,
+    .ant-picker-header-prev-btn,
+    .ant-picker-header-next-btn,
+    .ant-picker-header-super-next-btn {
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      svg {
+        max-width: 12px;
+        max-height: 12px;
+      }
+      &:hover svg g {
+        fill: #315efb;
+      }
+    }
+  }
+  .ant-picker-date-panel .ant-picker-body {
+    padding: 8px 16px;
+  }
+  .ant-picker-ranges {
+    padding: 10px 16px;
+  }
+  .ant-picker-now-btn {
+    color: #4965f2;
+    &:hover {
+      color: #315efb;
+    }
+  }
+  .ant-picker-cell {
+    color: #b8b9bf;
+  }
+  .ant-picker-cell-in-view {
+    color: rgba(0, 0, 0, 0.85);
+  }
+  .ant-picker-cell-in-view.ant-picker-cell-selected .ant-picker-cell-inner,
+  .ant-picker-ok .ant-btn-primary {
+    background: #4965f2;
+    border: none;
+    box-shadow: none;
+    &:hover {
+      background: #315efb;
+      border: none;
+      box-shadow: none;
+    }
+  }
+  .ant-picker-cell:hover:not(.ant-picker-cell-in-view) .ant-picker-cell-inner,
+  .ant-picker-cell:hover:not(.ant-picker-cell-selected):not(.ant-picker-cell-range-start):not(.ant-picker-cell-range-end):not(.ant-picker-cell-range-hover-start):not(.ant-picker-cell-range-hover-end)
+    .ant-picker-cell-inner {
+    background-color: #f2f7fc;
+    color: #4965f2;
+  }
+  .ant-picker-year-panel,
+  .ant-picker-month-panel {
+    & + div .ant-picker-now {
+      display: none;
+    }
+  }
+`;
+
+const DatePickerPopup = styled.div`
+  border-radius: 8px;
+  box-shadow: 0 0 10px 0 rgba(0,0,0,0.10);
+  overflow: hidden;
+`;
+
+const Wrapper = styled.div`
+  background: transparent !important;
+`;
+
+export function formatDate(date: string, format: string) {
+  let mom = dayjs(date);
+  if (isNumber(Number(date)) && !isNaN(Number(date)) && date !== "") {
+    mom = dayjs(Number(date));
+  }
+  if (!mom.isValid()) {
+    mom = dayjs.utc(date).local();
+  }
+
+  return mom.isValid() ? mom.format(format) : "";
+}
+
+const childrenMap = {
+  text: StringControl,
+  format: withDefault(StringControl, DATE_FORMAT),
+  inputFormat: withDefault(StringControl, DATE_FORMAT),
+};
+
+const getBaseValue: ColumnTypeViewFn<typeof childrenMap, string, string> = (props) => props.text;
+
+type DateEditProps = {
+  value: string;
+  onChange: (value: string) => void;
+  onChangeEnd: () => void;
+  showTime: boolean;
+  inputFormat: string;
+};
+
+// Memoized DateEdit component
+export const DateEdit = React.memo((props: DateEditProps) => {
+  const pickerRef = useRef<any>();
+  const mountedRef = useRef(true);
+  const [panelOpen, setPanelOpen] = useState(true);
+  
+  // Initialize tempValue with proper validation
+  const [tempValue, setTempValue] = useState<dayjs.Dayjs | null>(() => {
+    const initialValue = dayjs(props.value, DateParser);
+    return initialValue.isValid() ? initialValue : dayjs(0, DateParser);
+  });
+
+  // Memoize event handlers
+  const handleKeyDown = useCallback((e: React.KeyboardEvent<HTMLDivElement>) => {
+    if (!mountedRef.current) return;
+    if (e.key === "Enter" && !panelOpen) {
+      props.onChangeEnd();
+    }
+  }, [panelOpen, props.onChangeEnd]);
+
+  const handleMouseDown = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
+    if (!mountedRef.current) return;
+    e.stopPropagation();
+    e.preventDefault();
+  }, []);
+
+  const handleOpenChange = useCallback((open: boolean) => {
+    if (!mountedRef.current) return;
+    setPanelOpen(open);
+  }, []);
+
+  const handleChange = useCallback((value: dayjs.Dayjs | null, dateString: string | string[]) => {
+    if (!mountedRef.current) return;
+    props.onChange(dateString as string);
+  }, [props.onChange]);
+
+  const handleBlur = useCallback(() => {
+    if (!mountedRef.current) return;
+    props.onChangeEnd();
+  }, [props.onChangeEnd]);
+
+  // Update tempValue when props.value changes
+  useEffect(() => {
+    if (!mountedRef.current) return;
+    
+    const newValue = props.value ? dayjs(props.value, DateParser) : null;
+    if (newValue?.isValid()) {
+      setTempValue(newValue);
+    }
+  }, [props.value]);
+
+  // Cleanup event listeners and state
+  useEffect(() => {
+    return () => {
+      mountedRef.current = false;
+      setTempValue(null);
+      if (pickerRef.current) {
+        pickerRef.current = null;
+      }
+    };
+  }, []);
+
+  return (
+    <Wrapper
+      onKeyDown={handleKeyDown}
+      onMouseDown={handleMouseDown}
+    >
+      <DatePickerStyled
+        ref={pickerRef}
+        $open={panelOpen}
+        suffixIcon={<CalendarCompIconSmall />}
+        prevIcon={<PrevIcon />}
+        nextIcon={<IconNext />}
+        superNextIcon={<IconSuperNext />}
+        superPrevIcon={<SuperPrevIcon />}
+        format={props.inputFormat}
+        allowClear={true}
+        variant="borderless"
+        autoFocus
+        value={tempValue}
+        showTime={props.showTime}
+        showNow={true}
+        defaultOpen={true}
+        panelRender={(panelNode) => (
+          <DatePickerPopup>
+            <StylePanel>{panelNode}</StylePanel>
+          </DatePickerPopup>
+        )}
+        onOpenChange={handleOpenChange}
+        onChange={(date: unknown, dateString: string | string[]) => handleChange(date as Dayjs | null, dateString)}
+        onBlur={handleBlur}
+      />
+    </Wrapper>
+  );
+});
+
+DateEdit.displayName = 'DateEdit';
+
+export const DateComp = (function () {
+  return new ColumnTypeCompBuilder(
+    childrenMap,
+    (props, dispatch) => {
+      const value = props.changeValue ?? getBaseValue(props, dispatch);
+      return formatDate(value, props.format);
+    },
+    (nodeValue) => formatDate(nodeValue.text.value, nodeValue.format.value),
+    getBaseValue
+  )
+    .setEditViewFn((props) => (
+      <DateEdit
+        value={props.value}
+        onChange={props.onChange}
+        onChangeEnd={props.onChangeEnd}
+        showTime={false}
+        inputFormat={props.otherProps?.inputFormat}
+      />
+    ))
+    .setPropertyViewFn((children) => (
+      <>
+        {children.text.propertyView({
+          label: trans("table.columnValue"),
+          tooltip: ColumnValueTooltip,
+        })}
+        {formatPropertyView({ children, placeholder: DATE_FORMAT })}
+      </>
+    ))
+    .build();
+})();
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnDateTimeComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnDateTimeComp.tsx
new file mode 100644
index 0000000000..181fccba7b
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnDateTimeComp.tsx
@@ -0,0 +1,90 @@
+import {
+  ColumnTypeCompBuilder,
+  ColumnTypeViewFn,
+} from "comps/comps/tableComp/column/columnTypeCompBuilder";
+import { ColumnValueTooltip } from "comps/comps/tableComp/column/simpleColumnTypeComps";
+import { StringControl } from "comps/controls/codeControl";
+import { withDefault } from "comps/generators";
+import { formatPropertyView } from "comps/utils/propertyUtils";
+import { trans } from "i18n";
+import { DATE_TIME_FORMAT } from "util/dateTimeUtils";
+import { DateEdit, formatDate } from "./columnDateComp";
+import React, { useCallback, useEffect, useRef } from "react";
+
+const childrenMap = {
+  text: StringControl,
+  format: withDefault(StringControl, DATE_TIME_FORMAT),
+  inputFormat: withDefault(StringControl, DATE_TIME_FORMAT),
+};
+
+const getBaseValue: ColumnTypeViewFn<typeof childrenMap, string, string> = (props) => props.text;
+
+// Memoized DateTimeEdit component
+const DateTimeEdit = React.memo((props: {
+  value: string;
+  onChange: (value: string) => void;
+  onChangeEnd: () => void;
+  inputFormat: string;
+}) => {
+  const mountedRef = useRef(true);
+
+  // Memoize event handlers
+  const handleChange = useCallback((value: string) => {
+    if (!mountedRef.current) return;
+    props.onChange(value);
+  }, [props.onChange]);
+
+  const handleChangeEnd = useCallback(() => {
+    if (!mountedRef.current) return;
+    props.onChangeEnd();
+  }, [props.onChangeEnd]);
+
+  // Cleanup on unmount
+  useEffect(() => {
+    return () => {
+      mountedRef.current = false;
+    };
+  }, []);
+
+  return (
+    <DateEdit
+      value={props.value}
+      onChange={handleChange}
+      onChangeEnd={handleChangeEnd}
+      showTime={true}
+      inputFormat={props.inputFormat}
+    />
+  );
+});
+
+DateTimeEdit.displayName = 'DateTimeEdit';
+
+export const DateTimeComp = (function () {
+  return new ColumnTypeCompBuilder(
+    childrenMap,
+    (props, dispatch) => {
+      const value = props.changeValue ?? getBaseValue(props, dispatch);
+      return formatDate(value, props.format);
+    },
+    (nodeValue) => formatDate(nodeValue.text.value, nodeValue.format.value),
+    getBaseValue
+  )
+    .setEditViewFn((props) => (
+      <DateTimeEdit
+        value={props.value}
+        onChange={props.onChange}
+        onChangeEnd={props.onChangeEnd}
+        inputFormat={props.otherProps?.inputFormat}
+      />
+    ))
+    .setPropertyViewFn((children) => (
+      <>
+        {children.text.propertyView({
+          label: trans("table.columnValue"),
+          tooltip: ColumnValueTooltip,
+        })}
+        {formatPropertyView({ children, placeholder: DATE_TIME_FORMAT })}
+      </>
+    ))
+    .build();
+})();
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnDropdownComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnDropdownComp.tsx
new file mode 100644
index 0000000000..b78601a5fa
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnDropdownComp.tsx
@@ -0,0 +1,200 @@
+import React, { ReactNode, useCallback, useRef, useEffect, useMemo, ReactElement } from "react";
+import { DropdownOptionControl } from "comps/controls/optionsControl";
+import { StringControl } from "comps/controls/codeControl";
+import { trans } from "i18n";
+import { ColumnTypeCompBuilder, ColumnTypeViewFn } from "../columnTypeCompBuilder";
+import Menu from "antd/es/menu";
+import Dropdown from "antd/es/dropdown";
+import { dropdownControl } from "comps/controls/dropdownControl";
+import { IconControl } from "comps/controls/iconControl";
+import { withDefault } from "comps/generators";
+import { IconWrapper } from "util/bottomResUtils";
+import { ButtonTypeOptions } from "../simpleColumnTypeComps";
+import { useStyle } from "comps/controls/styleControl";
+import { ButtonStyle } from "comps/controls/styleControlConstants";
+import { Button100 } from "comps/comps/buttonComp/buttonCompConstants";
+import styled from "styled-components";
+import { ButtonType } from "antd/es/button";
+import { clickEvent, eventHandlerControl } from "comps/controls/eventHandlerControl";
+import { useCompClickEventHandler } from "@lowcoder-ee/comps/utils/useCompClickEventHandler";
+
+const StyledButton = styled(Button100)`
+  display: flex;
+  align-items: center;
+  gap: 0;
+  min-width: 30px;
+  width: auto;
+`;
+
+const StyledIconWrapper = styled(IconWrapper)`
+  margin: 0;
+`;
+
+const DropdownEventOptions = [clickEvent] as const;
+
+const childrenMap = {
+  buttonType: dropdownControl(ButtonTypeOptions, "primary"),
+  label: withDefault(StringControl, 'Menu'),
+  prefixIcon: IconControl,
+  suffixIcon: IconControl,
+  options: DropdownOptionControl,
+  onEvent: eventHandlerControl(DropdownEventOptions),
+};
+
+const getBaseValue: ColumnTypeViewFn<typeof childrenMap, string, string> = (props) => props.label;
+
+// Memoized dropdown menu component
+const DropdownMenu = React.memo(({ items, options, onEvent }: { items: any[]; options: any[]; onEvent: (eventName: string) => void }) => {
+  const mountedRef = useRef(true);
+  const handleClickEvent = useCompClickEventHandler({onEvent})
+
+  // Cleanup on unmount
+  useEffect(() => {
+    return () => {
+      mountedRef.current = false;
+    };
+  }, []);
+
+  const handleClick = useCallback(({ key }: { key: string }) => {
+    if (!mountedRef.current) return;
+    const item = items.find((o) => o.key === key);
+    const itemIndex = options.findIndex(option => option.label === item?.label);
+    item && options[itemIndex]?.onEvent("click");
+    // Also trigger the dropdown's main event handler
+    handleClickEvent();
+  }, [items, options, handleClickEvent]);
+
+  const handleMouseDown = useCallback((e: React.MouseEvent) => {
+    e.stopPropagation();
+    e.preventDefault();
+  }, []);
+
+  return (
+    <Menu
+      items={items}
+      onClick={handleClick}
+      onMouseDown={handleMouseDown}
+    />
+  );
+});
+
+DropdownMenu.displayName = 'DropdownMenu';
+
+const DropdownView = React.memo((props: {
+  buttonType: ButtonType;
+  label: string;
+  prefixIcon: ReactNode;
+  suffixIcon: ReactNode;
+  options: any[];
+  onEvent?: (eventName: string) => void;
+}) => {
+  const mountedRef = useRef(true);
+
+  // Cleanup on unmount
+  useEffect(() => {
+    return () => {
+      mountedRef.current = false;
+    };
+  }, []);
+
+  const hasOptionIcon = useMemo(() => 
+    props.options.findIndex((option) => (option.prefixIcon as ReactElement)?.props.value) > -1,
+    [props.options]
+  );
+
+  const items = useMemo(() => 
+    props.options
+      .filter((option) => !option.hidden)
+      .map((option, index) => ({
+        title: option.label,
+        label: option.label,
+        key: option.label + " - " + index,
+        disabled: option.disabled,
+        icon: hasOptionIcon && <span>{option.prefixIcon}</span>,
+        index,
+      })),
+    [props.options, hasOptionIcon]
+  );
+  
+  const hasPrefixIcon = useMemo(() => 
+    (props.prefixIcon as ReactElement)?.props.value,
+    [props.prefixIcon]
+  );
+
+  const hasSuffixIcon = useMemo(() => 
+    (props.suffixIcon as ReactElement)?.props.value,
+    [props.suffixIcon]
+  );
+
+  const buttonStyle = useStyle(ButtonStyle);
+
+  const menu = useMemo(() => (
+    <DropdownMenu items={items} options={props.options} onEvent={props.onEvent ?? (() => {})} />
+  ), [items, props.options, props.onEvent]);
+
+  return (
+    <Dropdown
+      trigger={["click"]}
+      placement="bottomRight"
+      popupRender={() => menu}
+    >
+      <StyledButton
+        type={props.buttonType}
+        $buttonStyle={props.buttonType === "primary" ? buttonStyle : undefined}
+      >
+        {hasPrefixIcon && (
+          <StyledIconWrapper style={{
+            marginRight: props.label || hasSuffixIcon ? '3px' : '0x',
+          }}>
+            {props.prefixIcon}
+          </StyledIconWrapper>
+        )}
+        {props.label || (hasPrefixIcon || hasSuffixIcon ? undefined : " ")}
+        {hasSuffixIcon && (
+          <StyledIconWrapper style={{
+            marginLeft: props.label || hasPrefixIcon ? '3px' : '0x',
+          }}>
+            {props.suffixIcon}
+          </StyledIconWrapper>
+        )}
+      </StyledButton>
+    </Dropdown>
+  );
+});
+
+DropdownView.displayName = 'DropdownView';
+
+export const ColumnDropdownComp = (function () {
+  return new ColumnTypeCompBuilder(
+    childrenMap,
+    (props) => {
+      return <DropdownView {...props} />;
+    },
+    (nodeValue) => nodeValue.label.value,
+    getBaseValue,
+  )
+    .setPropertyViewFn((children) => {
+      return (
+        <>
+          {children.buttonType.propertyView({
+            label: trans("table.type"),
+            radioButton: true,
+          })}
+          {children.label.propertyView({
+            label: trans("text"),
+          })}
+          {children.prefixIcon.propertyView({
+            label: trans("button.prefixIcon"),
+          })}
+          {children.suffixIcon.propertyView({
+            label: trans("button.suffixIcon"),
+          })}
+          {children.options.propertyView({
+            title: trans("optionsControl.optionList"),
+          })}
+          {children.onEvent.propertyView()}
+        </>
+      );
+    })
+    .build();
+})();
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnImgComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnImgComp.tsx
new file mode 100644
index 0000000000..d3d2041016
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnImgComp.tsx
@@ -0,0 +1,143 @@
+import React, { useState, useRef, useEffect, useCallback, useMemo } from "react";
+import { default as Input } from "antd/es/input";
+import {
+  ColumnTypeCompBuilder,
+  ColumnTypeViewFn,
+} from "comps/comps/tableComp/column/columnTypeCompBuilder";
+import { StringControl, NumberControl } from "comps/controls/codeControl";
+import { trans } from "i18n";
+import { withDefault } from "comps/generators";
+import { TacoImage } from "lowcoder-design";
+import styled from "styled-components";
+import { DEFAULT_IMG_URL } from "@lowcoder-ee/util/stringUtils";
+import { clickEvent, eventHandlerControl } from "comps/controls/eventHandlerControl";
+
+export const ColumnValueTooltip = trans("table.columnValueTooltip");
+
+const childrenMap = {
+  src: withDefault(StringControl, "{{currentCell}}"),
+  size: withDefault(NumberControl, "50"),
+  onEvent: eventHandlerControl([clickEvent]),
+};
+
+const StyledTacoImage = styled(TacoImage)`
+  pointer-events: auto !important;
+  cursor: pointer !important;
+  
+  &:hover {
+    opacity: 0.8;
+    transition: opacity 0.2s ease;
+  }
+`;
+
+// Memoized image component
+const ImageView = React.memo(({ src, size, onEvent }: { src: string; size: number; onEvent?: (eventName: string) => void }) => {
+  const mountedRef = useRef(true);
+
+  // Cleanup on unmount
+  useEffect(() => {
+    return () => {
+      mountedRef.current = false;
+    };
+  }, []);
+
+  const handleClick = useCallback(() => {
+    console.log("Image clicked!", { src, onEvent: !!onEvent }); // Debug log
+    if (mountedRef.current && onEvent) {
+      onEvent("click");
+    }
+  }, [onEvent, src]);
+
+  return (
+    <StyledTacoImage 
+      src={src || DEFAULT_IMG_URL} 
+      width={size}
+      onClick={handleClick}
+      style={{ cursor: 'pointer' }} // Inline style as backup
+    />
+  );
+});
+
+ImageView.displayName = 'ImageView';
+
+// Memoized edit component
+const ImageEdit = React.memo(({ value, onChange, onChangeEnd }: { value: string; onChange: (value: string) => void; onChangeEnd: () => void }) => {
+  const mountedRef = useRef(true);
+  const [currentValue, setCurrentValue] = useState(value);
+
+  // Cleanup on unmount
+  useEffect(() => {
+    return () => {
+      mountedRef.current = false;
+    };
+  }, []);
+
+  const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
+    if (mountedRef.current) {
+      const newValue = e.target.value;
+      setCurrentValue(newValue);
+      onChange(newValue);
+    }
+  }, [onChange]);
+
+  const handleBlur = useCallback(() => {
+    if (mountedRef.current) {
+      onChangeEnd();
+    }
+  }, [onChangeEnd]);
+
+  const handlePressEnter = useCallback(() => {
+    if (mountedRef.current) {
+      onChangeEnd();
+    }
+  }, [onChangeEnd]);
+
+  return (
+    <Input
+      value={currentValue}
+      autoFocus
+      variant="borderless"
+      onChange={handleChange}
+      onBlur={handleBlur}
+      onPressEnter={handlePressEnter}
+    />
+  );
+});
+
+ImageEdit.displayName = 'ImageEdit';
+
+const getBaseValue: ColumnTypeViewFn<typeof childrenMap, string, string> = (props) => props.src;
+
+export const ImageComp = (function () {
+  return new ColumnTypeCompBuilder(
+    childrenMap,
+    (props, dispatch) => {
+      const value = props.changeValue ?? getBaseValue(props, dispatch);
+      return <ImageView src={value} size={props.size} onEvent={props.onEvent} />;
+    },
+    (nodeValue) => nodeValue.src.value,
+    getBaseValue
+  )
+    .setEditViewFn((props) => (
+      <ImageEdit
+        value={props.value}
+        onChange={props.onChange}
+        onChangeEnd={props.onChangeEnd}
+      />
+    ))
+    .setPropertyViewFn((children) => {
+      return (
+        <>
+          {children.src.propertyView({
+            label: trans("table.imageSrc"),
+            tooltip: ColumnValueTooltip,
+          })}
+          {children.size.propertyView({
+            label: trans("table.imageSize"),
+          })}
+          {children.onEvent.propertyView()}
+        </>
+      );
+    })
+    .build();
+})();
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnLinkComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnLinkComp.tsx
new file mode 100644
index 0000000000..e93b3082a6
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnLinkComp.tsx
@@ -0,0 +1,145 @@
+import React, { useState, useRef, useEffect, useCallback, useMemo } from "react";
+import { default as Input } from "antd/es/input";
+import {
+  ColumnTypeCompBuilder,
+  ColumnTypeViewFn,
+} from "comps/comps/tableComp/column/columnTypeCompBuilder";
+import { BoolCodeControl, StringControl } from "comps/controls/codeControl";
+import { trans } from "i18n";
+import { disabledPropertyView } from "comps/utils/propertyUtils";
+import styled, { css } from "styled-components";
+import { styleControl } from "comps/controls/styleControl";
+import { TableColumnLinkStyle } from "comps/controls/styleControlConstants";
+import { clickEvent, eventHandlerControl, doubleClickEvent } from "comps/controls/eventHandlerControl";
+import { useCompClickEventHandler } from "@lowcoder-ee/comps/utils/useCompClickEventHandler";
+import { migrateOldData } from "@lowcoder-ee/comps/generators/simpleGenerators";
+import { fixOldActionData } from "comps/comps/tableComp/column/simpleColumnTypeComps";
+
+export const ColumnValueTooltip = trans("table.columnValueTooltip");
+
+const LinkEventOptions = [clickEvent, doubleClickEvent] as const;
+
+const childrenMap = {
+  text: StringControl,
+  onClick: eventHandlerControl(LinkEventOptions),
+  disabled: BoolCodeControl,
+  style: styleControl(TableColumnLinkStyle),
+};
+
+const disableCss = css`
+  &,
+  &:hover {
+    cursor: not-allowed;
+    color: rgba(0, 0, 0, 0.25) !important;
+  }
+`;
+
+const StyledLink = styled.a<{ $disabled: boolean }>`
+  ${(props) => props.$disabled && disableCss};
+`;
+
+// Memoized link component
+export const ColumnLink = React.memo(({ disabled, label, onClick }: { disabled: boolean; label: string; onClick: (eventName: string) => void }) => {
+  const handleClickEvent = useCompClickEventHandler({onEvent: onClick})
+  const handleClick = useCallback(() => {
+    if (!disabled) {
+      handleClickEvent();
+    }
+  }, [disabled, onClick]);
+
+  return (
+    <StyledLink
+      $disabled={disabled}
+      onClick={handleClick}
+    >
+      {label}
+    </StyledLink>
+  );
+});
+
+ColumnLink.displayName = 'ColumnLink';
+
+// Memoized edit component
+const LinkEdit = React.memo(({ value, onChange, onChangeEnd }: { value: string; onChange: (value: string) => void; onChangeEnd: () => void }) => {
+  const mountedRef = useRef(true);
+  const [currentValue, setCurrentValue] = useState(value);
+
+  // Cleanup on unmount
+  useEffect(() => {
+    return () => {
+      mountedRef.current = false;
+    };
+  }, []);
+
+  const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
+    if (mountedRef.current) {
+      const newValue = e.target.value;
+      setCurrentValue(newValue);
+      onChange(newValue);
+    }
+  }, [onChange]);
+
+  const handleBlur = useCallback(() => {
+    if (mountedRef.current) {
+      onChangeEnd();
+    }
+  }, [onChangeEnd]);
+
+  const handlePressEnter = useCallback(() => {
+    if (mountedRef.current) {
+      onChangeEnd();
+    }
+  }, [onChangeEnd]);
+
+  return (
+    <Input
+      value={currentValue}
+      autoFocus
+      variant="borderless"
+      onChange={handleChange}
+      onBlur={handleBlur}
+      onPressEnter={handlePressEnter}
+    />
+  );
+});
+
+LinkEdit.displayName = 'LinkEdit';
+
+const getBaseValue: ColumnTypeViewFn<typeof childrenMap, string, string> = (props) => props.text;
+
+const LinkCompTmp = (function () {
+  return new ColumnTypeCompBuilder(
+    childrenMap,
+    (props, dispatch) => {
+      const value = props.changeValue ?? getBaseValue(props, dispatch);
+      return <ColumnLink disabled={props.disabled} label={value} onClick={props.onClick} />;
+    },
+    (nodeValue) => nodeValue.text.value,
+    getBaseValue
+  )
+    .setEditViewFn((props) => (
+      <LinkEdit
+        value={props.value}
+        onChange={props.onChange}
+        onChangeEnd={props.onChangeEnd}
+      />
+    ))
+    .setPropertyViewFn((children) => (
+      <>
+        {children.text.propertyView({
+          label: trans("table.columnValue"),
+          tooltip: ColumnValueTooltip,
+        })}
+        {disabledPropertyView(children)}
+        {children.onClick.propertyView()}
+      </>
+    ))
+    .setStylePropertyViewFn((children) => (
+      <>
+        {children.style.getPropertyView()}
+      </>
+    ))
+    .build();
+})();
+
+export const LinkComp = migrateOldData(LinkCompTmp, fixOldActionData);
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnLinksComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnLinksComp.tsx
new file mode 100644
index 0000000000..5a7fae3d3e
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnLinksComp.tsx
@@ -0,0 +1,139 @@
+import React, { useState, useRef, useEffect, useCallback, useMemo } from "react";
+import { default as Menu } from "antd/es/menu";
+import { ColumnTypeCompBuilder } from "comps/comps/tableComp/column/columnTypeCompBuilder";
+import { BoolCodeControl, StringControl } from "comps/controls/codeControl";
+import { manualOptionsControl } from "comps/controls/optionsControl";
+import { MultiCompBuilder } from "comps/generators";
+import { disabledPropertyView, hiddenPropertyView } from "comps/utils/propertyUtils";
+import { trans } from "i18n";
+import styled from "styled-components";
+import { ColumnLink } from "comps/comps/tableComp/column/columnTypeComps/columnLinkComp";
+import { LightActiveTextColor, PrimaryColor } from "constants/style";
+import { clickEvent, eventHandlerControl, doubleClickEvent } from "comps/controls/eventHandlerControl";
+import { useCompClickEventHandler } from "@lowcoder-ee/comps/utils/useCompClickEventHandler";
+import { migrateOldData } from "@lowcoder-ee/comps/generators/simpleGenerators";
+import { fixOldActionData } from "comps/comps/tableComp/column/simpleColumnTypeComps";
+
+const MenuLinkWrapper = styled.div`
+  > a {
+    color: ${PrimaryColor} !important;
+
+    &:hover {
+      color: ${LightActiveTextColor} !important;
+    }
+  }
+`;
+
+const MenuWrapper = styled.div`
+  ul {
+    background: transparent !important;
+    border-bottom: 0;
+
+    li {
+      padding: 0 10px 0 0 !important;
+      line-height: normal !important;
+
+      &::after {
+        content: none !important;
+      }
+    }
+  }  
+`;
+
+const LinkEventOptions = [clickEvent, doubleClickEvent] as const;
+
+// Memoized menu item component
+const MenuItem = React.memo(({ option, index }: { option: any; index: number }) => {
+  return (
+    <MenuLinkWrapper>
+      <ColumnLink
+        disabled={option.disabled}
+        label={option.label}
+        onClick={option.onClick}
+      />
+    </MenuLinkWrapper>
+  );
+});
+
+MenuItem.displayName = 'MenuItem';
+
+const OptionItemTmp = new MultiCompBuilder(
+  {
+    label: StringControl,
+    onClick: eventHandlerControl(LinkEventOptions),
+    hidden: BoolCodeControl,
+    disabled: BoolCodeControl,
+  },
+  (props) => {
+    return props;
+  }
+)
+  .setPropertyViewFn((children) => {
+    return (
+      <>
+        {children.label.propertyView({ label: trans("label") })}
+        {hiddenPropertyView(children)}
+        {disabledPropertyView(children)}
+        {children.onClick.propertyView()}
+      </>
+    );
+  })
+  .build();
+
+const OptionItem = migrateOldData(OptionItemTmp, fixOldActionData);
+
+// Memoized menu component
+const LinksMenu = React.memo(({ options }: { options: any[] }) => {
+  const mountedRef = useRef(true);
+
+  // Cleanup on unmount
+  useEffect(() => {
+    return () => {
+      mountedRef.current = false;
+    };
+  }, []);
+
+  const menuItems = useMemo(() => 
+    options
+      .filter((o) => !o.hidden)
+      .map((option, index) => ({
+        key: index,
+        label: <MenuItem option={option} index={index} />
+      })),
+    [options]
+  );
+
+  return (
+    <MenuWrapper>
+      <Menu mode="horizontal" items={menuItems} />
+    </MenuWrapper>
+  );
+});
+
+LinksMenu.displayName = 'LinksMenu';
+
+const ColumnLinksCompTmp = (function () {
+  const childrenMap = {
+    options: manualOptionsControl(OptionItem, {
+      initOptions: [{ label: trans("table.option1") }],
+    }),
+  };
+  return new ColumnTypeCompBuilder(
+    childrenMap,
+    (props) => {
+      return <LinksMenu options={props.options} />;
+    },
+    () => ""
+  )
+    .setPropertyViewFn((children) => (
+      <>
+        {children.options.propertyView({
+          newOptionLabel: trans("table.option"),
+          title: trans("table.optionList"),
+        })}
+      </>
+    ))
+    .build();
+})();
+
+export const ColumnLinksComp = migrateOldData(ColumnLinksCompTmp, fixOldActionData);
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnMarkdownComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnMarkdownComp.tsx
new file mode 100644
index 0000000000..17ad78efd3
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnMarkdownComp.tsx
@@ -0,0 +1,128 @@
+import React, { useState, useRef, useEffect, useCallback, useMemo } from "react";
+import { default as Input } from "antd/es/input";
+import {
+  ColumnTypeCompBuilder,
+  ColumnTypeViewFn,
+} from "comps/comps/tableComp/column/columnTypeCompBuilder";
+import { ColumnValueTooltip } from "comps/comps/tableComp/column/simpleColumnTypeComps";
+import { StringControl } from "comps/controls/codeControl";
+import { trans } from "i18n";
+import { markdownCompCss, TacoMarkDown } from "lowcoder-design";
+import styled from "styled-components";
+import { clickEvent, eventHandlerControl } from "comps/controls/eventHandlerControl";
+
+const Wrapper = styled.div`
+  ${markdownCompCss};
+  max-height: 32px;
+  cursor: pointer;
+
+  > .markdown-body {
+    margin: 0;
+    p {
+      line-height: 21px;
+    }
+  }
+`;
+
+const MarkdownEventOptions = [clickEvent] as const;
+
+const childrenMap = {
+  text: StringControl,
+  onEvent: eventHandlerControl(MarkdownEventOptions),
+};
+
+const getBaseValue: ColumnTypeViewFn<typeof childrenMap, string, string> = (props) => props.text;
+
+// Memoized markdown view component
+const MarkdownView = React.memo(({ value, onEvent }: { value: string; onEvent?: (eventName: string) => void }) => {
+  const handleClick = useCallback(() => {
+    if (onEvent) {
+      onEvent("click");
+    }
+  }, [onEvent]);
+
+  return (
+    <Wrapper onClick={handleClick}>
+      <TacoMarkDown>{value}</TacoMarkDown>
+    </Wrapper>
+  );
+});
+
+MarkdownView.displayName = 'MarkdownView';
+
+// Memoized edit component with proper cleanup
+const MarkdownEdit = React.memo((props: {
+  value: string;
+  onChange: (value: string) => void;
+  onChangeEnd: () => void;
+}) => {
+  const [currentValue, setCurrentValue] = useState(props.value);
+  const mountedRef = useRef(true);
+
+  // Cleanup on unmount
+  useEffect(() => {
+    return () => {
+      mountedRef.current = false;
+      setCurrentValue('');
+    };
+  }, []);
+
+  const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
+    if (!mountedRef.current) return;
+    const value = e.target.value;
+    props.onChange(value);
+    setCurrentValue(value);
+  }, [props.onChange]);
+
+  const handleBlur = useCallback(() => {
+    if (!mountedRef.current) return;
+    props.onChangeEnd();
+  }, [props.onChangeEnd]);
+
+  const handlePressEnter = useCallback(() => {
+    if (!mountedRef.current) return;
+    props.onChangeEnd();
+  }, [props.onChangeEnd]);
+
+  return (
+    <Input
+      value={currentValue}
+      autoFocus
+      variant="borderless"
+      onChange={handleChange}
+      onBlur={handleBlur}
+      onPressEnter={handlePressEnter}
+    />
+  );
+});
+
+MarkdownEdit.displayName = 'MarkdownEdit';
+
+export const ColumnMarkdownComp = (function () {
+  return new ColumnTypeCompBuilder(
+    childrenMap,
+    (props, dispatch) => {
+      const value = props.changeValue ?? getBaseValue(props, dispatch);
+      return <MarkdownView value={value} onEvent={props.onEvent} />;
+    },
+    (nodeValue) => nodeValue.text.value,
+    getBaseValue
+  )
+    .setEditViewFn((props) => (
+      <MarkdownEdit
+        value={props.value}
+        onChange={props.onChange}
+        onChangeEnd={props.onChangeEnd}
+      />
+    ))
+    .setPropertyViewFn((children) => (
+      <>
+        {children.text.propertyView({
+          label: trans("table.columnValue"),
+          tooltip: ColumnValueTooltip,
+        })}
+        {children.onEvent.propertyView()}
+      </>
+    ))
+    .build();
+})();
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnProgressComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnProgressComp.tsx
new file mode 100644
index 0000000000..7e06f4c8ee
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnProgressComp.tsx
@@ -0,0 +1,166 @@
+import React, { useState, useRef, useEffect, useCallback, useMemo } from "react";
+import { NumberControl } from "comps/controls/codeControl";
+import { trans } from "i18n";
+import { default as InputNumber } from "antd/es/input-number";
+import { ColumnTypeCompBuilder, ColumnTypeViewFn } from "../columnTypeCompBuilder";
+import { ColumnValueTooltip } from "../simpleColumnTypeComps";
+import { ProgressStyle } from "comps/controls/styleControlConstants";
+import { useStyle } from "comps/controls/styleControl";
+import { BoolControl } from "comps/controls/boolControl";
+import { ProgressStyled as Progress } from "comps/comps/progressComp";
+import { TableMinusIcon, TablePlusIcon } from "lowcoder-design";
+import styled from "styled-components";
+
+const ProgressStyled = styled(Progress)`
+  display: flex;
+  align-items: center;
+  .ant-progress-outer {
+    height: 22px;
+    display: flex;
+    align-items: center;
+  }
+  .ant-progress-text {
+    margin-left: 6px;
+  }
+`;
+
+const InputNumberStyled = styled(InputNumber)`
+  background: transparent !important;
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  top: 0;
+  .ant-input-number-input-wrap {
+    height: 100%;
+    display: flex;
+    align-items: center;
+  }
+  .ant-input-number-handler-wrap {
+    top: 1.5px;
+    right: 1.5px;
+    height: calc(100% - 3px);
+    border-radius: 0;
+  }
+  .ant-input-number-handler-up {
+    border-bottom: 1px solid #d7d9e0;
+  }
+  .ant-input-number-handler-up,
+  .ant-input-number-handler-down {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    > span {
+      width: 16px;
+      height: 16px;
+      margin-top: 0;
+      position: unset;
+      transform: none;
+    }
+    &:hover {
+      &:not(.ant-input-number-handler-up-disabled):not(.ant-input-number-handler-down-disabled)
+        path {
+        fill: #315efb;
+      }
+    }
+  }
+`;
+
+const childrenMap = {
+  text: NumberControl,
+  showValue: BoolControl,
+};
+
+const getBaseValue: ColumnTypeViewFn<typeof childrenMap, number, number> = (props) => props.text;
+
+type ProgressEditProps = {
+  value: number;
+  onChange: (value: number) => void;
+  onChangeEnd: () => void;
+};
+
+const ProgressEdit = React.memo((props: ProgressEditProps) => {
+  const [currentValue, setCurrentValue] = useState(props.value);
+  const mountedRef = useRef(true);
+
+  // Cleanup on unmount
+  useEffect(() => {
+    return () => {
+      mountedRef.current = false;
+      setCurrentValue(0);
+    };
+  }, []);
+
+  const handleChange = useCallback((value: string | number | null) => {
+    if (!mountedRef.current) return;
+    const newValue = typeof value === 'number' ? value : 0;
+    props.onChange(newValue);
+    setCurrentValue(newValue);
+  }, [props.onChange]);
+
+  const handleBlur = useCallback(() => {
+    if (!mountedRef.current) return;
+    props.onChangeEnd();
+  }, [props.onChangeEnd]);
+
+  const handlePressEnter = useCallback(() => {
+    if (!mountedRef.current) return;
+    props.onChangeEnd();
+  }, [props.onChangeEnd]);
+
+  return (
+    <InputNumberStyled
+      min={0}
+      max={100}
+      value={currentValue}
+      autoFocus
+      variant="borderless"
+      controls={{ upIcon: <TablePlusIcon />, downIcon: <TableMinusIcon /> }}
+      onChange={handleChange}
+      onBlur={handleBlur}
+      onPressEnter={handlePressEnter}
+    />
+  );
+});
+
+ProgressEdit.displayName = 'ProgressEdit';
+
+export const ProgressComp = (function () {
+  return new ColumnTypeCompBuilder(
+    childrenMap,
+    (props, dispatch) => {
+      const value = props.changeValue ?? getBaseValue(props, dispatch);
+      const Progress = () => {
+        const style = useStyle(ProgressStyle);
+        return (
+          <ProgressStyled percent={Math.round(value)} showInfo={props.showValue} $style={style} />
+        );
+      };
+      return <Progress />;
+    },
+    (nodeValue) => nodeValue.text.value,
+    getBaseValue
+  )
+    .setEditViewFn((props) => {
+      return (
+        <ProgressEdit
+          value={props.value}
+          onChange={props.onChange}
+          onChangeEnd={props.onChangeEnd}
+        />
+      );
+    })
+    .setPropertyViewFn((children) => {
+      return (
+        <>
+          {children.text.propertyView({
+            label: trans("table.columnValue"),
+            tooltip: ColumnValueTooltip,
+          })}
+          {children.showValue.propertyView({
+            label: trans("table.showValue"),
+          })}
+        </>
+      );
+    })
+    .build();
+})();
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnRatingComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnRatingComp.tsx
new file mode 100644
index 0000000000..fc44cd9367
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnRatingComp.tsx
@@ -0,0 +1,139 @@
+import React, { useState, useRef, useEffect, useCallback, useMemo } from "react";
+import { NumberControl } from "comps/controls/codeControl";
+import { trans } from "i18n";
+import { ColumnTypeCompBuilder, ColumnTypeViewFn } from "../columnTypeCompBuilder";
+import { ColumnValueTooltip } from "../simpleColumnTypeComps";
+import styled from "styled-components";
+import { default as Rate } from "antd/es/rate";
+
+const RateStyled = styled(Rate)<{ isEdit?: boolean }>`
+  display: inline-flex;
+  align-items: center;
+  width: 100%;
+  overflow-x: auto;
+  overflow-x: overlay;
+  color: #ffd400;
+  display: block;
+  .ant-rate-star > div {
+    height: 18px;
+    width: 18px;
+  }
+  .ant-rate-star-half .ant-rate-star-first,
+  .ant-rate-star-full .ant-rate-star-second {
+    color: #ffd400;
+    position: absolute;
+  }
+  .ant-rate-star-first {
+    width: 100%;
+  }
+  .ant-rate-star-first,
+  .ant-rate-star-second {
+    display: inline-flex;
+    align-items: center;
+    color: #d7d9e0;
+    max-height: 20px;
+    bottom: 0;
+  }
+  svg {
+    height: 18px;
+    width: 18px;
+  }
+`;
+
+const Wrapper = styled.div`
+  background: transparent !important;
+  padding: 0 8px;
+`;
+
+const childrenMap = {
+  text: NumberControl,
+};
+
+const getBaseValue: ColumnTypeViewFn<typeof childrenMap, number, number> = (props) => props.text;
+
+type RatingEditProps = {
+  value: number;
+  onChange: (value: number) => void;
+  onChangeEnd: () => void;
+};
+
+const RatingEdit = React.memo((props: RatingEditProps) => {
+  const [currentValue, setCurrentValue] = useState(props.value);
+  const mountedRef = useRef(true);
+
+  // Cleanup on unmount
+  useEffect(() => {
+    return () => {
+      mountedRef.current = false;
+      setCurrentValue(0);
+    };
+  }, []);
+
+  const handleChange = useCallback((value: number) => {
+    if (!mountedRef.current) return;
+    props.onChange(value);
+    setCurrentValue(value);
+  }, [props.onChange]);
+
+  const handleBlur = useCallback((e: React.FocusEvent<HTMLDivElement>) => {
+    if (!mountedRef.current) return;
+    if (!e.currentTarget?.contains(e.relatedTarget)) {
+      props.onChangeEnd();
+    }
+  }, [props.onChangeEnd]);
+
+  const handleKeyDown = useCallback((e: React.KeyboardEvent<HTMLUListElement>) => {
+    if (!mountedRef.current) return;
+    if (e.key === "Enter") {
+      props.onChangeEnd();
+    }
+  }, [props.onChangeEnd]);
+
+  return (
+    <Wrapper
+      onBlur={handleBlur}
+    >
+      <RateStyled
+        autoFocus
+        isEdit={true}
+        value={currentValue}
+        onChange={handleChange}
+        onKeyDown={handleKeyDown}
+      />
+    </Wrapper>
+  );
+});
+
+RatingEdit.displayName = 'RatingEdit';
+
+export const RatingComp = (function () {
+  return new ColumnTypeCompBuilder(
+    childrenMap,
+    (props, dispatch) => {
+      const value = props.changeValue ?? getBaseValue(props, dispatch);
+      return <RateStyled disabled value={value} />;
+    },
+    (nodeValue) => nodeValue.text.value,
+    getBaseValue
+  )
+    .setEditViewFn((props) => {
+      return (
+        <RatingEdit
+          value={props.value}
+          onChange={props.onChange}
+          onChangeEnd={props.onChangeEnd}
+        />
+      );
+    })
+    .setPropertyViewFn((children) => {
+      return (
+        <>
+          {children.text.propertyView({
+            label: trans("table.columnValue"),
+            tooltip: ColumnValueTooltip,
+          })}
+        </>
+      );
+    })
+    .build();
+})();
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnSelectComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnSelectComp.tsx
new file mode 100644
index 0000000000..b54be87997
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnSelectComp.tsx
@@ -0,0 +1,232 @@
+import React, { useState, useRef, useEffect, useCallback, useMemo } from "react";
+
+import { SelectUIView } from "comps/comps/selectInputComp/selectCompConstants";
+import { StringControl, BoolCodeControl } from "comps/controls/codeControl";
+import { IconControl } from "comps/controls/iconControl";
+import { MultiCompBuilder } from "comps/generators";
+import { optionsControl } from "comps/controls/optionsControl";
+import { disabledPropertyView, hiddenPropertyView } from "comps/utils/propertyUtils";
+import { trans } from "i18n";
+import { ColumnTypeCompBuilder, ColumnTypeViewFn } from "../columnTypeCompBuilder";
+import { ColumnValueTooltip } from "../simpleColumnTypeComps";
+import { styled } from "styled-components";
+import { clickEvent, eventHandlerControl, doubleClickEvent } from "comps/controls/eventHandlerControl";
+
+const Wrapper = styled.div`
+  display: inline-flex;
+  align-items: center;
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  top: 0;
+  background: transparent !important;
+  padding: 8px;
+
+  > div {
+    width: 100%;
+    height: 100%;
+  }
+
+  .ant-select {
+    height: 100%;
+    .ant-select-selector {
+      padding: 0 7px;
+      height: 100%;
+      overflow: hidden;
+      .ant-select-selection-item {
+        display: inline-flex;
+        align-items: center;
+        padding-right: 24px;
+      }
+    }
+    .ant-select-arrow {
+      height: calc(100% - 3px);
+      width: fit-content;
+      top: 1.5px;
+      margin-top: 0;
+      background-color: white;
+      right: 1.5px;
+      border-right: 1px solid #d7d9e0;
+      cursor: pointer;
+      pointer-events: auto;
+      svg {
+        min-width: 18px;
+        min-height: 18px;
+      }
+      &:hover svg path {
+        fill: #315efb;
+      }
+    }
+    .ant-select-selector .ant-select-selection-search {
+      left: 7px;
+      input {
+        height: 100%;
+      }
+    }
+    &.ant-select-open {
+      .ant-select-arrow {
+        border-right: none;
+        border-left: 1px solid #d7d9e0;
+        svg g path {
+          fill: #315efb;
+        }
+      }
+      .ant-select-selection-item {
+        opacity: 0.4;
+      }
+    }
+  }
+`;
+
+const SelectOptionEventOptions = [clickEvent, doubleClickEvent] as const;
+
+// Create a new option type with event handlers for each option
+const SelectOptionWithEvents = new MultiCompBuilder(
+  {
+    value: StringControl,
+    label: StringControl,
+    prefixIcon: IconControl,
+    disabled: BoolCodeControl,
+    hidden: BoolCodeControl,
+    onEvent: eventHandlerControl(SelectOptionEventOptions),
+  },
+  (props) => props
+)
+  .setPropertyViewFn((children) => (
+    <>
+      {children.label.propertyView({ label: trans("label") })}
+      {children.value.propertyView({ label: trans("value") })}
+      {children.prefixIcon.propertyView({ label: trans("button.prefixIcon") })}
+      {disabledPropertyView(children)}
+      {hiddenPropertyView(children)}
+      {children.onEvent.propertyView()}
+    </>
+  ))
+  .build();
+
+const SelectOptionWithEventsControl = optionsControl(SelectOptionWithEvents, {
+  initOptions: [
+    { label: trans("optionsControl.optionI", { i: 1 }), value: "1" },
+    { label: trans("optionsControl.optionI", { i: 2 }), value: "2" },
+  ],
+  uniqField: "value",
+});
+
+const childrenMap = {
+  text: StringControl,
+  options: SelectOptionWithEventsControl,
+  onEvent: eventHandlerControl(SelectOptionEventOptions),
+};
+
+const getBaseValue: ColumnTypeViewFn<typeof childrenMap, string, string> = (props) => props.text;
+
+type SelectEditProps = {
+  initialValue: string;
+  onChange: (value: string) => void;
+  onChangeEnd: () => void;
+  options: any[];
+  onMainEvent?: (eventName: string) => void;
+};
+
+const SelectEdit = React.memo((props: SelectEditProps) => {
+  const [currentValue, setCurrentValue] = useState(props.initialValue);
+  const mountedRef = useRef(true);
+  const defaultProps: any = {};
+
+  // Cleanup on unmount
+  useEffect(() => {
+    return () => {
+      mountedRef.current = false;
+      setCurrentValue('');
+    };
+  }, []);
+
+  const handleChange = useCallback((val: string) => {
+    if (!mountedRef.current) return;
+    props.onChange(val);
+    setCurrentValue(val);
+    
+    // Trigger the specific option's event handler
+    const selectedOption = props.options.find(option => option.value === val);
+    if (selectedOption?.onEvent) {
+      selectedOption.onEvent("click");
+    }
+    
+    // Also trigger the main component's event handler
+    if (props.onMainEvent) {
+      props.onMainEvent("click");
+    }
+  }, [props.onChange, props.options, props.onMainEvent]);
+
+  const handleEvent = useCallback(async (eventName: string) => {
+    if (!mountedRef.current) return [] as unknown[];
+    if (eventName === "blur") {
+      props.onChangeEnd();
+    }
+    return [] as unknown[];
+  }, [props.onChangeEnd]);
+
+  const memoizedOptions = useMemo(() => props.options, [props.options]);
+
+  return (
+    <SelectUIView
+      autoFocus
+      allowClear
+      value={currentValue}
+      options={memoizedOptions}
+      onChange={handleChange}
+      onEvent={handleEvent}
+      // @ts-ignore
+      style={{}}
+      {...defaultProps}
+    />
+  );
+});
+
+SelectEdit.displayName = 'SelectEdit';
+
+export const ColumnSelectComp = (function () {
+  return new ColumnTypeCompBuilder(
+    childrenMap,
+    (props, dispatch) => {
+      const value = props.changeValue ?? getBaseValue(props, dispatch);
+      const option = props.options.find(x => x.value === value);
+      return (
+        <>
+          <span>{option?.prefixIcon}</span>
+          <span>{option?.label}</span>
+        </>
+      );
+    },
+    (nodeValue) => nodeValue.text.value,
+    getBaseValue,
+  )
+    .setEditViewFn((props) => {
+      return (
+        <Wrapper>
+          <SelectEdit
+            initialValue={props.value}
+            options={props.otherProps?.options || []}
+            onChange={props.onChange}
+            onChangeEnd={props.onChangeEnd}
+            onMainEvent={props.otherProps?.onEvent}
+          />
+        </Wrapper>
+      )
+    })
+    .setPropertyViewFn((children) => {
+      return (
+        <>
+          {children.text.propertyView({
+            label: trans("table.columnValue"),
+            tooltip: ColumnValueTooltip,
+          })}
+          {children.options.propertyView({
+            title: trans("optionsControl.optionList"),
+          })}
+          {children.onEvent.propertyView()}
+        </>
+      );
+    })
+    .build();
+})();
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnStatusComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnStatusComp.tsx
new file mode 100644
index 0000000000..61a8fbc6f8
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnStatusComp.tsx
@@ -0,0 +1,192 @@
+import { default as Badge } from "antd/es/badge";
+import {
+  ColumnTypeCompBuilder,
+  ColumnTypeViewFn,
+} from "comps/comps/tableComp/column/columnTypeCompBuilder";
+import { trans } from "i18n";
+import { StringControl, stringUnionControl } from "comps/controls/codeControl";
+import { DropdownStyled, Wrapper } from "./columnTagsComp";
+import React, { ReactNode, useContext, useState, useCallback, useRef, useEffect, useMemo } from "react";
+import { StatusContext } from "components/table/EditableCell";
+import { CustomSelect, PackUpIcon, ScrollBar } from "lowcoder-design";
+import { PresetStatusColorType } from "antd/es/_util/colors";
+
+export const ColumnValueTooltip = trans("table.columnValueTooltip");
+
+export const BadgeStatusOptions = [
+  "none",
+  "success",
+  "error",
+  "default",
+  "warning",
+  "processing",
+] as const;
+
+export type StatusType = PresetStatusColorType | "none";
+
+const childrenMap = {
+  text: StringControl,
+  status: stringUnionControl(BadgeStatusOptions, "none"),
+};
+
+const getBaseValue: ColumnTypeViewFn<
+  typeof childrenMap,
+  { value: string; status: StatusType },
+  { value: string; status: StatusType }
+> = (props) => ({
+  value: props.text,
+  status: props.status,
+});
+
+type StatusEditPropsType = {
+  value: { value: string; status: StatusType };
+  onChange: (value: { value: string; status: StatusType }) => void;
+  onChangeEnd: () => void;
+};
+
+const StatusEdit = React.memo((props: StatusEditPropsType) => {
+  const defaultStatus = useContext(StatusContext);
+  const [status, setStatus] = useState<Array<{ text: string; status: StatusType }>>(() => {
+    const result: Array<{ text: string; status: StatusType }> = [];
+    defaultStatus.forEach((item) => {
+      if (item.text.includes(",")) {
+        item.text.split(",").forEach((tag) => result.push({ text: tag, status: "none" }));
+      }
+      result.push({ text: item.text, status: item.status });
+    });
+    return result;
+  });
+  const [open, setOpen] = useState(false);
+  const mountedRef = useRef(true);
+
+  // Cleanup on unmount
+  useEffect(() => {
+    return () => {
+      mountedRef.current = false;
+      setStatus([]);
+      setOpen(false);
+    };
+  }, []);
+
+  // Update status when defaultStatus changes
+  useEffect(() => {
+    if (!mountedRef.current) return;
+    
+    const result: Array<{ text: string; status: StatusType }> = [];
+    defaultStatus.forEach((item) => {
+      if (item.text.includes(",")) {
+        item.text.split(",").forEach((tag) => result.push({ text: tag, status: "none" }));
+      }
+      result.push({ text: item.text, status: item.status });
+    });
+    setStatus(result);
+  }, [defaultStatus]);
+
+  const handleSearch = useCallback((value: string) => {
+    if (!mountedRef.current) return;
+    
+    if (defaultStatus.findIndex((item) => item.text.includes(value)) < 0) {
+      setStatus([...defaultStatus, { text: value, status: "none" }]);
+    } else {
+      setStatus(defaultStatus);
+    }
+    props.onChange({
+      value,
+      status: status.find((item) => item.text === value)?.status || "none",
+    });
+  }, [defaultStatus, status, props.onChange]);
+
+  const handleChange = useCallback((value: string) => {
+    if (!mountedRef.current) return;
+    props.onChange({
+      value,
+      status: status.find((item) => item.text === value)?.status || "none",
+    });
+    setOpen(false);
+  }, [status, props.onChange]);
+
+  const handleBlur = useCallback(() => {
+    if (!mountedRef.current) return;
+    props.onChangeEnd();
+    setOpen(false);
+  }, [props.onChangeEnd]);
+
+  const handleFocus = useCallback(() => {
+    if (!mountedRef.current) return;
+    setOpen(true);
+  }, []);
+
+  const handleClick = useCallback(() => {
+    if (!mountedRef.current) return;
+    setOpen(!open);
+  }, [open]);
+
+  const memoizedOptions = useMemo(() => 
+    BadgeStatusOptions.map((value, index) => (
+      <CustomSelect.Option value={value} key={index}>
+        {value === "none" ? value : <Badge status={value} text={value} />}
+      </CustomSelect.Option>
+    )),
+    []
+  );
+
+  return (
+    <Wrapper>
+      <CustomSelect
+        autoFocus
+        defaultOpen
+        variant="borderless"
+        optionLabelProp="children"
+        open={open}
+        defaultValue={props.value.value}
+        style={{ width: "100%" }}
+        suffixIcon={<PackUpIcon />}
+        showSearch
+        onSearch={handleSearch}
+        onChange={handleChange}
+        popupRender={(originNode: ReactNode) => (
+          <DropdownStyled>
+            <ScrollBar style={{ maxHeight: "256px" }}>{originNode}</ScrollBar>
+          </DropdownStyled>
+        )}
+        styles={{ popup: { root: { marginTop: "7px", padding: "8px 0 6px 0" }}}}
+        onBlur={handleBlur}
+        onFocus={handleFocus}
+        onClick={handleClick}
+      >
+        {memoizedOptions}
+      </CustomSelect>
+    </Wrapper>
+  );
+});
+
+StatusEdit.displayName = 'StatusEdit';
+
+export const BadgeStatusComp = (function () {
+  return new ColumnTypeCompBuilder(
+    childrenMap,
+    (props, dispatch) => {
+      const text = props.changeValue?.value ?? getBaseValue(props, dispatch).value;
+      const status = props.changeValue?.status ?? getBaseValue(props, dispatch).status;
+      return status === "none" ? text : <Badge status={status} text={text}/>;
+    },
+    (nodeValue) => [nodeValue.status.value, nodeValue.text.value].filter((t) => t).join(" "),
+    getBaseValue
+  )
+    .setEditViewFn((props) => (
+      <StatusEdit value={props.value} onChange={props.onChange} onChangeEnd={props.onChangeEnd} />
+    ))
+    .setPropertyViewFn((children) => (
+      <>
+        {children.text.propertyView({
+          label: trans("table.columnValue"),
+          tooltip: ColumnValueTooltip,
+        })}
+        {children.status.propertyView({
+          label: trans("table.status"),
+          tooltip: trans("table.statusTooltip"),
+        })}
+      </>
+    ))
+    .build();
+})();
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnSwitchComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnSwitchComp.tsx
new file mode 100644
index 0000000000..0cdeee48a4
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnSwitchComp.tsx
@@ -0,0 +1,168 @@
+import { BoolCodeControl } from "comps/controls/codeControl";
+import { trans } from "i18n";
+import { ColumnTypeCompBuilder, ColumnTypeViewFn } from "../columnTypeCompBuilder";
+import { ColumnValueTooltip } from "../simpleColumnTypeComps";
+import { InputFieldStyle } from "comps/controls/styleControlConstants";
+import styled from "styled-components";
+import { default as Switch } from "antd/es/switch";
+import { styleControl } from "comps/controls/styleControl";
+import { RefControl } from "comps/controls/refControl";
+import { booleanExposingStateControl } from "comps/controls/codeStateControl";
+import { changeEvent, eventHandlerControl } from "comps/controls/eventHandlerControl";
+import { disabledPropertyView } from "comps/utils/propertyUtils";
+import React, { useCallback, useRef, useEffect } from "react";
+
+const EventOptions = [
+  changeEvent,
+  {
+    label: trans("switchComp.open"),
+    value: "true",
+    description: trans("switchComp.openDesc"),
+  },
+  {
+    label: trans("switchComp.close"),
+    value: "false",
+    description: trans("switchComp.closeDesc"),
+  },
+] as const;
+
+const Wrapper = styled.div`
+  background: transparent !important;
+  padding: 0 8px;
+`
+
+const childrenMap = {
+  value: booleanExposingStateControl("value"),
+  switchState: BoolCodeControl,
+  onEvent: eventHandlerControl(EventOptions),
+  disabled: BoolCodeControl,
+  style: styleControl(InputFieldStyle),
+  // viewRef: RefControl<HTMLButtonElement>,
+};
+
+const getBaseValue: ColumnTypeViewFn<typeof childrenMap, boolean, boolean> = (props) => props.switchState;
+
+const SwitchView = React.memo(({ value, disabled, onEvent, valueControl }: {
+  value: boolean;
+  disabled: boolean;
+  // viewRef: (viewRef: HTMLButtonElement | null) => void;
+  onEvent: (event: string) => void;
+  valueControl: { onChange: (value: boolean) => void };
+}) => {
+  const mountedRef = useRef(true);
+
+  useEffect(() => {
+    return () => {
+      mountedRef.current = false;
+    };
+  }, []);
+
+  const handleChange = useCallback((checked: boolean) => {
+    if (!mountedRef.current) return;
+    valueControl.onChange(checked);
+    onEvent("change");
+    onEvent(checked ? "true" : "false");
+  }, [valueControl, onEvent]);
+
+  return (
+    <Switch 
+      checked={value}
+      disabled={disabled || true}
+      // ref={viewRef}
+      onChange={handleChange}
+    />
+  );
+});
+
+SwitchView.displayName = 'SwitchView';
+
+const SwitchEdit = React.memo(({ value, onChange, onChangeEnd }: {
+  value: boolean;
+  onChange: (value: boolean) => void;
+  onChangeEnd: () => void;
+}) => {
+  const mountedRef = useRef(true);
+
+  useEffect(() => {
+    return () => {
+      mountedRef.current = false;
+    };
+  }, []);
+
+  const handleChange = useCallback((checked: boolean) => {
+    if (!mountedRef.current) return;
+    onChange(checked);
+  }, [onChange]);
+
+  const handleBlur = useCallback(() => {
+    if (!mountedRef.current) return;
+    onChangeEnd();
+  }, [onChangeEnd]);
+
+  return (
+    <Wrapper onBlur={handleBlur}>
+      <Switch
+        autoFocus
+        defaultChecked={value}
+        disabled={false}
+        onChange={handleChange}
+      />
+    </Wrapper>
+  );
+});
+
+SwitchEdit.displayName = 'SwitchEdit';
+
+export const SwitchComp = (function () {
+  return new ColumnTypeCompBuilder(
+    childrenMap,
+    (props, dispatch) => {
+      const value = props.changeValue ?? getBaseValue(props, dispatch);
+      return (
+        <SwitchView
+          value={value}
+          disabled={props.disabled}
+          // viewRef={props.viewRef}
+          onEvent={props.onEvent}
+          valueControl={props.value}
+        />
+      );
+    },
+    (nodeValue) => nodeValue.switchState.value,
+    getBaseValue
+  )
+    .setEditViewFn((props) => {
+      return (
+        <Wrapper
+          onBlur={() => {
+            props.onChangeEnd()
+          }}
+        >
+          <Switch
+            autoFocus
+            defaultChecked={props.value}
+            disabled={false}
+            onChange={(checked, e) => {
+              props.onChange(checked);
+              props.otherProps?.onEvent?.("change");
+              props.otherProps?.onEvent?.(checked ? "true" : "false");
+            }}
+          />
+        </Wrapper>
+      );
+    })
+    .setPropertyViewFn((children) => {
+      return (
+        <>
+          {children.switchState.propertyView({
+            label: trans("table.columnValue"),
+            tooltip: ColumnValueTooltip,
+          })}
+          {children.onEvent.propertyView()}
+          {disabledPropertyView(children)}
+          
+        </>
+      );
+    })
+    .build();
+})();
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnTagsComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnTagsComp.tsx
new file mode 100644
index 0000000000..1e6a6e1a8a
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnTagsComp.tsx
@@ -0,0 +1,465 @@
+import { default as Tag } from "antd/es/tag";
+import { PresetStatusColorTypes } from "antd/es/_util/colors";
+import { TagsContext } from "components/table/EditableCell";
+import {
+  ColumnTypeCompBuilder,
+  ColumnTypeViewFn,
+} from "comps/comps/tableComp/column/columnTypeCompBuilder";
+import { ColumnValueTooltip } from "comps/comps/tableComp/column/simpleColumnTypeComps";
+import { codeControl } from "comps/controls/codeControl";
+import { trans } from "i18n";
+import styled from "styled-components";
+import _ from "lodash";
+import React, { ReactNode, useContext, useState, useRef, useEffect, useCallback, useMemo } from "react";
+import { toJson } from "really-relaxed-json";
+import { hashToNum } from "util/stringUtils";
+import { CustomSelect, PackUpIcon } from "lowcoder-design";
+import { ScrollBar } from "lowcoder-design";
+import { ColoredTagOptionControl } from "comps/controls/optionsControl";
+import { clickEvent, eventHandlerControl } from "comps/controls/eventHandlerControl";
+
+const colors = PresetStatusColorTypes;
+
+const isStringArray = (value: any) => {
+  return (
+    _.isArray(value) &&
+    value.every((v) => {
+      const type = typeof v;
+      return type === "string" || type === "number" || type === "boolean";
+    })
+  );
+};
+
+// accept string, number, boolean and array input
+const TagsControl = codeControl<Array<string> | string>(
+  (value) => {
+    if (isStringArray(value)) {
+      return value;
+    }
+    const valueType = typeof value;
+    if (valueType === "string") {
+      try {
+        const result = JSON.parse(toJson(value));
+        if (isStringArray(result)) {
+          return result;
+        }
+        return value;
+      } catch (e) {
+        return value;
+      }
+    } else if (valueType === "number" || valueType === "boolean") {
+      return value;
+    }
+    throw new TypeError(
+      `Type "Array<string> | string" is required, but find value: ${JSON.stringify(value)}`
+    );
+  },
+  { expectedType: "string | Array<string>", codeType: "JSON" }
+);
+
+function getTagColor(tagText : any, tagOptions: any[]) {
+  const foundOption = tagOptions.find((option: { label: any; }) => option.label === tagText);
+  if (foundOption) {
+    if (foundOption.colorType === "preset") {
+      return foundOption.presetColor;
+    } else if (foundOption.colorType === "custom") {
+      return undefined; // For custom colors, we'll use style instead
+    }
+    // Backward compatibility - if no colorType specified, assume it's the old color field
+    return foundOption.color;
+  }
+  // Default fallback
+  const index = Math.abs(hashToNum(tagText)) % colors.length;
+  return colors[index];
+}
+
+function getTagStyle(tagText: any, tagOptions: any[]) {
+  const foundOption = tagOptions.find((option: { label: any; }) => option.label === tagText);
+  if (foundOption) {
+    const style: any = {};
+    
+    // Handle color styling
+    if (foundOption.colorType === "custom") {
+      style.backgroundColor = foundOption.color;
+      style.color = foundOption.textColor;
+      style.border = `1px solid ${foundOption.color}`;
+    }
+    
+    // Add border styling if specified
+    if (foundOption.border) {
+      style.borderColor = foundOption.border;
+      if (!foundOption.colorType || foundOption.colorType !== "custom") {
+        style.border = `1px solid ${foundOption.border}`;
+      }
+    }
+    
+    // Add border radius if specified
+    if (foundOption.radius) {
+      style.borderRadius = foundOption.radius;
+    }
+    
+    // Add margin if specified
+    if (foundOption.margin) {
+      style.margin = foundOption.margin;
+    }
+    
+    // Add padding if specified
+    if (foundOption.padding) {
+      style.padding = foundOption.padding;
+    }
+    
+    return style;
+  }
+  return {};
+}
+
+function getTagIcon(tagText: any, tagOptions: any[]) {
+  const foundOption = tagOptions.find(option => option.label === tagText);
+  return foundOption ? foundOption.icon : undefined;
+}
+
+const childrenMap = {
+  text: TagsControl,
+  tagColors: ColoredTagOptionControl,
+  onEvent: eventHandlerControl([clickEvent]),
+};
+
+const getBaseValue: ColumnTypeViewFn<typeof childrenMap, string | string[], string | string[]> = (
+  props
+) => props.text;
+
+type TagEditPropsType = {
+  value: string | string[];
+  onChange: (value: string | string[]) => void;
+  onChangeEnd: () => void;
+  tagOptions: any[];
+};
+
+export const Wrapper = styled.div`
+  display: inline-flex;
+  align-items: center;
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  top: 0;
+  left: 0;
+  background: transparent !important;
+  padding: 8px;
+
+  > div {
+    width: 100%;
+    height: 100%;
+  }
+
+  .ant-select {
+    height: 100%;
+    .ant-select-selector {
+      padding: 0 7px;
+      height: 100%;
+      overflow: hidden;
+      .ant-select-selection-item {
+        display: inline-flex;
+        align-items: center;
+        padding-right: 24px;
+      }
+    }
+    .ant-select-arrow {
+      height: calc(100% - 3px);
+      width: fit-content;
+      top: 1.5px;
+      margin-top: 0;
+      background-color: white;
+      right: 1.5px;
+      border-right: 1px solid #d7d9e0;
+      cursor: pointer;
+      pointer-events: auto;
+      svg {
+        min-width: 18px;
+        min-height: 18px;
+      }
+      &:hover svg path {
+        fill: #315efb;
+      }
+    }
+    .ant-select-selector .ant-select-selection-search {
+      left: 7px;
+      input {
+        height: 100%;
+      }
+    }
+    &.ant-select-open {
+      .ant-select-arrow {
+        border-right: none;
+        border-left: 1px solid #d7d9e0;
+        svg g path {
+          fill: #315efb;
+        }
+      }
+      .ant-select-selection-item {
+        opacity: 0.4;
+      }
+    }
+  }
+  .ant-tag {
+    margin-left: 5px;
+  }
+  .ant-tag svg {
+    margin-right: 4px;
+  }
+`;
+
+export const DropdownStyled = styled.div`
+  .ant-select-item {
+    padding: 3px 8px;
+    margin: 0 0 2px 8px;
+    border-radius: 4px;
+
+    &.ant-select-item-option-active {
+      background-color: #f2f7fc;
+    }
+  }
+  .ant-select-item-option-content {
+    display: flex;
+    align-items: center;
+  }
+  .ant-tag {
+    margin-right: 0;
+  }
+  .ant-tag svg {
+    margin-right: 4px;
+  }
+`;
+
+export const TagStyled = styled(Tag)`
+  margin-right: 8px;
+  cursor: pointer;
+  svg {
+    margin-right: 4px;
+  }
+`;
+
+const TagEdit = React.memo((props: TagEditPropsType) => {
+  const defaultTags = useContext(TagsContext);
+  const [tags, setTags] = useState(() => {
+    const result: string[] = [];
+    defaultTags.forEach((item) => {
+      if (item.split(",")[1]) {
+        item.split(",").forEach((tag) => result.push(tag));
+      }
+      result.push(item);
+    });
+    return result;
+  });
+  const [open, setOpen] = useState(false);
+  const mountedRef = useRef(true);
+
+  // Memoize tag options to prevent unnecessary re-renders
+  const memoizedTagOptions = useMemo(() => props.tagOptions || [], [props.tagOptions]);
+
+  // Cleanup on unmount
+  useEffect(() => {
+    return () => {
+      mountedRef.current = false;
+      setTags([]);
+      setOpen(false);
+    };
+  }, []);
+
+  // Update tags when defaultTags changes
+  useEffect(() => {
+    if (!mountedRef.current) return;
+    
+    const result: string[] = [];
+    defaultTags.forEach((item) => {
+      if (item.split(",")[1]) {
+        item.split(",").forEach((tag) => result.push(tag));
+      }
+      result.push(item);
+    });
+    setTags(result);
+  }, [defaultTags]);
+
+  const handleSearch = useCallback((value: string) => {
+    if (!mountedRef.current) return;
+    
+    if (defaultTags.findIndex((item) => item.includes(value)) < 0) {
+      setTags([...defaultTags, value]);
+    } else {
+      setTags(defaultTags);
+    }
+    props.onChange(value);
+  }, [defaultTags, props.onChange]);
+
+  const handleChange = useCallback((value: string | string[]) => {
+    if (!mountedRef.current) return;
+    props.onChange(value);
+    setOpen(false);
+  }, [props.onChange]);
+
+  const handleBlur = useCallback(() => {
+    if (!mountedRef.current) return;
+    props.onChangeEnd();
+    setOpen(false);
+  }, [props.onChangeEnd]);
+
+  const handleTagClick = useCallback((tagText: string, e: React.MouseEvent) => {
+    e.stopPropagation();
+    const foundOption = memoizedTagOptions.find(option => option.label === tagText);
+    if (foundOption && foundOption.onEvent) {
+      foundOption.onEvent("click");
+    }
+  }, [memoizedTagOptions]);
+
+  return (
+    <Wrapper>
+      <CustomSelect
+        autoFocus
+        defaultOpen
+        variant="borderless"
+        optionLabelProp="children"
+        showSearch
+        defaultValue={props.value}
+        style={{ width: "100%" }}
+        open={open}
+        allowClear={true}
+        suffixIcon={<PackUpIcon />}
+        onSearch={handleSearch}
+        onChange={handleChange}
+        popupRender={(originNode: ReactNode) => (
+          <DropdownStyled>
+            <ScrollBar style={{ maxHeight: "256px" }}>{originNode}</ScrollBar>
+          </DropdownStyled>
+        )}
+        styles={{ popup: { root: { marginTop: "7px", padding: "8px 0 6px 0" }}}}
+        onFocus={() => {
+          if (mountedRef.current) {
+            setOpen(true);
+          }
+        }}
+        onBlur={handleBlur}
+        onClick={() => {
+          if (mountedRef.current) {
+            setOpen(!open);
+          }
+        }}
+      >
+        {tags.map((value, index) => (
+          <CustomSelect.Option value={value} key={index}>
+            {value.split(",")[1] ? (
+              value.split(",").map((item, i) => {
+                const tagColor = getTagColor(item, memoizedTagOptions);
+                const tagIcon = getTagIcon(item, memoizedTagOptions);
+                const tagStyle = getTagStyle(item, memoizedTagOptions);
+                
+                return (
+                  <Tag 
+                    color={tagColor}
+                    icon={tagIcon} 
+                    key={i} 
+                    style={{ 
+                      marginRight: tagStyle.margin ? undefined : "8px", 
+                      cursor: "pointer",
+                      ...tagStyle 
+                    }}
+                    onClick={(e) => handleTagClick(item, e)}
+                  >
+                    {item}
+                  </Tag>
+                );
+              })
+            ) : (
+              <Tag 
+                color={getTagColor(value, memoizedTagOptions)} 
+                icon={getTagIcon(value, memoizedTagOptions)} 
+                key={index}
+                style={{
+                  cursor: "pointer",
+                  ...getTagStyle(value, memoizedTagOptions)
+                }}
+                onClick={(e) => handleTagClick(value, e)}
+              >
+                {value}
+              </Tag>
+            )}
+          </CustomSelect.Option>
+        ))}
+      </CustomSelect>
+    </Wrapper>
+  );
+});
+
+TagEdit.displayName = 'TagEdit';
+
+export const ColumnTagsComp = (function () {
+  return new ColumnTypeCompBuilder(
+    childrenMap,
+    (props, dispatch) => {
+      const tagOptions = props.tagColors;
+      let value = props.changeValue ?? getBaseValue(props, dispatch);
+      value = typeof value === "string" && value.split(",")[1] ? value.split(",") : value;
+      const tags = _.isArray(value) ? value : (value.length ? [value] : []);
+      
+      const handleTagClick = (tagText: string) => {
+        const foundOption = tagOptions.find(option => option.label === tagText);
+        if (foundOption && foundOption.onEvent) {
+          foundOption.onEvent("click");
+        }
+        // Also trigger the main component's event handler
+        if (props.onEvent) {
+          props.onEvent("click");
+        }
+      };
+      
+      const view = tags.map((tag, index) => {
+        // The actual eval value is of type number or boolean
+        const tagText = String(tag);
+        const tagColor = getTagColor(tagText, tagOptions);
+        const tagIcon = getTagIcon(tagText, tagOptions);
+        const tagStyle = getTagStyle(tagText, tagOptions);
+        
+        return (
+          <div key={`${tag.split(' ').join('_')}-${index}`}>
+            <TagStyled 
+              color={tagColor} 
+              icon={tagIcon} 
+              key={index}
+              style={tagStyle}
+              onClick={() => handleTagClick(tagText)}
+            >
+              {tagText}
+            </TagStyled>
+          </div>
+        );
+      });
+      return view;
+    },
+    (nodeValue) => {
+      const text = nodeValue.text.value;
+      return _.isArray(text) ? text.join(",") : text;
+    },
+    getBaseValue
+  )
+    .setEditViewFn((props) => {
+      const text = props.value;
+      const value = _.isArray(text) ? text.join(",") : text;
+      return <TagEdit 
+        value={value} 
+        onChange={props.onChange} 
+        onChangeEnd={props.onChangeEnd} 
+        tagOptions={props.otherProps?.tagColors || []} 
+      />;
+    })
+    .setPropertyViewFn((children) => (
+      <>
+        {children.text.propertyView({
+          label: trans("table.columnValue"),
+          tooltip: ColumnValueTooltip,
+        })}
+        {children.tagColors.propertyView({
+          title: "Tag Options",
+        })}
+        {children.onEvent.propertyView()}
+      </>
+    ))
+    .build();
+})();
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnTimeComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnTimeComp.tsx
new file mode 100644
index 0000000000..f338f0f645
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/columnTimeComp.tsx
@@ -0,0 +1,193 @@
+import { default as TimePicker } from "antd/es/time-picker";
+import {
+  ColumnTypeCompBuilder,
+  ColumnTypeViewFn,
+} from "comps/comps/tableComp/column/columnTypeCompBuilder";
+import { ColumnValueTooltip } from "comps/comps/tableComp/column/simpleColumnTypeComps";
+import { StringControl } from "comps/controls/codeControl";
+import { withDefault } from "comps/generators";
+import { formatPropertyView } from "comps/utils/propertyUtils";
+import { trans } from "i18n";
+import dayjs from "dayjs";
+import React, { useEffect, useRef, useState, useCallback } from "react";
+import styled from "styled-components";
+import { TIME_FORMAT } from "util/dateTimeUtils";
+import { hasIcon } from "comps/utils";
+import { IconControl } from "comps/controls/iconControl";
+
+const TimePickerStyled = styled(TimePicker)<{ $open: boolean }>`
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  top: 0;
+  padding: 0;
+  padding-left: 11px;
+  .ant-picker-input {
+    height: 100%;
+  }
+  input {
+    padding-right: 18px;
+    cursor: pointer;
+  }
+  &.ant-picker-focused .ant-picker-suffix svg g {
+    stroke: ${(props) => props.$open && "#315EFB"};
+  }
+  .ant-picker-suffix {
+    height: calc(100% - 1px);
+    position: absolute;
+    right: 0;
+    top: 0.5px;
+    display: flex;
+    align-items: center;
+    padding: 0 3px;
+  }
+`;
+
+const Wrapper = styled.div`
+  background: transparent !important;
+`;
+
+export function formatTime(time: string, format: string) {
+  const parsedTime = dayjs(time, TIME_FORMAT);
+  return parsedTime.isValid() ? parsedTime.format(format) : "";
+}
+
+const childrenMap = {
+  text: StringControl,
+  prefixIcon: IconControl,
+  suffixIcon: IconControl,
+  format: withDefault(StringControl, TIME_FORMAT),
+  inputFormat: withDefault(StringControl, TIME_FORMAT),
+};
+
+const getBaseValue: ColumnTypeViewFn<typeof childrenMap, string, string> = (props) => props.text;
+
+type TimeEditProps = {
+  value: string;
+  onChange: (value: string) => void;
+  onChangeEnd: () => void;
+  inputFormat: string;
+};
+
+export const TimeEdit = React.memo((props: TimeEditProps) => {
+  const pickerRef = useRef<any>();
+  const [panelOpen, setPanelOpen] = useState(true);
+  const mountedRef = useRef(true);
+
+  // Initialize tempValue with proper validation
+  const [tempValue, setTempValue] = useState<dayjs.Dayjs | null>(() => {
+    const initialValue = dayjs(props.value, TIME_FORMAT);
+    return initialValue.isValid() ? initialValue : dayjs("00:00:00", TIME_FORMAT);
+  });
+
+  // Memoize event handlers
+  const handleKeyDown = useCallback((e: React.KeyboardEvent<HTMLDivElement>) => {
+    if (e.key === "Enter" && !panelOpen) {
+      props.onChangeEnd();
+    }
+  }, [panelOpen, props.onChangeEnd]);
+
+  const handleMouseDown = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
+    e.stopPropagation();
+    e.preventDefault();
+  }, []);
+
+  const handleOpenChange = useCallback((open: boolean) => {
+    if (mountedRef.current) {
+      setPanelOpen(open);
+    }
+  }, []);
+
+  const handleChange = useCallback((value: dayjs.Dayjs | null, dateString: string | string[]) => {
+    props.onChange(dateString as string);
+  }, [props.onChange]);
+
+  // Update tempValue when props.value changes
+  useEffect(() => {
+    if (!mountedRef.current) return;
+    
+    const newValue = props.value ? dayjs(props.value, TIME_FORMAT) : null;
+    if (newValue?.isValid()) {
+      setTempValue(newValue);
+    }
+  }, [props.value]);
+
+  // Cleanup event listeners and state
+  useEffect(() => {
+    return () => {
+      mountedRef.current = false;
+      setTempValue(null);
+      if (pickerRef.current) {
+        pickerRef.current = null;
+      }
+    };
+  }, []);
+
+  return (
+    <Wrapper
+      onKeyDown={handleKeyDown}
+      onMouseDown={handleMouseDown}
+    >
+      <TimePickerStyled
+        ref={pickerRef}
+        $open={panelOpen}
+        format={props.inputFormat}
+        allowClear={true}
+        autoFocus
+        value={tempValue}
+        defaultOpen={true}
+        onOpenChange={handleOpenChange}
+        onChange={handleChange}
+        onBlur={props.onChangeEnd}
+      />
+    </Wrapper>
+  );
+});
+
+TimeEdit.displayName = 'TimeEdit';
+
+export const TimeComp = (function () {
+  return new ColumnTypeCompBuilder(
+    childrenMap,
+    (props, dispatch) => {
+      const value = props.changeValue ?? getBaseValue(props, dispatch);
+      return (
+        <>
+          {hasIcon(props.prefixIcon) && (
+            <span>{props.prefixIcon}</span>
+          )}
+          <span>{value}</span>
+          {hasIcon(props.suffixIcon) && (
+            <span>{props.suffixIcon}</span>
+          )}
+        </>
+      );
+    },
+    (nodeValue) => formatTime(nodeValue.text.value, nodeValue.format.value),
+    getBaseValue
+  )
+    .setEditViewFn(({value, onChange, onChangeEnd, otherProps}) => (
+      <TimeEdit
+        value={value}
+        onChange={onChange}
+        onChangeEnd={onChangeEnd}
+        inputFormat={otherProps?.inputFormat}
+      />
+    ))
+    .setPropertyViewFn((children) => (
+      <>
+        {children.text.propertyView({
+          label: trans("table.columnValue"),
+          tooltip: ColumnValueTooltip,
+        })}
+        {children.prefixIcon.propertyView({
+          label: trans("button.prefixIcon"),
+        })}
+        {children.suffixIcon.propertyView({
+          label: trans("button.suffixIcon"),
+        })}
+        {formatPropertyView({ children, placeholder: TIME_FORMAT })}
+      </>
+    ))
+    .build();
+})();
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/simpleTextComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/simpleTextComp.tsx
new file mode 100644
index 0000000000..dcdffe3907
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/columnTypeComps/simpleTextComp.tsx
@@ -0,0 +1,121 @@
+import { default as Input } from "antd/es/input";
+import { StringOrNumberControl } from "comps/controls/codeControl";
+import { trans } from "i18n";
+import { ColumnTypeCompBuilder, ColumnTypeViewFn } from "../columnTypeCompBuilder";
+import { ColumnValueTooltip } from "../simpleColumnTypeComps";
+import { IconControl } from "comps/controls/iconControl";
+import { hasIcon } from "comps/utils";
+import React, { useCallback, useMemo } from "react";
+import { RecordConstructorToComp } from "lowcoder-core";
+import { clickEvent, doubleClickEvent, eventHandlerControl } from "comps/controls/eventHandlerControl";
+import styled from "styled-components";
+import { useCompClickEventHandler } from "@lowcoder-ee/comps/utils/useCompClickEventHandler";
+
+const TextEventOptions = [clickEvent, doubleClickEvent] as const;
+
+const TextWrapper = styled.div`
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  gap: 4px;
+`;
+
+const childrenMap = {
+  text: StringOrNumberControl,
+  prefixIcon: IconControl,
+  suffixIcon: IconControl,
+  onEvent: eventHandlerControl(TextEventOptions),
+};
+
+// Memoize the base value function to prevent unnecessary string creation
+const getBaseValue: ColumnTypeViewFn<typeof childrenMap, string | number, string | number> = (props) => 
+  typeof props.text === 'string' ? props.text : String(props.text);
+
+// Memoized icon components to prevent unnecessary re-renders
+const IconWrapper = React.memo(({ icon }: { icon: React.ReactNode }) => (
+  <span>{icon}</span>
+));
+
+interface SimpleTextContentProps {
+  value: string | number;
+  prefixIcon?: React.ReactNode;
+  suffixIcon?: React.ReactNode;
+  onEvent?: (eventName: string) => void;
+}
+
+interface SimpleTextEditViewProps {
+  value: string | number;
+  onChange: (value: string | number) => void;
+  onChangeEnd: () => void;
+}
+
+const SimpleTextContent = React.memo(({ value, prefixIcon, suffixIcon, onEvent }: SimpleTextContentProps) => {
+  const handleClickEvent = useCompClickEventHandler({onEvent: onEvent ?? (() => {})})
+  
+  const handleClick = useCallback(() => {
+     handleClickEvent()
+  }, [handleClickEvent]);
+
+  return (
+    <TextWrapper onClick={handleClick}>
+      {hasIcon(prefixIcon) && <IconWrapper icon={prefixIcon} />}
+      <span>{value}</span>
+      {hasIcon(suffixIcon) && <IconWrapper icon={suffixIcon} />}
+    </TextWrapper>
+  );
+});
+
+const SimpleTextEditView = React.memo(({ value, onChange, onChangeEnd }: SimpleTextEditViewProps) => {
+  const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
+    onChange(e.target.value);
+  }, [onChange]);
+
+  return (
+    <Input
+      defaultValue={value}
+      autoFocus
+      variant="borderless"
+      onChange={handleChange}
+      onBlur={onChangeEnd}
+      onPressEnter={onChangeEnd}
+    />
+  );
+});
+
+const SimpleTextPropertyView = React.memo(({ children }: { children: RecordConstructorToComp<typeof childrenMap> }) => {
+  return useMemo(() => (
+    <>
+      {children.text.propertyView({
+        label: trans("table.columnValue"),
+        tooltip: ColumnValueTooltip,
+      })}
+      {children.prefixIcon.propertyView({
+        label: trans("button.prefixIcon"),
+      })}
+      {children.suffixIcon.propertyView({
+        label: trans("button.suffixIcon"),
+      })}
+      {children.onEvent.propertyView()}
+    </>
+  ), [children.text, children.prefixIcon, children.suffixIcon, children.onEvent]);
+});
+
+export const SimpleTextComp = new ColumnTypeCompBuilder(
+    childrenMap,
+    (props, dispatch) => {
+      const value = props.changeValue ?? getBaseValue(props, dispatch);
+      return (
+        <SimpleTextContent
+          value={value}
+          prefixIcon={props.prefixIcon}
+          suffixIcon={props.suffixIcon}
+          onEvent={props.onEvent}
+        />
+      );
+    },
+    (nodeValue) => nodeValue.text.value,
+    getBaseValue
+  )
+    .setEditViewFn((props) => <SimpleTextEditView {...props} />)
+    .setPropertyViewFn((children) => <SimpleTextPropertyView children={children} />)
+    .build();
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/simpleColumnTypeComps.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/simpleColumnTypeComps.tsx
new file mode 100644
index 0000000000..f9bedc7549
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/simpleColumnTypeComps.tsx
@@ -0,0 +1,129 @@
+import { ColumnTypeCompBuilder } from "comps/comps/tableComp/column/columnTypeCompBuilder";
+import { ActionSelectorControlInContext } from "comps/controls/actionSelector/actionSelectorControl";
+import { BoolCodeControl, StringControl } from "comps/controls/codeControl";
+import { dropdownControl } from "comps/controls/dropdownControl";
+import { disabledPropertyView, loadingPropertyView } from "comps/utils/propertyUtils";
+import { trans } from "i18n";
+import { useStyle } from "comps/controls/styleControl";
+import { ButtonStyle } from "comps/controls/styleControlConstants";
+import { Button100 } from "comps/comps/buttonComp/buttonCompConstants";
+import { IconControl } from "comps/controls/iconControl";
+import { hasIcon } from "comps/utils";
+import React, { useCallback, useEffect, useMemo } from "react";
+import { CSSProperties } from "react";
+import { RecordConstructorToComp } from "lowcoder-core";
+import { ToViewReturn } from "@lowcoder-ee/comps/generators/multi";
+import { clickEvent, eventHandlerControl, doubleClickEvent } from "comps/controls/eventHandlerControl";
+import { migrateOldData } from "@lowcoder-ee/comps/generators/simpleGenerators";
+import { useCompClickEventHandler } from "@lowcoder-ee/comps/utils/useCompClickEventHandler";
+import { isArray } from "lodash";
+
+export const fixOldActionData = (oldData: any) => {
+  if (!oldData) return oldData;
+  if (Boolean(oldData.onClick && !isArray(oldData.onClick))) {
+    return {
+      ...oldData,
+      onClick: [{
+        name: "click",
+        handler: oldData.onClick,
+      }],
+    };
+  }
+  return oldData;
+}
+export const ColumnValueTooltip = trans("table.columnValueTooltip");
+
+export const ButtonTypeOptions = [
+  {
+    label: trans("table.primaryButton"),
+    value: "primary",
+  },
+  {
+    label: trans("table.defaultButton"),
+    value: "default",
+  },
+  {
+    label: trans("table.text"),
+    value: "text",
+  },
+] as const;
+
+const ButtonEventOptions = [clickEvent, doubleClickEvent] as const;
+
+const childrenMap = {
+  text: StringControl,
+  buttonType: dropdownControl(ButtonTypeOptions, "primary"),
+  onClick: eventHandlerControl(ButtonEventOptions),
+  loading: BoolCodeControl,
+  disabled: BoolCodeControl,
+  prefixIcon: IconControl,
+  suffixIcon: IconControl,
+};
+
+const ButtonStyled = React.memo(({ props }: { props: ToViewReturn<RecordConstructorToComp<typeof childrenMap>>}) => {
+  const style = useStyle(ButtonStyle);
+  const hasText = !!props.text;
+  const hasPrefixIcon = hasIcon(props.prefixIcon);
+  const hasSuffixIcon = hasIcon(props.suffixIcon);
+  const iconOnly = !hasText && (hasPrefixIcon || hasSuffixIcon);
+  const handleClickEvent = useCompClickEventHandler({onEvent: props.onClick})
+
+  const handleClick = useCallback((e: React.MouseEvent) => {
+    handleClickEvent()
+  }, [handleClickEvent]);
+
+  const buttonStyle = useMemo(() => ({
+    margin: 0,
+    width: iconOnly ? 'auto' : undefined,
+    minWidth: iconOnly ? 'auto' : undefined,
+    padding: iconOnly ? '0 8px' : undefined
+  } as CSSProperties), [iconOnly]);
+
+  return (
+    <Button100
+      type={props.buttonType}
+      onClick={handleClick}
+      loading={props.loading}
+      disabled={props.disabled}
+      $buttonStyle={props.buttonType === "primary" ? style : undefined}
+      style={buttonStyle}
+      icon={hasPrefixIcon ? props.prefixIcon : undefined}
+    >
+      {/* prevent the button from disappearing */}
+      {hasText ? props.text : (iconOnly ? null : " ")}
+      {hasSuffixIcon && !props.loading && <span style={{ marginLeft: hasText ? '8px' : 0 }}>{props.suffixIcon}</span>}
+    </Button100>
+  );
+});
+
+const ButtonCompTmp = (function () {
+  return new ColumnTypeCompBuilder(
+    childrenMap,
+    (props) => <ButtonStyled props={props} />,
+    (nodeValue) => nodeValue.text.value
+  )
+    .setPropertyViewFn((children) => (
+      <>
+        {children.text.propertyView({
+          label: trans("table.columnValue"),
+          tooltip: ColumnValueTooltip,
+        })}
+        {children.prefixIcon.propertyView({
+          label: trans("button.prefixIcon"),
+        })}
+        {children.suffixIcon.propertyView({
+          label: trans("button.suffixIcon"),
+        })}
+        {children.buttonType.propertyView({
+          label: trans("table.type"),
+          radioButton: true,
+        })}
+        {loadingPropertyView(children)}
+        {disabledPropertyView(children)}
+        {children.onClick.propertyView()}
+      </>
+    ))
+    .build();
+})();
+
+export const ButtonComp = migrateOldData(ButtonCompTmp, fixOldActionData);
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableColumnComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableColumnComp.tsx
new file mode 100644
index 0000000000..938983ac9e
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableColumnComp.tsx
@@ -0,0 +1,488 @@
+import { BoolControl } from "comps/controls/boolControl";
+import { ColorOrBoolCodeControl, NumberControl, RadiusControl, StringControl } from "comps/controls/codeControl";
+import { dropdownControl, HorizontalAlignmentControl } from "comps/controls/dropdownControl";
+import { MultiCompBuilder, stateComp, valueComp, withContext, withDefault } from "comps/generators";
+import { withSelectedMultiContext } from "comps/generators/withSelectedMultiContext";
+import { genRandomKey } from "comps/utils/idGenerator";
+import { trans } from "i18n";
+import _ from "lodash";
+import {
+  changeChildAction,
+  changeValueAction,
+  CompAction,
+  CompActionTypes,
+  ConstructorToComp,
+  ConstructorToDataType,
+  ConstructorToNodeType,
+  ConstructorToView,
+  deferAction,
+  fromRecord,
+  multiChangeAction,
+  withFunction,
+  wrapChildAction,
+} from "lowcoder-core";
+import { AlignClose, AlignLeft, AlignRight, IconRadius, BorderWidthIcon, TextSizeIcon, FontFamilyIcon, TextWeightIcon, ImageCompIcon, controlItem, Dropdown, OptionType } from "lowcoder-design";
+import { ColumnTypeComp, ColumnTypeCompMap } from "./columnTypeComp";
+import { ColorControl } from "comps/controls/colorControl";
+import { JSONValue } from "util/jsonTypes";
+import styled from "styled-components";
+import { TextOverflowControl } from "comps/controls/textOverflowControl";
+import { default as Divider } from "antd/es/divider";
+import { ColumnValueTooltip } from "./simpleColumnTypeComps";
+import { SummaryColumnComp } from "./tableSummaryColumnComp";
+import { list } from "@lowcoder-ee/comps/generators/list";
+import { EMPTY_ROW_KEY } from "../tableCompView";
+import React, { useCallback, useMemo } from "react";
+
+export type Render = ReturnType<ConstructorToComp<typeof RenderComp>["getOriginalComp"]>;
+export const RenderComp = withSelectedMultiContext(ColumnTypeComp);
+
+const columnWidthOptions = [
+  {
+    label: trans("table.auto"),
+    value: "auto",
+  },
+  {
+    label: trans("table.fixed"),
+    value: "fixed",
+  },
+] as const;
+
+const columnFixOptions = [
+  {
+    label: <AlignLeft />,
+    value: "left",
+  },
+  {
+    label: <AlignClose />,
+    value: "close",
+  },
+  {
+    label: <AlignRight />,
+    value: "right",
+  },
+] as const;
+
+const cellColorLabel = trans("table.cellColor");
+const CellColorTempComp = withContext(
+  new MultiCompBuilder({ color: ColorOrBoolCodeControl }, (props) => props.color)
+    .setPropertyViewFn((children) =>
+      children.color.propertyView({
+        label: cellColorLabel,
+        tooltip: trans("table.cellColorDesc"),
+      })
+    )
+    .build(),
+  ["currentCell", "currentRow"] as const
+);
+
+// @ts-ignore
+export class CellColorComp extends CellColorTempComp {
+  override getPropertyView() {
+    return controlItem({ filterText: cellColorLabel }, super.getPropertyView());
+  }
+}
+
+// fixme, should be infer from RowColorComp, but withContext type incorrect
+export type CellColorViewType = (param: {
+  currentRow: any;
+  currentCell: JSONValue | undefined; //number | string;
+}) => string;
+
+const cellTooltipLabel = trans("table.columnTooltip");
+const CellTooltipTempComp = withContext(
+  new MultiCompBuilder({ tooltip: StringControl }, (props) => props.tooltip)
+    .setPropertyViewFn((children) =>
+      children.tooltip.propertyView({
+        label: cellTooltipLabel,
+        tooltip: ColumnValueTooltip,
+      })
+    )
+    .build(),
+  ["currentCell", "currentRow", "currentIndex"] as const
+);
+
+// @ts-ignore
+export class CellTooltipComp extends CellTooltipTempComp {
+  override getPropertyView() {
+    return controlItem({ filterText: cellTooltipLabel }, super.getPropertyView());
+  }
+}
+
+// fixme, should be infer from RowColorComp, but withContext type incorrect
+export type CellTooltipViewType = (param: {
+  currentRow: any;
+  currentCell: JSONValue | undefined; //number | string;
+}) => string;
+
+
+export const columnChildrenMap = {
+  // column title
+  title: StringControl,
+  titleTooltip: StringControl,
+  showTitle: withDefault(BoolControl, true),
+  cellTooltip: CellTooltipComp,
+  // a custom column or a data column
+  isCustom: valueComp<boolean>(false),
+  // If it is a data column, it must be the name of the column and cannot be duplicated as a react key
+  dataIndex: valueComp<string>(""),
+  hide: BoolControl,
+  sortable: BoolControl,
+  width: NumberControl,
+  autoWidth: dropdownControl(columnWidthOptions, "auto"),
+  render: RenderComp,
+  align: HorizontalAlignmentControl,
+  tempHide: stateComp<boolean>(false),
+  fixed: dropdownControl(columnFixOptions, "close"),
+  editable: BoolControl,
+  background: withDefault(ColorControl, ""),
+  margin: withDefault(RadiusControl, ""),
+  text: withDefault(ColorControl, ""),
+  border: withDefault(ColorControl, ""),
+  borderWidth: withDefault(RadiusControl, ""),
+  radius: withDefault(RadiusControl, ""),
+  textSize: withDefault(RadiusControl, ""),
+  textWeight: withDefault(StringControl, "normal"),
+  fontFamily: withDefault(StringControl, "sans-serif"),
+  fontStyle: withDefault(StringControl, 'normal'),
+  cellColor: CellColorComp,
+  textOverflow: withDefault(TextOverflowControl, "wrap"),
+  linkColor: withDefault(ColorControl, "#3377ff"),
+  linkHoverColor: withDefault(ColorControl, ""),
+  linkActiveColor: withDefault(ColorControl, ""),
+  summaryColumns: withDefault(list(SummaryColumnComp), [
+    {}, {}, {}
+  ])
+};
+
+const StyledBorderRadiusIcon = styled(IconRadius)` width: 24px; margin: 0 8px 0 -3px; padding: 3px;`;
+const StyledBorderIcon = styled(BorderWidthIcon)` width: 24px; margin: 0 8px 0 -3px; padding: 3px;`;
+const StyledTextSizeIcon = styled(TextSizeIcon)` width: 24px; margin: 0 8px 0 -3px; padding: 3px;`;
+const StyledFontFamilyIcon = styled(FontFamilyIcon)` width: 24px; margin: 0 8px 0 -3px; padding: 3px;`;
+const StyledTextWeightIcon = styled(TextWeightIcon)` width: 24px; margin: 0 8px 0 -3px; padding: 3px;`;
+const StyledBackgroundImageIcon = styled(ImageCompIcon)` width: 24px; margin: 0 0px 0 -12px;`;
+
+/**
+ * export for test.
+ * Put it here temporarily to avoid circular dependencies
+ */
+const ColumnInitComp = new MultiCompBuilder(columnChildrenMap, (props, dispatch) => {
+  const onWidthResize = (width: number) => {
+    dispatch(
+      multiChangeAction({
+        width: changeValueAction(width, true),
+        autoWidth: changeValueAction("fixed", true),
+      })
+    );
+  };
+
+  return {
+    ...props,
+    onWidthResize,
+  };
+})
+  .setPropertyViewFn(() => <></>)
+  .build();
+
+const ColumnPropertyView = React.memo(({ 
+  comp, 
+  viewMode, 
+  summaryRowIndex 
+}: { 
+  comp: ColumnComp; 
+  viewMode: string; 
+  summaryRowIndex: number; 
+}) => {
+  const selectedColumn = comp.children.render.getSelectedComp();
+  const columnType = useMemo(() => 
+    selectedColumn.getComp().children.compType.getView(),
+    [selectedColumn]
+  );
+
+  const initialColumns = useMemo(() => 
+    selectedColumn.getParams()?.initialColumns as OptionType[] || [],
+    [selectedColumn]
+  );
+
+  const columnValue = useMemo(() => {
+    const column = selectedColumn.getComp().toJsonValue();
+    if (column.comp?.hasOwnProperty('src')) {
+      return (column.comp as any).src;
+    } else if (column.comp?.hasOwnProperty('text')) {
+      const value = (column.comp as any).text;
+      const isDynamicValue = initialColumns.find((column) => column.value === value);
+      return !isDynamicValue ? '{{currentCell}}' : value;
+    }
+    return '{{currentCell}}';
+  }, [selectedColumn, initialColumns]);
+
+  const summaryColumns = comp.children.summaryColumns.getView();
+
+  return (
+    <>
+      {viewMode === 'summary' && (
+        summaryColumns[summaryRowIndex].propertyView('')
+      )}
+      {viewMode === 'normal' && (
+        <>
+          {comp.children.title.propertyView({
+            label: trans("table.columnTitle"),
+            placeholder: comp.children.dataIndex.getView(),
+          })}
+          {comp.children.titleTooltip.propertyView({
+            label: trans("table.columnTitleTooltip"),
+          })}
+          {comp.children.cellTooltip.getPropertyView()}
+          <Dropdown
+            showSearch={true}
+            defaultValue={columnValue}
+            options={initialColumns}
+            label={trans("table.dataMapping")}
+            onChange={(value) => {
+              // Keep the previous text value, some components do not have text, the default value is currentCell
+              const compType = columnType;
+              let compValue: Record<string, string> = { text: value};
+              if(columnType === 'image') {
+                compValue = { src: value };
+              }
+              comp.children.render.dispatchChangeValueAction({
+                compType,
+                comp: compValue,
+              } as any);
+            }}
+          />
+          {/* FIXME: cast type currently, return type of withContext should be corrected later */}
+          {comp.children.render.getPropertyView()}
+          {comp.children.showTitle.propertyView({
+            label: trans("table.showTitle"),
+            tooltip: trans("table.showTitleTooltip"),
+          })}
+          {ColumnTypeCompMap[columnType].canBeEditable() &&
+            comp.children.editable.propertyView({ label: trans("table.editable") })}
+          {comp.children.sortable.propertyView({
+            label: trans("table.sortable"),
+          })}
+          {comp.children.hide.propertyView({
+            label: trans("prop.hide"),
+          })}
+          {comp.children.align.propertyView({
+            label: trans("table.align"),
+            radioButton: true,
+          })}
+          {comp.children.fixed.propertyView({
+            label: trans("table.fixedColumn"),
+            radioButton: true,
+          })}
+          {comp.children.autoWidth.propertyView({
+            label: trans("table.autoWidth"),
+            radioButton: true,
+          })}
+          {comp.children.autoWidth.getView() === "fixed" &&
+            comp.children.width.propertyView({ label: trans("prop.width") })}
+
+          {(columnType === 'link' || columnType === 'links') && (
+            <>
+              <Divider style={{ margin: '12px 0' }} />
+              {controlItem({}, (
+                <div>
+                  <b>{"Link Style"}</b>
+                </div>
+              ))}
+              {comp.children.linkColor.propertyView({
+                label: trans('text') // trans('style.background'),
+              })}
+              {comp.children.linkHoverColor.propertyView({
+                label: "Hover text", // trans('style.background'),
+              })}
+              {comp.children.linkActiveColor.propertyView({
+                label: "Active text", // trans('style.background'),
+              })}
+            </>
+          )}
+          <Divider style={{ margin: '12px 0' }} />
+          {controlItem({}, (
+            <div>
+              <b>{"Column Style"}</b>
+            </div>
+          ))}
+          {comp.children.background.propertyView({
+            label: trans('style.background'),
+          })}
+          {columnType !== 'link' && comp.children.text.propertyView({
+            label: trans('text'),
+          })}
+          {comp.children.border.propertyView({
+            label: trans('style.border')
+          })}
+          {comp.children.borderWidth.propertyView({
+            label: trans('style.borderWidth'),
+            preInputNode: <StyledBorderIcon as={BorderWidthIcon} title="" />,
+            placeholder: '1px',
+          })}
+          {comp.children.radius.propertyView({
+            label: trans('style.borderRadius'),
+            preInputNode: <StyledBorderRadiusIcon as={IconRadius} title="" />,
+            placeholder: '3px',
+          })}
+          {columnType !== 'markdown' && comp.children.textSize.propertyView({
+            label: trans('style.textSize'),
+            preInputNode: <StyledTextSizeIcon as={TextSizeIcon} title="" />,
+            placeholder: '14px',
+          })}
+          {comp.children.textWeight.propertyView({
+            label: trans('style.textWeight'),
+            preInputNode: <StyledTextWeightIcon as={TextWeightIcon} title="" />,
+            placeholder: 'normal',
+          })}
+          {comp.children.fontFamily.propertyView({
+            label: trans('style.fontFamily'),
+            preInputNode: <StyledFontFamilyIcon as={FontFamilyIcon} title="" />,
+            placeholder: 'sans-serif',
+          })}
+          {comp.children.fontStyle.propertyView({
+            label: trans('style.fontStyle'),
+            preInputNode: <StyledFontFamilyIcon as={FontFamilyIcon} title="" />,
+            placeholder: 'normal'
+          })}
+          {comp.children.textOverflow.getPropertyView()}
+          {comp.children.cellColor.getPropertyView()}
+        </>
+      )}
+    </>
+  );
+});
+
+ColumnPropertyView.displayName = 'ColumnPropertyView';
+
+export class ColumnComp extends ColumnInitComp {
+  override reduce(action: CompAction) {
+    let comp = super.reduce(action);
+    if (action.type === CompActionTypes.UPDATE_NODES_V2) {
+      // Reset context data without cleanup since components are managed by React
+      comp = comp.setChild(
+        "cellColor",
+        comp.children.cellColor.reduce(
+          CellColorComp.changeContextDataAction({
+            currentCell: undefined,
+            currentRow: {},
+          })
+        )
+      );
+      comp = comp.setChild(
+        "cellTooltip",
+        comp.children.cellTooltip.reduce(
+          CellTooltipComp.changeContextDataAction({
+            currentCell: undefined,
+            currentRow: {},
+            currentIndex: 0,
+          })
+        )
+      );
+    }
+    if (action.type === CompActionTypes.CHANGE_VALUE) {
+      const title = comp.children.title.unevaledValue;
+      const dataIndex = comp.children.dataIndex.getView();
+      if (!Boolean(title)) {
+        comp.children.title.dispatchChangeValueAction(dataIndex);
+      }
+    }
+    return comp;
+  }
+
+  override getView() {
+    const superView = super.getView();
+    const columnType = this.children.render.getSelectedComp().getComp().children.compType.getView();
+    return {
+      ...superView,
+      columnType,
+      editable: ColumnTypeCompMap[columnType].canBeEditable() && superView.editable,
+    };
+  }
+
+  exposingNode() {
+    const dataIndexNode = this.children.dataIndex.exposingNode();
+
+    const renderNode = withFunction(this.children.render.node(), (render) => ({
+      wrap: render.__comp__.wrap,
+      map: _.mapValues(render.__map__, (value) => value.comp),
+    }));
+    return fromRecord({
+      dataIndex: dataIndexNode,
+      render: renderNode,
+    });
+  }
+
+  propertyView(key: string, viewMode: string, summaryRowIndex: number) {
+    return <ColumnPropertyView comp={this} viewMode={viewMode} summaryRowIndex={summaryRowIndex} />;
+  }
+
+  getChangeSet() {
+    const dataIndex = this.children.dataIndex.getView();
+    const changeSet = _.mapValues(this.children.render.getMap(), (value) =>{
+     return value.getComp().children.comp.children.changeValue.getView()
+    });
+    return { [dataIndex]: changeSet };
+  }
+
+  dispatchClearChangeSet() {
+    this.children.render.dispatch(
+      deferAction(
+        RenderComp.forEachAction(
+          wrapChildAction(
+            "comp",
+            wrapChildAction("comp", changeChildAction("changeValue", null, false))
+          )
+        )
+      )
+    );
+    // clear render comp cache when change set is cleared
+    this.children.render.dispatch(RenderComp.clearAction());
+  }
+
+  dispatchClearInsertSet() {
+    const renderMap = this.children.render.getMap();
+    const insertMapKeys = Object.keys(renderMap).filter(key => key.startsWith(EMPTY_ROW_KEY));
+    insertMapKeys.forEach(key => {
+      const render = renderMap[key];
+      render.getComp().children.comp.children.changeValue.dispatchChangeValueAction(null);
+    });
+  }
+
+  static setSelectionAction(key: string) {
+    return wrapChildAction("render", RenderComp.setSelectionAction(key));
+  }
+}
+
+export type RawColumnType = ConstructorToView<typeof ColumnComp>;
+export type ColumNodeType = ConstructorToNodeType<typeof ColumnComp>;
+export type ColumnCompType = ConstructorToComp<typeof ColumnComp>;
+
+/**
+ * Custom column initialization data
+ */
+export function newCustomColumn(): ConstructorToDataType<typeof ColumnComp> {
+  return {
+    title: trans("table.customColumn"),
+    dataIndex: genRandomKey(),
+    isCustom: true,
+  };
+}
+
+/**
+ * Initialization data of primary column
+ */
+export function newPrimaryColumn(
+  key: string,
+  width: number,
+  title?: string,
+  isTag?: boolean
+): ConstructorToDataType<typeof ColumnComp> {
+  return {
+    title: title ?? key,
+    dataIndex: key,
+    isCustom: false,
+    autoWidth: "fixed",
+    width: width + "",
+    render: { compType: isTag ? "tag" : "text", comp: { text: "{{currentCell}}" } },
+  };
+}
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableColumnListComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableColumnListComp.tsx
new file mode 100644
index 0000000000..7ad933cf92
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableColumnListComp.tsx
@@ -0,0 +1,202 @@
+import { ColumnComp, newPrimaryColumn } from "comps/comps/tableComp/column/tableColumnComp";
+import {
+  calcColumnWidth,
+  COLUMN_CHILDREN_KEY,
+  supportChildrenTree,
+} from "comps/comps/tableComp/tableUtils";
+import { list } from "comps/generators/list";
+import { getReduceContext } from "comps/utils/reduceContext";
+import _ from "lodash";
+import {
+  CompAction,
+  customAction,
+  fromRecord,
+  isMyCustomAction,
+  RecordNode,
+} from "lowcoder-core";
+import { shallowEqual } from "react-redux";
+import { JSONObject, JSONValue } from "util/jsonTypes";
+import { lastValueIfEqual } from "util/objectUtils";
+import { EMPTY_ROW_KEY } from "../tableCompView";
+
+/**
+ * column list
+ */
+const ColumnListTmpComp = list(ColumnComp);
+
+/**
+ * rowExample is used for code prompts
+ */
+type RowExampleType = JSONObject | undefined;
+type ActionDataType = {
+  type: "dataChanged";
+  rowExample: RowExampleType;
+  doGeneColumn: boolean;
+  dynamicColumn: boolean;
+  data: Array<JSONObject>;
+};
+
+export function tableDataRowExample(data: Array<JSONObject>) {
+  if (!data?.length) {
+    return undefined;
+  }
+  
+  if (typeof data[0] === "string") {
+    // do not parse arrays in string format
+    return undefined;
+  }
+  const rowExample: Record<string, JSONValue | undefined> = {};
+  // merge head 50 data keys
+  data.slice(0, 50).forEach((d) => {
+    Object.keys(d).forEach((key) => {
+      if (!rowExample.hasOwnProperty(key)) {
+        rowExample[key] = d[key];
+      }
+    });
+  });
+  return rowExample;
+}
+
+export class ColumnListComp extends ColumnListTmpComp {
+  override reduce(action: CompAction): this {
+    if (isMyCustomAction<ActionDataType>(action, "dataChanged")) {
+      const rowExample = action.value.rowExample;
+      const { readOnly } = getReduceContext();
+      let comp = this;
+      if (action.value.doGeneColumn && (action.value.dynamicColumn || !readOnly)) {
+        const actions = this.geneColumnsAction(rowExample, action.value.data);
+        comp = this.reduce(this.multiAction(actions));
+      }
+      return comp;
+    }
+    return super.reduce(action);
+  }
+
+  getChangeSet(filterNewRowsChange?: boolean) {
+    const changeSet: Record<string, Record<string, JSONValue>> = {};
+    const columns = this.getView();
+    columns.forEach((column) => {
+      const columnChangeSet = column.getChangeSet();
+      Object.keys(columnChangeSet).forEach((dataIndex) => {
+        Object.keys(columnChangeSet[dataIndex]).forEach((key) => {
+          const includeChange = filterNewRowsChange
+            ? key.startsWith(EMPTY_ROW_KEY)
+            : !key.startsWith(EMPTY_ROW_KEY);
+          if (!_.isNil(columnChangeSet[dataIndex][key]) && includeChange) {
+            if (!changeSet[key]) changeSet[key] = {};
+            changeSet[key][dataIndex] = columnChangeSet[dataIndex][key];
+          }
+        });
+      });
+    });
+    return changeSet;
+  }
+
+  dispatchClearChangeSet() {
+    const columns = this.getView();
+    columns.forEach((column) => column.dispatchClearChangeSet());
+  }
+
+  dispatchClearInsertSet() {
+    const columns = this.getView();
+    columns.forEach((column) => column.dispatchClearInsertSet());
+  }
+
+  /**
+   * If the table data changes, call this method to trigger the action
+   */
+  dataChangedAction(param: {
+    rowExample: JSONObject;
+    doGeneColumn: boolean;
+    dynamicColumn: boolean;
+    data: Array<JSONObject>;
+  }) {
+    return customAction<ActionDataType>(
+      {
+        type: "dataChanged",
+        ...param,
+      },
+      true
+    );
+  }
+
+  /**
+   * According to the data, adjust the column
+   */
+  private geneColumnsAction(rowExample: RowExampleType, data: Array<JSONObject>) {
+    // If no data, return directly
+    if (rowExample === undefined || rowExample === null) {
+      return [];
+    }
+    const dataKeys = Object.keys(rowExample);
+    if (dataKeys.length === 0) {
+      return [];
+    }
+    const columnsView = this.getView();
+    const actions: Array<any> = [];
+    let deleteCnt = 0;
+    columnsView.forEach((column, index) => {
+      if (column.getView().isCustom) {
+        return;
+      }
+      const dataIndex = column.getView().dataIndex;
+      if (dataIndex === COLUMN_CHILDREN_KEY || !dataKeys.find((key) => dataIndex === key)) {
+        // to Delete
+        actions.push(this.deleteAction(index - deleteCnt));
+        deleteCnt += 1;
+      }
+    });
+    // The order should be the same as the data
+    dataKeys.forEach((key) => {
+      if (key === COLUMN_CHILDREN_KEY && supportChildrenTree(data)) {
+        return;
+      }
+      if (!columnsView.find((column) => column.getView().dataIndex === key)) {
+        // to Add
+        actions.push(this.pushAction(newPrimaryColumn(key, calcColumnWidth(key, data))));
+      }
+    });
+    if (actions.length === 0) {
+      return [];
+    }
+    return actions;
+  }
+
+  withParamsNode() {
+    const columns = this.getView();
+    const nodes = _(columns)
+      .map((col) => col.children.render.getOriginalComp().node())
+      .toPairs()
+      .fromPairs()
+      .value();
+    const result = lastValueIfEqual(
+      this,
+      "withParamsNode",
+      [fromRecord(nodes), nodes] as const,
+      (a, b) => shallowEqual(a[1], b[1])
+    )[0];
+    return result;
+  }
+
+  getColumnsNode<T extends keyof ColumnComp["children"]>(
+    field: T
+  ): RecordNode<Record<string, ReturnType<ColumnComp["children"][T]["node"]>>> {
+    const columns = this.getView();
+    const nodes = _(columns)
+      .map((col) => col.children[field].node() as ReturnType<ColumnComp["children"][T]["node"]>)
+      .toPairs()
+      .fromPairs()
+      .value();
+    const result = lastValueIfEqual(
+      this,
+      "col_nodes_" + field,
+      [fromRecord(nodes), nodes] as const,
+      (a, b) => shallowEqual(a[1], b[1])
+    )[0];
+    return result;
+  }
+
+  setSelectionAction(key: string) {
+    return this.forEachAction(ColumnComp.setSelectionAction(key));
+  }
+}
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableSummaryColumnComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableSummaryColumnComp.tsx
new file mode 100644
index 0000000000..6b0e2c4068
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableSummaryColumnComp.tsx
@@ -0,0 +1,205 @@
+import { RadiusControl, StringControl } from "comps/controls/codeControl";
+import { HorizontalAlignmentControl } from "comps/controls/dropdownControl";
+import { MultiCompBuilder, valueComp, withDefault } from "comps/generators";
+import { withSelectedMultiContext } from "comps/generators/withSelectedMultiContext";
+import { trans } from "i18n";
+import _ from "lodash";
+import {
+  changeChildAction,
+  CompAction,
+  ConstructorToComp,
+  deferAction,
+  fromRecord,
+  withFunction,
+  wrapChildAction,
+} from "lowcoder-core";
+import { IconRadius, TextSizeIcon, FontFamilyIcon, TextWeightIcon, controlItem } from "lowcoder-design";
+import { ColumnTypeComp } from "./columnTypeComp";
+import { ColorControl } from "comps/controls/colorControl";
+import styled from "styled-components";
+import { TextOverflowControl } from "comps/controls/textOverflowControl";
+import { default as Divider } from "antd/es/divider";
+export type Render = ReturnType<ConstructorToComp<typeof RenderComp>["getOriginalComp"]>;
+export const RenderComp = withSelectedMultiContext(ColumnTypeComp);
+
+export const columnChildrenMap = {
+  cellTooltip: StringControl,
+  // a custom column or a data column
+  isCustom: valueComp<boolean>(false),
+  // If it is a data column, it must be the name of the column and cannot be duplicated as a react key
+  dataIndex: valueComp<string>(""),
+  render: RenderComp,
+  align: HorizontalAlignmentControl,
+  background: withDefault(ColorControl, ""),
+  margin: withDefault(RadiusControl, ""),
+  text: withDefault(ColorControl, ""),
+  border: withDefault(ColorControl, ""),
+  radius: withDefault(RadiusControl, ""),
+  textSize: withDefault(RadiusControl, ""),
+  textWeight: withDefault(StringControl, "normal"),
+  fontFamily: withDefault(StringControl, "sans-serif"),
+  fontStyle: withDefault(StringControl, 'normal'),
+  cellColor: StringControl,
+  textOverflow: withDefault(TextOverflowControl, "wrap"),
+  linkColor: withDefault(ColorControl, "#3377ff"),
+  linkHoverColor: withDefault(ColorControl, ""),
+  linkActiveColor: withDefault(ColorControl, ""),
+};
+
+const StyledBorderRadiusIcon = styled(IconRadius)` width: 24px; margin: 0 8px 0 -3px; padding: 3px;`;
+const StyledTextSizeIcon = styled(TextSizeIcon)` width: 24px; margin: 0 8px 0 -3px; padding: 3px;`;
+const StyledFontFamilyIcon = styled(FontFamilyIcon)` width: 24px; margin: 0 8px 0 -3px; padding: 3px;`;
+const StyledTextWeightIcon = styled(TextWeightIcon)` width: 24px; margin: 0 8px 0 -3px; padding: 3px;`;
+
+/**
+ * export for test.
+ * Put it here temporarily to avoid circular dependencies
+ */
+const ColumnInitComp = new MultiCompBuilder(columnChildrenMap, (props, dispatch) => {
+  return {
+    ...props,
+  };
+})
+  .setPropertyViewFn(() => <></>)
+  .build();
+
+export class SummaryColumnComp extends ColumnInitComp {
+  override reduce(action: CompAction) {
+    const comp = super.reduce(action);
+    return comp;
+  }
+
+  override getView() {
+    const superView = super.getView();
+    const columnType = this.children.render.getSelectedComp().getComp().children.compType.getView();
+    return {
+      ...superView,
+      columnType,
+    };
+  }
+
+  exposingNode() {
+    const dataIndexNode = this.children.dataIndex.exposingNode();
+
+    const renderNode = withFunction(this.children.render.node(), (render) => ({
+      wrap: render.__comp__.wrap,
+      map: _.mapValues(render.__map__, (value) => value.comp),
+    }));
+    return fromRecord({
+      dataIndex: dataIndexNode,
+      render: renderNode,
+    });
+  }
+
+  propertyView(key: string) {
+    const columnType = this.children.render.getSelectedComp().getComp().children.compType.getView();
+    const column = this.children.render.getSelectedComp().getComp().toJsonValue();
+    let columnValue = '{{currentCell}}';
+    if (column.comp?.hasOwnProperty('src')) {
+      columnValue = (column.comp as any).src;
+    } else if (column.comp?.hasOwnProperty('text')) {
+      columnValue = (column.comp as any).text;
+    }
+
+    return (
+      <>
+        {this.children.cellTooltip.propertyView({
+          label: trans("table.columnTooltip"),
+        })}
+        {this.children.render.getPropertyView()}
+        {this.children.align.propertyView({
+          label: trans("table.align"),
+          radioButton: true,
+        })}
+        {(columnType === 'link' || columnType === 'links') && (
+          <>
+            <Divider style={{ margin: '12px 0' }} />
+            {controlItem({}, (
+              <div>
+                <b>{"Link Style"}</b>
+              </div>
+            ))}
+            {this.children.linkColor.propertyView({
+              label: trans('text')
+            })}
+            {this.children.linkHoverColor.propertyView({
+              label: "Hover text",
+            })}
+            {this.children.linkActiveColor.propertyView({
+              label: "Active text",
+            })}
+          </>
+        )}
+        <Divider style={{ margin: '12px 0' }} />
+        {controlItem({}, (
+          <div>
+            <b>{"Column Style"}</b>
+          </div>
+        ))}
+        {this.children.background.propertyView({
+          label: trans('style.background'),
+        })}
+        {columnType !== 'link' && this.children.text.propertyView({
+          label: trans('text'),
+        })}
+        {this.children.border.propertyView({
+          label: trans('style.border')
+        })}
+        {this.children.radius.propertyView({
+          label: trans('style.borderRadius'),
+          preInputNode: <StyledBorderRadiusIcon as={IconRadius} title="" />,
+          placeholder: '3px',
+        })}
+        {this.children.textSize.propertyView({
+          label: trans('style.textSize'),
+          preInputNode: <StyledTextSizeIcon as={TextSizeIcon} title="" />,
+          placeholder: '14px',
+        })}
+        {this.children.textWeight.propertyView({
+          label: trans('style.textWeight'),
+          preInputNode: <StyledTextWeightIcon as={TextWeightIcon} title="" />,
+          placeholder: 'normal',
+        })}
+        {this.children.fontFamily.propertyView({
+          label: trans('style.fontFamily'),
+          preInputNode: <StyledFontFamilyIcon as={FontFamilyIcon} title="" />,
+          placeholder: 'sans-serif',
+        })}
+        {this.children.fontStyle.propertyView({
+          label: trans('style.fontStyle'),
+          preInputNode: <StyledFontFamilyIcon as={FontFamilyIcon} title="" />,
+          placeholder: 'normal'
+        })}
+        {/* {this.children.textOverflow.getPropertyView()} */}
+        {this.children.cellColor.propertyView({
+          label: trans("table.cellColor"),
+        })}
+      </>
+    );
+  }
+
+  getChangeSet() {
+    const dataIndex = this.children.dataIndex.getView();
+    const changeSet = _.mapValues(this.children.render.getMap(), (value) =>
+      value.getComp().children.comp.children.changeValue.getView()
+    );
+    return { [dataIndex]: changeSet };
+  }
+
+  dispatchClearChangeSet() {
+    this.children.render.dispatch(
+      deferAction(
+        RenderComp.forEachAction(
+          wrapChildAction(
+            "comp",
+            wrapChildAction("comp", changeChildAction("changeValue", null, false))
+          )
+        )
+      )
+    );
+  }
+
+  static setSelectionAction(key: string) {
+    return wrapChildAction("render", RenderComp.setSelectionAction(key));
+  }
+}
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/expansionControl.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/expansionControl.tsx
new file mode 100644
index 0000000000..98044cd318
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/expansionControl.tsx
@@ -0,0 +1,116 @@
+import {
+  ContainerBaseProps,
+  gridItemCompToGridItems,
+  InnerGrid,
+} from "comps/comps/containerComp/containerView";
+import { BoolControl } from "comps/controls/boolControl";
+import { SlotControl } from "comps/controls/slotControl";
+import { withSelectedMultiContext } from "comps/generators";
+import { ControlItemCompBuilder } from "comps/generators/controlCompBuilder";
+import { BackgroundColorContext } from "comps/utils/backgroundColorContext";
+import { trans } from "i18n";
+import _ from "lodash";
+import { ConstructorToView, wrapChildAction } from "lowcoder-core";
+import { createContext, useContext, useCallback } from "react";
+import { tryToNumber } from "util/convertUtils";
+import { SimpleContainerComp } from "../containerBase/simpleContainerComp";
+import { OB_ROW_ORI_INDEX, RecordType } from "./tableUtils";
+import { NameGenerator } from "comps/utils";
+import { JSONValue } from "util/jsonTypes";
+
+const ContextSlotControl = withSelectedMultiContext(SlotControl);
+export const ExpandViewContext = createContext(false);
+
+const ContainerView = (props: ContainerBaseProps) => {
+  return <InnerGrid {...props} emptyRows={15} autoHeight />;
+};
+
+function ExpandView(props: { containerProps: ConstructorToView<typeof SimpleContainerComp> }) {
+  const { containerProps } = props;
+  const background = useContext(BackgroundColorContext);
+  return (
+    <ContainerView
+      {...containerProps}
+      isDroppable={false}
+      isDraggable={false}
+      isResizable={false}
+      isSelectable={false}
+      bgColor={background}
+      items={gridItemCompToGridItems(containerProps.items)}
+      hintPlaceholder=""
+      containerPadding={[4, 4]}
+    />
+  );
+}
+
+let ExpansionControlTmp = (function () {
+  const label = trans("table.expandable");
+  return new ControlItemCompBuilder(
+    {
+      expandable: BoolControl,
+      slot: ContextSlotControl,
+    },
+    () => ({ expandableConfig: {}, expandModalView: null })
+    )
+    .setControlItemData({ filterText: label })
+    .setPropertyViewFn((children, dispatch) => {
+      return (
+        <> 
+          {children.expandable.propertyView({ label })}
+          {children.expandable.getView() &&
+            children.slot
+              .getSelectedComp()
+              .getComp()
+              .propertyView({ buttonText: trans("table.configExpandedView") })}
+        </>
+      );
+    })
+    .build();
+})();
+
+export class ExpansionControl extends ExpansionControlTmp {
+  getView() {
+    if (!this.children.expandable.getView()) {
+      return { expandableConfig: {}, expandModalView: null };
+    }
+    const selectedContainer = this.children.slot.getSelectedComp();
+    return {
+      expandableConfig: {
+        expandedRowRender: (record: RecordType, index: number) => {
+          const slotControl = this.children.slot.getView()(
+            {
+              currentRow: _.omit(record, OB_ROW_ORI_INDEX),
+              currentIndex: index,
+              currentOriginalIndex: tryToNumber(record[OB_ROW_ORI_INDEX]),
+            },
+            String(record[OB_ROW_ORI_INDEX])
+          );
+          const containerProps = slotControl.children.container.getView();
+          return (
+            <ExpandViewContext.Provider value={true}>
+              <ExpandView key={record[OB_ROW_ORI_INDEX]} containerProps={containerProps} />
+            </ExpandViewContext.Provider>
+          );
+        },
+      },
+      expandModalView: selectedContainer.getView(),
+    };
+  }
+
+  setSelectionAction(selection: string, params?: Record<string, unknown>) {
+    return wrapChildAction("slot", ContextSlotControl.setSelectionAction(selection, params));
+  }
+
+  getPasteValue(nameGenerator: NameGenerator): JSONValue {
+    return {
+      ...this.toJsonValue(),
+      slot: this.children.slot.getSelectedComp().getComp().getPasteValue(nameGenerator),
+    };
+  }
+
+  reduce(action: any) {
+    const comp = super.reduce(action);
+    // console.info("ExpansionControl reduce. action: ", action, "\nthis: ", this, "\ncomp: ", comp);
+    return comp;
+  }
+}
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/index.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/index.tsx
new file mode 100644
index 0000000000..3caa488f33
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/index.tsx
@@ -0,0 +1 @@
+export { TableLiteComp } from "./tableComp";
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/mockTableComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/mockTableComp.tsx
new file mode 100644
index 0000000000..65e1660f3f
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/mockTableComp.tsx
@@ -0,0 +1,72 @@
+import { withDefault } from "comps/generators";
+import { TableLiteComp } from ".";
+import { newPrimaryColumn } from "comps/comps/tableComp/column/tableColumnComp";
+import { NameGenerator } from "../../utils";
+import { ConstructorToDataType } from "lowcoder-core";
+import { EditorState } from "../../editorState";
+import { isArrayLikeObject } from "lodash";
+import { i18nObjs } from "i18n";
+import { calcColumnWidth } from "comps/comps/tableComp/tableUtils";
+// for test only
+const dataSource = [
+  {
+    key: 0,
+    date: "2018-02-11",
+    amount: 120,
+    type: "income",
+    note: "transfer",
+  },
+  {
+    key: 1,
+    date: "2018-03-11",
+    amount: 243,
+    type: "income",
+    note: "transfer",
+  },
+  {
+    key: 2,
+    date: "2018-04-11",
+    amount: 98,
+    type: "income",
+    note: "transfer",
+  },
+];
+for (let i = 0; i < 53; i += 1) {
+  dataSource.push({
+    key: 3 + i,
+    date: "2018-04-11",
+    amount: 98 + i,
+    type: "income" + (i % 3),
+    note: "transfer" + (i % 5),
+  });
+}
+
+const tableInitValue = {
+  toolbar: {
+    showDownload: true,
+    showFilter: true,
+    showRefresh: true,
+  },
+};
+
+const tableData = {
+  ...tableInitValue,
+  data: JSON.stringify(i18nObjs.table.defaultData, null, " "),
+  columns: i18nObjs.table.columns.map((t: any) =>
+    newPrimaryColumn(t.key, calcColumnWidth(t.key, i18nObjs.table.defaultData), t.title, t.isTag)
+  ),
+};
+export const MockTableComp = withDefault(TableLiteComp, tableData);
+
+export function defaultTableData(
+  compName: string,
+  nameGenerator: NameGenerator,
+  editorState?: EditorState
+): ConstructorToDataType<typeof TableLiteComp> {
+  const selectedQueryComp = editorState?.selectedQueryComp();
+  const data = selectedQueryComp?.children.data.getView();
+  const queryName = selectedQueryComp?.children.name.getView();
+  return isArrayLikeObject(data)
+    ? { ...tableInitValue, data: `{{ ${queryName}.data }}` }
+    : tableData;
+}
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/paginationControl.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/paginationControl.tsx
new file mode 100644
index 0000000000..eb5b4a0f8a
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/paginationControl.tsx
@@ -0,0 +1,90 @@
+import { BoolControl } from "comps/controls/boolControl";
+import { ArrayNumberControl, NumberControl } from "comps/controls/codeControl";
+import { stateComp, valueComp, withDefault } from "comps/generators";
+import { ControlNodeCompBuilder } from "comps/generators/controlCompBuilder";
+import { migrateOldData } from "comps/generators/simpleGenerators";
+import { trans } from "i18n";
+import { changeChildAction, ConstructorToNodeType } from "lowcoder-core";
+
+const DEFAULT_PAGE_SIZE = 5;
+
+export function getPageSize(
+  showSizeChanger: boolean,
+  pageSize: number,
+  pageSizeOptions: number[],
+  changeablePageSize: number
+) {
+  if (showSizeChanger) {
+    return changeablePageSize || pageSizeOptions[0] || DEFAULT_PAGE_SIZE;
+  } else {
+    return pageSize || DEFAULT_PAGE_SIZE;
+  }
+}
+
+export const PaginationTmpControl = (function () {
+  const childrenMap = {
+    showQuickJumper: BoolControl,
+    showSizeChanger: BoolControl,
+    hideOnSinglePage: BoolControl,
+    changeablePageSize: migrateOldData(valueComp<number>(5), Number),
+    pageSize: NumberControl,
+    total: NumberControl,
+    pageNo: stateComp<number>(1),
+    pageSizeOptions: withDefault(ArrayNumberControl, "[5, 10, 20, 50]"),
+  };
+  return new ControlNodeCompBuilder(childrenMap, (props, dispatch) => {
+    return {
+      showQuickJumper: props.showQuickJumper,
+      showSizeChanger: props.showSizeChanger,
+      total: props.total,
+      hideOnSinglePage: props.hideOnSinglePage,
+      pageSize: getPageSize(
+        props.showSizeChanger,
+        props.pageSize,
+        props.pageSizeOptions,
+        props.changeablePageSize
+      ),
+      current: props.pageNo,
+      pageSizeOptions: props.pageSizeOptions,
+      onChange: (page: number, pageSize: number) => {
+        props.showSizeChanger &&
+          pageSize !== props.changeablePageSize &&
+          dispatch(changeChildAction("changeablePageSize", pageSize, true));
+        page !== props.pageNo && dispatch(changeChildAction("pageNo", page, false));
+      },
+    };
+  })
+    .setPropertyViewFn((children) => [
+      children.showQuickJumper.propertyView({
+        label: trans("table.showQuickJumper"),
+      }),
+      children.hideOnSinglePage.propertyView({
+        label: trans("table.hideOnSinglePage"),
+      }),
+      children.showSizeChanger.propertyView({
+        label: trans("table.showSizeChanger"),
+      }),
+      children.showSizeChanger.getView()
+        ? children.pageSizeOptions.propertyView({
+            label: trans("table.pageSizeOptions"),
+          })
+        : children.pageSize.propertyView({
+            label: trans("table.pageSize"),
+            placeholder: String(DEFAULT_PAGE_SIZE),
+          }),
+      children.total.propertyView({
+        label: trans("table.total"),
+        tooltip: trans("table.totalTooltip"),
+      }),
+    ])
+    .build();
+})();
+
+export class PaginationControl extends PaginationTmpControl {
+  getOffset() {
+    const pagination = this.getView();
+    return (pagination.current - 1) * pagination.pageSize;
+  }
+}
+
+export type PaginationNodeType = ConstructorToNodeType<typeof PaginationControl>;
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/selectionControl.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/selectionControl.tsx
new file mode 100644
index 0000000000..037516d91b
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/selectionControl.tsx
@@ -0,0 +1,134 @@
+import { TableRowSelection } from "antd/es/table/interface";
+import { dropdownControl } from "comps/controls/dropdownControl";
+import { stateComp } from "comps/generators";
+import { trans } from "i18n";
+import { changeChildAction, ConstructorToComp } from "lowcoder-core";
+import { TableOnEventView } from "./tableTypes";
+import { OB_ROW_ORI_INDEX, RecordType } from "comps/comps/tableComp/tableUtils";
+import { ControlNodeCompBuilder } from "comps/generators/controlCompBuilder";
+
+// double-click detection constants
+const DOUBLE_CLICK_THRESHOLD = 300; // ms
+let lastClickTime = 0;
+let clickTimer: ReturnType<typeof setTimeout>;
+
+const modeOptions = [
+  {
+    label: trans("selectionControl.single"),
+    value: "single",
+  },
+  {
+    label: trans("selectionControl.multiple"),
+    value: "multiple",
+  },
+  {
+    label: trans("selectionControl.close"),
+    value: "close",
+  },
+] as const;
+
+/**
+ * Currently use index as key
+ */
+function getKey(record: RecordType) {
+  return record[OB_ROW_ORI_INDEX];
+}
+
+export function getSelectedRowKeys(
+  selection: ConstructorToComp<typeof SelectionControl>
+): Array<string> {
+  const mode = selection.children.mode.getView();
+  switch (mode) {
+    case "single":
+      return [selection.children.selectedRowKey.getView()];
+    case "multiple":
+      return selection.children.selectedRowKeys.getView();
+    default:
+      return [];
+  }
+}
+
+export const SelectionControl = (function () {
+  const childrenMap = {
+    mode: dropdownControl(modeOptions, "single"),
+    selectedRowKey: stateComp<string>("0"),
+    selectedRowKeys: stateComp<Array<string>>([]),
+  };
+  return new ControlNodeCompBuilder(childrenMap, (props, dispatch) => {
+    const changeSelectedRowKey = (record: RecordType) => {
+      const key = getKey(record);
+      if (key !== props.selectedRowKey) {
+        dispatch(changeChildAction("selectedRowKey", key, false));
+      }
+    };
+
+    return (onEvent: TableOnEventView) => {
+      const handleClick = (record: RecordType) => {
+        return () => {
+          const now = Date.now();
+          clearTimeout(clickTimer);
+          if (now - lastClickTime < DOUBLE_CLICK_THRESHOLD) {
+            
+            changeSelectedRowKey(record);
+            onEvent("doubleClick");
+            if (getKey(record) !== props.selectedRowKey) {
+              onEvent("rowSelectChange");
+            }
+          } else {
+            clickTimer = setTimeout(() => {
+              changeSelectedRowKey(record);
+              onEvent("rowClick");
+              if (getKey(record) !== props.selectedRowKey) {
+                onEvent("rowSelectChange");
+              }
+            }, DOUBLE_CLICK_THRESHOLD);
+          }
+          lastClickTime = now;
+        };
+      };
+
+      if (props.mode === "single" || props.mode === "close") {
+        return {
+          rowKey: getKey,
+          rowClassName: (record: RecordType, index: number, indent: number) => {
+            if (props.mode === "close") {
+              return "";
+            }
+            return getKey(record) === props.selectedRowKey ? "ant-table-row-selected" : "";
+          },
+          onRow: (record: RecordType, index: number | undefined) => ({
+            onClick: handleClick(record),
+          }),
+        };
+      }
+
+      const result: TableRowSelection<any> = {
+        type: "checkbox",
+        selectedRowKeys: props.selectedRowKeys,
+        preserveSelectedRowKeys: true,
+        onChange: (selectedRowKeys) => {
+          dispatch(changeChildAction("selectedRowKeys", selectedRowKeys as string[], false));
+          onEvent("rowSelectChange");
+        },
+        onSelect: (record: RecordType) => {
+          changeSelectedRowKey(record);
+          onEvent("rowClick");
+        },
+      };
+      return {
+        rowKey: getKey,
+        rowSelection: result,
+        onRow: (record: RecordType) => ({
+          onClick: handleClick(record),
+        }),
+      };
+    };
+  })
+    .setPropertyViewFn((children) =>
+      children.mode.propertyView({
+        label: trans("selectionControl.mode"),
+        radioButton: true,
+      })
+    )
+    .build();
+})();
\ No newline at end of file
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.test.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.test.tsx
new file mode 100644
index 0000000000..96121e4f59
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.test.tsx
@@ -0,0 +1,213 @@
+import { ColumnComp } from "comps/comps/tableComp/column/tableColumnComp";
+import { evalAndReduce } from "comps/utils";
+import _ from "lodash";
+import { fromValue } from "lowcoder-core";
+import { MemoryRouter } from "react-router-dom";
+import { MockTableComp } from "./mockTableComp";
+import { TableLiteComp } from "./tableComp";
+import { OB_ROW_ORI_INDEX } from "./tableUtils";
+
+test("test column", () => {
+  const columnData = {
+    title: "name",
+    // editable: true, // TODO: change to boolean
+  };
+  let comp = new ColumnComp({ value: columnData });
+  comp = evalAndReduce(comp);
+  const columnOutput = comp.getView();
+  expect(columnOutput.title).toEqual(columnData.title);
+  // expect(columnOutput.editable).toEqual(columnData.editable);
+});
+
+test("test column render", () => {
+  const columnData = {
+    render: {
+      compType: "text" as const,
+      comp: {
+        text: "{{currentRow.id}}",
+      },
+    },
+    // editable: true, // TODO: change to boolean
+  };
+  let comp = new ColumnComp({ value: columnData });
+  comp = evalAndReduce(comp);
+  const columnOutput = comp.getView();
+  expect(
+    (
+      columnOutput
+        .render(
+          {
+            currentCell: null,
+            currentIndex: null,
+            currentRow: { id: "hello" },
+            currentOriginalIndex: null,
+          },
+          "0"
+        )
+        .getView()
+        .view({}) as any
+    ).props.normalView
+  ).toEqual("hello");
+  // FIXME: see what should be output if the input is wrong
+  // expect(columnOutput.render()).toEqual("");
+  // expect(columnOutput.render(null, "def")).toEqual("");
+});
+
+test("test table", async () => {
+  // jest.setTimeout(1000);
+  const tableData = {
+    data: JSON.stringify([{ a: 1 }]),
+    columns: [
+      {
+        dataIndex: "a",
+        hide: true,
+      },
+      {
+        title: "custom",
+        dataIndex: "",
+        isCustom: true,
+      },
+    ],
+  };
+  const exposingInfo: any = {
+    query1: fromValue({ data: [{ q: 1 }] }),
+    query2: fromValue({ data: [{ q2: 2 }] }),
+  };
+  let comp = new TableLiteComp({
+    dispatch: (action: any) => {
+      comp = evalAndReduce(comp.reduce(action), exposingInfo);
+    },
+    value: tableData,
+  });
+  comp = evalAndReduce(comp);
+  let columns = comp.children.columns.getView();
+  expect(columns.length).toEqual(2);
+  comp = evalAndReduce(comp.reduce(comp.changeChildAction("data", '[{"a":1, "c":2, "d":3}]')));
+  await new Promise((r) => setTimeout(r, 20));
+  columns = comp.children.columns.getView();
+  expect(columns.length).toEqual(4);
+  expect(columns[0].getView().dataIndex).toEqual("a");
+  expect(columns[0].getView().hide).toBe(true);
+  expect(columns[1].getView().title).toEqual("custom");
+  expect(columns[2].getView().title).toEqual("c");
+  expect(columns[3].getView().title).toEqual("d");
+}, 1000);
+
+// FIXME: add a single test for the click action of the table
+
+function DebugContainer(props: any) {
+  return (
+    <MemoryRouter initialEntries={[{ pathname: "/", search: "?value=teresa_teng" }]}>
+      {props.comp.getView()}
+    </MemoryRouter>
+  );
+}
+
+test("test mock table render", () => {
+  let comp = new MockTableComp({});
+  comp = evalAndReduce(comp);
+  // render(<DebugContainer comp={comp}/>);
+  // screen.getByText(/Date/i);
+});
+
+test("test table data transform", () => {
+  function getAndExpectTableData(expectDisplayDataLen: number, comp: any) {
+    const exposingValues = comp.exposingValues;
+    const displayData = exposingValues["displayData"];
+    const { data } = comp.getProps();
+    const filteredData = comp.filterData;
+    // Transform, sort, filter the raw data.
+    expect(data.length).toEqual(3);
+    expect(displayData.length).toEqual(expectDisplayDataLen);
+    // Remove the custom column, displayData is the same as tranFormData, if title is not defined
+    expect(displayData.map((d: any) => _.omit(d, "custom"))).toEqual(
+      _.map(filteredData, (row) => _.omit(row, OB_ROW_ORI_INDEX))
+    );
+    return { transformedData: filteredData, data, displayData };
+  }
+
+  const tableData = {
+    data: JSON.stringify([
+      { id: 1, name: "gg" },
+      { id: 5, name: "gg2" },
+      { id: 3, name: "jjj" },
+    ]),
+    columns: [
+      {
+        dataIndex: "id",
+        isCustom: false,
+        sortable: true,
+        render: { compType: "text" as const, comp: { text: "{{currentCell}}" } },
+      },
+      {
+        dataIndex: "name",
+        isCustom: false,
+        render: { compType: "text" as const, comp: { text: "{{currentCell}}" } },
+      },
+      {
+        title: "custom",
+        dataIndex: "ealekfg",
+        isCustom: true,
+        render: {
+          compType: "image" as const,
+          comp: {
+            src: "{{currentRow.id}}",
+          },
+        },
+      },
+    ],
+  };
+  let comp = new TableLiteComp({
+    dispatch: (action: any) => {
+      comp = evalAndReduce(comp.reduce(action));
+    },
+    value: tableData,
+  });
+  comp = evalAndReduce(comp);
+  // id sort
+  comp = evalAndReduce(
+    comp.reduce(
+      comp.changeChildAction("sort", [
+        {
+          column: "id",
+          desc: true,
+        },
+      ])
+    )
+  );
+  let { transformedData, data, displayData } = getAndExpectTableData(3, comp);
+  expect(transformedData.map((d: any) => d["id"])).toEqual([5, 3, 1]);
+  // search
+  comp = evalAndReduce(
+    comp.reduce(
+      comp.changeChildAction("toolbar", {
+        searchText: "gg",
+      })
+    )
+  );
+  getAndExpectTableData(2, comp);
+  // filter
+  comp = evalAndReduce(
+    comp.reduce(
+      comp.changeChildAction("toolbar", {
+        showFilter: true,
+        filter: {
+          stackType: "and",
+          filters: [
+            {
+              columnKey: "id",
+              filterValue: "4",
+              operator: "gt",
+            },
+            {
+              columnKey: "id",
+              filterValue: "5",
+              operator: "lte",
+            },
+          ],
+        },
+      })
+    )
+  );
+  getAndExpectTableData(1, comp);
+});
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx
new file mode 100644
index 0000000000..3f3b324115
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx
@@ -0,0 +1,1019 @@
+import { tableDataRowExample } from "comps/comps/tableComp/column/tableColumnListComp";
+import { getPageSize } from "comps/comps/tableComp/paginationControl";
+import { EMPTY_ROW_KEY, TableCompView } from "comps/comps/tableLiteComp/tableCompView";
+import { TableFilter } from "comps/comps/tableComp/tableToolbarComp";
+import {
+  columnHide,
+  ColumnsAggrData,
+  COLUMN_CHILDREN_KEY,
+  filterData,
+  genSelectionParams,
+  getColumnsAggr,
+  getOriDisplayData,
+  OB_ROW_ORI_INDEX,
+  RecordType,
+  sortData,
+  transformDispalyData,
+  tranToTableRecord,
+} from "comps/comps/tableComp/tableUtils";
+import { isTriggerAction } from "comps/controls/actionSelector/actionSelectorControl";
+import { withPropertyViewFn, withViewFn } from "comps/generators";
+import { childrenToProps } from "comps/generators/multi";
+import { HidableView } from "comps/generators/uiCompBuilder";
+import { withDispatchHook } from "comps/generators/withDispatchHook";
+import {
+  CompDepsConfig,
+  depsConfig,
+  DepsConfig,
+  NameConfig,
+  withExposingConfigs,
+} from "comps/generators/withExposing";
+import { withMethodExposing } from "comps/generators/withMethodExposing";
+import { MAP_KEY } from "comps/generators/withMultiContext";
+import { NameGenerator } from "comps/utils";
+import { trans } from "i18n";
+import _, { isArray } from "lodash";
+import {
+  changeChildAction,
+  CompAction,
+  CompActionTypes,
+  deferAction,
+  executeQueryAction,
+  fromRecord,
+  FunctionNode,
+  Node,
+  onlyEvalAction,
+  RecordNode,
+  RecordNodeToValue,
+  routeByNameAction,
+  ValueAndMsg,
+  withFunction,
+  wrapChildAction,
+} from "lowcoder-core";
+import { saveDataAsFile } from "util/fileUtils";
+import { JSONObject, JSONValue } from "util/jsonTypes";
+import { lastValueIfEqual, shallowEqual } from "util/objectUtils";
+import { IContainer } from "../containerBase";
+import { getSelectedRowKeys } from "./selectionControl";
+import { compTablePropertyView } from "./tablePropertyView";
+import { RowColorComp, RowHeightComp, SortValue, TableChildrenView, TableInitComp } from "./tableTypes";
+
+import { useContext, useState } from "react";
+import { EditorContext } from "comps/editorState";
+
+export class TableImplComp extends TableInitComp implements IContainer {
+  private prevUnevaledValue?: string;
+  readonly filterData: RecordType[] = [];
+  readonly columnAggrData: ColumnsAggrData = {};
+
+  override autoHeight(): boolean {
+    return this.children.autoHeight.getView();
+  }
+
+  getTableAutoHeight() {
+    return this.children.autoHeight.getView();
+  }
+  
+  private getSlotContainer() {
+    return this.children.expansion.children.slot.getSelectedComp().getComp().children.container;
+  }
+
+  findContainer(key: string) {
+    return this.getSlotContainer().findContainer(key);
+  }
+
+  getCompTree() {
+    return this.getSlotContainer().getCompTree();
+  }
+
+  getPasteValue(nameGenerator: NameGenerator) {
+    return {
+      ...this.toJsonValue(),
+      expansion: this.children.expansion.getPasteValue(nameGenerator),
+    };
+  }
+
+  realSimpleContainer(key?: string) {
+    return this.getSlotContainer().realSimpleContainer(key);
+  }
+
+  downloadData(fileName: string) {
+    saveDataAsFile({
+      data: (this as any).exposingValues["displayData"],
+      filename: fileName,
+      fileType: "csv",
+      delimiter: this.children.toolbar.children.columnSeparator.getView(),
+    });
+  }
+
+  refreshData(allQueryNames: Array<string>, setLoading: (loading: boolean) => void) {
+    const deps: Array<string> = this.children.data.exposingNode().dependNames();
+    const depsQueryNames = deps.map((d) => d.split(".")[0]);
+    if (_.isEmpty(depsQueryNames)) {
+      // Independent query, using local data, giving a fake loading effect
+      setLoading(true);
+      setTimeout(() => setLoading(false), 200);
+      return;
+    }
+    const queryNameSet = new Set(allQueryNames);
+    depsQueryNames.forEach((name) => {
+      if (queryNameSet.has(name)) {
+        this.dispatch(deferAction(routeByNameAction(name, executeQueryAction({}))));
+      }
+    });
+  }
+
+  // only for test?
+  getProps() {
+    return childrenToProps(_.omit(this.children, "style")) as TableChildrenView;
+  }
+
+  shouldGenerateColumn(comp: this, nextRowExample?: JSONObject) {
+    const columnKeys = comp.children.columns
+      .getView()
+      .map((col) => {
+        const colView = col.getView();
+        if (colView.isCustom) {
+          return "";
+        } else {
+          return colView.dataIndex;
+        }
+      })
+      .filter((t) => !!t);
+    const nextUnevaledVal = comp.children.data.unevaledValue;
+    const prevUnevaledVal = this.prevUnevaledValue;
+    if (!nextRowExample) {
+      this.prevUnevaledValue = nextUnevaledVal;
+      return false;
+    }
+    let doGenColumn = false;
+    const nextRowKeys = Object.keys(nextRowExample);
+    const dynamicColumn = comp.children.dynamicColumn.getView();
+    if (!prevUnevaledVal && columnKeys.length === 0) {
+      // the first time
+      doGenColumn = true;
+    } else if (prevUnevaledVal && nextUnevaledVal !== prevUnevaledVal) {
+      // modify later
+      doGenColumn = true;
+    } else if (dynamicColumn) {
+      doGenColumn = true;
+    } else if (
+      columnKeys.length < nextRowKeys.length &&
+      columnKeys.every((key) => nextRowKeys.includes(key))
+    ) {
+      // new column is automatically generated
+      doGenColumn = true;
+    }
+    if (!doGenColumn) {
+      const toBeGenRow = comp.children.dataRowExample.getView();
+      const columnKeyChanged =
+        columnKeys.length !== nextRowKeys.length ||
+        !_.isEqual(_.sortBy(columnKeys), _.sortBy(nextRowKeys));
+      // The data has changed, but can't judge the auto generation
+      if (columnKeyChanged && !_.isEqual(toBeGenRow, nextRowExample)) {
+        setTimeout(() => {
+          comp.children.dataRowExample.dispatchChangeValueAction(nextRowExample);
+        });
+      } else if (!columnKeyChanged && toBeGenRow) {
+        setTimeout(() => {
+          comp.children.dataRowExample.dispatchChangeValueAction(null);
+        });
+      }
+    }
+    this.prevUnevaledValue = nextUnevaledVal;
+    return doGenColumn;
+  }
+
+  override reduce(action: CompAction): this {
+    let comp = super.reduce(action);
+    let dataChanged = false;
+    if (action.type === CompActionTypes.UPDATE_NODES_V2) {
+      const nextRowExample = tableDataRowExample(comp.children.data.getView());
+      dataChanged =
+        comp.children.data !== this.children.data &&
+        !_.isEqual(this.children.data.getView(), comp.children.data.getView());
+      if (dataChanged) {
+        // update rowColor context
+        comp = comp.setChild(
+          "rowColor",
+          comp.children.rowColor.reduce(
+            RowColorComp.changeContextDataAction({
+              currentRow: nextRowExample,
+              currentIndex: 0,
+              currentOriginalIndex: 0,
+              columnTitle: nextRowExample ? Object.keys(nextRowExample)[0] : undefined,
+            })
+          )
+        );
+        comp = comp.setChild(
+          "rowHeight",
+          comp.children.rowHeight.reduce(
+            RowHeightComp.changeContextDataAction({
+              currentRow: nextRowExample,
+              currentIndex: 0,
+              currentOriginalIndex: 0,
+              columnTitle: nextRowExample ? Object.keys(nextRowExample)[0] : undefined,
+            })
+          )
+        );
+      }
+
+      if (dataChanged) {
+        const doGene = comp.shouldGenerateColumn(comp, nextRowExample);
+        const actions: CompAction[] = [];
+        actions.push(
+          wrapChildAction(
+            "columns",
+            comp.children.columns.dataChangedAction({
+              rowExample: nextRowExample || {},
+              doGeneColumn: doGene,
+              dynamicColumn: comp.children.dynamicColumn.getView(),
+              data: comp.children.data.getView(),
+            })
+          )
+        );
+        doGene && actions.push(comp.changeChildAction("dataRowExample", null));
+        setTimeout(() => {
+          actions.forEach((action) => comp.dispatch(deferAction(action)));
+        }, 0);
+      }
+    }
+
+    let needMoreEval = false;
+
+    const thisSelection = getSelectedRowKeys(this.children.selection)[0] ?? "0";
+    const newSelection = getSelectedRowKeys(comp.children.selection)[0] ?? "0";
+    const selectionChanged =
+      this.children.selection !== comp.children.selection && thisSelection !== newSelection;
+    if (
+      (action.type === CompActionTypes.CUSTOM &&
+        comp.children.columns.getView().length !== this.children.columns.getView().length) ||
+      selectionChanged
+    ) {
+      comp = comp.setChild(
+        "columns",
+        comp.children.columns.reduce(comp.children.columns.setSelectionAction(newSelection))
+      );
+      needMoreEval = true;
+    }
+
+    let params = comp.children.expansion.children.slot.getCachedParams(newSelection);
+    if (selectionChanged || _.isNil(params) || dataChanged) {
+      params =
+        _.isNil(params) || dataChanged
+          ? genSelectionParams(comp.filterData, newSelection)
+          : undefined;
+      comp = comp.setChild(
+        "expansion",
+        comp.children.expansion.reduce(
+          comp.children.expansion.setSelectionAction(newSelection, params)
+        )
+      );
+      needMoreEval = true;
+    }
+    if (action.type === CompActionTypes.UPDATE_NODES_V2 && needMoreEval) {
+      setTimeout(() => comp.dispatch(onlyEvalAction()));
+    }
+    // console.info("exit tableComp reduce. action: ", action, "\nthis: ", this, "\ncomp: ", comp);
+    return comp;
+  }
+
+  override extraNode() {
+    const extra = {
+      sortedData: this.sortDataNode(),
+      filterData: this.filterNode(),
+      oriDisplayData: this.oriDisplayDataNode(),
+      columnAggrData: this.columnAggrNode(),
+    };
+    return {
+      node: extra,
+      updateNodeFields: (value: any) => ({
+        filterData: value.filterData,
+        columnAggrData: value.columnAggrData,
+      }),
+    };
+  }
+
+  // handle sort: data -> sortedData
+  sortDataNode() {
+    const nodes: {
+      data: Node<JSONObject[]>;
+      sort: Node<SortValue[]>;
+      dataIndexes: RecordNode<Record<string, Node<string>>>;
+      sortables: RecordNode<Record<string, Node<ValueAndMsg<boolean>>>>;
+      withParams: RecordNode<_.Dictionary<any>>,
+  } = {
+      data: this.children.data.exposingNode(),
+      sort: this.children.sort.node(),
+      dataIndexes: this.children.columns.getColumnsNode("dataIndex"),
+      sortables: this.children.columns.getColumnsNode("sortable"),
+      withParams: this.children.columns.withParamsNode(),
+    };
+    const sortedDataNode = withFunction(fromRecord(nodes), (input) => {
+      const { data, sort, dataIndexes, sortables } = input;
+      const sortColumns = _(dataIndexes)
+        .mapValues((dataIndex, idx) => ({ sortable: !!sortables[idx] }))
+        .mapKeys((sortable, idx) => dataIndexes[idx])
+        .value();
+      const dataColumns = _(dataIndexes)
+        .mapValues((dataIndex, idx) => ({
+          dataIndex,
+          render: input.withParams[idx] as any,
+        }))
+        .value();
+      const updatedData: Array<RecordType> = data.map((row, index) => ({
+        ...row,
+        [OB_ROW_ORI_INDEX]: index + "",
+      }));
+      const updatedDataMap: Record<string, RecordType> = {};
+      updatedData.forEach((row) => {
+        updatedDataMap[row[OB_ROW_ORI_INDEX]] = row;
+      })
+      const originalData = getOriDisplayData(updatedData, 1000, Object.values(dataColumns))
+      const sortedData = sortData(originalData, sortColumns, sort);
+
+      // console.info( "sortNode. data: ", data, " sort: ", sort, " columns: ", columns, " sortedData: ", sortedData);
+      const newData = sortedData.map(row => {
+        return {
+          ...row,
+          ...updatedDataMap[row[OB_ROW_ORI_INDEX]],
+        }
+      });
+      return newData;
+    });
+    return lastValueIfEqual(this, "sortedDataNode", [sortedDataNode, nodes] as const, (a, b) =>
+      shallowEqual(a[1], b[1])
+    )[0];
+  }
+
+  // handle hide/search/filter: sortedData->filteredData
+  filterNode() {
+    const nodes = {
+      data: this.sortDataNode(),
+      searchValue: this.children.searchText.node(),
+      filter: this.children.toolbar.children.filter.node(),
+      showFilter: this.children.toolbar.children.showFilter.node(),
+    };
+    let context = this;
+    const filteredDataNode = withFunction(fromRecord(nodes), (input) => {
+      const { data, searchValue, filter, showFilter } = input;
+      const filteredData = filterData(data, searchValue.value, filter, showFilter.value);
+      // console.info("filterNode. data: ", data, " filter: ", filter, " filteredData: ", filteredData);
+      // if data is changed on search then trigger event
+      if(Boolean(searchValue.value) && data.length !== filteredData.length) {
+        const onEvent = context.children.onEvent.getView();
+        setTimeout(() => {
+          onEvent("dataSearch");
+        });
+      }
+      return filteredData.map((row) => tranToTableRecord(row, row[OB_ROW_ORI_INDEX]));
+    });
+    return lastValueIfEqual(this, "filteredDataNode", [filteredDataNode, nodes] as const, (a, b) =>
+      shallowEqual(a[1], b[1])
+    )[0];
+  }
+
+  oriDisplayDataNode() {
+    const nodes = {
+      data: this.filterNode(),
+      // --> pageSize
+      showSizeChanger: this.children.pagination.children.showSizeChanger.node(),
+      pageSize: this.children.pagination.children.pageSize.node(),
+      pageSizeOptions: this.children.pagination.children.pageSizeOptions.node(),
+      changablePageSize: this.children.pagination.children.changeablePageSize.node(),
+      // <-- pageSize
+      withParams: this.children.columns.withParamsNode(),
+      dataIndexes: this.children.columns.getColumnsNode("dataIndex"),
+    };
+    const resNode = withFunction(fromRecord(nodes), (input) => {
+      const columns = _(input.dataIndexes)
+        .mapValues((dataIndex, idx) => ({
+          dataIndex,
+          render: input.withParams[idx],
+        }))
+        .value();
+      const pageSize = getPageSize(
+        input.showSizeChanger.value,
+        input.pageSize.value,
+        input.pageSizeOptions.value,
+        input.changablePageSize
+      );
+      return getOriDisplayData(input.data, pageSize, Object.values(columns));
+    });
+    return lastValueIfEqual(this, "oriDisplayDataNode", [resNode, nodes] as const, (a, b) =>
+      shallowEqual(a[1], b[1])
+    )[0];
+  }
+
+  displayDataIndexesNode() {
+    const nodes = {
+      oriDisplayData: this.oriDisplayDataNode(),
+    };
+    const resNode = withFunction(fromRecord(nodes), (input) => {
+      return _(input.oriDisplayData)
+        .map((row, idx) => [row[OB_ROW_ORI_INDEX], idx] as [string, number])
+        .fromPairs()
+        .value();
+    });
+    return lastValueIfEqual(this, "displayDataIndexesNode", [resNode, nodes] as const, (a, b) =>
+      shallowEqual(a[1], b[1])
+    )[0];
+  }
+
+  private getUpsertSetResNode(
+    nodes: Record<string, RecordNode<Record<string, Node<any>>>>,
+    filterNewRows?: boolean,
+  ) {
+    return withFunction(fromRecord(nodes), (input) => {
+      // merge input.dataIndexes and input.withParams into one structure
+      const dataIndexRenderDict = _(input.dataIndexes)
+        .mapValues((dataIndex, idx) => input.renders[idx])
+        .mapKeys((render, idx) => input.dataIndexes[idx])
+        .value();
+      const record: Record<string, Record<string, JSONValue>> = {};
+      _.forEach(dataIndexRenderDict, (render, dataIndex) => {
+        _.forEach(render[MAP_KEY], (value, key) => {
+          const changeValue = (value.comp as any).comp.changeValue;
+          const includeRecord = (filterNewRows && key.startsWith(EMPTY_ROW_KEY)) || (!filterNewRows && !key.startsWith(EMPTY_ROW_KEY));
+          if (!_.isNil(changeValue) && includeRecord) {
+            if (!record[key]) record[key] = {};
+            record[key][dataIndex] = changeValue;
+          }
+        });
+      });
+      return record;
+    });
+  }
+
+  changeSetNode() {
+    const nodes = {
+      dataIndexes: this.children.columns.getColumnsNode("dataIndex"),
+      renders: this.children.columns.getColumnsNode("render"),
+    };
+
+    const resNode = this.getUpsertSetResNode(nodes);
+    return lastValueIfEqual(this, "changeSetNode", [resNode, nodes] as const, (a, b) =>
+      shallowEqual(a[1], b[1])
+    )[0];
+  }
+
+  insertSetNode() {
+    const nodes = {
+      dataIndexes: this.children.columns.getColumnsNode("dataIndex"),
+      renders: this.children.columns.getColumnsNode("render"),
+    };
+
+    const resNode = this.getUpsertSetResNode(nodes, true);
+    return lastValueIfEqual(this, "insertSetNode", [resNode, nodes] as const, (a, b) =>
+      shallowEqual(a[1], b[1])
+    )[0];
+  }
+
+  private getToUpsertRowsResNodes(
+    nodes: Record<string, FunctionNode<any, any>>
+  ) {
+    return withFunction(fromRecord(nodes), (input) => {
+      const res = _(input.changeSet)
+        .map((changeValues, oriIndex) => {
+          const idx = input.indexes[oriIndex];
+          const oriRow = _.omit(input.oriDisplayData[idx], OB_ROW_ORI_INDEX);
+          return { ...oriRow, ...changeValues };
+        })
+        .value();
+      // console.info("toUpdateRowsNode. input: ", input, " res: ", res);
+      return res;
+    });
+  }
+
+  toUpdateRowsNode() {
+    const nodes = {
+      oriDisplayData: this.oriDisplayDataNode(),
+      indexes: this.displayDataIndexesNode(),
+      changeSet: this.changeSetNode(),
+    };
+
+    const resNode = this.getToUpsertRowsResNodes(nodes);
+    return lastValueIfEqual(this, "toUpdateRowsNode", [resNode, nodes] as const, (a, b) =>
+      shallowEqual(a[1], b[1])
+    )[0];
+  }
+
+  toInsertRowsNode() {
+    const nodes = {
+      oriDisplayData: this.oriDisplayDataNode(),
+      indexes: this.displayDataIndexesNode(),
+      changeSet: this.insertSetNode(),
+    };
+
+    const resNode = this.getToUpsertRowsResNodes(nodes);
+    return lastValueIfEqual(this, "toInsertRowsNode", [resNode, nodes] as const, (a, b) =>
+      shallowEqual(a[1], b[1])
+    )[0];
+  }
+
+  columnAggrNode() {
+    const nodes = {
+      oriDisplayData: this.oriDisplayDataNode(),
+      withParams: this.children.columns.withParamsNode(),
+      dataIndexes: this.children.columns.getColumnsNode("dataIndex"),
+    };
+    const resNode = withFunction(fromRecord(nodes), (input) => {
+      const dataIndexWithParamsDict = _(input.dataIndexes)
+        .mapValues((dataIndex, idx) => input.withParams[idx])
+        .mapKeys((withParams, idx) => input.dataIndexes[idx])
+        .value();
+      const res = getColumnsAggr(input.oriDisplayData, dataIndexWithParamsDict);
+      // console.info("columnAggrNode: ", res);
+      return res;
+    });
+    return lastValueIfEqual(this, "columnAggrNode", [resNode, nodes] as const, (a, b) =>
+      shallowEqual(a[1], b[1])
+    )[0];
+  }
+}
+
+let TableTmpComp = withViewFn(TableImplComp, (comp) => {
+  const [emptyRows, setEmptyRows] = useState([]);
+  return (
+    <HidableView hidden={comp.children.hidden.getView()}>
+      <TableCompView
+        comp={comp}
+        onRefresh={(allQueryNames, setLoading) => comp.refreshData(allQueryNames, setLoading)}
+        onDownload={(fileName) => comp.downloadData(fileName)}
+      />
+    </HidableView>
+  );
+});
+
+
+const withEditorModeStatus = (Component:any) => (props:any) => {
+  const editorModeStatus = useContext(EditorContext).editorModeStatus;
+  const {ref, ...otherProps} = props;
+  return <Component {...otherProps} editorModeStatus={editorModeStatus} />;
+};
+
+// Use this HOC when defining TableTmpComp
+TableTmpComp = withPropertyViewFn(TableTmpComp, (comp) => withEditorModeStatus(compTablePropertyView)(comp));
+
+// TableTmpComp = withPropertyViewFn(TableTmpComp, compTablePropertyView);
+
+
+
+
+
+/**
+ * Hijack children's execution events and ensure that selectedRow is modified first (you can also add a triggeredRow field).
+ */
+TableTmpComp = withDispatchHook(TableTmpComp, (dispatch) => (action) => {
+  if (!dispatch) {
+    return;
+  }
+  if (isTriggerAction(action)) {
+    const context = action.value.context;
+    if (context && !_.isNil(context["currentOriginalIndex"])) {
+      const key = context["currentOriginalIndex"] + "";
+      dispatch(wrapChildAction("selection", changeChildAction("selectedRowKey", key, false)));
+    }
+    // action.context;
+  }
+  return dispatch(action);
+});
+
+function _indexKeyToRecord(data: JSONObject[], key: string) {
+  const keyPath = (key + "").split("-");
+  let currentData = data;
+  let res = undefined;
+  for (let k of keyPath) {
+    const index = Number(k);
+    if (index >= 0 && Array.isArray(currentData) && index < currentData.length) {
+      res = currentData[index];
+      currentData = res[COLUMN_CHILDREN_KEY] as JSONObject[];
+    }
+  }
+  return res;
+}
+
+function toDisplayIndex(displayData: JSONObject[], selectRowKey: string) {
+  const keyPath = selectRowKey.split("-");
+  const originSelectKey = keyPath[0];
+  if (!originSelectKey) {
+    return "";
+  }
+  let displayIndex;
+  displayData.forEach((data, index) => {
+    if (data[OB_ROW_ORI_INDEX] === originSelectKey) {
+      displayIndex = index;
+    }
+  });
+  if (displayIndex && keyPath.length > 1) {
+    return [displayIndex, ...keyPath.slice(1)].join("-");
+  }
+  return displayIndex;
+}
+
+TableTmpComp = withMethodExposing(TableTmpComp, [
+  {
+    method: {
+      name: "setFilter",
+      description: "",
+      params: [{ name: "filter", type: "JSON" }],
+    },
+    execute: (comp, values) => {
+      if (values[0]) {
+        const param = values[0] as TableFilter;
+        const currentVal = comp.children.toolbar.children.filter.getView();
+        comp.children.toolbar.children.filter.dispatchChangeValueAction({
+          ...currentVal,
+          ...param,
+        });
+      }
+    },
+  },
+  {
+    method: {
+      name: "setPage",
+      description: "",
+      params: [{ name: "page", type: "number" }],
+    },
+    execute: (comp, values) => {
+      const page = values[0] as number;
+      if (page && page > 0) {
+        comp.children.pagination.children.pageNo.dispatchChangeValueAction(page);
+      }
+    },
+  },
+  {
+    method: {
+      name: "setSort",
+      description: "",
+      params: [
+        { name: "sortColumn", type: "string" },
+        { name: "sortDesc", type: "boolean" },
+      ],
+    },
+    execute: (comp, values) => {
+      if (values[0]) {
+        comp.children.sort.dispatchChangeValueAction([
+          {
+            column: values[0] as string,
+            desc: values[1] as boolean,
+          },
+        ]);
+      }
+    },
+  },
+  {
+    method: {
+      name: "setMultiSort",
+      description: "",
+      params: [
+        { name: "sortColumns", type: "arrayObject"},
+      ],
+    },
+    execute: (comp, values) => {
+      const sortColumns = values[0];
+      if (!isArray(sortColumns)) {
+        return Promise.reject("setMultiSort function only accepts array of sort objects i.e. [{column: column_name, desc: boolean}]")
+      }
+      if (sortColumns && isArray(sortColumns)) {
+        comp.children.sort.dispatchChangeValueAction(sortColumns as SortValue[]);
+      }
+    },
+  },
+  {
+    method: {
+      name: "resetSelections",
+      description: "",
+      params: [],
+    },
+    execute: (comp) => {
+      comp.children.selection.children.selectedRowKey.dispatchChangeValueAction("0");
+      comp.children.selection.children.selectedRowKeys.dispatchChangeValueAction([]);
+    },
+  },
+  {
+    method: {
+      name: "selectAll",
+      description: "Select all rows in the current filtered view",
+      params: [],
+    },
+    execute: (comp) => {
+      const displayData = comp.filterData ?? [];
+      const allKeys = displayData.map((row) => row[OB_ROW_ORI_INDEX] + "");
+      comp.children.selection.children.selectedRowKey.dispatchChangeValueAction(allKeys[0] || "0");
+      comp.children.selection.children.selectedRowKeys.dispatchChangeValueAction(allKeys);
+    },
+  },  
+  {
+    method: {
+      name: "cancelChanges",
+      description: "",
+      params: [],
+    },
+    execute: (comp, values) => {
+      comp.children.columns.dispatchClearChangeSet();
+    },
+  },
+  {
+    method: {
+      name: "cancelInsertChanges",
+      description: "",
+      params: [],
+    },
+    execute: (comp, values) => {
+      comp.children.columns.dispatchClearInsertSet();
+    },
+  },
+  {
+    method: {
+      name: "setExpandedRows",
+      description: "",
+      params: [
+        { name: "expandedRows", type: "arrayString"},
+      ],
+    },
+    execute: (comp, values) => {
+      const expandedRows = values[0];
+      if (!isArray(expandedRows)) {
+        return Promise.reject("setExpandedRows function only accepts array of string i.e. ['1', '2', '3']")
+      }
+      if (expandedRows && isArray(expandedRows)) {
+        comp.children.currentExpandedRows.dispatchChangeValueAction(expandedRows as string[]);
+      }
+    },
+  }
+]);
+
+// exposing data
+export const TableLiteComp = withExposingConfigs(TableTmpComp, [
+  new DepsConfig(
+    "selectedRow",
+    (children) => {
+      return {
+        selectedRowKey: children.selection.children.selectedRowKey.node(),
+        data: children.data.exposingNode(),
+      };
+    },
+    (input) => {
+      if (!input.data) {
+        return undefined;
+      }
+      return _indexKeyToRecord(input.data, input.selectedRowKey);
+    },
+    trans("table.selectedRowDesc")
+  ),
+  new DepsConfig(
+    "selectedRows",
+    (children) => {
+      return {
+        selectedRowKeys: children.selection.children.selectedRowKeys.node(),
+        data: children.data.exposingNode(),
+      };
+    },
+    (input) => {
+      if (!input.data) {
+        return undefined;
+      }
+      return input.selectedRowKeys.flatMap((key: string) => {
+        const result = _indexKeyToRecord(input.data, key);
+        return result === undefined ? [] : [result];
+      });
+    },
+    trans("table.selectedRowsDesc")
+  ),
+  new CompDepsConfig(
+    "selectedIndex",
+    (comp) => {
+      return {
+        oriDisplayData: comp.oriDisplayDataNode(),
+        selectedRowKey: comp.children.selection.children.selectedRowKey.node(),
+      };
+    },
+    (input) => {
+      return toDisplayIndex(input.oriDisplayData, input.selectedRowKey);
+    },
+    trans("table.selectedIndexDesc")
+  ),
+  new CompDepsConfig(
+    "selectedIndexes",
+    (comp) => {
+      return {
+        oriDisplayData: comp.oriDisplayDataNode(),
+        selectedRowKeys: comp.children.selection.children.selectedRowKeys.node(),
+      };
+    },
+    (input) => {
+      return input.selectedRowKeys.flatMap((key: string) => {
+        const result = toDisplayIndex(input.oriDisplayData, key);
+        return result === undefined ? [] : [result];
+      });
+    },
+    trans("table.selectedIndexDesc")
+  ),
+  new CompDepsConfig(
+    "changeSet",
+    (comp) => ({
+      changeSet: comp.changeSetNode(),
+    }),
+    (input) => input.changeSet,
+    trans("table.changeSetDesc")
+  ),
+  new CompDepsConfig(
+    "insertSet",
+    (comp) => ({
+      insertSet: comp.insertSetNode(),
+    }),
+    (input) => input.insertSet,
+    trans("table.changeSetDesc")
+  ),
+  new CompDepsConfig(
+    "toUpdateRows",
+    (comp) => ({
+      toUpdateRows: comp.toUpdateRowsNode(),
+    }),
+    (input) => {
+      return input.toUpdateRows;
+    },
+    trans("table.toUpdateRowsDesc")
+  ),
+  new CompDepsConfig(
+    "toInsertRows",
+    (comp) => ({
+      toInsertRows: comp.toInsertRowsNode(),
+    }),
+    (input) => {
+      return input.toInsertRows;
+    },
+    trans("table.toUpdateRowsDesc")
+  ),
+  new DepsConfig(
+    "pageNo",
+    (children) => {
+      return {
+        pageNo: children.pagination.children.pageNo.exposingNode(),
+      };
+    },
+    (input) => input.pageNo,
+    trans("table.pageNoDesc")
+  ),
+  new DepsConfig(
+    "pageSize",
+    (children) => {
+      return {
+        showSizeChanger: children.pagination.children.showSizeChanger.node(),
+        changeablePageSize: children.pagination.children.changeablePageSize.node(),
+        pageSize: children.pagination.children.pageSize.node(),
+        pageSizeOptions: children.pagination.children.pageSizeOptions.node(),
+      };
+    },
+    (input) => {
+      return getPageSize(
+        input.showSizeChanger.value,
+        input.pageSize.value,
+        input.pageSizeOptions.value,
+        input.changeablePageSize
+      );
+    },
+    trans("table.pageSizeDesc")
+  ),
+  new DepsConfig(
+    "sortColumn",
+    (children) => {
+      return {
+        sort: children.sort.node(),
+        columns: children.columns.node()!,
+      };
+    },
+    (input) => {
+      const sortIndex = input.sort[0]?.column;
+      const column = Object.values(input.columns as any).find(
+        (c: any) => c.dataIndex === sortIndex
+      ) as any;
+      if (column?.isCustom && column?.title.value) {
+        return column.title.value;
+      } else {
+        return sortIndex;
+      }
+    },
+    trans("table.sortColumnDesc")
+  ),
+  new DepsConfig(
+    "sortColumns",
+    (children) => {
+      return {
+        sort: children.sort.node(),
+      };
+    },
+    (input) => {
+      return input.sort;
+    },
+    trans("table.sortColumnDesc")
+  ),
+  depsConfig({
+    name: "sortDesc",
+    desc: trans("table.sortDesc"),
+    depKeys: ["sort"],
+    func: (input) => {
+      return input.sort[0]?.desc || false;
+    },
+  }),
+  new DepsConfig(
+    "pageOffset",
+    (children) => {
+      return {
+        showSizeChanger: children.pagination.children.showSizeChanger.node(),
+        changeablePageSize: children.pagination.children.changeablePageSize.node(),
+        pageSize: children.pagination.children.pageSize.node(),
+        pageSizeOptions: children.pagination.children.pageSizeOptions.node(),
+        pageNo: children.pagination.children.pageNo.node(),
+      };
+    },
+    (input) => {
+      return (
+        getPageSize(
+          input.showSizeChanger.value,
+          input.pageSize.value,
+          input.pageSizeOptions.value,
+          input.changeablePageSize
+        ) *
+        (input.pageNo - 1)
+      );
+    },
+    trans("table.pageOffsetDesc")
+  ),
+  new CompDepsConfig(
+    "displayData",
+    (comp) => {
+      return {
+        oriDisplayData: comp.oriDisplayDataNode(),
+        dataIndexes: comp.children.columns.getColumnsNode("dataIndex"),
+        titles: comp.children.columns.getColumnsNode("title"),
+        // --> hide
+        hides: comp.children.columns.getColumnsNode("hide"),
+        tempHides: comp.children.columns.getColumnsNode("tempHide"),
+        columnSetting: comp.children.toolbar.children.columnSetting.node(),
+        // <-- hide
+      };
+    },
+    (input) => {
+      const dataIndexTitleDict = _(input.dataIndexes)
+        .pickBy(
+          (_1, idx) =>
+            !columnHide({
+              hide: input.hides[idx].value,
+              tempHide: input.tempHides[idx],
+              enableColumnSetting: input.columnSetting.value,
+            })
+        )
+        .mapValues((_dataIndex, idx) => input.titles[idx]?.value)
+        .mapKeys((_title, idx) => input.dataIndexes[idx])
+        .value();
+      return transformDispalyData(input.oriDisplayData, dataIndexTitleDict);
+    },
+    trans("table.displayDataDesc")
+  ),
+  new DepsConfig(
+    "filter",
+    (children) => {
+      return {
+        filter: children.toolbar.children.filter.node(),
+      };
+    },
+    (input) => {
+      return input.filter;
+    },
+    trans("table.filterDesc")
+  ),
+  new DepsConfig(
+    "selectedCell",
+    (children) => {
+      return {
+        selectedCell: children.selectedCell.node(),
+      };
+    },
+    (input) => {
+      return input.selectedCell;
+    },
+    trans("table.selectedCellDesc")
+  ),
+  depsConfig({
+    name: "currentExpandedRow",
+    desc: trans("table.sortDesc"),
+    depKeys: ["currentExpandedRows"],
+    func: (input) => {
+      if (input.currentExpandedRows.length > 0) {
+        return input.currentExpandedRows[input.currentExpandedRows.length - 1];
+      }
+      return "";
+    },
+  }),
+  depsConfig({
+    name: "currentExpandedRows",
+    desc: trans("table.sortDesc"),
+    depKeys: ["currentExpandedRows"],
+    func: (input) => {
+      return input.currentExpandedRows;
+    },
+  }),
+  new NameConfig("data", trans("table.dataDesc")),
+]);
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableCompView.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableCompView.tsx
new file mode 100644
index 0000000000..dc6c88b0d1
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableCompView.tsx
@@ -0,0 +1,1176 @@
+import { default as Table, TableProps, ColumnType } from "antd/es/table";
+import { TableCellContext, TableRowContext } from "comps/comps/tableComp/tableContext";
+import { TableToolbar } from "comps/comps/tableComp/tableToolbarComp";
+import { RowColorViewType, RowHeightViewType, TableEventOptionValues } from "comps/comps/tableComp/tableTypes";
+import {
+  COL_MIN_WIDTH,
+  COLUMN_CHILDREN_KEY,
+  ColumnsAggrData,
+  columnsToAntdFormat,
+  CustomColumnType,
+  OB_ROW_ORI_INDEX,
+  onTableChange,
+  RecordType,
+  supportChildrenTree,
+} from "comps/comps/tableComp/tableUtils";
+import {
+  handleToHoverRow,
+  handleToSelectedRow,
+  TableColumnLinkStyleType,
+  TableColumnStyleType,
+  TableHeaderStyleType,
+  TableRowStyleType,
+  TableStyleType,
+  ThemeDetail,
+  TableToolbarStyleType,
+} from "comps/controls/styleControlConstants";
+import { CompNameContext, EditorContext } from "comps/editorState";
+import { BackgroundColorContext } from "comps/utils/backgroundColorContext";
+import { PrimaryColor } from "constants/style";
+import { trans } from "i18n";
+import _, { isEqual } from "lodash";
+import { darkenColor, isDarkColor, isValidColor, ScrollBar } from "lowcoder-design";
+import React, { Children, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
+import { Resizable } from "react-resizable";
+import styled, { css } from "styled-components";
+import { useMergeCompStyles, useUserViewMode } from "util/hooks";
+import { TableImplComp } from "./tableComp";
+import { useResizeDetector } from "react-resize-detector";
+import { SlotConfigContext } from "comps/controls/slotControl";
+import { EmptyContent } from "pages/common/styledComponent";
+import { messageInstance } from "lowcoder-design/src/components/GlobalInstances";
+import { ReactRef, ResizeHandleAxis } from "layout/gridLayoutPropTypes";
+import { CellColorViewType } from "./column/tableColumnComp";
+import { defaultTheme } from "@lowcoder-ee/constants/themeConstants";
+import { childrenToProps } from "@lowcoder-ee/comps/generators/multi";
+import { getVerticalMargin } from "@lowcoder-ee/util/cssUtil";
+import { TableSummary } from "./tableSummaryComp";
+import Skeleton from "antd/es/skeleton";
+import { SkeletonButtonProps } from "antd/es/skeleton/Button";
+import { ThemeContext } from "@lowcoder-ee/comps/utils/themeContext";
+import { useUpdateEffect } from "react-use";
+
+export const EMPTY_ROW_KEY = 'empty_row';
+
+function genLinerGradient(color: string) {
+  return isValidColor(color) ? `linear-gradient(${color}, ${color})` : color;
+}
+
+const getStyle = (
+  style: TableStyleType,
+  rowStyle: TableRowStyleType,
+  headerStyle: TableHeaderStyleType,
+  toolbarStyle: TableToolbarStyleType,
+) => {
+  const background = genLinerGradient(style.background);
+  const selectedRowBackground = genLinerGradient(rowStyle.selectedRowBackground);
+  const hoverRowBackground = genLinerGradient(rowStyle.hoverRowBackground);
+  const alternateBackground = genLinerGradient(rowStyle.alternateBackground);
+
+  return css`
+    .ant-table-body {
+      background: ${genLinerGradient(style.background)};
+    }
+    .ant-table-tbody {
+      > tr:nth-of-type(2n + 1) {
+        background: ${genLinerGradient(rowStyle.background)};
+      }
+
+      > tr:nth-of-type(2n) {
+        background: ${alternateBackground};
+      }
+
+      // selected row
+      > tr:nth-of-type(2n + 1).ant-table-row-selected {
+        background: ${selectedRowBackground}, ${rowStyle.background} !important;
+        > td.ant-table-cell {
+          background: transparent !important;
+        }
+
+        // > td.ant-table-cell-row-hover,
+        &:hover {
+          background: ${hoverRowBackground}, ${selectedRowBackground}, ${rowStyle.background} !important;
+        }
+      }
+
+      > tr:nth-of-type(2n).ant-table-row-selected {
+        background: ${selectedRowBackground}, ${alternateBackground} !important;
+        > td.ant-table-cell {
+          background: transparent !important;
+        }
+
+        // > td.ant-table-cell-row-hover,
+        &:hover {
+          background: ${hoverRowBackground}, ${selectedRowBackground}, ${alternateBackground} !important;
+        }
+      }
+
+      // hover row
+      > tr:nth-of-type(2n + 1):hover {
+        background: ${hoverRowBackground}, ${rowStyle.background} !important;
+        > td.ant-table-cell-row-hover {
+          background: transparent;
+        }
+      }
+      > tr:nth-of-type(2n):hover {
+        background: ${hoverRowBackground}, ${alternateBackground} !important;
+        > td.ant-table-cell-row-hover {
+          background: transparent;
+        }
+      }
+
+      > tr.ant-table-expanded-row {
+        background: ${background};
+      }
+    }
+  `;
+};
+
+const TitleResizeHandle = styled.span`
+  position: absolute;
+  top: 0;
+  right: -5px;
+  width: 10px;
+  height: 100%;
+  cursor: col-resize;
+  z-index: 1;
+`;
+
+const BackgroundWrapper = styled.div<{
+  $style: TableStyleType;
+  $tableAutoHeight: boolean;
+  $showHorizontalScrollbar: boolean;
+  $showVerticalScrollbar: boolean;
+  $fixedToolbar: boolean;
+}>`
+  display: flex;
+  flex-direction: column;
+  background: ${(props) => props.$style.background} !important;
+  border-radius: ${(props) => props.$style.radius} !important;
+  padding: ${(props) => props.$style.padding} !important;
+  margin: ${(props) => props.$style.margin} !important;
+  border-style: ${(props) => props.$style.borderStyle} !important;
+  border-width: ${(props) => `${props.$style.borderWidth} !important`};
+  border-color: ${(props) => `${props.$style.border} !important`};
+  height: calc(100% - ${(props) => props.$style.margin && getVerticalMargin(props.$style.margin.split(' '))});
+  overflow: hidden;
+
+  > div.table-scrollbar-wrapper {
+    overflow: auto;
+    ${(props) => props.$fixedToolbar && `height: auto`};
+
+    ${(props) => (props.$showHorizontalScrollbar || props.$showVerticalScrollbar) && `
+      .simplebar-content-wrapper {
+        overflow: auto !important;
+      }  
+    `}
+
+    ${(props) => !props.$showHorizontalScrollbar && `
+      div.simplebar-horizontal {
+        visibility: hidden !important;
+      }  
+    `}
+    ${(props) => !props.$showVerticalScrollbar && `
+      div.simplebar-vertical {
+        visibility: hidden !important;
+      }  
+    `}
+  }
+`;
+
+// TODO: find a way to limit the calc function for max-height only to first Margin value
+const TableWrapper = styled.div<{
+  $style: TableStyleType;
+  $headerStyle: TableHeaderStyleType;
+  $toolbarStyle: TableToolbarStyleType;
+  $rowStyle: TableRowStyleType;
+  $toolbarPosition: "above" | "below" | "close";
+  $fixedHeader: boolean;
+  $fixedToolbar: boolean;
+  $visibleResizables: boolean;
+  $showHRowGridBorder?: boolean;
+}>`
+  .ant-table-wrapper {
+    border-top: unset;
+    border-color: inherit;
+  }
+
+  .ant-table-row-expand-icon {
+    color: ${PrimaryColor};
+  }
+
+  .ant-table .ant-table-cell-with-append .ant-table-row-expand-icon {
+    margin: 0;
+    top: 18px;
+    left: 4px;
+  }
+
+  .ant-table.ant-table-small .ant-table-cell-with-append .ant-table-row-expand-icon {
+    top: 10px;
+  }
+
+  .ant-table.ant-table-middle .ant-table-cell-with-append .ant-table-row-expand-icon {
+    top: 14px;
+    margin-right:5px;
+  }
+
+  .ant-table {
+    background: ${(props) =>props.$style.background};
+    .ant-table-container {
+      border-left: unset;
+      border-top: none !important;
+      border-inline-start: none !important;
+
+      &::after {
+        box-shadow: none !important;
+      }
+
+      .ant-table-content {
+        overflow: unset !important
+      }
+
+      // A table expand row contains table
+      .ant-table-tbody .ant-table-wrapper:only-child .ant-table {
+        margin: 0;
+      }
+
+      table {
+        border-top: unset;
+
+        > .ant-table-thead {
+          ${(props) =>
+            props.$fixedHeader && `
+              position: sticky;
+              position: -webkit-sticky;
+              // top: ${props.$fixedToolbar ? '47px' : '0'};
+              top: 0;
+              z-index: 2;
+            `
+          }
+          > tr {
+            background: ${(props) => props.$headerStyle.headerBackground}; 
+          }
+          > tr > th {
+            background: transparent;
+            border-color: ${(props) => props.$headerStyle.border};
+            border-width: ${(props) => props.$headerStyle.borderWidth};
+            color: ${(props) => props.$headerStyle.headerText};
+            // border-inline-end: ${(props) => `${props.$headerStyle.borderWidth} solid ${props.$headerStyle.border}`} !important;
+            
+            /* Proper styling for fixed header cells */
+            &.ant-table-cell-fix-left, &.ant-table-cell-fix-right {
+              z-index: 1; 
+              background: ${(props) => props.$headerStyle.headerBackground};
+            }
+            
+          
+            
+            > div {
+              margin: ${(props) => props.$headerStyle.margin};
+
+              &, .ant-table-column-title > div {
+                font-size: ${(props) => props.$headerStyle.textSize};
+                font-weight: ${(props) => props.$headerStyle.textWeight};
+                font-family: ${(props) => props.$headerStyle.fontFamily};
+                font-style: ${(props) => props.$headerStyle.fontStyle};
+                color:${(props) => props.$headerStyle.text}
+              }
+            }
+
+            &:last-child {
+              border-inline-end: none !important;
+            }
+            &.ant-table-column-has-sorters:hover {
+              background-color: ${(props) => darkenColor(props.$headerStyle.headerBackground, 0.05)};
+            }
+  
+            > .ant-table-column-sorters > .ant-table-column-sorter {
+              color: ${(props) => props.$headerStyle.headerText === defaultTheme.textDark ? "#bfbfbf" : props.$headerStyle.headerText};
+            }
+
+            &::before {
+              background-color: ${(props) => props.$headerStyle.border};
+              width: ${(props) => (props.$visibleResizables ? "1px" : "0px")} !important;
+            }
+          }
+        }
+
+        > thead > tr > th,
+        > tbody > tr > td {
+          border-color: ${(props) => props.$headerStyle.border};
+          ${(props) => !props.$showHRowGridBorder && `border-bottom: 0px;`}
+        }
+
+        td {
+          padding: 0px 0px;
+          // ${(props) => props.$showHRowGridBorder ? 'border-bottom: 1px solid #D7D9E0 !important;': `border-bottom: 0px;`}
+          
+          /* Proper styling for Fixed columns in the table body */
+          &.ant-table-cell-fix-left, &.ant-table-cell-fix-right {
+            z-index: 1; 
+            background: inherit;
+            background-color: ${(props) => props.$style.background};
+            transition: background-color 0.3s;
+          }
+          
+        }
+        
+        /* Fix for selected and hovered rows */
+        tr.ant-table-row-selected td.ant-table-cell-fix-left,
+        tr.ant-table-row-selected td.ant-table-cell-fix-right {
+          background-color: ${(props) => props.$rowStyle?.selectedRowBackground || '#e6f7ff'} !important;
+        }
+        
+        tr.ant-table-row:hover td.ant-table-cell-fix-left,
+        tr.ant-table-row:hover td.ant-table-cell-fix-right {
+          background-color: ${(props) => props.$rowStyle?.hoverRowBackground || '#f5f5f5'} !important;
+        }
+
+        thead > tr:first-child {
+          th:last-child {
+            border-right: unset;
+          }
+        }
+
+        tbody > tr > td:last-child {
+          border-right: unset !important;
+        }
+
+        .ant-empty-img-simple-g {
+          fill: #fff;
+        }
+
+        > thead > tr:first-child {
+          th:first-child {
+            border-top-left-radius: 0px;
+          }
+
+          th:last-child {
+            border-top-right-radius: 0px;
+          }
+        }
+      }
+
+      .ant-table-expanded-row-fixed:after {
+        border-right: unset !important;
+      }
+    }
+  }
+  
+  ${(props) =>
+    props.$style && getStyle(props.$style, props.$rowStyle, props.$headerStyle, props.$toolbarStyle)}
+`;
+
+const TableTh = styled.th<{ width?: number }>`
+  overflow: hidden;
+
+  > div {
+    overflow: hidden;
+    white-space: pre;
+    text-overflow: ellipsis;
+  }
+
+  ${(props) => props.width && `width: ${props.width}px`};
+`;
+
+interface TableTdProps {
+  $background: string;
+  $style: TableColumnStyleType & { rowHeight?: string };
+  $defaultThemeDetail: ThemeDetail;
+  $linkStyle?: TableColumnLinkStyleType;
+  $isEditing: boolean;
+  $tableSize?: string;
+  $autoHeight?: boolean;
+  $customAlign?: 'left' | 'center' | 'right';
+}
+const TableTd = styled.td<TableTdProps>`
+  .ant-table-row-expand-icon,
+  .ant-table-row-indent {
+    display: ${(props) => (props.$isEditing ? "none" : "initial")};
+  }
+  &.ant-table-row-expand-icon-cell {
+    background: ${(props) => props.$background};
+    border-color: ${(props) => props.$style.border};
+  }
+  background: ${(props) => props.$background} !important;
+  border-color: ${(props) => props.$style.border} !important;
+  border-radius: ${(props) => props.$style.radius};
+  padding: 0 !important;
+  text-align: ${(props) => props.$customAlign || 'left'} !important;
+
+  > div:not(.editing-border, .editing-wrapper),
+  .editing-wrapper .ant-input,
+  .editing-wrapper .ant-input-number,
+  .editing-wrapper .ant-picker {
+    margin: ${(props) => props.$isEditing ? '0px' : props.$style.margin};
+    color: ${(props) => props.$style.text};
+    font-weight: ${(props) => props.$style.textWeight};
+    font-family: ${(props) => props.$style.fontFamily};
+    overflow: hidden; 
+    display: flex;
+    justify-content: ${(props) => props.$customAlign === 'center' ? 'center' : props.$customAlign === 'right' ? 'flex-end' : 'flex-start'};
+    align-items: center;
+    text-align: ${(props) => props.$customAlign || 'left'};
+    box-sizing: border-box;
+    ${(props) => props.$tableSize === 'small' && `
+      padding: 1px 8px;
+      font-size: ${props.$defaultThemeDetail.textSize == props.$style.textSize ? '14px !important' : props.$style.textSize + ' !important'};
+      font-style:${props.$style.fontStyle} !important;
+      min-height: ${props.$style.rowHeight || '14px'};
+      line-height: 20px;
+      ${!props.$autoHeight && `
+        overflow-y: auto;
+        max-height: ${props.$style.rowHeight || '28px'};
+      `};
+    `};
+    ${(props) => props.$tableSize === 'middle' && `
+      padding: 8px 8px;
+      font-size: ${props.$defaultThemeDetail.textSize == props.$style.textSize ? '16px !important' : props.$style.textSize + ' !important'};
+      font-style:${props.$style.fontStyle} !important;
+      min-height: ${props.$style.rowHeight || '24px'};
+      line-height: 24px;
+      ${!props.$autoHeight && `
+        overflow-y: auto;
+        max-height: ${props.$style.rowHeight || '48px'};
+      `};
+    `};
+    ${(props) => props.$tableSize === 'large' && `
+      padding: 16px 16px;
+      font-size: ${props.$defaultThemeDetail.textSize == props.$style.textSize ? '18px !important' : props.$style.textSize + ' !important'};
+      font-style:${props.$style.fontStyle} !important;
+      min-height: ${props.$style.rowHeight || '48px'};
+      ${!props.$autoHeight && `
+        overflow-y: auto;
+        max-height: ${props.$style.rowHeight || '96px'};
+      `};
+    `};
+    
+    > .ant-badge > .ant-badge-status-text,
+    > div > .markdown-body {
+      color: ${(props) => props.$style.text};
+    }
+
+    > div > svg g {
+      stroke: ${(props) => props.$style.text};
+    }
+
+    // dark link|links color
+    > a,
+    > div  a {
+      color: ${(props) => props.$linkStyle?.text};
+
+      &:hover {
+        color: ${(props) => props.$linkStyle?.hoverText};
+      }
+
+      &:active {
+        color: ${(props) => props.$linkStyle?.activeText};
+      }
+    }
+  }
+`;
+
+const TableTdLoading = styled(Skeleton.Button)<SkeletonButtonProps & {
+  $tableSize?: string;
+}>`
+  width: 90% !important;
+  display: table !important;
+
+  .ant-skeleton-button {
+    min-width: auto !important;
+    display: block !important;
+    ${(props) => props.$tableSize === 'small' && `
+      height: 20px !important;
+    `}
+    ${(props) => props.$tableSize === 'middle' && `
+      height: 24px !important;
+    `}
+    ${(props) => props.$tableSize === 'large' && `
+      height: 28px !important;
+    `}
+  }
+`;
+
+const ResizeableTitle = (props: any) => {
+  const { onResize, onResizeStop, width, viewModeResizable, ...restProps } = props;
+  const [childWidth, setChildWidth] = useState(0);
+  const resizeRef = useRef<HTMLDivElement>(null);
+  const isUserViewMode = useUserViewMode();
+
+  const updateChildWidth = useCallback(() => {
+    if (resizeRef.current) {
+      const width = resizeRef.current.getBoundingClientRect().width;
+      setChildWidth(width);
+    }
+  }, []);
+
+  useEffect(() => {
+    updateChildWidth();
+    const resizeObserver = new ResizeObserver(() => {
+      updateChildWidth();
+    });
+
+    if (resizeRef.current) {
+      resizeObserver.observe(resizeRef.current);
+    }
+
+    return () => {
+      resizeObserver.disconnect();
+    };
+  }, [updateChildWidth]);
+
+  const isNotDataColumn = _.isNil(restProps.title);
+  if ((isUserViewMode && !restProps.viewModeResizable) || isNotDataColumn) {
+    return <TableTh ref={resizeRef} {...restProps} width={width} />;
+  }
+
+  return (
+    <Resizable
+      width={width > 0 ? width : childWidth}
+      height={0}
+      onResize={(e: React.SyntheticEvent, { size }: { size: { width: number } }) => {
+        e.stopPropagation();
+        onResize(size.width);
+      }}
+      onResizeStart={(e: React.SyntheticEvent) => {
+        updateChildWidth();
+        e.stopPropagation();
+        e.preventDefault();
+      }}
+      onResizeStop={onResizeStop}
+      draggableOpts={{ enableUserSelectHack: false }}
+      handle={(axis: ResizeHandleAxis, ref: ReactRef<HTMLDivElement>) => (
+        <TitleResizeHandle
+          ref={ref}
+          onClick={(e) => {
+            e.preventDefault();
+            e.stopPropagation();
+          }}
+        />
+      )}
+    >
+      <TableTh ref={resizeRef} {...restProps} title="" />
+    </Resizable>
+  );
+};
+
+type CustomTableProps<RecordType> = Omit<TableProps<RecordType>, "components" | "columns"> & {
+  columns: CustomColumnType<RecordType>[];
+  viewModeResizable: boolean;
+  visibleResizables: boolean;
+  rowColorFn: RowColorViewType;
+  rowHeightFn: RowHeightViewType;
+  columnsStyle: TableColumnStyleType;
+  size?: string;
+  rowAutoHeight?: boolean;
+  customLoading?: boolean;
+  onCellClick: (columnName: string, dataIndex: string) => void;
+};
+
+const TableCellView = React.memo((props: {
+  record: RecordType;
+  title: string;
+  rowColorFn: RowColorViewType;
+  rowHeightFn: RowHeightViewType;
+  cellColorFn: CellColorViewType;
+  rowIndex: number;
+  children: any;
+  columnsStyle: TableColumnStyleType;
+  columnStyle: TableColumnStyleType;
+  linkStyle: TableColumnLinkStyleType;
+  tableSize?: string;
+  autoHeight?: boolean;
+  loading?: boolean;
+  customAlign?: 'left' | 'center' | 'right';
+}) => {
+  const {
+    record,
+    title,
+    rowIndex,
+    rowColorFn,
+    rowHeightFn,
+    cellColorFn,
+    children,
+    columnsStyle,
+    columnStyle,
+    linkStyle,
+    tableSize,
+    autoHeight,
+    loading,
+    customAlign,
+    ...restProps
+  } = props;
+
+  const [editing, setEditing] = useState(false);
+  const rowContext = useContext(TableRowContext);
+  
+  // Memoize style calculations
+  const style = useMemo(() => {
+    if (!record) return null;
+    const rowColor = rowColorFn({
+      currentRow: record,
+      currentIndex: rowIndex,
+      currentOriginalIndex: record[OB_ROW_ORI_INDEX],
+      columnTitle: title,
+    });
+    const rowHeight = rowHeightFn({
+      currentRow: record,
+      currentIndex: rowIndex,
+      currentOriginalIndex: record[OB_ROW_ORI_INDEX],
+      columnTitle: title,
+    });
+    const cellColor = cellColorFn({
+      currentCell: record[title],
+      currentRow: record,
+    });
+
+    return {
+      background: cellColor || rowColor || columnStyle.background || columnsStyle.background,
+      margin: columnStyle.margin || columnsStyle.margin,
+      text: columnStyle.text || columnsStyle.text,
+      border: columnStyle.border || columnsStyle.border,
+      radius: columnStyle.radius || columnsStyle.radius,
+      // borderWidth: columnStyle.borderWidth || columnsStyle.borderWidth,
+      textSize: columnStyle.textSize || columnsStyle.textSize,
+      textWeight: columnsStyle.textWeight || columnStyle.textWeight,
+      fontFamily: columnsStyle.fontFamily || columnStyle.fontFamily,
+      fontStyle: columnsStyle.fontStyle || columnStyle.fontStyle,
+      rowHeight: rowHeight,
+    };
+  }, [record, rowIndex, title, rowColorFn, rowHeightFn, cellColorFn, columnStyle, columnsStyle]);
+
+  let tdView;
+  if (!record) {
+    tdView = <td {...restProps}>{children}</td>;
+  } else {
+    let { background } = style!;
+    if (rowContext.hover) {
+      background = 'transparent';
+    }
+
+    tdView = (
+      <TableTd
+        {...restProps}
+        $background={background}
+        $style={style!}
+        $defaultThemeDetail={defaultTheme}
+        $linkStyle={linkStyle}
+        $isEditing={editing}
+        $tableSize={tableSize}
+        $autoHeight={autoHeight}
+        $customAlign={customAlign}
+      >
+        {loading
+          ? <TableTdLoading block active $tableSize={tableSize} />
+          : children
+        }
+      </TableTd>
+    );
+  }
+
+  return (
+    <TableCellContext.Provider value={{ isEditing: editing, setIsEditing: setEditing }}>
+      {tdView}
+    </TableCellContext.Provider>
+  );
+});
+
+const TableRowView = React.memo((props: any) => {
+  const [hover, setHover] = useState(false);
+  const [selected, setSelected] = useState(false);
+
+  // Memoize event handlers
+  const handleMouseEnter = useCallback(() => setHover(true), []);
+  const handleMouseLeave = useCallback(() => setHover(false), []);
+  const handleFocus = useCallback(() => setSelected(true), []);
+  const handleBlur = useCallback(() => setSelected(false), []);
+
+  return (
+    <TableRowContext.Provider value={{ hover, selected }}>
+      <tr
+        {...props}
+        tabIndex={-1}
+        onMouseEnter={handleMouseEnter}
+        onMouseLeave={handleMouseLeave}
+        onFocus={handleFocus}
+        onBlur={handleBlur}
+      />
+    </TableRowContext.Provider>
+  );
+});
+
+/**
+ * A table with adjustable column width, width less than 0 means auto column width
+ */
+function ResizeableTableComp<RecordType extends object>(props: CustomTableProps<RecordType>) {
+  const {
+    columns,
+    viewModeResizable,
+    visibleResizables,
+    rowColorFn,
+    rowHeightFn,
+    columnsStyle,
+    size,
+    rowAutoHeight,
+    customLoading,
+    onCellClick,
+    ...restProps
+  } = props;
+  const [resizeData, setResizeData] = useState({ index: -1, width: -1 });
+
+  // Memoize resize handlers
+  const handleResize = useCallback((width: number, index: number) => {
+    setResizeData({ index, width });
+  }, []);
+
+  const handleResizeStop = useCallback((width: number, index: number, onWidthResize?: (width: number) => void) => {
+    setResizeData({ index: -1, width: -1 });
+    if (onWidthResize) {
+      onWidthResize(width);
+    }
+  }, []);
+
+  // Memoize cell handlers
+  const createCellHandler = useCallback((col: CustomColumnType<RecordType>) => {
+    return (record: RecordType, index: number) => ({
+      record,
+      title: String(col.dataIndex),
+      rowColorFn,
+      rowHeightFn,
+      cellColorFn: col.cellColorFn,
+      rowIndex: index,
+      columnsStyle,
+      columnStyle: col.style,
+      linkStyle: col.linkStyle,
+      tableSize: size,
+      autoHeight: rowAutoHeight,
+      onClick: () => onCellClick(col.titleText, String(col.dataIndex)),
+      loading: customLoading,
+      customAlign: col.align,
+    });
+  }, [rowColorFn, rowHeightFn, columnsStyle, size, rowAutoHeight, onCellClick, customLoading]);
+
+  // Memoize header cell handlers
+  const createHeaderCellHandler = useCallback((col: CustomColumnType<RecordType>, index: number, resizeWidth: number) => {
+    return () => ({
+      width: resizeWidth,
+      title: col.titleText,
+      viewModeResizable,
+      onResize: (width: React.SyntheticEvent) => {
+        if (width) {
+          handleResize(Number(width), index);
+        }
+      },
+      onResizeStop: (e: React.SyntheticEvent, { size }: { size: { width: number } }) => {
+        handleResizeStop(size.width, index, col.onWidthResize);
+      },
+    });
+  }, [viewModeResizable, handleResize, handleResizeStop]);
+
+  // Memoize columns to prevent unnecessary re-renders
+  const memoizedColumns = useMemo(() => {
+    return columns.map((col, index) => {
+      const { width, style, linkStyle, cellColorFn, onWidthResize, ...restCol } = col;
+      const resizeWidth = (resizeData.index === index ? resizeData.width : col.width) ?? 0;
+      
+      const column: ColumnType<RecordType> = {
+        ...restCol,
+        width: typeof resizeWidth === "number" && resizeWidth > 0 ? resizeWidth : undefined,
+        minWidth: typeof resizeWidth === "number" && resizeWidth > 0 ? undefined : COL_MIN_WIDTH,
+        onCell: (record: RecordType, index?: number) => createCellHandler(col)(record, index ?? 0),
+        onHeaderCell: () => createHeaderCellHandler(col, index, Number(resizeWidth))(),
+      };
+      return column;
+    });
+  }, [columns, resizeData, createCellHandler, createHeaderCellHandler]);
+
+  return (
+    <Table<RecordType>
+      components={{
+        header: {
+          cell: ResizeableTitle,
+        },
+        body: {
+          cell: TableCellView,
+          row: TableRowView,
+        },
+      }}
+      {...restProps}
+      pagination={false}
+      columns={memoizedColumns}
+      scroll={{
+        x: COL_MIN_WIDTH * columns.length,
+      }}
+    />
+  );
+}
+ResizeableTableComp.whyDidYouRender = true;
+
+const ResizeableTable = React.memo(ResizeableTableComp) as typeof ResizeableTableComp;
+
+
+const createNewEmptyRow = (
+  rowIndex: number,
+  columnsAggrData: ColumnsAggrData,
+) => {
+  const emptyRowData: RecordType = {
+    [OB_ROW_ORI_INDEX]: `${EMPTY_ROW_KEY}_${rowIndex}`,
+  };
+  Object.keys(columnsAggrData).forEach(columnKey => {
+    emptyRowData[columnKey] = '';
+  });
+  return emptyRowData;
+}
+
+export const TableCompView = React.memo((props: {
+  comp: InstanceType<typeof TableImplComp>;
+  onRefresh: (allQueryNames: Array<string>, setLoading: (loading: boolean) => void) => void;
+  onDownload: (fileName: string) => void;
+}) => {
+  const [expandedRowKeys, setExpandedRowKeys] = useState<string[]>([]);
+  const [emptyRowsMap, setEmptyRowsMap] = useState<Record<string, RecordType>>({});
+  const editorState = useContext(EditorContext);
+  const currentTheme = useContext(ThemeContext)?.theme;
+  const showDataLoadingIndicators = currentTheme?.showDataLoadingIndicators;
+  const { width, ref } = useResizeDetector({
+    refreshMode: "debounce",
+    refreshRate: 600,
+    handleHeight: false,
+  });
+  const viewMode = useUserViewMode();
+  const compName = useContext(CompNameContext);
+  const [loading, setLoading] = useState(false);
+  const { comp, onDownload, onRefresh } = props;
+  const compChildren = comp.children;
+  const style = compChildren.style.getView();
+  const rowStyle = compChildren.rowStyle.getView();
+  const headerStyle = compChildren.headerStyle.getView();
+  const toolbarStyle = compChildren.toolbarStyle.getView();
+  const hideToolbar = compChildren.hideToolbar.getView()
+  const rowAutoHeight = compChildren.rowAutoHeight.getView();
+  const tableAutoHeight = comp.getTableAutoHeight();
+  const showHorizontalScrollbar = compChildren.showHorizontalScrollbar.getView();
+  const showVerticalScrollbar = compChildren.showVerticalScrollbar.getView();
+  const visibleResizables = compChildren.visibleResizables.getView();
+  const showHRowGridBorder = compChildren.showHRowGridBorder.getView();
+  const columnsStyle = compChildren.columnsStyle.getView();
+  const summaryRowStyle = compChildren.summaryRowStyle.getView();
+  const changeSet = useMemo(() => compChildren.columns.getChangeSet(), [compChildren.columns]);
+  const insertSet = useMemo(() => compChildren.columns.getChangeSet(true), [compChildren.columns]);
+  const hasChange = useMemo(() => !_.isEmpty(changeSet) || !_.isEmpty(insertSet), [changeSet, insertSet]);
+  const columns = useMemo(() => compChildren.columns.getView(), [compChildren.columns]);
+  const columnViews = useMemo(() => columns.map((c) => c.getView()), [columns]);
+  const data = comp.filterData;
+  const sort = useMemo(() => compChildren.sort.getView(), [compChildren.sort]);
+  const toolbar = useMemo(() => compChildren.toolbar.getView(), [compChildren.toolbar]);
+  const showSummary = useMemo(() => compChildren.showSummary.getView(), [compChildren.showSummary]);
+  const summaryRows = useMemo(() => compChildren.summaryRows.getView(), [compChildren.summaryRows]);
+  const inlineAddNewRow = useMemo(() => compChildren.inlineAddNewRow.getView(), [compChildren.inlineAddNewRow]);
+  const pagination = useMemo(() => compChildren.pagination.getView(), [compChildren.pagination]);
+  const size = useMemo(() => compChildren.size.getView(), [compChildren.size]);
+  const editModeClicks = useMemo(() => compChildren.editModeClicks.getView(), [compChildren.editModeClicks]);
+  const onEvent = useMemo(() => compChildren.onEvent.getView(), [compChildren.onEvent]);
+  const currentExpandedRows = useMemo(() => compChildren.currentExpandedRows.getView(), [compChildren.currentExpandedRows]);
+  const dynamicColumn = compChildren.dynamicColumn.getView();
+  const dynamicColumnConfig = useMemo(
+    () => compChildren.dynamicColumnConfig.getView(),
+    [compChildren.dynamicColumnConfig]
+  );
+  const columnsAggrData = comp.columnAggrData;
+  const expansion = useMemo(() => compChildren.expansion.getView(), [compChildren.expansion]);
+  const antdColumns = useMemo(
+    () =>
+      columnsToAntdFormat(
+        columnViews,
+        sort,
+        toolbar.columnSetting,
+        size,
+        dynamicColumn,
+        dynamicColumnConfig,
+        columnsAggrData,
+        editModeClicks,
+        onEvent,
+      ),
+    [
+      columnViews,
+      sort,
+      toolbar.columnSetting,
+      size,
+      dynamicColumn,
+      dynamicColumnConfig,
+      columnsAggrData,
+      editModeClicks,
+    ]
+  );
+
+  const supportChildren = useMemo(
+    () => supportChildrenTree(compChildren.data.getView()),
+    [compChildren.data]
+  );
+
+  const updateEmptyRows = useCallback(() => {
+    if (!inlineAddNewRow) {
+      setEmptyRowsMap({})
+      setTimeout(() => compChildren.columns.dispatchClearInsertSet());
+      return;
+    }
+
+    let emptyRows: Record<string, RecordType> = {...emptyRowsMap};
+    const existingRowsKeys = Object.keys(emptyRows);
+    const existingRowsCount = existingRowsKeys.length;
+    const updatedRowsKeys = Object.keys(insertSet).filter(
+      key => key.startsWith(EMPTY_ROW_KEY)
+    );
+    const updatedRowsCount = updatedRowsKeys.length;
+    const removedRowsKeys = existingRowsKeys.filter(
+      x => !updatedRowsKeys.includes(x)
+    );
+
+    if (removedRowsKeys.length === existingRowsCount) {
+      const newRowIndex = 0;
+      const newRowKey = `${EMPTY_ROW_KEY}_${newRowIndex}`;
+      setEmptyRowsMap({
+        [newRowKey]: createNewEmptyRow(newRowIndex, columnsAggrData)
+      });
+      const ele = document.querySelector<HTMLElement>(`[data-row-key=${newRowKey}]`);
+      if (ele) {
+        ele.style.display = '';
+      }
+      return;
+    }
+
+    removedRowsKeys.forEach(rowKey => {
+      if (
+        rowKey === existingRowsKeys[existingRowsCount - 1]
+        || rowKey === existingRowsKeys[existingRowsCount - 2]
+      ) {
+        delete emptyRows[rowKey];
+      } else {
+        const ele = document.querySelector<HTMLElement>(`[data-row-key=${rowKey}]`);
+        if (ele) {
+          ele.style.display = 'none';
+        }
+      }
+    })
+    const lastRowKey = updatedRowsCount ? updatedRowsKeys[updatedRowsCount - 1] : '';
+    const lastRowIndex = lastRowKey ? parseInt(lastRowKey.replace(`${EMPTY_ROW_KEY}_`, '')) : -1;
+
+    const newRowIndex = lastRowIndex + 1;
+    const newRowKey = `${EMPTY_ROW_KEY}_${newRowIndex}`;
+    emptyRows[newRowKey] = createNewEmptyRow(newRowIndex, columnsAggrData);
+    setEmptyRowsMap(emptyRows);
+  }, [
+    inlineAddNewRow,
+    JSON.stringify(insertSet),
+    setEmptyRowsMap,
+    createNewEmptyRow,
+  ]);
+
+  useEffect(() => {
+    updateEmptyRows();
+  }, [updateEmptyRows]);
+
+  useUpdateEffect(() => {
+    if (!isEqual(currentExpandedRows, expandedRowKeys)) {
+      compChildren.currentExpandedRows.dispatchChangeValueAction(expandedRowKeys);
+    }
+  }, [expandedRowKeys]);
+
+  useUpdateEffect(() => {
+    if (!isEqual(currentExpandedRows, expandedRowKeys)) {
+      setExpandedRowKeys(currentExpandedRows);
+    }
+  }, [currentExpandedRows]);
+
+  const pageDataInfo = useMemo(() => {
+    // Data pagination
+    let pagedData = data;
+    let current = pagination.current;
+    const total = pagination.total || data.length;
+    if (data.length > pagination.pageSize) {
+      // Local pagination
+      let offset = (current - 1) * pagination.pageSize;
+      if (offset >= total) {
+        current = 1;
+        offset = 0;
+      }
+      pagedData = pagedData.slice(offset, offset + pagination.pageSize);
+    }
+
+    return {
+      total: total,
+      current: current,
+      data: pagedData,
+    };
+  }, [pagination, data]);
+
+  const childrenProps = childrenToProps(comp.children);
+
+  useMergeCompStyles(
+    childrenProps as Record<string, any>,
+    comp.dispatch
+  );
+
+  const handleChangeEvent = useCallback(
+    (eventName: TableEventOptionValues) => {
+      if (eventName === "saveChanges" && !compChildren.onEvent.isBind(eventName)) {
+        !viewMode && messageInstance.warning(trans("table.saveChangesNotBind"));
+        return;
+      }
+      compChildren.onEvent.getView()(eventName);
+      setTimeout(() => compChildren.columns.dispatchClearChangeSet());
+    },
+    [viewMode, compChildren.onEvent, compChildren.columns]
+  );
+
+  const toolbarView = !hideToolbar && (
+    <TableToolbar
+      toolbar={toolbar}
+      $style={toolbarStyle}
+      pagination={{
+        ...pagination,
+        total: pageDataInfo.total,
+        current: pageDataInfo.current,
+      }}
+      columns={columns}
+      onRefresh={() =>
+        onRefresh(
+          editorState.queryCompInfoList().map((info) => info.name),
+          setLoading
+        )
+      }
+      onDownload={() => {
+        handleChangeEvent("download");
+        onDownload(`${compName}-data`)
+      }}
+      hasChange={hasChange}
+      onSaveChanges={() => handleChangeEvent("saveChanges")}
+      onCancelChanges={() => {
+        handleChangeEvent("cancelChanges");
+        if (inlineAddNewRow) {
+          setEmptyRowsMap({});
+        }
+      }}
+      onEvent={onEvent}
+    />
+  );
+
+  const summaryView = () => {
+    if (!showSummary) return undefined;
+    return (
+      <TableSummary
+        tableSize={size}
+        istoolbarPositionBelow={toolbar.position === "below"}
+        multiSelectEnabled={compChildren.selection.children.mode.value === 'multiple'}
+        expandableRows={Boolean(expansion.expandModalView)}
+        summaryRows={parseInt(summaryRows)}
+        columns={columns}
+        summaryRowStyle={summaryRowStyle}
+        dynamicColumn={dynamicColumn}
+        dynamicColumnConfig={dynamicColumnConfig}
+      />
+    );
+  }
+
+  if (antdColumns.length === 0) {
+    return (
+      <div>
+        {toolbar.position === "above" && !hideToolbar && toolbarView}
+        <EmptyContent text={trans("table.emptyColumns")} />
+        {toolbar.position === "below" && !hideToolbar && toolbarView}
+      </div>
+    );
+  }
+
+  const hideScrollbar = !showHorizontalScrollbar && !showVerticalScrollbar;
+  const showTableLoading = loading ||
+    // fixme isLoading type
+    ((showDataLoadingIndicators) &&
+      (compChildren.data as any).isLoading()) ||
+    compChildren.loading.getView();
+
+  return (
+    <BackgroundColorContext.Provider value={style.background} >
+      <BackgroundWrapper
+        ref={ref}
+        $style={style}
+        $tableAutoHeight={tableAutoHeight}
+        $showHorizontalScrollbar={showHorizontalScrollbar}
+        $showVerticalScrollbar={showVerticalScrollbar}
+        $fixedToolbar={toolbar.fixedToolbar}
+      >
+        {toolbar.position === "above" && !hideToolbar && (toolbar.fixedToolbar || (tableAutoHeight && showHorizontalScrollbar)) && toolbarView}
+        <ScrollBar
+          className="table-scrollbar-wrapper"
+          style={{ height: "100%", margin: "0px", padding: "0px" }}
+          hideScrollbar={hideScrollbar}
+          prefixNode={toolbar.position === "above" && !toolbar.fixedToolbar && !(tableAutoHeight && showHorizontalScrollbar) && toolbarView}
+          suffixNode={toolbar.position === "below" && !toolbar.fixedToolbar && !(tableAutoHeight && showHorizontalScrollbar) && toolbarView}
+        >
+          <TableWrapper
+            $style={style}
+            $rowStyle={rowStyle}
+            $headerStyle={headerStyle}
+            $toolbarStyle={toolbarStyle}
+            $toolbarPosition={toolbar.position}
+            $fixedHeader={compChildren.fixedHeader.getView()}
+            $fixedToolbar={toolbar.fixedToolbar && toolbar.position === 'above'}
+            $visibleResizables={visibleResizables}
+            $showHRowGridBorder={showHRowGridBorder}
+          >
+            <ResizeableTable<RecordType>
+              expandable={{
+                ...expansion.expandableConfig,
+                childrenColumnName: supportChildren
+                  ? COLUMN_CHILDREN_KEY
+                  : "OB_CHILDREN_KEY_PLACEHOLDER",
+                fixed: "left",
+                onExpand: (expanded) => {
+                  if (expanded) {
+                    handleChangeEvent('rowExpand')
+                  } else {
+                    handleChangeEvent('rowShrink')
+                  }
+                },
+                onExpandedRowsChange: (expandedRowKeys) => {
+                  setExpandedRowKeys(expandedRowKeys as unknown as string[]);
+                },
+                expandedRowKeys: expandedRowKeys,
+              }}
+              // rowKey={OB_ROW_ORI_INDEX}
+              rowColorFn={compChildren.rowColor.getView() as any}
+              rowHeightFn={compChildren.rowHeight.getView() as any}
+              {...compChildren.selection.getView()(onEvent)}
+              bordered={compChildren.showRowGridBorder.getView()}
+              onChange={(pagination, filters, sorter, extra) => {
+                onTableChange(pagination, filters, sorter, extra, comp.dispatch, onEvent);
+              }}
+              showHeader={!compChildren.hideHeader.getView()}
+              columns={antdColumns}
+              columnsStyle={columnsStyle}
+              viewModeResizable={compChildren.viewModeResizable.getView()}
+              visibleResizables={compChildren.visibleResizables.getView()}
+              dataSource={pageDataInfo.data.concat(Object.values(emptyRowsMap))}
+              size={compChildren.size.getView()}
+              rowAutoHeight={rowAutoHeight}
+              tableLayout="fixed"
+              customLoading={showTableLoading}
+              onCellClick={(columnName: string, dataIndex: string) => {
+                comp.children.selectedCell.dispatchChangeValueAction({
+                  name: columnName,
+                  dataIndex: dataIndex,
+                });
+              }}
+              summary={summaryView}
+            />
+            <SlotConfigContext.Provider value={{ modalWidth: width && Math.max(width, 300) }}>
+              {expansion.expandModalView}
+            </SlotConfigContext.Provider>
+          </TableWrapper>
+        </ScrollBar>
+        {toolbar.position === "below" && !hideToolbar && (toolbar.fixedToolbar || (tableAutoHeight && showHorizontalScrollbar)) && toolbarView}
+      </BackgroundWrapper>
+
+    </BackgroundColorContext.Provider>
+  );
+});
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableContext.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableContext.tsx
new file mode 100644
index 0000000000..47067a9e03
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableContext.tsx
@@ -0,0 +1,12 @@
+import React from "react";
+import _ from "lodash";
+
+export const TableRowContext = React.createContext<{
+  hover: boolean;
+  selected: boolean;
+}>({ hover: false, selected: false });
+
+export const TableCellContext = React.createContext<{
+  isEditing: boolean;
+  setIsEditing: (e: boolean) => void;
+}>({ isEditing: false, setIsEditing: _.noop });
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableDynamicColumn.test.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableDynamicColumn.test.tsx
new file mode 100644
index 0000000000..d38cdb9877
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableDynamicColumn.test.tsx
@@ -0,0 +1,241 @@
+import { TableComp } from "comps/comps/tableComp/tableComp";
+import { columnsToAntdFormat } from "comps/comps/tableComp/tableUtils";
+import { evalAndReduce } from "comps/utils";
+import { reduceInContext } from "comps/utils/reduceContext";
+import _ from "lodash";
+import { changeChildAction, fromValue, SimpleNode } from "lowcoder-core";
+import { JSONObject } from "util/jsonTypes";
+
+const expectColumn = (
+  comp: InstanceType<typeof TableComp>,
+  expectValues: Array<{
+    dataIndex: string;
+    title: string;
+    hide?: boolean;
+    isCustom?: boolean;
+  }>
+) => {
+  const columns = comp.children.columns.getView();
+  const columnViews = columns.map((c) => c.getView());
+  expect(expectValues.length).toEqual(columnViews.length);
+  expectValues.forEach((val) => {
+    const column = columnViews.find((c) => c.dataIndex === val.dataIndex);
+    if (!column) {
+      throw new Error(`expect column: ${JSON.stringify(val)}, but not found.`);
+    }
+    Object.keys(val).forEach((key) => {
+      const colVal = (column as any)[key];
+      const expectVal = (val as any)[key];
+      if (expectVal !== undefined) {
+        if (!_.isEqual(colVal, expectVal)) {
+          throw new Error(`ColumnKey:${key}, expect: "${expectVal}", but found: "${colVal}"`);
+        }
+      }
+    });
+  });
+  // with dynamic config
+  const dynamicColumnConfig = comp.children.dynamicColumnConfig.getView();
+  if (dynamicColumnConfig?.length > 0) {
+    const onEvent = (eventName: any) => {};
+    const antdColumns = columnsToAntdFormat(
+      columnViews,
+      comp.children.sort.getView(),
+      comp.children.toolbar.getView().columnSetting,
+      comp.children.size.getView(),
+      comp.children.dynamicColumn.getView(),
+      dynamicColumnConfig,
+      comp.columnAggrData,
+      comp.children.editModeClicks.getView(),
+      onEvent,
+    );
+    expect(columnViews.length).toBeGreaterThanOrEqual(antdColumns.length);
+    antdColumns.forEach((column) => {
+      const dataIndex = (column as any).dataIndex;
+      const colView = columnViews.find((c) => c.dataIndex === dataIndex);
+      if (!colView) {
+        throw new Error(`Error, column should not be undefined, column: ${JSON.stringify(column)}`);
+      }
+      const configName = colView.isCustom ? colView.title : colView.dataIndex;
+      if (!dynamicColumnConfig.includes(configName)) {
+        throw new Error(`dynamic config test fail: unexpect column: ${configName}`);
+      }
+    });
+  }
+};
+
+function getTableInitData() {
+  const exposingInfo: Record<string, SimpleNode<any>> = {
+    query1: fromValue({ data: [{ q1: 1 }] }),
+    query2: fromValue({ data: [{ q2: 2 }] }),
+  };
+  return {
+    tableData: {
+      data: JSON.stringify([{ a: 1 }]),
+      columns: [
+        {
+          dataIndex: "a",
+          title: "a",
+          hide: true,
+        },
+        {
+          title: "custom",
+          dataIndex: "custom1",
+          isCustom: true,
+        },
+      ],
+    },
+    exposingInfo: exposingInfo,
+    initColumns: [
+      {
+        dataIndex: "a",
+        hide: true,
+        title: "a",
+      },
+      {
+        dataIndex: "custom1",
+        hide: false,
+        title: "custom",
+        isCustom: true,
+      },
+    ],
+  };
+}
+
+async function sleep() {
+  await new Promise((r) => setTimeout(r, 20));
+}
+
+test("test table dynamic columns: Change unEvalValue", async () => {
+  // 0. Init check
+  const { initColumns, tableData, exposingInfo } = getTableInitData();
+  let comp = new TableComp({
+    dispatch: (action) => {
+      comp = evalAndReduce(comp.reduce(action), exposingInfo);
+    },
+    value: tableData,
+  });
+  comp = evalAndReduce(comp);
+  expectColumn(comp, initColumns);
+  /** 1. Change unEvalValue data, change column whatever **/
+  // 1.1 add column c & d
+  comp = evalAndReduce(
+    comp.reduce(comp.changeChildAction("data", JSON.stringify([{ a: 1, c: 2, d: 3 }])))
+  );
+  await sleep();
+  const columnsAfterAdd = [
+    ...initColumns,
+    {
+      dataIndex: "c",
+      hide: false,
+      title: "c",
+    },
+    {
+      dataIndex: "d",
+      title: "d",
+    },
+  ];
+  expectColumn(comp, columnsAfterAdd);
+  // 1.2 del column a
+  comp = evalAndReduce(
+    comp.reduce(comp.changeChildAction("data", JSON.stringify([{ c: 2, d: 3 }])))
+  );
+  await sleep();
+  expectColumn(
+    comp,
+    columnsAfterAdd.filter((c) => c.dataIndex !== "a")
+  );
+}, 1000);
+
+async function dynamicColumnsTest(
+  dynamicColumn: boolean,
+  isViewMode: boolean,
+  dynamicConfigs?: Array<string>
+) {
+  const { initColumns, tableData, exposingInfo } = getTableInitData();
+  // init comp
+  let comp = new TableComp({
+    dispatch: (action) => {
+      let tmpComp;
+      if (isViewMode) {
+        tmpComp = reduceInContext({ readOnly: isViewMode }, () => comp.reduce(action));
+      } else {
+        tmpComp = comp.reduce(action);
+      }
+      comp = evalAndReduce(tmpComp, exposingInfo);
+    },
+    value: {
+      ...tableData,
+      dynamicColumn: dynamicColumn,
+      ...(dynamicColumn &&
+        dynamicConfigs && { dynamicColumnConfig: JSON.stringify(dynamicConfigs) }),
+    },
+  });
+  comp = evalAndReduce(comp);
+
+  const updateTableComp = async () => {
+    comp = evalAndReduce(
+      comp.reduce(comp.changeChildAction("data", "{{query1.data}}")),
+      exposingInfo
+    );
+    await sleep();
+  };
+  // change data to query1
+  const query1Columns = [
+    {
+      dataIndex: "q1",
+      title: "q1",
+    },
+    {
+      dataIndex: "custom1",
+      title: "custom",
+      isCustom: true,
+    },
+  ];
+  await updateTableComp();
+  if (!dynamicColumn && isViewMode) {
+    expectColumn(comp, initColumns);
+  } else {
+    expectColumn(comp, query1Columns);
+  }
+  // change query data, add column: a
+  const addData: Array<JSONObject> = [{ q1: 1, a: 2 }];
+  exposingInfo.query1 = fromValue({ data: addData });
+  await updateTableComp();
+  const columnsAfterAdd = [
+    ...query1Columns,
+    {
+      dataIndex: "a",
+      title: "a",
+    },
+  ];
+  expect(comp.children.data.getView()).toEqual(addData);
+  if (!dynamicColumn && isViewMode) {
+    expectColumn(comp, initColumns);
+  } else {
+    expectColumn(comp, columnsAfterAdd);
+  }
+  // change query data, del column: q1
+  const delData = [{ a: 2 }];
+  exposingInfo.query1 = fromValue({ data: delData });
+  await updateTableComp();
+  expect(comp.children.data.getView()).toEqual(delData);
+  if (dynamicColumn) {
+    expectColumn(
+      comp,
+      columnsAfterAdd.filter((c) => c.dataIndex !== "q1")
+    );
+  } else if (isViewMode) {
+    expectColumn(comp, initColumns);
+  } else {
+    expectColumn(comp, columnsAfterAdd);
+  }
+}
+
+test("test table dynamic columns", async () => {
+  await dynamicColumnsTest(false, false);
+  await dynamicColumnsTest(false, true);
+  await dynamicColumnsTest(true, false);
+  await dynamicColumnsTest(true, true);
+  await dynamicColumnsTest(true, false, ["custom", "q1"]);
+  await dynamicColumnsTest(true, true, ["custom", "q1"]);
+}, 2000);
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tablePropertyView.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tablePropertyView.tsx
new file mode 100644
index 0000000000..70bb8ce05a
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tablePropertyView.tsx
@@ -0,0 +1,650 @@
+import {
+  ColumnCompType,
+  newCustomColumn,
+  RawColumnType,
+} from "comps/comps/tableComp/column/tableColumnComp";
+import { hiddenPropertyView, loadingPropertyView } from "comps/utils/propertyUtils";
+import { trans } from "i18n";
+import { changeValueAction, deferAction, MultiBaseComp, wrapChildAction } from "lowcoder-core";
+import {
+  BluePlusIcon,
+  CheckBox,
+  CloseEyeIcon,
+  controlItem,
+  CustomModal,
+  Dropdown,
+  labelCss,
+  LinkButton,
+  OpenEyeIcon,
+  Option,
+  OptionItem,
+  RedButton,
+  RefreshIcon,
+  Section,
+  sectionNames,
+  TextLabel,
+  ToolTipLabel,
+} from "lowcoder-design";
+import { tableDataDivClassName } from "pages/tutorials/tutorialsConstant";
+import styled, { css } from "styled-components";
+import { getSelectedRowKeys } from "./selectionControl";
+import { TableChildrenType } from "./tableTypes";
+import React, { useMemo, useState, useCallback } from "react";
+import { GreyTextColor } from "constants/style";
+import { alignOptions } from "comps/controls/dropdownControl";
+import { ColumnTypeCompMap } from "comps/comps/tableComp/column/columnTypeComp";
+import Segmented from "antd/es/segmented";
+import { CheckboxChangeEvent } from "antd/es/checkbox";
+
+const InsertDiv = styled.div`
+  display: flex;
+  justify-content: end;
+  width: 100%;
+  gap: 8px;
+  align-items: center;
+`;
+const Graylabel = styled.span`
+  ${labelCss};
+  color: #8b8fa3;
+`;
+
+const StyledRefreshIcon = styled(RefreshIcon)`
+  width: 16px;
+  height: 16px;
+  cursor: pointer;
+
+  &:hover {
+    g g {
+      stroke: #4965f2;
+    }
+  }
+`;
+
+const eyeIconCss = css`
+  height: 16px;
+  width: 16px;
+  display: inline-block;
+
+  &:hover {
+    cursor: pointer;
+  }
+
+  &:hover path {
+    fill: #315efb;
+  }
+`;
+
+const CloseEye = styled(CloseEyeIcon)`
+  ${eyeIconCss}
+`;
+const OpenEye = styled(OpenEyeIcon)`
+  ${eyeIconCss}
+`;
+
+const ColumnDropdown = styled(Dropdown)`
+  width: 100px;
+
+  &,
+  > div {
+    height: 22px;
+  }
+
+  .ant-segmented-item-label {
+    height: 18px;
+    min-height: 18px;
+    line-height: 18px;
+    padding: 0;
+  }
+`;
+
+const ColumnBatchOptionWrapper = styled.div`
+  display: flex;
+  align-items: center;
+  color: ${GreyTextColor}
+  line-height: 16px;
+  font-size: 13px;
+`;
+
+type ViewOptionType = "normal" | "summary";
+
+const summaryRowOptions = [
+  {
+    label: "Row 1",
+    value: 0,
+  },
+  {
+    label: "Row 2",
+    value: 1,
+  },
+  {
+    label: "Row 3",
+    value: 2,
+  },
+];
+
+const columnViewOptions = [
+  {
+    label: "Normal",
+    value: "normal",
+  },
+  {
+    label: "Summary",
+    value: "summary",
+  },
+];
+
+const columnFilterOptions = [
+  { label: trans("table.allColumn"), value: "all" },
+  { label: trans("table.visibleColumn"), value: "visible" },
+];
+type ColumnFilterOptionValueType = typeof columnFilterOptions[number]["value"];
+
+const columnBatchOptions = [
+  {
+    label: trans("prop.hide"),
+    value: "hide",
+  },
+  {
+    label: trans("table.editable"),
+    value: "editable",
+  },
+  {
+    label: trans("table.autoWidth"),
+    value: "autoWidth",
+  },
+  {
+    label: trans("table.sortable"),
+    value: "sortable",
+  },
+  {
+    label: trans("table.align"),
+    value: "align",
+  },
+] as const;
+
+type ColumnBatchOptionValueType = typeof columnBatchOptions[number]["value"];
+
+const HideIcon = React.memo((props: { hide: boolean; setHide: (hide: boolean) => void }) => {
+  const { hide, setHide } = props;
+  const Eye = hide ? CloseEye : OpenEye;
+  
+  const handleClick = useCallback((e: React.MouseEvent) => {
+    e.stopPropagation();
+    setHide(!hide);
+  }, [hide, setHide]);
+
+  return <Eye onClick={handleClick} />;
+});
+
+HideIcon.displayName = 'HideIcon';
+
+function ColumnBatchCheckBox<T extends keyof ColumnCompType["children"]>({
+  childrenKey,
+  column,
+  convertValueFunc,
+}: {
+  childrenKey: T;
+  column: ColumnCompType | Array<ColumnCompType>;
+  convertValueFunc?: (checked: boolean) => RawColumnType[T];
+}) {
+  const isChecked = useCallback((column: ColumnCompType) => {
+    if (childrenKey === "autoWidth") {
+      return column.children.autoWidth.getView() === "auto";
+    } else {
+      return column.children[childrenKey].getView();
+    }
+  }, [childrenKey]);
+
+  const convertValue = useCallback((checked: boolean) => 
+    convertValueFunc ? convertValueFunc(checked) : checked,
+    [convertValueFunc]
+  );
+
+  const isBatch = Array.isArray(column);
+  const columns = isBatch ? column : [column];
+  
+  const disabledStatus = useMemo(() => columns.map((c) => {
+    if (childrenKey !== "editable") {
+      return false;
+    }
+    const columnType = c.children.render
+      .getOriginalComp()
+      .children.comp.children.compType.getView();
+    return !ColumnTypeCompMap[columnType].canBeEditable();
+  }), [columns, childrenKey]);
+
+  const { allChecked, allNotChecked } = useMemo(() => {
+    let allChecked = true;
+    let allNotChecked = true;
+    
+    columns.forEach((c, index) => {
+      if (disabledStatus[index]) {
+        if (!isBatch) {
+          allChecked = false;
+        }
+        return;
+      }
+      if (isChecked(c)) {
+        allNotChecked = false;
+      } else {
+        allChecked = false;
+      }
+    });
+    
+    return { allChecked, allNotChecked };
+  }, [columns, disabledStatus, isBatch, isChecked]);
+
+  const onCheckChange = useCallback((checked: boolean) => {
+    columns.forEach(
+      (c, index) =>
+        !disabledStatus[index] &&
+        c.children[childrenKey].dispatch(
+          deferAction(changeValueAction(convertValue(checked) as any, true))
+        )
+    );
+  }, [columns, disabledStatus, childrenKey, convertValue]);
+
+  if (childrenKey === "hide") {
+    return <HideIcon hide={allChecked} setHide={onCheckChange} />;
+  }
+
+  return (
+    <CheckBox
+      indeterminate={!allChecked && !allNotChecked}
+      disabled={!isBatch && disabledStatus[0]}
+      checked={allChecked}
+      onChange={(e: CheckboxChangeEvent) => {
+        onCheckChange(e.target.checked);
+      }}
+    />
+  );
+}
+
+const ColumnBatchView: Record<
+  ColumnBatchOptionValueType,
+  (column: ColumnCompType | Array<ColumnCompType>) => JSX.Element
+> = {
+  hide: (column) => <ColumnBatchCheckBox childrenKey="hide" column={column} />,
+  editable: (column) => <ColumnBatchCheckBox childrenKey="editable" column={column} />,
+  sortable: (column) => <ColumnBatchCheckBox childrenKey="sortable" column={column} />,
+  autoWidth: (column) => (
+    <ColumnBatchCheckBox
+      childrenKey="autoWidth"
+      column={column}
+      convertValueFunc={(checked) => (checked ? "auto" : "fixed")}
+    />
+  ),
+  align: (column) => {
+    const columns = Array.isArray(column) ? column : [column];
+    const value = Array.isArray(column) ? undefined : column.children.align.getView();
+    return (
+      <ColumnDropdown
+        options={alignOptions}
+        value={value}
+        radioButton={true}
+        onChange={(value) => {
+          columns.forEach((c) =>
+            c.children.align.dispatch(deferAction(changeValueAction(value, true)))
+          );
+        }}
+      />
+    );
+  },
+};
+
+function ColumnPropertyView<T extends MultiBaseComp<TableChildrenType>>(props: {
+  comp: T;
+  columnLabel: string;
+}) {
+  const { comp } = props;
+  const [viewMode, setViewMode] = useState<ViewOptionType>('normal');
+  const [summaryRow, setSummaryRow] = useState(0);
+  const [columnFilterType, setColumnFilterType] = useState<ColumnFilterOptionValueType>("all");
+  const [columnBatchType, setColumnBatchType] = useState<ColumnBatchOptionValueType>("hide");
+  
+  const selection = useMemo(() => getSelectedRowKeys(comp.children.selection)[0] ?? "0", [comp.children.selection]);
+  const columns = useMemo(() => comp.children.columns.getView(), [comp.children.columns]);
+  const rowExample = useMemo(() => comp.children.dataRowExample.getView(), [comp.children.dataRowExample]);
+  const dynamicColumn = useMemo(() => comp.children.dynamicColumn.getView(), [comp.children.dynamicColumn]);
+  const data = useMemo(() => comp.children.data.getView(), [comp.children.data]);
+  const columnOptionItems = useMemo(
+    () => columns.filter((c) => columnFilterType === "all" || !c.children.hide.getView()),
+    [columnFilterType, columns]
+  );
+  const summaryRows = parseInt(comp.children.summaryRows.getView());
+
+  const handleViewModeChange = useCallback((value: string) => {
+    setViewMode(value as ViewOptionType);
+  }, []);
+
+  const handleSummaryRowChange = useCallback((value: number) => {
+    setSummaryRow(value);
+  }, []);
+
+  const handleColumnFilterChange = useCallback((value: ColumnFilterOptionValueType) => {
+    setColumnFilterType(value);
+  }, []);
+
+  const handleColumnBatchTypeChange = useCallback((value: ColumnBatchOptionValueType) => {
+    setColumnBatchType(value);
+  }, []);
+
+  const columnOptionToolbar = (
+    <InsertDiv>
+      <div style={{ display: "flex", alignItems: "center", marginRight: "auto" }}>
+        <TextLabel label={props.columnLabel} />
+        <Graylabel>{" (" + columns.length + ")"}</Graylabel>
+      </div>
+      {rowExample && (
+        <ToolTipLabel title={trans("table.refreshButtonTooltip")}>
+          <StyledRefreshIcon
+            onClick={() => {
+              // console.log("comp", comp);
+              comp.dispatch(
+                wrapChildAction(
+                  "columns",
+                  comp.children.columns.dataChangedAction({
+                    rowExample,
+                    doGeneColumn: true,
+                    dynamicColumn: dynamicColumn,
+                    data: data,
+                  })
+                )
+              );
+              // the function below is not working
+              // comp.dispatch(comp.changeChildAction("dataRowExample", null));
+            }}
+          />
+        </ToolTipLabel>
+      )}
+      <LinkButton
+        icon={<BluePlusIcon />}
+        text={trans("addItem")}
+        onClick={() => {
+          comp.children.columns.dispatch(comp.children.columns.pushAction(newCustomColumn()));
+        }}
+      />
+    </InsertDiv>
+  );
+
+  return (
+    <>
+      <Option
+        headerItem={
+          <OptionItem
+            title={
+              <Dropdown
+                border={true}
+                style={{ width: "auto", marginLeft: "8px" }}
+                value={columnFilterType}
+                options={columnFilterOptions}
+                label=""
+                onChange={(value) => {
+                  handleColumnFilterChange(value);
+                }}
+              />
+            }
+            config={{ dataIndex: "header" }}
+            draggable={false}
+            optionExtra={
+              <ColumnBatchOptionWrapper>
+                <Dropdown
+                  border={true}
+                  style={{ width: "auto" }}
+                  value={columnBatchType}
+                  options={columnBatchOptions}
+                  label=""
+                  onChange={(value) => {
+                    handleColumnBatchTypeChange(value);
+                  }}
+                />
+                {ColumnBatchView[columnBatchType](columns)}
+              </ColumnBatchOptionWrapper>
+            }
+          />
+        }
+        itemExtra={(column) => {
+          return (
+            <div
+              style={{
+                display: "flex",
+                alignItems: "center",
+                height: "100%",
+              }}
+              onClick={(e) => e.stopPropagation()}
+            >
+              {ColumnBatchView[columnBatchType](column)}
+            </div>
+          );
+        }}
+        items={columnOptionItems}
+        optionToolbar={columnOptionToolbar}
+        itemTitle={(column) => {
+          const columnView = column.getView();
+          if (columnView.hide) {
+            return <span style={{ color: GreyTextColor }}>{columnView.title}</span>;
+          }
+          return columnView.title;
+        }}
+        popoverTitle={(column) => {
+          const columnView = column.getView();
+          return columnView.isCustom ? trans("table.customColumn") : columnView.dataIndex;
+        }}
+        content={(column, index) => (
+          <>
+            <Segmented
+              block
+              options={columnViewOptions}
+              value={viewMode}
+              onChange={(k) => handleViewModeChange(k as ViewOptionType)}
+            />
+            {viewMode === 'summary' && (
+              <Segmented
+                block
+                options={summaryRowOptions.slice(0, summaryRows)}
+                value={summaryRow}
+                onChange={(k) => handleSummaryRowChange(k)}
+              />
+            )}
+            {column.propertyView(selection, viewMode, summaryRow)}
+            {column.getView().isCustom && (
+              <RedButton
+                onClick={() => {
+                  CustomModal.confirm({
+                    title: trans("table.deleteColumn"),
+                    content: trans("table.confirmDeleteColumn") + `${column.getView().title}?`,
+                    onConfirm: () =>
+                      comp.children.columns.dispatch(comp.children.columns.deleteAction(index)),
+                    confirmBtnType: "delete",
+                    okText: trans("delete"),
+                  });
+                }}
+              >
+                {trans("delete")}
+              </RedButton>
+            )}
+          </>
+        )}
+        onAdd={() => {
+          comp.children.columns.dispatch(comp.children.columns.pushAction(newCustomColumn()));
+        }}
+        onMove={(fromIndex, toIndex) => {
+          const action = comp.children.columns.arrayMoveAction(fromIndex, toIndex);
+          comp.children.columns.dispatch(action);
+        }}
+        dataIndex={(column) => column.getView().dataIndex}
+        scrollable={true}
+      />
+    </>
+  );
+}
+
+function columnPropertyView<T extends MultiBaseComp<TableChildrenType>>(comp: T) {
+  const columnLabel = trans("table.columnNum");
+  // const dynamicColumn = comp.children.dynamicColumn.getView();
+  return [
+    controlItem(
+      { filterText: columnLabel },
+      <ColumnPropertyView comp={comp} columnLabel={columnLabel} />
+    ),
+    /* comp.children.dynamicColumn.propertyView({ label: trans("table.dynamicColumn") }),
+    dynamicColumn &&
+      comp.children.dynamicColumnConfig.propertyView({
+        label: trans("table.dynamicColumnConfig"),
+        tooltip: trans("table.dynamicColumnConfigDesc"),
+      }), */
+  ];
+}
+
+export function compTablePropertyView<T extends MultiBaseComp<TableChildrenType> & { editorModeStatus: string }>(comp: T) {
+  const editorModeStatus = comp.editorModeStatus;
+  const dataLabel = trans("data");
+
+  return (
+    <>
+      {["logic", "both"].includes(editorModeStatus) && (
+        <Section name={trans("table.dataDesc")}>
+          {controlItem(
+            { filterText: dataLabel },
+            <div className={tableDataDivClassName}>
+              {comp.children.data.propertyView({
+                label: dataLabel,
+              })}
+            </div>
+          )}
+        </Section>
+      )}
+
+      {["layout", "both"].includes(editorModeStatus) && (
+        <Section name={trans("prop.columns")}>
+          {columnPropertyView(comp)}
+        </Section>
+      )}
+
+      {["logic", "both"].includes(editorModeStatus) && (
+        <>
+          <Section name={sectionNames.interaction}>
+            {comp.children.onEvent.getPropertyView()}
+            {hiddenPropertyView(comp.children)}
+            {loadingPropertyView(comp.children)}
+            {comp.children.selection.getPropertyView()}
+            {comp.children.editModeClicks.propertyView({
+              label: trans("table.editMode"),
+              radioButton: true,
+            })}
+            {comp.children.searchText.propertyView({
+              label: trans("table.searchText"),
+              tooltip: trans("table.searchTextTooltip"),
+              placeholder: "{{input1.value}}",
+            })}
+          </Section>
+
+          <Section name={"Summary"}>
+            {comp.children.showSummary.propertyView({
+              label: trans("table.showSummary")
+            })}
+            {comp.children.showSummary.getView() &&
+              comp.children.summaryRows.propertyView({
+              label: trans("table.totalSummaryRows"),
+              radioButton: true,
+            })}
+          </Section>
+
+          <Section name={"Insert Rows"}>
+            {comp.children.inlineAddNewRow.propertyView({
+              label: trans("table.inlineAddNewRow")
+            })}
+          </Section>
+
+          <Section name={trans("prop.toolbar")}>
+            {comp.children.toolbar.getPropertyView()}
+          </Section>
+        </>
+      )}
+
+      {["layout", "both"].includes(editorModeStatus) && (
+        <>
+          <Section name={sectionNames.layout}>
+            {comp.children.size.propertyView({
+              label: trans("table.tableSize"),
+              radioButton: true,
+            })}
+            {comp.children.autoHeight.getPropertyView()}
+            {comp.children.showHorizontalScrollbar.propertyView({
+              label: trans("prop.showHorizontalScrollbar"),
+            })}
+            {!comp.children.autoHeight.getView() && comp.children.showVerticalScrollbar.propertyView({
+              label: trans("prop.showVerticalScrollbar"),
+            })}
+            {comp.children.fixedHeader.propertyView({
+              label: trans("table.fixedHeader"),
+              tooltip: trans("table.fixedHeaderTooltip")
+            })}
+            {comp.children.hideHeader.propertyView({
+              label: trans("table.hideHeader"),
+            })}
+            {comp.children.hideToolbar.propertyView({
+              label: trans("table.hideToolbar"),
+            })}
+            {comp.children.viewModeResizable.propertyView({
+              label: trans("table.viewModeResizable"),
+              tooltip: trans("table.viewModeResizableTooltip"),
+            })}
+            {comp.children.visibleResizables.propertyView({
+              label: trans("table.visibleResizables"),
+              tooltip: trans("table.visibleResizablesTooltip"),
+            })}
+          </Section>
+          <Section name={trans("prop.pagination")}>
+            {comp.children.pagination.getPropertyView()}
+          </Section>
+        </>
+      )}
+
+      {["logic", "both"].includes(editorModeStatus) && (
+        <>
+          <Section name={sectionNames.advanced}>
+            {comp.children.expansion.getPropertyView()}
+            {comp.children.dynamicColumn.propertyView({ label: trans("table.dynamicColumn") })}
+            {comp.children.dynamicColumn.getView() &&
+              comp.children.dynamicColumnConfig.propertyView({
+                label: trans("table.dynamicColumnConfig"),
+                tooltip: trans("table.dynamicColumnConfigDesc"),
+            })}
+          </Section>
+        </>
+      )}
+
+      {["layout", "both"].includes(editorModeStatus) && (
+        <><Section name={"Table Style"}>
+            {comp.children.style.getPropertyView()} 
+          </Section>
+          <Section name={"Header Style"}>
+            {comp.children.headerStyle.getPropertyView()}
+          </Section>
+          <Section name={"Toolbar Style"}>
+            {comp.children.toolbarStyle.getPropertyView()}
+          </Section>
+          <Section name={"Row Style"}>
+            {comp.children.showRowGridBorder.propertyView({
+              label: trans("table.showVerticalRowGridBorder"),
+            })}
+            {comp.children.showHRowGridBorder.propertyView({
+              label: trans("table.showHorizontalRowGridBorder"),
+            })}
+            {comp.children.rowStyle.getPropertyView()}
+            {comp.children.rowAutoHeight.getPropertyView()}
+            {comp.children.rowHeight.getPropertyView()}
+            {comp.children.rowColor.getPropertyView()}
+          </Section>
+          <Section name={"Column Style"}>
+            {comp.children.columnsStyle.getPropertyView()}
+          </Section>
+          <Section name={"Summary Row Style"}>
+            {comp.children.summaryRowStyle.getPropertyView()}
+          </Section>
+        </>
+      )}
+    </>
+  );
+}
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableSummaryComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableSummaryComp.tsx
new file mode 100644
index 0000000000..e026d5aef4
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableSummaryComp.tsx
@@ -0,0 +1,291 @@
+import { ThemeDetail } from "api/commonSettingApi";
+import { ColumnComp } from "comps/comps/tableComp/column/tableColumnComp";
+import { TableColumnLinkStyleType, TableColumnStyleType, TableSummaryRowStyleType } from "comps/controls/styleControlConstants";
+import styled from "styled-components";
+import { defaultTheme } from "@lowcoder-ee/constants/themeConstants";
+import Table from "antd/es/table";
+import { ReactNode, useMemo, memo, useCallback } from "react";
+import Tooltip from "antd/es/tooltip";
+
+const CellContainer = styled.div<{
+  $textAlign?: 'left' | 'center' | 'right';
+}>`
+  display: flex;
+  justify-content: ${(props) => {
+    switch (props.$textAlign) {
+      case 'left':
+        return 'flex-start';
+      case 'center':
+        return 'center';
+      case 'right':
+        return 'flex-end';
+      default:
+        return 'flex-start';
+    }
+  }};
+`;
+
+const TableSummaryRow = styled(Table.Summary.Row)<{
+  $istoolbarPositionBelow: boolean;
+  $background: string;
+}>`
+  ${props => `background: ${props.$background}`};
+
+  td:last-child {
+    border-right: unset !important;
+  }
+
+  ${props => !props.$istoolbarPositionBelow && `
+    &:last-child td {
+      border-bottom: none !important;
+    }
+  `}
+  
+`;
+
+const TableSummarCell = styled(Table.Summary.Cell)<{
+  $style: TableSummaryRowStyleType;
+  $defaultThemeDetail: ThemeDetail;
+  $linkStyle?: TableColumnLinkStyleType;
+  $tableSize?: string;
+  $autoHeight?: boolean;
+}>`
+  background: ${(props) => props.$style.background} !important;
+  border-color: ${(props) => props.$style.border} !important;
+  // border-width: ${(props) => props.$style.borderWidth} !important;
+  // border-style: ${(props) => props.$style.borderStyle} !important;
+  border-radius: ${(props) => props.$style.radius};
+  padding: 0 !important;
+
+  > div {
+    margin: ${(props) => props.$style.margin};
+    color: ${(props) => props.$style.text};
+    font-weight: ${(props) => props.$style.textWeight};
+    font-family: ${(props) => props.$style.fontFamily};
+    overflow: hidden; 
+    ${(props) => props.$tableSize === 'small' && `
+      padding: 1px 8px;
+      font-size: ${props.$defaultThemeDetail.textSize == props.$style.textSize ? '14px !important' : props.$style.textSize + ' !important'};
+      font-style:${props.$style.fontStyle} !important;
+      min-height: 14px;
+      line-height: 20px;
+      ${!props.$autoHeight && `
+        overflow-y: auto;
+        max-height: 28px;
+      `};
+    `};
+    ${(props) => props.$tableSize === 'middle' && `
+      padding: 8px 8px;
+      font-size: ${props.$defaultThemeDetail.textSize == props.$style.textSize ? '16px !important' : props.$style.textSize + ' !important'};
+      font-style:${props.$style.fontStyle} !important;
+      min-height: 24px;
+      line-height: 24px;
+      ${!props.$autoHeight && `
+        overflow-y: auto;
+        max-height: 48px;
+      `};
+    `};
+    ${(props) => props.$tableSize === 'large' && `
+      padding: 16px 16px;
+      font-size: ${props.$defaultThemeDetail.textSize == props.$style.textSize ? '18px !important' : props.$style.textSize + ' !important'};
+      font-style:${props.$style.fontStyle} !important;
+      min-height: 48px;
+      ${!props.$autoHeight && `
+        overflow-y: auto;
+        max-height: 96px;
+      `};
+    `};
+    
+    > .ant-badge > .ant-badge-status-text,
+    > div > .markdown-body {
+      color: ${(props) => props.$style.text};
+    }
+
+    > div > svg g {
+      stroke: ${(props) => props.$style.text};
+    }
+
+    > a,
+    > div  a {
+      color: ${(props) => props.$linkStyle?.text};
+
+      &:hover {
+        color: ${(props) => props.$linkStyle?.hoverText};
+      }
+
+      &:active {
+        color: ${(props) => props.$linkStyle?.activeText}};
+      }
+    }
+  }
+`;
+
+const CellWrapper = memo(({
+  children,
+  tooltipTitle,
+}: {
+  children: ReactNode,
+  tooltipTitle?: string,
+}) => {
+  if (tooltipTitle) {
+    return (
+      <Tooltip title={tooltipTitle} placement="topLeft">
+        {children}
+      </Tooltip>
+    )
+  }
+  return (
+    <>{children}</>
+  )
+});
+
+CellWrapper.displayName = 'CellWrapper';
+
+const TableSummaryCellView = memo(function TableSummaryCellView(props: {
+  index: number;
+  key: string;
+  children: any;
+  align?: any;
+  rowStyle: TableSummaryRowStyleType;
+  columnStyle: TableColumnStyleType;
+  linkStyle: TableColumnLinkStyleType;
+  tableSize?: string;
+  autoHeight?: boolean;
+  cellColor: string;
+  cellTooltip: string;
+}) {
+  const {
+    children,
+    rowStyle,
+    columnStyle,
+    tableSize,
+    autoHeight,
+    cellColor,
+    cellTooltip,
+    ...restProps
+  } = props;
+
+  const style = useMemo(() => ({
+    background: cellColor || columnStyle.background,
+    margin: columnStyle.margin || rowStyle.margin,
+    text: columnStyle.text || rowStyle.text,
+    border: columnStyle.border || rowStyle.border,
+    borderWidth: rowStyle.borderWidth,
+    borderStyle: rowStyle.borderStyle,
+    radius: columnStyle.radius || rowStyle.radius,
+    textSize: columnStyle.textSize || rowStyle.textSize,
+    textWeight: rowStyle.textWeight || columnStyle.textWeight,
+    fontFamily: rowStyle.fontFamily || columnStyle.fontFamily,
+    fontStyle: rowStyle.fontStyle || columnStyle.fontStyle,
+  }), [cellColor, columnStyle, rowStyle]);
+
+  return (
+    <TableSummarCell
+      {...restProps}
+      $style={style}
+      $defaultThemeDetail={defaultTheme}
+      $tableSize={tableSize}
+      $autoHeight={autoHeight}
+    >
+      <CellWrapper tooltipTitle={cellTooltip}>
+        <CellContainer $textAlign={restProps.align}>
+          {children}
+        </CellContainer>
+      </CellWrapper>
+    </TableSummarCell>
+  );
+});
+
+TableSummaryCellView.displayName = 'TableSummaryCellView';
+
+export const TableSummary = memo(function TableSummary(props: {
+  tableSize: string;
+  expandableRows: boolean;
+  multiSelectEnabled: boolean;
+  summaryRows: number;
+  columns: ColumnComp[];
+  summaryRowStyle: TableSummaryRowStyleType;
+  istoolbarPositionBelow: boolean;
+  dynamicColumn: boolean;
+  dynamicColumnConfig: string[];
+}) {
+  const {
+    columns,
+    summaryRows,
+    summaryRowStyle,
+    tableSize,
+    expandableRows,
+    multiSelectEnabled,
+    istoolbarPositionBelow,
+    dynamicColumn,
+    dynamicColumnConfig,
+  } = props;
+
+  const visibleColumns = useMemo(() => {
+    let cols = columns.filter(col => !col.getView().hide);
+    if (dynamicColumn && dynamicColumnConfig?.length) {
+      cols = cols.filter(col => {
+        const colView = col.getView();
+        return dynamicColumnConfig.includes(colView.isCustom ? colView.title : colView.dataIndex)
+      })
+    }
+    if (expandableRows) {
+      cols.unshift(new ColumnComp({}));
+    }
+    if (multiSelectEnabled) {
+      cols.unshift(new ColumnComp({}));
+    }
+    return cols;
+  }, [columns, expandableRows, multiSelectEnabled, dynamicColumn, dynamicColumnConfig]);
+
+  const renderSummaryCell = useCallback((column: ColumnComp, rowIndex: number, index: number) => {
+    const summaryColumn = column.children.summaryColumns.getView()[rowIndex].getView();
+    return (
+      <TableSummaryCellView
+        index={index}
+        key={`summary-${rowIndex}-${column.getView().dataIndex}-${index}`}
+        tableSize={tableSize}
+        rowStyle={summaryRowStyle}
+        align={summaryColumn.align}
+        cellColor={summaryColumn.cellColor}
+        cellTooltip={summaryColumn.cellTooltip}
+        columnStyle={{
+          background: summaryColumn.background,
+          margin: summaryColumn.margin,
+          text: summaryColumn.text,
+          border: summaryColumn.border,
+          radius: summaryColumn.radius,
+          textSize: summaryColumn.textSize,
+          textWeight: summaryColumn.textWeight,
+          fontStyle: summaryColumn.fontStyle,
+          fontFamily: summaryColumn.fontFamily,
+        }}
+        linkStyle={{
+          text: summaryColumn.linkColor,
+          hoverText: summaryColumn.linkHoverColor,
+          activeText: summaryColumn.linkActiveColor,
+        }}
+      >
+        {summaryColumn.render({}, '').getView().view({})}
+      </TableSummaryCellView>
+    );
+  }, [tableSize, summaryRowStyle]);
+
+  if (!visibleColumns.length) return <></>;
+
+  return (
+    <Table.Summary>
+      {Array.from(Array(summaryRows)).map((_, rowIndex) => (
+        <TableSummaryRow
+          key={rowIndex}
+          $istoolbarPositionBelow={istoolbarPositionBelow}
+          $background={summaryRowStyle.background}
+        >
+          {visibleColumns.map((column, index) => renderSummaryCell(column, rowIndex, index))}
+        </TableSummaryRow>
+      ))}
+    </Table.Summary>
+  );
+});
+
+TableSummary.displayName = 'TableSummary';
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableToolbarComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableToolbarComp.tsx
new file mode 100644
index 0000000000..2969e3c46d
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableToolbarComp.tsx
@@ -0,0 +1,947 @@
+import { default as Button } from "antd/es/button";
+import { default as Pagination, PaginationProps } from "antd/es/pagination";
+import { default as Popover } from "antd/es/popover";
+import { ThemeDetail } from "api/commonSettingApi";
+import { ColumnCompType } from "comps/comps/tableComp/column/tableColumnComp";
+import { TableOnEventView } from "comps/comps/tableComp/tableTypes";
+import { BoolControl } from "comps/controls/boolControl";
+import { StringControl } from "comps/controls/codeControl";
+import { dropdownControl } from "comps/controls/dropdownControl";
+import { TableToolbarStyleType } from "comps/controls/styleControlConstants";
+import { stateComp, withDefault } from "comps/generators";
+import { genRandomKey } from "comps/utils/idGenerator";
+import { ThemeContext } from "comps/utils/themeContext";
+import { trans } from "i18n";
+import _, { isNil } from "lodash";
+import { changeChildAction, ConstructorToView } from "lowcoder-core";
+import {
+  AlignBottom,
+  AlignClose,
+  AlignTop,
+  BluePlusIcon,
+  CheckBox,
+  CommonTextLabel,
+  CustomSelect,
+  DeleteIcon,
+  DownloadIcon,
+  FilterIcon,
+  LinkButton,
+  pageItemRender,
+  RefreshIcon,
+  TableColumnVisibilityIcon,
+  SuspensionBox,
+  TacoButton,
+  TacoInput,
+  ValueFromOption,
+} from "lowcoder-design";
+import React, { useContext, useEffect, useMemo, useRef, useState, memo, useCallback } from "react";
+import styled, { css } from "styled-components";
+import { JSONValue } from "util/jsonTypes";
+import { ControlNodeCompBuilder } from "comps/generators/controlCompBuilder";
+import { defaultTheme } from "@lowcoder-ee/constants/themeConstants";
+import type { CheckboxChangeEvent } from 'antd/es/checkbox';
+
+type ToolbarRowType = ConstructorToView<typeof TableToolbarComp>;
+
+const SaveChangeButtons = styled.div`
+  display: flex;
+  align-items: center;
+  gap: 8px;
+`;
+
+const getStyle = (
+  style: TableToolbarStyleType,
+  filtered: boolean,
+  theme: ThemeDetail,
+  position: ToolbarRowType["position"],
+  fixedToolbar: boolean,
+) => {
+  return css`
+    background: ${style.background};
+    // Implement horizontal scrollbar and vertical page number selection is not blocked
+    padding: 13px 12px;
+    position: sticky;
+    postion: -webkit-sticky;
+    left: 0px !important;
+    margin: ${style.margin} !important;
+    z-index: 999;
+
+    ${fixedToolbar && position === 'below' && `bottom: 0;`};
+    ${fixedToolbar && position === 'above' && `top: 0;` };
+
+    .toolbar-icons {
+      .refresh,
+      .download {
+        cursor: pointer;
+
+        * {
+          ${style.toolbarText !== defaultTheme.textDark ? `stroke: ${style.toolbarText}` : null};
+        }
+
+        &:hover * {
+          stroke: ${theme?.primary};
+        }
+      }
+
+      .filter {
+        cursor: pointer;
+
+        * {
+          ${filtered
+            ? `stroke: ${defaultTheme.primary}`
+            : style.toolbarText !== defaultTheme.textDark
+            ? `stroke: ${style.toolbarText}`
+            : null}
+        }
+
+        &:hover * {
+          stroke: ${theme?.primary};
+        }
+      }
+
+      .column-setting {
+        width: 20px;
+        cursor: pointer;
+
+        * {
+          ${style.toolbarText && style.toolbarText !== defaultTheme.textDark ? `fill: ${style.toolbarText}` : `fill: #8b8fa3`} 
+        }
+
+        &:hover * {
+          fill: ${theme?.primary};
+        }
+      }
+    }
+
+    .ant-pagination-prev,
+    .ant-pagination-next {
+      path {
+        ${style.toolbarText !== defaultTheme.textDark ? `fill: ${style.toolbarText}` : null};
+      }
+
+      svg:hover {
+        path {
+          fill: ${theme?.primary};
+        }
+      }
+    }
+
+    .ant-pagination {
+      color: ${style.toolbarText};
+    }
+
+    .ant-pagination-item-active {
+      border-color: ${style.border || theme?.primary};
+
+      a {
+        color: ${theme?.textDark};
+      }
+    }
+
+    .ant-pagination-item:not(.ant-pagination-item-active) a {
+      color: ${style.toolbarText};
+
+      &:hover {
+        color: ${theme?.primary};
+      }
+    }
+
+    .ant-select:not(.ant-select-disabled):hover .ant-select-selector,
+    .ant-select-focused:not(.ant-select-disabled).ant-select:not(.ant-select-customize-input)
+      .ant-select-selector,
+    .ant-pagination-options-quick-jumper input:hover,
+    .ant-pagination-options-quick-jumper input:focus {
+      border-color: ${style.border || theme?.primary};
+    }
+  `;
+};
+
+const ToolbarWrapper = styled.div<{
+  $style: TableToolbarStyleType;
+  $filtered: boolean;
+  $theme: ThemeDetail;
+  $position: ToolbarRowType["position"];
+  $fixedToolbar: boolean;
+}>`
+  // overflow: auto;
+  ${(props) => props.$style && getStyle(
+    props.$style,
+    props.$filtered,
+    props.$theme,
+    props.$position,
+    props.$fixedToolbar,
+  )}
+`;
+
+const ToolbarWrapper2 = styled.div`
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  min-width: max-content;
+  height: 21px;
+  width: 100%;
+`;
+
+const ToolbarIcons = styled.div`
+  display: flex;
+  align-items: center;
+  gap: 8px;
+`;
+
+const StyledDeleteIcon = styled(DeleteIcon)<{ disabled: boolean }>`
+  height: 16px;
+  width: 16px;
+
+  ${(props) =>
+    props.disabled &&
+    `
+      cursor: not-allowed;
+      g g {
+      stroke: #D7D9E0;
+    }
+  `}
+  :hover:not([disabled]) {
+    cursor: pointer;
+
+    g g {
+      stroke: #315efb;
+    }
+  }
+`;
+
+const FilterItem = styled.div`
+  display: flex;
+  align-items: center;
+  gap: 8px;
+`;
+
+const FilterViewContent = styled.div`
+  min-height: 92px;
+  overflow: auto;
+
+  > div {
+    margin-top: 4px;
+  }
+`;
+
+const FilterViewBottom = styled.div`
+  display: flex;
+  align-items: end;
+  height: 44px;
+  justify-content: end;
+  gap: 4px;
+
+  > button {
+    width: 76px;
+    height: 28px;
+  }
+`;
+
+const ColumnCheckItem = styled.div`
+  display: flex;
+  gap: 4px;
+`;
+
+const StyledCustomSelect = styled(CustomSelect)<{ $width: string }>`
+  .ant-select {
+  width: ${props => props.$width};
+  }
+`;
+
+const CustomSelectionOptionInner = styled.div`
+  width: 72px;
+  font-size: 13px;
+  line-height: 13px;
+`;
+
+const StyledTacoInput = styled(TacoInput)`
+  width: 136px;
+`;
+
+const StyledLinkButton = styled(LinkButton)`
+  margin-right: auto;
+`;
+
+const ColumnSettingContent = styled.div`
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+`;
+
+const ColumnSettingFooter = styled.div`
+  display: flex;
+  align-items: flex-end;
+  justify-content: space-between;
+  height: 32px;
+`;
+
+const filterStackOptions = [
+  {
+    label: trans("table.and"),
+    value: "and",
+  },
+  {
+    label: trans("table.or"),
+    value: "or",
+  },
+] as const;
+
+const positionOptions = [
+  {
+    label: <AlignBottom />,
+    value: "below",
+  },
+  {
+    label: <AlignTop />,
+    value: "above",
+  },
+  {
+    label: <AlignClose />,
+    value: "close",
+  },
+] as const;
+
+const tableFilterOperators = [
+  "contain",
+  "notContain",
+  "equal",
+  "notEqual",
+  "isEmpty",
+  "isNotEmpty",
+  "gt",
+  "gte",
+  "lt",
+  "lte",
+] as const;
+
+const noValueOperators: TableFilterOperator[] = ["isEmpty", "isNotEmpty"];
+
+type TableFilterOperator = typeof tableFilterOperators[number];
+export const tableFilterOperatorMap: Record<
+  TableFilterOperator,
+  {
+    label: string;
+    filter: (filterValue: string, data: JSONValue | undefined) => boolean;
+  }
+> = {
+  contain: {
+    label: trans("table.contains"),
+    filter: (filterValue, data) => {
+      if (isNil(data)) {
+        return false;
+      }
+      return data.toString().toLowerCase().includes(filterValue.toLowerCase());
+    },
+  },
+  notContain: {
+    label: trans("table.notContain"),
+    filter: (filterValue, data) => {
+      if (isNil(data)) {
+        return true;
+      }
+      return !data.toString().toLowerCase().includes(filterValue.toLowerCase());
+    },
+  },
+  equal: {
+    label: trans("table.equals"),
+    filter: (filterValue, data) => {
+      if (isNil(data)) {
+        return false;
+      }
+      return data.toString() === filterValue;
+    },
+  },
+  notEqual: {
+    label: trans("table.isNotEqual"),
+    filter: (filterValue, data) => {
+      if (isNil(data)) {
+        return true;
+      }
+      return data.toString() !== filterValue;
+    },
+  },
+  isEmpty: {
+    label: trans("table.isEmpty"),
+    filter: (filterValue, data) => {
+      return data === "" || isNil(data);
+    },
+  },
+  isNotEmpty: {
+    label: trans("table.isNotEmpty"),
+    filter: (filterValue, data) => {
+      return data !== "" && !isNil(data);
+    },
+  },
+  gt: {
+    label: trans("table.greater"),
+    filter: (value, data) => {
+      return _.gt(data, value);
+    },
+  },
+  gte: {
+    label: trans("table.greaterThanOrEquals"),
+    filter: (value, data) => {
+      return _.gte(data, value);
+    },
+  },
+  lt: {
+    label: trans("table.lessThan"),
+    filter: (value, data) => {
+      return _.gt(value, data);
+    },
+  },
+  lte: {
+    label: trans("table.lessThanOrEquals"),
+    filter: (value, data) => {
+      return _.gte(value, data);
+    },
+  },
+} as const;
+
+const tableFilterOperatorOptions = tableFilterOperators.map((operator) => ({
+  label: tableFilterOperatorMap[operator].label,
+  value: operator,
+}));
+
+type TableFilterDataType = {
+  columnKey: string;
+  filterValue: string;
+  operator: TableFilterOperator;
+};
+
+export type TableFilter = {
+  stackType: ValueFromOption<typeof filterStackOptions>;
+  filters: TableFilterDataType[];
+};
+
+const genFilterViewItem = (
+  columnKey: string = "",
+  filterValue: string = "",
+  operator?: TableFilterOperator
+) => {
+  return {
+    key: genRandomKey(),
+    columnKey: columnKey,
+    filterValue: filterValue,
+    operator: operator,
+  } as const;
+};
+
+type FilterItemType = ReturnType<typeof genFilterViewItem>;
+
+function validFilterItem(filter: FilterItemType) {
+  return (
+    filter.columnKey &&
+    filter.operator &&
+    (filter.filterValue !== "" || noValueOperators.includes(filter.operator))
+  );
+}
+
+function emptyFilterItem(filter: FilterItemType) {
+  return !filter.columnKey && !filter.operator && filter.filterValue === "";
+}
+
+const FilterFooter = memo(function FilterFooter(props: {
+  filterItems: FilterItemType[];
+  setFilterItems: (items: FilterItemType[]) => void;
+  setVisible: (v: boolean) => void;
+}) {
+  const { filterItems, setFilterItems, setVisible } = props;
+
+  const handleAddFilter = useCallback(() => {
+    setFilterItems(filterItems.concat(genFilterViewItem()));
+  }, [filterItems, setFilterItems]);
+
+  const handleClearFilters = useCallback(() => {
+    setFilterItems([genFilterViewItem()]);
+  }, [setFilterItems]);
+
+  const handleClose = useCallback(() => {
+    setVisible(false);
+  }, [setVisible]);
+
+  return (
+    <FilterViewBottom>
+      <StyledLinkButton
+        text={trans("addItem")}
+        icon={<BluePlusIcon />}
+        onClick={handleAddFilter}
+      />
+      <TacoButton
+        onClick={handleClearFilters}
+        buttonType="delete"
+      >
+        {trans("table.clear")}
+      </TacoButton>
+      <TacoButton buttonType="primary" onClick={handleClose}>
+        {trans("ok")}
+      </TacoButton>
+    </FilterViewBottom>
+  );
+});
+
+FilterFooter.displayName = 'FilterFooter';
+
+const FilterContent = memo(function FilterContent(props: {
+  filterItems: FilterItemType[];
+  columnKeyNames: Array<[string, string]>;
+  stackTypeState: TableFilter["stackType"];
+  stackTypeValue: TableFilter["stackType"] | undefined;
+  updateFilter: (filterItem: FilterItemType) => void;
+  handleStackTypeChange: (value: TableFilter["stackType"]) => void;
+  removeFilter: (key: string) => void;
+}) {
+  const {
+    filterItems,
+    columnKeyNames,
+    stackTypeState,
+    stackTypeValue,
+    updateFilter,
+    handleStackTypeChange,
+    removeFilter,
+  } = props;
+
+  const handleColumnKeyChange = useCallback((filter: FilterItemType, value: string) => {
+    updateFilter({ ...filter, columnKey: value });
+  }, [updateFilter]);
+
+  const handleOperatorChange = useCallback((filter: FilterItemType, value: TableFilterOperator) => {
+    updateFilter({ ...filter, operator: value });
+  }, [updateFilter]);
+
+  const handleFilterValueChange = useCallback((filter: FilterItemType, e: React.ChangeEvent<HTMLInputElement>) => {
+    updateFilter({ ...filter, filterValue: e.target.value });
+  }, [updateFilter]);
+
+  return (
+    <FilterViewContent>
+      {filterItems &&
+        filterItems.map((filter, index) => (
+          <FilterItem key={filter.key}>
+            {index === 0 && (
+              <CommonTextLabel style={{ width: "65px" }}>
+                {trans("table.filterRule")}
+              </CommonTextLabel>
+            )}
+            {index >= 1 && (
+              <StyledCustomSelect
+                $width="65px"
+                border
+                defaultValue={stackTypeState}
+                value={stackTypeValue}
+                disabled={index > 1}
+                onChange={handleStackTypeChange}
+              >
+                {filterStackOptions.map((item) => (
+                  <CustomSelect.Option key={item.value} value={item.value}>
+                    <CustomSelectionOptionInner>
+                      {item.label}
+                    </CustomSelectionOptionInner>
+                  </CustomSelect.Option>
+                ))}
+              </StyledCustomSelect>
+            )}
+            <StyledCustomSelect
+              $width="160px"
+              options={columnKeyNames.map((c) => ({ label: c[1], value: c[0] }))}
+              value={filter.columnKey}
+              placeholder={trans("table.chooseColumnName")}
+              allowClear
+              onChange={(value) => handleColumnKeyChange(filter, value)}
+            />
+            <StyledCustomSelect
+              $width="160px"
+              defaultValue={filter.operator}
+              placeholder={trans("table.chooseCondition")}
+              allowClear
+              options={tableFilterOperatorOptions}
+              onChange={(value) => handleOperatorChange(filter, value)}
+            />
+            <StyledTacoInput
+              disabled={filter.operator && noValueOperators.includes(filter.operator)}
+              defaultValue={filter.filterValue}
+              onChange={(e) => handleFilterValueChange(filter, e)}
+            />
+            <StyledDeleteIcon
+              disabled={filterItems.length === 1 && emptyFilterItem(filterItems[0])}
+              onClick={() => removeFilter(filter.key)}
+            />
+          </FilterItem>
+        ))}
+    </FilterViewContent>
+  );
+});
+
+FilterContent.displayName = 'FilterContent';
+
+const TableFilterView = memo(function TableFilterView(props: {
+  columnKeyNames: Array<[string, string]>;
+  tableFilter: TableFilter;
+  onFilterChange: (filters: TableFilterDataType[], stackType: TableFilter["stackType"]) => void;
+  setVisible: (v: boolean) => void;
+}) {
+  const { columnKeyNames, tableFilter, onFilterChange, setVisible } = props;
+  const [stackTypeState, setStackTypeState] = useState(tableFilter.stackType);
+  const [filterItems, setFilterItems] = useState<FilterItemType[]>(() => 
+    tableFilter.filters.length > 0
+      ? tableFilter.filters.map((f) => genFilterViewItem(f.columnKey, f.filterValue, f.operator))
+      : [genFilterViewItem()]
+  );
+  const stackTypeValue = useMemo(() => filterStackOptions.find((f) => f.value === stackTypeState)?.value, [stackTypeState]);
+
+  const updateFilter = useCallback((filterItem: FilterItemType) => {
+    setFilterItems((items) =>
+      items.map((item) => (item.key === filterItem.key ? filterItem : item))
+    );
+  }, []);
+
+  const removeFilter = useCallback((key: string) => {
+    if (filterItems.length === 1) {
+      setFilterItems([genFilterViewItem()]);
+    } else {
+      setFilterItems((items) => items.filter((item) => item.key !== key));
+    }
+  }, [filterItems]);
+
+  const handleStackTypeChange = useCallback((value: TableFilter["stackType"]) => {
+    setStackTypeState(value);
+  }, []);
+
+  useEffect(() => {
+    onFilterChange(
+      filterItems
+        .filter(validFilterItem)
+        .map((f) => ({
+          filterValue: f.filterValue,
+          operator: f.operator!,
+          columnKey: f.columnKey,
+        })),
+      stackTypeState
+    );
+  }, [stackTypeState, filterItems, onFilterChange]);
+
+  return (
+    <SuspensionBox
+      title={trans("table.filter")}
+      onClose={() => setVisible(false)}
+      width={600}
+      scrollable
+      contentMaxHeight={292}
+      content={
+        <FilterContent
+          filterItems={filterItems}
+          columnKeyNames={columnKeyNames}
+          stackTypeState={stackTypeState}
+          stackTypeValue={stackTypeValue}
+          updateFilter={updateFilter}
+          handleStackTypeChange={handleStackTypeChange}
+          removeFilter={removeFilter}
+        />
+      }
+      footer={
+        <FilterFooter
+          filterItems={filterItems}
+          setFilterItems={setFilterItems}
+          setVisible={setVisible}
+        />
+      }
+    />
+  );
+});
+
+TableFilterView.displayName = 'TableFilterView';
+
+const ColumnSetting = memo(function ColumnSetting(props: {
+  columns: Array<ColumnCompType>;
+  setVisible: (v: boolean) => void;
+}) {
+  const { columns, setVisible } = props;
+
+  // Memoize checked states for all columns
+  const checkedStates = useMemo(() => columns.map((c) => !c.getView().tempHide), [columns]);
+
+  // Memoize allChecked calculation
+  const allChecked = useMemo(() => checkedStates.every(Boolean), [checkedStates]);
+
+  // Memoize checkViews rendering
+  const checkViews = useMemo(() =>
+    columns.map((c, idx) => {
+      const columnView = c.getView();
+      const checked = checkedStates[idx];
+      return (
+        <ColumnCheckItem key={columnView.dataIndex}>
+          <CheckBox
+            checked={checked}
+            onChange={(e: CheckboxChangeEvent) => {
+              c.children.tempHide.dispatchChangeValueAction(!e.target.checked);
+            }}
+          />
+          <CommonTextLabel>{columnView.title || columnView.dataIndex}</CommonTextLabel>
+        </ColumnCheckItem>
+      );
+    }),
+    [columns, checkedStates]
+  );
+
+  // Memoize the 'Select All' onChange handler
+  const handleSelectAll = useCallback((e: CheckboxChangeEvent) => {
+    const checked = e.target.checked;
+    columns.forEach((c) => {
+      const tempHide = c.children.tempHide.getView();
+      // fixme batch dispatch
+      if (checked && tempHide) {
+        c.children.tempHide.dispatchChangeValueAction(false);
+      } else if (!checked && !tempHide) {
+        c.children.tempHide.dispatchChangeValueAction(true);
+      }
+    });
+  }, [columns]);
+
+  return (
+    <SuspensionBox
+      title={trans("table.columnShows")}
+      onClose={() => setVisible(false)}
+      width={160}
+      contentMaxHeight={150}
+      scrollable
+      content={<ColumnSettingContent>{checkViews}</ColumnSettingContent>}
+      footer={
+        <ColumnSettingFooter>
+          <ColumnCheckItem>
+            <CheckBox
+              checked={allChecked}
+              onChange={handleSelectAll}
+            />
+            <CommonTextLabel>{trans("table.selectAll")}</CommonTextLabel>
+          </ColumnCheckItem>
+        </ColumnSettingFooter>
+      }
+    />
+  );
+});
+
+ColumnSetting.displayName = 'ColumnSetting';
+
+const ToolbarPopover = memo(function ToolbarPopover(props: {
+  visible: boolean;
+  setVisible: (v: boolean) => void;
+  Icon: React.FC<React.SVGProps<SVGSVGElement>>;
+  iconClassName: string;
+  content: JSX.Element;
+}) {
+  const { visible, setVisible, Icon, iconClassName, content } = props;
+
+  const handleVisibleChange = useCallback((v: boolean) => {
+    setVisible(v);
+  }, [setVisible]);
+
+  return (
+    <Popover
+      open={visible}
+      onOpenChange={handleVisibleChange}
+      content={content}
+      trigger="click"
+      placement="bottomRight"
+      styles={{
+        body: {
+          padding: 0,
+        },
+      }}
+    >
+      <Icon className={iconClassName} />
+    </Popover>
+  );
+});
+
+ToolbarPopover.displayName = 'ToolbarPopover';
+
+export const TableToolbar = memo(function TableToolbar(props: {
+  toolbar: ToolbarRowType;
+  $style: TableToolbarStyleType;
+  pagination: PaginationProps;
+  columns: Array<ColumnCompType>;
+  onRefresh: () => void;
+  onDownload: () => void;
+  hasChange: boolean;
+  onSaveChanges: () => void;
+  onCancelChanges: () => void;
+  onEvent: TableOnEventView;
+}) {
+  const {
+    toolbar,
+    $style,
+    pagination,
+    columns,
+    onRefresh,
+    onDownload,
+    hasChange,
+    onSaveChanges,
+    onCancelChanges,
+    onEvent,
+  } = props;
+
+  const theme = useContext(ThemeContext)?.theme;
+  const filtered = useMemo(() => toolbar.filter.filters.length > 0, [toolbar.filter.filters.length]);
+  const [filterVisible, setFilterVisible] = useState(false);
+  const [columnSettingVisible, setColumnSettingVisible] = useState(false);
+  const visibleColumns = useMemo(() => columns.filter((column) => !column.getView().hide), [columns]);
+
+  const handleRefresh = useCallback(() => {
+    onRefresh();
+    onEvent("refresh");
+  }, [onRefresh, onEvent]);
+
+  const handleDownload = useCallback(() => {
+    onDownload();
+    onEvent("download");
+  }, [onDownload, onEvent]);
+
+  const handleSaveChanges = useCallback(() => {
+    onSaveChanges();
+    onEvent("saveChanges");
+  }, [onSaveChanges, onEvent]);
+
+  const handleCancelChanges = useCallback(() => {
+    onCancelChanges();
+    onEvent("cancelChanges");
+  }, [onCancelChanges, onEvent]);
+
+  const handleColumnFilterChange = useCallback((filters: TableFilterDataType[], stackType: TableFilter["stackType"]) => {
+    if (
+      !_.isEqual(filters, toolbar.filter.filters) ||
+      stackType !== toolbar.filter.stackType
+    ) {
+      toolbar.onFilterChange(filters, stackType);
+      onEvent("filterChange");
+    }
+  }, [toolbar, onEvent]);
+
+  const columnKeyNameTuple = useMemo(() => {
+    return visibleColumns.map((column) => {
+      const c = column.getView();
+      return [c.dataIndex, c.title || c.dataIndex] as [string, string];
+    });
+  }, [visibleColumns]);
+
+  return (
+    <ToolbarWrapper
+      $style={$style}
+      $filtered={filtered}
+      $theme={theme || defaultTheme}
+      $position={toolbar.position}
+      $fixedToolbar={toolbar.fixedToolbar}
+    >
+      <ToolbarWrapper2>
+        <ToolbarIcons className="toolbar-icons">
+          {toolbar.showRefresh && (
+            <RefreshIcon className="refresh" onClick={handleRefresh} />
+          )}
+          {toolbar.showFilter && (
+            <ToolbarPopover
+              visible={filterVisible}
+              setVisible={setFilterVisible}
+              Icon={FilterIcon}
+              iconClassName="filter"
+              content={
+                <TableFilterView
+                  columnKeyNames={columnKeyNameTuple}
+                  tableFilter={toolbar.filter}
+                  setVisible={setFilterVisible}
+                  onFilterChange={handleColumnFilterChange}
+                />
+              }
+            />
+          )}
+          {toolbar.showDownload && (
+            <DownloadIcon className="download" onClick={handleDownload} />
+          )}
+          {toolbar.columnSetting && (
+            <ToolbarPopover
+              visible={columnSettingVisible}
+              setVisible={setColumnSettingVisible}
+              Icon={TableColumnVisibilityIcon}
+              iconClassName="column-setting"
+              content={<ColumnSetting columns={columns} setVisible={setColumnSettingVisible} />}
+            />
+          )}
+        </ToolbarIcons>
+        <Pagination
+          size="small"
+          {...pagination}
+          itemRender={pageItemRender}
+          onChange={(page, pageSize) => {
+            pagination.onChange && pagination.onChange(page, pageSize);
+            if (page !== pagination.current) {
+              onEvent("pageChange");
+            }
+          }}
+        />
+        {hasChange && toolbar.showUpdateButtons && (
+          <SaveChangeButtons>
+            <TacoButton onClick={handleCancelChanges}>{trans("cancel")}</TacoButton>
+            <TacoButton buttonType="primary" onClick={handleSaveChanges}>
+              {trans("table.saveChanges")}
+            </TacoButton>
+          </SaveChangeButtons>
+        )}
+      </ToolbarWrapper2>
+    </ToolbarWrapper>
+  );
+});
+
+TableToolbar.displayName = 'TableToolbar';
+
+export const TableToolbarComp = (function () {
+  const childrenMap = {
+    showRefresh: BoolControl,
+    showDownload: BoolControl,
+    showFilter: BoolControl,
+    columnSetting: BoolControl,
+    fixedToolbar: BoolControl,
+    // searchText: StringControl,
+    filter: stateComp<TableFilter>({ stackType: "and", filters: [] }),
+    position: dropdownControl(positionOptions, "below"),
+    columnSeparator: withDefault(StringControl, ','),
+    showUpdateButtons: withDefault(BoolControl, true),
+  };
+
+  return new ControlNodeCompBuilder(childrenMap, (props, dispatch) => {
+    return {
+      ...props,
+      onFilterChange: (filters: TableFilterDataType[], stackType: TableFilter["stackType"]) => {
+        dispatch(
+          changeChildAction(
+            "filter",
+            {
+              stackType: stackType,
+              filters: filters,
+            },
+            false
+          )
+        );
+      },
+    };
+  })
+    .setPropertyViewFn((children) => [
+      children.position.propertyView({ label: trans("table.position"), radioButton: true }),
+      children.fixedToolbar.propertyView({
+        label: trans("table.fixedToolbar"),
+        tooltip: trans("table.fixedToolbarTooltip")
+      }),
+      children.showUpdateButtons.propertyView({ label: trans("table.showUpdateButtons")}),
+      children.showFilter.propertyView({ label: trans("table.showFilter") }),
+      children.showRefresh.propertyView({ label: trans("table.showRefresh") }),
+      children.showDownload.propertyView({ label: trans("table.showDownload") }),
+      children.showDownload.getView() && children.columnSeparator.propertyView({
+        label: trans("table.columnSeparator"),
+        tooltip: trans("table.columnSeparatorTooltip"),
+      }),
+      children.columnSetting.propertyView({ label: trans("table.columnSetting") }),
+      /* children.searchText.propertyView({
+        label: trans("table.searchText"),
+        tooltip: trans("table.searchTextTooltip"),
+        placeholder: "{{input1.value}}",
+      }), */
+    ])
+    .build();
+})();
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableTypes.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableTypes.tsx
new file mode 100644
index 0000000000..f80bcf3b58
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableTypes.tsx
@@ -0,0 +1,282 @@
+import { ColumnListComp } from "comps/comps/tableComp/column/tableColumnListComp";
+import { TableToolbarComp } from "comps/comps/tableComp/tableToolbarComp";
+import { BoolControl, BoolPureControl } from "comps/controls/boolControl";
+import {
+  ArrayStringControl,
+  BoolCodeControl,
+  ColorOrBoolCodeControl,
+  HeightOrBoolCodeControl,
+  JSONObjectArrayControl,
+  RadiusControl,
+  StringControl,
+} from "comps/controls/codeControl";
+import { dropdownControl } from "comps/controls/dropdownControl";
+import { eventHandlerControl } from "comps/controls/eventHandlerControl";
+import { styleControl } from "comps/controls/styleControl";
+import { TableColumnStyle, TableRowStyle, TableStyle, TableToolbarStyle, TableHeaderStyle, TableSummaryRowStyle } from "comps/controls/styleControlConstants";
+import {
+  MultiCompBuilder,
+  stateComp,
+  UICompBuilder,
+  valueComp,
+  withContext,
+  withDefault,
+} from "comps/generators";
+import { uiChildren } from "comps/generators/uiCompBuilder";
+import { withIsLoadingMethod } from "comps/generators/withIsLoading";
+import { trans } from "i18n";
+import {
+  ConstructorToView,
+  RecordConstructorToComp,
+  RecordConstructorToView,
+} from "lowcoder-core";
+import { controlItem } from "lowcoder-design";
+import { JSONArray, JSONObject } from "util/jsonTypes";
+import { ExpansionControl } from "./expansionControl";
+import { PaginationControl } from "./paginationControl";
+import { SelectionControl } from "./selectionControl";
+import { AutoHeightControl } from "comps/controls/autoHeightControl";
+
+const editModeClickOptions = [
+  {
+    label: trans("table.singleClick"),
+    value: "single",
+  },
+  {
+    label: trans("table.doubleClick"),
+    value: "double",
+  },
+] as const;
+
+const summarRowsOptions = [
+  {
+    label: "1",
+    value: "1",
+  },
+  {
+    label: "2",
+    value: "2",
+  },
+  {
+    label: "3",
+    value: "3",
+  },
+] as const;
+
+const sizeOptions = [
+  {
+    label: trans("table.small"),
+    value: "small",
+  },
+  {
+    label: trans("table.middle"),
+    value: "middle",
+  },
+  {
+    label: trans("table.large"),
+    value: "large",
+  },
+] as const;
+
+export const TableEventOptions = [
+  {
+    label: trans("table.saveChanges"),
+    value: "saveChanges",
+    description: trans("table.saveChanges"),
+  },
+  {
+    label: trans("table.cancelChanges"),
+    value: "cancelChanges",
+    description: trans("table.cancelChanges"),
+  },
+  {
+    label: trans("table.rowSelectChange"),
+    value: "rowSelectChange",
+    description: trans("table.rowSelectChange"),
+  },
+  {
+    label: trans("table.rowClick"),
+    value: "rowClick",
+    description: trans("table.rowClick"),
+  },
+  {
+    label: trans("table.rowExpand"),
+    value: "rowExpand",
+    description: trans("table.rowExpand"),
+  },
+  {
+    label: trans("table.rowShrink"),
+    value: "rowShrink",
+    description: trans("table.rowShrink"),
+  },
+  {
+    label: trans("table.columnEdited"),
+    value: "columnEdited",
+    description: trans("table.columnEdited"),
+  },
+  {
+    label: trans("table.search"),
+    value: "dataSearch",
+    description: trans("table.search"),
+  },
+  {
+    label: trans("table.download"),
+    value: "download",
+    description: trans("table.download"),
+  },
+  {
+    label: trans("table.filterChange"),
+    value: "filterChange",
+    description: trans("table.filterChange"),
+  },
+  {
+    label: trans("table.sortChange"),
+    value: "sortChange",
+    description: trans("table.sortChange"),
+  },
+  {
+    label: trans("table.pageChange"),
+    value: "pageChange",
+    description: trans("table.pageChange"),
+  },
+  {
+    label: trans("table.refresh"),
+    value: "refresh",
+    description: trans("table.refresh"),
+  },
+  {
+    label: trans("event.doubleClick"),
+    value: "doubleClick",
+    description: trans("event.doubleClickDesc"),
+  }
+] as const;
+
+export type TableEventOptionValues = typeof TableEventOptions[number]['value'];
+
+export type SortValue = {
+  column?: string;
+  desc?: boolean;
+};
+
+const TableEventControl = eventHandlerControl(TableEventOptions);
+
+const rowColorLabel = trans("table.rowColor");
+const RowColorTempComp = withContext(
+  new MultiCompBuilder({
+    color: ColorOrBoolCodeControl,
+  }, (props) => props.color)
+    .setPropertyViewFn((children) =>
+      children.color.propertyView({
+        label: rowColorLabel,
+        tooltip: trans("table.rowColorDesc"),
+      })
+    )
+    .build(),
+  ["currentRow", "currentIndex", "currentOriginalIndex", "columnTitle"] as const
+);
+
+// @ts-ignore
+export class RowColorComp extends RowColorTempComp {
+  override getPropertyView() {
+    return controlItem({ filterText: rowColorLabel }, super.getPropertyView());
+  }
+}
+
+// fixme, should be infer from RowColorComp, but withContext type incorrect
+export type RowColorViewType = (param: {
+  currentRow: any;
+  currentIndex: number;
+  currentOriginalIndex: number | string;
+  columnTitle: string;
+}) => string;
+
+const rowHeightLabel = trans("table.rowHeight");
+const RowHeightTempComp = withContext(
+  new MultiCompBuilder({
+    height: HeightOrBoolCodeControl,
+  }, (props) => props.height)
+    .setPropertyViewFn((children) =>
+      children.height.propertyView({
+        label: rowHeightLabel,
+        tooltip: trans("table.rowHeightDesc"),
+      })
+    )
+    .build(),
+  ["currentRow", "currentIndex", "currentOriginalIndex", "columnTitle"] as const
+);
+
+// @ts-ignore
+export class RowHeightComp extends RowHeightTempComp {
+  override getPropertyView() {
+    return controlItem({ filterText: rowHeightLabel }, super.getPropertyView());
+  }
+}
+
+// fixme, should be infer from RowHeightComp, but withContext type incorrect
+export type RowHeightViewType = (param: {
+  currentRow: any;
+  currentIndex: number;
+  currentOriginalIndex: number | string;
+  columnTitle: string;
+}) => string;
+
+const tableChildrenMap = {
+  // hideBordered: BoolControl,
+  showHeaderGridBorder: BoolControl,
+  showRowGridBorder: withDefault(BoolControl,true),
+  showHRowGridBorder: withDefault(BoolControl,true),
+  hideHeader: BoolControl,
+  fixedHeader: BoolControl,
+  autoHeight: withDefault(AutoHeightControl, "auto"),
+  showVerticalScrollbar: BoolControl,
+  showHorizontalScrollbar: BoolControl,
+  data: withIsLoadingMethod(JSONObjectArrayControl),
+  newData: stateComp<JSONArray>([]),
+  columns: ColumnListComp,
+  size: dropdownControl(sizeOptions, "middle"),
+  selection: SelectionControl,
+  pagination: PaginationControl,
+  sort: valueComp<Array<SortValue>>([]),
+  toolbar: TableToolbarComp,
+  showSummary: BoolControl,
+  summaryRows: dropdownControl(summarRowsOptions, "1"),
+  style: styleControl(TableStyle, 'style'),
+  rowStyle: styleControl(TableRowStyle, 'rowStyle'),
+  summaryRowStyle: styleControl(TableSummaryRowStyle, 'summaryRowStyle'),
+  toolbarStyle: styleControl(TableToolbarStyle, 'toolbarStyle'),
+  hideToolbar: withDefault(BoolControl,false),
+  headerStyle: styleControl(TableHeaderStyle, 'headerStyle'),
+  searchText: StringControl,
+  columnsStyle: styleControl(TableColumnStyle, 'columnsStyle'),
+  viewModeResizable: BoolControl,
+  visibleResizables: BoolControl,
+  // sample data for regenerating columns
+  dataRowExample: stateComp<JSONObject | null>(null),
+  onEvent: TableEventControl,
+  loading: BoolCodeControl,
+  rowColor: RowColorComp,
+  rowAutoHeight: withDefault(AutoHeightControl, "auto"),
+  tableAutoHeight: withDefault(AutoHeightControl, "auto"),
+  rowHeight: RowHeightComp,
+  dynamicColumn: BoolPureControl,
+  // todo: support object config
+  dynamicColumnConfig: ArrayStringControl,
+  expansion: ExpansionControl,
+  selectedCell: stateComp<JSONObject>({}),
+  inlineAddNewRow: BoolControl,
+  editModeClicks: dropdownControl(editModeClickOptions, "single"),
+  currentExpandedRows: stateComp<string[]>([]),
+};
+
+export const TableInitComp = (function () {
+  return new UICompBuilder(tableChildrenMap, () => {
+    return <></>;
+  })
+    .setPropertyViewFn(() => <></>)
+    .build();
+})();
+
+const uiChildrenMap = uiChildren(tableChildrenMap);
+export type TableChildrenType = RecordConstructorToComp<typeof uiChildrenMap>;
+export type TableChildrenView = RecordConstructorToView<typeof uiChildrenMap>;
+export type TableOnEventView = ConstructorToView<typeof TableEventControl>;
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableUtils.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableUtils.tsx
new file mode 100644
index 0000000000..025e91e51e
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableUtils.tsx
@@ -0,0 +1,492 @@
+import {
+  ColumnType,
+  FilterValue,
+  SorterResult,
+  TableCurrentDataSource,
+  TablePaginationConfig,
+} from "antd/es/table/interface";
+import type { SortOrder } from "antd/es/table/interface";
+import { __COLUMN_DISPLAY_VALUE_FN } from "comps/comps/tableComp/column/columnTypeCompBuilder";
+import { CellColorViewType, RawColumnType, Render } from "comps/comps/tableComp/column/tableColumnComp";
+import { TableFilter, tableFilterOperatorMap } from "comps/comps/tableComp/tableToolbarComp";
+import { SortValue, TableOnEventView } from "comps/comps/tableComp/tableTypes";
+import _ from "lodash";
+import { changeChildAction, CompAction, NodeToValue } from "lowcoder-core";
+import { EditableIcon } from "lowcoder-design";
+import { tryToNumber } from "util/convertUtils";
+import { JSONObject, JSONValue } from "util/jsonTypes";
+import { StatusType } from "./column/columnTypeComps/columnStatusComp";
+import { ColumnListComp, tableDataRowExample } from "./column/tableColumnListComp";
+import { TableColumnLinkStyleType, TableColumnStyleType } from "comps/controls/styleControlConstants";
+import Tooltip from "antd/es/tooltip";
+import InfoCircleOutlined from "@ant-design/icons/InfoCircleOutlined";
+import { EMPTY_ROW_KEY } from "./tableCompView";
+import dayjs from "dayjs";
+
+export const COLUMN_CHILDREN_KEY = "children";
+export const OB_ROW_ORI_INDEX = "__ob_origin_index";
+export const OB_ROW_RECORD = "__ob_origin_record";
+
+export const COL_MIN_WIDTH = 55;
+export const COL_MAX_WIDTH = 500;
+
+/**
+ * Add __originIndex__, mainly for the logic of the default key
+ */
+export type RecordType = JSONObject & { [OB_ROW_ORI_INDEX]: string };
+
+export function filterData(
+  data: Array<RecordType>,
+  searchValue: string,
+  filter: TableFilter,
+  showFilter: boolean
+) {
+  let resultData = data;
+  if (searchValue) {
+    resultData = resultData.filter((row) => {
+      let searchLower = searchValue?.toLowerCase();
+      if (!searchLower) {
+        return true;
+      } else {
+        return Object.values(row).find((v) => v?.toString().toLowerCase().includes(searchLower));
+      }
+    });
+  }
+  if (showFilter && filter.filters.length > 0) {
+    resultData = resultData.filter((row) => {
+      // filter
+      for (let f of filter.filters) {
+        const columnValue = row[f.columnKey];
+        const result = tableFilterOperatorMap[f.operator].filter(f.filterValue, columnValue);
+        if (filter.stackType === "or" && result) {
+          // one condition is met
+          return true;
+        } else if (filter.stackType === "and" && !result) {
+          // one condition is not met
+          return false;
+        }
+      }
+      if (filter.filters.length === 0) {
+        return true;
+      } else if (filter.stackType === "and") {
+        return true;
+      } else if (filter.stackType === "or") {
+        return false;
+      }
+      return true;
+    });
+  }
+  return resultData;
+}
+
+export function sortData(
+  data: Array<JSONObject>,
+  columns: Record<string, { sortable: boolean }>, // key: dataIndex
+  sorter: Array<SortValue>
+): Array<RecordType> {
+  let resultData: Array<RecordType> = data.map((row, index) => ({
+    ...row,
+    [OB_ROW_ORI_INDEX]: index + "",
+  }));
+  if (sorter.length > 0) {
+    const [sortColumns, sortMethods] = _(sorter)
+      .filter((s) => {
+        return !!s.column && columns[s.column]?.sortable;
+      })
+      .map((s) => [s.column, s.desc ? "desc" : "asc"] as const)
+      .unzip()
+      .value() as [string[], ("desc" | "asc")[]];
+    resultData = _.orderBy(
+      resultData,
+      sortColumns.map((colName) => {
+        return (obj) => {
+          const val = obj[colName];
+          if (typeof val === "string") {
+            return val.toLowerCase();
+          } else {
+            return val;
+          }
+        };
+      }),
+      sortMethods
+    );
+  }
+  return resultData;
+}
+
+export function columnHide({
+  hide,
+  tempHide,
+  enableColumnSetting,
+}: {
+  hide: boolean;
+  tempHide: boolean;
+  enableColumnSetting: boolean;
+}) {
+  if (enableColumnSetting) {
+    return tempHide || hide;
+  } else {
+    return hide;
+  }
+}
+
+export function buildOriginIndex(index: string, childIndex: string) {
+  return index + "-" + childIndex;
+}
+
+export function tranToTableRecord(dataObj: JSONObject, index: string | number): RecordType {
+  const indexString = index + "";
+  if (Array.isArray(dataObj[COLUMN_CHILDREN_KEY])) {
+    return {
+      ...dataObj,
+      [OB_ROW_ORI_INDEX]: indexString,
+      children: dataObj[COLUMN_CHILDREN_KEY].map((child: any, i: number) =>
+        tranToTableRecord(child, buildOriginIndex(indexString, i + ""))
+      ),
+    };
+  }
+  return {
+    ...dataObj,
+    [OB_ROW_ORI_INDEX]: indexString,
+  };
+}
+
+export function getOriDisplayData(
+  data: Array<RecordType>,
+  pageSize: number,
+  columns: Array<{ dataIndex: string; render: NodeToValue<ReturnType<Render["node"]>> }>
+) {
+  return data.map((row, idx) => {
+    const displayData: RecordType = { [OB_ROW_ORI_INDEX]: row[OB_ROW_ORI_INDEX] };
+    columns.forEach((col) => {
+      // if (!row.hasOwnProperty(col.dataIndex)) return;
+      const node = col.render.wrap({
+        currentCell: row[col.dataIndex],
+        currentRow: _.omit(row, OB_ROW_ORI_INDEX),
+        currentIndex: idx % pageSize,
+        currentOriginalIndex: row[OB_ROW_ORI_INDEX],
+      }) as any;
+      if (Array.isArray(row[COLUMN_CHILDREN_KEY])) {
+        displayData[COLUMN_CHILDREN_KEY] = getOriDisplayData(
+          row[COLUMN_CHILDREN_KEY] as Array<RecordType>,
+          pageSize,
+          columns
+        );
+      }
+      const colValue = node.comp[__COLUMN_DISPLAY_VALUE_FN](node.comp);
+      if (colValue !== null) {
+        displayData[col.dataIndex] = colValue;
+      }
+    });
+    return displayData;
+  });
+}
+
+export function transformDispalyData(
+  oriDisplayData: JSONObject[],
+  dataIndexTitleDict: _.Dictionary<string>
+): JSONObject[] {
+  return oriDisplayData.map((row) => {
+    const transData = _(row)
+      .omit(OB_ROW_ORI_INDEX)
+      .mapKeys((value, key) => dataIndexTitleDict[key] || key)
+      .value();
+    if (Array.isArray(row[COLUMN_CHILDREN_KEY])) {
+      return {
+        ...transData,
+        [COLUMN_CHILDREN_KEY]: transformDispalyData(
+          row[COLUMN_CHILDREN_KEY] as JSONObject[],
+          dataIndexTitleDict
+        ),
+      };
+    }
+    return transData;
+  });
+}
+
+export type ColumnsAggrData = Record<string, Record<string, JSONValue> & { compType: string }>;
+
+export function getColumnsAggr(
+  oriDisplayData: JSONObject[],
+  dataIndexWithParamsDict: NodeToValue<
+    ReturnType<InstanceType<typeof ColumnListComp>["withParamsNode"]>
+  >
+): ColumnsAggrData {
+  return _.mapValues(dataIndexWithParamsDict, (withParams, dataIndex) => {
+    const compType = (withParams.wrap() as any).compType;
+    const res: Record<string, JSONValue> & { compType: string } = { compType };
+    if (compType === "tag") {
+      res.uniqueTags = _(oriDisplayData)
+        .map((row) => row[dataIndex]!)
+        .filter((tag) => !!tag)
+        .uniq()
+        .value();
+    } else if (compType === "badgeStatus") {
+      res.uniqueStatus = _(oriDisplayData)
+        .map((row) => {
+          const value = row[dataIndex] as any;
+          if (value.split(" ")[1]) {
+            return {
+              status: value.slice(0, value.indexOf(" ")),
+              text: value.slice(value.indexOf(" ") + 1),
+            };
+          } else {
+            return {
+              status: value,
+              text: "",
+            };
+          }
+        })
+        .uniqBy("text")
+        .value();
+    }
+    return res;
+  });
+}
+
+function renderTitle(props: { title: string; tooltip: string; editable: boolean }) {
+  const { title, tooltip, editable } = props;
+  return (
+    <div>
+      <Tooltip title={tooltip}>
+        <span style={{borderBottom: tooltip ? '1px dotted' : ''}}>
+          {title}
+        </span>
+      </Tooltip>
+      {editable && <EditableIcon style={{ verticalAlign: "baseline", marginLeft: "4px" }} />}
+    </div>
+  );
+}
+
+function getInitialColumns(
+  columnsAggrData: ColumnsAggrData,
+  customColumns: string[],
+) {
+  let initialColumns = [];
+  Object.keys(columnsAggrData).forEach(column => {
+    if(customColumns.includes(column)) return;
+    initialColumns.push({
+      label: column,
+      value: `{{currentRow.${column}}}`
+    });
+  });
+  initialColumns.push({
+    label: 'Select with handlebars',
+    value: '{{currentCell}}',
+  })
+  return initialColumns;
+}
+
+export type CustomColumnType<RecordType> = ColumnType<RecordType> & {
+  onWidthResize?: (width: number) => void;
+  titleText: string;
+  style: TableColumnStyleType;
+  linkStyle: TableColumnLinkStyleType;
+  cellColorFn: CellColorViewType;
+};
+
+/**
+ * convert column in raw format into antd format
+ */
+export function columnsToAntdFormat(
+  columns: Array<RawColumnType>,
+  sort: SortValue[],
+  enableColumnSetting: boolean,
+  size: string,
+  dynamicColumn: boolean,
+  dynamicColumnConfig: Array<string>,
+  columnsAggrData: ColumnsAggrData,
+  editMode: string,
+  onTableEvent: (eventName: any) => void,
+): Array<CustomColumnType<RecordType>> {
+  const customColumns = columns.filter(col => col.isCustom).map(col => col.dataIndex);
+  const initialColumns = getInitialColumns(columnsAggrData, customColumns);
+  const sortMap: Map<string | undefined, SortOrder> = new Map(
+    sort.map((s) => [s.column, s.desc ? "descend" : "ascend"])
+  );
+  const sortedColumns = _.sortBy(columns, (c) => {
+    if (c.fixed === "left") {
+      return -1;
+    } else if (c.fixed === "right") {
+      return Number.MAX_SAFE_INTEGER;
+    } else if (dynamicColumnConfig.length > 0) {
+      // sort by dynamic config array
+      const index = dynamicColumnConfig.indexOf(c.isCustom ? c.title : c.dataIndex);
+      if (index >= 0) {
+        return index;
+      }
+    }
+    return 0;
+  });
+  return sortedColumns.flatMap((column, mIndex) => {
+    if (
+      columnHide({
+        hide: column.hide,
+        tempHide: column.tempHide,
+        enableColumnSetting: enableColumnSetting,
+      })
+    ) {
+      return [];
+    }
+    if (
+      dynamicColumn &&
+      dynamicColumnConfig.length > 0 &&
+      !dynamicColumnConfig.includes(column.isCustom ? column.title : column.dataIndex)
+    ) {
+      return [];
+    }
+    const tags = ((columnsAggrData[column.dataIndex] ?? {}).uniqueTags ?? []) as string[];
+    const status = ((columnsAggrData[column.dataIndex] ?? {}).uniqueStatus ?? []) as {
+      text: string;
+      status: StatusType;
+    }[];
+    const title = renderTitle({ title: column.title, tooltip: column.titleTooltip, editable: column.editable });
+
+    return {
+      key: `${column.dataIndex}-${mIndex}`,
+      title: column.showTitle ? title : '',
+      titleText: column.title,
+      dataIndex: column.dataIndex,
+      align: column.align,
+      width: column.autoWidth === "auto" ? 0 : column.width,
+      fixed: column.fixed === "close" ? false : column.fixed,
+      style: {
+        background: column.background,
+        margin: column.margin,
+        text: column.text,
+        border: column.border,
+        radius: column.radius,
+        textSize: column.textSize,
+        textWeight: column.textWeight,
+        fontStyle:column.fontStyle,
+        fontFamily: column.fontFamily,
+        borderWidth: column.borderWidth,
+      },
+      linkStyle: {
+        text: column.linkColor,
+        hoverText: column.linkHoverColor,
+        activeText: column.linkActiveColor,
+      },
+      cellColorFn: column.cellColor,
+      onWidthResize: column.onWidthResize,
+      render: (value: any, record: RecordType, index: number) => {
+        const row = _.omit(record, OB_ROW_ORI_INDEX);
+        return column
+          .render(
+            {
+              currentCell: value,
+              currentRow: row,
+              currentIndex: index,
+              currentOriginalIndex: tryToNumber(record[OB_ROW_ORI_INDEX]),
+              initialColumns,
+            },
+            String(record[OB_ROW_ORI_INDEX])
+          )
+          .getView()
+          .view({
+            editable: record[OB_ROW_ORI_INDEX].startsWith(EMPTY_ROW_KEY) || column.editable,
+            tableSize: size,
+            candidateTags: tags,
+            candidateStatus: status,
+            textOverflow: column.textOverflow,
+            cellTooltip: column.cellTooltip({
+              currentCell: value,
+              currentRow: row,
+              currentIndex: index,
+            }),
+            editMode,
+            onTableEvent,
+            cellIndex: `${column.dataIndex}-${index}`,
+          });
+      },
+      ...(column.sortable
+        ? {
+            sorter: {
+              multiple: (sortedColumns.length - mIndex) + 1,
+              compare: column.columnType === 'date' || column.columnType === 'dateTime'
+                ? (a,b) => {
+                  return dayjs(a[column.dataIndex] as string).unix() - dayjs(b[column.dataIndex] as string).unix();
+                }
+              : undefined
+            },
+            sortOrder: sortMap.get(column.dataIndex),
+            showSorterTooltip: false,
+          }
+        : {}),
+    };
+  });
+}
+
+function getSortValue(sortResult: SorterResult<RecordType>) {
+  return sortResult.column?.dataIndex
+    ? {
+        column: sortResult.column.dataIndex.toString(),
+        desc: sortResult.order === "descend",
+      }
+    : null;
+}
+
+export function onTableChange(
+  pagination: TablePaginationConfig,
+  filters: Record<string, FilterValue | null>,
+  sorter: SorterResult<RecordType> | SorterResult<RecordType>[],
+  extra: TableCurrentDataSource<RecordType>,
+  dispatch: (action: CompAction<JSONValue>) => void,
+  onEvent: TableOnEventView
+) {
+  if (extra.action === "sort") {
+    let sortValues: SortValue[] = [];
+    if (Array.isArray(sorter)) {
+      // multi-column sort
+      sorter.forEach((s) => {
+        const v = getSortValue(s);
+        v && sortValues.push(v);
+      });
+    } else {
+      const v = getSortValue(sorter);
+      v && sortValues.push(v);
+    }
+    dispatch(changeChildAction("sort", sortValues, true));
+    onEvent("sortChange");
+  }
+}
+
+export function calcColumnWidth(columnKey: string, data: Array<JSONObject>) {
+  const getWidth = (str: string) => {
+    const byteLength = new Blob([str]).size;
+    return str.length === byteLength ? str.length * 10 : str.length * 20;
+  };
+  const cellWidth =
+    _.max(
+      data.map((d) => {
+        const cellValue = d[columnKey];
+        if (!cellValue) {
+          return COL_MIN_WIDTH;
+        }
+        return getWidth(cellValue.toString());
+      })
+    ) || 0;
+  const titleWidth = getWidth(columnKey);
+  return Math.max(Math.min(COL_MAX_WIDTH, Math.max(titleWidth, cellWidth) + 10), COL_MIN_WIDTH);
+}
+
+export function genSelectionParams(
+  filterData: RecordType[],
+  selection: string
+): Record<string, unknown> | undefined {
+  const idx = filterData?.findIndex((row) => row[OB_ROW_ORI_INDEX] === selection);
+  if (!Boolean(filterData) || idx < 0) {
+    return undefined;
+  }
+  const currentRow = filterData[idx];
+  return {
+    currentRow: _.omit(currentRow, OB_ROW_ORI_INDEX),
+    currentIndex: idx,
+    currentOriginalIndex: tryToNumber(currentRow[OB_ROW_ORI_INDEX]),
+  };
+}
+
+export function supportChildrenTree(data: Array<JSONObject>) {
+  const rowSample = tableDataRowExample(data) as any;
+  return rowSample && Array.isArray(rowSample[COLUMN_CHILDREN_KEY]);
+}

From 9ff640de0969e80b972f73f7b00cc2aee2bd46dd Mon Sep 17 00:00:00 2001
From: Faran Javed <faran1997@outlook.com>
Date: Tue, 5 Aug 2025 21:24:23 +0500
Subject: [PATCH 2/7] register table lite comp

---
 client/packages/lowcoder/src/comps/index.tsx   | 18 ++++++++++++++++++
 .../lowcoder/src/comps/uiCompRegistry.ts       |  1 +
 .../packages/lowcoder/src/i18n/locales/en.ts   |  4 ++++
 .../src/pages/editor/editorConstants.tsx       |  1 +
 4 files changed, 24 insertions(+)

diff --git a/client/packages/lowcoder/src/comps/index.tsx b/client/packages/lowcoder/src/comps/index.tsx
index 609ddf5b0a..0bbf0b7312 100644
--- a/client/packages/lowcoder/src/comps/index.tsx
+++ b/client/packages/lowcoder/src/comps/index.tsx
@@ -122,6 +122,8 @@ import {
 import { ModuleComp } from "./comps/moduleComp/moduleComp";
 import { TableComp } from "./comps/tableComp/tableComp";
 import { defaultTableData } from "./comps/tableComp/mockTableComp";
+import { TableLiteComp } from "./comps/tableLiteComp/tableComp";
+import { defaultTableData as defaultTableLiteData } from "./comps/tableLiteComp/mockTableComp";
 import { ContainerComp, defaultContainerData } from "./comps/containerComp/containerComp";
 import { ColumnLayoutComp } from "./comps/columnLayout/columnLayout";
 import { TabbedContainerComp } from "./comps/tabs/tabbedContainerComp";
@@ -489,6 +491,22 @@ export var uiCompMap: Registry = {
     defaultDataFn: defaultTableData,
   },
 
+  tableLite: {
+    name: trans("uiComp.tableLiteCompName"),
+    enName: "Table Lite",
+    description: trans("uiComp.tableLiteCompDesc"),
+    categories: ["dashboards", "projectmanagement"],
+    icon: TableCompIcon,
+    keywords: trans("uiComp.tableLiteCompKeywords"),
+    comp: TableLiteComp,
+    layoutInfo: {
+      w: 12,
+      h: 40,
+    },
+    withoutLoading: true,
+    defaultDataFn: defaultTableLiteData,
+  },
+
   pivotTable: {
     name: trans("uiComp.pivotTableCompName"),
     enName: "pivotTable",
diff --git a/client/packages/lowcoder/src/comps/uiCompRegistry.ts b/client/packages/lowcoder/src/comps/uiCompRegistry.ts
index 07f0e54b43..f8e09763ce 100644
--- a/client/packages/lowcoder/src/comps/uiCompRegistry.ts
+++ b/client/packages/lowcoder/src/comps/uiCompRegistry.ts
@@ -93,6 +93,7 @@ export type UICompType =
   | "dropdown"
   | "text"
   | "table"
+  | "tableLite"
   | "image"
   | "progress"
   | "progressCircle"
diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts
index 80f6672884..1b3ecab91c 100644
--- a/client/packages/lowcoder/src/i18n/locales/en.ts
+++ b/client/packages/lowcoder/src/i18n/locales/en.ts
@@ -1133,6 +1133,10 @@ export const en = {
     "tableCompDesc": "A rich table component for displaying data in a structured table format, with options for sorting and filtering, tree Data display and extensible Rows.",
     "tableCompKeywords": "table, data, sorting, filtering",
 
+    "tableLiteCompName": "Table Lite",
+    "tableLiteCompDesc": "A lightweight, high-performance table component optimized for displaying many rows fast, with essential sorting and filtering capabilities.",
+    "tableLiteCompKeywords": "table, data, sorting, filtering, performance, lite",
+
     "imageCompName": "Image",
     "imageCompDesc": "A component for displaying images, supporting various formats based on URI or Base64 Data.",
     "imageCompKeywords": "image, display, media, Base64",
diff --git a/client/packages/lowcoder/src/pages/editor/editorConstants.tsx b/client/packages/lowcoder/src/pages/editor/editorConstants.tsx
index beea9cae7a..54cd5faaf4 100644
--- a/client/packages/lowcoder/src/pages/editor/editorConstants.tsx
+++ b/client/packages/lowcoder/src/pages/editor/editorConstants.tsx
@@ -237,6 +237,7 @@ export const CompStateIcon: {
   signature: <MemoizedIcon Icon={SignatureCompIconSmall} />,
   step: <MemoizedIcon Icon={StepCompIconSmall} />,
   table: <MemoizedIcon Icon={TableCompIconSmall} />,
+  tableLite: <MemoizedIcon Icon={TableCompIconSmall} />,
   text: <MemoizedIcon Icon={TextCompIconSmall} />,
   multiTags: <MemoizedIcon Icon={TagsCompIconSmall} />, 
   timeline: <MemoizedIcon Icon={TimeLineCompIconSmall} />,

From 491782f36f24e256491afe849770baa00503d697 Mon Sep 17 00:00:00 2001
From: Faran Javed <faran1997@outlook.com>
Date: Tue, 5 Aug 2025 22:36:02 +0500
Subject: [PATCH 3/7] remove expansion data layer

---
 .../comps/comps/tableLiteComp/tableComp.tsx   | 79 +------------------
 1 file changed, 2 insertions(+), 77 deletions(-)

diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx
index 3f3b324115..c1c36cb932 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx
@@ -53,7 +53,7 @@ import {
 import { saveDataAsFile } from "util/fileUtils";
 import { JSONObject, JSONValue } from "util/jsonTypes";
 import { lastValueIfEqual, shallowEqual } from "util/objectUtils";
-import { IContainer } from "../containerBase";
+
 import { getSelectedRowKeys } from "./selectionControl";
 import { compTablePropertyView } from "./tablePropertyView";
 import { RowColorComp, RowHeightComp, SortValue, TableChildrenView, TableInitComp } from "./tableTypes";
@@ -61,7 +61,7 @@ import { RowColorComp, RowHeightComp, SortValue, TableChildrenView, TableInitCom
 import { useContext, useState } from "react";
 import { EditorContext } from "comps/editorState";
 
-export class TableImplComp extends TableInitComp implements IContainer {
+export class TableImplComp extends TableInitComp {
   private prevUnevaledValue?: string;
   readonly filterData: RecordType[] = [];
   readonly columnAggrData: ColumnsAggrData = {};
@@ -73,29 +73,6 @@ export class TableImplComp extends TableInitComp implements IContainer {
   getTableAutoHeight() {
     return this.children.autoHeight.getView();
   }
-  
-  private getSlotContainer() {
-    return this.children.expansion.children.slot.getSelectedComp().getComp().children.container;
-  }
-
-  findContainer(key: string) {
-    return this.getSlotContainer().findContainer(key);
-  }
-
-  getCompTree() {
-    return this.getSlotContainer().getCompTree();
-  }
-
-  getPasteValue(nameGenerator: NameGenerator) {
-    return {
-      ...this.toJsonValue(),
-      expansion: this.children.expansion.getPasteValue(nameGenerator),
-    };
-  }
-
-  realSimpleContainer(key?: string) {
-    return this.getSlotContainer().realSimpleContainer(key);
-  }
 
   downloadData(fileName: string) {
     saveDataAsFile({
@@ -256,21 +233,6 @@ export class TableImplComp extends TableInitComp implements IContainer {
       );
       needMoreEval = true;
     }
-
-    let params = comp.children.expansion.children.slot.getCachedParams(newSelection);
-    if (selectionChanged || _.isNil(params) || dataChanged) {
-      params =
-        _.isNil(params) || dataChanged
-          ? genSelectionParams(comp.filterData, newSelection)
-          : undefined;
-      comp = comp.setChild(
-        "expansion",
-        comp.children.expansion.reduce(
-          comp.children.expansion.setSelectionAction(newSelection, params)
-        )
-      );
-      needMoreEval = true;
-    }
     if (action.type === CompActionTypes.UPDATE_NODES_V2 && needMoreEval) {
       setTimeout(() => comp.dispatch(onlyEvalAction()));
     }
@@ -724,24 +686,6 @@ TableTmpComp = withMethodExposing(TableTmpComp, [
       comp.children.columns.dispatchClearInsertSet();
     },
   },
-  {
-    method: {
-      name: "setExpandedRows",
-      description: "",
-      params: [
-        { name: "expandedRows", type: "arrayString"},
-      ],
-    },
-    execute: (comp, values) => {
-      const expandedRows = values[0];
-      if (!isArray(expandedRows)) {
-        return Promise.reject("setExpandedRows function only accepts array of string i.e. ['1', '2', '3']")
-      }
-      if (expandedRows && isArray(expandedRows)) {
-        comp.children.currentExpandedRows.dispatchChangeValueAction(expandedRows as string[]);
-      }
-    },
-  }
 ]);
 
 // exposing data
@@ -996,24 +940,5 @@ export const TableLiteComp = withExposingConfigs(TableTmpComp, [
     },
     trans("table.selectedCellDesc")
   ),
-  depsConfig({
-    name: "currentExpandedRow",
-    desc: trans("table.sortDesc"),
-    depKeys: ["currentExpandedRows"],
-    func: (input) => {
-      if (input.currentExpandedRows.length > 0) {
-        return input.currentExpandedRows[input.currentExpandedRows.length - 1];
-      }
-      return "";
-    },
-  }),
-  depsConfig({
-    name: "currentExpandedRows",
-    desc: trans("table.sortDesc"),
-    depKeys: ["currentExpandedRows"],
-    func: (input) => {
-      return input.currentExpandedRows;
-    },
-  }),
   new NameConfig("data", trans("table.dataDesc")),
 ]);

From b43d31e3b21aaa329fce52c9db7d999654a4eaca Mon Sep 17 00:00:00 2001
From: Faran Javed <faran1997@outlook.com>
Date: Wed, 6 Aug 2025 13:05:11 +0500
Subject: [PATCH 4/7] remove expansion functionality from table

---
 .../comps/tableLiteComp/expansionControl.tsx  | 116 ------------------
 .../comps/tableLiteComp/tableCompView.tsx     |  47 +------
 .../comps/tableLiteComp/tablePropertyView.tsx |   1 -
 .../comps/tableLiteComp/tableSummaryComp.tsx  |   7 +-
 .../comps/tableLiteComp/tableToolbarComp.tsx  |   5 +-
 .../comps/comps/tableLiteComp/tableTypes.tsx  |  13 --
 6 files changed, 10 insertions(+), 179 deletions(-)
 delete mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/expansionControl.tsx

diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/expansionControl.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/expansionControl.tsx
deleted file mode 100644
index 98044cd318..0000000000
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/expansionControl.tsx
+++ /dev/null
@@ -1,116 +0,0 @@
-import {
-  ContainerBaseProps,
-  gridItemCompToGridItems,
-  InnerGrid,
-} from "comps/comps/containerComp/containerView";
-import { BoolControl } from "comps/controls/boolControl";
-import { SlotControl } from "comps/controls/slotControl";
-import { withSelectedMultiContext } from "comps/generators";
-import { ControlItemCompBuilder } from "comps/generators/controlCompBuilder";
-import { BackgroundColorContext } from "comps/utils/backgroundColorContext";
-import { trans } from "i18n";
-import _ from "lodash";
-import { ConstructorToView, wrapChildAction } from "lowcoder-core";
-import { createContext, useContext, useCallback } from "react";
-import { tryToNumber } from "util/convertUtils";
-import { SimpleContainerComp } from "../containerBase/simpleContainerComp";
-import { OB_ROW_ORI_INDEX, RecordType } from "./tableUtils";
-import { NameGenerator } from "comps/utils";
-import { JSONValue } from "util/jsonTypes";
-
-const ContextSlotControl = withSelectedMultiContext(SlotControl);
-export const ExpandViewContext = createContext(false);
-
-const ContainerView = (props: ContainerBaseProps) => {
-  return <InnerGrid {...props} emptyRows={15} autoHeight />;
-};
-
-function ExpandView(props: { containerProps: ConstructorToView<typeof SimpleContainerComp> }) {
-  const { containerProps } = props;
-  const background = useContext(BackgroundColorContext);
-  return (
-    <ContainerView
-      {...containerProps}
-      isDroppable={false}
-      isDraggable={false}
-      isResizable={false}
-      isSelectable={false}
-      bgColor={background}
-      items={gridItemCompToGridItems(containerProps.items)}
-      hintPlaceholder=""
-      containerPadding={[4, 4]}
-    />
-  );
-}
-
-let ExpansionControlTmp = (function () {
-  const label = trans("table.expandable");
-  return new ControlItemCompBuilder(
-    {
-      expandable: BoolControl,
-      slot: ContextSlotControl,
-    },
-    () => ({ expandableConfig: {}, expandModalView: null })
-    )
-    .setControlItemData({ filterText: label })
-    .setPropertyViewFn((children, dispatch) => {
-      return (
-        <> 
-          {children.expandable.propertyView({ label })}
-          {children.expandable.getView() &&
-            children.slot
-              .getSelectedComp()
-              .getComp()
-              .propertyView({ buttonText: trans("table.configExpandedView") })}
-        </>
-      );
-    })
-    .build();
-})();
-
-export class ExpansionControl extends ExpansionControlTmp {
-  getView() {
-    if (!this.children.expandable.getView()) {
-      return { expandableConfig: {}, expandModalView: null };
-    }
-    const selectedContainer = this.children.slot.getSelectedComp();
-    return {
-      expandableConfig: {
-        expandedRowRender: (record: RecordType, index: number) => {
-          const slotControl = this.children.slot.getView()(
-            {
-              currentRow: _.omit(record, OB_ROW_ORI_INDEX),
-              currentIndex: index,
-              currentOriginalIndex: tryToNumber(record[OB_ROW_ORI_INDEX]),
-            },
-            String(record[OB_ROW_ORI_INDEX])
-          );
-          const containerProps = slotControl.children.container.getView();
-          return (
-            <ExpandViewContext.Provider value={true}>
-              <ExpandView key={record[OB_ROW_ORI_INDEX]} containerProps={containerProps} />
-            </ExpandViewContext.Provider>
-          );
-        },
-      },
-      expandModalView: selectedContainer.getView(),
-    };
-  }
-
-  setSelectionAction(selection: string, params?: Record<string, unknown>) {
-    return wrapChildAction("slot", ContextSlotControl.setSelectionAction(selection, params));
-  }
-
-  getPasteValue(nameGenerator: NameGenerator): JSONValue {
-    return {
-      ...this.toJsonValue(),
-      slot: this.children.slot.getSelectedComp().getComp().getPasteValue(nameGenerator),
-    };
-  }
-
-  reduce(action: any) {
-    const comp = super.reduce(action);
-    // console.info("ExpansionControl reduce. action: ", action, "\nthis: ", this, "\ncomp: ", comp);
-    return comp;
-  }
-}
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableCompView.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableCompView.tsx
index dc6c88b0d1..020d6c215a 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableCompView.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableCompView.tsx
@@ -1,7 +1,7 @@
 import { default as Table, TableProps, ColumnType } from "antd/es/table";
-import { TableCellContext, TableRowContext } from "comps/comps/tableComp/tableContext";
-import { TableToolbar } from "comps/comps/tableComp/tableToolbarComp";
-import { RowColorViewType, RowHeightViewType, TableEventOptionValues } from "comps/comps/tableComp/tableTypes";
+import { TableCellContext, TableRowContext } from "comps/comps/tableLiteComp/tableContext";
+import { TableToolbar } from "comps/comps/tableLiteComp/tableToolbarComp";
+import { RowColorViewType, RowHeightViewType, TableEventOptionValues } from "comps/comps/tableLiteComp/tableTypes";
 import {
   COL_MIN_WIDTH,
   COLUMN_CHILDREN_KEY,
@@ -12,7 +12,7 @@ import {
   onTableChange,
   RecordType,
   supportChildrenTree,
-} from "comps/comps/tableComp/tableUtils";
+} from "comps/comps/tableLiteComp/tableUtils";
 import {
   handleToHoverRow,
   handleToSelectedRow,
@@ -870,14 +870,12 @@ export const TableCompView = React.memo((props: {
   const size = useMemo(() => compChildren.size.getView(), [compChildren.size]);
   const editModeClicks = useMemo(() => compChildren.editModeClicks.getView(), [compChildren.editModeClicks]);
   const onEvent = useMemo(() => compChildren.onEvent.getView(), [compChildren.onEvent]);
-  const currentExpandedRows = useMemo(() => compChildren.currentExpandedRows.getView(), [compChildren.currentExpandedRows]);
   const dynamicColumn = compChildren.dynamicColumn.getView();
   const dynamicColumnConfig = useMemo(
     () => compChildren.dynamicColumnConfig.getView(),
     [compChildren.dynamicColumnConfig]
   );
   const columnsAggrData = comp.columnAggrData;
-  const expansion = useMemo(() => compChildren.expansion.getView(), [compChildren.expansion]);
   const antdColumns = useMemo(
     () =>
       columnsToAntdFormat(
@@ -970,17 +968,7 @@ export const TableCompView = React.memo((props: {
     updateEmptyRows();
   }, [updateEmptyRows]);
 
-  useUpdateEffect(() => {
-    if (!isEqual(currentExpandedRows, expandedRowKeys)) {
-      compChildren.currentExpandedRows.dispatchChangeValueAction(expandedRowKeys);
-    }
-  }, [expandedRowKeys]);
 
-  useUpdateEffect(() => {
-    if (!isEqual(currentExpandedRows, expandedRowKeys)) {
-      setExpandedRowKeys(currentExpandedRows);
-    }
-  }, [currentExpandedRows]);
 
   const pageDataInfo = useMemo(() => {
     // Data pagination
@@ -1032,7 +1020,7 @@ export const TableCompView = React.memo((props: {
         total: pageDataInfo.total,
         current: pageDataInfo.current,
       }}
-      columns={columns}
+      columns={columns as any}
       onRefresh={() =>
         onRefresh(
           editorState.queryCompInfoList().map((info) => info.name),
@@ -1062,7 +1050,6 @@ export const TableCompView = React.memo((props: {
         tableSize={size}
         istoolbarPositionBelow={toolbar.position === "below"}
         multiSelectEnabled={compChildren.selection.children.mode.value === 'multiple'}
-        expandableRows={Boolean(expansion.expandModalView)}
         summaryRows={parseInt(summaryRows)}
         columns={columns}
         summaryRowStyle={summaryRowStyle}
@@ -1119,31 +1106,12 @@ export const TableCompView = React.memo((props: {
             $showHRowGridBorder={showHRowGridBorder}
           >
             <ResizeableTable<RecordType>
-              expandable={{
-                ...expansion.expandableConfig,
-                childrenColumnName: supportChildren
-                  ? COLUMN_CHILDREN_KEY
-                  : "OB_CHILDREN_KEY_PLACEHOLDER",
-                fixed: "left",
-                onExpand: (expanded) => {
-                  if (expanded) {
-                    handleChangeEvent('rowExpand')
-                  } else {
-                    handleChangeEvent('rowShrink')
-                  }
-                },
-                onExpandedRowsChange: (expandedRowKeys) => {
-                  setExpandedRowKeys(expandedRowKeys as unknown as string[]);
-                },
-                expandedRowKeys: expandedRowKeys,
-              }}
-              // rowKey={OB_ROW_ORI_INDEX}
               rowColorFn={compChildren.rowColor.getView() as any}
               rowHeightFn={compChildren.rowHeight.getView() as any}
               {...compChildren.selection.getView()(onEvent)}
               bordered={compChildren.showRowGridBorder.getView()}
               onChange={(pagination, filters, sorter, extra) => {
-                onTableChange(pagination, filters, sorter, extra, comp.dispatch, onEvent);
+                onTableChange(pagination, filters, sorter, extra, comp.dispatch, onEvent as any);
               }}
               showHeader={!compChildren.hideHeader.getView()}
               columns={antdColumns}
@@ -1163,9 +1131,6 @@ export const TableCompView = React.memo((props: {
               }}
               summary={summaryView}
             />
-            <SlotConfigContext.Provider value={{ modalWidth: width && Math.max(width, 300) }}>
-              {expansion.expandModalView}
-            </SlotConfigContext.Provider>
           </TableWrapper>
         </ScrollBar>
         {toolbar.position === "below" && !hideToolbar && (toolbar.fixedToolbar || (tableAutoHeight && showHorizontalScrollbar)) && toolbarView}
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tablePropertyView.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tablePropertyView.tsx
index 70bb8ce05a..403d962ff4 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tablePropertyView.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tablePropertyView.tsx
@@ -604,7 +604,6 @@ export function compTablePropertyView<T extends MultiBaseComp<TableChildrenType>
       {["logic", "both"].includes(editorModeStatus) && (
         <>
           <Section name={sectionNames.advanced}>
-            {comp.children.expansion.getPropertyView()}
             {comp.children.dynamicColumn.propertyView({ label: trans("table.dynamicColumn") })}
             {comp.children.dynamicColumn.getView() &&
               comp.children.dynamicColumnConfig.propertyView({
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableSummaryComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableSummaryComp.tsx
index e026d5aef4..ca0d3aaa75 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableSummaryComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableSummaryComp.tsx
@@ -200,7 +200,6 @@ TableSummaryCellView.displayName = 'TableSummaryCellView';
 
 export const TableSummary = memo(function TableSummary(props: {
   tableSize: string;
-  expandableRows: boolean;
   multiSelectEnabled: boolean;
   summaryRows: number;
   columns: ColumnComp[];
@@ -214,7 +213,6 @@ export const TableSummary = memo(function TableSummary(props: {
     summaryRows,
     summaryRowStyle,
     tableSize,
-    expandableRows,
     multiSelectEnabled,
     istoolbarPositionBelow,
     dynamicColumn,
@@ -229,14 +227,11 @@ export const TableSummary = memo(function TableSummary(props: {
         return dynamicColumnConfig.includes(colView.isCustom ? colView.title : colView.dataIndex)
       })
     }
-    if (expandableRows) {
-      cols.unshift(new ColumnComp({}));
-    }
     if (multiSelectEnabled) {
       cols.unshift(new ColumnComp({}));
     }
     return cols;
-  }, [columns, expandableRows, multiSelectEnabled, dynamicColumn, dynamicColumnConfig]);
+  }, [columns, multiSelectEnabled, dynamicColumn, dynamicColumnConfig]);
 
   const renderSummaryCell = useCallback((column: ColumnComp, rowIndex: number, index: number) => {
     const summaryColumn = column.children.summaryColumns.getView()[rowIndex].getView();
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableToolbarComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableToolbarComp.tsx
index 2969e3c46d..d4f58422f3 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableToolbarComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableToolbarComp.tsx
@@ -2,8 +2,9 @@ import { default as Button } from "antd/es/button";
 import { default as Pagination, PaginationProps } from "antd/es/pagination";
 import { default as Popover } from "antd/es/popover";
 import { ThemeDetail } from "api/commonSettingApi";
-import { ColumnCompType } from "comps/comps/tableComp/column/tableColumnComp";
-import { TableOnEventView } from "comps/comps/tableComp/tableTypes";
+import { ColumnCompType } from "comps/comps/tableLiteComp/column/tableColumnComp";
+import { ColumnComp } from "./column/tableColumnComp";
+import { TableOnEventView } from "comps/comps/tableLiteComp/tableTypes";
 import { BoolControl } from "comps/controls/boolControl";
 import { StringControl } from "comps/controls/codeControl";
 import { dropdownControl } from "comps/controls/dropdownControl";
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableTypes.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableTypes.tsx
index f80bcf3b58..e707c88bfa 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableTypes.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableTypes.tsx
@@ -32,7 +32,6 @@ import {
 } from "lowcoder-core";
 import { controlItem } from "lowcoder-design";
 import { JSONArray, JSONObject } from "util/jsonTypes";
-import { ExpansionControl } from "./expansionControl";
 import { PaginationControl } from "./paginationControl";
 import { SelectionControl } from "./selectionControl";
 import { AutoHeightControl } from "comps/controls/autoHeightControl";
@@ -99,16 +98,6 @@ export const TableEventOptions = [
     value: "rowClick",
     description: trans("table.rowClick"),
   },
-  {
-    label: trans("table.rowExpand"),
-    value: "rowExpand",
-    description: trans("table.rowExpand"),
-  },
-  {
-    label: trans("table.rowShrink"),
-    value: "rowShrink",
-    description: trans("table.rowShrink"),
-  },
   {
     label: trans("table.columnEdited"),
     value: "columnEdited",
@@ -261,11 +250,9 @@ const tableChildrenMap = {
   dynamicColumn: BoolPureControl,
   // todo: support object config
   dynamicColumnConfig: ArrayStringControl,
-  expansion: ExpansionControl,
   selectedCell: stateComp<JSONObject>({}),
   inlineAddNewRow: BoolControl,
   editModeClicks: dropdownControl(editModeClickOptions, "single"),
-  currentExpandedRows: stateComp<string[]>([]),
 };
 
 export const TableInitComp = (function () {

From 13cdeffe097c0b6bf74c7b21ca1a116e9d59f1c7 Mon Sep 17 00:00:00 2001
From: Faran Javed <faran1997@outlook.com>
Date: Wed, 6 Aug 2025 15:07:51 +0500
Subject: [PATCH 5/7] fix imports for table lite comp

---
 .../tableLiteComp/column/tableColumnListComp.tsx |  4 ++--
 .../comps/tableLiteComp/selectionControl.tsx     |  2 +-
 .../src/comps/comps/tableLiteComp/tableComp.tsx  | 10 +++++-----
 .../comps/comps/tableLiteComp/tableCompView.tsx  | 16 ++++++++--------
 .../comps/tableLiteComp/tablePropertyView.tsx    |  2 +-
 .../comps/tableLiteComp/tableSummaryComp.tsx     |  2 +-
 .../src/comps/comps/tableLiteComp/tableTypes.tsx |  4 ++--
 .../src/comps/comps/tableLiteComp/tableUtils.tsx |  8 ++++----
 8 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableColumnListComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableColumnListComp.tsx
index 7ad933cf92..f7c14dd491 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableColumnListComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableColumnListComp.tsx
@@ -1,9 +1,9 @@
-import { ColumnComp, newPrimaryColumn } from "comps/comps/tableComp/column/tableColumnComp";
+import { ColumnComp, newPrimaryColumn } from "./tableColumnComp";
 import {
   calcColumnWidth,
   COLUMN_CHILDREN_KEY,
   supportChildrenTree,
-} from "comps/comps/tableComp/tableUtils";
+} from "../tableUtils";
 import { list } from "comps/generators/list";
 import { getReduceContext } from "comps/utils/reduceContext";
 import _ from "lodash";
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/selectionControl.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/selectionControl.tsx
index 037516d91b..af64f4ac6c 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/selectionControl.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/selectionControl.tsx
@@ -4,7 +4,7 @@ import { stateComp } from "comps/generators";
 import { trans } from "i18n";
 import { changeChildAction, ConstructorToComp } from "lowcoder-core";
 import { TableOnEventView } from "./tableTypes";
-import { OB_ROW_ORI_INDEX, RecordType } from "comps/comps/tableComp/tableUtils";
+import { OB_ROW_ORI_INDEX, RecordType } from "./tableUtils";
 import { ControlNodeCompBuilder } from "comps/generators/controlCompBuilder";
 
 // double-click detection constants
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx
index c1c36cb932..ce873d9924 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx
@@ -1,7 +1,7 @@
-import { tableDataRowExample } from "comps/comps/tableComp/column/tableColumnListComp";
-import { getPageSize } from "comps/comps/tableComp/paginationControl";
-import { EMPTY_ROW_KEY, TableCompView } from "comps/comps/tableLiteComp/tableCompView";
-import { TableFilter } from "comps/comps/tableComp/tableToolbarComp";
+import { tableDataRowExample } from "./column/tableColumnListComp";
+import { getPageSize } from "./paginationControl";
+import { EMPTY_ROW_KEY, TableCompView } from "./tableCompView";
+import { TableFilter } from "./tableToolbarComp";
 import {
   columnHide,
   ColumnsAggrData,
@@ -15,7 +15,7 @@ import {
   sortData,
   transformDispalyData,
   tranToTableRecord,
-} from "comps/comps/tableComp/tableUtils";
+} from "./tableUtils";
 import { isTriggerAction } from "comps/controls/actionSelector/actionSelectorControl";
 import { withPropertyViewFn, withViewFn } from "comps/generators";
 import { childrenToProps } from "comps/generators/multi";
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableCompView.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableCompView.tsx
index 020d6c215a..90c94595b3 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableCompView.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableCompView.tsx
@@ -1,7 +1,7 @@
 import { default as Table, TableProps, ColumnType } from "antd/es/table";
-import { TableCellContext, TableRowContext } from "comps/comps/tableLiteComp/tableContext";
-import { TableToolbar } from "comps/comps/tableLiteComp/tableToolbarComp";
-import { RowColorViewType, RowHeightViewType, TableEventOptionValues } from "comps/comps/tableLiteComp/tableTypes";
+import { TableCellContext, TableRowContext } from "./tableContext";
+import { TableToolbar } from "./tableToolbarComp";
+import { RowColorViewType, RowHeightViewType, TableEventOptionValues } from "./tableTypes";
 import {
   COL_MIN_WIDTH,
   COLUMN_CHILDREN_KEY,
@@ -12,7 +12,7 @@ import {
   onTableChange,
   RecordType,
   supportChildrenTree,
-} from "comps/comps/tableLiteComp/tableUtils";
+} from "./tableUtils";
 import {
   handleToHoverRow,
   handleToSelectedRow,
@@ -1020,7 +1020,7 @@ export const TableCompView = React.memo((props: {
         total: pageDataInfo.total,
         current: pageDataInfo.current,
       }}
-      columns={columns as any}
+      columns={columns}
       onRefresh={() =>
         onRefresh(
           editorState.queryCompInfoList().map((info) => info.name),
@@ -1106,12 +1106,12 @@ export const TableCompView = React.memo((props: {
             $showHRowGridBorder={showHRowGridBorder}
           >
             <ResizeableTable<RecordType>
-              rowColorFn={compChildren.rowColor.getView() as any}
-              rowHeightFn={compChildren.rowHeight.getView() as any}
+              rowColorFn={compChildren.rowColor.getView()}
+              rowHeightFn={compChildren.rowHeight.getView()}
               {...compChildren.selection.getView()(onEvent)}
               bordered={compChildren.showRowGridBorder.getView()}
               onChange={(pagination, filters, sorter, extra) => {
-                onTableChange(pagination, filters, sorter, extra, comp.dispatch, onEvent as any);
+                onTableChange(pagination, filters, sorter, extra, comp.dispatch, onEvent);
               }}
               showHeader={!compChildren.hideHeader.getView()}
               columns={antdColumns}
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tablePropertyView.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tablePropertyView.tsx
index 403d962ff4..7d509cb917 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tablePropertyView.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tablePropertyView.tsx
@@ -2,7 +2,7 @@ import {
   ColumnCompType,
   newCustomColumn,
   RawColumnType,
-} from "comps/comps/tableComp/column/tableColumnComp";
+} from "./column/tableColumnComp";
 import { hiddenPropertyView, loadingPropertyView } from "comps/utils/propertyUtils";
 import { trans } from "i18n";
 import { changeValueAction, deferAction, MultiBaseComp, wrapChildAction } from "lowcoder-core";
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableSummaryComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableSummaryComp.tsx
index ca0d3aaa75..45751c385d 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableSummaryComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableSummaryComp.tsx
@@ -1,5 +1,5 @@
 import { ThemeDetail } from "api/commonSettingApi";
-import { ColumnComp } from "comps/comps/tableComp/column/tableColumnComp";
+import { ColumnComp } from "./column/tableColumnComp";
 import { TableColumnLinkStyleType, TableColumnStyleType, TableSummaryRowStyleType } from "comps/controls/styleControlConstants";
 import styled from "styled-components";
 import { defaultTheme } from "@lowcoder-ee/constants/themeConstants";
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableTypes.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableTypes.tsx
index e707c88bfa..6f6380f56f 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableTypes.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableTypes.tsx
@@ -1,5 +1,5 @@
-import { ColumnListComp } from "comps/comps/tableComp/column/tableColumnListComp";
-import { TableToolbarComp } from "comps/comps/tableComp/tableToolbarComp";
+import { ColumnListComp } from "./column/tableColumnListComp";
+import { TableToolbarComp } from "./tableToolbarComp";
 import { BoolControl, BoolPureControl } from "comps/controls/boolControl";
 import {
   ArrayStringControl,
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableUtils.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableUtils.tsx
index 025e91e51e..cc32d73159 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableUtils.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableUtils.tsx
@@ -6,10 +6,10 @@ import {
   TablePaginationConfig,
 } from "antd/es/table/interface";
 import type { SortOrder } from "antd/es/table/interface";
-import { __COLUMN_DISPLAY_VALUE_FN } from "comps/comps/tableComp/column/columnTypeCompBuilder";
-import { CellColorViewType, RawColumnType, Render } from "comps/comps/tableComp/column/tableColumnComp";
-import { TableFilter, tableFilterOperatorMap } from "comps/comps/tableComp/tableToolbarComp";
-import { SortValue, TableOnEventView } from "comps/comps/tableComp/tableTypes";
+import { __COLUMN_DISPLAY_VALUE_FN } from "./column/columnTypeCompBuilder";
+import { CellColorViewType, RawColumnType, Render } from "./column/tableColumnComp";
+import { TableFilter, tableFilterOperatorMap } from "./tableToolbarComp";
+import { SortValue, TableOnEventView } from "./tableTypes";
 import _ from "lodash";
 import { changeChildAction, CompAction, NodeToValue } from "lowcoder-core";
 import { EditableIcon } from "lowcoder-design";

From 966c4df61d59e0d933f87074ae13126f8de427bd Mon Sep 17 00:00:00 2001
From: Faran Javed <faran1997@outlook.com>
Date: Wed, 6 Aug 2025 22:11:04 +0500
Subject: [PATCH 6/7] remove edit functionality

---
 .../tableLiteComp/column/tableColumnComp.tsx  |   6 +-
 .../column/tableColumnListComp.tsx            |   6 +-
 .../comps/comps/tableLiteComp/tableComp.tsx   |   5 +-
 .../comps/tableLiteComp/tableCompView.tsx     | 118 ++----------------
 .../comps/tableLiteComp/tableContext.tsx      |   5 -
 .../comps/tableLiteComp/tablePropertyView.tsx |  26 +---
 .../comps/tableLiteComp/tableToolbarComp.tsx  |  37 +-----
 .../comps/comps/tableLiteComp/tableTypes.tsx  |  30 +----
 .../comps/comps/tableLiteComp/tableUtils.tsx  |  11 +-
 9 files changed, 21 insertions(+), 223 deletions(-)

diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableColumnComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableColumnComp.tsx
index 938983ac9e..f82a1a981c 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableColumnComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableColumnComp.tsx
@@ -31,7 +31,6 @@ import { default as Divider } from "antd/es/divider";
 import { ColumnValueTooltip } from "./simpleColumnTypeComps";
 import { SummaryColumnComp } from "./tableSummaryColumnComp";
 import { list } from "@lowcoder-ee/comps/generators/list";
-import { EMPTY_ROW_KEY } from "../tableCompView";
 import React, { useCallback, useMemo } from "react";
 
 export type Render = ReturnType<ConstructorToComp<typeof RenderComp>["getOriginalComp"]>;
@@ -441,11 +440,10 @@ export class ColumnComp extends ColumnInitComp {
 
   dispatchClearInsertSet() {
     const renderMap = this.children.render.getMap();
-    const insertMapKeys = Object.keys(renderMap).filter(key => key.startsWith(EMPTY_ROW_KEY));
-    insertMapKeys.forEach(key => {
+    Object.keys(renderMap).forEach(key => {
       const render = renderMap[key];
       render.getComp().children.comp.children.changeValue.dispatchChangeValueAction(null);
-    });
+    }); 
   }
 
   static setSelectionAction(key: string) {
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableColumnListComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableColumnListComp.tsx
index f7c14dd491..6e48e1f214 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableColumnListComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/column/tableColumnListComp.tsx
@@ -17,7 +17,6 @@ import {
 import { shallowEqual } from "react-redux";
 import { JSONObject, JSONValue } from "util/jsonTypes";
 import { lastValueIfEqual } from "util/objectUtils";
-import { EMPTY_ROW_KEY } from "../tableCompView";
 
 /**
  * column list
@@ -79,10 +78,7 @@ export class ColumnListComp extends ColumnListTmpComp {
       const columnChangeSet = column.getChangeSet();
       Object.keys(columnChangeSet).forEach((dataIndex) => {
         Object.keys(columnChangeSet[dataIndex]).forEach((key) => {
-          const includeChange = filterNewRowsChange
-            ? key.startsWith(EMPTY_ROW_KEY)
-            : !key.startsWith(EMPTY_ROW_KEY);
-          if (!_.isNil(columnChangeSet[dataIndex][key]) && includeChange) {
+          if (!_.isNil(columnChangeSet[dataIndex][key])) {
             if (!changeSet[key]) changeSet[key] = {};
             changeSet[key][dataIndex] = columnChangeSet[dataIndex][key];
           }
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx
index ce873d9924..6ee2dd9f46 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx
@@ -1,6 +1,6 @@
 import { tableDataRowExample } from "./column/tableColumnListComp";
 import { getPageSize } from "./paginationControl";
-import { EMPTY_ROW_KEY, TableCompView } from "./tableCompView";
+import { TableCompView } from "./tableCompView";
 import { TableFilter } from "./tableToolbarComp";
 import {
   columnHide,
@@ -396,8 +396,7 @@ export class TableImplComp extends TableInitComp {
       _.forEach(dataIndexRenderDict, (render, dataIndex) => {
         _.forEach(render[MAP_KEY], (value, key) => {
           const changeValue = (value.comp as any).comp.changeValue;
-          const includeRecord = (filterNewRows && key.startsWith(EMPTY_ROW_KEY)) || (!filterNewRows && !key.startsWith(EMPTY_ROW_KEY));
-          if (!_.isNil(changeValue) && includeRecord) {
+          if (!_.isNil(changeValue)) {
             if (!record[key]) record[key] = {};
             record[key][dataIndex] = changeValue;
           }
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableCompView.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableCompView.tsx
index 90c94595b3..084136a552 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableCompView.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableCompView.tsx
@@ -1,5 +1,5 @@
 import { default as Table, TableProps, ColumnType } from "antd/es/table";
-import { TableCellContext, TableRowContext } from "./tableContext";
+import {  TableRowContext } from "./tableContext";
 import { TableToolbar } from "./tableToolbarComp";
 import { RowColorViewType, RowHeightViewType, TableEventOptionValues } from "./tableTypes";
 import {
@@ -50,8 +50,6 @@ import { SkeletonButtonProps } from "antd/es/skeleton/Button";
 import { ThemeContext } from "@lowcoder-ee/comps/utils/themeContext";
 import { useUpdateEffect } from "react-use";
 
-export const EMPTY_ROW_KEY = 'empty_row';
-
 function genLinerGradient(color: string) {
   return isValidColor(color) ? `linear-gradient(${color}, ${color})` : color;
 }
@@ -378,7 +376,6 @@ interface TableTdProps {
   $style: TableColumnStyleType & { rowHeight?: string };
   $defaultThemeDetail: ThemeDetail;
   $linkStyle?: TableColumnLinkStyleType;
-  $isEditing: boolean;
   $tableSize?: string;
   $autoHeight?: boolean;
   $customAlign?: 'left' | 'center' | 'right';
@@ -386,7 +383,7 @@ interface TableTdProps {
 const TableTd = styled.td<TableTdProps>`
   .ant-table-row-expand-icon,
   .ant-table-row-indent {
-    display: ${(props) => (props.$isEditing ? "none" : "initial")};
+    display: initial;
   }
   &.ant-table-row-expand-icon-cell {
     background: ${(props) => props.$background};
@@ -398,11 +395,8 @@ const TableTd = styled.td<TableTdProps>`
   padding: 0 !important;
   text-align: ${(props) => props.$customAlign || 'left'} !important;
 
-  > div:not(.editing-border, .editing-wrapper),
-  .editing-wrapper .ant-input,
-  .editing-wrapper .ant-input-number,
-  .editing-wrapper .ant-picker {
-    margin: ${(props) => props.$isEditing ? '0px' : props.$style.margin};
+  > div {
+    margin: ${(props) => props.$style.margin};
     color: ${(props) => props.$style.text};
     font-weight: ${(props) => props.$style.textWeight};
     font-family: ${(props) => props.$style.fontFamily};
@@ -601,7 +595,6 @@ const TableCellView = React.memo((props: {
     ...restProps
   } = props;
 
-  const [editing, setEditing] = useState(false);
   const rowContext = useContext(TableRowContext);
   
   // Memoize style calculations
@@ -655,7 +648,6 @@ const TableCellView = React.memo((props: {
         $style={style!}
         $defaultThemeDetail={defaultTheme}
         $linkStyle={linkStyle}
-        $isEditing={editing}
         $tableSize={tableSize}
         $autoHeight={autoHeight}
         $customAlign={customAlign}
@@ -668,11 +660,7 @@ const TableCellView = React.memo((props: {
     );
   }
 
-  return (
-    <TableCellContext.Provider value={{ isEditing: editing, setIsEditing: setEditing }}>
-      {tdView}
-    </TableCellContext.Provider>
-  );
+  return tdView;
 });
 
 const TableRowView = React.memo((props: any) => {
@@ -809,26 +797,13 @@ ResizeableTableComp.whyDidYouRender = true;
 const ResizeableTable = React.memo(ResizeableTableComp) as typeof ResizeableTableComp;
 
 
-const createNewEmptyRow = (
-  rowIndex: number,
-  columnsAggrData: ColumnsAggrData,
-) => {
-  const emptyRowData: RecordType = {
-    [OB_ROW_ORI_INDEX]: `${EMPTY_ROW_KEY}_${rowIndex}`,
-  };
-  Object.keys(columnsAggrData).forEach(columnKey => {
-    emptyRowData[columnKey] = '';
-  });
-  return emptyRowData;
-}
+
 
 export const TableCompView = React.memo((props: {
   comp: InstanceType<typeof TableImplComp>;
   onRefresh: (allQueryNames: Array<string>, setLoading: (loading: boolean) => void) => void;
   onDownload: (fileName: string) => void;
 }) => {
-  const [expandedRowKeys, setExpandedRowKeys] = useState<string[]>([]);
-  const [emptyRowsMap, setEmptyRowsMap] = useState<Record<string, RecordType>>({});
   const editorState = useContext(EditorContext);
   const currentTheme = useContext(ThemeContext)?.theme;
   const showDataLoadingIndicators = currentTheme?.showDataLoadingIndicators;
@@ -865,10 +840,8 @@ export const TableCompView = React.memo((props: {
   const toolbar = useMemo(() => compChildren.toolbar.getView(), [compChildren.toolbar]);
   const showSummary = useMemo(() => compChildren.showSummary.getView(), [compChildren.showSummary]);
   const summaryRows = useMemo(() => compChildren.summaryRows.getView(), [compChildren.summaryRows]);
-  const inlineAddNewRow = useMemo(() => compChildren.inlineAddNewRow.getView(), [compChildren.inlineAddNewRow]);
   const pagination = useMemo(() => compChildren.pagination.getView(), [compChildren.pagination]);
   const size = useMemo(() => compChildren.size.getView(), [compChildren.size]);
-  const editModeClicks = useMemo(() => compChildren.editModeClicks.getView(), [compChildren.editModeClicks]);
   const onEvent = useMemo(() => compChildren.onEvent.getView(), [compChildren.onEvent]);
   const dynamicColumn = compChildren.dynamicColumn.getView();
   const dynamicColumnConfig = useMemo(
@@ -886,7 +859,6 @@ export const TableCompView = React.memo((props: {
         dynamicColumn,
         dynamicColumnConfig,
         columnsAggrData,
-        editModeClicks,
         onEvent,
       ),
     [
@@ -897,7 +869,6 @@ export const TableCompView = React.memo((props: {
       dynamicColumn,
       dynamicColumnConfig,
       columnsAggrData,
-      editModeClicks,
     ]
   );
 
@@ -906,69 +877,6 @@ export const TableCompView = React.memo((props: {
     [compChildren.data]
   );
 
-  const updateEmptyRows = useCallback(() => {
-    if (!inlineAddNewRow) {
-      setEmptyRowsMap({})
-      setTimeout(() => compChildren.columns.dispatchClearInsertSet());
-      return;
-    }
-
-    let emptyRows: Record<string, RecordType> = {...emptyRowsMap};
-    const existingRowsKeys = Object.keys(emptyRows);
-    const existingRowsCount = existingRowsKeys.length;
-    const updatedRowsKeys = Object.keys(insertSet).filter(
-      key => key.startsWith(EMPTY_ROW_KEY)
-    );
-    const updatedRowsCount = updatedRowsKeys.length;
-    const removedRowsKeys = existingRowsKeys.filter(
-      x => !updatedRowsKeys.includes(x)
-    );
-
-    if (removedRowsKeys.length === existingRowsCount) {
-      const newRowIndex = 0;
-      const newRowKey = `${EMPTY_ROW_KEY}_${newRowIndex}`;
-      setEmptyRowsMap({
-        [newRowKey]: createNewEmptyRow(newRowIndex, columnsAggrData)
-      });
-      const ele = document.querySelector<HTMLElement>(`[data-row-key=${newRowKey}]`);
-      if (ele) {
-        ele.style.display = '';
-      }
-      return;
-    }
-
-    removedRowsKeys.forEach(rowKey => {
-      if (
-        rowKey === existingRowsKeys[existingRowsCount - 1]
-        || rowKey === existingRowsKeys[existingRowsCount - 2]
-      ) {
-        delete emptyRows[rowKey];
-      } else {
-        const ele = document.querySelector<HTMLElement>(`[data-row-key=${rowKey}]`);
-        if (ele) {
-          ele.style.display = 'none';
-        }
-      }
-    })
-    const lastRowKey = updatedRowsCount ? updatedRowsKeys[updatedRowsCount - 1] : '';
-    const lastRowIndex = lastRowKey ? parseInt(lastRowKey.replace(`${EMPTY_ROW_KEY}_`, '')) : -1;
-
-    const newRowIndex = lastRowIndex + 1;
-    const newRowKey = `${EMPTY_ROW_KEY}_${newRowIndex}`;
-    emptyRows[newRowKey] = createNewEmptyRow(newRowIndex, columnsAggrData);
-    setEmptyRowsMap(emptyRows);
-  }, [
-    inlineAddNewRow,
-    JSON.stringify(insertSet),
-    setEmptyRowsMap,
-    createNewEmptyRow,
-  ]);
-
-  useEffect(() => {
-    updateEmptyRows();
-  }, [updateEmptyRows]);
-
-
 
   const pageDataInfo = useMemo(() => {
     // Data pagination
@@ -1001,10 +909,6 @@ export const TableCompView = React.memo((props: {
 
   const handleChangeEvent = useCallback(
     (eventName: TableEventOptionValues) => {
-      if (eventName === "saveChanges" && !compChildren.onEvent.isBind(eventName)) {
-        !viewMode && messageInstance.warning(trans("table.saveChangesNotBind"));
-        return;
-      }
       compChildren.onEvent.getView()(eventName);
       setTimeout(() => compChildren.columns.dispatchClearChangeSet());
     },
@@ -1031,14 +935,6 @@ export const TableCompView = React.memo((props: {
         handleChangeEvent("download");
         onDownload(`${compName}-data`)
       }}
-      hasChange={hasChange}
-      onSaveChanges={() => handleChangeEvent("saveChanges")}
-      onCancelChanges={() => {
-        handleChangeEvent("cancelChanges");
-        if (inlineAddNewRow) {
-          setEmptyRowsMap({});
-        }
-      }}
       onEvent={onEvent}
     />
   );
@@ -1118,7 +1014,7 @@ export const TableCompView = React.memo((props: {
               columnsStyle={columnsStyle}
               viewModeResizable={compChildren.viewModeResizable.getView()}
               visibleResizables={compChildren.visibleResizables.getView()}
-              dataSource={pageDataInfo.data.concat(Object.values(emptyRowsMap))}
+              dataSource={pageDataInfo.data}
               size={compChildren.size.getView()}
               rowAutoHeight={rowAutoHeight}
               tableLayout="fixed"
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableContext.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableContext.tsx
index 47067a9e03..68c5a56fab 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableContext.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableContext.tsx
@@ -5,8 +5,3 @@ export const TableRowContext = React.createContext<{
   hover: boolean;
   selected: boolean;
 }>({ hover: false, selected: false });
-
-export const TableCellContext = React.createContext<{
-  isEditing: boolean;
-  setIsEditing: (e: boolean) => void;
-}>({ isEditing: false, setIsEditing: _.noop });
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tablePropertyView.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tablePropertyView.tsx
index 7d509cb917..2b837d4892 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tablePropertyView.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tablePropertyView.tsx
@@ -32,7 +32,7 @@ import { TableChildrenType } from "./tableTypes";
 import React, { useMemo, useState, useCallback } from "react";
 import { GreyTextColor } from "constants/style";
 import { alignOptions } from "comps/controls/dropdownControl";
-import { ColumnTypeCompMap } from "comps/comps/tableComp/column/columnTypeComp";
+import { ColumnTypeCompMap } from "./column/columnTypeComp";
 import Segmented from "antd/es/segmented";
 import { CheckboxChangeEvent } from "antd/es/checkbox";
 
@@ -144,10 +144,6 @@ const columnBatchOptions = [
     label: trans("prop.hide"),
     value: "hide",
   },
-  {
-    label: trans("table.editable"),
-    value: "editable",
-  },
   {
     label: trans("table.autoWidth"),
     value: "autoWidth",
@@ -202,15 +198,9 @@ function ColumnBatchCheckBox<T extends keyof ColumnCompType["children"]>({
 
   const isBatch = Array.isArray(column);
   const columns = isBatch ? column : [column];
-  
+    
   const disabledStatus = useMemo(() => columns.map((c) => {
-    if (childrenKey !== "editable") {
-      return false;
-    }
-    const columnType = c.children.render
-      .getOriginalComp()
-      .children.comp.children.compType.getView();
-    return !ColumnTypeCompMap[columnType].canBeEditable();
+    return false;
   }), [columns, childrenKey]);
 
   const { allChecked, allNotChecked } = useMemo(() => {
@@ -265,7 +255,6 @@ const ColumnBatchView: Record<
   (column: ColumnCompType | Array<ColumnCompType>) => JSX.Element
 > = {
   hide: (column) => <ColumnBatchCheckBox childrenKey="hide" column={column} />,
-  editable: (column) => <ColumnBatchCheckBox childrenKey="editable" column={column} />,
   sortable: (column) => <ColumnBatchCheckBox childrenKey="sortable" column={column} />,
   autoWidth: (column) => (
     <ColumnBatchCheckBox
@@ -528,10 +517,6 @@ export function compTablePropertyView<T extends MultiBaseComp<TableChildrenType>
             {hiddenPropertyView(comp.children)}
             {loadingPropertyView(comp.children)}
             {comp.children.selection.getPropertyView()}
-            {comp.children.editModeClicks.propertyView({
-              label: trans("table.editMode"),
-              radioButton: true,
-            })}
             {comp.children.searchText.propertyView({
               label: trans("table.searchText"),
               tooltip: trans("table.searchTextTooltip"),
@@ -550,11 +535,6 @@ export function compTablePropertyView<T extends MultiBaseComp<TableChildrenType>
             })}
           </Section>
 
-          <Section name={"Insert Rows"}>
-            {comp.children.inlineAddNewRow.propertyView({
-              label: trans("table.inlineAddNewRow")
-            })}
-          </Section>
 
           <Section name={trans("prop.toolbar")}>
             {comp.children.toolbar.getPropertyView()}
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableToolbarComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableToolbarComp.tsx
index d4f58422f3..e3a276f988 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableToolbarComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableToolbarComp.tsx
@@ -44,12 +44,6 @@ import type { CheckboxChangeEvent } from 'antd/es/checkbox';
 
 type ToolbarRowType = ConstructorToView<typeof TableToolbarComp>;
 
-const SaveChangeButtons = styled.div`
-  display: flex;
-  align-items: center;
-  gap: 8px;
-`;
-
 const getStyle = (
   style: TableToolbarStyleType,
   filtered: boolean,
@@ -763,9 +757,6 @@ export const TableToolbar = memo(function TableToolbar(props: {
   columns: Array<ColumnCompType>;
   onRefresh: () => void;
   onDownload: () => void;
-  hasChange: boolean;
-  onSaveChanges: () => void;
-  onCancelChanges: () => void;
   onEvent: TableOnEventView;
 }) {
   const {
@@ -775,9 +766,6 @@ export const TableToolbar = memo(function TableToolbar(props: {
     columns,
     onRefresh,
     onDownload,
-    hasChange,
-    onSaveChanges,
-    onCancelChanges,
     onEvent,
   } = props;
 
@@ -797,15 +785,7 @@ export const TableToolbar = memo(function TableToolbar(props: {
     onEvent("download");
   }, [onDownload, onEvent]);
 
-  const handleSaveChanges = useCallback(() => {
-    onSaveChanges();
-    onEvent("saveChanges");
-  }, [onSaveChanges, onEvent]);
-
-  const handleCancelChanges = useCallback(() => {
-    onCancelChanges();
-    onEvent("cancelChanges");
-  }, [onCancelChanges, onEvent]);
+ 
 
   const handleColumnFilterChange = useCallback((filters: TableFilterDataType[], stackType: TableFilter["stackType"]) => {
     if (
@@ -877,14 +857,6 @@ export const TableToolbar = memo(function TableToolbar(props: {
             }
           }}
         />
-        {hasChange && toolbar.showUpdateButtons && (
-          <SaveChangeButtons>
-            <TacoButton onClick={handleCancelChanges}>{trans("cancel")}</TacoButton>
-            <TacoButton buttonType="primary" onClick={handleSaveChanges}>
-              {trans("table.saveChanges")}
-            </TacoButton>
-          </SaveChangeButtons>
-        )}
       </ToolbarWrapper2>
     </ToolbarWrapper>
   );
@@ -903,7 +875,6 @@ export const TableToolbarComp = (function () {
     filter: stateComp<TableFilter>({ stackType: "and", filters: [] }),
     position: dropdownControl(positionOptions, "below"),
     columnSeparator: withDefault(StringControl, ','),
-    showUpdateButtons: withDefault(BoolControl, true),
   };
 
   return new ControlNodeCompBuilder(childrenMap, (props, dispatch) => {
@@ -929,7 +900,6 @@ export const TableToolbarComp = (function () {
         label: trans("table.fixedToolbar"),
         tooltip: trans("table.fixedToolbarTooltip")
       }),
-      children.showUpdateButtons.propertyView({ label: trans("table.showUpdateButtons")}),
       children.showFilter.propertyView({ label: trans("table.showFilter") }),
       children.showRefresh.propertyView({ label: trans("table.showRefresh") }),
       children.showDownload.propertyView({ label: trans("table.showDownload") }),
@@ -938,11 +908,6 @@ export const TableToolbarComp = (function () {
         tooltip: trans("table.columnSeparatorTooltip"),
       }),
       children.columnSetting.propertyView({ label: trans("table.columnSetting") }),
-      /* children.searchText.propertyView({
-        label: trans("table.searchText"),
-        tooltip: trans("table.searchTextTooltip"),
-        placeholder: "{{input1.value}}",
-      }), */
     ])
     .build();
 })();
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableTypes.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableTypes.tsx
index 6f6380f56f..023b804335 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableTypes.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableTypes.tsx
@@ -36,16 +36,7 @@ import { PaginationControl } from "./paginationControl";
 import { SelectionControl } from "./selectionControl";
 import { AutoHeightControl } from "comps/controls/autoHeightControl";
 
-const editModeClickOptions = [
-  {
-    label: trans("table.singleClick"),
-    value: "single",
-  },
-  {
-    label: trans("table.doubleClick"),
-    value: "double",
-  },
-] as const;
+
 
 const summarRowsOptions = [
   {
@@ -78,16 +69,6 @@ const sizeOptions = [
 ] as const;
 
 export const TableEventOptions = [
-  {
-    label: trans("table.saveChanges"),
-    value: "saveChanges",
-    description: trans("table.saveChanges"),
-  },
-  {
-    label: trans("table.cancelChanges"),
-    value: "cancelChanges",
-    description: trans("table.cancelChanges"),
-  },
   {
     label: trans("table.rowSelectChange"),
     value: "rowSelectChange",
@@ -98,11 +79,7 @@ export const TableEventOptions = [
     value: "rowClick",
     description: trans("table.rowClick"),
   },
-  {
-    label: trans("table.columnEdited"),
-    value: "columnEdited",
-    description: trans("table.columnEdited"),
-  },
+ 
   {
     label: trans("table.search"),
     value: "dataSearch",
@@ -210,7 +187,6 @@ export type RowHeightViewType = (param: {
 }) => string;
 
 const tableChildrenMap = {
-  // hideBordered: BoolControl,
   showHeaderGridBorder: BoolControl,
   showRowGridBorder: withDefault(BoolControl,true),
   showHRowGridBorder: withDefault(BoolControl,true),
@@ -251,8 +227,6 @@ const tableChildrenMap = {
   // todo: support object config
   dynamicColumnConfig: ArrayStringControl,
   selectedCell: stateComp<JSONObject>({}),
-  inlineAddNewRow: BoolControl,
-  editModeClicks: dropdownControl(editModeClickOptions, "single"),
 };
 
 export const TableInitComp = (function () {
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableUtils.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableUtils.tsx
index cc32d73159..dc146030ba 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableUtils.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableUtils.tsx
@@ -20,7 +20,6 @@ import { ColumnListComp, tableDataRowExample } from "./column/tableColumnListCom
 import { TableColumnLinkStyleType, TableColumnStyleType } from "comps/controls/styleControlConstants";
 import Tooltip from "antd/es/tooltip";
 import InfoCircleOutlined from "@ant-design/icons/InfoCircleOutlined";
-import { EMPTY_ROW_KEY } from "./tableCompView";
 import dayjs from "dayjs";
 
 export const COLUMN_CHILDREN_KEY = "children";
@@ -244,8 +243,8 @@ export function getColumnsAggr(
   });
 }
 
-function renderTitle(props: { title: string; tooltip: string; editable: boolean }) {
-  const { title, tooltip, editable } = props;
+function renderTitle(props: { title: string; tooltip: string }) {
+  const { title, tooltip } = props;
   return (
     <div>
       <Tooltip title={tooltip}>
@@ -253,7 +252,6 @@ function renderTitle(props: { title: string; tooltip: string; editable: boolean
           {title}
         </span>
       </Tooltip>
-      {editable && <EditableIcon style={{ verticalAlign: "baseline", marginLeft: "4px" }} />}
     </div>
   );
 }
@@ -296,7 +294,6 @@ export function columnsToAntdFormat(
   dynamicColumn: boolean,
   dynamicColumnConfig: Array<string>,
   columnsAggrData: ColumnsAggrData,
-  editMode: string,
   onTableEvent: (eventName: any) => void,
 ): Array<CustomColumnType<RecordType>> {
   const customColumns = columns.filter(col => col.isCustom).map(col => col.dataIndex);
@@ -340,7 +337,7 @@ export function columnsToAntdFormat(
       text: string;
       status: StatusType;
     }[];
-    const title = renderTitle({ title: column.title, tooltip: column.titleTooltip, editable: column.editable });
+    const title = renderTitle({ title: column.title, tooltip: column.titleTooltip });
 
     return {
       key: `${column.dataIndex}-${mIndex}`,
@@ -384,7 +381,6 @@ export function columnsToAntdFormat(
           )
           .getView()
           .view({
-            editable: record[OB_ROW_ORI_INDEX].startsWith(EMPTY_ROW_KEY) || column.editable,
             tableSize: size,
             candidateTags: tags,
             candidateStatus: status,
@@ -394,7 +390,6 @@ export function columnsToAntdFormat(
               currentRow: row,
               currentIndex: index,
             }),
-            editMode,
             onTableEvent,
             cellIndex: `${column.dataIndex}-${index}`,
           });

From 4d75755361601b7fdfa80f9ed2c9c25c61b2852b Mon Sep 17 00:00:00 2001
From: Faran Javed <faran1997@outlook.com>
Date: Fri, 8 Aug 2025 22:32:24 +0500
Subject: [PATCH 7/7] remove filter modal / table toolbar unnecessary code

---
 .../tableLiteComp/styles/toolbar.styles.ts    |  22 +
 .../comps/comps/tableLiteComp/tableComp.tsx   |  42 +-
 .../comps/tableLiteComp/tableCompView.tsx     |   4 +-
 .../comps/tableLiteComp/tableToolbarComp.tsx  | 701 +-----------------
 .../comps/comps/tableLiteComp/tableUtils.tsx  |   7 +-
 5 files changed, 60 insertions(+), 716 deletions(-)
 create mode 100644 client/packages/lowcoder/src/comps/comps/tableLiteComp/styles/toolbar.styles.ts

diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/styles/toolbar.styles.ts b/client/packages/lowcoder/src/comps/comps/tableLiteComp/styles/toolbar.styles.ts
new file mode 100644
index 0000000000..83fca93824
--- /dev/null
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/styles/toolbar.styles.ts
@@ -0,0 +1,22 @@
+import styled from "styled-components";
+
+export const ToolbarContainer = styled.div`
+  padding: 12px;
+  width: 100%;
+`;
+
+export const ToolbarRow = styled.div`
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  min-width: max-content;
+  width: 100%;
+`;
+
+export const ToolbarIcons = styled.div`
+  display: flex;
+  align-items: center;
+  gap: 8px;
+`;
+
+
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx
index 6ee2dd9f46..c3d39e1f74 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableComp.tsx
@@ -1,7 +1,6 @@
 import { tableDataRowExample } from "./column/tableColumnListComp";
 import { getPageSize } from "./paginationControl";
 import { TableCompView } from "./tableCompView";
-import { TableFilter } from "./tableToolbarComp";
 import {
   columnHide,
   ColumnsAggrData,
@@ -313,27 +312,28 @@ export class TableImplComp extends TableInitComp {
     const nodes = {
       data: this.sortDataNode(),
       searchValue: this.children.searchText.node(),
-      filter: this.children.toolbar.children.filter.node(),
-      showFilter: this.children.toolbar.children.showFilter.node(),
     };
     let context = this;
+  
     const filteredDataNode = withFunction(fromRecord(nodes), (input) => {
-      const { data, searchValue, filter, showFilter } = input;
-      const filteredData = filterData(data, searchValue.value, filter, showFilter.value);
-      // console.info("filterNode. data: ", data, " filter: ", filter, " filteredData: ", filteredData);
-      // if data is changed on search then trigger event
-      if(Boolean(searchValue.value) && data.length !== filteredData.length) {
+      const { data, searchValue } = input;
+      const filteredData = filterData(data, searchValue.value, { filters: [], stackType: "and" }, false);
+  
+      if (Boolean(searchValue.value) && data.length !== filteredData.length) {
         const onEvent = context.children.onEvent.getView();
         setTimeout(() => {
           onEvent("dataSearch");
         });
       }
+  
       return filteredData.map((row) => tranToTableRecord(row, row[OB_ROW_ORI_INDEX]));
     });
+  
     return lastValueIfEqual(this, "filteredDataNode", [filteredDataNode, nodes] as const, (a, b) =>
       shallowEqual(a[1], b[1])
     )[0];
   }
+  
 
   oriDisplayDataNode() {
     const nodes = {
@@ -494,7 +494,6 @@ export class TableImplComp extends TableInitComp {
 }
 
 let TableTmpComp = withViewFn(TableImplComp, (comp) => {
-  const [emptyRows, setEmptyRows] = useState([]);
   return (
     <HidableView hidden={comp.children.hidden.getView()}>
       <TableCompView
@@ -580,13 +579,14 @@ TableTmpComp = withMethodExposing(TableTmpComp, [
       params: [{ name: "filter", type: "JSON" }],
     },
     execute: (comp, values) => {
+      //TODO: add filter maybe
       if (values[0]) {
-        const param = values[0] as TableFilter;
-        const currentVal = comp.children.toolbar.children.filter.getView();
-        comp.children.toolbar.children.filter.dispatchChangeValueAction({
-          ...currentVal,
-          ...param,
-        });
+        // const param = values[0] as TableFilter;
+        // const currentVal = comp.children.toolbar.children.filter.getView();
+        // comp.children.toolbar.children.filter.dispatchChangeValueAction({
+        //   ...currentVal,
+        //   ...param,
+        // });
       }
     },
   },
@@ -915,18 +915,6 @@ export const TableLiteComp = withExposingConfigs(TableTmpComp, [
     },
     trans("table.displayDataDesc")
   ),
-  new DepsConfig(
-    "filter",
-    (children) => {
-      return {
-        filter: children.toolbar.children.filter.node(),
-      };
-    },
-    (input) => {
-      return input.filter;
-    },
-    trans("table.filterDesc")
-  ),
   new DepsConfig(
     "selectedCell",
     (children) => {
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableCompView.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableCompView.tsx
index 084136a552..4d9a6d92e1 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableCompView.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableCompView.tsx
@@ -804,6 +804,9 @@ export const TableCompView = React.memo((props: {
   onRefresh: (allQueryNames: Array<string>, setLoading: (loading: boolean) => void) => void;
   onDownload: (fileName: string) => void;
 }) => {
+
+  console.log("TableCompView");
+
   const editorState = useContext(EditorContext);
   const currentTheme = useContext(ThemeContext)?.theme;
   const showDataLoadingIndicators = currentTheme?.showDataLoadingIndicators;
@@ -918,7 +921,6 @@ export const TableCompView = React.memo((props: {
   const toolbarView = !hideToolbar && (
     <TableToolbar
       toolbar={toolbar}
-      $style={toolbarStyle}
       pagination={{
         ...pagination,
         total: pageDataInfo.total,
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableToolbarComp.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableToolbarComp.tsx
index e3a276f988..476bffaade 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableToolbarComp.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableToolbarComp.tsx
@@ -1,286 +1,32 @@
-import { default as Button } from "antd/es/button";
 import { default as Pagination, PaginationProps } from "antd/es/pagination";
 import { default as Popover } from "antd/es/popover";
-import { ThemeDetail } from "api/commonSettingApi";
 import { ColumnCompType } from "comps/comps/tableLiteComp/column/tableColumnComp";
-import { ColumnComp } from "./column/tableColumnComp";
 import { TableOnEventView } from "comps/comps/tableLiteComp/tableTypes";
 import { BoolControl } from "comps/controls/boolControl";
 import { StringControl } from "comps/controls/codeControl";
 import { dropdownControl } from "comps/controls/dropdownControl";
-import { TableToolbarStyleType } from "comps/controls/styleControlConstants";
-import { stateComp, withDefault } from "comps/generators";
-import { genRandomKey } from "comps/utils/idGenerator";
-import { ThemeContext } from "comps/utils/themeContext";
+import { withDefault } from "comps/generators";
 import { trans } from "i18n";
-import _, { isNil } from "lodash";
-import { changeChildAction, ConstructorToView } from "lowcoder-core";
+import { ConstructorToView } from "lowcoder-core";
 import {
   AlignBottom,
   AlignClose,
   AlignTop,
-  BluePlusIcon,
   CheckBox,
   CommonTextLabel,
-  CustomSelect,
-  DeleteIcon,
   DownloadIcon,
-  FilterIcon,
-  LinkButton,
   pageItemRender,
   RefreshIcon,
   TableColumnVisibilityIcon,
   SuspensionBox,
-  TacoButton,
-  TacoInput,
-  ValueFromOption,
 } from "lowcoder-design";
-import React, { useContext, useEffect, useMemo, useRef, useState, memo, useCallback } from "react";
-import styled, { css } from "styled-components";
-import { JSONValue } from "util/jsonTypes";
+import React, { useMemo, useState, memo, useCallback } from "react";
+import { ToolbarContainer, ToolbarRow, ToolbarIcons } from "./styles/toolbar.styles";
 import { ControlNodeCompBuilder } from "comps/generators/controlCompBuilder";
-import { defaultTheme } from "@lowcoder-ee/constants/themeConstants";
 import type { CheckboxChangeEvent } from 'antd/es/checkbox';
 
 type ToolbarRowType = ConstructorToView<typeof TableToolbarComp>;
 
-const getStyle = (
-  style: TableToolbarStyleType,
-  filtered: boolean,
-  theme: ThemeDetail,
-  position: ToolbarRowType["position"],
-  fixedToolbar: boolean,
-) => {
-  return css`
-    background: ${style.background};
-    // Implement horizontal scrollbar and vertical page number selection is not blocked
-    padding: 13px 12px;
-    position: sticky;
-    postion: -webkit-sticky;
-    left: 0px !important;
-    margin: ${style.margin} !important;
-    z-index: 999;
-
-    ${fixedToolbar && position === 'below' && `bottom: 0;`};
-    ${fixedToolbar && position === 'above' && `top: 0;` };
-
-    .toolbar-icons {
-      .refresh,
-      .download {
-        cursor: pointer;
-
-        * {
-          ${style.toolbarText !== defaultTheme.textDark ? `stroke: ${style.toolbarText}` : null};
-        }
-
-        &:hover * {
-          stroke: ${theme?.primary};
-        }
-      }
-
-      .filter {
-        cursor: pointer;
-
-        * {
-          ${filtered
-            ? `stroke: ${defaultTheme.primary}`
-            : style.toolbarText !== defaultTheme.textDark
-            ? `stroke: ${style.toolbarText}`
-            : null}
-        }
-
-        &:hover * {
-          stroke: ${theme?.primary};
-        }
-      }
-
-      .column-setting {
-        width: 20px;
-        cursor: pointer;
-
-        * {
-          ${style.toolbarText && style.toolbarText !== defaultTheme.textDark ? `fill: ${style.toolbarText}` : `fill: #8b8fa3`} 
-        }
-
-        &:hover * {
-          fill: ${theme?.primary};
-        }
-      }
-    }
-
-    .ant-pagination-prev,
-    .ant-pagination-next {
-      path {
-        ${style.toolbarText !== defaultTheme.textDark ? `fill: ${style.toolbarText}` : null};
-      }
-
-      svg:hover {
-        path {
-          fill: ${theme?.primary};
-        }
-      }
-    }
-
-    .ant-pagination {
-      color: ${style.toolbarText};
-    }
-
-    .ant-pagination-item-active {
-      border-color: ${style.border || theme?.primary};
-
-      a {
-        color: ${theme?.textDark};
-      }
-    }
-
-    .ant-pagination-item:not(.ant-pagination-item-active) a {
-      color: ${style.toolbarText};
-
-      &:hover {
-        color: ${theme?.primary};
-      }
-    }
-
-    .ant-select:not(.ant-select-disabled):hover .ant-select-selector,
-    .ant-select-focused:not(.ant-select-disabled).ant-select:not(.ant-select-customize-input)
-      .ant-select-selector,
-    .ant-pagination-options-quick-jumper input:hover,
-    .ant-pagination-options-quick-jumper input:focus {
-      border-color: ${style.border || theme?.primary};
-    }
-  `;
-};
-
-const ToolbarWrapper = styled.div<{
-  $style: TableToolbarStyleType;
-  $filtered: boolean;
-  $theme: ThemeDetail;
-  $position: ToolbarRowType["position"];
-  $fixedToolbar: boolean;
-}>`
-  // overflow: auto;
-  ${(props) => props.$style && getStyle(
-    props.$style,
-    props.$filtered,
-    props.$theme,
-    props.$position,
-    props.$fixedToolbar,
-  )}
-`;
-
-const ToolbarWrapper2 = styled.div`
-  display: flex;
-  align-items: center;
-  justify-content: space-between;
-  min-width: max-content;
-  height: 21px;
-  width: 100%;
-`;
-
-const ToolbarIcons = styled.div`
-  display: flex;
-  align-items: center;
-  gap: 8px;
-`;
-
-const StyledDeleteIcon = styled(DeleteIcon)<{ disabled: boolean }>`
-  height: 16px;
-  width: 16px;
-
-  ${(props) =>
-    props.disabled &&
-    `
-      cursor: not-allowed;
-      g g {
-      stroke: #D7D9E0;
-    }
-  `}
-  :hover:not([disabled]) {
-    cursor: pointer;
-
-    g g {
-      stroke: #315efb;
-    }
-  }
-`;
-
-const FilterItem = styled.div`
-  display: flex;
-  align-items: center;
-  gap: 8px;
-`;
-
-const FilterViewContent = styled.div`
-  min-height: 92px;
-  overflow: auto;
-
-  > div {
-    margin-top: 4px;
-  }
-`;
-
-const FilterViewBottom = styled.div`
-  display: flex;
-  align-items: end;
-  height: 44px;
-  justify-content: end;
-  gap: 4px;
-
-  > button {
-    width: 76px;
-    height: 28px;
-  }
-`;
-
-const ColumnCheckItem = styled.div`
-  display: flex;
-  gap: 4px;
-`;
-
-const StyledCustomSelect = styled(CustomSelect)<{ $width: string }>`
-  .ant-select {
-  width: ${props => props.$width};
-  }
-`;
-
-const CustomSelectionOptionInner = styled.div`
-  width: 72px;
-  font-size: 13px;
-  line-height: 13px;
-`;
-
-const StyledTacoInput = styled(TacoInput)`
-  width: 136px;
-`;
-
-const StyledLinkButton = styled(LinkButton)`
-  margin-right: auto;
-`;
-
-const ColumnSettingContent = styled.div`
-  display: flex;
-  flex-direction: column;
-  gap: 8px;
-`;
-
-const ColumnSettingFooter = styled.div`
-  display: flex;
-  align-items: flex-end;
-  justify-content: space-between;
-  height: 32px;
-`;
-
-const filterStackOptions = [
-  {
-    label: trans("table.and"),
-    value: "and",
-  },
-  {
-    label: trans("table.or"),
-    value: "or",
-  },
-] as const;
-
 const positionOptions = [
   {
     label: <AlignBottom />,
@@ -296,356 +42,6 @@ const positionOptions = [
   },
 ] as const;
 
-const tableFilterOperators = [
-  "contain",
-  "notContain",
-  "equal",
-  "notEqual",
-  "isEmpty",
-  "isNotEmpty",
-  "gt",
-  "gte",
-  "lt",
-  "lte",
-] as const;
-
-const noValueOperators: TableFilterOperator[] = ["isEmpty", "isNotEmpty"];
-
-type TableFilterOperator = typeof tableFilterOperators[number];
-export const tableFilterOperatorMap: Record<
-  TableFilterOperator,
-  {
-    label: string;
-    filter: (filterValue: string, data: JSONValue | undefined) => boolean;
-  }
-> = {
-  contain: {
-    label: trans("table.contains"),
-    filter: (filterValue, data) => {
-      if (isNil(data)) {
-        return false;
-      }
-      return data.toString().toLowerCase().includes(filterValue.toLowerCase());
-    },
-  },
-  notContain: {
-    label: trans("table.notContain"),
-    filter: (filterValue, data) => {
-      if (isNil(data)) {
-        return true;
-      }
-      return !data.toString().toLowerCase().includes(filterValue.toLowerCase());
-    },
-  },
-  equal: {
-    label: trans("table.equals"),
-    filter: (filterValue, data) => {
-      if (isNil(data)) {
-        return false;
-      }
-      return data.toString() === filterValue;
-    },
-  },
-  notEqual: {
-    label: trans("table.isNotEqual"),
-    filter: (filterValue, data) => {
-      if (isNil(data)) {
-        return true;
-      }
-      return data.toString() !== filterValue;
-    },
-  },
-  isEmpty: {
-    label: trans("table.isEmpty"),
-    filter: (filterValue, data) => {
-      return data === "" || isNil(data);
-    },
-  },
-  isNotEmpty: {
-    label: trans("table.isNotEmpty"),
-    filter: (filterValue, data) => {
-      return data !== "" && !isNil(data);
-    },
-  },
-  gt: {
-    label: trans("table.greater"),
-    filter: (value, data) => {
-      return _.gt(data, value);
-    },
-  },
-  gte: {
-    label: trans("table.greaterThanOrEquals"),
-    filter: (value, data) => {
-      return _.gte(data, value);
-    },
-  },
-  lt: {
-    label: trans("table.lessThan"),
-    filter: (value, data) => {
-      return _.gt(value, data);
-    },
-  },
-  lte: {
-    label: trans("table.lessThanOrEquals"),
-    filter: (value, data) => {
-      return _.gte(value, data);
-    },
-  },
-} as const;
-
-const tableFilterOperatorOptions = tableFilterOperators.map((operator) => ({
-  label: tableFilterOperatorMap[operator].label,
-  value: operator,
-}));
-
-type TableFilterDataType = {
-  columnKey: string;
-  filterValue: string;
-  operator: TableFilterOperator;
-};
-
-export type TableFilter = {
-  stackType: ValueFromOption<typeof filterStackOptions>;
-  filters: TableFilterDataType[];
-};
-
-const genFilterViewItem = (
-  columnKey: string = "",
-  filterValue: string = "",
-  operator?: TableFilterOperator
-) => {
-  return {
-    key: genRandomKey(),
-    columnKey: columnKey,
-    filterValue: filterValue,
-    operator: operator,
-  } as const;
-};
-
-type FilterItemType = ReturnType<typeof genFilterViewItem>;
-
-function validFilterItem(filter: FilterItemType) {
-  return (
-    filter.columnKey &&
-    filter.operator &&
-    (filter.filterValue !== "" || noValueOperators.includes(filter.operator))
-  );
-}
-
-function emptyFilterItem(filter: FilterItemType) {
-  return !filter.columnKey && !filter.operator && filter.filterValue === "";
-}
-
-const FilterFooter = memo(function FilterFooter(props: {
-  filterItems: FilterItemType[];
-  setFilterItems: (items: FilterItemType[]) => void;
-  setVisible: (v: boolean) => void;
-}) {
-  const { filterItems, setFilterItems, setVisible } = props;
-
-  const handleAddFilter = useCallback(() => {
-    setFilterItems(filterItems.concat(genFilterViewItem()));
-  }, [filterItems, setFilterItems]);
-
-  const handleClearFilters = useCallback(() => {
-    setFilterItems([genFilterViewItem()]);
-  }, [setFilterItems]);
-
-  const handleClose = useCallback(() => {
-    setVisible(false);
-  }, [setVisible]);
-
-  return (
-    <FilterViewBottom>
-      <StyledLinkButton
-        text={trans("addItem")}
-        icon={<BluePlusIcon />}
-        onClick={handleAddFilter}
-      />
-      <TacoButton
-        onClick={handleClearFilters}
-        buttonType="delete"
-      >
-        {trans("table.clear")}
-      </TacoButton>
-      <TacoButton buttonType="primary" onClick={handleClose}>
-        {trans("ok")}
-      </TacoButton>
-    </FilterViewBottom>
-  );
-});
-
-FilterFooter.displayName = 'FilterFooter';
-
-const FilterContent = memo(function FilterContent(props: {
-  filterItems: FilterItemType[];
-  columnKeyNames: Array<[string, string]>;
-  stackTypeState: TableFilter["stackType"];
-  stackTypeValue: TableFilter["stackType"] | undefined;
-  updateFilter: (filterItem: FilterItemType) => void;
-  handleStackTypeChange: (value: TableFilter["stackType"]) => void;
-  removeFilter: (key: string) => void;
-}) {
-  const {
-    filterItems,
-    columnKeyNames,
-    stackTypeState,
-    stackTypeValue,
-    updateFilter,
-    handleStackTypeChange,
-    removeFilter,
-  } = props;
-
-  const handleColumnKeyChange = useCallback((filter: FilterItemType, value: string) => {
-    updateFilter({ ...filter, columnKey: value });
-  }, [updateFilter]);
-
-  const handleOperatorChange = useCallback((filter: FilterItemType, value: TableFilterOperator) => {
-    updateFilter({ ...filter, operator: value });
-  }, [updateFilter]);
-
-  const handleFilterValueChange = useCallback((filter: FilterItemType, e: React.ChangeEvent<HTMLInputElement>) => {
-    updateFilter({ ...filter, filterValue: e.target.value });
-  }, [updateFilter]);
-
-  return (
-    <FilterViewContent>
-      {filterItems &&
-        filterItems.map((filter, index) => (
-          <FilterItem key={filter.key}>
-            {index === 0 && (
-              <CommonTextLabel style={{ width: "65px" }}>
-                {trans("table.filterRule")}
-              </CommonTextLabel>
-            )}
-            {index >= 1 && (
-              <StyledCustomSelect
-                $width="65px"
-                border
-                defaultValue={stackTypeState}
-                value={stackTypeValue}
-                disabled={index > 1}
-                onChange={handleStackTypeChange}
-              >
-                {filterStackOptions.map((item) => (
-                  <CustomSelect.Option key={item.value} value={item.value}>
-                    <CustomSelectionOptionInner>
-                      {item.label}
-                    </CustomSelectionOptionInner>
-                  </CustomSelect.Option>
-                ))}
-              </StyledCustomSelect>
-            )}
-            <StyledCustomSelect
-              $width="160px"
-              options={columnKeyNames.map((c) => ({ label: c[1], value: c[0] }))}
-              value={filter.columnKey}
-              placeholder={trans("table.chooseColumnName")}
-              allowClear
-              onChange={(value) => handleColumnKeyChange(filter, value)}
-            />
-            <StyledCustomSelect
-              $width="160px"
-              defaultValue={filter.operator}
-              placeholder={trans("table.chooseCondition")}
-              allowClear
-              options={tableFilterOperatorOptions}
-              onChange={(value) => handleOperatorChange(filter, value)}
-            />
-            <StyledTacoInput
-              disabled={filter.operator && noValueOperators.includes(filter.operator)}
-              defaultValue={filter.filterValue}
-              onChange={(e) => handleFilterValueChange(filter, e)}
-            />
-            <StyledDeleteIcon
-              disabled={filterItems.length === 1 && emptyFilterItem(filterItems[0])}
-              onClick={() => removeFilter(filter.key)}
-            />
-          </FilterItem>
-        ))}
-    </FilterViewContent>
-  );
-});
-
-FilterContent.displayName = 'FilterContent';
-
-const TableFilterView = memo(function TableFilterView(props: {
-  columnKeyNames: Array<[string, string]>;
-  tableFilter: TableFilter;
-  onFilterChange: (filters: TableFilterDataType[], stackType: TableFilter["stackType"]) => void;
-  setVisible: (v: boolean) => void;
-}) {
-  const { columnKeyNames, tableFilter, onFilterChange, setVisible } = props;
-  const [stackTypeState, setStackTypeState] = useState(tableFilter.stackType);
-  const [filterItems, setFilterItems] = useState<FilterItemType[]>(() => 
-    tableFilter.filters.length > 0
-      ? tableFilter.filters.map((f) => genFilterViewItem(f.columnKey, f.filterValue, f.operator))
-      : [genFilterViewItem()]
-  );
-  const stackTypeValue = useMemo(() => filterStackOptions.find((f) => f.value === stackTypeState)?.value, [stackTypeState]);
-
-  const updateFilter = useCallback((filterItem: FilterItemType) => {
-    setFilterItems((items) =>
-      items.map((item) => (item.key === filterItem.key ? filterItem : item))
-    );
-  }, []);
-
-  const removeFilter = useCallback((key: string) => {
-    if (filterItems.length === 1) {
-      setFilterItems([genFilterViewItem()]);
-    } else {
-      setFilterItems((items) => items.filter((item) => item.key !== key));
-    }
-  }, [filterItems]);
-
-  const handleStackTypeChange = useCallback((value: TableFilter["stackType"]) => {
-    setStackTypeState(value);
-  }, []);
-
-  useEffect(() => {
-    onFilterChange(
-      filterItems
-        .filter(validFilterItem)
-        .map((f) => ({
-          filterValue: f.filterValue,
-          operator: f.operator!,
-          columnKey: f.columnKey,
-        })),
-      stackTypeState
-    );
-  }, [stackTypeState, filterItems, onFilterChange]);
-
-  return (
-    <SuspensionBox
-      title={trans("table.filter")}
-      onClose={() => setVisible(false)}
-      width={600}
-      scrollable
-      contentMaxHeight={292}
-      content={
-        <FilterContent
-          filterItems={filterItems}
-          columnKeyNames={columnKeyNames}
-          stackTypeState={stackTypeState}
-          stackTypeValue={stackTypeValue}
-          updateFilter={updateFilter}
-          handleStackTypeChange={handleStackTypeChange}
-          removeFilter={removeFilter}
-        />
-      }
-      footer={
-        <FilterFooter
-          filterItems={filterItems}
-          setFilterItems={setFilterItems}
-          setVisible={setVisible}
-        />
-      }
-    />
-  );
-});
-
-TableFilterView.displayName = 'TableFilterView';
-
 const ColumnSetting = memo(function ColumnSetting(props: {
   columns: Array<ColumnCompType>;
   setVisible: (v: boolean) => void;
@@ -664,7 +60,7 @@ const ColumnSetting = memo(function ColumnSetting(props: {
       const columnView = c.getView();
       const checked = checkedStates[idx];
       return (
-        <ColumnCheckItem key={columnView.dataIndex}>
+        <div key={columnView.dataIndex}>
           <CheckBox
             checked={checked}
             onChange={(e: CheckboxChangeEvent) => {
@@ -672,7 +68,7 @@ const ColumnSetting = memo(function ColumnSetting(props: {
             }}
           />
           <CommonTextLabel>{columnView.title || columnView.dataIndex}</CommonTextLabel>
-        </ColumnCheckItem>
+        </div>
       );
     }),
     [columns, checkedStates]
@@ -699,17 +95,17 @@ const ColumnSetting = memo(function ColumnSetting(props: {
       width={160}
       contentMaxHeight={150}
       scrollable
-      content={<ColumnSettingContent>{checkViews}</ColumnSettingContent>}
+      content={<div>{checkViews}</div>}
       footer={
-        <ColumnSettingFooter>
-          <ColumnCheckItem>
+        <div>
+          <div>
             <CheckBox
               checked={allChecked}
               onChange={handleSelectAll}
             />
             <CommonTextLabel>{trans("table.selectAll")}</CommonTextLabel>
-          </ColumnCheckItem>
-        </ColumnSettingFooter>
+          </div>
+        </div>
       }
     />
   );
@@ -752,7 +148,6 @@ ToolbarPopover.displayName = 'ToolbarPopover';
 
 export const TableToolbar = memo(function TableToolbar(props: {
   toolbar: ToolbarRowType;
-  $style: TableToolbarStyleType;
   pagination: PaginationProps;
   columns: Array<ColumnCompType>;
   onRefresh: () => void;
@@ -761,19 +156,14 @@ export const TableToolbar = memo(function TableToolbar(props: {
 }) {
   const {
     toolbar,
-    $style,
     pagination,
     columns,
     onRefresh,
     onDownload,
     onEvent,
   } = props;
-
-  const theme = useContext(ThemeContext)?.theme;
-  const filtered = useMemo(() => toolbar.filter.filters.length > 0, [toolbar.filter.filters.length]);
-  const [filterVisible, setFilterVisible] = useState(false);
   const [columnSettingVisible, setColumnSettingVisible] = useState(false);
-  const visibleColumns = useMemo(() => columns.filter((column) => !column.getView().hide), [columns]);
+
 
   const handleRefresh = useCallback(() => {
     onRefresh();
@@ -785,54 +175,13 @@ export const TableToolbar = memo(function TableToolbar(props: {
     onEvent("download");
   }, [onDownload, onEvent]);
 
- 
-
-  const handleColumnFilterChange = useCallback((filters: TableFilterDataType[], stackType: TableFilter["stackType"]) => {
-    if (
-      !_.isEqual(filters, toolbar.filter.filters) ||
-      stackType !== toolbar.filter.stackType
-    ) {
-      toolbar.onFilterChange(filters, stackType);
-      onEvent("filterChange");
-    }
-  }, [toolbar, onEvent]);
-
-  const columnKeyNameTuple = useMemo(() => {
-    return visibleColumns.map((column) => {
-      const c = column.getView();
-      return [c.dataIndex, c.title || c.dataIndex] as [string, string];
-    });
-  }, [visibleColumns]);
-
   return (
-    <ToolbarWrapper
-      $style={$style}
-      $filtered={filtered}
-      $theme={theme || defaultTheme}
-      $position={toolbar.position}
-      $fixedToolbar={toolbar.fixedToolbar}
-    >
-      <ToolbarWrapper2>
-        <ToolbarIcons className="toolbar-icons">
+    <ToolbarContainer>
+      <ToolbarRow>
+        <ToolbarIcons>
           {toolbar.showRefresh && (
             <RefreshIcon className="refresh" onClick={handleRefresh} />
           )}
-          {toolbar.showFilter && (
-            <ToolbarPopover
-              visible={filterVisible}
-              setVisible={setFilterVisible}
-              Icon={FilterIcon}
-              iconClassName="filter"
-              content={
-                <TableFilterView
-                  columnKeyNames={columnKeyNameTuple}
-                  tableFilter={toolbar.filter}
-                  setVisible={setFilterVisible}
-                  onFilterChange={handleColumnFilterChange}
-                />
-              }
-            />
-          )}
           {toolbar.showDownload && (
             <DownloadIcon className="download" onClick={handleDownload} />
           )}
@@ -857,8 +206,8 @@ export const TableToolbar = memo(function TableToolbar(props: {
             }
           }}
         />
-      </ToolbarWrapper2>
-    </ToolbarWrapper>
+      </ToolbarRow>
+    </ToolbarContainer>
   );
 });
 
@@ -868,11 +217,9 @@ export const TableToolbarComp = (function () {
   const childrenMap = {
     showRefresh: BoolControl,
     showDownload: BoolControl,
-    showFilter: BoolControl,
     columnSetting: BoolControl,
     fixedToolbar: BoolControl,
     // searchText: StringControl,
-    filter: stateComp<TableFilter>({ stackType: "and", filters: [] }),
     position: dropdownControl(positionOptions, "below"),
     columnSeparator: withDefault(StringControl, ','),
   };
@@ -880,18 +227,7 @@ export const TableToolbarComp = (function () {
   return new ControlNodeCompBuilder(childrenMap, (props, dispatch) => {
     return {
       ...props,
-      onFilterChange: (filters: TableFilterDataType[], stackType: TableFilter["stackType"]) => {
-        dispatch(
-          changeChildAction(
-            "filter",
-            {
-              stackType: stackType,
-              filters: filters,
-            },
-            false
-          )
-        );
-      },
+     
     };
   })
     .setPropertyViewFn((children) => [
@@ -900,7 +236,6 @@ export const TableToolbarComp = (function () {
         label: trans("table.fixedToolbar"),
         tooltip: trans("table.fixedToolbarTooltip")
       }),
-      children.showFilter.propertyView({ label: trans("table.showFilter") }),
       children.showRefresh.propertyView({ label: trans("table.showRefresh") }),
       children.showDownload.propertyView({ label: trans("table.showDownload") }),
       children.showDownload.getView() && children.columnSeparator.propertyView({
diff --git a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableUtils.tsx b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableUtils.tsx
index dc146030ba..af5dce28c1 100644
--- a/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableUtils.tsx
+++ b/client/packages/lowcoder/src/comps/comps/tableLiteComp/tableUtils.tsx
@@ -8,18 +8,15 @@ import {
 import type { SortOrder } from "antd/es/table/interface";
 import { __COLUMN_DISPLAY_VALUE_FN } from "./column/columnTypeCompBuilder";
 import { CellColorViewType, RawColumnType, Render } from "./column/tableColumnComp";
-import { TableFilter, tableFilterOperatorMap } from "./tableToolbarComp";
 import { SortValue, TableOnEventView } from "./tableTypes";
 import _ from "lodash";
 import { changeChildAction, CompAction, NodeToValue } from "lowcoder-core";
-import { EditableIcon } from "lowcoder-design";
 import { tryToNumber } from "util/convertUtils";
 import { JSONObject, JSONValue } from "util/jsonTypes";
 import { StatusType } from "./column/columnTypeComps/columnStatusComp";
 import { ColumnListComp, tableDataRowExample } from "./column/tableColumnListComp";
 import { TableColumnLinkStyleType, TableColumnStyleType } from "comps/controls/styleControlConstants";
 import Tooltip from "antd/es/tooltip";
-import InfoCircleOutlined from "@ant-design/icons/InfoCircleOutlined";
 import dayjs from "dayjs";
 
 export const COLUMN_CHILDREN_KEY = "children";
@@ -37,7 +34,7 @@ export type RecordType = JSONObject & { [OB_ROW_ORI_INDEX]: string };
 export function filterData(
   data: Array<RecordType>,
   searchValue: string,
-  filter: TableFilter,
+  filter: { filters: any[], stackType: string },
   showFilter: boolean
 ) {
   let resultData = data;
@@ -56,7 +53,7 @@ export function filterData(
       // filter
       for (let f of filter.filters) {
         const columnValue = row[f.columnKey];
-        const result = tableFilterOperatorMap[f.operator].filter(f.filterValue, columnValue);
+        const result = f.operator.filter(f.filterValue, columnValue);
         if (filter.stackType === "or" && result) {
           // one condition is met
           return true;
