mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-03-07 19:17:07 +00:00
chore: 移除内置 setters
This commit is contained in:
parent
00300cbe8c
commit
32e235fcb1
@ -1,314 +0,0 @@
|
|||||||
import { Component, Fragment } from 'react';
|
|
||||||
import { Icon, Button, Message } from '@alifd/next';
|
|
||||||
import { Title, runInAction } from '@ali/lowcode-editor-core';
|
|
||||||
import { SetterType, FieldConfig, SetterConfig } from '@ali/lowcode-types';
|
|
||||||
import { SettingField } from '@ali/lowcode-designer';
|
|
||||||
import { createSettingFieldView } from '../settings/settings-pane';
|
|
||||||
import { PopupContext, PopupPipe } from '../popup';
|
|
||||||
import Sortable from './sortable';
|
|
||||||
import './style.less';
|
|
||||||
|
|
||||||
interface ArraySetterState {
|
|
||||||
items: SettingField[];
|
|
||||||
itemsMap: Map<string | number, SettingField>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ArraySetterProps {
|
|
||||||
value: any[];
|
|
||||||
field: SettingField;
|
|
||||||
itemSetter?: SetterType;
|
|
||||||
columns?: FieldConfig[];
|
|
||||||
multiValue?: boolean;
|
|
||||||
onChange?: Function;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ListSetter extends Component<ArraySetterProps, ArraySetterState> {
|
|
||||||
state: ArraySetterState = {
|
|
||||||
items: [],
|
|
||||||
itemsMap: new Map<string | number, SettingField>(),
|
|
||||||
};
|
|
||||||
|
|
||||||
onItemChange = async () => {
|
|
||||||
const { onChange } = this.props;
|
|
||||||
const { items } = this.state;
|
|
||||||
// setValue 的props数据合并会有一些延时,这里延时100毫秒来等待数据合并完成获取到最新数据
|
|
||||||
await this.delay(100);
|
|
||||||
onChange && onChange(items.map(item => {
|
|
||||||
return item && item.getValue();
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const { value, field, onChange } = this.props;
|
|
||||||
const itemsMap = new Map<string | number, SettingField>();
|
|
||||||
const items = [];
|
|
||||||
const valueLength = value && Array.isArray(value) ? value.length : 0;
|
|
||||||
|
|
||||||
for (let i = 0; i < valueLength; i++) {
|
|
||||||
const item = field.createField({
|
|
||||||
name: i,
|
|
||||||
setter: this.props.itemSetter,
|
|
||||||
forceInline: 1,
|
|
||||||
setValue: this.onItemChange,
|
|
||||||
});
|
|
||||||
items[i] = item;
|
|
||||||
itemsMap.set(item.id, item);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
items,
|
|
||||||
itemsMap,
|
|
||||||
}, () => {
|
|
||||||
// setValue 会触发onItemChange,需要在items被设值之后才能调用
|
|
||||||
runInAction(() => {
|
|
||||||
value && value.map((item, index) => {
|
|
||||||
items[index].setValue(item);
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
delay(ms) {
|
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
|
||||||
}
|
|
||||||
|
|
||||||
onSort(sortedIds: Array<string | number>) {
|
|
||||||
const { itemsMap } = this.state;
|
|
||||||
const { onChange, itemSetter, field } = this.props;
|
|
||||||
const sortValues = sortedIds.map((id) => {
|
|
||||||
const value = itemsMap.get(id)!.getValue();
|
|
||||||
return value;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 对itemsMap重新生成并刷新当前setter数据
|
|
||||||
const newItems: SettingField[] = [];
|
|
||||||
// const newItemsMap = {};
|
|
||||||
itemsMap.clear();
|
|
||||||
for (let i = 0; i < sortValues.length; i++) {
|
|
||||||
const newItem = field.createField({
|
|
||||||
name: i,
|
|
||||||
setter: itemSetter,
|
|
||||||
// FIXME:
|
|
||||||
forceInline: 1,
|
|
||||||
setValue: this.onItemChange,
|
|
||||||
});
|
|
||||||
newItems[i] = newItem;
|
|
||||||
|
|
||||||
itemsMap.set(newItem.id, newItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
items: newItems,
|
|
||||||
itemsMap,
|
|
||||||
}, () => {
|
|
||||||
sortValues && sortValues.map((value, index) => {
|
|
||||||
newItems[index].setValue(value);
|
|
||||||
return newItems;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private scrollToLast = false;
|
|
||||||
|
|
||||||
onAdd() {
|
|
||||||
const { items, itemsMap } = this.state;
|
|
||||||
const { itemSetter, onChange } = this.props;
|
|
||||||
const initialValue = typeof itemSetter === 'object' ? (itemSetter as any).initialValue : null;
|
|
||||||
const item = this.props.field.createField({
|
|
||||||
name: items.length,
|
|
||||||
setter: itemSetter,
|
|
||||||
// FIXME:
|
|
||||||
forceInline: 1,
|
|
||||||
setValue: this.onItemChange,
|
|
||||||
});
|
|
||||||
items.push(item);
|
|
||||||
itemsMap.set(item.id, item);
|
|
||||||
this.scrollToLast = true;
|
|
||||||
this.setState({
|
|
||||||
items: items.slice(),
|
|
||||||
}, () => {
|
|
||||||
item.setValue(typeof initialValue === 'function' ? initialValue(item) : initialValue);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onRemove(field: SettingField) {
|
|
||||||
const { onChange } = this.props;
|
|
||||||
const { items, itemsMap } = this.state;
|
|
||||||
let i = items.indexOf(field);
|
|
||||||
const values = items.map((item) => {
|
|
||||||
return item.getValue();
|
|
||||||
});
|
|
||||||
if (i < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
items.splice(i, 1);
|
|
||||||
values.splice(i, 1);
|
|
||||||
const l = items.length;
|
|
||||||
while (i < l) {
|
|
||||||
items[i].setKey(i);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
itemsMap.delete(field.id);
|
|
||||||
field.remove();
|
|
||||||
onChange && onChange(values);
|
|
||||||
this.setState({ items: items.slice() });
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
this.state.items.forEach((field) => {
|
|
||||||
field.purge();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
let columns: any = null;
|
|
||||||
if (this.props.columns) {
|
|
||||||
columns = this.props.columns.map((column) => (
|
|
||||||
<Title key={column.name} title={column.title || (column.name as string)} />
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
const { items } = this.state;
|
|
||||||
const { scrollToLast } = this;
|
|
||||||
this.scrollToLast = false;
|
|
||||||
const lastIndex = items.length - 1;
|
|
||||||
|
|
||||||
const content =
|
|
||||||
items.length > 0 ? (
|
|
||||||
<div className="lc-setter-list-scroll-body">
|
|
||||||
<Sortable itemClassName="lc-setter-list-card" onSort={this.onSort.bind(this)}>
|
|
||||||
{items.map((field, index) => (
|
|
||||||
<ArrayItem
|
|
||||||
key={field.id}
|
|
||||||
scrollIntoView={scrollToLast && index === lastIndex}
|
|
||||||
field={field}
|
|
||||||
onRemove={this.onRemove.bind(this, field)}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Sortable>
|
|
||||||
</div>
|
|
||||||
) : this.props.multiValue ? (<Message type="warning">当前选择了多个节点,且值不一致,修改会覆盖所有值</Message>) : (<Message type="notice">当前项目为空</Message>);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="lc-setter-list lc-block-setter">
|
|
||||||
{/* <div className="lc-block-setter-actions">
|
|
||||||
<Button size="medium" onClick={this.onAdd.bind(this)}>
|
|
||||||
<Icon type="add" />
|
|
||||||
<span>添加</span>
|
|
||||||
</Button>
|
|
||||||
</div> */}
|
|
||||||
{columns && <div className="lc-setter-list-columns">{columns}</div>}
|
|
||||||
{content}
|
|
||||||
<Button className="lc-setter-list-add" type="primary" onClick={this.onAdd.bind(this)}>
|
|
||||||
<Icon type="add" />
|
|
||||||
<span>添加一项</span>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ArrayItem extends Component<{
|
|
||||||
field: SettingField;
|
|
||||||
onRemove: () => void;
|
|
||||||
scrollIntoView: boolean;
|
|
||||||
}> {
|
|
||||||
shouldComponentUpdate() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private shell?: HTMLDivElement | null;
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
if (this.props.scrollIntoView && this.shell) {
|
|
||||||
this.shell.parentElement!.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { onRemove, field } = this.props;
|
|
||||||
return (
|
|
||||||
<div className="lc-listitem" ref={(ref) => { this.shell = ref; }}>
|
|
||||||
<div draggable className="lc-listitem-handler">
|
|
||||||
<Icon type="ellipsis" size="small" />
|
|
||||||
</div>
|
|
||||||
<div className="lc-listitem-body">{createSettingFieldView(field, field.parent)}</div>
|
|
||||||
<div className="lc-listitem-actions">
|
|
||||||
<div className="lc-listitem-action" onClick={onRemove}>
|
|
||||||
<Icon type="ashbin" size="small" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TableSetter extends ListSetter {
|
|
||||||
// todo:
|
|
||||||
// forceInline = 1
|
|
||||||
// has more actions
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class ArraySetter extends Component<{
|
|
||||||
value: any[];
|
|
||||||
field: SettingField;
|
|
||||||
itemSetter?: SetterType;
|
|
||||||
mode?: 'popup' | 'list';
|
|
||||||
forceInline?: boolean;
|
|
||||||
multiValue?: boolean;
|
|
||||||
}> {
|
|
||||||
static contextType = PopupContext;
|
|
||||||
|
|
||||||
private pipe: any;
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { mode, forceInline, ...props } = this.props;
|
|
||||||
const { field, itemSetter } = props;
|
|
||||||
let columns: FieldConfig[] | undefined;
|
|
||||||
if ((itemSetter as SetterConfig)?.componentName === 'ObjectSetter') {
|
|
||||||
const items: FieldConfig[] = (itemSetter as any).props?.config?.items;
|
|
||||||
if (items && Array.isArray(items)) {
|
|
||||||
columns = items.filter((item) => item.isRequired || item.important || (item.setter as any)?.isRequired);
|
|
||||||
if (columns.length > 4) {
|
|
||||||
columns = columns.slice(0, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mode === 'popup' || forceInline) {
|
|
||||||
const title = (
|
|
||||||
<Fragment>
|
|
||||||
编辑:
|
|
||||||
<Title title={field.title} />
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
if (!this.pipe) {
|
|
||||||
let width = 360;
|
|
||||||
if (columns) {
|
|
||||||
if (columns.length === 3) {
|
|
||||||
width = 480;
|
|
||||||
} else if (columns.length > 3) {
|
|
||||||
width = 600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.pipe = (this.context as PopupPipe).create({ width });
|
|
||||||
}
|
|
||||||
|
|
||||||
this.pipe.send(<TableSetter key={field.id} {...props} columns={columns} />, title);
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
type={forceInline ? 'normal' : 'primary'}
|
|
||||||
onClick={(e) => {
|
|
||||||
this.pipe.show((e as any).target, field.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon type="edit" />
|
|
||||||
{forceInline ? title : '编辑数组'}
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return <ListSetter {...props} columns={columns?.slice(0, 2)} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
.lc-sortable {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.lc-sortable-card {
|
|
||||||
box-sizing: border-box;
|
|
||||||
&:after, &:before {
|
|
||||||
content: "";
|
|
||||||
display: table;
|
|
||||||
}
|
|
||||||
&:after {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.lc-dragging {
|
|
||||||
outline: 2px dashed var(--color-brand);
|
|
||||||
outline-offset: -2px;
|
|
||||||
> * {
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
border-color: transparent !important;
|
|
||||||
box-shadow: none !important;
|
|
||||||
background: transparent !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[draggable] {
|
|
||||||
cursor: ns-resize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,223 +0,0 @@
|
|||||||
import { Component, Children, ReactElement } from 'react';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import './sortable.less';
|
|
||||||
|
|
||||||
class Sortable extends Component<{
|
|
||||||
className?: string;
|
|
||||||
itemClassName?: string;
|
|
||||||
onSort?: (sortedIds: Array<string | number>) => void;
|
|
||||||
dragImageSourceHandler?: (elem: Element) => Element;
|
|
||||||
children: ReactElement[];
|
|
||||||
}> {
|
|
||||||
private shell?: HTMLDivElement | null;
|
|
||||||
|
|
||||||
private items?: Array<string | number>;
|
|
||||||
|
|
||||||
private willDetach?: () => void;
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
const box = this.shell!;
|
|
||||||
|
|
||||||
let isDragEnd = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* target node to be dragged
|
|
||||||
*/
|
|
||||||
let source: Element | null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* node to be placed
|
|
||||||
*/
|
|
||||||
let ref: Element | null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* next sibling of the source node
|
|
||||||
*/
|
|
||||||
let origRef: Element | null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* accurately locate the node from event
|
|
||||||
*/
|
|
||||||
const locate = (e: DragEvent) => {
|
|
||||||
let y = e.clientY;
|
|
||||||
if (e.view !== window && e.view!.frameElement) {
|
|
||||||
y += e.view!.frameElement.getBoundingClientRect().top;
|
|
||||||
}
|
|
||||||
let node = box.firstElementChild as HTMLDivElement;
|
|
||||||
while (node) {
|
|
||||||
if (node !== source && node.dataset.id) {
|
|
||||||
const rect = node.getBoundingClientRect();
|
|
||||||
|
|
||||||
if (rect.height <= 0) continue;
|
|
||||||
if (y < rect.top + rect.height / 2) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
node = node.nextElementSibling as HTMLDivElement;
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* find the source node
|
|
||||||
*/
|
|
||||||
const getSource = (e: DragEvent) => {
|
|
||||||
const target = e.target as Element;
|
|
||||||
if (!target || !box.contains(target) || target === box) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
let node = box.firstElementChild;
|
|
||||||
while (node) {
|
|
||||||
if (node.contains(target)) {
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
node = node.nextElementSibling;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
const sort = (beforeId: string | number | null | undefined) => {
|
|
||||||
if (!source) return;
|
|
||||||
|
|
||||||
const sourceId = (source as HTMLDivElement).dataset.id;
|
|
||||||
const items = this.items!;
|
|
||||||
const origIndex = items.findIndex(id => id == sourceId);
|
|
||||||
|
|
||||||
let newIndex = beforeId ? items.findIndex(id => id == beforeId) : items.length;
|
|
||||||
|
|
||||||
if (origIndex < 0 || newIndex < 0) return;
|
|
||||||
if (this.props.onSort) {
|
|
||||||
if (newIndex > origIndex) {
|
|
||||||
newIndex -= 1;
|
|
||||||
}
|
|
||||||
if (origIndex === newIndex) return;
|
|
||||||
const item = items.splice(origIndex, 1);
|
|
||||||
items.splice(newIndex, 0, item[0]);
|
|
||||||
|
|
||||||
this.props.onSort(items);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const dragstart = (e: DragEvent) => {
|
|
||||||
isDragEnd = false;
|
|
||||||
source = getSource(e);
|
|
||||||
if (!source) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
origRef = source.nextElementSibling;
|
|
||||||
const rect = source.getBoundingClientRect();
|
|
||||||
let dragSource = source;
|
|
||||||
if (this.props.dragImageSourceHandler) {
|
|
||||||
dragSource = this.props.dragImageSourceHandler(source);
|
|
||||||
}
|
|
||||||
if (e.dataTransfer) {
|
|
||||||
e.dataTransfer.setDragImage(dragSource, e.clientX - rect.left, e.clientY - rect.top);
|
|
||||||
e.dataTransfer.effectAllowed = 'move';
|
|
||||||
e.dataTransfer.dropEffect = 'move';
|
|
||||||
try {
|
|
||||||
e.dataTransfer.setData('application/json', {} as any);
|
|
||||||
} catch (ex) {
|
|
||||||
// eslint-disable-line
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
source!.classList.add('lc-dragging');
|
|
||||||
}, 0);
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const placeAt = (beforeRef: Element | null) => {
|
|
||||||
if (beforeRef) {
|
|
||||||
if (beforeRef !== source) {
|
|
||||||
box.insertBefore(source!, beforeRef);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
box.appendChild(source!);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const adjust = (e: DragEvent) => {
|
|
||||||
if (isDragEnd) return;
|
|
||||||
ref = locate(e);
|
|
||||||
placeAt(ref);
|
|
||||||
};
|
|
||||||
|
|
||||||
let lastDragEvent: DragEvent | null;
|
|
||||||
const drag = (e: DragEvent) => {
|
|
||||||
if (!source) return;
|
|
||||||
e.preventDefault();
|
|
||||||
if (lastDragEvent) {
|
|
||||||
if (lastDragEvent.clientX === e.clientX && lastDragEvent.clientY === e.clientY) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastDragEvent = e;
|
|
||||||
if (e.dataTransfer) {
|
|
||||||
e.dataTransfer.effectAllowed = 'move';
|
|
||||||
}
|
|
||||||
adjust(e);
|
|
||||||
};
|
|
||||||
|
|
||||||
const dragend = (e: DragEvent) => {
|
|
||||||
isDragEnd = true;
|
|
||||||
if (!source) return;
|
|
||||||
e.preventDefault();
|
|
||||||
source.classList.remove('lc-dragging');
|
|
||||||
placeAt(origRef);
|
|
||||||
sort(ref ? (ref as HTMLDivElement).dataset.id : null);
|
|
||||||
source = null;
|
|
||||||
ref = null;
|
|
||||||
origRef = null;
|
|
||||||
lastDragEvent = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
box.addEventListener('dragstart', dragstart);
|
|
||||||
document.addEventListener('dragover', drag);
|
|
||||||
document.addEventListener('drag', drag);
|
|
||||||
document.addEventListener('dragend', dragend);
|
|
||||||
|
|
||||||
this.willDetach = () => {
|
|
||||||
box.removeEventListener('dragstart', dragstart);
|
|
||||||
document.removeEventListener('dragover', drag);
|
|
||||||
document.removeEventListener('drag', drag);
|
|
||||||
document.removeEventListener('dragend', dragend);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
if (this.willDetach) {
|
|
||||||
this.willDetach();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { className, itemClassName, children } = this.props;
|
|
||||||
const items: Array<string | number> = [];
|
|
||||||
const cards = Children.map(children, child => {
|
|
||||||
const id = child.key!;
|
|
||||||
items.push(id);
|
|
||||||
return (
|
|
||||||
<div key={id} data-id={id} className={classNames('lc-sortable-card', itemClassName)}>
|
|
||||||
{child}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
this.items = items;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={classNames('lc-sortable', className)}
|
|
||||||
ref={ref => {
|
|
||||||
this.shell = ref;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{cards}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Sortable;
|
|
||||||
@ -1,103 +0,0 @@
|
|||||||
.lc-setter-list {
|
|
||||||
[draggable] {
|
|
||||||
cursor: move;
|
|
||||||
}
|
|
||||||
color: var(--color-text);
|
|
||||||
|
|
||||||
.next-btn {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
line-height: 1 !important;
|
|
||||||
max-width: 100%;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lc-setter-list-add {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.lc-setter-list-columns {
|
|
||||||
display: flex;
|
|
||||||
> .lc-title {
|
|
||||||
flex: 1;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
margin-left: 47px;
|
|
||||||
margin-right: 28px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lc-setter-list-scroll-body {
|
|
||||||
margin: -8px -5px;
|
|
||||||
padding: 8px 10px;
|
|
||||||
overflow-y: auto;
|
|
||||||
max-height: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lc-setter-list-card {
|
|
||||||
border: 1px solid rgba(31,56,88,.2);
|
|
||||||
background-color: var(--color-block-background-light);
|
|
||||||
border-radius: 3px;
|
|
||||||
&:not(:last-child) {
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.lc-listitem {
|
|
||||||
position: relative;
|
|
||||||
outline: none;
|
|
||||||
display: flex;
|
|
||||||
align-items: stretch;
|
|
||||||
height: 34px;
|
|
||||||
|
|
||||||
.lc-listitem-actions {
|
|
||||||
margin: 0 3px;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-end;
|
|
||||||
.lc-listitem-action {
|
|
||||||
text-align: center;
|
|
||||||
cursor: pointer;
|
|
||||||
opacity: 0.6;
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.lc-listitem-body {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
align-items: stretch;
|
|
||||||
overflow: hidden;
|
|
||||||
min-width: 0;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
.lc-field {
|
|
||||||
padding: 0 !important;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
>.lc-field-body {
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
> * {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.next-btn {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.lc-listitem-handler {
|
|
||||||
margin-left: 2px;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
.next-icon-ellipsis {
|
|
||||||
transform: rotate(90deg);
|
|
||||||
}
|
|
||||||
opacity: 0.6;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,368 +0,0 @@
|
|||||||
import React, { Component, ComponentClass, ReactNode } from 'react';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { Dropdown, Menu } from '@alifd/next';
|
|
||||||
import {
|
|
||||||
SetterConfig,
|
|
||||||
CustomView,
|
|
||||||
DynamicProps,
|
|
||||||
DynamicSetter,
|
|
||||||
TitleContent,
|
|
||||||
isSetterConfig,
|
|
||||||
isDynamicSetter,
|
|
||||||
} from '@ali/lowcode-types';
|
|
||||||
import {
|
|
||||||
getSetter,
|
|
||||||
getSettersMap,
|
|
||||||
computed,
|
|
||||||
obx,
|
|
||||||
Title,
|
|
||||||
createSetterContent,
|
|
||||||
observer,
|
|
||||||
shallowIntl,
|
|
||||||
} from '@ali/lowcode-editor-core';
|
|
||||||
|
|
||||||
import { IconConvert } from '../../icons/convert';
|
|
||||||
import { intlNode } from '../../locale';
|
|
||||||
|
|
||||||
import './style.less';
|
|
||||||
import { SettingField } from '@ali/lowcode-designer';
|
|
||||||
import { IconVariable } from '../../icons/variable';
|
|
||||||
|
|
||||||
export interface SetterItem {
|
|
||||||
name: string;
|
|
||||||
title: TitleContent;
|
|
||||||
setter: string | DynamicSetter | CustomView;
|
|
||||||
props?: object | DynamicProps;
|
|
||||||
condition?: (field: SettingField) => boolean;
|
|
||||||
initialValue?: any | ((field: SettingField) => any);
|
|
||||||
list: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
function nomalizeSetters(setters?: Array<string | SetterConfig | CustomView | DynamicSetter>): SetterItem[] {
|
|
||||||
if (!setters) {
|
|
||||||
const normalized: SetterItem[] = [];
|
|
||||||
getSettersMap().forEach((setter, name) => {
|
|
||||||
if (name === 'MixedSetter') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
normalized.push({
|
|
||||||
name,
|
|
||||||
title: setter.title || name,
|
|
||||||
setter: name,
|
|
||||||
condition: setter.condition,
|
|
||||||
initialValue: setter.initialValue,
|
|
||||||
list: setter.recommend || false,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return normalized;
|
|
||||||
}
|
|
||||||
const names: string[] = [];
|
|
||||||
function generateName(n: string) {
|
|
||||||
let idx = 1;
|
|
||||||
let got = n;
|
|
||||||
while (names.indexOf(got) > -1) {
|
|
||||||
got = `${n}:${idx++}`;
|
|
||||||
}
|
|
||||||
names.push(got);
|
|
||||||
return got;
|
|
||||||
}
|
|
||||||
return setters.map((setter) => {
|
|
||||||
const config: any = {
|
|
||||||
setter,
|
|
||||||
list: true,
|
|
||||||
};
|
|
||||||
if (isSetterConfig(setter)) {
|
|
||||||
config.setter = setter.componentName;
|
|
||||||
config.props = setter.props;
|
|
||||||
config.condition = setter.condition;
|
|
||||||
config.initialValue = setter.initialValue;
|
|
||||||
config.title = setter.title;
|
|
||||||
}
|
|
||||||
if (typeof config.setter === 'string') {
|
|
||||||
config.name = config.setter;
|
|
||||||
names.push(config.name);
|
|
||||||
const info = getSetter(config.setter);
|
|
||||||
if (!config.title) {
|
|
||||||
config.title = info?.title || config.setter;
|
|
||||||
}
|
|
||||||
if (!config.condition) {
|
|
||||||
config.condition = info?.condition;
|
|
||||||
}
|
|
||||||
if (!config.initialValue) {
|
|
||||||
config.initialValue = info?.initialValue;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
config.name = generateName((config.setter as any)?.displayName || (config.setter as any)?.name || 'CustomSetter');
|
|
||||||
if (!config.title) {
|
|
||||||
config.title = config.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return config;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
interface VariableSetter extends ComponentClass {
|
|
||||||
show(params: object): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
@observer
|
|
||||||
export default class MixedSetter extends Component<{
|
|
||||||
field: SettingField;
|
|
||||||
setters?: Array<string | SetterConfig | CustomView | DynamicSetter>;
|
|
||||||
onSetterChange?: (field: SettingField, name: string) => void;
|
|
||||||
onChange?: (val: any) => void;
|
|
||||||
value?: any;
|
|
||||||
className?: string;
|
|
||||||
}> {
|
|
||||||
private setters = nomalizeSetters(this.props.setters);
|
|
||||||
|
|
||||||
// set name ,used in setting Transducer
|
|
||||||
static displayName = 'MixedSetter';
|
|
||||||
|
|
||||||
@obx.ref private used?: string;
|
|
||||||
|
|
||||||
@computed private getCurrentSetter() {
|
|
||||||
const { field } = this.props;
|
|
||||||
let firstMatched: SetterItem | undefined;
|
|
||||||
let firstDefault: SetterItem | undefined;
|
|
||||||
for (const setter of this.setters) {
|
|
||||||
if (setter.name === this.used) {
|
|
||||||
return setter;
|
|
||||||
}
|
|
||||||
if (!setter.condition) {
|
|
||||||
if (!firstDefault) {
|
|
||||||
firstDefault = setter;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!firstMatched && setter.condition(field)) {
|
|
||||||
firstMatched = setter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return firstMatched || firstDefault || this.setters[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// dirty fix vision variable setter logic
|
|
||||||
private hasVariableSetter = this.setters.some((item) => item.name === 'VariableSetter');
|
|
||||||
|
|
||||||
private useSetter = (name: string) => {
|
|
||||||
const { field } = this.props;
|
|
||||||
if (name === 'VariableSetter') {
|
|
||||||
const setterComponent = getSetter('VariableSetter')?.component as any;
|
|
||||||
if (setterComponent && setterComponent.isPopup) {
|
|
||||||
setterComponent.show({ prop: field });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (name === this.used) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const setter = this.setters.find((item) => item.name === name);
|
|
||||||
// reset hotValue
|
|
||||||
field.setHotValue(undefined);
|
|
||||||
this.used = name;
|
|
||||||
if (setter) {
|
|
||||||
this.handleInitial(setter);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private handleInitial({ initialValue }: SetterItem) {
|
|
||||||
const { field, onChange } = this.props;
|
|
||||||
let newValue: any = initialValue;
|
|
||||||
if (newValue && typeof newValue === 'function') {
|
|
||||||
newValue = newValue(field);
|
|
||||||
}
|
|
||||||
onChange && onChange(newValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
private shell: HTMLDivElement | null = null;
|
|
||||||
|
|
||||||
private checkIsBlockField() {
|
|
||||||
if (this.shell) {
|
|
||||||
const setter = this.shell.firstElementChild;
|
|
||||||
if (setter && setter.classList.contains('lc-block-setter')) {
|
|
||||||
this.shell.classList.add('lc-block-setter');
|
|
||||||
} else {
|
|
||||||
this.shell.classList.remove('lc-block-setter');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate() {
|
|
||||||
this.checkIsBlockField();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.checkIsBlockField();
|
|
||||||
}
|
|
||||||
|
|
||||||
private renderCurrentSetter(currentSetter?: SetterItem, extraProps?: object) {
|
|
||||||
const { className, field, setters, onSetterChange, ...restProps } = this.props;
|
|
||||||
if (!currentSetter) {
|
|
||||||
// TODO: use intl
|
|
||||||
if (restProps.value == null) {
|
|
||||||
return <span>NullValue</span>;
|
|
||||||
} else {
|
|
||||||
return <span>InvalidValue</span>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const { setter, props } = currentSetter;
|
|
||||||
let setterProps: any = {};
|
|
||||||
let setterType: any;
|
|
||||||
let dynamicProps: any = {};
|
|
||||||
if (isDynamicSetter(setter)) {
|
|
||||||
setterType = setter.call(field, field);
|
|
||||||
// { componentName: string; props: object }
|
|
||||||
if (typeof setterType === 'object' && typeof setterType.componentName === 'string') {
|
|
||||||
dynamicProps = setterType.props || {};
|
|
||||||
setterType = setterType.componentName;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setterType = setter;
|
|
||||||
}
|
|
||||||
if (props) {
|
|
||||||
setterProps = props;
|
|
||||||
if (typeof setterProps === 'function') {
|
|
||||||
setterProps = setterProps(field);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return createSetterContent(setterType, {
|
|
||||||
...shallowIntl(setterProps),
|
|
||||||
field,
|
|
||||||
...restProps,
|
|
||||||
...extraProps,
|
|
||||||
...dynamicProps,
|
|
||||||
onInitial: () => {
|
|
||||||
this.handleInitial(currentSetter);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private contentsFromPolyfill(setterComponent: VariableSetter) {
|
|
||||||
const { field } = this.props;
|
|
||||||
|
|
||||||
const n = this.setters.length;
|
|
||||||
|
|
||||||
let setterContent: any;
|
|
||||||
let actions: any;
|
|
||||||
if (n < 3) {
|
|
||||||
const tipContent = field.isUseVariable()
|
|
||||||
? intlNode('Binded: {expr}', { expr: field.getVariableValue() })
|
|
||||||
: intlNode('Variable Binding');
|
|
||||||
if (n === 1) {
|
|
||||||
// =1: 原地展示<当前绑定的值,点击调用 VariableSetter.show>,icon 高亮是否->isUseVaiable,点击 VariableSetter.show
|
|
||||||
setterContent = (
|
|
||||||
<a
|
|
||||||
onClick={() => {
|
|
||||||
setterComponent.show({ prop: field });
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{tipContent}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// =2: 另外一个 Setter 原地展示,icon 高亮,点击弹出调用 VariableSetter.show
|
|
||||||
// FIXME! use variable placeholder setter
|
|
||||||
const otherSetter = this.setters.find((item) => item.name !== 'VariableSetter')!;
|
|
||||||
setterContent = this.renderCurrentSetter(otherSetter, {
|
|
||||||
value: field.getMockOrValue(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
actions = (
|
|
||||||
<Title
|
|
||||||
className={field.isUseVariable() ? 'variable-binded' : ''}
|
|
||||||
title={{
|
|
||||||
icon: <IconVariable size={24} />,
|
|
||||||
tip: tipContent,
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
setterComponent.show({ prop: field });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// >=3: 原地展示当前 setter<当前绑定的值,点击调用 VariableSetter.show>,icon tip 提示绑定的值,点击展示切换 Setter,点击其它 setter 直接切换,点击 Variable Setter-> VariableSetter.show
|
|
||||||
const currentSetter = field.isUseVariable()
|
|
||||||
? this.setters.find((item) => item.name === 'VariableSetter')
|
|
||||||
: this.getCurrentSetter();
|
|
||||||
if (currentSetter?.name === 'VariableSetter') {
|
|
||||||
setterContent = (
|
|
||||||
<a
|
|
||||||
onClick={() => {
|
|
||||||
setterComponent.show({ prop: field });
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{intlNode('Binded: {expr}', { expr: field.getVariableValue() })}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
setterContent = this.renderCurrentSetter(currentSetter);
|
|
||||||
}
|
|
||||||
actions = this.renderSwitchAction(currentSetter);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
setterContent,
|
|
||||||
actions,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private renderSwitchAction(currentSetter?: SetterItem) {
|
|
||||||
const usedName = currentSetter?.name || this.used;
|
|
||||||
const triggerNode = (
|
|
||||||
<Title
|
|
||||||
title={{
|
|
||||||
tip: intlNode('Switch Setter'),
|
|
||||||
// FIXME: got a beautiful icon
|
|
||||||
icon: <IconConvert size={24} />,
|
|
||||||
}}
|
|
||||||
className="lc-switch-trigger"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<Dropdown trigger={triggerNode} triggerType="click" align="tr br">
|
|
||||||
<Menu selectMode="single" hasSelectedIcon selectedKeys={usedName} onItemClick={this.useSetter}>
|
|
||||||
{this.setters
|
|
||||||
.filter((setter) => setter.list || setter.name === usedName)
|
|
||||||
.map((setter) => {
|
|
||||||
return (
|
|
||||||
<Menu.Item key={setter.name}>
|
|
||||||
<Title title={setter.title} />
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Menu>
|
|
||||||
</Dropdown>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { className } = this.props;
|
|
||||||
let contents: {
|
|
||||||
setterContent: ReactNode,
|
|
||||||
actions: ReactNode,
|
|
||||||
} | undefined;
|
|
||||||
if (this.hasVariableSetter) {
|
|
||||||
// polyfill vision variable setter logic
|
|
||||||
const setterComponent = getSetter('VariableSetter')?.component as any;
|
|
||||||
if (setterComponent && setterComponent.isPopup) {
|
|
||||||
contents = this.contentsFromPolyfill(setterComponent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!contents) {
|
|
||||||
const currentSetter = this.getCurrentSetter();
|
|
||||||
contents = {
|
|
||||||
setterContent: this.renderCurrentSetter(currentSetter),
|
|
||||||
actions: this.renderSwitchAction(currentSetter),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div ref={(shell) => { this.shell = shell; }} className={classNames('lc-setter-mixed', className)}>
|
|
||||||
{contents.setterContent}
|
|
||||||
<div className="lc-setter-actions">{contents.actions}</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,70 +0,0 @@
|
|||||||
.lc-setter-mixed {
|
|
||||||
min-width: 0;
|
|
||||||
margin-right: 26px;
|
|
||||||
display: block;
|
|
||||||
position: relative;
|
|
||||||
>.lc-setter-actions {
|
|
||||||
position: absolute;
|
|
||||||
right: -4px;
|
|
||||||
top: 50%;
|
|
||||||
transform: translate(100%, -50%);
|
|
||||||
.lc-switch-trigger {
|
|
||||||
cursor: pointer;
|
|
||||||
opacity: 0.6;
|
|
||||||
&:hover {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.lc-title {
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.lc-title-icon svg {
|
|
||||||
fill: #8f9bb3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.variable-binded {
|
|
||||||
background: var(--color-brand, #006cff);
|
|
||||||
color: #fff!important;
|
|
||||||
border-radius: 3px;
|
|
||||||
|
|
||||||
.lc-title-icon svg {
|
|
||||||
fill: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.next-input,.next-date-picker,.next-month-picker {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
&.lc-block-setter {
|
|
||||||
position: static;
|
|
||||||
margin-right: 0;
|
|
||||||
>.lc-setter-actions {
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.lc-block-field > .lc-field-body > .lc-setter-mixed {
|
|
||||||
position: static;
|
|
||||||
margin-right: 0;
|
|
||||||
>.lc-setter-actions {
|
|
||||||
position: absolute;
|
|
||||||
right: 12px;
|
|
||||||
top: 0;
|
|
||||||
height: 32px;
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.lc-block-field > .lc-field-body > .lc-setter-mixed{
|
|
||||||
|
|
||||||
}
|
|
||||||
.lc-accordion-field > .lc-field-body > .lc-setter-mixed {
|
|
||||||
position: static;
|
|
||||||
margin-right: 0;
|
|
||||||
> .lc-setter-actions {
|
|
||||||
right: 36px;
|
|
||||||
top: 4px;
|
|
||||||
transform: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,203 +0,0 @@
|
|||||||
import { Component, Fragment } from 'react';
|
|
||||||
import { Icon, Button } from '@alifd/next';
|
|
||||||
import { SetterType, FieldConfig, CustomView } from '@ali/lowcode-types';
|
|
||||||
import { createSettingFieldView } from '../settings/settings-pane';
|
|
||||||
import { PopupContext, PopupPipe } from '../popup';
|
|
||||||
import { isSettingField, SettingField } from '@ali/lowcode-designer';
|
|
||||||
import './style.less';
|
|
||||||
import { Title } from '@ali/lowcode-editor-core';
|
|
||||||
|
|
||||||
export default class ObjectSetter extends Component<{
|
|
||||||
field: SettingField;
|
|
||||||
descriptor?: string | ((rowField: SettingField) => string);
|
|
||||||
config: ObjectSetterConfig;
|
|
||||||
mode?: 'popup' | 'form';
|
|
||||||
// 1: in tablerow 2: in listrow 3: in column-cell
|
|
||||||
forceInline?: number;
|
|
||||||
}> {
|
|
||||||
render() {
|
|
||||||
const { mode, forceInline = 0, ...props } = this.props;
|
|
||||||
if (forceInline || mode === 'popup') {
|
|
||||||
if (forceInline > 2 || mode === 'popup') {
|
|
||||||
// popup
|
|
||||||
return <RowSetter {...props} primaryButton={!forceInline} />;
|
|
||||||
} else {
|
|
||||||
return <RowSetter columns={forceInline > 1 ? 2 : 4} {...props} />;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// form
|
|
||||||
return <FormSetter {...props} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ObjectSetterConfig {
|
|
||||||
items?: FieldConfig[];
|
|
||||||
extraSetter?: SetterType;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RowSetterProps {
|
|
||||||
field: SettingField;
|
|
||||||
descriptor?: string | ((rowField: SettingField) => string);
|
|
||||||
config: ObjectSetterConfig;
|
|
||||||
columns?: number;
|
|
||||||
primaryButton?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
class RowSetter extends Component<RowSetterProps> {
|
|
||||||
static contextType = PopupContext;
|
|
||||||
|
|
||||||
state: any = {
|
|
||||||
descriptor: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
private items?: SettingField[];
|
|
||||||
|
|
||||||
constructor(props: RowSetterProps) {
|
|
||||||
super(props);
|
|
||||||
const { config, descriptor, field, columns } = props;
|
|
||||||
const items: SettingField[] = [];
|
|
||||||
if (columns && config?.items) {
|
|
||||||
const l = Math.min(config.items.length, columns);
|
|
||||||
for (let i = 0; i < l; i++) {
|
|
||||||
const conf = config.items[i];
|
|
||||||
if (conf.isRequired || conf.important || (conf.setter as any)?.isRequired) {
|
|
||||||
const item = field.createField({
|
|
||||||
...conf,
|
|
||||||
// in column-cell
|
|
||||||
forceInline: 3,
|
|
||||||
});
|
|
||||||
items.push(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (items.length > 0) {
|
|
||||||
this.items = items;
|
|
||||||
}
|
|
||||||
|
|
||||||
let firstRun = true;
|
|
||||||
field.onEffect(() => {
|
|
||||||
const state: any = {};
|
|
||||||
if (descriptor) {
|
|
||||||
if (typeof descriptor === 'function') {
|
|
||||||
state.descriptor = descriptor(field);
|
|
||||||
} else {
|
|
||||||
state.descriptor = field.getPropValue(descriptor);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
state.descriptor = field.title;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (firstRun) {
|
|
||||||
firstRun = false;
|
|
||||||
this.state = state;
|
|
||||||
} else {
|
|
||||||
this.setState(state);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldComponentUpdate(_: any, nextState: any) {
|
|
||||||
if (this.state.decriptor !== nextState.decriptor) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private pipe: any;
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { items } = this;
|
|
||||||
const { field, primaryButton, config } = this.props;
|
|
||||||
|
|
||||||
if (!this.pipe) {
|
|
||||||
this.pipe = (this.context as PopupPipe).create({ width: 320 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const title = (
|
|
||||||
<Fragment>
|
|
||||||
编辑:
|
|
||||||
<Title title={this.state.descriptor} />
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
|
|
||||||
this.pipe.send(<FormSetter key={field.id} field={field} config={config} />, title);
|
|
||||||
|
|
||||||
if (items) {
|
|
||||||
return (
|
|
||||||
<div className="lc-setter-object-row">
|
|
||||||
<div
|
|
||||||
className="lc-setter-object-row-edit"
|
|
||||||
onClick={(e) => {
|
|
||||||
this.pipe.show((e as any).target, field.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon size="small" type="edit" />
|
|
||||||
</div>
|
|
||||||
<div className="lc-setter-object-row-body">{items.map((item) => createSettingFieldView(item, field))}</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Button
|
|
||||||
type={primaryButton === false ? 'normal' : 'primary'}
|
|
||||||
onClick={(e) => {
|
|
||||||
this.pipe.show((e as any).target, field.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon type="edit" />
|
|
||||||
{title}
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FormSetterProps {
|
|
||||||
field: SettingField;
|
|
||||||
config: ObjectSetterConfig;
|
|
||||||
}
|
|
||||||
class FormSetter extends Component<FormSetterProps> {
|
|
||||||
private items: (SettingField | CustomView)[];
|
|
||||||
|
|
||||||
constructor(props: RowSetterProps) {
|
|
||||||
super(props);
|
|
||||||
const { config, field } = props;
|
|
||||||
const { extraProps } = field;
|
|
||||||
|
|
||||||
if (Array.isArray(field.items) && field.items.length > 0) {
|
|
||||||
field.items.forEach((item: SettingField | CustomView) => {
|
|
||||||
if (isSettingField(item)) {
|
|
||||||
const originalSetValue = item.extraProps.setValue;
|
|
||||||
item.extraProps.setValue = (...args) => {
|
|
||||||
// 调用子字段本身的 setValue
|
|
||||||
originalSetValue?.apply(null, args);
|
|
||||||
// 调用父字段本身的 setValue
|
|
||||||
extraProps.setValue?.apply(null, args);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.items = field.items;
|
|
||||||
} else {
|
|
||||||
this.items = (config?.items || []).map((conf) => field.createField({
|
|
||||||
...conf,
|
|
||||||
setValue: extraProps?.setValue,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
// TODO: extraConfig for custom fields
|
|
||||||
}
|
|
||||||
|
|
||||||
shouldComponentUpdate() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { field } = this.props;
|
|
||||||
return (
|
|
||||||
<div className="lc-setter-object lc-block-setter">
|
|
||||||
{this.items.map((item, index) => createSettingFieldView(item, field, index))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
.lc-setter-object-row {
|
|
||||||
display: flex;
|
|
||||||
align-items: stretch;
|
|
||||||
width: 100%;
|
|
||||||
.lc-setter-object-row-edit {
|
|
||||||
width: 20px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
.lc-setter-object-row-body {
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
align-items: center;
|
|
||||||
.lc-field {
|
|
||||||
padding: 0 !important;
|
|
||||||
.lc-field-body {
|
|
||||||
padding: 0 !important; margin: 0 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
> * {
|
|
||||||
flex: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
margin-left: 2px;
|
|
||||||
min-width: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
.lc-setter-slot {
|
|
||||||
display: flex;
|
|
||||||
align-items: start;
|
|
||||||
.lc-slot-params {
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.lc-setter-slot-column {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
@ -1,100 +0,0 @@
|
|||||||
import { Component } from 'react';
|
|
||||||
import { isJSSlot } from '@ali/lowcode-types';
|
|
||||||
import { Button, Input, Icon } from '@alifd/next';
|
|
||||||
import './index.less';
|
|
||||||
|
|
||||||
export default class SlotSetter extends Component<{
|
|
||||||
value: any;
|
|
||||||
onChange?: (value: any) => void;
|
|
||||||
onInitial?: () => void;
|
|
||||||
// 是否支持设置入参
|
|
||||||
supportParams?: boolean;
|
|
||||||
}> {
|
|
||||||
private handleInitial = () => {
|
|
||||||
const { onChange, onInitial } = this.props;
|
|
||||||
if (onInitial) {
|
|
||||||
onInitial();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!onChange) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onChange({
|
|
||||||
type: 'JSSlot',
|
|
||||||
value: null,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const { value, onChange, supportParams } = this.props;
|
|
||||||
if (!isJSSlot(value)) {
|
|
||||||
return (
|
|
||||||
<Button type="primary" onClick={this.handleInitial}>
|
|
||||||
开启插槽
|
|
||||||
</Button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const hasParams = value.params && Array.isArray(value.params);
|
|
||||||
return (
|
|
||||||
<div className="lc-setter-slot lc-setter-slot-column">
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
// TODO: use slot first child literal value pad
|
|
||||||
onChange && onChange(null);
|
|
||||||
}}
|
|
||||||
type="secondary"
|
|
||||||
>
|
|
||||||
关闭插槽
|
|
||||||
</Button>
|
|
||||||
{hasParams ? (
|
|
||||||
<Input
|
|
||||||
className="lc-slot-params"
|
|
||||||
addonTextBefore="入参"
|
|
||||||
placeholder="插槽入参,以逗号风格"
|
|
||||||
value={value.params!.join(',')}
|
|
||||||
autoFocus
|
|
||||||
onChange={(val) => {
|
|
||||||
val = val.trim();
|
|
||||||
const params = val ? val.split(/ *, */) : [];
|
|
||||||
onChange &&
|
|
||||||
onChange({
|
|
||||||
...value,
|
|
||||||
params: params.length == 0 ? [''] : params,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
addonAfter={
|
|
||||||
<Button
|
|
||||||
type="secondary"
|
|
||||||
onClick={() => {
|
|
||||||
onChange &&
|
|
||||||
onChange({
|
|
||||||
...value,
|
|
||||||
params: [''],
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Icon type="close" />
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
) : supportParams ? (
|
|
||||||
<Button
|
|
||||||
className="lc-slot-params"
|
|
||||||
type="primary"
|
|
||||||
onClick={() => {
|
|
||||||
onChange &&
|
|
||||||
onChange({
|
|
||||||
...value,
|
|
||||||
params: [],
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
添加入参
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,62 +1,9 @@
|
|||||||
import { registerSetter } from '@ali/lowcode-editor-core';
|
|
||||||
import { registerMetadataTransducer } from '@ali/lowcode-designer';
|
import { registerMetadataTransducer } from '@ali/lowcode-designer';
|
||||||
import ArraySetter from './components/array-setter';
|
|
||||||
import ObjectSetter from './components/object-setter';
|
|
||||||
import MixedSetter from './components/mixed-setter';
|
|
||||||
import { isPlainObject } from '@ali/lowcode-utils';
|
|
||||||
import parseJSFunc from './transducers/parse-func';
|
import parseJSFunc from './transducers/parse-func';
|
||||||
import parseProps from './transducers/parse-props';
|
import parseProps from './transducers/parse-props';
|
||||||
import addonCombine from './transducers/addon-combine';
|
import addonCombine from './transducers/addon-combine';
|
||||||
import SlotSetter from './components/slot-setter';
|
|
||||||
import { isJSSlot } from '@ali/lowcode-types';
|
|
||||||
|
|
||||||
export const registerDefaults = () => {
|
export const registerDefaults = () => {
|
||||||
registerSetter('ArraySetter', {
|
|
||||||
component: ArraySetter,
|
|
||||||
defaultProps: {},
|
|
||||||
title: 'ArraySetter', // TODO
|
|
||||||
condition: (field: any) => {
|
|
||||||
const v = field.getValue();
|
|
||||||
return v == null || Array.isArray(v);
|
|
||||||
},
|
|
||||||
initialValue: [],
|
|
||||||
recommend: true,
|
|
||||||
});
|
|
||||||
registerSetter('ObjectSetter', {
|
|
||||||
component: ObjectSetter,
|
|
||||||
// todo: defaultProps
|
|
||||||
defaultProps: {},
|
|
||||||
title: 'ObjectSetter', // TODO
|
|
||||||
condition: (field: any) => {
|
|
||||||
const v = field.getValue();
|
|
||||||
return v == null || isPlainObject(v);
|
|
||||||
},
|
|
||||||
initialValue: {},
|
|
||||||
recommend: true,
|
|
||||||
});
|
|
||||||
registerSetter('SlotSetter', {
|
|
||||||
component: SlotSetter,
|
|
||||||
title: {
|
|
||||||
type: 'i18n',
|
|
||||||
'zh-CN': '插槽输入',
|
|
||||||
'en-US': 'Slot Setter',
|
|
||||||
},
|
|
||||||
condition: (field: any) => {
|
|
||||||
return isJSSlot(field.getValue());
|
|
||||||
},
|
|
||||||
initialValue: (field: any, value: any) => {
|
|
||||||
if (isJSSlot(value)) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
type: 'JSSlot',
|
|
||||||
value,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
recommend: true,
|
|
||||||
});
|
|
||||||
registerSetter('MixedSetter', MixedSetter);
|
|
||||||
|
|
||||||
// parseFunc
|
// parseFunc
|
||||||
registerMetadataTransducer(parseJSFunc, 1, 'parse-func');
|
registerMetadataTransducer(parseJSFunc, 1, 'parse-func');
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user