feat(ui): new WorkflowElements using antd
This commit is contained in:
71
ui/src/components/provider/AccessProviderSelect.tsx
Normal file
71
ui/src/components/provider/AccessProviderSelect.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import { memo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Avatar, Select, type SelectProps, Space, Tag, Typography } from "antd";
|
||||
|
||||
import { ACCESS_USAGES, accessProvidersMap } from "@/domain/provider";
|
||||
|
||||
export type AccessProviderSelectProps = Omit<
|
||||
SelectProps,
|
||||
"filterOption" | "filterSort" | "labelRender" | "options" | "optionFilterProp" | "optionLabelProp" | "optionRender"
|
||||
>;
|
||||
|
||||
const AccessProviderSelect = (props: AccessProviderSelectProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const options = Array.from(accessProvidersMap.values()).map((item) => ({
|
||||
key: item.type,
|
||||
value: item.type,
|
||||
label: t(item.name),
|
||||
}));
|
||||
|
||||
const renderOption = (key: string) => {
|
||||
const provider = accessProvidersMap.get(key);
|
||||
return (
|
||||
<div className="flex items-center justify-between gap-4 max-w-full overflow-hidden">
|
||||
<Space className="flex-grow max-w-full truncate" size={4}>
|
||||
<Avatar src={provider?.icon} size="small" />
|
||||
<Typography.Text className="leading-loose" ellipsis>
|
||||
{t(provider?.name ?? "")}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
<div>
|
||||
{provider?.usage === ACCESS_USAGES.APPLY && (
|
||||
<>
|
||||
<Tag color="orange">{t("access.props.provider.usage.dns")}</Tag>
|
||||
</>
|
||||
)}
|
||||
{provider?.usage === ACCESS_USAGES.DEPLOY && (
|
||||
<>
|
||||
<Tag color="blue">{t("access.props.provider.usage.host")}</Tag>
|
||||
</>
|
||||
)}
|
||||
{provider?.usage === ACCESS_USAGES.ALL && (
|
||||
<>
|
||||
<Tag color="orange">{t("access.props.provider.usage.dns")}</Tag>
|
||||
<Tag color="blue">{t("access.props.provider.usage.host")}</Tag>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Select
|
||||
{...props}
|
||||
labelRender={({ label, value }) => {
|
||||
if (label) {
|
||||
return renderOption(value as string);
|
||||
}
|
||||
|
||||
return <Typography.Text type="secondary">{props.placeholder}</Typography.Text>;
|
||||
}}
|
||||
options={options}
|
||||
optionFilterProp={undefined}
|
||||
optionLabelProp={undefined}
|
||||
optionRender={(option) => renderOption(option.data.value)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(AccessProviderSelect);
|
||||
75
ui/src/components/provider/DeployProviderPicker.tsx
Normal file
75
ui/src/components/provider/DeployProviderPicker.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import { memo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useDebounceEffect } from "ahooks";
|
||||
import { Avatar, Card, Col, Empty, Flex, Input, Row, Typography } from "antd";
|
||||
|
||||
import Show from "@/components/Show";
|
||||
import { deployProvidersMap } from "@/domain/provider";
|
||||
|
||||
export type DeployProviderPickerProps = {
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
onSelect?: (value: string) => void;
|
||||
};
|
||||
|
||||
const DeployProviderPicker = ({ className, style, onSelect }: DeployProviderPickerProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const allProviders = Array.from(deployProvidersMap.values());
|
||||
const [providers, setProviders] = useState(allProviders);
|
||||
const [keyword, setKeyword] = useState<string>();
|
||||
useDebounceEffect(
|
||||
() => {
|
||||
if (keyword) {
|
||||
setProviders(
|
||||
allProviders.filter((provider) => {
|
||||
const value = keyword.toLowerCase();
|
||||
return provider.type.toLowerCase().includes(value) || provider.name.toLowerCase().includes(value);
|
||||
})
|
||||
);
|
||||
} else {
|
||||
setProviders(allProviders);
|
||||
}
|
||||
},
|
||||
[keyword],
|
||||
{ wait: 300 }
|
||||
);
|
||||
|
||||
const handleProviderTypeSelect = (value: string) => {
|
||||
onSelect?.(value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={className} style={style}>
|
||||
<Input.Search placeholder={t("workflow_node.deploy.search.provider_type.placeholder")} onChange={(e) => setKeyword(e.target.value.trim())} />
|
||||
|
||||
<div className="mt-4">
|
||||
<Show when={providers.length > 0} fallback={<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}>
|
||||
<Row gutter={[16, 16]}>
|
||||
{providers.map((provider, index) => {
|
||||
return (
|
||||
<Col key={index} span={12}>
|
||||
<Card
|
||||
className="w-full h-16 shadow-sm overflow-hidden"
|
||||
styles={{ body: { height: "100%", padding: "0.5rem 1rem" } }}
|
||||
hoverable
|
||||
onClick={() => {
|
||||
handleProviderTypeSelect(provider.type);
|
||||
}}
|
||||
>
|
||||
<Flex className="size-full overflow-hidden" align="center" gap={8}>
|
||||
<Avatar src={provider.icon} size="small" />
|
||||
<Typography.Text className="line-clamp-2">{t(provider.name)}</Typography.Text>
|
||||
</Flex>
|
||||
</Card>
|
||||
</Col>
|
||||
);
|
||||
})}
|
||||
</Row>
|
||||
</Show>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(DeployProviderPicker);
|
||||
51
ui/src/components/provider/DeployProviderSelect.tsx
Normal file
51
ui/src/components/provider/DeployProviderSelect.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import { memo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Avatar, Select, type SelectProps, Space, Typography } from "antd";
|
||||
|
||||
import { deployProvidersMap } from "@/domain/provider";
|
||||
|
||||
export type DeployProviderSelectProps = Omit<
|
||||
SelectProps,
|
||||
"filterOption" | "filterSort" | "labelRender" | "options" | "optionFilterProp" | "optionLabelProp" | "optionRender"
|
||||
>;
|
||||
|
||||
const DeployProviderSelect = (props: DeployProviderSelectProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const options = Array.from(deployProvidersMap.values()).map((item) => ({
|
||||
key: item.type,
|
||||
value: item.type,
|
||||
label: t(item.name),
|
||||
}));
|
||||
|
||||
const renderOption = (key: string) => {
|
||||
const provider = deployProvidersMap.get(key);
|
||||
return (
|
||||
<Space className="flex-grow max-w-full truncate overflow-hidden" size={4}>
|
||||
<Avatar src={provider?.icon} size="small" />
|
||||
<Typography.Text className="leading-loose" ellipsis>
|
||||
{t(provider?.name ?? "")}
|
||||
</Typography.Text>
|
||||
</Space>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Select
|
||||
{...props}
|
||||
labelRender={({ label, value }) => {
|
||||
if (label) {
|
||||
return renderOption(value as string);
|
||||
}
|
||||
|
||||
return <Typography.Text type="secondary">{props.placeholder}</Typography.Text>;
|
||||
}}
|
||||
options={options}
|
||||
optionFilterProp={undefined}
|
||||
optionLabelProp={undefined}
|
||||
optionRender={(option) => renderOption(option.data.value)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(DeployProviderSelect);
|
||||
Reference in New Issue
Block a user