diff --git a/client/packages/openblocks-cli/package.json b/client/packages/openblocks-cli/package.json index 06635d64..5e9779aa 100644 --- a/client/packages/openblocks-cli/package.json +++ b/client/packages/openblocks-cli/package.json @@ -2,6 +2,7 @@ "name": "openblocks-cli", "description": "CLI tool used to start build publish openblocks components", "version": "0.0.24", + "license": "MIT", "bin": "./index.js", "type": "module", "exports": { diff --git a/client/packages/openblocks-comps/src/comps/calendarComp/calendarComp.tsx b/client/packages/openblocks-comps/src/comps/calendarComp/calendarComp.tsx index a896976e..e398b8fa 100644 --- a/client/packages/openblocks-comps/src/comps/calendarComp/calendarComp.tsx +++ b/client/packages/openblocks-comps/src/comps/calendarComp/calendarComp.tsx @@ -279,6 +279,13 @@ let CalendarBasicComp = (function () { }); }; + let initialDate = defaultDate; + try { + initialDate = new Date(defaultDate).toISOString(); + } catch (error) { + initialDate = undefined; + } + return ( > { return ret; } + @memoized() override fetchInfo( exposingNodes: Record>, options?: FetchInfoOptions diff --git a/client/packages/openblocks-core/src/eval/fetchCheckNode.tsx b/client/packages/openblocks-core/src/eval/fetchCheckNode.tsx index 7f3d9166..124ff394 100644 --- a/client/packages/openblocks-core/src/eval/fetchCheckNode.tsx +++ b/client/packages/openblocks-core/src/eval/fetchCheckNode.tsx @@ -28,6 +28,7 @@ export class FetchCheckNode extends AbstractNode { return this.child.dependValues(); } + @memoized() override fetchInfo(exposingNodes: Record>) { return this.child.fetchInfo(exposingNodes, this.options); } diff --git a/client/packages/openblocks-core/src/eval/functionNode.tsx b/client/packages/openblocks-core/src/eval/functionNode.tsx index 0d6ac450..9776ea47 100644 --- a/client/packages/openblocks-core/src/eval/functionNode.tsx +++ b/client/packages/openblocks-core/src/eval/functionNode.tsx @@ -35,6 +35,7 @@ export class FunctionNode extends AbstractNode { return this.child.dependValues(); } + @memoized() override fetchInfo(exposingNodes: Record>, options?: FetchInfoOptions) { return this.child.fetchInfo(exposingNodes, options); } diff --git a/client/packages/openblocks-core/src/eval/recordNode.tsx b/client/packages/openblocks-core/src/eval/recordNode.tsx index 6de6230e..4ec9082e 100644 --- a/client/packages/openblocks-core/src/eval/recordNode.tsx +++ b/client/packages/openblocks-core/src/eval/recordNode.tsx @@ -57,6 +57,7 @@ export class RecordNode>> extends Abstrac return ret; } + @memoized() override fetchInfo(exposingNodes: Record>, options?: FetchInfoOptions) { let isFetching = false; let ready = true; diff --git a/client/packages/openblocks-core/src/eval/types/evalTypes.tsx b/client/packages/openblocks-core/src/eval/types/evalTypes.tsx index a0254a31..a11b0287 100644 --- a/client/packages/openblocks-core/src/eval/types/evalTypes.tsx +++ b/client/packages/openblocks-core/src/eval/types/evalTypes.tsx @@ -1,5 +1,5 @@ export type EvalMethods = Record>; -export type CodeType = undefined | "JSON" | "Function"; +export type CodeType = undefined | "JSON" | "Function" | "PureJSON"; export type CodeFunction = (args?: Record, runInHost?: boolean) => any; diff --git a/client/packages/openblocks-core/src/eval/wrapNode.tsx b/client/packages/openblocks-core/src/eval/wrapNode.tsx index 6bad0fe0..2f9166f6 100644 --- a/client/packages/openblocks-core/src/eval/wrapNode.tsx +++ b/client/packages/openblocks-core/src/eval/wrapNode.tsx @@ -47,6 +47,7 @@ export class WrapNode extends AbstractNode { return this.delegate.evaluate(this.wrap(exposingNodes, methods), this.moduleExposingMethods); } + @memoized() override fetchInfo(exposingNodes: Record>) { return this.delegate.fetchInfo(this.wrap(exposingNodes, {})); } diff --git a/client/packages/openblocks-design/src/components/Section.tsx b/client/packages/openblocks-design/src/components/Section.tsx index b6add0b5..feae5402 100644 --- a/client/packages/openblocks-design/src/components/Section.tsx +++ b/client/packages/openblocks-design/src/components/Section.tsx @@ -118,7 +118,7 @@ export const BaseSection = (props: ISectionConfig) => { {props.name}
- {props.additionalButton} + {open && props.additionalButton}
diff --git a/client/packages/openblocks-design/src/components/edit.tsx b/client/packages/openblocks-design/src/components/edit.tsx index 649d103f..a4c79494 100644 --- a/client/packages/openblocks-design/src/components/edit.tsx +++ b/client/packages/openblocks-design/src/components/edit.tsx @@ -131,7 +131,10 @@ export const EditText = (props: EditTextProps) => { {props.text} !props.disabled && setEditing(true)} + onClick={(e) => { + e.stopPropagation(); + !props.disabled && setEditing(true); + }} className={"taco-edit-text-icon"} /> diff --git a/client/packages/openblocks-sdk/package.json b/client/packages/openblocks-sdk/package.json index 2358b7bc..d7eaca84 100644 --- a/client/packages/openblocks-sdk/package.json +++ b/client/packages/openblocks-sdk/package.json @@ -54,5 +54,6 @@ }, "keywords": [ "openblocks" - ] + ], + "license": "MIT" } diff --git a/client/packages/openblocks/src/components/DraggableTree/DraggableItem.tsx b/client/packages/openblocks/src/components/DraggableTree/DraggableItem.tsx index 3809c59c..9cf2a533 100644 --- a/client/packages/openblocks/src/components/DraggableTree/DraggableItem.tsx +++ b/client/packages/openblocks/src/components/DraggableTree/DraggableItem.tsx @@ -4,6 +4,7 @@ import styled from "styled-components"; import { DraggableTreeContext } from "./DraggableTreeContext"; const Wrapper = styled.div<{ + showPositionLine: boolean; dragging: boolean; isOver: boolean; dropInAsSub: boolean; @@ -27,7 +28,9 @@ const Wrapper = styled.div<{ &::before { content: ""; display: ${(props) => - (props.showPositionLineDot ?? false) && props.isOver ? "block" : "none"}; + (props.showPositionLineDot ?? false) && props.isOver && props.showPositionLine + ? "block" + : "none"}; position: absolute; background-color: #315efb; height: ${(props) => props.positionLineDotDiameter}px; @@ -43,7 +46,7 @@ const Wrapper = styled.div<{ &::after { content: ""; - display: ${(props) => (props.isOver ? "block" : "none")}; + display: ${(props) => (props.isOver && props.showPositionLine ? "block" : "none")}; height: ${(props) => props.positionLineHeight ?? 4}px; border-radius: 4px; position: absolute; @@ -76,8 +79,10 @@ function DraggableItem(props: IProps, ref: Ref) { } = props; const context = useContext(DraggableTreeContext); const positionLineIndent = context.positionLineIndent?.(path, dropInAsSub); + const showPositionLine = (context.showDropInPositionLine ?? true) || !dropInAsSub; return ( g > g { + stroke: ${ActiveTextColor}; + } } `; +const StyledDownloadIcon = styled(DownloadBoldIcon)` + cursor: pointer; -const Markdown = styled(TacoMarkDown)` - font-size: 14px; + :hover { + & > g > g { + stroke: ${ActiveTextColor}; + } + } `; const handleDownload = ( props: Pick & { url: string; setLoading: (loading: boolean) => void; + setError: (error: URLErrorType) => void; } ) => { const trimUrl = props.url.trim(); - if (!/^https?.+/.test(trimUrl)) { - message.error(trans("preLoad.jsLibraryURLError")); - return; - } - if (!props.onCheck(trimUrl)) { - message.error(trans("preLoad.jsLibraryExist")); - return; - } props.setLoading(true); return props .onLoad(trimUrl) @@ -78,13 +85,19 @@ const handleDownload = ( }) .catch((e) => { if (props.runInHost) { - message.error(trans("preLoad.jsLibraryInstallFailedHost", { message: e.message })); + props.setError({ + title: trans("preLoad.jsLibraryInstallFailed"), + description: trans("preLoad.jsLibraryInstallFailedHost", { message: e.message }), + }); } else { - message.error( - - {trans("preLoad.jsLibraryInstallFailedCloud", { message: e.message })} - - ); + props.setError({ + title: trans("preLoad.jsLibraryInstallFailed"), + description: ( + + {trans("preLoad.jsLibraryInstallFailedCloud", { message: e.message })} + + ), + }); } log.warn(e); props.setLoading(false); @@ -94,6 +107,7 @@ const handleDownload = ( const JSLibraryCard = ( props: Pick & { meta: RecommendedJSLibraryMeta; + setError: (error: URLErrorType) => void; } ) => { const { meta } = props; @@ -116,8 +130,7 @@ const JSLibraryCard = ( ) : !props.onCheck(meta.downloadUrl) ? ( ) : ( - handleDownload({ ...props, @@ -134,6 +147,74 @@ const JSLibraryCard = ( ); }; +const ErrorWrapper = styled.div` + border-radius: 8px; + background: #fff3f1; + padding: 10px 16px; + white-space: pre-wrap; + margin-top: 16px; + + .error-title { + font-size: 14px; + font-weight: 500; + display: flex; + justify-content: space-between; + + .close-button { + width: 8px; + height: 8px; + margin-right: -8px; + cursor: pointer; + color: #8b8fa3; + display: none; + + :hover { + color: #000000; + } + } + } + + :hover { + .close-button { + display: block; + } + } + + .error-description a { + :hover { + color: #315efb; + } + } + + .markdown-body { + background-color: unset; + font-size: 13px; + } +`; +const Error = (props: { + title: string; + description: ReactNode; + setError: (error: URLErrorType) => void; +}) => ( + +
+
+ + {props.title} +
+ props.setError(undefined)} /> +
+
{props.description}
+
+); + +const HelpText = styled(TacoMarkDown)` + font-size: 13px; + color: ${GreyTextColor}; + margin: 8px 0; + line-height: 13px; +`; + interface JSLibraryModalProps { trigger: ReactNode; runInHost: boolean; @@ -142,10 +223,14 @@ interface JSLibraryModalProps { onSuccess: (url: string) => void; } +type URLErrorType = { title: string; description: ReactNode } | undefined; + export function JSLibraryModal(props: JSLibraryModalProps) { const [visible, setVisible] = useState(false); const [loading, setLoading] = useState(false); const [url, setURL] = useState(""); + const [urlError, setURLError] = useState(undefined); + const [installError, setInstallError] = useState(undefined); const dispatch = useDispatch(); @@ -178,26 +263,46 @@ export function JSLibraryModal(props: JSLibraryModalProps) { onCancel={() => { setVisible(false); }} - afterClose={() => setURL("")} + afterClose={() => { + setURLError(undefined); + setInstallError(undefined); + setURL(""); + }} destroyOnClose={true} showOkButton={false} showCancelButton={false} width="648px" > - URL +
URL
+ {trans("preLoad.urlTooltip")} { setURL(e.target.value); + const trimUrl = e.target.value.trim(); + if (trimUrl) { + if (!/^https?.+/.test(trimUrl)) { + setURLError(trans("preLoad.jsLibraryURLError")); + return; + } + if (!props.onCheck(trimUrl)) { + setURLError(trans("preLoad.jsLibraryExist")); + return; + } + } + setURLError(undefined); }} /> - handleDownload({ + disabled={!!urlError} + onClick={() => { + setInstallError(undefined); + return handleDownload({ ...props, url, setLoading, @@ -206,19 +311,35 @@ export function JSLibraryModal(props: JSLibraryModalProps) { setVisible(false); setLoading(false); }, - }) - } + setError: setInstallError, + }); + }} style={{ minWidth: "80px" }} > {trans("preLoad.add")} - {trans("preLoad.recommended")} - - {recommends.map((r, idx) => ( - - ))} - + + {urlError &&
{urlError}
} + + {typeof installError === "object" && ( + + )} + + {recommends.length > 0 && ( + <> +
{trans("preLoad.recommended")}
+ + {recommends.map((r, idx) => ( + + ))} + + + )} ); diff --git a/client/packages/openblocks/src/components/JSLibraryTree.tsx b/client/packages/openblocks/src/components/JSLibraryTree.tsx index 396a4e27..0bdd60a7 100644 --- a/client/packages/openblocks/src/components/JSLibraryTree.tsx +++ b/client/packages/openblocks/src/components/JSLibraryTree.tsx @@ -34,6 +34,7 @@ const DescWrapper = styled(Typography.Paragraph)` line-height: 1.5em; font-size: 13px; color: #8b8fa3; + margin-bottom: 8px !important; `; export const JSLibraryInfo = (props: { exportedAs?: string; description?: string }) => { return ( @@ -45,7 +46,9 @@ export const JSLibraryInfo = (props: { exportedAs?: string; description?: string )} {props.description && ( - {props.description} + + {props.description} + )} ); @@ -101,14 +104,12 @@ const JSLibraryCollapse = styled(Collapse)<{ mode: "row" | "column" }>` } ` : css` - width: 270px; - .ant-typography { margin-bottom: 10px; } .lib-label { - margin-right: 15px; + margin-right: 16px; width: 256px; } `} @@ -118,6 +119,7 @@ const JSLibraryCollapse = styled(Collapse)<{ mode: "row" | "column" }>` .ant-collapse-ghost > .ant-collapse-item > .ant-collapse-content > .ant-collapse-content-box { padding-left: 14px; + margin-right: 16px; } .lib-label { @@ -170,49 +172,50 @@ export const JSLibraryTree = (props: { .filter(({ name }) => !metas[name]) .map(({ name }) => name); !!notExistMetas.length && dispatch(fetchJSLibraryMetasAction(notExistMetas)); - }, [nameAndVersions, metas, dispatch]); + }, []); return ( ({ - key: idx, - title: ( -
- - window.open(meta.homepage, "_blank"), - }, - ] - : []), - ...(meta.deletable - ? ([ - { - text: trans("delete"), - onClick: () => { - props.onDelete(idx); - }, - type: "delete", - }, - ] as const) - : []), - ]} - > - - -
- ), - data: ( - - ), - }))} + config={finalMetas.map((meta, idx) => { + const options = []; + if (meta.homepage) { + options.push({ + text: trans("preLoad.viewJSLibraryDocument"), + onClick: () => window.open(meta.homepage, "_blank"), + }); + } + if (meta.deletable) { + options.push({ + text: trans("delete"), + onClick: () => { + props.onDelete(idx); + }, + type: "delete", + } as const); + } + return { + key: idx, + title: ( +
+ + {!!options.length && ( + + + + )} +
+ ), + data: ( + + ), + }; + })} /> ); }; diff --git a/client/packages/openblocks/src/comps/comps/preLoadComp.tsx b/client/packages/openblocks/src/comps/comps/preLoadComp.tsx index 79dc9f4d..e52ed142 100644 --- a/client/packages/openblocks/src/comps/comps/preLoadComp.tsx +++ b/client/packages/openblocks/src/comps/comps/preLoadComp.tsx @@ -275,10 +275,9 @@ const PreloadCompBase = new MultiCompBuilder(childrenMap, () => {}) .setPropertyViewFn((children) => ) .build(); -const AddIcon = styled(PlusIcon)` - margin: 15px 16px 0 0; - position: absolute; +const AddJSLibraryButton = styled.div` cursor: pointer; + margin-right: 16px; g g { stroke: #8b8fa3; @@ -293,10 +292,6 @@ const AddIcon = styled(PlusIcon)` const JSLibraryWrapper = styled.div` position: relative; - - .section-header svg { - margin-right: 13px; - } `; export class PreloadComp extends PreloadCompBase { @@ -325,15 +320,15 @@ export class PreloadComp extends PreloadCompBase { backgroundColor: "#fff", }} additionalButton={ -
+ } + trigger={} onCheck={(url) => !libs.getAllLibs().includes(url)} onLoad={(url) => libs.loadScript(url)} onSuccess={(url) => libs.dispatch(libs.pushAction(url))} /> -
+ } > {this.children.libs.getPropertyView()} diff --git a/client/packages/openblocks/src/i18n/locales/en.ts b/client/packages/openblocks/src/i18n/locales/en.ts index 76d2f0e3..929919d9 100644 --- a/client/packages/openblocks/src/i18n/locales/en.ts +++ b/client/packages/openblocks/src/i18n/locales/en.ts @@ -1921,6 +1921,8 @@ export const en = { jsLibraryHelpText: "Add JavaScript libraries to your current application via URL addresses. lodash, moment, uuid, numbro are built into the system for immediate use. JavaScript libraries are loaded before the application is initialized, which can have an impact on application performance.", exportedAs: "Exported as", + urlTooltip: + "URL address of the JavaScript library, [unpkg.com](https://unpkg.com/) or [jsdelivr.net](https://www.jsdelivr.com/) is recommended", recommended: "Recommended", viewJSLibraryDocument: "Document", jsLibraryURLError: "Invalid URL", @@ -1928,9 +1930,10 @@ export const en = { jsLibraryEmptyContent: "No JavaScript libraries added", jsLibraryDownloadError: "JavaScript library download error", jsLibraryInstallSuccess: "JavaScript library installed successfully", + jsLibraryInstallFailed: "JavaScript library installation failed", jsLibraryInstallFailedCloud: - "JavaScript library installation failed\nPerhaps the library is not available in the sandbox, [Documentation](https://docs.openblocks.dev/build-apps/write-javascript/use-third-party-libraries#manually-import-libraries)\n{message}", - jsLibraryInstallFailedHost: "JavaScript library installation failed\n{message}", + "Perhaps the library is not available in the sandbox, [Documentation](https://docs.openblocks.dev/build-apps/write-javascript/use-third-party-libraries#manually-import-libraries)\n{message}", + jsLibraryInstallFailedHost: "{message}", add: "Add New", jsHelpText: "Add a global method or variable to the current application.", cssHelpText: diff --git a/client/packages/openblocks/src/pages/common/help.tsx b/client/packages/openblocks/src/pages/common/help.tsx index 6e065992..0072fbfb 100644 --- a/client/packages/openblocks/src/pages/common/help.tsx +++ b/client/packages/openblocks/src/pages/common/help.tsx @@ -135,7 +135,17 @@ const SpanStyled = styled.span<{ selected?: boolean }>` display: block; width: 26px; height: 26px; + cursor: pointer; svg { + height: 26px; + width: 26px; + padding: 5px; + + &:hover, + &:active { + background: #8b8fa37f; + border-radius: 4px; + } ${(props) => props.selected && ` diff --git a/client/packages/openblocks/src/pages/common/shortcutListPopup.tsx b/client/packages/openblocks/src/pages/common/shortcutListPopup.tsx index 89c7cae9..24270578 100644 --- a/client/packages/openblocks/src/pages/common/shortcutListPopup.tsx +++ b/client/packages/openblocks/src/pages/common/shortcutListPopup.tsx @@ -58,6 +58,7 @@ const CloseIconWrapper = styled.div` width: 16px; height: 16px; cursor: pointer; + color: #7a7c80; :hover svg g line { stroke: #ffffff; diff --git a/client/packages/openblocks/src/pages/editor/bottom/BottomSidebar.tsx b/client/packages/openblocks/src/pages/editor/bottom/BottomSidebar.tsx index 913bb7a6..3e1621db 100644 --- a/client/packages/openblocks/src/pages/editor/bottom/BottomSidebar.tsx +++ b/client/packages/openblocks/src/pages/editor/bottom/BottomSidebar.tsx @@ -24,7 +24,7 @@ import { DraggableTreeNodeItemRenderProps, } from "components/DraggableTree/types"; import RefTreeComp from "comps/comps/refTreeComp"; -import { ActiveTextColor, NormalMenuIconColor } from "constants/style"; +import { ActiveTextColor, BorderActiveColor, NormalMenuIconColor } from "constants/style"; const Contain = styled.div` flex-grow: 1; @@ -269,6 +269,7 @@ export function BottomSidebar(props: BottomSidebarProps) { disable={!!search} unfoldAll={!!search} showSubInDragOverlay={false} + showDropInPositionLine={false} showPositionLineDot positionLineDotDiameter={4} positionLineHeight={1} @@ -316,20 +317,28 @@ export function BottomSidebar(props: BottomSidebarProps) { ); } -const ColumnDiv = styled.div<{ +const HighlightBorder = styled.div<{ active: boolean; foldable: boolean; level: number }>` + flex: 1; + display: flex; + padding-left: ${(props) => props.level * 20 + (props.foldable ? 0 : 14)}px; + border-radius: 4px; + border: 1px solid ${(props) => (props.active ? BorderActiveColor : "transparent")}; + align-items: center; + justify-content: center; +`; + +interface ColumnDivProps { $color?: boolean; - level: number; - foldable: boolean; isOverlay: boolean; -}>` +} + +const ColumnDiv = styled.div` width: 100%; height: 25px; display: flex; user-select: none; - align-items: center; - justify-content: center; - padding: 0 15px 0 2px; - padding-left: ${(props) => 2 + props.level * 20 + (props.foldable ? 0 : 14)}px; + padding-left: 2px; + padding-right: 15px; /* background-color: #ffffff; */ /* margin: 2px 0; */ background-color: ${(props) => (props.isOverlay ? "rgba(255, 255, 255, 0.11)" : "")}; @@ -403,8 +412,18 @@ interface BottomSidebarItemProps extends DraggableTreeNodeItemRenderProps { } function BottomSidebarItem(props: BottomSidebarItemProps) { - const { id, resComp, isOverlay, path, isFolded, onDelete, onCopy, onSelect, onToggleFold } = - props; + const { + id, + resComp, + isOver, + isOverlay, + path, + isFolded, + onDelete, + onCopy, + onSelect, + onToggleFold, + } = props; const [error, setError] = useState(undefined); const [editing, setEditing] = useState(false); const editorState = useContext(EditorContext); @@ -451,36 +470,32 @@ function BottomSidebarItem(props: BottomSidebarItemProps) { }; return ( - - {isFolder && {!isFolded ? : }} - {icon} -
- setEditing(editing)} - /> - -
- {!readOnly && !isOverlay && ( - - - - )} + + + {isFolder && {!isFolded ? : }} + {icon} +
+ setEditing(editing)} + /> + +
+ {!readOnly && !isOverlay && ( + + + + )} +
); } diff --git a/client/packages/openblocks/src/pages/editor/editorView.tsx b/client/packages/openblocks/src/pages/editor/editorView.tsx index 1062ad0b..a702544a 100644 --- a/client/packages/openblocks/src/pages/editor/editorView.tsx +++ b/client/packages/openblocks/src/pages/editor/editorView.tsx @@ -100,24 +100,11 @@ const HelpDiv = styled.div` right: auto; height: 28px; bottom: 36px; - cursor: pointer; > div.shortcutList { left: 42px; bottom: 2px; } - - svg { - height: 26px; - width: 26px; - padding: 5px; - - &:hover, - &:active { - background: #8b8fa37f; - border-radius: 4px; - } - } } `; const SettingsDiv = styled.div` diff --git a/server/api-service/openblocks-server/src/main/java/com/openblocks/api/home/UserHomeApiServiceImpl.java b/server/api-service/openblocks-server/src/main/java/com/openblocks/api/home/UserHomeApiServiceImpl.java index a17a2550..9369806e 100644 --- a/server/api-service/openblocks-server/src/main/java/com/openblocks/api/home/UserHomeApiServiceImpl.java +++ b/server/api-service/openblocks-server/src/main/java/com/openblocks/api/home/UserHomeApiServiceImpl.java @@ -212,7 +212,9 @@ public Flux getAllAuthorisedApplications4CurrentOrgMember(@ }) .filter(application -> isNull(applicationType) || application.getApplicationType() == applicationType.getValue()) .filter(application -> isNull(applicationStatus) || application.getApplicationStatus() == applicationStatus) - .cache(); + .cache() + .collectList() + .flatMapIterable(Function.identity()); // last view time Mono> applicationLastViewTimeMapMono = userApplicationInteractionService.findByUserId(visitorId)