feat: show value state

This commit is contained in:
kangwei 2020-05-10 21:00:10 +08:00
parent f75a51e77e
commit bd49e50295
15 changed files with 215 additions and 56 deletions

View File

@ -8,7 +8,7 @@
align-items: center;
pointer-events: none;
background-color: rgba(0, 0, 0, 0.4);
opacity: 0.5;
//opacity: 0.9;
box-shadow: 0 0 6px grey;
transform: translate(-10%, -50%);
.lc-ghost {

View File

@ -84,35 +84,6 @@ export class SettingField extends SettingPropEntry implements SettingEntry {
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() {
this.disposeItems();
}

View File

@ -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);
}
/**
*
*/
clearPropValue(propName: string | number) {
const path = this.path.concat(propName).join('.');
this.top.clearPropValue(path);
}
/**
*
*/

View File

@ -136,6 +136,15 @@ export class SettingTopEntry implements SettingEntry {
});
}
/**
*
*/
clearPropValue(propName: string) {
this.nodes.forEach((node) => {
node.clearPropValue(propName);
});
}
/**
*
*/

View File

@ -395,6 +395,13 @@ export class Node<Schema extends NodeSchema = NodeSchema> {
this.getProp(path, true)!.setValue(value);
}
/**
*
*/
clearPropValue(path: string): void {
this.getProp(path, false)?.unset();
}
/**
*
*/

View File

@ -245,7 +245,9 @@ export class Prop implements IPropParent {
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 {
if (!other || other.isUnset()) {
return this.isUnset() ? 0 : 2;

View File

@ -1,17 +1,21 @@
import { Component } from 'react';
import classNames from 'classnames';
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 { PopupPipe, PopupContext } from '../popup';
import { intl, intlNode } from '../../locale';
import './index.less';
import { IconClear } from 'editor-skeleton/src/icons/clear';
export interface FieldProps {
className?: string;
title?: TitleContent | null;
defaultDisplay?: 'accordion' | 'inline' | 'block';
collapsed?: boolean;
valueState?: number;
onExpandChange?: (expandState: boolean) => void;
onClear?: () => void;
}
export class Field extends Component<FieldProps> {
@ -73,9 +77,10 @@ export class Field extends Component<FieldProps> {
}
render() {
const { className, children, title } = this.props;
const { className, children, title, valueState, onClear } = this.props;
const { display, collapsed } = this.state;
const isAccordion = display === 'accordion';
return (
<div
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-title">
{createValueState(valueState, onClear)}
<Title title={title || ''} />
</div>
{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 {
width?: number;
}

View File

@ -11,6 +11,54 @@
.lc-field-title {
display: flex;
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 {
// margin-right: @x-gap;
@ -75,7 +123,7 @@
// background: var(--color-block-background-shallow, rgba(31,56,88,.06));
// border-bottom: 1px solid var(--color-line-normal);
// color: var(--color-title);
padding: 0 16px;
padding: 0 16px 0 10px;
background-color: #F7F9FC;
color: #8F9BB3;
user-select: none;

View File

@ -8,7 +8,9 @@ export interface FieldProps {
title?: TitleContent | null;
display?: 'accordion' | 'inline' | 'block' | 'plain' | 'popup' | 'entry';
collapsed?: boolean;
valueState?: number;
onExpandChange?: (collapsed: boolean) => void;
onClear?: () => void;
[extra: string]: any;
}

View File

@ -1,10 +1,11 @@
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 { Field, createField } from '../field';
import PopupService from '../popup';
import { SettingField, isSettingField, SettingTopEntry, SettingEntry } from '@ali/lowcode-designer';
import { isSetterConfig, CustomView } from '@ali/lowcode-types';
import { intl } from '../../locale';
@observer
class SettingFieldView extends Component<{ field: SettingField }> {
@ -41,26 +42,19 @@ class SettingFieldView extends Component<{ field: SettingField }> {
setterType = setter;
}
let value = null;
if (field.type === 'field') {
if (defaultValue != null && !('defaultValue' in setterProps)) {
setterProps.defaultValue = defaultValue;
if (initialValue == null) {
initialValue = defaultValue;
}
if (defaultValue != null && !('defaultValue' in setterProps)) {
setterProps.defaultValue = defaultValue;
if (initialValue == null) {
initialValue = defaultValue;
}
if (field.valueState > 0) {
value = field.getValue();
} else {
setterProps.multiValue = true;
if (!('placeholder' in setterProps)) {
// FIXME! move to locale file
setterProps.placeholder = intl({
type: 'i18n',
'zh-CN': '多种值',
'en-US': 'Multiple Value',
});
}
}
if (field.valueState === -1) {
setterProps.multiValue = true;
if (!('placeholder' in setterProps)) {
setterProps.placeholder = intl('Multiple Value');
}
} else {
value = field.getValue();
}
// todo: error handling
@ -69,7 +63,9 @@ class SettingFieldView extends Component<{ field: SettingField }> {
{
title: field.title,
collapsed: !field.expanded,
valueState: field.isRequired ? 10 : field.valueState,
onExpandChange: (expandState) => field.setExpanded(expandState),
onClear: () => field.clearValue(),
},
createSetterContent(setterType, {
...shallowIntl(setterProps),

View 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';

View File

@ -1,5 +1,9 @@
{
"Binded: {expr}": "Binded: {expr}",
"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"
}

View File

@ -1,5 +1,9 @@
{
"Binded: {expr}": "已绑定: {expr}",
"Variable Binding": "变量绑定",
"Switch Setter": "切换设置器"
"Switch Setter": "切换设置器",
"Multiple Value, Click to Clear": "多种值, 点击清除",
"Required": "必填项",
"Setted Value, Click to Clear": "已设置值,点击清除",
"Multiple Value": "多种值"
}

View File

@ -2,7 +2,7 @@
height: 100%;
width: 100%;
position: relative;
z-index: 20;
z-index: 2;
background-color: white;
> .lc-outline-tree-container {

View File

@ -50,6 +50,9 @@ export interface SettingTarget {
// 设置子项属性值
setPropValue(propName: string | number, value: any): void;
// 清除已设置值
clearPropValue(propName: string | number): void;
// 获取顶层附属属性值
getExtraPropValue(propName: string): any;