Skip to content

Commit 3fd02e0

Browse files
committed
Get tour step refs to persist past reload
I don't know exactly what I was doing wrong, so I copied the dropdown control and modified it to accept the editor state. this could most likely be done cleaner by allowing for the existing dropdown control to accept a function with generic arguments, but that can be cleaned up later.
1 parent 26ea604 commit 3fd02e0

File tree

3 files changed

+181
-81
lines changed

3 files changed

+181
-81
lines changed

client/packages/lowcoder/src/comps/comps/tourComp/componentSelectorControl.tsx

Lines changed: 18 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ const ExecuteCompTmpAction = (function() {
4646
params: ParamsValueControl
4747
};
4848
return new MultiCompBuilder(childrenMap, () => {
49-
return () => undefined as (React.RefObject<HTMLElement> | undefined);
49+
// return () => undefined as (React.RefObject<HTMLElement> | undefined);
50+
// return () => undefined as (string | undefined);
51+
return undefined as (string | undefined);
5052
// return () => Promise.resolve(undefined as unknown);
5153
})
5254
.setPropertyViewFn(() => <></>)
@@ -72,45 +74,24 @@ export function targetCompAction(params: ExecuteCompActionOptions) {
7274
}
7375
}
7476

75-
selectedComp: React.RefObject<HTMLDivElement> | undefined;
77+
// selectedComp: React.RefObject<HTMLDivElement> | undefined;
78+
selectedComp: string|undefined;
7679
compList: (GridItemComp | HookComp | InstanceType<typeof TemporaryStateItemComp>)[] = [];
7780

78-
updateSelectedComp(compName: string): void {
79-
const compListItem = this.compList.find((compItem) => compItem.children.name.getView() === compName);
80-
if (compListItem) {
81-
console.log(`setting selected comp to ${compListItem}`)
82-
this.selectedComp = ((compListItem as MultiBaseComp).children.comp as GridItemComp).getRef();
83-
}
84-
}
81+
// updateSelectedComp(compName: string): void {
82+
// const compListItem = this.compList.find((compItem) => compItem.children.name.getView() === compName);
83+
// if (compListItem) {
84+
// console.log(`setting selected comp to ${compListItem}`)
85+
// this.selectedComp = ((compListItem as MultiBaseComp).children.comp as GridItemComp).getRef();
86+
// }
87+
// }
8588

86-
override getView(): () => (React.RefObject<HTMLElement> | undefined) {
87-
return () => this.selectedComp;
89+
// override getView(): () => (React.RefObject<HTMLElement> | undefined) {
90+
// override getView(): () => (string | undefined) {
91+
override getView(): string | undefined {
92+
return this.selectedComp;
8893
}
8994

90-
// override getView() {
91-
// const name = this.children.name.getView();
92-
// if (!name) {
93-
// return () => Promise.resolve();
94-
// }
95-
// return () =>
96-
// getPromiseAfterDispatch(
97-
// this.dispatch,
98-
// routeByNameAction(
99-
// name,
100-
// customAction<ExecuteAction>(
101-
// {
102-
// type: "execute",
103-
// methodName: this.children.methodName.getView(),
104-
// params: this.children.params.getView().map((x) => x.getView())
105-
// },
106-
// false
107-
// )
108-
// ),
109-
// {
110-
// notHandledError: trans("eventHandler.notHandledError")
111-
// }
112-
// );
113-
// }
11495
exposingNode() {
11596
return this.node();
11697
}
@@ -130,7 +111,6 @@ export function targetCompAction(params: ExecuteCompActionOptions) {
130111
});
131112

