Skip to content

Commit d137a8c

Browse files
authored
Merge branch 'dev' into fix/css-issues
2 parents c3f9c34 + f6bb40f commit d137a8c

27 files changed

+1297
-260
lines changed

client/packages/lowcoder-core/lib/index.d.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/// <reference types="react" />
2-
import { ReactNode } from 'react';
2+
import React, { ReactNode } from 'react';
33

44
type EvalMethods = Record<string, Record<string, Function>>;
55
type CodeType = undefined | "JSON" | "Function" | "PureJSON";
@@ -613,6 +613,7 @@ declare abstract class MultiBaseComp<ChildrenType extends Record<string, Comp<un
613613
toJsonValue(): DataType;
614614
autoHeight(): boolean;
615615
changeChildAction(childName: string & keyof ChildrenType, value: ConstructorToDataType<new (...params: any) => ChildrenType[typeof childName]>): CompAction<JSONValue>;
616+
getRef(): React.RefObject<HTMLDivElement>;
616617
}
617618
declare function mergeExtra(e1: ExtraNodeType | undefined, e2: ExtraNodeType): ExtraNodeType;
618619

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

+1
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ export { ReactComponent as TextCompIcon } from "./icon-text-display.svg";
106106
export { ReactComponent as SwitchCompIcon } from "./icon-switch.svg";
107107
export { ReactComponent as TableCompIcon } from "./icon-table-comp.svg";
108108
export { ReactComponent as SelectCompIcon } from "./icon-insert-select.svg";
109+
export { ReactComponent as IconModal } from "./icon-modal.svg";
109110
export { ReactComponent as CheckboxCompIcon } from "./icon-checkboxes.svg";
110111
export { ReactComponent as RadioCompIcon } from "./icon-radio.svg";
111112
export { ReactComponent as TimeCompIcon } from "./icon-time.svg";

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

