fix: settings pane

This commit is contained in:
kangwei 2020-04-21 04:31:43 +08:00
parent 92ea6505c1
commit 27db010b8a
17 changed files with 244 additions and 151 deletions

View File

@ -38,6 +38,10 @@ export interface FieldExtraProps {
* internal use
*/
forceInline?: number;
/**
* compatiable vision display
*/
display?: 'accordion' | 'inline' | 'block' | 'plain' | 'popup' | 'entry';
}
export interface FieldConfig extends FieldExtraProps {

View File

@ -7,44 +7,89 @@ import './index.less';
export interface FieldProps {
className?: string;
// span
title?: TitleContent | null;
defaultDisplay?: 'accordion' | 'inline' | 'block';
collapsed?: boolean;
onExpandChange?: (expandState: boolean) => void;
}
export class CommonField extends Component<FieldProps> {
private shell: HTMLDivElement | null = null;
export class Field extends Component<FieldProps> {
state = {
collapsed: this.props.collapsed,
display: this.props.defaultDisplay || 'inline',
};
private checkIsBlockField() {
if (this.shell) {
const setter = this.shell.lastElementChild!.firstElementChild;
if (setter && setter.classList.contains('lc-block-setter')) {
this.shell.classList.add('lc-block-field');
this.shell.classList.remove('lc-inline-field');
} else {
this.shell.classList.remove('lc-block-field');
this.shell.classList.add('lc-inline-field');
}
private toggleExpand = () => {
const { onExpandChange } = this.props;
const collapsed = !this.state.collapsed;
this.setState({
collapsed,
});
onExpandChange && onExpandChange(!collapsed);
};
private body: HTMLDivElement | null = null;
private dispose?: () => void;
private deployBlockTesting() {
if (this.dispose) {
this.dispose();
}
}
componentDidUpdate() {
this.checkIsBlockField();
const body = this.body;
if (!body) {
return;
}
const check = () => {
const setter = body.firstElementChild;
if (setter && setter.classList.contains('lc-block-setter')) {
this.setState({
display: 'block',
});
} else {
this.setState({
display: 'inline',
});
}
};
const observer = new MutationObserver(check);
check();
observer.observe(body, {
childList: true,
subtree: false,
attributes: true,
attributeFilter: ['class'],
});
this.dispose = () => observer.disconnect();
}
componentDidMount() {
this.checkIsBlockField();
const { defaultDisplay } = this.props;
if (!defaultDisplay || defaultDisplay === 'inline') {
this.deployBlockTesting();
}
}
componentWillUnmount() {
if (this.dispose) {
this.dispose();
}
}
render() {
const { className, children, title } = this.props;
const { display, collapsed } = this.state;
const isAccordion = display === 'accordion';
return (
<div ref={(shell) => (this.shell = shell)} className={classNames('lc-field lc-inline-field', className)}>
{title && (
<div className="lc-field-head">
<div className="lc-field-title">
<Title title={title} />
</div>
<div
className={classNames(`lc-field lc-${display}-field`, className, {
'lc-field-is-collapsed': isAccordion && collapsed,
})}
>
<div className="lc-field-head" onClick={isAccordion ? this.toggleExpand : undefined}>
<div className="lc-field-title">
<Title title={title || ''} />
</div>
)}
<div className="lc-field-body">{children}</div>
{isAccordion && <Icon className="lc-field-icon" type="arrow-up" size="xs" />}
</div>
<div key="body" ref={(shell) => (this.body = shell)} className="lc-field-body">
{children}
</div>
</div>
);
}
@ -96,15 +141,13 @@ export class PopupField extends Component<PopupFieldProps> {
}
}
export type EntryFieldProps = FieldProps;
export interface EntryFieldProps extends FieldProps {
stageName?: string;
}
export class EntryField extends Component<EntryFieldProps> {
constructor(props: any) {
super(props);
}
render() {
const { propName, stageName, tip, title, className } = this.props;
const { stageName, title, className } = this.props;
const classNameList = classNames('engine-setting-field', 'engine-entry-field', className);
const fieldProps: any = {};
@ -117,8 +160,8 @@ export class EntryField extends Component<EntryFieldProps> {
<span className="engine-field-title" key="field-title">
{title}
</span>,
renderTip(tip, { propName }),
<Icons name="arrow" className="engine-field-arrow" size="12px" key="engine-field-arrow-icon" />,
// renderTip(tip, { propName }),
// <Icons name="arrow" className="engine-field-arrow" size="12px" key="engine-field-arrow-icon" />,
];
return (
@ -128,3 +171,14 @@ export class EntryField extends Component<EntryFieldProps> {
);
}
}
export class PlainField extends Component<FieldProps> {
render() {
const { className, children } = this.props;
return (
<div className={classNames(`lc-field lc-plain-field`, className)}>
<div className="lc-field-body">{children}</div>
</div>
);
}
}

View File

@ -21,6 +21,17 @@
}
}
&.lc-plain-field {
// for top-level style
padding: 8px 10px;
> .lc-field-body {
flex: 1;
min-width: 0;
display: flex;
align-items: center;
}
}
&.lc-inline-field {
display: flex;
align-items: center;

View File

@ -0,0 +1,28 @@
import { ReactNode, createElement } from 'react';
import { TitleContent } from '@ali/lowcode-globals';
import './index.less';
import { Field, PopupField, EntryField, PlainField } from './fields';
export interface FieldProps {
className?: string;
title?: TitleContent | null;
display?: 'accordion' | 'inline' | 'block' | 'plain' | 'popup' | 'entry';
collapsed?: boolean;
onExpandChange?: (collapsed: boolean) => void;
[extra: string]: any;
}
export function createField(props: FieldProps, children: ReactNode, type?: 'accordion' | 'inline' | 'block' | 'plain' | 'popup' | 'entry') {
if (type === 'popup') {
return createElement(PopupField, props, children);
}
if (type === 'entry') {
return createElement(EntryField, props, children);
}
if (type === 'plain' || !props.title) {
return createElement(PlainField, props, children);
}
return createElement(Field, { ...props, defaultDisplay: type }, children);
}
export { Field, PopupField, EntryField, PlainField };

View File

@ -1,65 +0,0 @@
import { Component } from 'react';
import classNames from 'classnames';
import { Icon } from '@alifd/next';
import { Title, TitleContent } from '@ali/lowcode-globals';
import './index.less';
import { CommonField, PopupField } from './fields';
export interface FieldProps {
className?: string;
// span
title?: TitleContent | null;
type?: string;
}
export class Field extends Component<FieldProps> {
render() {
const { type, ...rest } = this.props;
if (type === 'popup') {
return <PopupField {...rest} />;
}
return <CommonField {...rest} />;
}
}
export interface FieldGroupProps extends FieldProps {
defaultCollapsed?: boolean;
onExpandChange?: (collapsed: boolean) => void;
}
export class FieldGroup extends Component<FieldGroupProps> {
state = {
collapsed: this.props.defaultCollapsed,
};
toggleExpand() {
const { onExpandChange } = this.props;
const collapsed = !this.state.collapsed;
this.setState({
collapsed,
});
onExpandChange && onExpandChange(collapsed);
}
render() {
const { className, children, title } = this.props;
return (
<div
className={classNames('lc-field lc-accordion-field', className, {
'lc-field-is-collapsed': this.state.collapsed,
})}
>
{title && (
<div className="lc-field-head" onClick={this.toggleExpand.bind(this)}>
<div className="lc-field-title">
<Title title={title} />
</div>
<Icon className="lc-field-icon" type="arrow-up" size="xs" />
</div>
)}
<div className="lc-field-body">{children}</div>
</div>
);
}
}

View File

@ -0,0 +1,5 @@
* 拖拽排序有问题
* forceInline 有问题
* 部分改变不响应
* 样式还原
* autofocus

View File

@ -133,7 +133,7 @@ export class ListSetter extends Component<ArraySetterProps, ArraySetterState> {
render() {
let columns: any = null;
if (this.props.columns) {
columns = this.props.columns.map((column) => <Title title={column.title || (column.name as string)} />);
columns = this.props.columns.map((column) => <Title key={column.name} title={column.title || (column.name as string)} />);
}
const { items } = this.state;

View File

@ -75,6 +75,11 @@
text-overflow: ellipsis;
.lc-field {
padding: 0 !important;
display: flex;
align-items: center;
>.lc-field-body {
justify-content: center;
}
}
> * {
width: 100%;

View File

@ -103,22 +103,6 @@ export default class MixedSetter extends Component<{
});
}
private checkIsBlockField() {
if (this.shell) {
const setter = this.shell.lastElementChild!.firstElementChild;
if (setter && setter.classList.contains('lc-block-setter')) {
this.shell.classList.add('lc-block-setter');
} else {
this.shell.classList.remove('lc-block-field');
}
}
}
componentDidUpdate() {
this.checkIsBlockField();
}
componentDidMount() {
this.checkIsBlockField();
}
private useSetter: (id: string) => {
const { field, onChange } = this.props;

View File

@ -391,4 +391,47 @@ export class SettingPropEntry implements SettingTarget {
onValueChange() {
return () => {};
}
getId() {
return this.id;
}
getName(): string {
return this.path.join('.');
}
getKey() {
return this.name;
}
/*
getDefaultValue() {
return this.extraProps.defaultValue;
}
getConfig<K extends keyof IPropConfig>(configName?: K): IPropConfig[K] | IPropConfig {
if (configName) {
return this.config[configName];
}
return this.config;
}
*/
/*
isHidden() {
return false;
}
isDisabled() {
return false;
}
getSetter() {
}
*/
isIgnore() {
return false;
}
}

View File

@ -1,4 +1,4 @@
import { TitleContent, computed, isDynamicSetter, SetterType, DynamicSetter, FieldExtraProps, FieldConfig, CustomView, isCustomView } from '@ali/lowcode-globals';
import { TitleContent, computed, isDynamicSetter, SetterType, DynamicSetter, FieldExtraProps, FieldConfig, CustomView, isCustomView, obx } from '@ali/lowcode-globals';
import { Transducer } from '../utils';
import { SettingPropEntry } from './setting-entry';
import { SettingTarget } from './setting-target';
@ -26,9 +26,20 @@ export class SettingField extends SettingPropEntry implements SettingTarget {
return this._setter;
}
@obx.ref private _expanded = true;
get expanded(): boolean {
return this._expanded;
}
setExpanded(value: boolean) {
this._expanded = value;
}
constructor(readonly parent: SettingTarget, config: FieldConfig) {
super(parent, config.name, config.type);
console.info(config);
const { title, items, setter, extraProps, ...rest } = config;
this._title = title;
this._setter = setter;
@ -37,6 +48,7 @@ export class SettingField extends SettingPropEntry implements SettingTarget {
...extraProps,
};
this.isRequired = config.isRequired || (setter as any)?.isRequired;
this._expanded = extraProps?.defaultCollapsed ? false : true;
// initial items
if (this.type === 'group' && items) {

View File

@ -8,7 +8,7 @@ import {
createSetterContent,
observer,
} from '@ali/lowcode-globals';
import { Field, FieldGroup } from '../field';
import { Field, createField } from '../field';
import PopupService from '../popup';
import { SettingField, isSettingField } from './setting-field';
import { SettingTarget } from './setting-target';
@ -20,7 +20,7 @@ class SettingFieldView extends Component<{ field: SettingField }> {
const { field } = this.props;
const { extraProps } = field;
const { condition, defaultValue } = extraProps;
const visible = field.isOneNode && typeof condition === 'function' ? !condition(field) : true;
const visible = field.isOneNode && typeof condition === 'function' ? condition(field) !== false : true;
if (!visible) {
return null;
}
@ -62,28 +62,29 @@ class SettingFieldView extends Component<{ field: SettingField }> {
}
}
}
// todo: error handling
return (
<Field title={extraProps.forceInline ? null : field.title}>
{createSetterContent(setterType, {
...shallowIntl(setterProps),
forceInline: extraProps.forceInline,
key: field.id,
// === injection
prop: field, // for compatible vision
field,
// === IO
value, // reaction point
onChange: (value: any) => {
this.setState({
value,
});
field.setValue(value);
},
})}
</Field>
);
return createField({
title: field.title,
collapsed: !field.expanded,
onExpandChange: (expandState) => field.setExpanded(expandState),
}, createSetterContent(setterType, {
...shallowIntl(setterProps),
forceInline: extraProps.forceInline,
key: field.id,
// === injection
prop: field, // for compatible vision
field,
// === IO
value, // reaction point
onChange: (value: any) => {
this.setState({
value,
});
field.setValue(value);
},
}), extraProps.forceInline ? 'plain' : extraProps.display);
}
}
@ -96,17 +97,20 @@ class SettingGroupView extends Component<{ field: SettingField }> {
render() {
const { field } = this.props;
const { extraProps } = field;
const { condition, defaultCollapsed } = extraProps;
const visible = field.isOneNode && typeof condition === 'function' ? !condition(field) : true;
const { condition } = extraProps;
const visible = field.isOneNode && typeof condition === 'function' ? condition(field) !== false : true;
if (!visible) {
return null;
}
// todo: split collapsed state | field.items for optimize
return (
<FieldGroup title={field.title} defaultCollapsed={defaultCollapsed}>
<Field defaultDisplay="accordion" title={field.title} collapsed={!field.expanded} onExpandChange={(expandState) => {
field.setExpanded(expandState);
}}>
{field.items.map((item, index) => createSettingFieldView(item, field, index))}
</FieldGroup>
</Field>
);
}
}

View File

@ -54,10 +54,6 @@
overflow-y: auto;
}
.lc-settings-pane {
padding-bottom: 50px;
}
// ====== reset fusion-tabs =====
.lc-settings-tabs {
position: relative;
@ -114,6 +110,13 @@
}
}
.lc-settings-pane {
padding-bottom: 50px;
.next-btn {
line-height: 1 !important;
}
}
html.lc-cursor-dragging:not(.lowcode-has-fixed-tree) {
.lc-settings-main .lc-outline-pane {
display: block;

View File

@ -102,11 +102,12 @@ export default function(metadata: TransformedComponentMetadata): TransformedComp
title: 'Ref',
setter: 'StringSetter',
},
/*
{
name: '!more',
title: '更多',
setter: 'PropertiesSetter',
},
},*/
],
});
const combined: FieldConfig[] = [
@ -168,11 +169,13 @@ export default function(metadata: TransformedComponentMetadata): TransformedComp
}
if (isRoot) {
/*
combined.push({
name: '#advanced',
title: { type: 'i18n', 'zh-CN': '高级', 'en-US': 'Advance' },
items: [],
});
*/
} else {
combined.push({
name: '#advanced',

View File

@ -216,7 +216,7 @@ export function upgradePropConfig(config: OldPropConfig) {
if (tip) {
if (typeof title !== 'object' || isI18nData(title) || isValidElement(title)) {
newConfig.title = {
title,
label: title,
tip: tip.content,
docUrl: tip.url
};
@ -615,7 +615,9 @@ export function upgradeMetadata(oldConfig: OldPrototypeConfig) {
experimental.callbacks = callbacks;
const props = upgradeConfigure(configure || []);
meta.configure = { props, component };
const events = {};
const styles = {};
meta.configure = { props, component, events, styles };
meta.experimental = experimental;
return meta;
}

View File

@ -16,7 +16,7 @@ async function load() {
const externals = ['react', 'react-dom', 'prop-types', 'react-router', 'react-router-dom', '@ali/recore'];
async function loadAssets() {
const assets = await editor.utils.get('./assets.json');
const assets = await editor.utils.get('./legao-assets.json');
if (assets.packages) {
assets.packages.forEach((item: any) => {

View File

@ -11,7 +11,7 @@ html.engine-cursor-ew-resize, html.engine-cursor-ew-resize * {
}
body, #engine {
position: absolute;
position: fixed;
left: 0;
right: 0;
bottom: 0;