feat: manually mirror opencoze's code from bytedance
Change-Id: I09a73aadda978ad9511264a756b2ce51f5761adf
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
.imageflow-canvas-border-width {
|
||||
:global{
|
||||
.semi-slider-handle{
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type FC } from 'react';
|
||||
|
||||
import classnames from 'classnames';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { Slider } from '@coze-arch/coze-design';
|
||||
|
||||
import styles from './border-width.module.less';
|
||||
|
||||
interface IProps {
|
||||
value: number;
|
||||
onChange: (value: number) => void;
|
||||
options?: [number, number, number];
|
||||
min?: number;
|
||||
max?: number;
|
||||
}
|
||||
export const BorderWidth: FC<IProps> = props => {
|
||||
const { value, onChange, min, max } = props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames(
|
||||
'flex gap-[12px] text-[14px]',
|
||||
styles['imageflow-canvas-border-width'],
|
||||
)}
|
||||
>
|
||||
<div className="w-full flex items-center gap-[8px]">
|
||||
<div className="min-w-[42px]">
|
||||
{I18n.t('imageflow_canvas_stroke_width')}
|
||||
</div>
|
||||
<div className="flex-1 min-w-[320px]">
|
||||
<Slider
|
||||
min={min}
|
||||
max={max}
|
||||
step={1}
|
||||
showArrow={false}
|
||||
value={value}
|
||||
onChange={o => {
|
||||
onChange(o as number);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
.color-picker-slider{
|
||||
:global{
|
||||
.semi-slider{
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.semi-slider-handle{
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useCallback, type FC, useMemo } from 'react';
|
||||
|
||||
import classnames from 'classnames';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozCheckMarkFill } from '@coze-arch/coze-design/icons';
|
||||
import { Input, Slider } from '@coze-arch/coze-design';
|
||||
|
||||
import styles from './color-picker.module.less';
|
||||
interface IProps {
|
||||
// #ffffffff
|
||||
value: string | number;
|
||||
onChange: (value: string | number) => void;
|
||||
showOpacity?: boolean;
|
||||
showColor?: boolean;
|
||||
readonly?: boolean;
|
||||
}
|
||||
|
||||
const colors = [
|
||||
'#000000',
|
||||
'#ffffff',
|
||||
'#C6C6CD',
|
||||
'#FF441E',
|
||||
'#3EC254',
|
||||
'#4D53E8',
|
||||
'#00B2B2',
|
||||
'#FF9600',
|
||||
];
|
||||
|
||||
const ColorRect = (props: {
|
||||
color: string;
|
||||
size?: number;
|
||||
onClick?: () => void;
|
||||
selected?: boolean;
|
||||
className?: string;
|
||||
}) => {
|
||||
const { color, size = 24, onClick, selected, className } = props;
|
||||
return (
|
||||
<div
|
||||
onClick={onClick}
|
||||
className={`${className} rounded-[4px]`}
|
||||
style={{ backgroundColor: color, width: size, height: size }}
|
||||
>
|
||||
<div
|
||||
className={classnames([
|
||||
'relative top-0 left-0',
|
||||
'flex items-center justify-center',
|
||||
'rounded-[4px] border border-solid border-stroke',
|
||||
])}
|
||||
style={{
|
||||
width: size,
|
||||
height: size,
|
||||
color: color !== '#ffffff' ? '#fff' : '#000',
|
||||
}}
|
||||
>
|
||||
{selected ? <IconCozCheckMarkFill /> : undefined}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const isHexOpacityColor = (value: string): boolean =>
|
||||
/^#[0-9A-Fa-f]{8}$/.test(value);
|
||||
const isHexColor = (value: string): boolean => /^#[0-9A-Fa-f]{6}$/.test(value);
|
||||
const opacity16To255ScaleTo100 = (v: string): number => parseInt(v, 16) / 2.55;
|
||||
const opacity100ScaleTo255To16 = (v: number): string =>
|
||||
Math.floor(v * 2.55)
|
||||
.toString(16)
|
||||
.padStart(2, '0');
|
||||
|
||||
export const ColorPicker: FC<IProps> = props => {
|
||||
const {
|
||||
value = '#ffffffff',
|
||||
onChange,
|
||||
showOpacity = true,
|
||||
showColor = true,
|
||||
readonly = false,
|
||||
} = props;
|
||||
|
||||
const { color, opacity } = useMemo(() => {
|
||||
if (!showColor) {
|
||||
return {
|
||||
opacity: (value as number) * 100,
|
||||
};
|
||||
}
|
||||
return {
|
||||
color: (value as string).substring(0, 7),
|
||||
opacity: opacity16To255ScaleTo100((value as string).substring(7, 9)),
|
||||
};
|
||||
}, [value, showColor]);
|
||||
|
||||
const _onChange = useCallback(
|
||||
(v: string) => {
|
||||
if (isHexOpacityColor(v)) {
|
||||
onChange(v);
|
||||
}
|
||||
},
|
||||
[onChange],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col w-full gap-[12px] text-[14px]">
|
||||
{showColor ? (
|
||||
<div className="flex items-center w-full gap-[16px]">
|
||||
<div className="flex items-center flex-1 gap-[12px]">
|
||||
{colors.map(c => {
|
||||
const selected =
|
||||
c.toUpperCase() === (color as string).toUpperCase();
|
||||
return (
|
||||
<ColorRect
|
||||
key={`rect-${c}`}
|
||||
className={`${readonly ? '' : 'cursor-pointer'}`}
|
||||
selected={selected}
|
||||
onClick={() => {
|
||||
if (readonly) {
|
||||
return;
|
||||
}
|
||||
_onChange(`${c}${opacity100ScaleTo255To16(opacity)}`);
|
||||
}}
|
||||
color={c}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<Input
|
||||
// 因为是不受控模式,当点击色块时,需要重置 input.value。所以这里以 color 为 key
|
||||
key={`input-${color}`}
|
||||
disabled={readonly}
|
||||
prefix={<ColorRect color={color as string} size={16} />}
|
||||
type="text"
|
||||
className="w-[110px]"
|
||||
// 为什么不使用受控模式?使用受控模式,用户输入过程中触发的格式校验处理起来比较麻烦
|
||||
defaultValue={color}
|
||||
onChange={v => {
|
||||
if (isHexColor(v)) {
|
||||
_onChange(`${v}${opacity100ScaleTo255To16(opacity)}`);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : undefined}
|
||||
{showOpacity ? (
|
||||
<div className="w-full flex items-center gap-[8px]">
|
||||
<div className="min-w-[80px]">
|
||||
{I18n.t('imageflow_canvas_transparency')}
|
||||
</div>
|
||||
<div
|
||||
className={classnames(
|
||||
'flex-1 min-w-[320px]',
|
||||
styles['color-picker-slider'],
|
||||
)}
|
||||
>
|
||||
<Slider
|
||||
min={0}
|
||||
showArrow={false}
|
||||
max={100}
|
||||
step={1}
|
||||
value={opacity}
|
||||
disabled={readonly}
|
||||
onChange={o => {
|
||||
if (!showColor) {
|
||||
onChange((o as number) / 100);
|
||||
} else {
|
||||
_onChange(`${color}${opacity100ScaleTo255To16(o as number)}`);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : undefined}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type FC, useCallback, useMemo } from 'react';
|
||||
|
||||
import { clamp } from 'lodash-es';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozFontSize } from '@coze-arch/coze-design/icons';
|
||||
import { Select, type SelectProps, Tooltip } from '@coze-arch/coze-design';
|
||||
|
||||
interface IProps extends Omit<SelectProps, 'onChange'> {
|
||||
value: number;
|
||||
onChange: (value: number) => void;
|
||||
min: number;
|
||||
max: number;
|
||||
}
|
||||
|
||||
export const FontSize: FC<IProps> = props => {
|
||||
const { onChange, min, max, optionList, value, ...rest } = props;
|
||||
const _onChange = useCallback(
|
||||
(v: number) => {
|
||||
if (isFinite(v)) {
|
||||
onChange?.(clamp(v, min, max));
|
||||
}
|
||||
},
|
||||
[onChange, min, max],
|
||||
);
|
||||
|
||||
const _optionsList = useMemo(() => {
|
||||
const _options = [...(optionList ?? [])];
|
||||
if (!_options.map(o => o.value).includes(value)) {
|
||||
_options.unshift({
|
||||
label: `${value}`,
|
||||
value,
|
||||
});
|
||||
}
|
||||
return _options;
|
||||
}, [optionList, value]);
|
||||
|
||||
return (
|
||||
<div className="flex gap-[8px] items-center">
|
||||
{/* <IconCozFontSize className="text-[16px] m-[8px]" /> */}
|
||||
<Tooltip
|
||||
content={I18n.t('imageflow_canvas_text_tooltip1')}
|
||||
mouseEnterDelay={300}
|
||||
mouseLeaveDelay={300}
|
||||
>
|
||||
<Select
|
||||
{...rest}
|
||||
prefix={
|
||||
<IconCozFontSize className="text-[16px] coz-fg-secondary m-[8px]" />
|
||||
}
|
||||
/**
|
||||
* 因为开启了 allowCreate,所以 optionList 不会再响应动态变化
|
||||
* 这里给个 key ,重新渲染 select,保证 optionList 符合预期
|
||||
*/
|
||||
key={_optionsList.map(d => d.label).join()}
|
||||
value={value}
|
||||
optionList={_optionsList}
|
||||
filter
|
||||
allowCreate
|
||||
onChange={v => {
|
||||
_onChange(v as number);
|
||||
}}
|
||||
style={{ width: '98px' }}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { type FC } from 'react';
|
||||
|
||||
import { Select } from '@coze-arch/coze-design';
|
||||
|
||||
import { Uploader } from './uploader';
|
||||
import { TextType } from './text-type';
|
||||
import { TextFamily } from './text-family';
|
||||
import { TextAlign } from './text-align';
|
||||
import { SingleSelect } from './single-select';
|
||||
import { RefSelect } from './ref-select';
|
||||
import { LineHeight } from './line-height';
|
||||
import { LabelSelect } from './label-select';
|
||||
import { InputNumber } from './input-number';
|
||||
import { FontSize } from './font-size';
|
||||
import { ColorPicker } from './color-picker';
|
||||
import { BorderWidth } from './border-width';
|
||||
|
||||
export const setters: Record<string, FC<any>> = {
|
||||
ColorPicker,
|
||||
TextAlign,
|
||||
InputNumber,
|
||||
TextType,
|
||||
SingleSelect,
|
||||
BorderWidth,
|
||||
Select,
|
||||
TextFamily,
|
||||
FontSize,
|
||||
LineHeight,
|
||||
LabelSelect,
|
||||
Uploader,
|
||||
RefSelect,
|
||||
};
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
import {
|
||||
InputNumber as CozeInputNumber,
|
||||
type InputNumberProps,
|
||||
} from '@coze-arch/coze-design';
|
||||
|
||||
export const InputNumber = forwardRef<InputNumberProps, InputNumberProps>(
|
||||
props => {
|
||||
const { onChange, min, max, value, ...rest } = props;
|
||||
return (
|
||||
<CozeInputNumber
|
||||
{...rest}
|
||||
min={min}
|
||||
max={max}
|
||||
value={value}
|
||||
// InputNumber 长按 + - 时,会一直触发变化。这里有 bug,有时定时器清不掉,会鬼畜(一直增加/减小)。
|
||||
// 把 pressInterval 设置成 24h ,变相禁用长按增减
|
||||
pressInterval={1000 * 60 * 60 * 24}
|
||||
onNumberChange={v => {
|
||||
if (Number.isFinite(v)) {
|
||||
if (typeof min === 'number' && (v as number) < min) {
|
||||
onChange?.(min);
|
||||
} else if (typeof max === 'number' && (v as number) > max) {
|
||||
onChange?.(max);
|
||||
} else {
|
||||
const _v = Number((v as number).toFixed(1));
|
||||
if (_v !== value) {
|
||||
onChange?.(Number((v as number).toFixed(1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type FC } from 'react';
|
||||
|
||||
import { Select, type SelectProps } from '@coze-arch/coze-design';
|
||||
|
||||
type IProps = SelectProps & { label: string };
|
||||
export const LabelSelect: FC<IProps> = props => {
|
||||
const { label, ...rest } = props;
|
||||
return (
|
||||
<div className="w-full flex gap-[8px] justify-between items-center text-[14px]">
|
||||
<div className="min-w-[80px]">{label}</div>
|
||||
<Select {...rest} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type FC, useCallback, useMemo } from 'react';
|
||||
|
||||
import { clamp } from 'lodash-es';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozFontHeight } from '@coze-arch/coze-design/icons';
|
||||
import { Select, Tooltip, type SelectProps } from '@coze-arch/coze-design';
|
||||
|
||||
interface IProps extends Omit<SelectProps, 'onChange'> {
|
||||
value: number;
|
||||
onChange: (value: number) => void;
|
||||
min: number;
|
||||
max: number;
|
||||
}
|
||||
|
||||
export const LineHeight: FC<IProps> = props => {
|
||||
const { onChange, min, max, value, optionList, ...rest } = props;
|
||||
const _onChange = useCallback(
|
||||
(v: string) => {
|
||||
const _v = Number(`${v}`.replace('%', ''));
|
||||
if (isFinite(_v)) {
|
||||
onChange?.(Number((clamp(_v, min, max) / 100).toFixed(2)));
|
||||
}
|
||||
},
|
||||
[onChange, min, max],
|
||||
);
|
||||
|
||||
const _optionsList = useMemo(() => {
|
||||
const _options = [...(optionList ?? [])];
|
||||
if (
|
||||
!_options
|
||||
.map(o => o.value)
|
||||
.includes(
|
||||
Number((Number(`${value}`.replace('%', '')) * 100).toFixed(0)),
|
||||
)
|
||||
) {
|
||||
_options.unshift({
|
||||
label: `${Number((value * 100).toFixed(0))}%`,
|
||||
value: Number((value * 100).toFixed(0)),
|
||||
});
|
||||
}
|
||||
return _options;
|
||||
}, [optionList, value]);
|
||||
|
||||
return (
|
||||
<div className="flex gap-[8px] items-center">
|
||||
{/* <IconCozFontHeight className="text-[16px] m-[8px]" /> */}
|
||||
<Tooltip
|
||||
content={I18n.t('imageflow_canvas_text_tooltip2')}
|
||||
mouseEnterDelay={300}
|
||||
mouseLeaveDelay={300}
|
||||
>
|
||||
<Select
|
||||
prefix={
|
||||
<IconCozFontHeight className="text-[16px] coz-fg-secondary m-[8px]" />
|
||||
}
|
||||
{...rest}
|
||||
/**
|
||||
* 因为开启了 allowCreate,所以 optionList 不会再响应动态变化
|
||||
* 这里给个 key ,重新渲染 select,保证 optionList 符合预期
|
||||
*/
|
||||
key={_optionsList.map(d => d.label).join()}
|
||||
filter
|
||||
value={Number((value * 100).toFixed(0))}
|
||||
allowCreate
|
||||
onChange={v => {
|
||||
_onChange(v as string);
|
||||
}}
|
||||
optionList={_optionsList}
|
||||
style={{ width: '104px' }}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
.ref-select{
|
||||
// :global(.semi-select-content-wrapper){
|
||||
// width: 100%;
|
||||
// }
|
||||
|
||||
:global(.semi-select-selection){
|
||||
@apply !ml-[4px];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type FC } from 'react';
|
||||
|
||||
import cls from 'classnames';
|
||||
import { ViewVariableType } from '@coze-workflow/base/types';
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozImage, IconCozString } from '@coze-arch/coze-design/icons';
|
||||
import {
|
||||
type RenderSelectedItemFn,
|
||||
Select,
|
||||
type SelectProps,
|
||||
Tag,
|
||||
} from '@coze-arch/coze-design';
|
||||
|
||||
import { useGlobalContext } from '../../context';
|
||||
|
||||
import s from './ref-select.module.less';
|
||||
|
||||
type IProps = SelectProps & {
|
||||
value: string;
|
||||
label: string;
|
||||
labelInside: boolean;
|
||||
};
|
||||
export const RefSelect: FC<IProps> = props => {
|
||||
const { value: objectId, label, labelInside, className } = props;
|
||||
const { customVariableRefs, variables, updateRefByObjectId } =
|
||||
useGlobalContext();
|
||||
|
||||
const targetRef = customVariableRefs?.find(ref => ref.objectId === objectId);
|
||||
const value = targetRef?.variableId;
|
||||
const targetVariable = variables?.find(v => v.id === value);
|
||||
|
||||
return (
|
||||
<div className="w-full text-[14px] flex flex-row gap-[8px] items-center">
|
||||
{!labelInside && label ? (
|
||||
<div className="text-[14px] min-w-[80px]">{label}</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<Select
|
||||
prefix={
|
||||
labelInside && label ? (
|
||||
<div className="text-[14px] pl-[8px] pr-[4px] py-[2px] min-w-[40px]">
|
||||
{label}
|
||||
</div>
|
||||
) : undefined
|
||||
}
|
||||
showClear
|
||||
showTick={false}
|
||||
placeholder={I18n.t('imageflow_canvas_select_var', {}, '选择变量')}
|
||||
value={targetRef?.variableId}
|
||||
className={cls(className, s['ref-select'])}
|
||||
onChange={d => {
|
||||
updateRefByObjectId?.({
|
||||
objectId,
|
||||
variable: d ? variables?.find(v => v.id === d) : undefined,
|
||||
});
|
||||
}}
|
||||
renderSelectedItem={
|
||||
((options: { value: string; label: React.ReactNode }) => {
|
||||
const { value: _value, label: _label } = options;
|
||||
const variable = variables?.find(v => v.id === _value);
|
||||
if (variable) {
|
||||
return (
|
||||
<Tag color="primary" className="w-full">
|
||||
<div className="flex flex-row items-center gap-[4px] w-full">
|
||||
{variable.type === ViewVariableType.String ? (
|
||||
<IconCozString className="coz-fg-dim" />
|
||||
) : (
|
||||
<IconCozImage className="coz-fg-dim" />
|
||||
)}
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<div className="truncate w-full overflow-hidden">
|
||||
{variable.name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Tag>
|
||||
);
|
||||
}
|
||||
return _label;
|
||||
}) as RenderSelectedItemFn
|
||||
}
|
||||
>
|
||||
{value && !targetVariable ? (
|
||||
<Select.Option value={value}>
|
||||
<Tag className="max-w-full m-[8px]" color="yellow">
|
||||
<div className="truncate overflow-hidden">
|
||||
{I18n.t('imageflow_canvas_var_delete', {}, '变量被删除')}
|
||||
</div>
|
||||
</Tag>
|
||||
</Select.Option>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{variables?.map(v => (
|
||||
<Select.Option value={v.id}>
|
||||
<div className="flex flex-row items-center gap-[4px] w-full p-[8px] max-w-[400px]">
|
||||
{v.type === ViewVariableType.String ? (
|
||||
<IconCozString className="coz-fg-dim" />
|
||||
) : (
|
||||
<IconCozImage className="coz-fg-dim" />
|
||||
)}
|
||||
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<div className="truncate w-full overflow-hidden">{v.name}</div>
|
||||
</div>
|
||||
{v.id === value ? (
|
||||
<Tag color="primary">
|
||||
{I18n.t('eval_status_referenced', {}, '已引用')}
|
||||
</Tag>
|
||||
) : null}
|
||||
</div>
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { forwardRef } from 'react';
|
||||
|
||||
import {
|
||||
SingleSelect as CozeSingleSelect,
|
||||
type SingleSelectProps,
|
||||
} from '@coze-arch/coze-design';
|
||||
|
||||
export const SingleSelect = forwardRef<SingleSelectProps, SingleSelectProps>(
|
||||
props => {
|
||||
// (props, ref) => {
|
||||
const { onChange, ...rest } = props;
|
||||
return (
|
||||
<CozeSingleSelect
|
||||
{...rest}
|
||||
// ref={ref}
|
||||
onChange={v => {
|
||||
onChange?.(v.target.value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { type FC } from 'react';
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import {
|
||||
IconCozTextAlignCenter,
|
||||
IconCozTextAlignLeft,
|
||||
IconCozTextAlignRight,
|
||||
} from '@coze-arch/coze-design/icons';
|
||||
import { Select, type RenderSelectedItemFn } from '@coze-arch/coze-design';
|
||||
|
||||
import { TextAlign as TextAlignEnum } from '../../typings';
|
||||
|
||||
interface IProps {
|
||||
value: TextAlignEnum;
|
||||
onChange: (value: TextAlignEnum) => void;
|
||||
}
|
||||
export const TextAlign: FC<IProps> = props => {
|
||||
const { value, onChange } = props;
|
||||
|
||||
return (
|
||||
<Select
|
||||
// borderless
|
||||
className="border-0 hover:border-0 focus:border-0"
|
||||
value={value}
|
||||
onChange={v => {
|
||||
onChange(v as TextAlignEnum);
|
||||
}}
|
||||
optionList={[
|
||||
{
|
||||
icon: <IconCozTextAlignLeft className="text-[16px]" />,
|
||||
label: I18n.t('card_builder_hover_align_left'),
|
||||
value: TextAlignEnum.LEFT,
|
||||
},
|
||||
{
|
||||
icon: <IconCozTextAlignCenter className="text-[16px]" />,
|
||||
label: I18n.t('card_builder_hover_align_horizontal'),
|
||||
value: TextAlignEnum.CENTER,
|
||||
},
|
||||
{
|
||||
icon: <IconCozTextAlignRight className="text-[16px]" />,
|
||||
label: I18n.t('card_builder_hover_align_right'),
|
||||
value: TextAlignEnum.RIGHT,
|
||||
},
|
||||
].map(d => ({
|
||||
...d,
|
||||
label: (
|
||||
<div className="flex flex-row items-center gap-[4px]">
|
||||
{d.icon}
|
||||
{d.label}
|
||||
</div>
|
||||
),
|
||||
}))}
|
||||
renderSelectedItem={
|
||||
((option: { icon: React.ReactNode }) => {
|
||||
const { icon } = option;
|
||||
return <div className="flex flex-row items-center">{icon}</div>;
|
||||
}) as RenderSelectedItemFn
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
.imageflow-canvas-font-family-cascader {
|
||||
:global{
|
||||
.semi-cascader-option-lists{
|
||||
height: 300px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type FC } from 'react';
|
||||
|
||||
import { Cascader } from '@coze-arch/coze-design';
|
||||
|
||||
import s from './text-family.module.less';
|
||||
|
||||
interface IProps {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
}
|
||||
export const TextFamily: FC<IProps> = props => {
|
||||
// (props, ref) => {
|
||||
const { onChange, value, ...rest } = props;
|
||||
return (
|
||||
<Cascader
|
||||
{...rest}
|
||||
value={value?.split('-')?.reverse()}
|
||||
onChange={v => {
|
||||
onChange?.((v as string[])?.reverse()?.join('-'));
|
||||
}}
|
||||
dropdownClassName={s['imageflow-canvas-font-family-cascader']}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type FC } from 'react';
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import {
|
||||
IconCozFixedSize,
|
||||
IconCozAutoWidth,
|
||||
} from '@coze-arch/coze-design/icons';
|
||||
import { Tooltip } from '@coze-arch/coze-design';
|
||||
|
||||
import { MyIconButton } from '../icon-button';
|
||||
import { Mode } from '../../typings';
|
||||
|
||||
interface IProps {
|
||||
value: Mode;
|
||||
onChange: (value: Mode) => void;
|
||||
}
|
||||
export const TextType: FC<IProps> = props => {
|
||||
const { value, onChange } = props;
|
||||
return (
|
||||
<div className="flex gap-[12px]">
|
||||
<Tooltip
|
||||
mouseEnterDelay={300}
|
||||
mouseLeaveDelay={300}
|
||||
content={I18n.t('imageflow_canvas_text1')}
|
||||
>
|
||||
<MyIconButton
|
||||
inForm
|
||||
color={value === Mode.INLINE_TEXT ? 'highlight' : 'secondary'}
|
||||
onClick={() => {
|
||||
onChange(Mode.INLINE_TEXT);
|
||||
}}
|
||||
icon={<IconCozAutoWidth className="text-[16px]" />}
|
||||
/>
|
||||
</Tooltip>
|
||||
|
||||
<Tooltip
|
||||
mouseEnterDelay={300}
|
||||
mouseLeaveDelay={300}
|
||||
content={I18n.t('imageflow_canvas_text2')}
|
||||
>
|
||||
<MyIconButton
|
||||
inForm
|
||||
color={value === Mode.BLOCK_TEXT ? 'highlight' : 'secondary'}
|
||||
onClick={() => {
|
||||
onChange(Mode.BLOCK_TEXT);
|
||||
}}
|
||||
icon={<IconCozFixedSize className="text-[16px]" />}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2025 coze-dev Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { type FC } from 'react';
|
||||
|
||||
import { I18n } from '@coze-arch/i18n';
|
||||
import { IconCozLoading, IconCozUpload } from '@coze-arch/coze-design/icons';
|
||||
import { Button } from '@coze-arch/coze-design';
|
||||
|
||||
import { ImageUpload } from '../topbar/image-upload';
|
||||
|
||||
interface IProps {
|
||||
getLabel: (isRefElement: boolean) => string;
|
||||
onChange: (url: string) => void;
|
||||
isRefElement: boolean;
|
||||
}
|
||||
export const Uploader: FC<IProps> = props => {
|
||||
const { getLabel, onChange, isRefElement } = props;
|
||||
return (
|
||||
<div className="w-full flex gap-[8px] justify-between items-center text-[14px]">
|
||||
<div className="min-w-[80px]">{getLabel(isRefElement)}</div>
|
||||
<ImageUpload
|
||||
disabledTooltip
|
||||
onChange={onChange}
|
||||
tooltip={I18n.t('card_builder_image')}
|
||||
className="flex-1"
|
||||
>
|
||||
{({ loading, cancel }) => (
|
||||
<Button
|
||||
className="w-full"
|
||||
color="primary"
|
||||
onClick={() => {
|
||||
loading && cancel();
|
||||
}}
|
||||
icon={
|
||||
loading ? (
|
||||
<IconCozLoading className={'loading coz-fg-dim'} />
|
||||
) : (
|
||||
<IconCozUpload />
|
||||
)
|
||||
}
|
||||
>
|
||||
{loading
|
||||
? I18n.t('imageflow_canvas_cancel_change', {}, '取消上传')
|
||||
: I18n.t('imageflow_canvas_change_img', {}, '更换图片')}
|
||||
</Button>
|
||||
)}
|
||||
</ImageUpload>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user