+5-3
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,10 @@ const StyledDivider = styled(Divider) <IProps>`
4646
}};
4747
padding: ${(props) => props.$style.padding};
4848
49-
border-top: ${(props) => (props.$style.borderWidth ? props.$style.borderWidth : "1px")} ${(props) => (props.dashed ? "dashed" : "solid")} ${(props) => props.$style.color};
50-
49+
border-top: ${(props) => (props.$style.borderWidth && props.$style.borderWidth != "0px" ? props.$style.borderWidth : "1px")} ${(props) => (props.dashed ? "dashed" : "solid")} ${(props) => props.$style.border};
50+
""
5151
.ant-divider-inner-text::before, .ant-divider-inner-text::after {
52-
border-block-start: ${(props) => (props.$style.borderWidth ? props.$style.borderWidth : "1px")} ${(props) => (props.dashed ? "dashed" : "solid")} ${(props) => props.$style.color} !important;
52+
border-block-start: ${(props) => (props.$style.borderWidth && props.$style.borderWidth != "0px" ? props.$style.borderWidth : "1px")} ${(props) => (props.dashed ? "dashed" : "solid")} ${(props) => props.$style.border} !important;
5353
border-block-start-color: inherit;
5454
border-block-end: 0;
5555
}
@@ -81,6 +81,8 @@ function fixOldStyleData(oldData: any) {
8181
return oldData;
8282
}
8383

84+
85+
8486
// Compatible with historical style data 2022-8-26
8587
export const DividerComp = migrateOldData(
8688
new UICompBuilder(childrenMap, (props) => {

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

+92-62
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@ import { BoolCodeControl } from "comps/controls/codeControl";
33
import { BoolControl } from "comps/controls/boolControl";
44
import { stringExposingStateControl, numberExposingStateControl } from "comps/controls/codeStateControl";
55
import { ChangeEventHandlerControl } from "comps/controls/eventHandlerControl";
6-
import { LabelControl } from "comps/controls/labelControl";
76
import { StepOptionControl } from "comps/controls/optionsControl";
87
import { styleControl } from "comps/controls/styleControl";
9-
import { StepsStyle, StepsStyleType } from "comps/controls/styleControlConstants";
8+
import { StepsStyle, StepsStyleType, heightCalculator, widthCalculator, marginCalculator } from "comps/controls/styleControlConstants";
109
import styled, { css } from "styled-components";
1110
import { UICompBuilder } from "../../generators";
1211
import { CommonNameConfig, NameConfig, withExposingConfigs } from "../../generators/withExposing";
@@ -17,7 +16,6 @@ import { trans } from "i18n";
1716
import { hasIcon } from "comps/utils";
1817
import { RefControl } from "comps/controls/refControl";
1918
import { dropdownControl } from "comps/controls/dropdownControl";
20-
2119
import { useContext, useState, useEffect } from "react";
2220
import { EditorContext } from "comps/editorState";
2321

@@ -79,15 +77,16 @@ const statusOptions = [
7977
]
8078

8179
const StepsChildrenMap = {
82-
initialValue: numberExposingStateControl("0"),
80+
initialValue: numberExposingStateControl("1"),
8381
value: stringExposingStateControl("value"),
84-
stepsStatus : stringExposingStateControl("process"),
82+
stepStatus : stringExposingStateControl("process"),
83+
stepPercent: numberExposingStateControl("60"),
8584
size: dropdownControl(sizeOptions, "default"),
8685
displayType : dropdownControl(typeOptions, "default"),
8786
direction: dropdownControl(directionOptions, "horizontal"),
8887
showDots : BoolControl,
8988
showIcons : BoolControl,
90-
label: LabelControl,
89+
selectable : BoolControl,
9190
labelPlacement: dropdownControl(directionOptions, "horizontal"),
9291
disabled: BoolCodeControl,
9392
onEvent: ChangeEventHandlerControl,
@@ -99,56 +98,81 @@ const StepsChildrenMap = {
9998
let StepControlBasicComp = (function () {
10099
return new UICompBuilder(StepsChildrenMap, (props) => {
101100

102-
// enabling user interaction to change the current step
103-
const [current, setCurrent] = useState(0);
101+
const StyledWrapper = styled.div<{ style: StepsStyleType }>`
102+
min-height: 24px;
103+
max-width: ${widthCalculator(props.style.margin)};
104+
max-height: ${heightCalculator(props.style.margin)};
105+
display: flex;
106+
flex-direction: column;
107+
justify-content: center;
108+
align-items: center;
109+
text-decoration: ${props.style.textDecoration};
110+
font-style: ${props.style.fontStyle};
111+
font-weight: ${props.style.textWeight};
112+
font-size: ${props.style.textSize};
113+
text-transform: ${props.style.textTransform};
114+
margin: ${props.style.margin};
115+
padding: ${props.style.padding};
116+
background-color: ${props.style.background};
117+
border: ${props.style.borderWidth} solid ${props.style.border};
118+
border-radius: ${props.style.radius};
119+
background-image: ${props.style.backgroundImage};
120+
background-repeat: ${props.style.backgroundImageRepeat};
121+
background-size: ${props.style.backgroundImageSize};
122+
background-position: ${props.style.backgroundImagePosition};
123+
background-origin: ${props.style.backgroundImageOrigin};
124+
.ant-steps-item { padding-top: 5px !important; }
125+
.ant-steps.ant-steps-label-vertical.ant-steps-small .ant-steps-item-icon { margin-top: 17px !important; }
126+
.ant-steps.ant-steps-label-vertical.ant-steps-default .ant-steps-item-icon { margin-top: 12px !important; }
127+
.ant-steps.ant-steps-dot .ant-steps-item-process .ant-steps-icon .ant-steps-icon-dot { margin-top: 4px !important; }
128+
.ant-steps.ant-steps-default .ant-steps-item-icon .ant-steps-icon-dot { margin-top: 9px !important; }
129+
.ant-steps.ant-steps-small .ant-steps-item-icon .ant-steps-icon-dot { margin-top: 4px !important; }
130+
.ant-steps.ant-steps-dot .ant-steps-item-title { margin-top: 10px !important; }
131+
.ant-steps.ant-steps-default.ant-steps-with-progress.ant-steps-label-horizontal .ant-steps-item.ant-steps-item-custom div.ant-steps-item-icon { margin-top:4px !important; }
132+
.ant-steps.ant-steps-default.ant-steps-with-progress.ant-steps-label-vertical .ant-steps-item.ant-steps-item-custom div.ant-steps-item-icon { margin-top:17px !important; }
133+
.ant-steps.ant-steps-dot.ant-steps-small.ant-steps-with-progress .ant-steps-item-icon .ant-progress { inset-block-start: -8px !important; inset-inline-start: -11px !important; }
134+
.ant-steps.ant-steps-dot.ant-steps-default.ant-steps-with-progress .ant-steps-item-icon .ant-progress { inset-block-start: -7px !important; inset-inline-start: -13px !important; }
135+
.ant-steps.ant-steps-small.ant-steps-with-progress .ant-steps-item:not(.ant-steps-item-custom) .ant-progress { inset-block-start: -5px !important; inset-inline-start: -5px !important; }
136+
.ant-steps.ant-steps-default.ant-steps-with-progress .ant-steps-item:not(.ant-steps-item-custom) .ant-progress { inset-block-start: -5px !important; inset-inline-start: -5px !important; }
137+
.ant-steps.ant-steps-small.ant-steps-with-progress .ant-steps-item.ant-steps-item-custom .ant-progress { inset-block-start: -5px !important; inset-inline-start: -10px !important; }
138+
.ant-steps.ant-steps-default.ant-steps-with-progress .ant-steps-item.ant-steps-item-custom .ant-progress { inset-block-start: -4px !important; inset-inline-start: -13px !important; }
139+
`;
140+
141+
const [current, setCurrent] = useState(props.initialValue.value - 1); // Convert 1-based index to 0-based.
104142

105-
// updating the state of current by the expose value
106143
useEffect(() => {
107-
setCurrent(Number(props.value.value));
144+
const newValue = Number(props.value.value);
145+
setCurrent(newValue - 1); // Adjust for 0-based index.
108146
}, [props.value.value]);
109147

110-
111-
const onChange = (current: number) => {
112-
setCurrent(current);
113-
if (props.options[current]?.value !== undefined) {
114-
props.value.onChange(props.options[current].value+"");
148+
const onChange = (index: number) => {
149+
if (props.selectable == false) return;
150+
const newIndex = Math.max(0, index);
151+
setCurrent(newIndex);
152+
if (props.options[newIndex]?.value !== undefined) {
153+
props.value.onChange(newIndex + 1 + ""); // Convert back to 1-based index for display.
115154
props.onEvent("change");
116155
}
117156
};
118157

119-
// margin-top: 17px; is important cause the dots where placed wrong.
120-
/*
121-
.ant-steps.ant-steps-small .ant-steps-item-icon {
122-
margin-top: 17px;
123-
}
124-
*/
125-
const StepsWrapper = styled.div`
126-
width: 100%;
127-
min-height: 24px;
128-
129-
`;
130-
131-
return props.label({
132-
children: (
133-
<StepsWrapper ref={props.viewRef}>
134-
<ConfigProvider
135-
theme={{
136-
components: {
137-
Steps: {
138-
colorPrimary: '#00b96b',
139-
algorithm: true,
140-
}
141-
},
142-
}}
143-
>
158+
return (
159+
<ConfigProvider
160+
theme={{
161+
token: {
162+
colorPrimary: props.style.activeBackground,
163+
colorText: props.style.titleText,
164+
colorTextDescription: props.style.text,
165+
fontFamily: props.style.fontFamily,
166+
}
167+
}}
168+
>
169+
<StyledWrapper style={props.style}>
144170
<Steps
145-
initial={Number(props.initialValue.value) - 1}
171+
initial={props.initialValue.value -1}
146172
current={current}
147-
onChange={(current) => {
148-
onChange(current);
149-
}}
150-
percent={60}
151-
status={props.stepsStatus.value as "error" | "finish" | "process" | "wait"}
173+
onChange={onChange}
174+
percent={props.stepPercent.value}
175+
status={props.stepStatus.value as "error" | "finish" | "process" | "wait"}
152176
type={props.displayType}
153177
size={props.size}
154178
labelPlacement={props.labelPlacement}
@@ -166,16 +190,16 @@ let StepControlBasicComp = (function () {
166190
/>
167191
))}
168192
</Steps>
169-
</ConfigProvider>
170-
</StepsWrapper>
171-
),
172-
});
193+
</StyledWrapper>
194+
</ConfigProvider>
195+
);
196+
173197
})
174198
.setPropertyViewFn((children) => (
175199
<>
176200
<Section name={sectionNames.basic}>
177201
{children.options.propertyView({})}
178-
{children.initialValue.propertyView({ label: trans("step.initialValue") })}
202+
{children.initialValue.propertyView({ label: trans("step.initialValue"), tooltip : trans("step.initialValueTooltip")})}
179203
</Section>
180204

181205
{["logic", "both"].includes(useContext(EditorContext).editorModeStatus) && (
@@ -184,14 +208,12 @@ let StepControlBasicComp = (function () {
184208
{children.onEvent.getPropertyView()}
185209
{disabledPropertyView(children)}
186210
{hiddenPropertyView(children)}
187-
{children.stepsStatus.propertyView({label: trans("step.status")})}
211+
{children.stepStatus.propertyView({label: trans("step.status")})}
212+
{children.stepPercent.propertyView({label: trans("step.percent")})}
213+
{children.selectable.propertyView({label: trans("step.selectable")})}
188214
</Section></>
189215
)}
190216

191-
{["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && (
192-
children.label.getPropertyView()
193-
)}
194-
195217
{["layout", "both"].includes(useContext(EditorContext).editorModeStatus) && (
196218
<Section name={sectionNames.layout}>
197219
{children.size.propertyView({
@@ -206,12 +228,18 @@ let StepControlBasicComp = (function () {
206228
label: trans("step.direction"),
207229
radioButton: true,
208230
})}
209-
{children.labelPlacement.propertyView({
210-
label: trans("step.labelPlacement"),
211-
radioButton: true,
212-
})}
213-
{children.showDots.propertyView({label: trans("step.showDots")})}
214-
{children.showIcons.propertyView({label: trans("step.showIcons")})}
231+
{ children.direction.getView() == "horizontal" &&
232+
children.labelPlacement.propertyView({
233+
label: trans("step.labelPlacement"),
234+
radioButton: true,
235+
})
236+
}
237+
{ children.displayType.getView() != "inline" && !children.showIcons.getView() && (
238+
children.showDots.propertyView({label: trans("step.showDots")}
239+
))}
240+
{ children.displayType.getView() != "inline" && !children.showDots.getView() && (
241+
children.showIcons.propertyView({label: trans("step.showIcons")}
242+
))}
215243
</Section>
216244
)}
217245

@@ -228,5 +256,7 @@ let StepControlBasicComp = (function () {
228256

229257
export const StepComp = withExposingConfigs(StepControlBasicComp, [
230258
new NameConfig("value", trans("step.valueDesc")),
259+
new NameConfig("stepStatus", trans("step.status") ),
260+
new NameConfig("stepPercent", trans("step.percent")),
231261
...CommonNameConfig,
232262
]);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import { trans } from "i18n";
2+
import {
3+
CommonNameConfig,
4+
MultiBaseComp,
5+
NameConfig,
6+
stringExposingStateControl,
7+
UICompBuilder,
8+
withExposingConfigs,
9+
withMethodExposing
10+
} from "lowcoder-sdk";
11+
import { TourChildrenMap, TourPropertyView } from "./tourPropertyView";
12+
import { Tour, TourProps } from "antd";
13+
import React, { useContext } from "react";
14+
import { EditorContext } from "@lowcoder-ee/comps/editorState";
15+
import { GridItemComp } from "@lowcoder-ee/comps/comps/gridItemComp";
16+
import { HookComp } from "@lowcoder-ee/comps/hooks/hookComp";
17+
import { TemporaryStateItemComp } from "@lowcoder-ee/comps/comps/temporaryStateComp";
18+
19+
/**
20+
* This component builds the Property Panel and the fake 'UI' for the Tour component
21+
*/
22+
let TourBasicComp = (function() {
23+
const childrenMap = {
24+
...TourChildrenMap,
25+
defaultValue: stringExposingStateControl("defaultValue"),
26+
value: stringExposingStateControl("value")
27+
// style: styleControl(SelectStyle),
28+
};
29+
return new UICompBuilder(childrenMap, (props, dispatch) => {
30+
const editorState = useContext(EditorContext);
31+
const compMap: (GridItemComp | HookComp | InstanceType<typeof TemporaryStateItemComp>)[] = Object.values(editorState.getAllUICompMap());
32+
33+
const steps: TourProps["steps"] = props.options.map((step) => {
34+
const targetName = step.target;
35+
let target = undefined;
36+
const compListItem = compMap.find((compItem) => compItem.children.name.getView() === targetName);
37+
if (compListItem) {
38+
console.log(`setting selected comp to ${compListItem}`);
39+
try {
40+
target = ((compListItem as MultiBaseComp).children.comp as GridItemComp).getRef?.();
41+
} catch (e) {
42+
target = ((compListItem as MultiBaseComp).children.comp as HookComp).getRef?.();
43+
}
44+
}
45+
46+
return {
47+
/**
48+
* I'm pretty sure it's safe to use dangerouslySetInnerHTML here as any creator of an app
49+
* will have unrestricted access to the data of any user anyway. E.g. have a button that
50+
* just sends the current cookies wherever, thus the developer of the app must be trusted
51+
* in all cases
52+
* This even applies to things like <b onmouseover="alert('mouseover');">, because the
53+
* app creator might desire functionality like this.
54+
*/
55+
title: (<div dangerouslySetInnerHTML={{ __html: step.title }} />),
56+
description: (<div dangerouslySetInnerHTML={{ __html: step.description }} />),
57+
target: target?.current,
58+
arrow: step.arrow,
59+
placement: step.placement === "" ? undefined : step.placement,
60+
mask: step.mask,
61+
cover: step.cover ? (<img src={step.cover} />) : undefined,
62+
type: step.type === "" ? undefined : step.type,
63+
};
64+
});
65+
66+
return (
67+
<Tour
68+
steps={steps}
69+
open={props.open.value}
70+
onClose={() => props.open.onChange(false)}
71+
// indicatorsRender={(current, total) => props.indicatorsRender(current, total)} // todo enable later
72+
disabledInteraction={props.disabledInteraction}
73+
arrow={props.arrow}
74+
placement={props.placement === "" ? undefined : props.placement}
75+
type={props.type === "" ? undefined : props.type}
76+
mask={props.mask}
77+
/>
78+
);
79+
})
80+
.setPropertyViewFn((children) => <TourPropertyView {...children} />)
81+
.build();
82+
})();
83+
84+
export const TourComp = withMethodExposing(TourBasicComp, [
85+
{
86+
method: {
87+
name: "startTour",
88+
description: "Triggers the tour to start",
89+
params: []
90+
},
91+
execute: (comp, values) => {
92+
comp.children.open.getView().onChange(true);
93+
}
94+
}
95+
]);

0 commit comments

Comments
 (0)