mirror of
https://github.com/alibaba/lowcode-engine.git
synced 2026-01-20 07:14:23 +00:00
feat: show value state
This commit is contained in:
parent
f75a51e77e
commit
bd49e50295
@ -8,7 +8,7 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
background-color: rgba(0, 0, 0, 0.4);
|
background-color: rgba(0, 0, 0, 0.4);
|
||||||
opacity: 0.5;
|
//opacity: 0.9;
|
||||||
box-shadow: 0 0 6px grey;
|
box-shadow: 0 0 6px grey;
|
||||||
transform: translate(-10%, -50%);
|
transform: translate(-10%, -50%);
|
||||||
.lc-ghost {
|
.lc-ghost {
|
||||||
|
|||||||
@ -84,35 +84,6 @@ export class SettingField extends SettingPropEntry implements SettingEntry {
|
|||||||
return new SettingField(this, config);
|
return new SettingField(this, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====== 当前属性读写 =====
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 判断当前属性值是否一致
|
|
||||||
* 0 无值/多种值
|
|
||||||
* 1 类似值,比如数组长度一样
|
|
||||||
* 2 单一植
|
|
||||||
*/
|
|
||||||
get valueState(): number {
|
|
||||||
if (this.type !== 'field') {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
const propName = this.path.join('.');
|
|
||||||
const first = this.nodes[0].getProp(propName)!;
|
|
||||||
let l = this.nodes.length;
|
|
||||||
let state = 2;
|
|
||||||
while (l-- > 1) {
|
|
||||||
const next = this.nodes[l].getProp(propName, false);
|
|
||||||
const s = first.compare(next);
|
|
||||||
if (s > 1) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (s === 1) {
|
|
||||||
state = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
purge() {
|
purge() {
|
||||||
this.disposeItems();
|
this.disposeItems();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -97,6 +97,38 @@ export class SettingPropEntry implements SettingEntry {
|
|||||||
|
|
||||||
// ====== 当前属性读写 =====
|
// ====== 当前属性读写 =====
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断当前属性值是否一致
|
||||||
|
* -1 多种值
|
||||||
|
* 0 无值
|
||||||
|
* 1 类似值,比如数组长度一样
|
||||||
|
* 2 单一植
|
||||||
|
*/
|
||||||
|
@computed get valueState(): number {
|
||||||
|
if (this.type !== 'field') {
|
||||||
|
const { getValue } = this.extraProps;
|
||||||
|
return getValue ? (getValue(this, undefined) === undefined ? 0 : 1) : 0;
|
||||||
|
}
|
||||||
|
const propName = this.path.join('.');
|
||||||
|
const first = this.nodes[0].getProp(propName)!;
|
||||||
|
let l = this.nodes.length;
|
||||||
|
let state = 2;
|
||||||
|
while (l-- > 1) {
|
||||||
|
const next = this.nodes[l].getProp(propName, false);
|
||||||
|
const s = first.compare(next);
|
||||||
|
if (s > 1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (s === 1) {
|
||||||
|
state = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state === 2 && first.isUnset()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取当前属性值
|
* 获取当前属性值
|
||||||
*/
|
*/
|
||||||
@ -122,6 +154,19 @@ export class SettingPropEntry implements SettingEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除已设置的值
|
||||||
|
*/
|
||||||
|
clearValue() {
|
||||||
|
if (this.type === 'field') {
|
||||||
|
this.parent.clearPropValue(this.name);
|
||||||
|
}
|
||||||
|
const { setValue } = this.extraProps;
|
||||||
|
if (setValue) {
|
||||||
|
setValue(this, undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取子项
|
* 获取子项
|
||||||
*/
|
*/
|
||||||
@ -138,6 +183,14 @@ export class SettingPropEntry implements SettingEntry {
|
|||||||
this.top.setPropValue(path, value);
|
this.top.setPropValue(path, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除已设置值
|
||||||
|
*/
|
||||||
|
clearPropValue(propName: string | number) {
|
||||||
|
const path = this.path.concat(propName).join('.');
|
||||||
|
this.top.clearPropValue(path);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取子级属性值
|
* 获取子级属性值
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -136,6 +136,15 @@ export class SettingTopEntry implements SettingEntry {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除已设置值
|
||||||
|
*/
|
||||||
|
clearPropValue(propName: string) {
|
||||||
|
this.nodes.forEach((node) => {
|
||||||
|
node.clearPropValue(propName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取子级属性值
|
* 获取子级属性值
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -395,6 +395,13 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
|
|||||||
this.getProp(path, true)!.setValue(value);
|
this.getProp(path, true)!.setValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除已设置的值
|
||||||
|
*/
|
||||||
|
clearPropValue(path: string): void {
|
||||||
|
this.getProp(path, false)?.unset();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置多个属性值,和原有值合并
|
* 设置多个属性值,和原有值合并
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -245,7 +245,9 @@ export class Prop implements IPropParent {
|
|||||||
return typeof this.key === 'string' && this.key.charAt(0) === '!';
|
return typeof this.key === 'string' && this.key.charAt(0) === '!';
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: improve this logic
|
/**
|
||||||
|
* @returns 0: the same 1: maybe & like 2: not the same
|
||||||
|
*/
|
||||||
compare(other: Prop | null): number {
|
compare(other: Prop | null): number {
|
||||||
if (!other || other.isUnset()) {
|
if (!other || other.isUnset()) {
|
||||||
return this.isUnset() ? 0 : 2;
|
return this.isUnset() ? 0 : 2;
|
||||||
|
|||||||
@ -1,17 +1,21 @@
|
|||||||
import { Component } from 'react';
|
import { Component } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Icon } from '@alifd/next';
|
import { Icon } from '@alifd/next';
|
||||||
import { Title } from '@ali/lowcode-editor-core';
|
import { Title, Tip } from '@ali/lowcode-editor-core';
|
||||||
import { TitleContent } from '@ali/lowcode-types';
|
import { TitleContent } from '@ali/lowcode-types';
|
||||||
import { PopupPipe, PopupContext } from '../popup';
|
import { PopupPipe, PopupContext } from '../popup';
|
||||||
|
import { intl, intlNode } from '../../locale';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
import { IconClear } from 'editor-skeleton/src/icons/clear';
|
||||||
|
|
||||||
export interface FieldProps {
|
export interface FieldProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
title?: TitleContent | null;
|
title?: TitleContent | null;
|
||||||
defaultDisplay?: 'accordion' | 'inline' | 'block';
|
defaultDisplay?: 'accordion' | 'inline' | 'block';
|
||||||
collapsed?: boolean;
|
collapsed?: boolean;
|
||||||
|
valueState?: number;
|
||||||
onExpandChange?: (expandState: boolean) => void;
|
onExpandChange?: (expandState: boolean) => void;
|
||||||
|
onClear?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Field extends Component<FieldProps> {
|
export class Field extends Component<FieldProps> {
|
||||||
@ -73,9 +77,10 @@ export class Field extends Component<FieldProps> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { className, children, title } = this.props;
|
const { className, children, title, valueState, onClear } = this.props;
|
||||||
const { display, collapsed } = this.state;
|
const { display, collapsed } = this.state;
|
||||||
const isAccordion = display === 'accordion';
|
const isAccordion = display === 'accordion';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classNames(`lc-field lc-${display}-field`, className, {
|
className={classNames(`lc-field lc-${display}-field`, className, {
|
||||||
@ -84,6 +89,7 @@ export class Field extends Component<FieldProps> {
|
|||||||
>
|
>
|
||||||
<div className="lc-field-head" onClick={isAccordion ? this.toggleExpand : undefined}>
|
<div className="lc-field-head" onClick={isAccordion ? this.toggleExpand : undefined}>
|
||||||
<div className="lc-field-title">
|
<div className="lc-field-title">
|
||||||
|
{createValueState(valueState, onClear)}
|
||||||
<Title title={title || ''} />
|
<Title title={title || ''} />
|
||||||
</div>
|
</div>
|
||||||
{isAccordion && <Icon className="lc-field-icon" type="arrow-up" size="xs" />}
|
{isAccordion && <Icon className="lc-field-icon" type="arrow-up" size="xs" />}
|
||||||
@ -96,6 +102,49 @@ export class Field extends Component<FieldProps> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* **交互专利点**
|
||||||
|
*
|
||||||
|
* -1 多种值
|
||||||
|
* 0 | null 无值
|
||||||
|
* 1 类似值,比如数组长度一样
|
||||||
|
* 2 单一植
|
||||||
|
* 10 必填
|
||||||
|
*
|
||||||
|
* TODO: turn number to enum
|
||||||
|
*/
|
||||||
|
function createValueState(valueState?: number, onClear?: () => void) {
|
||||||
|
let tip: any = null;
|
||||||
|
let className: string = 'lc-valuestate';
|
||||||
|
let icon: any = null;
|
||||||
|
if (valueState) {
|
||||||
|
if (valueState < 0) {
|
||||||
|
// multiple value 橘黄色点: tip:多种值,点击清除
|
||||||
|
tip = intlNode('Multiple Value, Click to Clear');
|
||||||
|
className += ' valuestate-multiple';
|
||||||
|
icon = <IconClear size={6} />;
|
||||||
|
} else if (valueState === 10) {
|
||||||
|
// isset orangered tip: 必填项
|
||||||
|
tip = intlNode('Required');
|
||||||
|
className += ' valuestate-required';
|
||||||
|
onClear = undefined;
|
||||||
|
} else if (valueState > 0) {
|
||||||
|
// isset 蓝点 tip: 已设置值,点击清除
|
||||||
|
tip = intlNode('Setted Value, Click to Clear');
|
||||||
|
className += ' valuestate-isset';
|
||||||
|
icon = <IconClear size={6} />;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onClear = undefined;
|
||||||
|
// unset 占位空间
|
||||||
|
}
|
||||||
|
|
||||||
|
return <i className={className} onClick={onClear}>
|
||||||
|
{icon}
|
||||||
|
{tip && <Tip>{tip}</Tip>}
|
||||||
|
</i>;
|
||||||
|
}
|
||||||
|
|
||||||
export interface PopupFieldProps extends FieldProps {
|
export interface PopupFieldProps extends FieldProps {
|
||||||
width?: number;
|
width?: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,54 @@
|
|||||||
.lc-field-title {
|
.lc-field-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
.lc-valuestate {
|
||||||
|
height: 6px;
|
||||||
|
width: 6px;
|
||||||
|
min-width: 6px;
|
||||||
|
border-radius: 100%;
|
||||||
|
margin-right: 2px;
|
||||||
|
pointer-events: none;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: white;
|
||||||
|
> svg {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
&.valuestate-multiple {
|
||||||
|
background-color: rgb(232, 145, 83);
|
||||||
|
pointer-events: auto;
|
||||||
|
&:hover {
|
||||||
|
background-color: rgb(223, 139, 30);
|
||||||
|
cursor: pointer;
|
||||||
|
transform: scale(2);
|
||||||
|
transform-origin: center;
|
||||||
|
> svg {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.valuestate-isset {
|
||||||
|
background-color: rgba(124, 177, 238, 0.6);
|
||||||
|
pointer-events: auto;
|
||||||
|
&:hover {
|
||||||
|
background-color: rgb(45, 126, 219);
|
||||||
|
cursor: pointer;
|
||||||
|
transform: scale(2);
|
||||||
|
transform-origin: center;
|
||||||
|
> svg {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.valuestate-required {
|
||||||
|
background-color: rgb(250, 82, 76);
|
||||||
|
pointer-events: auto;
|
||||||
|
&:hover {
|
||||||
|
background-color: rgb(224, 46, 40);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.lc-field-icon {
|
.lc-field-icon {
|
||||||
// margin-right: @x-gap;
|
// margin-right: @x-gap;
|
||||||
@ -75,7 +123,7 @@
|
|||||||
// background: var(--color-block-background-shallow, rgba(31,56,88,.06));
|
// background: var(--color-block-background-shallow, rgba(31,56,88,.06));
|
||||||
// border-bottom: 1px solid var(--color-line-normal);
|
// border-bottom: 1px solid var(--color-line-normal);
|
||||||
// color: var(--color-title);
|
// color: var(--color-title);
|
||||||
padding: 0 16px;
|
padding: 0 16px 0 10px;
|
||||||
background-color: #F7F9FC;
|
background-color: #F7F9FC;
|
||||||
color: #8F9BB3;
|
color: #8F9BB3;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|||||||
@ -8,7 +8,9 @@ export interface FieldProps {
|
|||||||
title?: TitleContent | null;
|
title?: TitleContent | null;
|
||||||
display?: 'accordion' | 'inline' | 'block' | 'plain' | 'popup' | 'entry';
|
display?: 'accordion' | 'inline' | 'block' | 'plain' | 'popup' | 'entry';
|
||||||
collapsed?: boolean;
|
collapsed?: boolean;
|
||||||
|
valueState?: number;
|
||||||
onExpandChange?: (collapsed: boolean) => void;
|
onExpandChange?: (collapsed: boolean) => void;
|
||||||
|
onClear?: () => void;
|
||||||
[extra: string]: any;
|
[extra: string]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import { Component } from 'react';
|
import { Component } from 'react';
|
||||||
import { intl, shallowIntl, createSetterContent, observer } from '@ali/lowcode-editor-core';
|
import { shallowIntl, createSetterContent, observer } from '@ali/lowcode-editor-core';
|
||||||
import { createContent } from '@ali/lowcode-utils';
|
import { createContent } from '@ali/lowcode-utils';
|
||||||
import { Field, createField } from '../field';
|
import { Field, createField } from '../field';
|
||||||
import PopupService from '../popup';
|
import PopupService from '../popup';
|
||||||
import { SettingField, isSettingField, SettingTopEntry, SettingEntry } from '@ali/lowcode-designer';
|
import { SettingField, isSettingField, SettingTopEntry, SettingEntry } from '@ali/lowcode-designer';
|
||||||
import { isSetterConfig, CustomView } from '@ali/lowcode-types';
|
import { isSetterConfig, CustomView } from '@ali/lowcode-types';
|
||||||
|
import { intl } from '../../locale';
|
||||||
|
|
||||||
@observer
|
@observer
|
||||||
class SettingFieldView extends Component<{ field: SettingField }> {
|
class SettingFieldView extends Component<{ field: SettingField }> {
|
||||||
@ -41,26 +42,19 @@ class SettingFieldView extends Component<{ field: SettingField }> {
|
|||||||
setterType = setter;
|
setterType = setter;
|
||||||
}
|
}
|
||||||
let value = null;
|
let value = null;
|
||||||
if (field.type === 'field') {
|
|
||||||
if (defaultValue != null && !('defaultValue' in setterProps)) {
|
if (defaultValue != null && !('defaultValue' in setterProps)) {
|
||||||
setterProps.defaultValue = defaultValue;
|
setterProps.defaultValue = defaultValue;
|
||||||
if (initialValue == null) {
|
if (initialValue == null) {
|
||||||
initialValue = defaultValue;
|
initialValue = defaultValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (field.valueState > 0) {
|
if (field.valueState === -1) {
|
||||||
value = field.getValue();
|
|
||||||
} else {
|
|
||||||
setterProps.multiValue = true;
|
setterProps.multiValue = true;
|
||||||
if (!('placeholder' in setterProps)) {
|
if (!('placeholder' in setterProps)) {
|
||||||
// FIXME! move to locale file
|
setterProps.placeholder = intl('Multiple Value');
|
||||||
setterProps.placeholder = intl({
|
|
||||||
type: 'i18n',
|
|
||||||
'zh-CN': '多种值',
|
|
||||||
'en-US': 'Multiple Value',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
value = field.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: error handling
|
// todo: error handling
|
||||||
@ -69,7 +63,9 @@ class SettingFieldView extends Component<{ field: SettingField }> {
|
|||||||
{
|
{
|
||||||
title: field.title,
|
title: field.title,
|
||||||
collapsed: !field.expanded,
|
collapsed: !field.expanded,
|
||||||
|
valueState: field.isRequired ? 10 : field.valueState,
|
||||||
onExpandChange: (expandState) => field.setExpanded(expandState),
|
onExpandChange: (expandState) => field.setExpanded(expandState),
|
||||||
|
onClear: () => field.clearValue(),
|
||||||
},
|
},
|
||||||
createSetterContent(setterType, {
|
createSetterContent(setterType, {
|
||||||
...shallowIntl(setterProps),
|
...shallowIntl(setterProps),
|
||||||
|
|||||||
11
packages/editor-skeleton/src/icons/clear.tsx
Normal file
11
packages/editor-skeleton/src/icons/clear.tsx
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { SVGIcon, IconProps } from "@ali/lowcode-utils";
|
||||||
|
|
||||||
|
export function IconClear(props: IconProps) {
|
||||||
|
return (
|
||||||
|
<SVGIcon viewBox="0 0 1024 1024" {...props}>
|
||||||
|
<path d="M761.6 701.44a21.333333 21.333333 0 0 1 0 30.293333l-29.866667 29.866667a21.333333 21.333333 0 0 1-30.293333 0L512 572.16l-189.44 189.44a21.333333 21.333333 0 0 1-30.293333 0l-29.866667-29.866667a21.333333 21.333333 0 0 1 0-30.293333L451.84 512 262.4 322.56a21.333333 21.333333 0 0 1 0-30.293333l29.866667-29.866667a21.333333 21.333333 0 0 1 30.293333 0L512 451.84l189.44-189.44a21.333333 21.333333 0 0 1 30.293333 0l29.866667 29.866667a21.333333 21.333333 0 0 1 0 30.293333L572.16 512z" />
|
||||||
|
</SVGIcon>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
IconClear.displayName = 'Clear';
|
||||||
@ -1,5 +1,9 @@
|
|||||||
{
|
{
|
||||||
"Binded: {expr}": "Binded: {expr}",
|
"Binded: {expr}": "Binded: {expr}",
|
||||||
"Variable Binding": "Variable Binding",
|
"Variable Binding": "Variable Binding",
|
||||||
"Switch Setter": "Switch Setter"
|
"Switch Setter": "Switch Setter",
|
||||||
|
"Multiple Value, Click to Clear": "Multiple Value, Click to Clear",
|
||||||
|
"Required": "Required",
|
||||||
|
"Setted Value, Click to Clear": "Setted Value, Click to Clear",
|
||||||
|
"Multiple Value": "Multiple Value"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
{
|
{
|
||||||
"Binded: {expr}": "已绑定: {expr}",
|
"Binded: {expr}": "已绑定: {expr}",
|
||||||
"Variable Binding": "变量绑定",
|
"Variable Binding": "变量绑定",
|
||||||
"Switch Setter": "切换设置器"
|
"Switch Setter": "切换设置器",
|
||||||
|
"Multiple Value, Click to Clear": "多种值, 点击清除",
|
||||||
|
"Required": "必填项",
|
||||||
|
"Setted Value, Click to Clear": "已设置值,点击清除",
|
||||||
|
"Multiple Value": "多种值"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 20;
|
z-index: 2;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
|
|
||||||
> .lc-outline-tree-container {
|
> .lc-outline-tree-container {
|
||||||
|
|||||||
@ -50,6 +50,9 @@ export interface SettingTarget {
|
|||||||
// 设置子项属性值
|
// 设置子项属性值
|
||||||
setPropValue(propName: string | number, value: any): void;
|
setPropValue(propName: string | number, value: any): void;
|
||||||
|
|
||||||
|
// 清除已设置值
|
||||||
|
clearPropValue(propName: string | number): void;
|
||||||
|
|
||||||
// 获取顶层附属属性值
|
// 获取顶层附属属性值
|
||||||
getExtraPropValue(propName: string): any;
|
getExtraPropValue(propName: string): any;
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user