Skip to content

Commit b7f1d33

Browse files
Merge pull request #1818 from kamalqureshi/tags_component
Tags component
2 parents 12f2d45 + 3ede19e commit b7f1d33

File tree

11 files changed

+340
-8
lines changed

11 files changed

+340
-8
lines changed

client/packages/lowcoder-design/src/icons/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@ export { ReactComponent as VideoCameraStreamCompIconSmall } from "./v2/camera-st
355355
export { ReactComponent as VideoScreenshareCompIconSmall } from "./v2/screen-share-stream-s.svg"; // new
356356
export { ReactComponent as SignatureCompIconSmall } from "./v2/signature-s.svg";
357357
export { ReactComponent as StepCompIconSmall } from "./v2/steps-s.svg";
358+
export { ReactComponent as TagsCompIconSmall } from "./v2/tags-s.svg"
358359

359360

360361
export { ReactComponent as CandlestickChartCompIconSmall } from "./v2/candlestick-chart-s.svg"; // new
@@ -468,6 +469,7 @@ export { ReactComponent as SignatureCompIcon } from "./v2/signature-m.svg";
468469
export { ReactComponent as GanttCompIcon } from "./v2/gantt-chart-m.svg";
469470
export { ReactComponent as KanbanCompIconSmall } from "./v2/kanban-s.svg";
470471
export { ReactComponent as KanbanCompIcon } from "./v2/kanban-m.svg";
472+
export { ReactComponent as TagsCompIcon } from "./v2/tags-l.svg";
471473

472474
export { ReactComponent as CandlestickChartCompIcon } from "./v2/candlestick-chart-m.svg";
473475
export { ReactComponent as FunnelChartCompIcon } from "./v2/funnel-chart-m.svg";
Lines changed: 10 additions & 0 deletions
Loading
Lines changed: 10 additions & 0 deletions
Loading

