Skip to content

Commit 8d84a65

Browse files
committed
add template section and fix type related issues for getRef
1 parent 9c39773 commit 8d84a65

12 files changed

Lines changed: 275 additions & 17 deletions

File tree

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/// <reference types="react" />
22
import * as react from 'react';
3-
import React, { ReactNode } from 'react';
3+
import { ReactNode, RefObject } from 'react';
44
import * as react_jsx_runtime from 'react/jsx-runtime';
55

66
type EvalMethods = Record<string, Record<string, Function>>;
@@ -388,6 +388,10 @@ interface Comp<ViewReturn = any, DataType extends JSONValue = JSONValue, NodeTyp
388388
*/
389389
changeDispatch(dispatch: DispatchType): this;
390390
changeValueAction(value: DataType): ChangeValueAction;
391+
/**
392+
* Optional DOM ref for components that render a root element (e.g. UI comps, tour targets).
393+
*/
394+
getRef(): RefObject<HTMLDivElement> | undefined;
391395
}
392396
declare abstract class AbstractComp<ViewReturn = any, DataType extends JSONValue = JSONValue, NodeType extends OptionalNodeType = OptionalNodeType> implements Comp<ViewReturn, DataType, NodeType> {
393397
dispatch: DispatchType;
@@ -403,6 +407,7 @@ declare abstract class AbstractComp<ViewReturn = any, DataType extends JSONValue
403407
*/
404408
dispatchChangeValueAction(value: DataType): void;
405409
changeValueAction(value: DataType): ChangeValueAction;
410+
getRef(): RefObject<HTMLDivElement> | undefined;
406411
/**
407412
* don't override the function, override nodeWithout function instead
408413
* FIXME: node reference mustn't be changed if this object is changed
@@ -616,7 +621,6 @@ declare abstract class MultiBaseComp<ChildrenType extends Record<string, Comp<un
616621
toJsonValue(): DataType;
617622
autoHeight(): boolean;
618623
changeChildAction(childName: string & keyof ChildrenType, value: ConstructorToDataType<new (...params: any) => ChildrenType[typeof childName]>): CompAction<JSONValue>;
619-
getRef(): React.RefObject<HTMLDivElement>;
620624
}
621625
declare function mergeExtra(e1: ExtraNodeType | undefined, e2: ExtraNodeType): ExtraNodeType;
622626

client/packages/lowcoder-core/lib/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3529,6 +3529,9 @@ class AbstractComp {
35293529
changeValueAction(value) {
35303530
return changeValueAction(value, true);
35313531
}
3532+
getRef() {
3533+
return undefined;
3534+
}
35323535
/**
35333536
* don't override the function, override nodeWithout function instead
35343537
* FIXME: node reference mustn't be changed if this object is changed

client/packages/lowcoder-core/src/baseComps/comp.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { changeValueAction, ChangeValueAction, CompAction, CompActionTypes } from "actions";
22
import { Node } from "eval";
3-
import { ReactNode } from "react";
3+
import { ReactNode, RefObject } from "react";
44
import { memo } from "util/cacheUtils";
55
import { JSONValue } from "util/jsonTypes";
66
import { setFieldsNoTypeCheck } from "util/objectUtils";
@@ -34,6 +34,11 @@ export interface Comp<
3434
changeDispatch(dispatch: DispatchType): this;
3535

3636
changeValueAction(value: DataType): ChangeValueAction;
37+
38+
/**
39+
* Optional DOM ref for components that render a root element (e.g. UI comps, tour targets).
40+
*/
41+
getRef(): RefObject<HTMLDivElement> | undefined;
3742
}
3843

3944
export abstract class AbstractComp<
@@ -73,6 +78,10 @@ export abstract class AbstractComp<
7378
return changeValueAction(value, true);
7479
}
7580

