Skip to content

Commit 6a66978

Browse files
Merge pull request #1939 from iamfaran/feat/table-lite
[Feat] Table Lite Component
2 parents 1793cdf + 85f690f commit 6a66978

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+9303
-1
lines changed

client/packages/lowcoder/src/comps/comps/tableComp/tablePropertyView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ const ColumnDropdown = styled(Dropdown)`
100100
const ColumnBatchOptionWrapper = styled.div`
101101
display: flex;
102102
align-items: center;
103-
color: ${GreyTextColor}
103+
color: ${GreyTextColor};
104104
line-height: 16px;
105105
font-size: 13px;
106106
`;
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import { DateTimeComp } from "comps/comps/tableComp/column/columnTypeComps/columnDateTimeComp";
2+
import { TimeComp } from "./columnTypeComps/columnTimeComp";
3+
import { ButtonComp } from "./simpleColumnTypeComps";
4+
import { withType } from "comps/generators";
5+
import { trans } from "i18n";
6+
import { Dropdown } from "lowcoder-design/src/components/Dropdown";
7+
import { BooleanComp } from "./columnTypeComps/columnBooleanComp";
8+
import { SwitchComp } from "./columnTypeComps/columnSwitchComp";
9+
import { DateComp } from "./columnTypeComps/columnDateComp";
10+
import { ImageComp } from "./columnTypeComps/columnImgComp";
11+
import { LinkComp } from "./columnTypeComps/columnLinkComp";
12+
import { ColumnLinksComp } from "./columnTypeComps/columnLinksComp";
13+
import { ColumnMarkdownComp } from "./columnTypeComps/columnMarkdownComp";
14+
import { ProgressComp } from "./columnTypeComps/columnProgressComp";
15+
import { RatingComp } from "./columnTypeComps/columnRatingComp";
16+
import { BadgeStatusComp } from "./columnTypeComps/columnStatusComp";
17+
import { ColumnTagsComp } from "./columnTypeComps/columnTagsComp";
18+
import { ColumnSelectComp } from "./columnTypeComps/columnSelectComp";
19+
import { SimpleTextComp } from "./columnTypeComps/simpleTextComp";
20+
import { ColumnNumberComp } from "./columnTypeComps/ColumnNumberComp";
21+
22+
import { ColumnAvatarsComp } from "./columnTypeComps/columnAvatarsComp";
23+
import { ColumnDropdownComp } from "./columnTypeComps/columnDropdownComp";
24+
25+
export type CellProps = {
26+
tableSize?: string;
27+
candidateTags?: string[];
28+
candidateStatus?: { text: string; status: any }[];
29+
textOverflow?: boolean;
30+
cellTooltip?: string;
31+
onTableEvent?: (eventName: any) => void;
32+
cellIndex?: string;
33+
};
34+
35+
const actionOptions = [
36+
{
37+
label: trans("table.avatars"),
38+
value: "avatars",
39+
},
40+
{
41+
label: trans("table.text"),
42+
value: "text",
43+
},
44+
{
45+
label: trans("table.number"),
46+
value: "number",
47+
},
48+
{
49+
label: trans("table.link"),
50+
value: "link",
51+
},
52+
{
53+
label: trans("table.links"),
54+
value: "links",
55+
},
56+
{
57+
label: trans("table.tag"),
58+
value: "tag",
59+
},
60+
{
61+
label: trans("table.select"),
62+
value: "select",
63+
},
64+
{
65+
label: trans("table.dropdown"),
66+
value: "dropdown",
67+
},
68+
{
69+
label: trans("table.badgeStatus"),
70+
value: "badgeStatus",
71+
},
72+
{
73+
label: trans("table.button"),
74+
value: "button",
75+
},
76+
{
77+
label: trans("table.image"),
78+
value: "image",
79+
},
80+
{
81+
label: trans("table.time"),
82+
value: "time",
83+
},
84+
85+
{
86+
label: trans("table.date"),
87+
value: "date",
88+
},
89+
{
90+
label: trans("table.dateTime"),
91+
value: "dateTime",
92+
},
93+
{
94+
label: "Markdown",
95+
value: "markdown",
96+
},
97+
{
98+
label: trans("table.boolean"),
99+
value: "boolean",
100+
},
101+
{
102+
label: trans("table.switch"),
103+
value: "switch",
104+
},
105+
{
106+
label: trans("table.rating"),
107+
value: "rating",
108+
},
109+
{
110+
label: trans("table.progress"),
111+
value: "progress",
112+
},
113+
] as const;
114+
115+
export const ColumnTypeCompMap = {
116+
avatars: ColumnAvatarsComp,
117+
text: SimpleTextComp,
118+
number: ColumnNumberComp,
119+
button: ButtonComp,
120+
badgeStatus: BadgeStatusComp,
121+
link: LinkComp,
122+
tag: ColumnTagsComp,
123+
select: ColumnSelectComp,
124+
dropdown: ColumnDropdownComp,
125+
links: ColumnLinksComp,
126+
image: ImageComp,
127+
markdown: ColumnMarkdownComp,
128+
dateTime: DateTimeComp,
129+
boolean: BooleanComp,
130+
switch: SwitchComp,
131+
rating: RatingComp,
132+
progress: ProgressComp,
133+
date: DateComp,
134+
time: TimeComp,
135+
};
136+
137+
type ColumnTypeMapType = typeof ColumnTypeCompMap;
138+
export type ColumnTypeKeys = keyof ColumnTypeMapType;
139+
140+
const TypedColumnTypeComp = withType(ColumnTypeCompMap, "text");
141+
142+
export class ColumnTypeComp extends TypedColumnTypeComp {
143+
override getView() {
144+
const childView = this.children.comp.getView();
145+
return {
146+
view: (cellProps: CellProps) => {
147+
return childView(cellProps);
148+
},
149+
value: this.children.comp.getDisplayValue(),
150+
};
151+
}
152+
153+
private handleTypeChange: (value: ColumnTypeKeys) => void = (value) => {
154+
// Keep the previous text value, some components do not have text, the default value is currentCell
155+
let textRawData = "{{currentCell}}";
156+
if (this.children.comp.children.hasOwnProperty("text")) {
157+
textRawData = (this.children.comp.children as any).text.toJsonValue();
158+
}
159+
this.dispatchChangeValueAction({
160+
compType: value,
161+
comp: { text: textRawData },
162+
} as any);
163+
}
164+
165+
override getPropertyView() {
166+
return (
167+
<>
168+
<Dropdown
169+
showSearch={true}
170+
value={this.children.compType.getView()}
171+
options={actionOptions}
172+
label={trans("table.columnType")}
173+
onChange={this.handleTypeChange}
174+
/>
175+
{this.children.comp.getPropertyView()}
176+
</>
177+
);
178+
}
179+
}
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
import { CellViewReturn } from "components/table/EditableCell";
2+
import { stateComp } from "comps/generators";
3+
import {
4+
MultiCompBuilder,
5+
PropertyViewFnTypeForComp,
6+
ToConstructor,
7+
ViewFnTypeForComp,
8+
} from "comps/generators/multi";
9+
import _ from "lodash";
10+
import {
11+
CompConstructor,
12+
ConstructorToNodeType,
13+
fromRecord,
14+
NodeToValue,
15+
RecordConstructorToComp,
16+
withFunction,
17+
} from "lowcoder-core";
18+
import { ReactNode } from "react";
19+
import { JSONValue } from "util/jsonTypes";
20+
21+
export const __COLUMN_DISPLAY_VALUE_FN = "__COLUMN_DISPLAY_VALUE_FN";
22+
23+
type RecordConstructorToNodeValue<T> = {
24+
[K in keyof T]: NodeToValue<ConstructorToNodeType<T[K]>>;
25+
};
26+
27+
type ViewValueFnType<ChildrenCtorMap extends Record<string, CompConstructor<unknown>>> = (
28+
nodeValue: RecordConstructorToNodeValue<ChildrenCtorMap>
29+
) => JSONValue;
30+
31+
export type ColumnTypeViewFn<ChildrenCtroMap, T extends JSONValue, ViewReturn> = ViewFnTypeForComp<
32+
ViewReturn,
33+
RecordConstructorToComp<ChildrenCtroMap>
34+
>;
35+
36+
export class ColumnTypeCompBuilder<
37+
ChildrenCtorMap extends Record<string, CompConstructor<unknown>>,
38+
T extends JSONValue = JSONValue
39+
> {
40+
private childrenMap: ChildrenCtorMap;
41+
private propertyViewFn?: PropertyViewFnTypeForComp<
42+
RecordConstructorToComp<ChildrenCtorMap>
43+
>;
44+
private stylePropertyViewFn?: PropertyViewFnTypeForComp<
45+
RecordConstructorToComp<ChildrenCtorMap>
46+
>;
47+
private cleanupFunctions: (() => void)[] = [];
48+
49+
constructor(
50+
childrenMap: ChildrenCtorMap,
51+
private viewFn: ColumnTypeViewFn<ChildrenCtorMap, T, ReactNode>,
52+
private displayValueFn: ViewValueFnType<ChildrenCtorMap>,
53+
private baseValueFn?: ColumnTypeViewFn<ChildrenCtorMap, T, T>
54+
) {
55+
this.childrenMap = { ...childrenMap } as ChildrenCtorMap;
56+
}
57+
58+
setEditViewFn(_: any) {
59+
// Edit views are disabled in Table Lite; keep chainability without storing
60+
return this;
61+
}
62+
63+
setPropertyViewFn(
64+
propertyViewFn: PropertyViewFnTypeForComp<
65+
RecordConstructorToComp<ChildrenCtorMap>
66+
>
67+
) {
68+
this.propertyViewFn = propertyViewFn;
69+
return this;
70+
}
71+
72+
setStylePropertyViewFn(
73+
stylePropertyViewFn: PropertyViewFnTypeForComp<
74+
RecordConstructorToComp<ChildrenCtorMap>
75+
>
76+
) {
77+
this.stylePropertyViewFn = stylePropertyViewFn;
78+
return this;
79+
}
80+
81+
build() {
82+
if (!this.propertyViewFn) {
83+
throw new Error("need property view fn");
84+
}
85+
86+
// Memoize the props processing
87+
const memoizedViewFn = _.memoize(
88+
(props: any, dispatch: any) => {
89+
const baseValue = this.baseValueFn?.(props, dispatch);
90+
const normalView = this.viewFn(props, dispatch);
91+
return normalView;
92+
},
93+
(props) => {
94+
let safeOptions = [];
95+
let safeAvatars = [];
96+
if(props.options) {
97+
safeOptions = props.options.map((option: Record<string, any>) => {
98+
const {prefixIcon, suffixIcon, ...safeOption} = option;
99+
return safeOption;
100+
})
101+
}
102+
if(props.avatars) {
103+
safeAvatars = props.avatars.map((avatar: Record<string, any>) => {
104+
const {AvatarIcon, ...safeAvatar} = avatar;
105+
return safeAvatar;
106+
})
107+
}
108+
const {
109+
prefixIcon,
110+
suffixIcon,
111+
iconFalse,
112+
iconTrue,
113+
iconNull,
114+
tagColors,
115+
options,
116+
avatars,
117+
...safeProps
118+
} = props;
119+
return safeProps;
120+
}
121+
);
122+
123+
const viewFn: ColumnTypeViewFn<ChildrenCtorMap, T, CellViewReturn> =
124+
(props, dispatch): CellViewReturn =>
125+
(cellProps) => memoizedViewFn({ ...props, ...cellProps } as any, dispatch);
126+
127+
const ColumnTypeCompTmp = new MultiCompBuilder(
128+
(this.childrenMap as unknown) as ToConstructor<
129+
RecordConstructorToComp<ChildrenCtorMap>
130+
>,
131+
viewFn
132+
)
133+
.setPropertyViewFn(this.propertyViewFn)
134+
.build();
135+
136+
const displayValueFn = this.displayValueFn;
137+
138+
return class extends ColumnTypeCompTmp {
139+
// table cell data
140+
private _displayValue: JSONValue = null;
141+
private cleanupFunctions: (() => void)[] = [];
142+
constructor(props: any) {
143+
super(props);
144+
this.cleanupFunctions.push(() => {
145+
this._displayValue = null;
146+
memoizedViewFn.cache.clear?.();
147+
});
148+
}
149+
150+
override extraNode() {
151+
return {
152+
node: {
153+
[__COLUMN_DISPLAY_VALUE_FN]: withFunction(
154+
fromRecord(this.childrenNode()),
155+
() => displayValueFn
156+
),
157+
},
158+
updateNodeFields: (value: any) => {
159+
const displayValueFunc = value[__COLUMN_DISPLAY_VALUE_FN];
160+
this._displayValue = displayValueFunc(value);
161+
return { displayValue: this._displayValue };
162+
},
163+
};
164+
}
165+
166+
/**
167+
* Get the data actually displayed by the table cell
168+
*/
169+
getDisplayValue() {
170+
return this._displayValue;
171+
}
172+
173+
componentWillUnmount() {
174+
// Cleanup all registered cleanup functions
175+
this.cleanupFunctions.forEach(cleanup => cleanup());
176+
this.cleanupFunctions = [];
177+
}
178+
};
179+
}
180+
181+
// Cleanup method to be called when the builder is no longer needed
182+
cleanup() {
183+
this.cleanupFunctions.forEach(cleanup => cleanup());
184+
this.cleanupFunctions = [];
185+
}
186+
}

0 commit comments

Comments
 (0)