client/packages/lowcoder/src/comps/comps/selectInputComp/multiSelectComp.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ let MultiSelectBasicComp = (function () {
3030
padding: PaddingControl,
3131
};
3232
return new UICompBuilder(childrenMap, (props, dispatch) => {
33-
const valueSet = new Set<any>(props.options.map((o) => o.value)); // Filter illegal default values entered by the user
33+
const valueSet = new Set<any>((props.options as any[]).map((o: any) => o.value)); // Filter illegal default values entered by the user
3434
const [
3535
validateState,
3636
handleChange,

client/packages/lowcoder/src/comps/comps/selectInputComp/selectComp.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ let SelectBasicComp = (function () {
3939
const propsRef = useRef<RecordConstructorToView<typeof childrenMap>>(props);
4040
propsRef.current = props;
4141

42-
const valueSet = new Set<any>(props.options.map((o) => o.value)); // Filter illegal default values entered by the user
42+
const valueSet = new Set<any>((props.options as any[]).map((o: any) => o.value)); // Filter illegal default values entered by the user
4343

4444
return props.label({
4545
required: props.required,
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import {
2+
BoolCodeControl,
3+
ButtonEventHandlerControl,
4+
InputLikeStyle,
5+
NameConfig,
6+
Section,
7+
UICompBuilder,
8+
hiddenPropertyView,
9+
sectionNames,
10+
showDataLoadingIndicatorsPropertyView,
11+
styleControl,
12+
withExposingConfigs
13+
} from "@lowcoder-ee/index.sdk";
14+
import styled from "styled-components";
15+
import React, { useContext } from "react";
16+
import { trans } from "i18n";
17+
import { Tag } from "antd";
18+
import { EditorContext } from "comps/editorState";
19+
import { PresetStatusColorTypes } from "antd/es/_util/colors";
20+
import { hashToNum } from "util/stringUtils";
21+
import { TagsCompOptionsControl } from "comps/controls/optionsControl";
22+
import { useCompClickEventHandler } from "@lowcoder-ee/comps/utils/useCompClickEventHandler";
23+
24+
const colors = PresetStatusColorTypes;
25+
26+
// These functions are used for individual tag styling
27+
function getTagColor(tagText : any, tagOptions: any[]) {
28+
const foundOption = tagOptions.find((option: { label: any; }) => option.label === tagText);
29+
if (foundOption) {
30+
if (foundOption.colorType === "preset") {
31+
return foundOption.presetColor;
32+
} else if (foundOption.colorType === "custom") {
33+
return undefined;
34+
}
35+
return foundOption.color;
36+
}
37+
const index = Math.abs(hashToNum(tagText)) % colors.length;
38+
return colors[index];
39+
}
40+
41+
const getTagStyle = (tagText: any, tagOptions: any[], baseStyle: any = {}) => {
42+
const foundOption = tagOptions.find((option: { label: any; }) => option.label === tagText);
43+
if (foundOption) {
44+
const style: any = { ...baseStyle };
45+
46+
if (foundOption.colorType === "custom") {
47+
style.backgroundColor = foundOption.color;
48+
style.color = foundOption.textColor;
49+
style.border = `1px solid ${foundOption.color}`;
50+
}
51+
52+
if (foundOption.border) {
53+
style.borderColor = foundOption.border;
54+
if (!foundOption.colorType || foundOption.colorType !== "custom") {
55+
style.border = `1px solid ${foundOption.border}`;
56+
}
57+
}
58+
59+
if (foundOption.radius) {
60+
style.borderRadius = foundOption.radius;
61+
}
62+
63+
if (foundOption.margin) {
64+
style.margin = foundOption.margin;
65+
}
66+
67+
if (foundOption.padding) {
68+
style.padding = foundOption.padding;
69+
}
70+
71+
return style;
72+
}
73+
return baseStyle;
74+
};
75+
76+
function getTagIcon(tagText: any, tagOptions: any[]) {
77+
const foundOption = tagOptions.find(option => option.label === tagText);
78+
return foundOption ? foundOption.icon : undefined;
79+
}
80+
81+
const multiTags = (function () {
82+
83+
const StyledTag = styled(Tag)<{ $style: any, $bordered: boolean, $customStyle: any }>`
84+
display: flex;
85+
justify-content: center;
86+
align-items: center;
87+
width: 100%;
88+
background: ${(props) => props.$customStyle?.backgroundColor || props.$style?.background};
89+
color: ${(props) => props.$customStyle?.color || props.$style?.text};
90+
border-radius: ${(props) => props.$customStyle?.borderRadius || props.$style?.borderRadius};
91+
border: ${(props) => {
92+
if (props.$customStyle?.border) return props.$customStyle.border;
93+
return props.$bordered ? `${props.$style?.borderStyle} ${props.$style?.borderWidth} ${props.$style?.border}` : 'none';
94+
}};
95+
padding: ${(props) => props.$customStyle?.padding || props.$style?.padding};
96+
margin: ${(props) => props.$customStyle?.margin || props.$style?.margin};
97+
font-size: ${(props) => props.$style?.textSize};
98+
font-weight: ${(props) => props.$style?.fontWeight};
99+
cursor: pointer;
100+
`;
101+
102+
const StyledTagContainer = styled.div`
103+
display: flex;
104+
gap: 5px;
105+
padding: 5px;
106+
`;
107+
108+
const childrenMap = {
109+
options: TagsCompOptionsControl,
110+
style: styleControl(InputLikeStyle, 'style'),
111+
onEvent: ButtonEventHandlerControl,
112+
borderless: BoolCodeControl,
113+
enableIndividualStyling: BoolCodeControl,
114+
};
115+
116+
return new UICompBuilder(childrenMap, (props) => {
117+
const handleClickEvent = useCompClickEventHandler({onEvent: props.onEvent});
118+
119+
return (
120+
<StyledTagContainer>
121+
{props.options.map((tag, index) => {
122+
123+
// Use individual styling only if enableIndividualStyling is true
124+
const tagColor = props.enableIndividualStyling ? getTagColor(tag.label, props.options) : undefined;
125+
const tagIcon = props.enableIndividualStyling ? getTagIcon(tag.label, props.options) : tag.icon;
126+
const tagStyle = props.enableIndividualStyling ? getTagStyle(tag.label, props.options, props.style) : {};
127+
128+
return (
129+
<StyledTag
130+
key={`tag-${index}`}
131+
$style={props.style}
132+
$bordered={!props.borderless}
133+
$customStyle={tagStyle}
134+
icon={tagIcon}
135+
color={tagColor}
136+
onClick={() => handleClickEvent()}
137+
>
138+
{tag.label}
139+
</StyledTag>
140+
);
141+
})}
142+
</StyledTagContainer>
143+
);
144+
})
145+
.setPropertyViewFn((children: any) => {
146+
return (
147+
<>
148+
<Section name="Basic">
149+
{children.options.propertyView({})}
150+
</Section>
151+
152+
{["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && (
153+
<Section name={sectionNames.interaction}>
154+
{children.onEvent.getPropertyView()}
155+
{hiddenPropertyView(children)}
156+
{showDataLoadingIndicatorsPropertyView(children)}
157+
</Section>
158+
)}
159+
160+
{["layout", "both"].includes(
161+
useContext(EditorContext).editorModeStatus
162+
) && (
163+
<Section name={sectionNames.style}>
164+
{children.enableIndividualStyling.propertyView({
165+
label: trans("style.individualStyling"),
166+
tooltip: trans("style.individualStylingTooltip")
167+
})}
168+
{children.borderless.propertyView({ label: trans("style.borderless") })}
169+
{children.style.getPropertyView()}
170+
</Section>
171+
)}
172+
</>
173+
)
174+
})
175+
.build();
176+
})()
177+
178+
export const MultiTagsComp = withExposingConfigs(multiTags, [new NameConfig("options", "")]);
179+

client/packages/lowcoder/src/comps/controls/optionsControl.tsx

Lines changed: 106 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -557,6 +557,7 @@ const TabsOption = new MultiCompBuilder(
557557
</>
558558
))
559559
.build();
560+
560561

561562
export const TabsOptionControl = manualOptionsControl(TabsOption, {
562563
initOptions: [
@@ -567,6 +568,37 @@ export const TabsOptionControl = manualOptionsControl(TabsOption, {
567568
autoIncField: "id",
568569
});
569570

571+
const TagsOption = new MultiCompBuilder(
572+
{
573+
id: valueComp<number>(-1),
574+
label: StringControl,
575+
icon: IconControl,
576+
iconPosition: withDefault(LeftRightControl, "left"),
577+
hidden: BoolCodeControl,
578+
},
579+
(props) => props
580+
)
581+
.setPropertyViewFn((children) => (
582+
<>
583+
{children.label.propertyView({ label: trans("label") })}
584+
{children.icon.propertyView({ label: trans("icon") })}
585+
{children.iconPosition.propertyView({
586+
label: trans("tabbedContainer.iconPosition"),
587+
radioButton: true,
588+
})}
589+
{hiddenPropertyView(children)}
590+
</>
591+
))
592+
.build();
593+
594+
export const TagsOptionControl = optionsControl(TagsOption, {
595+
initOptions: [
596+
{ id: 0, label: "Option 1" },
597+
{ id: 1, label: "Option 2" },
598+
],
599+
autoIncField: "id",
600+
});
601+
570602
const StyledIcon = styled.span`
571603
margin: 0 4px 0 14px;
572604
`;
@@ -750,14 +782,83 @@ export const StepOptionControl = optionsControl(StepOption, {
750782
uniqField: "label",
751783
});
752784

785+
let TagsCompOptions = new MultiCompBuilder(
786+
{
787+
label: StringControl,
788+
icon: IconControl,
789+
colorType: withDefault(dropdownControl([
790+
{ label: trans("style.preset"), value: "preset" },
791+
{ label: trans("style.custom"), value: "custom" },
792+
] as const, "preset"), "preset"),
793+
presetColor: withDefault(dropdownControl(TAG_PRESET_COLORS, "blue"), "blue"),
794+
color: withDefault(ColorControl, "#1890ff"),
795+
textColor: withDefault(ColorControl, "#ffffff"),
796+
border: withDefault(ColorControl, ""),
797+
radius: withDefault(RadiusControl, ""),
798+
margin: withDefault(StringControl, ""),
799+
padding: withDefault(StringControl, ""),
800+
},
801+
(props) => props
802+
).build();
803+
804+
TagsCompOptions = class extends TagsCompOptions implements OptionCompProperty {
805+
propertyView(param: { autoMap?: boolean }) {
806+
const colorType = this.children.colorType.getView();
807+
return (
808+
<>
809+
{this.children.label.propertyView({ label: trans("coloredTagOptionControl.tag") })}
810+
{this.children.icon.propertyView({ label: trans("coloredTagOptionControl.icon") })}
811+
{this.children.colorType.propertyView({
812+
label: trans("style.colorType"),
813+
radioButton: true
814+
})}
815+
{colorType === "preset" && this.children.presetColor.propertyView({
816+
label: trans("style.presetColor")
817+
})}
818+
{colorType === "custom" && (
819+
<>
820+
{this.children.color.propertyView({ label: trans("coloredTagOptionControl.color") })}
821+
{this.children.textColor.propertyView({ label: trans("style.textColor") })}
822+
</>
823+
)}
824+
{this.children.border.propertyView({
825+
label: trans('style.border')
826+
})}
827+
{this.children.radius.propertyView({
828+
label: trans('style.borderRadius'),
829+
preInputNode: <StyledIcon as={IconRadius} title="" />,
830+
placeholder: '3px',
831+
})}
832+
{this.children.margin.propertyView({
833+
label: trans('style.margin'),
834+
preInputNode: <StyledIcon as={ExpandIcon} title="" />,
835+
placeholder: '3px',
836+
})}
837+
{this.children.padding.propertyView({
838+
label: trans('style.padding'),
839+
preInputNode: <StyledIcon as={CompressIcon} title="" />,
840+
placeholder: '3px',
841+
})}
842+
</>
843+
);
844+
}
845+
};
846+
847+
export const TagsCompOptionsControl = optionsControl(TagsCompOptions, {
848+
initOptions: [
849+
{ label: "Option 1", colorType: "preset", presetColor: "blue" },
850+
{ label: "Option 2", colorType: "preset", presetColor: "green" }
851+
],
852+
uniqField: "label",
853+
});
753854

754855
let ColoredTagOption = new MultiCompBuilder(
755856
{
756857
label: StringControl,
757858
icon: IconControl,
758859
colorType: withDefault(dropdownControl([
759-
{ label: "Preset", value: "preset" },
760-
{ label: "Custom", value: "custom" },
860+
{ label: trans("style.preset"), value: "preset" },
861+
{ label: trans("style.custom"), value: "custom" },
761862
] as const, "preset"), "preset"),
762863
presetColor: withDefault(dropdownControl(TAG_PRESET_COLORS, "blue"), "blue"),
763864
color: withDefault(ColorControl, "#1890ff"),
@@ -779,16 +880,16 @@ ColoredTagOption = class extends ColoredTagOption implements OptionCompProperty
779880
{this.children.label.propertyView({ label: trans("coloredTagOptionControl.tag") })}
780881
{this.children.icon.propertyView({ label: trans("coloredTagOptionControl.icon") })}
781882
{this.children.colorType.propertyView({
782-
label: "Color Type",
883+
label: trans("style.colorType"),
783884
radioButton: true
784885
})}
785886
{colorType === "preset" && this.children.presetColor.propertyView({
786-
label: "Preset Color"
887+
label: trans("style.presetColor")
787888
})}
788889
{colorType === "custom" && (
789890
<>
790891
{this.children.color.propertyView({ label: trans("coloredTagOptionControl.color") })}
791-
{this.children.textColor.propertyView({ label: "Text Color" })}
892+
{this.children.textColor.propertyView({ label: trans("style.textColor") })}
792893
</>
793894
)}
794895
{this.children.border.propertyView({

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy