diff --git a/client/packages/lowcoder-design/src/components/iconSelect/index.tsx b/client/packages/lowcoder-design/src/components/iconSelect/index.tsx index eaae8bfe5..ac3264aa2 100644 --- a/client/packages/lowcoder-design/src/components/iconSelect/index.tsx +++ b/client/packages/lowcoder-design/src/components/iconSelect/index.tsx @@ -1,16 +1,24 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import type { IconDefinition } from "@fortawesome/free-regular-svg-icons"; -import { default as Popover } from "antd/es/popover"; -import { ActionType } from '@rc-component/trigger/lib/interface'; +import { Popover } from "antd"; +import { ActionType } from "@rc-component/trigger/lib/interface"; import { TacoInput } from "components/tacoInput"; import { Tooltip } from "components/toolTip"; import { trans } from "i18n/design"; import _ from "lodash"; -import { ReactNode, useEffect, useCallback, useMemo, useRef, useState } from "react"; +import { + ReactNode, + useEffect, + useCallback, + useMemo, + useRef, + useState, +} from "react"; import Draggable from "react-draggable"; import { default as List, ListRowProps } from "react-virtualized/dist/es/List"; import styled from "styled-components"; import { CloseIcon, SearchIcon } from "icons"; +import { ANTDICON } from "../../../../lowcoder/src/comps/comps/timelineComp/antIcon"; const PopupContainer = styled.div` width: 408px; @@ -110,11 +118,23 @@ const IconItemContainer = styled.div` class Icon { readonly title: string; - constructor(readonly def: IconDefinition, readonly names: string[]) { - this.title = def.iconName.split("-").map(_.upperFirst).join(" "); + constructor(readonly def: IconDefinition | any, readonly names: string[]) { + if (def?.iconName) { + this.title = def.iconName.split("-").map(_.upperFirst).join(" "); + } else { + this.title = names[0].slice(5); + this.def = def; + } } getView() { - return ; + if (this.names[0]?.startsWith("antd/")) return this.def; + else + return ( + + ); } } @@ -144,6 +164,13 @@ async function getAllIcons() { } } } + //append ant icon + for (let key of Object.keys(ANTDICON)) { + ret["antd/" + key] = new Icon( + ANTDICON[key.toLowerCase() as keyof typeof ANTDICON], + ["antd/" + key] + ); + } allIcons = ret; return ret; } @@ -151,7 +178,11 @@ async function getAllIcons() { export const iconPrefix = "/icon:"; export function removeQuote(value?: string) { - return value ? (value.startsWith('"') && value.endsWith('"') ? value.slice(1, -1) : value) : ""; + return value + ? value.startsWith('"') && value.endsWith('"') + ? value.slice(1, -1) + : value + : ""; } function getIconKey(value?: string) { @@ -171,7 +202,8 @@ export function useIcon(value?: string) { function search( allIcons: Record, searchText: string, - searchKeywords?: Record + searchKeywords?: Record, + IconType?: "OnlyAntd" | "All" | "default" | undefined ) { const tokens = searchText .toLowerCase() @@ -182,6 +214,8 @@ function search( if (icon.names.length === 0) { return false; } + if (IconType === "OnlyAntd" && !key.startsWith("antd/")) return false; + if (IconType === "default" && key.startsWith("antd/")) return false; let text = icon.names .flatMap((name) => [name, searchKeywords?.[name]]) .filter((t) => t) @@ -198,16 +232,20 @@ const IconPopup = (props: { label?: ReactNode; onClose: () => void; searchKeywords?: Record; + IconType?: "OnlyAntd" | "All" | "default" | undefined; }) => { const [searchText, setSearchText] = useState(""); const [allIcons, setAllIcons] = useState>({}); const searchResults = useMemo( - () => search(allIcons, searchText, props.searchKeywords), + () => search(allIcons, searchText, props.searchKeywords, props.IconType), [searchText, allIcons] ); const onChangeRef = useRef(props.onChange); onChangeRef.current = props.onChange; - const onChangeIcon = useCallback((key: string) => onChangeRef.current(iconPrefix + key), []); + const onChangeIcon = useCallback( + (key: string) => onChangeRef.current(iconPrefix + key), + [] + ); const columnNum = 8; useEffect(() => { @@ -217,24 +255,26 @@ const IconPopup = (props: { const rowRenderer = useCallback( (p: ListRowProps) => ( - {searchResults.slice(p.index * columnNum, (p.index + 1) * columnNum).map(([key, icon]) => ( - - { - onChangeIcon(key); - }} + {searchResults + .slice(p.index * columnNum, (p.index + 1) * columnNum) + .map(([key, icon]) => ( + - {icon.getView()} - - - ))} + { + onChangeIcon(key); + }} + > + {icon.getView()} + + + ))} ), [searchResults, allIcons, onChangeIcon] @@ -279,6 +319,7 @@ export const IconSelectBase = (props: { leftOffset?: number; parent?: HTMLElement | null; searchKeywords?: Record; + IconType?: "OnlyAntd" | "All" | "default" | undefined; }) => { const { setVisible, parent } = props; return ( @@ -290,7 +331,11 @@ export const IconSelectBase = (props: { onOpenChange={setVisible} getPopupContainer={parent ? () => parent : undefined} // hide the original background when dragging the popover is allowed - overlayInnerStyle={{ border: "none", boxShadow: "none", background: "transparent" }} + overlayInnerStyle={{ + border: "none", + boxShadow: "none", + background: "transparent", + }} // when dragging is allowed, always re-location to avoid the popover exceeds the screen destroyTooltipOnHide content={ @@ -299,6 +344,7 @@ export const IconSelectBase = (props: { label={props.label} onClose={() => setVisible?.(false)} searchKeywords={props.searchKeywords} + IconType={props.IconType} /> } > @@ -312,6 +358,7 @@ export const IconSelect = (props: { label?: ReactNode; children?: ReactNode; searchKeywords?: Record; + IconType?: "OnlyAntd" | "All" | "default" | undefined; }) => { const [visible, setVisible] = useState(false); return ( diff --git a/client/packages/lowcoder-design/src/icons/IconCompIcon.svg b/client/packages/lowcoder-design/src/icons/IconCompIcon.svg new file mode 100644 index 000000000..ce549b56c --- /dev/null +++ b/client/packages/lowcoder-design/src/icons/IconCompIcon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/packages/lowcoder-design/src/icons/index.ts b/client/packages/lowcoder-design/src/icons/index.ts index 938cd1fc4..662c4b0ed 100644 --- a/client/packages/lowcoder-design/src/icons/index.ts +++ b/client/packages/lowcoder-design/src/icons/index.ts @@ -278,6 +278,28 @@ export { ReactComponent as SignatureIcon } from "./icon-signature.svg"; export { ReactComponent as ManualIcon } from "./icon-manual.svg"; export { ReactComponent as WarnIcon } from "./icon-warn.svg"; export { ReactComponent as SyncManualIcon } from "./icon-sync-manual.svg"; + +export { ReactComponent as DangerIcon } from "icons/icon-danger.svg"; +export { ReactComponent as TableMinusIcon } from "icons/icon-table-minus.svg"; +export { ReactComponent as TablePlusIcon } from "icons/icon-table-plus.svg"; +export { ReactComponent as MobileAppIcon } from "icons/icon-mobile-app.svg"; +export { ReactComponent as MobileNavIcon } from "icons/icon-navigation-mobile.svg"; +export { ReactComponent as PcNavIcon } from "icons/icon-navigation-pc.svg"; +export { ReactComponent as UnLockIcon } from "icons/icon-unlock.svg"; +export { ReactComponent as CalendarDeleteIcon } from "icons/icon-calendar-delete.svg"; +export { ReactComponent as TableCheckedIcon } from "icons/icon-table-checked.svg"; +export { ReactComponent as TableUnCheckedIcon } from "icons/icon-table-boolean-false.svg"; +export { ReactComponent as FileFolderIcon } from "icons/icon-editor-folder.svg"; +export { ReactComponent as ExpandIcon } from "icons/icon-expand.svg"; +export { ReactComponent as CompressIcon } from "icons/icon-compress.svg"; +export { ReactComponent as TableCellsIcon } from "icons/icon-table-cells.svg"; // Added By Aqib Mirza +export { ReactComponent as TimeLineIcon } from "icons/icon-timeline-comp.svg" +export { ReactComponent as LottieIcon } from "icons/icon-lottie.svg"; +export { ReactComponent as MentionIcon } from "icons/icon-mention-comp.svg"; +export { ReactComponent as AutoCompleteCompIcon } from "icons/icon-autocomplete-comp.svg"; + +export { ReactComponent as IconCompIcon } from "icons/IconCompIcon.svg"; + export { ReactComponent as DangerIcon } from "./icon-danger.svg"; export { ReactComponent as TableMinusIcon } from "./icon-table-minus.svg"; export { ReactComponent as TablePlusIcon } from "./icon-table-plus.svg"; @@ -612,4 +634,5 @@ export { ReactComponent as MentionIcon } from "./icon-mention-comp.svg"; export { ReactComponent as AutoCompleteCompIcon } from "./icon-autocomplete-comp.svg"; export { ReactComponent as WidthIcon } from "./icon-width.svg"; export { ReactComponent as ResponsiveLayoutCompIcon } from "./remix/layout-column-line.svg"; // Closest match for responsive layout component -export { ReactComponent as TextSizeIcon } from "./icon-text-size.svg"; */ \ No newline at end of file +export { ReactComponent as TextSizeIcon } from "./icon-text-size.svg"; */ + diff --git a/client/packages/lowcoder/src/comps/comps/iconComp.tsx b/client/packages/lowcoder/src/comps/comps/iconComp.tsx new file mode 100644 index 000000000..41b73da71 --- /dev/null +++ b/client/packages/lowcoder/src/comps/comps/iconComp.tsx @@ -0,0 +1,142 @@ +import { useEffect, useRef, useState } from "react"; +import styled, { css } from "styled-components"; +import { RecordConstructorToView } from "lowcoder-core"; +import { styleControl } from "comps/controls/styleControl"; +import _ from "lodash"; +import { + IconStyle, + IconStyleType, + heightCalculator, + widthCalculator, +} from "comps/controls/styleControlConstants"; +import { UICompBuilder } from "comps/generators/uiCompBuilder"; +import { withDefault } from "../generators"; +import { + NameConfigHidden, + withExposingConfigs, +} from "comps/generators/withExposing"; +import { Section, sectionNames } from "lowcoder-design"; +import { hiddenPropertyView } from "comps/utils/propertyUtils"; +import { trans } from "i18n"; +import { NumberControl } from "comps/controls/codeControl"; +import { IconControl } from "comps/controls/iconControl"; +import ReactResizeDetector from "react-resize-detector"; +import { AutoHeightControl } from "../controls/autoHeightControl"; +import { + clickEvent, + eventHandlerControl, +} from "../controls/eventHandlerControl"; + +const Container = styled.div<{ $style: IconStyleType | undefined }>` + height: 100%; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + svg { + object-fit: contain; + pointer-events: auto; + } + ${(props) => props.$style && getStyle(props.$style)} +`; + +const getStyle = (style: IconStyleType) => { + return css` + svg { + color: ${style.fill}; + } + padding: ${style.padding}; + border: 1px solid ${style.border}; + border-radius: ${style.radius}; + margin: ${style.margin}; + max-width: ${widthCalculator(style.margin)}; + max-height: ${heightCalculator(style.margin)}; + `; +}; + +const EventOptions = [clickEvent] as const; + +const childrenMap = { + style: styleControl(IconStyle), + icon: withDefault(IconControl, "/icon:antd/homefilled"), + autoHeight: withDefault(AutoHeightControl, "auto"), + iconSize: withDefault(NumberControl, 20), + onEvent: eventHandlerControl(EventOptions), +}; + +const IconView = (props: RecordConstructorToView) => { + const conRef = useRef(null); + const [width, setWidth] = useState(0); + const [height, setHeight] = useState(0); + + useEffect(() => { + if (height && width) { + onResize(); + } + }, [height, width]); + + const onResize = () => { + const container = conRef.current; + setWidth(container?.clientWidth ?? 0); + setHeight(container?.clientHeight ?? 0); + }; + + return ( + + props.onEvent("click")} + > + {props.icon} + + + ); +}; + +let IconBasicComp = (function () { + return new UICompBuilder(childrenMap, (props) => ) + .setPropertyViewFn((children) => ( + <> +
+ {children.icon.propertyView({ + label: trans("iconComp.icon"), + IconType: "All", + })} + {children.autoHeight.propertyView({ + label: trans("iconComp.autoSize"), + })} + {!children.autoHeight.getView() && + children.iconSize.propertyView({ + label: trans("iconComp.iconSize"), + })} +
+
+ {children.onEvent.getPropertyView()} +
+
+ {hiddenPropertyView(children)} +
+
+ {children.style.getPropertyView()} +
+ + )) + .build(); +})(); + +IconBasicComp = class extends IconBasicComp { + override autoHeight(): boolean { + return false; + } +}; + +export const IconComp = withExposingConfigs(IconBasicComp, [ + NameConfigHidden, +]); diff --git a/client/packages/lowcoder/src/comps/controls/controlParams.tsx b/client/packages/lowcoder/src/comps/controls/controlParams.tsx index 0ee9de8e6..7b84c439d 100644 --- a/client/packages/lowcoder/src/comps/controls/controlParams.tsx +++ b/client/packages/lowcoder/src/comps/controls/controlParams.tsx @@ -18,6 +18,7 @@ export interface ControlParams extends CodeEditorControlParams { preInputNode?: ReactNode; childrenWrapperStyle?: CSSProperties; extraChildren?: ReactNode; + IconType?: "OnlyAntd" | "All" | "default" | undefined; } export interface ControlType { diff --git a/client/packages/lowcoder/src/comps/controls/dropdownControl.tsx b/client/packages/lowcoder/src/comps/controls/dropdownControl.tsx index 01b7b832c..ea8e6f006 100644 --- a/client/packages/lowcoder/src/comps/controls/dropdownControl.tsx +++ b/client/packages/lowcoder/src/comps/controls/dropdownControl.tsx @@ -28,6 +28,7 @@ interface DropdownControlParams extends ControlParams { showSearch?: boolean; dropdownStyle?: React.CSSProperties; labelStyle?: React.CSSProperties; + IconType?: "OnlyAntd" | "All" | "default" | undefined; } interface DropdownPropertyViewProps diff --git a/client/packages/lowcoder/src/comps/controls/iconControl.tsx b/client/packages/lowcoder/src/comps/controls/iconControl.tsx index 26a768b4e..ca954b47d 100644 --- a/client/packages/lowcoder/src/comps/controls/iconControl.tsx +++ b/client/packages/lowcoder/src/comps/controls/iconControl.tsx @@ -75,6 +75,7 @@ const IconPicker = (props: { value: string; onChange: (value: string) => void; label?: ReactNode; + IconType?: "OnlyAntd" | "All" | "default" | undefined; }) => { const icon = useIcon(props.value); return ( @@ -82,6 +83,7 @@ const IconPicker = (props: { onChange={props.onChange} label={props.label} searchKeywords={i18nObjs.iconSearchKeywords} + IconType={props.IconType} > {icon ? ( @@ -251,7 +253,7 @@ export class IconControl extends AbstractComp - {this.useCodeEditor && } + {this.useCodeEditor && } ); } @@ -262,6 +264,7 @@ export class IconControl extends AbstractComp this.dispatchChangeValueAction(x)} label={params.label} + IconType={params.IconType} /> )} diff --git a/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx b/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx index a9749121f..9626e4fbf 100644 --- a/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx +++ b/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx @@ -1078,6 +1078,10 @@ export const NavigationStyle = [ export const ImageStyle = [getStaticBorder("#00000000"), RADIUS, BORDER_WIDTH, MARGIN, PADDING] as const; +export const IconStyle = [getStaticBackground("#00000000"), + getStaticBorder("#00000000"), FILL, RADIUS, MARGIN, PADDING] as const; + + export const ListViewStyle = BG_STATIC_BORDER_RADIUS; export const JsonSchemaFormStyle = BG_STATIC_BORDER_RADIUS; @@ -1331,6 +1335,7 @@ export type DividerStyleType = StyleConfigType; export type ProgressStyleType = StyleConfigType; export type NavigationStyleType = StyleConfigType; export type ImageStyleType = StyleConfigType; +export type IconStyleType = StyleConfigType; export type ListViewStyleType = StyleConfigType; export type JsonSchemaFormStyleType = StyleConfigType; export type TreeSelectStyleType = StyleConfigType; diff --git a/client/packages/lowcoder/src/comps/index.tsx b/client/packages/lowcoder/src/comps/index.tsx index 0de77fadc..072eec5e9 100644 --- a/client/packages/lowcoder/src/comps/index.tsx +++ b/client/packages/lowcoder/src/comps/index.tsx @@ -130,6 +130,41 @@ import { CommentIcon, MentionIcon, AutoCompleteCompIcon, + + IconCompIcon, +} from "lowcoder-design"; +// from Mousheng +import { defaultFormData, FormComp } from "./comps/formComp/formComp"; +import { IFrameComp } from "./comps/iframeComp"; +import { defaultGridData, defaultListViewData, GridComp, ListViewComp } from "./comps/listViewComp"; +import { ModuleComp } from "./comps/moduleComp/moduleComp"; +import { NavComp } from "./comps/navComp/navComp"; +import { TableComp } from "./comps/tableComp"; +import { registerComp, UICompManifest, UICompType } from "./uiCompRegistry"; +import { QRCodeComp } from "./comps/qrCodeComp"; +import { JsonExplorerComp } from "./comps/jsonComp/jsonExplorerComp"; +import { JsonEditorComp } from "./comps/jsonComp/jsonEditorComp"; +import { TreeComp } from "./comps/treeComp/treeComp"; +import { TreeSelectComp } from "./comps/treeComp/treeSelectComp"; +import { trans } from "i18n"; +import { remoteComp } from "./comps/remoteComp/remoteComp"; +import { AudioComp } from "./comps/mediaComp/audioComp"; +import { VideoComp } from "./comps/mediaComp/videoComp"; +import { DrawerComp } from "./hooks/drawerComp"; +import { CarouselComp } from "./comps/carouselComp"; +import { ToggleButtonComp } from "./comps/buttonComp/toggleButtonComp"; +import { defaultCollapsibleContainerData } from "./comps/containerComp/collapsibleContainerComp"; +import { RemoteCompInfo } from "types/remoteComp"; +import { ScannerComp } from "./comps/buttonComp/scannerComp"; +import { SignatureComp } from "./comps/signatureComp"; +import { TimeLineComp } from "./comps/timelineComp/timelineComp"; +import { MentionComp } from "./comps/textInputComp/mentionComp"; +import { AutoCompleteComp } from "./comps/autoCompleteComp/autoCompleteComp" +import { IconComp } from "./comps/iconComp"; +// from Mousheng + +//Added by Aqib Mirza +import { JsonLottieComp } from "./comps/jsonComp/jsonLottieComp"; ResponsiveLayoutCompIcon, MermaidIcon, } from "lowcoder-design"; @@ -1023,6 +1058,23 @@ var uiCompMap: Registry = { }, }, +// from Mousheng + icon: { + name: trans("uiComp.iconCompName"), + enName: "icon", + description: trans("uiComp.iconCompDesc"), + categories: ["dataDisplay"], + icon: IconCompIcon, + keywords: trans("uiComp.iconCompKeywords"), + comp: IconComp, + layoutInfo: { + w: 2, + h: 10, + }, + }, + +// from Mousheng + // Integration iframe: { diff --git a/client/packages/lowcoder/src/comps/uiCompRegistry.ts b/client/packages/lowcoder/src/comps/uiCompRegistry.ts index f015a46db..c74893811 100644 --- a/client/packages/lowcoder/src/comps/uiCompRegistry.ts +++ b/client/packages/lowcoder/src/comps/uiCompRegistry.ts @@ -119,12 +119,14 @@ export type UICompType = | "calendar" | "signature" | "jsonLottie" //Added By Aqib Mirza + | "icon" //Added By Mousheng | "timeline" //Added By Mousheng | "comment" //Added By Mousheng | "mention" //Added By Mousheng | "autocomplete" //Added By Mousheng | "responsiveLayout"; + export const uiCompRegistry = {} as Record; export function registerComp( diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts index 74b2296b9..bd41b118a 100644 --- a/client/packages/lowcoder/src/i18n/locales/en.ts +++ b/client/packages/lowcoder/src/i18n/locales/en.ts @@ -1068,7 +1068,11 @@ export const en = { // ninth part - + "iconComp": { + "icon": "Icon", + "autoSize": "Icon AutoSize", + "iconSize": "Icon Size", + }, "numberInput": { "formatter": "Format", "precision": "Precision", diff --git a/client/packages/lowcoder/src/i18n/locales/zh.ts b/client/packages/lowcoder/src/i18n/locales/zh.ts index 28194e00f..e0634a234 100644 --- a/client/packages/lowcoder/src/i18n/locales/zh.ts +++ b/client/packages/lowcoder/src/i18n/locales/zh.ts @@ -879,6 +879,9 @@ uiComp: { responsiveLayoutCompName: "响应式布局", responsiveLayoutCompDesc: "响应式布局", responsiveLayoutCompKeywords: "", + iconCompName: "图标", + iconCompDesc: "图标", + iconCompKeywords: "tb", }, comp: { menuViewDocs: "查看文档", @@ -2630,6 +2633,11 @@ timeLine: { helpvalue: "评论内容", helpcreatedAt: "创建时间", }, + iconComp: { + icon: "图标", + autoSize: "图标自动大小", + iconSize: "图标大小", + }, mention:{ mentionList: "提及列表", }, diff --git a/client/packages/lowcoder/src/pages/editor/editorConstants.tsx b/client/packages/lowcoder/src/pages/editor/editorConstants.tsx index 2820e14c9..5d69824e3 100644 --- a/client/packages/lowcoder/src/pages/editor/editorConstants.tsx +++ b/client/packages/lowcoder/src/pages/editor/editorConstants.tsx @@ -41,7 +41,9 @@ import { CommentIcon, MentionIcon, AutoCompleteCompIcon, + IconCompIcon, ResponsiveLayoutCompIcon, + } from "lowcoder-design"; export const CompStateIcon: { @@ -116,5 +118,7 @@ export const CompStateIcon: { comment: , mention: , autocomplete: , + icon: , responsiveLayout: , + };