Skip to content

Commit fa2d9e4

Browse files
Merge pull request #2023 from iamfaran/feat/table-tags
[Feat]: add new pending tags in suggestions for Table Tags Column
2 parents 242bbf4 + 90b1ac0 commit fa2d9e4

File tree

3 files changed

+91
-44
lines changed

3 files changed

+91
-44
lines changed

client/packages/lowcoder/src/comps/comps/tableComp/column/columnTypeComps/columnTagsComp.tsx

Lines changed: 46 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ const TagsControl = codeControl<Array<string> | string>(
5757
{ expectedType: "string | Array<string>", codeType: "JSON" }
5858
);
5959

60-
function getTagColor(tagText : any, tagOptions: any[]) {
61-
const foundOption = tagOptions.find((option: { label: any; }) => option.label === tagText);
60+
function getTagColor(tagText: string, tagOptions: TagOption[]): string | undefined {
61+
const foundOption = tagOptions.find(option => option.label === tagText);
6262
if (foundOption) {
6363
if (foundOption.colorType === "preset") {
6464
return foundOption.presetColor;
@@ -73,10 +73,10 @@ function getTagColor(tagText : any, tagOptions: any[]) {
7373
return colors[index];
7474
}
7575

76-
function getTagStyle(tagText: any, tagOptions: any[]) {
77-
const foundOption = tagOptions.find((option: { label: any; }) => option.label === tagText);
76+
function getTagStyle(tagText: string, tagOptions: TagOption[]): React.CSSProperties {
77+
const foundOption = tagOptions.find(option => option.label === tagText);
7878
if (foundOption) {
79-
const style: any = {};
79+
const style: React.CSSProperties = {};
8080

8181
// Handle color styling
8282
if (foundOption.colorType === "custom") {
@@ -113,11 +113,23 @@ function getTagStyle(tagText: any, tagOptions: any[]) {
113113
return {};
114114
}
115115

116-
function getTagIcon(tagText: any, tagOptions: any[]) {
116+
function getTagIcon(tagText: string, tagOptions: TagOption[]): React.ReactNode | undefined {
117117
const foundOption = tagOptions.find(option => option.label === tagText);
118118
return foundOption ? foundOption.icon : undefined;
119119
}
120120

121+
// Utility function to process comma-separated tags into individual tags
122+
function processTagItems(tagItems: string[]): string[] {
123+
const result: string[] = [];
124+
tagItems.forEach((item) => {
125+
if (item.split(",")[1]) {
126+
item.split(",").forEach((tag) => result.push(tag));
127+
}
128+
result.push(item);
129+
});
130+
return result;
131+
}
132+
121133
const childrenMap = {
122134
text: TagsControl,
123135
tagColors: ColoredTagOptionControl,
@@ -128,11 +140,25 @@ const getBaseValue: ColumnTypeViewFn<typeof childrenMap, string | string[], stri
128140
props
129141
) => props.text;
130142

143+
interface TagOption {
144+
label: string;
145+
colorType?: "preset" | "custom";
146+
presetColor?: string;
147+
color?: string;
148+
textColor?: string;
149+
border?: string;
150+
radius?: string;
151+
margin?: string;
152+
padding?: string;
153+
icon?: React.ReactNode;
154+
onEvent?: (eventType: string) => void;
155+
}
156+
131157
type TagEditPropsType = {
132158
value: string | string[];
133159
onChange: (value: string | string[]) => void;
134160
onChangeEnd: () => void;
135-
tagOptions: any[];
161+
tagOptions: TagOption[];
136162
};
137163

138164
export const Wrapper = styled.div`
@@ -240,16 +266,7 @@ export const TagStyled = styled(Tag)`
240266

241267
const TagEdit = React.memo((props: TagEditPropsType) => {
242268
const defaultTags = useContext(TagsContext);
243-
const [tags, setTags] = useState(() => {
244-
const result: string[] = [];
245-
defaultTags.forEach((item) => {
246-
if (item.split(",")[1]) {
247-
item.split(",").forEach((tag) => result.push(tag));
248-
}
249-
result.push(item);
250-
});
251-
return result;
252-
});
269+
const [tags, setTags] = useState(() => processTagItems(defaultTags));
253270
const [open, setOpen] = useState(false);
254271
const mountedRef = useRef(true);
255272

@@ -268,24 +285,16 @@ const TagEdit = React.memo((props: TagEditPropsType) => {
268285
// Update tags when defaultTags changes
269286
useEffect(() => {
270287
if (!mountedRef.current) return;
271-
272-
const result: string[] = [];
273-
defaultTags.forEach((item) => {
274-
if (item.split(",")[1]) {
275-
item.split(",").forEach((tag) => result.push(tag));
276-
}
277-
result.push(item);
278-
});
279-
setTags(result);
288+
setTags(processTagItems(defaultTags));
280289
}, [defaultTags]);
281290

282291
const handleSearch = useCallback((value: string) => {
283292
if (!mountedRef.current) return;
284293

285294
if (defaultTags.findIndex((item) => item.includes(value)) < 0) {
286-
setTags([...defaultTags, value]);
295+
setTags([...processTagItems(defaultTags), value]);
287296
} else {
288-
setTags(defaultTags);
297+
setTags(processTagItems(defaultTags));
289298
}
290299
props.onChange(value);
291300
}, [defaultTags, props.onChange]);
@@ -426,17 +435,15 @@ export const ColumnTagsComp = (function () {
426435
const tagStyle = getTagStyle(tagText, tagOptions);
427436

428437
return (
429-
<React.Fragment key={`${tag.split(' ').join('_')}-${index}`}>
430-
<TagStyled
431-
color={tagColor}
432-
icon={tagIcon}
433-
key={index}
434-
style={tagStyle}
435-
onClick={(e) => handleTagClick(e, tagText)}
436-
>
437-
{tagText}
438-
</TagStyled>
439-
</React.Fragment>
438+
<TagStyled
439+
key={`${tagText.split(' ').join('_')}-${index}`}
440+
color={tagColor}
441+
icon={tagIcon}
442+
style={tagStyle}
443+
onClick={(e) => handleTagClick(e, tagText)}
444+
>
445+
{tagText}
446+
</TagStyled>
440447
);
441448
});
442449
return (

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,6 @@ export class TableImplComp extends TableInitComp implements IContainer {
481481
return { ...oriRow, ...changeValues };
482482
})
483483
.value();
484-
// console.info("toUpdateRowsNode. input: ", input, " res: ", res);
485484
return res;
486485
});
487486
}
@@ -517,14 +516,25 @@ export class TableImplComp extends TableInitComp implements IContainer {
517516
oriDisplayData: this.oriDisplayDataNode(),
518517
withParams: this.children.columns.withParamsNode(),
519518
dataIndexes: this.children.columns.getColumnsNode("dataIndex"),
519+
changeSet: this.changeSetNode(),
520520
};
521521
const resNode = withFunction(fromRecord(nodes), (input) => {
522522
const dataIndexWithParamsDict = _(input.dataIndexes)
523523
.mapValues((dataIndex, idx) => input.withParams[idx])
524524
.mapKeys((withParams, idx) => input.dataIndexes[idx])
525525
.value();
526-
const res = getColumnsAggr(input.oriDisplayData, dataIndexWithParamsDict);
527-
// console.info("columnAggrNode: ", res);
526+
527+
const columnChangeSets: Record<string, Record<string, any>> = {};
528+
_.forEach(input.changeSet, (rowData, rowId) => {
529+
_.forEach(rowData, (value, dataIndex) => {
530+
if (!columnChangeSets[dataIndex]) {
531+
columnChangeSets[dataIndex] = {};
532+
}
533+
columnChangeSets[dataIndex][rowId] = value;
534+
});
535+
});
536+
537+
const res = getColumnsAggr(input.oriDisplayData, dataIndexWithParamsDict, columnChangeSets);
528538
return res;
529539
});
530540
return lastValueIfEqual(this, "columnAggrNode", [resNode, nodes] as const, (a, b) =>

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

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,17 +230,47 @@ export function getColumnsAggr(
230230
oriDisplayData: JSONObject[],
231231
dataIndexWithParamsDict: NodeToValue<
232232
ReturnType<InstanceType<typeof ColumnListComp>["withParamsNode"]>
233-
>
233+
>,
234+
columnChangeSets?: Record<string, Record<string, any>>
234235
): ColumnsAggrData {
235236
return _.mapValues(dataIndexWithParamsDict, (withParams, dataIndex) => {
236237
const compType = (withParams.wrap() as any).compType;
237238
const res: Record<string, JSONValue> & { compType: string } = { compType };
239+
238240
if (compType === "tag") {
239-
res.uniqueTags = _(oriDisplayData)
241+
const originalTags = _(oriDisplayData)
240242
.map((row) => row[dataIndex]!)
241243
.filter((tag) => !!tag)
244+
.value();
245+
246+
const pendingChanges = columnChangeSets?.[dataIndex] || {};
247+
const pendingTags = _(pendingChanges)
248+
.values()
249+
.filter((value) => !!value)
250+
.value();
251+
252+
const extractTags = (value: any): string[] => {
253+
if (!value) return [];
254+
if (_.isArray(value)) return value.map(String);
255+
if (typeof value === "string") {
256+
// Handle comma-separated tags
257+
if (value.includes(",")) {
258+
return value.split(",").map(tag => tag.trim()).filter(tag => tag);
259+
}
260+
return [value];
261+
}
262+
return [String(value)];
263+
};
264+
265+
const allTags = [
266+
...originalTags.flatMap(extractTags),
267+
...pendingTags.flatMap(extractTags)
268+
];
269+
270+
res.uniqueTags = _(allTags)
242271
.uniq()
243272
.value();
273+
244274
} else if (compType === "badgeStatus") {
245275
res.uniqueStatus = _(oriDisplayData)
246276
.map((row) => {

0 commit comments

Comments
 (0)