81+
getRef(): RefObject<HTMLDivElement> | undefined {
82+
return undefined;
83+
}
84+
7685
/**
7786
* don't override the function, override nodeWithout function instead
7887
* FIXME: node reference mustn't be changed if this object is changed

client/packages/lowcoder/src/api/commonSettingApi.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export interface CommonSettingResponseData {
1818
runJavaScriptInHost?: boolean | null;
1919
preloadLibs?: string[] | null;
2020
npmPlugins?: string[] | null;
21+
npmTemplates?: string[] | null;
2122
applyPreloadCSSToHomePage?: boolean | null;
2223
defaultHomePage?: string | null;
2324
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ export class GridItemComp extends TmpComp {
135135
// Or add a node specifically for providing exposingData data
136136
}
137137

138+
override getRef() {
139+
return this.children.comp.getRef?.();
140+
}
141+
138142
exposingInfo(): ExposingInfo {
139143
return this.children.comp.exposingInfo();
140144
}

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

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,6 @@ import { withMethodExposing } from "../../generators/withMethodExposing";
44
import { UICompBuilder } from "../../generators";
55
import { stringExposingStateControl } from "comps/controls/codeStateControl";
66
import { CommonNameConfig, NameConfig, withExposingConfigs } from "../../generators/withExposing";
7-
import { MultiBaseComp } from "lowcoder-core";
8-
9-
// FALK TODO: Check imports
10-
// import { MultiBaseComp } from "lowcoder-core";
11-
// import { UICompBuilder } from "comps/generators/uiCompBuilder";
12-
// import { stringExposingStateControl } from "comps/controls/codeStateControl";
13-
// import { withMethodExposing } from "comps/generators/withMethodExposing";
147

158
import { TourChildrenMap, TourPropertyView } from "./tourPropertyView";
169
import { Tour, TourProps } from "antd";
@@ -39,12 +32,7 @@ let TourBasicComp = (function() {
3932
let target = undefined;
4033
const compListItem = compMap.find((compItem) => compItem.children.name.getView() === targetName);
4134
if (compListItem) {
42-
// console.log(`setting selected comp to ${compListItem}`);
43-
try {
44-
target = ((compListItem as MultiBaseComp).children.comp as GridItemComp).getRef?.();
45-
} catch (e) {
46-
target = ((compListItem as MultiBaseComp).children.comp as HookComp).getRef?.();
47-
}
35+
target = compListItem.getRef();
4836
}
4937

5038
return {

client/packages/lowcoder/src/comps/hooks/hookComp.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,10 @@ export class HookComp extends HookTmpComp {
205205
return this.children.comp.exposingInfo();
206206
}
207207

208+
override getRef() {
209+
return this.children.comp.getRef?.();
210+
}
211+
208212
override getView() {
209213
const view = this.children?.comp?.getView();
210214
if (!view) {

client/packages/lowcoder/src/i18n/locales/en.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,10 @@ export const en = {
145145
"extensionTab": "Extensions",
146146
"modulesTab": "Modules",
147147
"moduleListTitle": "Modules",
148+
"templateListTitle": "Templates",
148149
"pluginListTitle": "Plugins",
149150
"emptyModules": "Modules are reusable Mikro-Apps. You can embed them in your App.",
151+
"emptyTemplates": "No templates added yet. Add npm templates with layout drop-zones to enhance your project.",
150152
"searchNotFound": "Can't find the right component?",
151153
"emptyPlugins": "No plugins added yet. Add npm plugins to enhance your project.",
152154
"contactUs": "Contact Us",
@@ -4672,13 +4674,18 @@ export const en = {
46724674
"npm": {
46734675
"invalidNpmPackageName": "Invalid npm Package Name or URL.",
46744676
"pluginExisted": "This npm Plugin Already Existed",
4677+
"templateExisted": "This npm Template Already Existed",
46754678
"compNotFound": "Component {compName} Not Found.",
46764679
"addPluginModalTitle": "Add Plugin from a npm Repository",
4680+
"addTemplateModalTitle": "Add Template from a npm Repository",
46774681
"pluginNameLabel": "npm Package's URL or Name",
4682+
"templateNameLabel": "npm Package's URL or Name",
46784683
"noCompText": "No Components.",
46794684
"compsLoading": "Loading...",
46804685
"removePluginBtnText": "Remove",
4681-
"addPluginBtnText": "Add npm Plugin"
4686+
"removeTemplateBtnText": "Remove",
4687+
"addPluginBtnText": "Add npm Plugin",
4688+
"addTemplateBtnText": "Add npm Template"
46824689
},
46834690
"toggleButton": {
46844691
"valueDesc": "The Default Value of the Toggle Button, For Example: False",

client/packages/lowcoder/src/pages/editor/right/ExtensionPanel.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import ModulePanel from "./ModulePanel";
22
import PluginPanel from "./PluginPanel";
3+
import TemplatePanel from "./TemplatePanel";
34
import { RightPanelContentWrapper } from "./styledComponent";
45

56
export default function ExtensionPanel() {
67
return (
78
<RightPanelContentWrapper>
89
<ModulePanel />
10+
<TemplatePanel />
911
<PluginPanel />
1012
</RightPanelContentWrapper>
1113
);
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import axios from "axios";
2+
import { EmptyContent } from "components/EmptyContent";
3+
import { LinkButton } from "lowcoder-design";
4+
import { useApplicationId, useShallowEqualSelector } from "util/hooks";
5+
import { useContext, useEffect, useMemo, useState } from "react";
6+
import { useDispatch, useSelector } from "react-redux";
7+
import { AppState } from "redux/reducers";
8+
import { packageMetaReadyAction } from "redux/reduxActions/npmPluginActions";
9+
import styled from "styled-components";
10+
import { NpmPackageMeta } from "types/remoteComp";
11+
import { PluginCompItem } from "../PluginPanel/PluginCompItem";
12+
import { NPM_REGISTRY_URL } from "constants/npmPlugins";
13+
import { trans } from "i18n";
14+
import { RightContext } from "../rightContext";
15+
16+
const TemplateViewWrapper = styled.div`
17+
margin-bottom: 12px;
18+
.remove-btn {
19+
display: none;
20+
}
21+
&:hover {
22+
.remove-btn {
23+
display: block;
24+
}
25+
}
26+
`;
27+
28+
const TemplateViewTitle = styled.div`
29+
height: 22px;
30+
display: flex;
31+
flex-direction: row;
32+
align-items: center;
33+
margin-bottom: 2px;
34+
`;
35+
36+
const TemplateViewTitleText = styled.div`
37+
flex: 1;
38+
font-size: 13px;
39+
overflow: hidden;
40+
text-overflow: ellipsis;
41+
white-space: nowrap;
42+
color: #b8b9bf;
43+
`;
44+
45+
const TemplateViewContent = styled.div`
46+
padding-top: 4px;
47+
margin-bottom: 12px;
48+
`;
49+
50+
interface TemplateItemProps {
51+
name: string;
52+
onRemove: () => void;
53+
}
54+
55+
export function TemplateItem(props: TemplateItemProps) {
56+
const { name, onRemove } = props;
57+
const dispatch = useDispatch();
58+
const appId = useApplicationId();
59+
const { onDrag, searchValue } = useContext(RightContext);
60+
const [loading, setLoading] = useState(false);
61+
const packageMeta = useShallowEqualSelector(
62+
(state: AppState) => state.npmPlugin.packageMeta[name]
63+
);
64+
const currentVersion = useSelector((state: AppState) => state.npmPlugin.packageVersion[name]);
65+
const versions = useMemo(() => packageMeta?.versions || {}, [packageMeta?.versions]);
66+
const comps = versions[currentVersion]?.lowcoder?.comps || {};
67+
const compNames = Object.keys(comps);
68+
69+
useEffect(() => {
70+
setLoading(true);
71+
axios.get<NpmPackageMeta>(`${NPM_REGISTRY_URL}/${appId || 'none'}/${name}`).then((res) => {
72+
if (res.status >= 400) {
73+
return;
74+
}
75+
setLoading(false);
76+
dispatch(packageMetaReadyAction(name, res.data));
77+
});
78+
}, [dispatch, name]);
79+
80+
const filteredCompNames = compNames.filter(
81+
(i) => !searchValue || i.toLowerCase().indexOf(searchValue.toLowerCase()) !== -1
82+
);
83+
const hasComps = filteredCompNames.length > 0;
84+
85+
return (
86+
<TemplateViewWrapper>
87+
<TemplateViewTitle>
88+
<TemplateViewTitleText>{name}</TemplateViewTitleText>
89+
<LinkButton
90+
onClick={onRemove}
91+
className="remove-btn"
92+
text={trans("npm.removeTemplateBtnText")}
93+
/>
94+
</TemplateViewTitle>
95+
<TemplateViewContent>
96+
{!hasComps && <EmptyContent text={loading ? "Loading..." : "No components found."} />}
97+
{hasComps &&
98+
filteredCompNames.map((compName) => (
99+
<PluginCompItem
100+
onDrag={onDrag}
101+
key={compName}
102+
compName={compName}
103+
compMeta={comps[compName]}
104+
packageName={name}
105+
packageVersion={currentVersion}
106+
/>
107+
))}
108+
</TemplateViewContent>
109+
</TemplateViewWrapper>
110+
);
111+
}

0 commit comments

Comments
 (0)