Skip to content

Icons expansion #338

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
图标选择组件添加antD图标
iconControl add antd icon
  • Loading branch information
mousheng committed Aug 9, 2023
commit 5919277935c49790e51a4f1756d762aa216f8ba6
101 changes: 74 additions & 27 deletions client/packages/lowcoder-design/src/components/iconSelect/index.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import type { IconDefinition } from "@fortawesome/free-regular-svg-icons";
import { Popover } from "antd";
import { ActionType } from '@rc-component/trigger/lib/interface';
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 { List, ListRowProps } from "react-virtualized";
import styled from "styled-components";
import { CloseIcon, SearchIcon } from "icons";
import { ANTDICON } from "../../../../lowcoder/src/comps/comps/timelineComp/antIcon";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is ANTDICON in timelineComp?


const PopupContainer = styled.div`
width: 408px;
Expand Down Expand Up @@ -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 <FontAwesomeIcon icon={this.def} style={{ width: "1em", height: "1em" }} />;
if (this.names[0].startsWith("antd/")) return this.def;
else
return (
<FontAwesomeIcon
icon={this.def}
style={{ width: "1em", height: "1em" }}
/>
);
}
}

Expand Down Expand Up @@ -144,14 +164,25 @@ 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;
}

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) {
Expand All @@ -171,7 +202,8 @@ export function useIcon(value?: string) {
function search(
allIcons: Record<string, Icon>,
searchText: string,
searchKeywords?: Record<string, string>
searchKeywords?: Record<string, string>,
IconType?: "OnlyAntd" | "All" | "default" | undefined
) {
const tokens = searchText
.toLowerCase()
Expand All @@ -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)
Expand All @@ -198,16 +232,20 @@ const IconPopup = (props: {
label?: ReactNode;
onClose: () => void;
searchKeywords?: Record<string, string>;
IconType?: "OnlyAntd" | "All" | "default" | undefined;
}) => {
const [searchText, setSearchText] = useState("");
const [allIcons, setAllIcons] = useState<Record<string, Icon>>({});
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(() => {
Expand All @@ -217,24 +255,26 @@ const IconPopup = (props: {
const rowRenderer = useCallback(
(p: ListRowProps) => (
<IconRow key={p.key} style={p.style}>
{searchResults.slice(p.index * columnNum, (p.index + 1) * columnNum).map(([key, icon]) => (
<Tooltip
key={key}
title={icon.title}
placement="bottom"
align={{ offset: [0, -7, 0, 0] }}
destroyTooltipOnHide
>
<IconItemContainer
tabIndex={0}
onClick={() => {
onChangeIcon(key);
}}
{searchResults
.slice(p.index * columnNum, (p.index + 1) * columnNum)
.map(([key, icon]) => (
<Tooltip
key={key}
title={icon.title}
placement="bottom"
align={{ offset: [0, -7, 0, 0] }}
destroyTooltipOnHide
>
{icon.getView()}
</IconItemContainer>
</Tooltip>
))}
<IconItemContainer
tabIndex={0}
onClick={() => {
onChangeIcon(key);
}}
>
{icon.getView()}
</IconItemContainer>
</Tooltip>
))}
</IconRow>
),
[searchResults, allIcons, onChangeIcon]
Expand Down Expand Up @@ -279,6 +319,7 @@ export const IconSelectBase = (props: {
leftOffset?: number;
parent?: HTMLElement | null;
searchKeywords?: Record<string, string>;
IconType?: "OnlyAntd" | "All" | "default" | undefined;
}) => {
const { setVisible, parent } = props;
return (
Expand All @@ -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={
Expand All @@ -299,6 +344,7 @@ export const IconSelectBase = (props: {
label={props.label}
onClose={() => setVisible?.(false)}
searchKeywords={props.searchKeywords}
IconType={props.IconType}
/>
}
>
Expand All @@ -312,6 +358,7 @@ export const IconSelect = (props: {
label?: ReactNode;
children?: ReactNode;
searchKeywords?: Record<string, string>;
IconType?: "OnlyAntd" | "All" | "default" | undefined;
}) => {
const [visible, setVisible] = useState(false);
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface ControlParams extends CodeEditorControlParams {
preInputNode?: ReactNode;
childrenWrapperStyle?: CSSProperties;
extraChildren?: ReactNode;
IconType?: "OnlyAntd" | "All" | "default" | undefined;
}

export interface ControlType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ interface DropdownControlParams<T extends OptionsType> extends ControlParams {
showSearch?: boolean;
dropdownStyle?: React.CSSProperties;
labelStyle?: React.CSSProperties;
IconType?: "OnlyAntd" | "All" | "default" | undefined;
}

interface DropdownPropertyViewProps<T extends OptionsType>
Expand Down
5 changes: 4 additions & 1 deletion client/packages/lowcoder/src/comps/controls/iconControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,15 @@ const IconPicker = (props: {
value: string;
onChange: (value: string) => void;
label?: ReactNode;
IconType?: "OnlyAntd" | "All" | "default" | undefined;
}) => {
const icon = useIcon(props.value);
return (
<IconSelect
onChange={props.onChange}
label={props.label}
searchKeywords={i18nObjs.iconSearchKeywords}
IconType={props.IconType}
>
<TacoButton style={{ width: "100%" }}>
{icon ? (
Expand Down Expand Up @@ -251,7 +253,7 @@ export class IconControl extends AbstractComp<ReactNode, string, Node<ValueAndMs
{ filterText: params.label },
<Wrapper>
<SwitchWrapper label={params.label} tooltip={params.tooltip} lastNode={jsContent} />
{this.useCodeEditor && <IconCodeEditor codeControl={this.codeControl} params={params} />}
{this.useCodeEditor && <IconCodeEditor codeControl={this.codeControl} params={params}/>}
</Wrapper>
);
}
Expand All @@ -262,6 +264,7 @@ export class IconControl extends AbstractComp<ReactNode, string, Node<ValueAndMs
value={this.codeControl.getView()}
onChange={(x) => this.dispatchChangeValueAction(x)}
label={params.label}
IconType={params.IconType}
/>
)}
</ControlPropertyViewWrapper>
Expand Down