132113
function changeMethodAction(compName: string, methodName: string) {
133-
console.log("sldjafldkdf")
134114
const currentMethods = compMethods[compName] ?? {};
135115
const params = currentMethods[methodName];
136116
return {
@@ -161,7 +141,8 @@ export function targetCompAction(params: ExecuteCompActionOptions) {
161141
onChange={(value) => {
162142
console.log(`the value is ${value}`);
163143
// After the value is changed, update `selectedComp`
164-
this.updateSelectedComp(value);
144+
// this.updateSelectedComp(value);
145+
this.selectedComp = value;
165146

166147
console.log("am i here? ")
167148
return this.dispatchChangeValueAction(

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

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { styleControl } from "comps/controls/styleControl";
22
import { SelectStyle } from "comps/controls/styleControlConstants";
33
import { trans } from "i18n";
44
import {
5-
CommonNameConfig,
5+
CommonNameConfig, MultiBaseComp,
66
NameConfig,
77
stringExposingStateControl,
88
UICompBuilder,
@@ -13,6 +13,12 @@ import { baseSelectRefMethods, TourChildrenMap, TourPropertyView } from "./tourC
1313
import { TourInputCommonConfig } from "./tourInputConstants";
1414
import { Tour, TourProps } from "antd";
1515
import { PlacementType } from "@lowcoder-ee/comps/controls/tourStepControl";
16+
import { useContext } from "react";
17+
import { EditorContext } from "@lowcoder-ee/comps/editorState";
18+
import { GridItemComp } from "@lowcoder-ee/comps/comps/gridItemComp";
19+
import { HookComp } from "@lowcoder-ee/comps/hooks/hookComp";
20+
import { TemporaryStateItemComp } from "@lowcoder-ee/comps/comps/temporaryStateComp";
21+
import { delay } from "redux-saga/effects";
1622

1723
/**
1824
* This component builds the Property Panel and the fake 'UI' for the Tour component
@@ -25,13 +31,31 @@ let TourBasicComp = (function () {
2531
// style: styleControl(SelectStyle),
2632
};
2733
return new UICompBuilder(childrenMap, (props, dispatch) => {
28-
34+
const editorState = useContext(EditorContext);
35+
console.log("EDITOR STATE IS HERE")
36+
console.log(editorState)
37+
const compMap: (GridItemComp | HookComp | InstanceType<typeof TemporaryStateItemComp>)[] = Object.values(editorState.getAllUICompMap());
38+
console.log("COMP MAP IS HERE")
39+
console.log(compMap)
40+
2941
const steps: TourProps['steps'] = props.options.map((step) => {
42+
const targetName = step.target;
43+
let target = undefined;
44+
const compListItem = compMap.find((compItem) => compItem.children.name.getView() === targetName);
45+
if (compListItem) {
46+
console.log(`setting selected comp to ${compListItem}`)
47+
try {
48+
target = ((compListItem as MultiBaseComp).children.comp as GridItemComp).getRef();
49+
} catch (e) {
50+
target = ((compListItem as MultiBaseComp).children.comp as HookComp).getRef();
51+
}
52+
}
53+
3054
return {
3155
title: step.title,
3256
description: step.description,
33-
target: step.target()?.current,
34-
// arrow: step.arrow,
57+
target: target?.current,
58+
arrow: step.arrow || true,
3559
placement: step.placement as PlacementType,
3660
}
3761
})

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

Lines changed: 135 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { ViewDocIcon } from "assets/icons";
21
import { BoolCodeControl, StringControl } from "comps/controls/codeControl";
32
import { dropdownControl } from "comps/controls/dropdownControl";
43
import { MultiCompBuilder, withDefault } from "comps/generators";
@@ -15,12 +14,14 @@ import {
1514
ConstructorToView,
1615
fromRecord,
1716
MultiBaseComp,
17+
SimpleAbstractComp,
1818
withFunction
1919
} from "lowcoder-core";
20-
import { controlItem, Option } from "lowcoder-design";
21-
import styled from "styled-components";
20+
import { controlItem, Dropdown, Option, OptionsType, ValueFromOption } from "lowcoder-design";
2221
import { getNextEntityName } from "util/stringUtils";
23-
import { TargetCompAction } from "@lowcoder-ee/comps/comps/tourComp/componentSelectorControl";
22+
import { BoolControl, ControlParams } from "lowcoder-sdk";
23+
import { ReactNode, useContext, useEffect, useState } from "react";
24+
import { EditorContext, EditorState } from "@lowcoder-ee/comps/editorState";
2425
// import { PlacementType } from "@rc-component"
2526
export type PlacementType = 'left' | 'leftTop' | 'leftBottom' | 'right' | 'rightTop' | 'rightBottom' | 'top' | 'topLeft' | 'topRight' | 'bottom' | 'bottomLeft' | 'bottomRight' | 'center';
2627

@@ -205,35 +206,6 @@ export function manualTourStepsControl<T extends TourStepControlType>(
205206
: ManualOptionControl;
206207
}
207208

208-
const TipLabel = styled.p`
209-
display: inline;
210-
margin: 2px 0 0 0;
211-
padding: 0;
212-
213-
font-size: 13px;
214-
color: #9195a3;
215-
line-height: 18px;
216-
cursor: pointer;
217-
218-
:hover g g {
219-
stroke: #315efb;
220-
}
221-
`;
222-
const DocIcon = styled(ViewDocIcon)`
223-
transform: translateY(1px);
224-
margin-right: 6px;
225-
`;
226-
227-
const optionListDocUrl = trans("docUrls.optionList");
228-
const OptionTip = optionListDocUrl ? (
229-
<TipLabel onClick={() => window.open(optionListDocUrl)}>
230-
<DocIcon title={trans("optionsControl.viewDocs")} />
231-
{trans("optionsControl.tip")}
232-
</TipLabel>
233-
) : (
234-
<></>
235-
);
236-
237209
type TourStepChildType = {
238210
title: InstanceType<typeof StringControl>,
239211
};
@@ -308,14 +280,131 @@ const PlacementOptions: {label: string, value: PlacementType}[] = [
308280
{ label: "Bottom Right", value: "bottomRight"},
309281
];
310282

283+
284+
export function editorStateDropdownControl<T extends OptionsType>(
285+
options: ((editorState: EditorState) => T),
286+
defaultValue: ValueFromOption<T>
287+
) {
288+
return class extends editorStateDropdownAbstractControl(options, defaultValue) {
289+
override getView() {
290+
return this.value;
291+
}
292+
};
293+
}
294+
295+
interface EditorStateDropdownControlParams<T extends OptionsType> extends ControlParams {
296+
radioButton?: boolean;
297+
border?: boolean;
298+
type?: "oneline";
299+
disabled?: boolean;
300+
// parent comp may batch dispatch in some cases
301+
disableDispatchValueChange?: boolean;
302+
onChange?: (value: string) => void;
303+
options?: T;
304+
showSearch?: boolean;
305+
dropdownStyle?: React.CSSProperties;
306+
labelStyle?: React.CSSProperties;
307+
IconType?: "OnlyAntd" | "All" | "default" | undefined;
308+
}
309+
type EditorStateDropdownOptions<T extends OptionsType> = (editorState: EditorState) => T;
310+
311+
interface DropdownPropertyViewProps<T extends OptionsType>
312+
extends Omit<EditorStateDropdownControlParams<T>, "options"> {
313+
options: EditorStateDropdownOptions<T>;
314+
onChange: (value: ValueFromOption<T>) => void;
315+
value: ValueFromOption<T>;
316+
}
317+
318+
function EditorStateDropdownPropertyView<T extends OptionsType>(props: DropdownPropertyViewProps<T>) {
319+
const { options, onChange, value, ...params } = props;
320+
const [finalOptions, setFinalOptions] = useState<T>(
321+
typeof options === "function" ? ([] as unknown as T) : options
322+
);
323+
const editorState = useContext(EditorContext);
324+
325+
useEffect(() => {
326+
if (typeof options !== "function") {
327+
setFinalOptions(options);
328+
return;
329+
}
330+
if (!finalOptions?.length) {
331+
setFinalOptions(options(editorState))
332+
//.then((items) => setFinalOptions(items));
333+
}
334+
}, [finalOptions.length, options]);
335+
336+
return (
337+
<Dropdown
338+
placement={params.placement}
339+
toolTip={params.tooltip}
340+
value={value}
341+
options={finalOptions}
342+
radioButton={params.radioButton}
343+
border={params.border}
344+
type={params.type}
345+
label={params.label}
346+
showSearch={params.showSearch}
347+
onChange={onChange}
348+
disabled={params.disabled}
349+
dropdownStyle={props.dropdownStyle}
350+
labelStyle={props.labelStyle}
351+
/>
352+
);
353+
}
354+
355+
/**
356+
* Leave a getView method unimplemented, because the type cannot be changed by inheritance
357+
*/
358+
export function editorStateDropdownAbstractControl<T extends OptionsType>(
359+
options: ((editorState: EditorState) => T),
360+
defaultValue: ValueFromOption<T>
361+
) {
362+
abstract class DropdownControl extends SimpleAbstractComp<ValueFromOption<T>> {
363+
override getDefaultValue(): ValueFromOption<T> {
364+
return defaultValue;
365+
}
366+
367+
propertyView(params: EditorStateDropdownControlParams<T>) {
368+
return controlItem(
369+
{ filterText: params.label },
370+
<EditorStateDropdownPropertyView<T>
371+
{...params}
372+
value={this.value}
373+
options={options}
374+
onChange={(value) => {
375+
if (!params.disableDispatchValueChange) {
376+
this.dispatchChangeValueAction(value);
377+
}
378+
params.onChange?.(value);
379+
}}
380+
/>
381+
);
382+
}
383+
384+
getPropertyView(): ReactNode {
385+
throw new Error("Method not implemented.");
386+
}
387+
}
388+
389+
return DropdownControl;
390+
}
391+
311392
let TourStep = new MultiCompBuilder(
312393
{
313-
target: TargetCompAction,
314-
// target: dropdownControl(editorState.getAllUICompMap(), ""),
315-
arrow: BoolCodeControl,
394+
// target: TargetCompAction,
395+
target: editorStateDropdownControl((editorState) => {
396+
console.log("Is editor defined? ")
397+
console.log(editorState)
398+
return Object.values(editorState.getAllUICompMap()).map((it) => ({
399+
label: it.children.name.getView(),
400+
value: it.children.name.getView(),
401+
}))
402+
},
403+
""),
404+
arrow: BoolControl,
316405
title: StringControl,
317406
description: StringControl,
318-
placement: dropdownControl(PlacementOptions, "center"),
407+
placement: dropdownControl(PlacementOptions, "bottom"),
319408
hidden: BoolCodeControl,
320409
},
321410
(props) => props
@@ -333,11 +422,17 @@ TourStep = class extends TourStep implements TourStepCompProperty {
333422
label: "Description",
334423
placeholder: "Welcome to lowcoder, this is your first tutorial step",
335424
})}
336-
{this.children.target.propertyView()}
425+
{this.children.target.propertyView({
426+
label: "TARGET",
427+
radioButton: false,
428+
})}
337429
{this.children.placement.propertyView({
338430
label: trans("textShow.verticalAlignment"),
339431
radioButton: false
340432
})}
433+
{this.children.arrow.propertyView({
434+
label: "Arrow"
435+
})}
341436
{hiddenPropertyView(this.children)}
342437
</>
343438
);
@@ -346,7 +441,7 @@ TourStep = class extends TourStep implements TourStepCompProperty {
346441

347442
export const TourStepControl = tourStepsControl(TourStep, {
348443
initOptions: [
349-
{ title: "BLAHHH", description: "I love tutorials" },
350-
{ title: "second title", description: "Because they mean I don't have to teach people" },
444+
{ title: "Welcome", description: "Welcome to lowcoder" },
445+
{ title: "Step 2", description: "This is a tutorial step" },
351446
],
352447
});

0 commit comments

Comments
 (0)