diff --git a/packages/designer/src/designer/drag-ghost/ghost.less b/packages/designer/src/designer/drag-ghost/ghost.less index 30a24afdb..5e2167a09 100644 --- a/packages/designer/src/designer/drag-ghost/ghost.less +++ b/packages/designer/src/designer/drag-ghost/ghost.less @@ -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 { diff --git a/packages/designer/src/designer/setting/setting-field.ts b/packages/designer/src/designer/setting/setting-field.ts index a024afaac..7bc53370a 100644 --- a/packages/designer/src/designer/setting/setting-field.ts +++ b/packages/designer/src/designer/setting/setting-field.ts @@ -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(); } diff --git a/packages/designer/src/designer/setting/setting-prop-entry.ts b/packages/designer/src/designer/setting/setting-prop-entry.ts index 303ef9caa..21ce01b0a 100644 --- a/packages/designer/src/designer/setting/setting-prop-entry.ts +++ b/packages/designer/src/designer/setting/setting-prop-entry.ts @@ -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); + } + /** * 获取子级属性值 */ diff --git a/packages/designer/src/designer/setting/setting-top-entry.ts b/packages/designer/src/designer/setting/setting-top-entry.ts index 8595861d4..9690b8dca 100644 --- a/packages/designer/src/designer/setting/setting-top-entry.ts +++ b/packages/designer/src/designer/setting/setting-top-entry.ts @@ -136,6 +136,15 @@ export class SettingTopEntry implements SettingEntry { }); } + /** + * 清除已设置值 + */ + clearPropValue(propName: string) { + this.nodes.forEach((node) => { + node.clearPropValue(propName); + }); + } + /** * 获取子级属性值 */ diff --git a/packages/designer/src/document/node/node.ts b/packages/designer/src/document/node/node.ts index bbe624cd0..eb326fd14 100644 --- a/packages/designer/src/document/node/node.ts +++ b/packages/designer/src/document/node/node.ts @@ -395,6 +395,13 @@ export class Node { this.getProp(path, true)!.setValue(value); } + /** + * 清除已设置的值 + */ + clearPropValue(path: string): void { + this.getProp(path, false)?.unset(); + } + /** * 设置多个属性值,和原有值合并 */ diff --git a/packages/designer/src/document/node/props/prop.ts b/packages/designer/src/document/node/props/prop.ts index da257b6bf..518f62d26 100644 --- a/packages/designer/src/document/node/props/prop.ts +++ b/packages/designer/src/document/node/props/prop.ts @@ -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; diff --git a/packages/editor-skeleton/src/components/field/fields.tsx b/packages/editor-skeleton/src/components/field/fields.tsx index 74a596520..30b28d7cf 100644 --- a/packages/editor-skeleton/src/components/field/fields.tsx +++ b/packages/editor-skeleton/src/components/field/fields.tsx @@ -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 { @@ -73,9 +77,10 @@ export class Field extends Component { } 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 (
{ >
+ {createValueState(valueState, onClear)} </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; } diff --git a/packages/editor-skeleton/src/components/field/index.less b/packages/editor-skeleton/src/components/field/index.less index 00e073111..f1b155b2f 100644 --- a/packages/editor-skeleton/src/components/field/index.less +++ b/packages/editor-skeleton/src/components/field/index.less @@ -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; diff --git a/packages/editor-skeleton/src/components/field/index.ts b/packages/editor-skeleton/src/components/field/index.ts index b0f3c497d..9c83ac392 100644 --- a/packages/editor-skeleton/src/components/field/index.ts +++ b/packages/editor-skeleton/src/components/field/index.ts @@ -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; } diff --git a/packages/editor-skeleton/src/components/settings/settings-pane.tsx b/packages/editor-skeleton/src/components/settings/settings-pane.tsx index 0ab2ce23a..bffa799fa 100644 --- a/packages/editor-skeleton/src/components/settings/settings-pane.tsx +++ b/packages/editor-skeleton/src/components/settings/settings-pane.tsx @@ -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), diff --git a/packages/editor-skeleton/src/icons/clear.tsx b/packages/editor-skeleton/src/icons/clear.tsx new file mode 100644 index 000000000..5fa5f866c --- /dev/null +++ b/packages/editor-skeleton/src/icons/clear.tsx @@ -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'; diff --git a/packages/editor-skeleton/src/locale/en-US.json b/packages/editor-skeleton/src/locale/en-US.json index 1d50bd8be..571c555d7 100644 --- a/packages/editor-skeleton/src/locale/en-US.json +++ b/packages/editor-skeleton/src/locale/en-US.json @@ -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" } diff --git a/packages/editor-skeleton/src/locale/zh-CN.json b/packages/editor-skeleton/src/locale/zh-CN.json index 1bac4b1e6..0ed161004 100644 --- a/packages/editor-skeleton/src/locale/zh-CN.json +++ b/packages/editor-skeleton/src/locale/zh-CN.json @@ -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": "多种值" } diff --git a/packages/plugin-outline-pane/src/views/style.less b/packages/plugin-outline-pane/src/views/style.less index 129d2968d..056061f11 100644 --- a/packages/plugin-outline-pane/src/views/style.less +++ b/packages/plugin-outline-pane/src/views/style.less @@ -2,7 +2,7 @@ height: 100%; width: 100%; position: relative; - z-index: 20; + z-index: 2; background-color: white; > .lc-outline-tree-container { diff --git a/packages/types/src/setting-target.ts b/packages/types/src/setting-target.ts index 3848fc154..6f686e069 100644 --- a/packages/types/src/setting-target.ts +++ b/packages/types/src/setting-target.ts @@ -50,6 +50,9 @@ export interface SettingTarget { // 设置子项属性值 setPropValue(propName: string | number, value: any): void; + // 清除已设置值 + clearPropValue(propName: string | number): void; + // 获取顶层附属属性值 getExtraPropValue(propName: string): any;