feat: form、crud、textarea、selecth支持外观配置 (#10649)

* feat(editor): 表单支持外观配置

* bugfix

* 优化获取cssVar逻辑

* 优化外观默认值

* 删除外观继承相关逻辑

* 更新快照

* 表单布局

* 表单默认mode优化为flex

* 优化colorpicker交互

* 优化编辑器表单布局配置

* 表单静态展示外观配置

* bugfix

* feat: crud2支持外观配置

* crud 外观默认值

* bugfix

* 多行文本支持外观配置

* 下拉框支持外观配置

* 修复table表头无法居中对齐问题

* style: 优化表格样式

* bugfix

---------

Co-authored-by: qinhaoyan <30946345+qinhaoyan@users.noreply.github.com>
This commit is contained in:
qkiroc 2024-07-18 19:04:07 +08:00 committed by GitHub
parent 8e93124781
commit d43922db81
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
66 changed files with 1937 additions and 814 deletions

View File

@ -49,10 +49,15 @@ import LazyComponent from '../components/LazyComponent';
import {isAlive} from 'mobx-state-tree';
import type {LabelAlign} from './Item';
import {injectObjectChain} from '../utils';
import {
CustomStyleClassName,
injectObjectChain,
setThemeClassName
} from '../utils';
import {reaction} from 'mobx';
import groupBy from 'lodash/groupBy';
import isEqual from 'lodash/isEqual';
import CustomStyle from '../components/CustomStyle';
export interface FormHorizontal {
left?: number;
@ -1958,6 +1963,11 @@ export default class Form extends React.Component<FormProps, object> {
staticClassName,
static: isStatic = false,
loadingConfig,
themeCss,
id,
wrapperCustomStyle,
env,
wrapWithPanel,
testid
} = this.props;
@ -1986,7 +1996,25 @@ export default class Form extends React.Component<FormProps, object> {
`Form--${mode || 'normal'}`,
columnCount ? `Form--column Form--column-${columnCount}` : null,
staticClassName && isStatic ? staticClassName : className,
isStatic ? 'Form--isStatic' : null
isStatic ? 'Form--isStatic' : null,
setThemeClassName({
...this.props,
name: [
'formControlClassName',
'itemClassName',
'staticClassName',
'itemLabelClassName'
],
id,
themeCss
}),
!wrapWithPanel &&
setThemeClassName({
...this.props,
name: 'wrapperCustomStyle',
id,
themeCss: wrapperCustomStyle
})
)}
onSubmit={this.handleFormSubmit}
noValidate
@ -2060,6 +2088,69 @@ export default class Form extends React.Component<FormProps, object> {
show: store.drawerOpen
}
)}
<CustomStyle
{...this.props}
config={{
themeCss,
classNames: [
wrapWithPanel && {
key: 'panelClassName'
},
!wrapWithPanel && {
key: 'formControlClassName'
},
{
key: 'headerControlClassName',
weights: {
default: {
parent: `.${cx('Panel')}`
}
}
},
wrapWithPanel && {
key: 'headerTitleControlClassName'
},
wrapWithPanel && {
key: 'bodyControlClassName'
},
wrapWithPanel && {
key: 'actionsControlClassName',
weights: {
default: {
parent: `.${cx('Panel--form')}`
}
}
},
{
key: 'itemClassName',
weights: {
default: {
inner: `.${cx('Form-item')}`
}
}
},
{
key: 'staticClassName',
weights: {
default: {
inner: `.${cx('Form-static')}`
}
}
},
{
key: 'itemLabelClassName',
weights: {
default: {
inner: `.${cx('Form-label')}`
}
}
}
].filter(n => n) as CustomStyleClassName[],
wrapperCustomStyle,
id
}}
env={env}
/>
</WrapperComponent>
);
}
@ -2083,7 +2174,10 @@ export default class Form extends React.Component<FormProps, object> {
affixFooter,
lazyLoad,
translate: __,
footer
footer,
id,
wrapperCustomStyle,
themeCss
} = this.props;
let body: JSX.Element = this.renderBody();
@ -2096,7 +2190,22 @@ export default class Form extends React.Component<FormProps, object> {
title: __(title)
},
{
className: cx(panelClassName, 'Panel--form'),
className: cx(
panelClassName,
'Panel--form',
setThemeClassName({
...this.props,
name: 'wrapperCustomStyle',
id,
themeCss: wrapperCustomStyle
}),
setThemeClassName({
...this.props,
name: 'panelClassName',
id,
themeCss
})
),
style: style,
formStore: this.props.store,
children: body,
@ -2106,11 +2215,35 @@ export default class Form extends React.Component<FormProps, object> {
disabled: store.loading,
btnDisabled: store.loading || store.validating,
headerClassName,
headerControlClassName: setThemeClassName({
...this.props,
name: 'headerControlClassName',
id,
themeCss
}),
headerTitleControlClassName: setThemeClassName({
...this.props,
name: 'headerTitleControlClassName',
id,
themeCss
}),
footer,
footerClassName,
footerWrapClassName,
actionsClassName,
actionsControlClassName: setThemeClassName({
...this.props,
name: 'actionsControlClassName',
id,
themeCss
}),
bodyClassName,
bodyControlClassName: setThemeClassName({
...this.props,
name: 'bodyControlClassName',
id,
themeCss
}),
affixFooter
}
) as JSX.Element;

View File

@ -1224,7 +1224,9 @@ export class FormItemWrap extends React.Component<FormItemProps> {
const horizontal = props.horizontal || props.formHorizontal || {};
const left = getWidthRate(horizontal.left);
const right = getWidthRate(horizontal.right);
const labelAlign = props.labelAlign || props.formLabelAlign;
const labelAlign =
(props.labelAlign !== 'inherit' && props.labelAlign) ||
props.formLabelAlign;
const labelWidth = props.labelWidth || props.formLabelWidth;
return (
@ -1966,7 +1968,8 @@ export class FormItemWrap extends React.Component<FormItemProps> {
themeCss,
id,
wrapperCustomStyle,
env
env,
classnames: cx
} = this.props;
const mode = this.props.mode || formMode;
@ -2004,7 +2007,13 @@ export class FormItemWrap extends React.Component<FormItemProps> {
themeCss: themeCss || css,
classNames: [
{
key: 'labelClassName'
key: 'labelClassName',
weights: {
default: {
suf: `.${cx('Form-label')}`,
parent: `.${cx('Form-item')}`
}
}
},
{
key: 'descriptionClassName'
@ -2222,10 +2231,10 @@ export function asFormItem(config: Omit<FormItemConfig, 'component'>) {
...rest
} = this.props;
const controlSize =
size && ['xs', 'sm', 'md', 'lg', 'full'].includes(size)
? size
: defaultSize;
const isRuleSize =
size && ['xs', 'sm', 'md', 'lg', 'full'].includes(size);
const controlSize = isRuleSize ? size : defaultSize;
//@ts-ignore
const isOpened = this.state.isOpened;
@ -2246,6 +2255,9 @@ export function asFormItem(config: Omit<FormItemConfig, 'component'>) {
ref={supportRef ? this.refFn : undefined}
forwardedRef={supportRef ? undefined : this.refFn}
formItem={model}
style={{
width: !isRuleSize && size ? size : undefined
}}
className={cx(
`Form-control`,
{

View File

@ -40,6 +40,7 @@ export const inheritValueMap: PlainObject = {
interface extra {
important?: boolean;
parent?: string;
inner?: string;
pre?: string;
suf?: string;
@ -179,7 +180,8 @@ export function formatStyle(
default: '',
hover: ':hover',
active: ':hover:active',
disabled: '.is-disabled'
focused: '',
disabled: ''
};
for (let item of classNames) {
@ -200,6 +202,7 @@ export function formatStyle(
default: {},
hover: {},
active: {},
focused: {},
disabled: {}
};
Object.keys(body).forEach(key => {
@ -210,6 +213,8 @@ export function formatStyle(
statusMap.hover[key.replace(':hover', '')] = body[key];
} else if (!!~key.indexOf(':active')) {
statusMap.active[key.replace(':active', '')] = body[key];
} else if (!!~key.indexOf(':focused')) {
statusMap.focused[key.replace(':focused', '')] = body[key];
} else if (!!~key.indexOf(':disabled')) {
statusMap.disabled[key.replace(':disabled', '')] = body[key];
} else {
@ -269,11 +274,13 @@ export function formatStyle(
if (styles.length > 0) {
const cx = (weights?.pre || '') + className + (weights?.suf || '');
const inner = weights?.inner || '';
const parent = weights?.parent || '';
res.push({
className: cx + status2string[status] + inner,
content: `.${cx + status2string[status]} ${inner}{\n ${styles.join(
'\n '
)}\n}`
className: parent + cx + status2string[status] + inner,
content: `${parent} .${
cx + status2string[status]
} ${inner}{\n ${styles.join('\n ')}\n}`
});
// TODO:切换状态暂时先不改变组件的样式
// if (['hover', 'active', 'disabled'].includes(status)) {
@ -297,11 +304,12 @@ export interface CustomStyleClassName {
default?: extra;
hover?: extra;
active?: extra;
focused?: extra;
disabled?: extra;
};
}
export function insertCustomStyle(prams: {
export function insertCustomStyle(params: {
themeCss: any;
classNames: CustomStyleClassName[];
id: string;
@ -318,7 +326,7 @@ export function insertCustomStyle(prams: {
customStyleClassPrefix,
doc,
data
} = prams;
} = params;
if (!themeCss) {
return;
}
@ -503,7 +511,7 @@ export function formatInputThemeCss(themeCss: any) {
}
export function setThemeClassName(params: {
name: string;
name: string | string[];
id?: string;
themeCss: any;
extra?: string;
@ -514,17 +522,25 @@ export function setThemeClassName(params: {
return '';
}
if (name !== 'wrapperCustomStyle' && !themeCss[name]) {
return '';
}
let index = '';
if (typeof data?.index === 'number') {
index = `-${data.index}`;
}
return (
`${name}-${id.replace?.('u:', '') || id}` +
(extra ? `-${extra}` : '') +
index
);
function setClassName(name: string, id: string) {
if (name !== 'wrapperCustomStyle' && !themeCss[name]) {
return '';
}
return (
`${name}-${id.replace?.('u:', '') || id}` +
(extra ? `-${extra}` : '') +
index
);
}
if (typeof name === 'string') {
return setClassName(name, id);
} else {
return name.map(n => setClassName(n, id)).join(' ');
}
}

View File

@ -382,7 +382,7 @@
}
.action-desc {
margin-left: 16px;
color: var(--Form-item-fontColor);
color: var(--Form-item-color);
}
.cmpt-action-select,

View File

@ -54,6 +54,8 @@
@import './style-control/style-common';
@import './style-control/theme-css-code';
@import './style-control/flex-layout';
@import './style-control/col-count';
@import './style-control/col-size';
/* 组件样式 */
@import './components/button';

View File

@ -81,7 +81,7 @@
label {
flex-shrink: 0;
margin-right: 8px;
color: var(--Form-item-fontColor);
color: var(--Form-item-color);
}
.flex {

View File

@ -0,0 +1,30 @@
.ColCount {
display: flex;
background-color: #f7f7f9;
padding: 2px;
border-radius: 4px;
&-item {
text-align: center;
line-height: 30px;
flex: 1;
min-width: 0;
height: 30px;
cursor: pointer;
&:not(.is-disabled):hover {
background: #fff;
color: #2468f2;
}
}
.is-active {
background: #fff;
color: #2468f2;
border-radius: 4px;
}
.is-disabled {
cursor: not-allowed;
color: #ccc;
}
}
.LabelAlign {
width: 100%;
}

View File

@ -0,0 +1,28 @@
.ColSize {
display: flex;
background-color: #f7f7f9;
padding: 2px;
border-radius: 4px;
&-item {
text-align: center;
line-height: 28px;
flex: 1;
min-width: 0;
width: 32px;
height: 28px;
cursor: pointer;
&:not(.is-disabled):hover {
background: #fff;
color: #2468f2;
}
}
.is-active {
background: #fff;
color: #2468f2;
border-radius: 4px;
}
.is-disabled {
cursor: not-allowed;
color: #ccc;
}
}

View File

@ -54,6 +54,7 @@ export {
export let themeConfig: any = {};
export let themeOptionsData: any = {};
export let cssVars: any = {};
export function __uri(id: string) {
return id;
@ -1180,11 +1181,52 @@ export function setThemeConfig(config: any) {
themeConfig = config;
themeOptionsData = getGlobalData(themeConfig);
themeUselessPropKeys = Object.keys(getThemeConfig());
cssVars = getAllCssVar();
}
/**
* css变量
* @param id id
* @param selectorText
* @returns css变量
*/
export function getCssVarById(id: string, selectorText: string) {
const styleSheets = document.styleSheets;
let cssVars: PlainObject = {};
for (const styleSheet of styleSheets) {
if ((styleSheet.ownerNode as Element)?.id === id) {
for (let i = 0; i < styleSheet.cssRules.length; i++) {
const cssRule = styleSheet.cssRules[i] as any;
if ((cssRule as any).selectorText?.includes(selectorText)) {
const cssText = cssRule.style.cssText;
const cssArr = cssText.split('; ');
cssArr.forEach((item: string) => {
if (item) {
const [key, value] = item.split(': ');
cssVars[key] = value;
}
});
}
}
break;
}
}
return cssVars;
}
export function getAllCssVar() {
const cssVars = getCssVarById('baseStyle', ':root, .AMISCSSWrapper');
const themeCssVars = getCssVarById(
'themeCss',
'.app-popover, #editor-preview-body'
);
return Object.assign({}, cssVars, themeCssVars);
}
// 获取主题数据和样式选择器数据
export function getThemeConfig() {
return {themeConfig, ...themeOptionsData};
return {themeConfig, ...themeOptionsData, cssVars};
}
const backgroundMap: PlainObject = {

View File

@ -26,7 +26,12 @@
rel="stylesheet"
href="../../../node_modules/@fortawesome/fontawesome-free/css/v4-shims.css"
/>
<link rel="stylesheet" title="cxd" href="../amis-ui/scss/themes/cxd.scss" />
<link
rel="stylesheet"
title="cxd"
id="baseStyle"
href="../amis-ui/scss/themes/cxd.scss"
/>
<link rel="stylesheet" title="cxd" href="../amis-ui/scss/helper.scss" />
<link rel="stylesheet" href="../amis-editor-core/scss/editor.scss" />
<link rel="stylesheet" href="../../../examples/doc.css" />

View File

@ -614,16 +614,18 @@ export class ApiDSBuilder extends DSBuilder<
id,
type: 'form',
title: '表单',
mode: 'horizontal',
mode: 'flex',
labelAlign: 'top',
dsType: this.key,
feat: feat,
body: fields.map(f => {
body: fields.map((f, index) => {
const type = f.inputType
? displayType2inputType(f.inputType) ?? 'input-text'
: 'input-text';
return {
...pick(f, ['name', 'label']),
row: index,
type,
...this.appendSchema2InputControl(type)
};

View File

@ -45,6 +45,9 @@ import './renderer/event-control/index';
import './renderer/TreeOptionControl';
import './renderer/TransferTableControl';
import './renderer/style-control/ThemeCssCode';
import './renderer/style-control/ColCount';
import './renderer/style-control/ColSize';
import './renderer/style-control/LabelAlign';
import './renderer/ButtonGroupControl';
import './renderer/FlexSettingControl';
import './renderer/FieldSetting';

View File

@ -139,10 +139,11 @@ export class ButtonPlugin extends BasePlugin {
label: '文字',
name: `themeCss.className.font:${state}`,
visibleOn: visibleOn,
editorThemePath: [
`button1.type.\${level}.${state}.body.font-color`,
`button1.size.\${size}.body.font`
]
editorValueToken: {
'color': `--button-\${level || "default"}-${state}-font-color`,
'*': '--button-size-${size || "default"}'
},
state
}),
getSchemaTpl('theme:colorPicker', {
label: '背景',
@ -151,28 +152,33 @@ export class ButtonPlugin extends BasePlugin {
needGradient: true,
needImage: true,
visibleOn: visibleOn,
editorThemePath: `button1.type.\${level}.${state}.body.bg-color`
editorValueToken: `--button-\${level || "default"}-${state}-bg-color`,
state
}),
getSchemaTpl('theme:border', {
name: `themeCss.className.border:${state}`,
visibleOn: visibleOn,
editorThemePath: `button1.type.\${level}.${state}.body.border`
editorValueToken: `--button-\${level || "default"}-${state}`,
state
}),
getSchemaTpl('theme:paddingAndMargin', {
name: `themeCss.className.padding-and-margin:${state}`,
visibleOn: visibleOn,
editorThemePath: `button1.size.\${size}.body.padding-and-margin`
editorValueToken: '--button-size-${size || "default"}',
state
}),
getSchemaTpl('theme:radius', {
name: `themeCss.className.radius:${state}`,
visibleOn: visibleOn,
editorThemePath: `button1.size.\${size}.body.border`
editorValueToken: '--button-size-${size || "default"}',
state
}),
getSchemaTpl('theme:select', {
label: '图标尺寸',
name: `themeCss.iconClassName.iconSize:${state}`,
visibleOn: visibleOn,
editorThemePath: `button1.size.\${size}.body.icon-size`
editorValueToken: '--button-size-${size || "default"}-icon-size',
state
})
];
};

View File

@ -483,7 +483,7 @@ export class BaseCRUDPlugin extends BasePlugin {
baseCRUDPanelBody = (context: BuildPanelEventContext) => {
return getSchemaTpl('tabs', [
this.renderPropsTab(context),
this.renderStylesTab(context),
// this.renderStylesTab(context),
this.renderEventTab(context)
]);
};

View File

@ -123,7 +123,13 @@ export class FormPlugin extends BasePlugin {
label: '表单集合',
matchRegion: (elem: JSX.Element) => !!elem?.props.noValidate,
renderMethod: 'renderBody',
preferTag: '表单项'
preferTag: '表单项',
dndMode: (schema: any) => {
if (schema.mode === 'flex') {
return 'flex';
}
return 'default';
}
},
{
@ -1091,12 +1097,66 @@ export class FormPlugin extends BasePlugin {
{
title: '布局',
body: [
getSchemaTpl('formItemMode', {
isForm: true,
/** Form组件默认为normal模式 */
defaultValue: 'normal'
}),
getSchemaTpl('horizontal'),
{
label: '布局',
name: 'mode',
type: 'select',
pipeIn: defaultValue('flex'),
options: [
{
label: '网格',
value: 'flex'
},
{
label: '内联',
value: 'inline'
},
{
label: '水平',
value: 'horizontal'
},
{
label: '垂直',
value: 'normal'
}
],
pipeOut: (v: string) => (v ? v : undefined),
onChange: (
value: string,
oldValue: string,
model: any,
form: any
) => {
const body = [...form.data.body];
let temp = body;
if (value === 'flex') {
temp = body?.map((item: any, index: number) => {
return {
...item,
row: index,
mode: undefined
};
});
} else {
temp = body?.map((item: any, index: number) => {
return {
...item,
row: undefined,
colSize: undefined,
labelAlign: undefined,
mode: undefined
};
});
}
form.setValueByName('body', temp);
}
},
{
type: 'col-count',
name: '__rolCount',
label: tipedLabel('列数', '仅对PC页面生效'),
visibleOn: 'this.mode === "flex"'
},
{
label: '列数',
name: 'columnCount',
@ -1106,6 +1166,7 @@ export class FormPlugin extends BasePlugin {
precision: 0,
resetValue: '',
unitOptions: ['列'],
hiddenOn: 'this.mode === "flex"',
pipeOut: (value: string) => {
if (value && typeof value === 'string') {
const count = Number.parseInt(
@ -1120,12 +1181,7 @@ export class FormPlugin extends BasePlugin {
return undefined;
}
}
}
]
},
{
title: '其他',
body: [
},
getSchemaTpl('switch', {
name: 'wrapWithPanel',
label: tipedLabel(
@ -1144,6 +1200,142 @@ export class FormPlugin extends BasePlugin {
})
]
},
getSchemaTpl('theme:base', {
classname: 'formControlClassName',
title: '表单样式',
needState: false,
hiddenOn: isWrapped
}),
getSchemaTpl('theme:base', {
classname: 'panelClassName',
title: 'Panel样式',
editorValueToken: '--Panel',
hidePadding: true,
needState: false,
visibleOn: isWrapped
}),
getSchemaTpl('theme:base', {
classname: 'headerControlClassName',
title: '标题区样式',
visibleOn: isWrapped,
editorValueToken: '--Panel-heading',
hideRadius: true,
hideShadow: true,
hideMargin: true,
needState: false,
extra: [
getSchemaTpl('theme:font', {
name: 'themeCss.headerTitleControlClassName.font',
editorValueToken: '--Panel-heading'
})
]
}),
getSchemaTpl('theme:base', {
classname: 'bodyControlClassName',
title: '内容区样式',
editorValueToken: '--Panel-body',
hideRadius: true,
hideShadow: true,
hideBorder: true,
hideMargin: true,
hideBackground: true,
needState: false,
visibleOn: isWrapped
}),
{
title: '表单项样式',
body: [
{
type: 'select',
name: 'labelAlign',
label: '标题位置',
selectFirst: true,
hiddenOn:
'this.mode === "normal" || this.mode === "inline" || this.mode === "horizontal"',
options: [
{
label: '上下布局',
value: 'top'
},
{
label: '水平居左',
value: 'left'
},
{
label: '水平居右',
value: 'right'
}
]
},
{
type: 'select',
name: 'labelAlign',
label: '标题位置',
selectFirst: true,
hiddenOn:
'this.mode === "normal" || this.mode === "inline" || this.mode === "flex"',
options: [
{
label: '水平居左',
value: 'left'
},
{
label: '水平居右',
value: 'right'
}
]
},
getSchemaTpl('theme:select', {
label: '标题宽度',
name: 'labelWidth',
hiddenOn:
'this.mode === "normal" || this.labelAlign === "top"'
}),
getSchemaTpl('theme:font', {
label: '标题文字',
editorValueToken: '--Form-item',
hasSenior: false,
name: 'themeCss.itemLabelClassName.font'
}),
getSchemaTpl('theme:paddingAndMargin', {
label: '标题边距',
hidePadding: true,
name: 'themeCss.itemLabelClassName.padding-and-margin'
}),
getSchemaTpl('theme:paddingAndMargin', {
label: '表单项边距',
hidePadding: true,
name: 'themeCss.itemClassName.padding-and-margin'
}),
getSchemaTpl('theme:font', {
label: '静态展示文字',
editorValueToken: '--Form-static',
name: 'themeCss.staticClassName.font',
visibleOn: '!!this.static || !!this.staticOn'
})
]
},
getSchemaTpl('theme:base', {
classname: 'actionsControlClassName',
title: '操作区样式',
editorValueToken: '--Panel-footer',
hideRadius: true,
hideShadow: true,
hideMargin: true,
needState: false,
visibleOn: isWrapped
}),
{
title: '自定义样式',
body: [
{
type: 'theme-cssCode',
label: false
}
]
},
/** */
getSchemaTpl('style:classNames', {
isFormItem: false,

View File

@ -13,20 +13,20 @@ import {ValidatorTag} from '../../validator';
const addBtnCssClassName = 'themeCss.addBtnControlClassName';
const IconCssClassName = 'themeCss.iconControlClassName';
const editorPath = 'inputImage.base';
const editorPath = '--inputImage-base';
const inputStateFunc = (visibleOn: string, state: string) => {
return [
getSchemaTpl('theme:border', {
name: `${addBtnCssClassName}.border:${state}`,
visibleOn: visibleOn,
editorThemePath: `${editorPath}.${state}.body.border`
editorValueToken: `${editorPath}-${state}`
}),
getSchemaTpl('theme:colorPicker', {
label: '文字',
name: `${addBtnCssClassName}.color:${state}`,
labelMode: 'input',
visibleOn: visibleOn,
editorThemePath: `${editorPath}.${state}.body.color`
editorValueToken: `${editorPath}-${state}-color`
}),
getSchemaTpl('theme:colorPicker', {
label: '背景',
@ -35,14 +35,14 @@ const inputStateFunc = (visibleOn: string, state: string) => {
needGradient: true,
needImage: true,
visibleOn: visibleOn,
editorThemePath: `${editorPath}.${state}.body.bg-color`
editorValueToken: `${editorPath}-${state}-bg-color`
}),
getSchemaTpl('theme:colorPicker', {
label: '图标',
name: `${addBtnCssClassName}.icon-color:${state}`,
labelMode: 'input',
visibleOn: visibleOn,
editorThemePath: `${editorPath}.${state}.body.icon-color`
editorValueToken: `${editorPath}-${state}-icon-color`
})
];
};
@ -506,7 +506,7 @@ export class ImageControlPlugin extends BasePlugin {
getSchemaTpl('theme:radius', {
name: `${addBtnCssClassName}.border-radius`,
label: '圆角',
editorThemePath: `${editorPath}.default.body.border`
editorValueToken: `${editorPath}-default`
}),
{
name: `${addBtnCssClassName}.--inputImage-base-default-icon`,
@ -517,12 +517,12 @@ export class ImageControlPlugin extends BasePlugin {
getSchemaTpl('theme:select', {
name: `${IconCssClassName}.iconSize`,
label: '图标大小',
editorThemePath: `${editorPath}.default.body.icon-size`
editorValueToken: `${editorPath}-default-icon-size`
}),
getSchemaTpl('theme:select', {
name: `${IconCssClassName}.margin-bottom`,
label: '图标底边距',
editorThemePath: `${editorPath}.default.body.icon-margin`
editorValueToken: `${editorPath}-default-icon-margin`
})
]
},

View File

@ -289,8 +289,7 @@ export class NumberControlPlugin extends BasePlugin {
getSchemaTpl(
'collapseGroup',
[
getSchemaTpl('style:formItem', {
renderer: context.info.renderer,
getSchemaTpl('theme:formItem', {
schema: [
{
label: '快捷编辑',
@ -317,7 +316,7 @@ export class NumberControlPlugin extends BasePlugin {
body: [
...inputStateTpl(
'themeCss.inputControlClassName',
'inputNumber.base.base'
'--inputNumber-${displayMode || "base"}'
)
]
},

View File

@ -416,7 +416,7 @@ export class TextControlPlugin extends BasePlugin {
body: getSchemaTpl(
'collapseGroup',
[
getSchemaTpl('style:formItem', {renderer}),
getSchemaTpl('theme:formItem'),
getSchemaTpl('theme:form-label'),
getSchemaTpl('theme:form-description'),
{
@ -424,7 +424,7 @@ export class TextControlPlugin extends BasePlugin {
body: [
...inputStateTpl(
'themeCss.inputControlClassName',
'input.base.default'
'--input-default'
)
]
},

View File

@ -134,10 +134,14 @@ export class PickerControlPlugin extends BasePlugin {
panelTitle = '列表选取';
panelBodyCreator = (context: BaseEventContext) => {
const pickStyleStateFunc = (visibleOn: string, state: string) => {
const cssToken =
state === 'default'
? 'base'
: `status-${state === 'focused' ? 'focus' : state}`;
return [
getSchemaTpl('theme:border', {
name: `themeCss.pickControlClassName.border:${state}`,
editorThemePath: `pick.status.body.${state}-border`,
editorValueToken: `--Pick-${cssToken}`,
visibleOn: visibleOn
}),
getSchemaTpl('theme:colorPicker', {
@ -146,43 +150,7 @@ export class PickerControlPlugin extends BasePlugin {
needGradient: true,
needImage: true,
name: `themeCss.pickControlClassName.background:${state}`,
editorThemePath: `pick.status.body.${state}-bgColor`,
visibleOn: visibleOn
})
];
};
const pickDisabledSateFunc = (visibleOn: string, state: string) => {
return [
getSchemaTpl('theme:border', {
name: `themeCss.pickControlDisabledClassName.border`,
editorThemePath: `pick.status.body.${state}-border`,
visibleOn: visibleOn
}),
getSchemaTpl('theme:colorPicker', {
label: '背景',
labelMode: 'input',
needGradient: true,
needImage: true,
name: `themeCss.pickControlDisabledClassName.background`,
editorThemePath: `pick.status.body.${state}-bgColor`,
visibleOn: visibleOn
})
];
};
const pickStyleFunc = (visibleOn: string, state: string) => {
return [
getSchemaTpl('theme:border', {
name: `themeCss.pickControlClassName.border:${state}`,
editorThemePath: `pick.base.body.border`,
visibleOn: visibleOn
}),
getSchemaTpl('theme:colorPicker', {
label: '背景',
labelMode: 'input',
needGradient: true,
needImage: true,
name: `themeCss.pickControlClassName.background:${state}`,
editorThemePath: `pick.base.body.bgColor`,
editorValueToken: `--Pick-${cssToken}-bgColor`,
visibleOn: visibleOn
})
];
@ -494,7 +462,7 @@ export class PickerControlPlugin extends BasePlugin {
},
{
label: '聚焦',
value: 'focus'
value: 'focused'
},
{
label: '禁用',
@ -502,13 +470,16 @@ export class PickerControlPlugin extends BasePlugin {
}
]
},
...pickStyleFunc(
...pickStyleStateFunc(
"${__editorState == 'default' || !__editorState}",
'default'
),
...pickStyleStateFunc("${__editorState == 'hover'}", 'hover'),
...pickStyleStateFunc("${__editorState == 'focus'}", 'active'),
...pickDisabledSateFunc(
...pickStyleStateFunc(
"${__editorState == 'focused'}",
'focused'
),
...pickStyleStateFunc(
"${__editorState == 'disabled'}",
'disabled'
)
@ -519,7 +490,7 @@ export class PickerControlPlugin extends BasePlugin {
body: [
getSchemaTpl('theme:font', {
name: 'themeCss.pickFontClassName.font:default',
editorThemePath: 'pick.base.body.value-font'
editorValueToken: '--Pick-base-value'
}),
getSchemaTpl('theme:colorPicker', {
label: '背景',
@ -527,15 +498,15 @@ export class PickerControlPlugin extends BasePlugin {
needGradient: true,
needImage: true,
name: 'themeCss.pickValueWrapClassName.background',
editorThemePath: 'pick.base.body.value-bgColor'
editorValueToken: '--Pick-base-value-bgColor'
}),
getSchemaTpl('theme:border', {
name: 'themeCss.pickValueWrapClassName.border:default',
editorThemePath: 'pick.base.body.value-border'
editorValueToken: '--Pick-base-value'
}),
getSchemaTpl('theme:radius', {
name: 'themeCss.pickValueWrapClassName.radius',
editorThemePath: 'pick.base.body.value-radius'
editorValueToken: '--Pick-base'
}),
getSchemaTpl('theme:colorPicker', {
label: '图标颜色',
@ -543,7 +514,7 @@ export class PickerControlPlugin extends BasePlugin {
needGradient: true,
needImage: true,
name: 'themeCss.pickValueIconClassName.color',
editorThemePath: 'pick.base.body.value-icon-color'
editorValueToken: '--Pick-base-value-icon-color'
}),
getSchemaTpl('theme:colorPicker', {
label: '图标hover颜色',
@ -551,7 +522,7 @@ export class PickerControlPlugin extends BasePlugin {
needGradient: true,
needImage: true,
name: 'themeCss.pickValueIconClassName.color:hover',
editorThemePath: 'pick.base.body.value-hover-icon-color'
editorValueToken: '--Pick-base-value-hover-icon-color'
})
]
},
@ -568,7 +539,7 @@ export class PickerControlPlugin extends BasePlugin {
// getSchemaTpl('theme:size', {
// name: 'themeCss.pickControlClassName.--Pick-base-icon-size',
// label: '图标大小',
// editorThemePath: `default.body.icon-size`
// editorValueToken: `default.body.icon-size`
// }),
getSchemaTpl('theme:colorPicker', {
label: '颜色',
@ -576,7 +547,7 @@ export class PickerControlPlugin extends BasePlugin {
needGradient: true,
needImage: true,
name: 'themeCss.pickIconClassName.color',
editorThemePath: 'pick.base.body.icon-color'
editorValueToken: '--Pick-base-icon-color'
})
]
},

View File

@ -28,6 +28,7 @@ import type {
BaseEventContext,
EditorManager
} from 'amis-editor-core';
import {inputStateTpl} from '../../renderer/style-control/helper';
export class SelectControlPlugin extends BasePlugin {
static id = 'SelectControlPlugin';
@ -301,7 +302,29 @@ export class SelectControlPlugin extends BasePlugin {
title: '外观',
body: [
getSchemaTpl('collapseGroup', [
getSchemaTpl('style:formItem', {renderer: context.info.renderer}),
getSchemaTpl('theme:formItem'),
getSchemaTpl('theme:form-label'),
getSchemaTpl('theme:form-description'),
getSchemaTpl('theme:form-description'),
{
title: '选择框样式',
body: [
...inputStateTpl('themeCss.selectControlClassName', '--select')
]
},
{
title: '下拉框样式',
body: [
...inputStateTpl(
'themeCss.selectPopoverClassName',
'--select',
{
state: ['default', 'hover', 'focused']
}
)
]
},
getSchemaTpl('theme:cssCode'),
getSchemaTpl('style:classNames')
])
]

View File

@ -6,6 +6,7 @@ import type {BaseEventContext} from 'amis-editor-core';
import {ValidatorTag} from '../../validator';
import {getEventControlConfig} from '../../renderer/event-control/helper';
import {RendererPluginAction, RendererPluginEvent} from 'amis-editor-core';
import {inputStateTpl} from '../../renderer/style-control/helper';
export class TextareaControlPlugin extends BasePlugin {
static id = 'TextareaControlPlugin';
@ -183,8 +184,7 @@ export class TextareaControlPlugin extends BasePlugin {
title: '外观',
body: [
getSchemaTpl('collapseGroup', [
getSchemaTpl('style:formItem', {
renderer: context.info.renderer,
getSchemaTpl('theme:formItem', {
schema: [
{
type: 'input-number',
@ -200,6 +200,18 @@ export class TextareaControlPlugin extends BasePlugin {
}
]
}),
getSchemaTpl('theme:form-label'),
getSchemaTpl('theme:form-description'),
{
title: '多行文本样式',
body: [
...inputStateTpl(
'themeCss.inputControlClassName',
'--input-textarea'
)
]
},
getSchemaTpl('theme:cssCode'),
getSchemaTpl('style:classNames')
])
]

View File

@ -355,20 +355,22 @@ export class ImagePlugin extends BasePlugin {
getSchemaTpl('theme:font', {
label: '标题文字',
name: 'themeCss.titleControlClassName.font',
editorThemePath: 'image.image.default.normal.body.font'
editorValueToken: '--image-image-normal'
}),
getSchemaTpl('theme:paddingAndMargin', {
label: '标题边距',
name: 'themeCss.titleControlClassName.padding-and-margin'
name: 'themeCss.titleControlClassName.padding-and-margin',
editorValueToken: '--image-image-normal-title'
}),
getSchemaTpl('theme:font', {
label: '描述文字',
name: 'themeCss.desControlClassName.font',
editorThemePath: 'image.image.default.description.body.font'
editorValueToken: '--image-image-description'
}),
getSchemaTpl('theme:paddingAndMargin', {
label: '描述边距',
name: 'themeCss.desControlClassName.padding-and-margin'
name: 'themeCss.desControlClassName.padding-and-margin',
editorValueToken: '--image-image-description'
}),
{
name: 'themeCss.iconControlClassName.--image-image-normal-icon',

View File

@ -1000,19 +1000,6 @@ export class Table2Plugin extends BasePlugin {
{
title: '行设置',
body: [
{
name: 'lineHeight',
label: '行高度',
type: 'select',
placeholder: '请选择高度',
options: [
{label: '跟随内容', value: ''},
{label: '高', value: 'large'},
{label: '中', value: 'middle'}
],
clearable: false,
value: ''
},
{
type: 'ae-Switch-More',
mode: 'normal',
@ -1337,31 +1324,31 @@ export class Table2Plugin extends BasePlugin {
{
title: '基本',
body: [
getSchemaTpl('switch', {
name: 'bordered',
label: '边框',
pipeIn: defaultValue(false)
}),
{
name: 'size',
label: '控件尺寸',
type: 'select',
pipeIn: defaultValue('default'),
options: [
{
label: '小',
value: 'small'
},
{
label: '默认',
value: 'default'
},
{
label: '大',
value: 'large'
}
]
},
// getSchemaTpl('switch', {
// name: 'bordered',
// label: '边框',
// pipeIn: defaultValue(false)
// }),
// {
// name: 'size',
// label: '控件尺寸',
// type: 'select',
// pipeIn: defaultValue('default'),
// options: [
// {
// label: '小',
// value: 'small'
// },
// {
// label: '默认',
// value: 'default'
// },
// {
// label: '大',
// value: 'large'
// }
// ]
// },
getSchemaTpl('switch', {
name: 'autoFillHeight',
label: '高度自适应'
@ -1384,20 +1371,10 @@ export class Table2Plugin extends BasePlugin {
]
},
{
type: 'input-group',
type: 'amis-theme-select',
name: 'scroll.y',
visibleOn: 'this.scroll && this.scroll.y !== null',
label: '高度值',
body: [
{
type: 'input-number',
name: 'scroll.y'
},
{
type: 'tpl',
addOnclassName: 'border-0 bg-none',
tpl: 'px'
}
]
label: '高度值'
},
{
@ -1422,21 +1399,10 @@ export class Table2Plugin extends BasePlugin {
]
},
{
type: 'input-group',
visibleOn: 'this.scroll && this.scroll.x !== null',
type: 'amis-theme-select',
name: 'scroll.x',
label: '宽度值',
body: [
{
type: 'input-number',
name: 'scroll.x'
},
{
type: 'tpl',
addOnclassName: 'border-0 bg-none',
tpl: 'px'
}
]
visibleOn: 'this.scroll && this.scroll.x !== null',
label: '宽度值'
},
{
name: 'indentSize',
@ -1464,7 +1430,84 @@ export class Table2Plugin extends BasePlugin {
}
]
},
{
title: '表头',
body: [
getSchemaTpl('theme:colorPicker', {
name: 'themeCss.tableHeadClassname.background',
needCustom: true,
needGradient: true,
needImage: true,
labelMode: 'input',
label: '背景',
editorValueToken: '--table-header-bg-color'
}),
getSchemaTpl('theme:paddingAndMargin', {
name: 'themeCss.tableHeadClassname.paddingAndMargin',
hideMargin: true,
editorValueToken: '--table'
}),
getSchemaTpl('theme:border', {
name: 'themeCss.tableHeadClassname.border',
label: '边框',
editorValueToken: {
'rightBorderColor': '--Table-thead-borderColor',
'rightBorderWidth': '--Table-thead-borderWidth',
'*': '--table-header'
}
}),
getSchemaTpl('theme:font', {
name: 'themeCss.tableHeadClassname.font',
editorValueToken: '--table-header'
})
]
},
getSchemaTpl('theme:base', {
title: '单元格',
classname: 'tableBodyClassname',
editorValueToken: '--table-body',
hideShadow: true,
hideRadius: true,
hideBorder: true,
hidePaddingAndMargin: true,
state: ['default', 'hover'],
extra: [
{
type: 'amis-theme-select',
label: '行高',
name: 'themeCss.tableBodyClassname.height',
editorValueToken: '--table-body-line-height',
mode: 'default'
},
getSchemaTpl('theme:paddingAndMargin', {
name: 'themeCss.tableRowClassname.paddingAndMargin',
hideMargin: true,
editorValueToken: '--table'
}),
getSchemaTpl('theme:border', {
name: 'themeCss.tableRowClassname.border',
editorValueToken: {
'bottomBorderColor': '--Table-borderColor',
'bottomBorderWidth': '--Table-borderWidth',
'*': '--table'
}
}),
getSchemaTpl('theme:font', {
name: 'themeCss.tableBodyClassname.font',
editorValueToken: '--table-body'
})
]
}),
{
title: '自定义样式',
body: [
{
type: 'theme-cssCode',
label: false
}
]
},
getSchemaTpl('style:classNames', {
isFormItem: false,
schema: [

View File

@ -722,17 +722,17 @@ export class TableCell2Plugin extends BasePlugin {
}
].filter(Boolean)
)
},
{
title: '外观',
body: [
getSchemaTpl('className'),
getSchemaTpl('className', {
name: 'innerClassName',
label: '内部 CSS 类名'
})
]
}
// {
// title: '外观',
// body: [
// getSchemaTpl('className'),
// getSchemaTpl('className', {
// name: 'innerClassName',
// label: '内部 CSS 类名'
// })
// ]
// }
]);
};
}

View File

@ -0,0 +1,61 @@
import {FormControlProps, FormItem} from 'amis-core';
import React from 'react';
import cx from 'classnames';
const colCount = [1, 2, 3, 4];
const ColCount: React.FC<FormControlProps> = props => {
const [value, setValue] = React.useState(props.value);
function handleClick(value: number) {
const store = props.manager.store;
const body = [...(props.data.body || [])];
let row = 0;
let count = value;
for (let i = 0; i < body.length; i++) {
// 需要独占一行的组件
if (body[i].$$dragMode === 'hv') {
count = value;
body[i] = {
...body[i],
row: ++row
};
row++;
} else {
count--;
body[i] = {
...body[i],
row,
colSize: value > 1 ? `1/${value}` : '1'
};
if (count === 0) {
row++;
count = value;
}
}
}
props.setValue(body, 'body');
setValue(value);
}
return (
<div className="ColCount">
{colCount.map(n => (
<div
key={n}
className={cx('ColCount-item', value === n && 'is-active')}
onClick={() => handleClick(n)}
>
{n}
</div>
))}
</div>
);
};
@FormItem({type: 'col-count', strictMode: false})
export class ColCountRenderer extends React.Component<FormControlProps> {
render() {
return <ColCount {...this.props} />;
}
}

View File

@ -0,0 +1,154 @@
import {FormControlProps, FormItem} from 'amis-core';
import React from 'react';
import cx from 'classnames';
import {JSONUpdate} from 'amis-editor-core';
const baseColSize = ['1/4', '1/3', '1/2', '2/3', '3/4', '1'];
const colSizeMap: {
[key: number]: string[];
} = {
1: baseColSize,
2: baseColSize,
3: ['1/3', '1/2', '1'],
4: ['1/4', '1']
};
function getColSize(value: string, count: number) {
if (!value.includes('/')) {
return value;
}
const [a, b] = value.split('/').map(n => parseInt(n, 10));
const c = b - a;
if (c % count === 0) {
return `${c / count}/${b}`;
} else {
return `${c}/${b * count}`;
}
}
const ColSize: React.FC<FormControlProps> = props => {
const store = props.manager.store;
const body = [...store.getSchemaParentById(store.activeId)];
const node = store.getNodeById(store.activeId);
const row = props.data.row;
const rowItem = body.filter((item: any) => item.row === row);
const length = rowItem.length;
const parent = store.getNodeById(node.parentId);
const isFlex = parent?.schema?.mode === 'flex';
const value = isFlex ? props.data.colSize : props.data.size;
function handleColSizeChange(value: string) {
if (
!colSizeMap[length].includes(value) ||
node?.schema?.$$dragMode === 'hv'
) {
return;
}
let list = [...body];
if (length > 1) {
if (value === '1') {
// 如果设置为占整行需要调整自身和后面的元素的row
let nodeIndex = list.findIndex((item: any) => item.$$id === node.id);
let hasBefore = false;
list = list.map((item, index) => {
if (item.row === row) {
if (index < nodeIndex) {
hasBefore = true;
}
if (index === nodeIndex && hasBefore) {
item.row += 1;
}
}
if (index > nodeIndex) {
item.row += hasBefore ? 2 : 1;
}
return item;
});
} else {
// 非整行需要调整同行元素的colSize
const colSize = getColSize(value, length - 1);
list = list.map((item: any) => {
if (item.row === row && item.$$id !== node.id) {
item.colSize = colSize;
}
return item;
});
}
}
const schema = JSONUpdate(store.schema, node.parentId, {
[node.parentRegion]: list
});
store.setSchema(schema);
props.setValue(value, 'colSize');
}
function handleSizeChange(value: string) {
props.setValue(value, 'size');
}
return isFlex ? (
<div className="ColSize">
{baseColSize.map(n => (
<div
className={cx(
'ColSize-item',
value === n && 'is-active',
!colSizeMap[length]?.includes(n) && 'is-disabled',
node.schema.$$dragMode === 'hv' && 'is-disabled'
)}
key={n}
onClick={() => handleColSizeChange(n)}
>
{n}
</div>
))}
</div>
) : (
props.render('size', {
label: false,
type: 'amis-theme-select',
name: 'size',
value,
options: [
{
label: '占满',
value: 'full'
},
{
label: '极小',
value: 'xs'
},
{
label: '小',
value: 'sm'
},
{
label: '中',
value: 'md'
},
{
label: '大',
value: 'lg'
},
...(props.data?.sizesOptions?.filter((item: any) => {
return !['var(--sizes-size-0)', 'var(--sizes-size-1)'].includes(
item.value
);
}) || [])
],
onChange: handleSizeChange
})
);
};
@FormItem({type: 'col-size', strictMode: false})
export class ColSizeRenderer extends React.Component<FormControlProps> {
render() {
return <ColSize {...this.props} />;
}
}

View File

@ -0,0 +1,62 @@
import {FormControlProps, FormItem} from 'amis-core';
import {Select} from 'amis-ui';
import React from 'react';
const LabelAlign: React.FC<FormControlProps> = props => {
const store = props.manager.store;
const node = store.getNodeById(store.activeId);
const parent = store.getNodeById(node.parentId);
const parentMode = parent?.schema?.mode;
function handleSizeChange(res: any) {
const value = res.value;
if (!value) {
return;
}
props.onChange(value);
if (parentMode !== 'flex') {
// 历史包袱只能改变mode
if (value === 'inherit') {
props.setValue(undefined, 'mode');
} else if (value !== 'top') {
props.setValue('horizontal', 'mode');
} else {
props.setValue('normal', 'mode');
}
}
}
return (
<Select
className=":LabelAlign"
value={props.value || 'inherit'}
onChange={handleSizeChange}
clearable={false}
options={[
{
label: '继承',
value: 'inherit'
},
{
label: '上下布局',
value: 'top'
},
{
label: '水平居左',
value: 'left'
},
{
label: '水平居右',
value: 'right'
}
]}
/>
);
};
@FormItem({type: 'label-align', strictMode: false})
export class LabelAlignRenderer extends React.Component<FormControlProps> {
render() {
return <LabelAlign {...this.props} />;
}
}

View File

@ -1,10 +1,18 @@
import {getSchemaTpl} from 'amis-editor-core';
export const inputStateTpl = (className: string, path: string = '') => {
export const inputStateTpl = (
className: string,
token: string = '',
options: {
state: string[];
} = {
state: ['default', 'hover', 'focused', 'disabled']
}
) => {
return [
{
type: 'select',
name: '__editorState',
name: `__editorState${className}`,
label: '状态',
selectFirst: true,
options: [
@ -17,19 +25,39 @@ export const inputStateTpl = (className: string, path: string = '') => {
value: 'hover'
},
{
label: '点击',
value: 'active'
label: '选中',
value: 'focused'
},
{
label: '禁用',
value: 'disabled'
}
]
].filter(item => options.state.includes(item.value))
},
...inputStateFunc(
"${__editorState == 'default' || !__editorState}",
`\${__editorState${className} == 'default' || !__editorState${className}}`,
'default',
className,
path
token
),
...inputStateFunc("${__editorState == 'hover'}", 'hover', className, path),
...inputStateFunc("${__editorState == 'active'}", 'active', className, path)
...inputStateFunc(
`\${__editorState${className} == 'hover'}`,
'hover',
className,
token
),
...inputStateFunc(
`\${__editorState${className} == 'focused'}`,
'focused',
className,
token
),
...inputStateFunc(
`\${__editorState${className} == 'disabled'}`,
'disabled',
className,
token
)
];
};
@ -37,15 +65,17 @@ export const inputStateFunc = (
visibleOn: string,
state: string,
className: string,
path: string,
token: string,
options: any = []
) => {
const cssToken = state === 'focused' ? 'active' : state;
return [
getSchemaTpl('theme:font', {
label: '文字',
name: `${className}.font:${state}`,
visibleOn: visibleOn,
editorThemePath: `${path}.${state}.body.font`
editorValueToken: `${token}-${cssToken}`,
state
}),
getSchemaTpl('theme:colorPicker', {
label: '背景',
@ -54,22 +84,26 @@ export const inputStateFunc = (
needGradient: true,
needImage: true,
visibleOn: visibleOn,
editorThemePath: `${path}.${state}.body.bg-color`
editorValueToken: `${token}-${cssToken}-bg-color`,
state
}),
getSchemaTpl('theme:border', {
name: `${className}.border:${state}`,
visibleOn: visibleOn,
editorThemePath: `${path}.${state}.body.border`
editorValueToken: `${token}-${cssToken}`,
state
}),
getSchemaTpl('theme:paddingAndMargin', {
name: `${className}.padding-and-margin:${state}`,
visibleOn: visibleOn,
editorThemePath: `${path}.${state}.body.padding-and-margin`
editorValueToken: `${token}-${cssToken}`,
state
}),
getSchemaTpl('theme:radius', {
name: `${className}.radius:${state}`,
visibleOn: visibleOn,
editorThemePath: `${path}.${state}.body.border`
editorValueToken: `${token}-${cssToken}`,
state
}),
...options
];

View File

@ -118,12 +118,14 @@ setSchemaTpl(
label: '垂直',
value: 'normal'
},
config?.isForm
? null
: {
label: '继承',
value: ''
}
!config?.isForm && {
label: '继承',
value: ''
},
config?.isForm && {
label: '网格',
value: 'flex'
}
].filter(i => i),
pipeOut: (v: string) => (v ? v : undefined)
})
@ -261,6 +263,27 @@ setSchemaTpl('labelHide', () =>
})
);
setSchemaTpl('theme:labelHide', () =>
getSchemaTpl('switch', {
name: '__label',
label: '隐藏标题',
onChange: (value: any, origin: any, item: any, form: any) => {
if (value) {
form.setValueByName(
'$$tempLabel',
form.getValueByName('label') || item.label
);
form.setValueByName('label', false);
} else {
form.setValueByName(
'label',
form.getValueByName('$$tempLabel') || item['$$tempLabel'] || ''
);
}
}
})
);
setSchemaTpl('placeholder', {
label: '占位提示',
name: 'placeholder',

View File

@ -16,28 +16,46 @@ setSchemaTpl('style:formItem', ({renderer, schema}: any) => {
};
});
setSchemaTpl('theme:formItem', ({schema}: any = {}) => {
return {
title: '表单项',
key: 'formItem',
body: [
getSchemaTpl('theme:labelHide'),
{
type: 'col-size',
name: '__size',
label: '宽度'
}
].concat(schema)
};
});
setSchemaTpl(
'style:classNames',
(config: {
schema: SchemaCollection;
isFormItem: boolean;
unsupportStatic?: boolean;
collapsed?: boolean;
}) => {
const {
isFormItem = true,
unsupportStatic = false,
schema = []
schema = [],
collapsed = true
} = config || {};
return {
title: 'CSS 类名',
collapsed,
body: (isFormItem
? [
getSchemaTpl('className', {
label: '表单项'
}),
getSchemaTpl('className', {
label: '标',
label: '标',
name: 'labelClassName'
}),
getSchemaTpl('className', {
@ -441,16 +459,25 @@ setSchemaTpl('theme:cssCode', () => {
// form label
setSchemaTpl('theme:form-label', () => {
return {
title: 'Label样式',
title: '标题样式',
visibleOn: 'this.label !== false',
body: [
{
type: 'label-align',
name: 'labelAlign',
label: '位置'
},
getSchemaTpl('theme:select', {
label: '宽度',
name: 'labelWidth'
name: 'labelWidth',
hiddenOn: 'this.labelAlign == "top"'
}),
getSchemaTpl('theme:font', {
label: '文字',
name: 'themeCss.labelClassName.font:default',
editorThemePath: 'form.item.default.label.body.font'
hasSenior: false,
editorValueToken: '--Form-item'
}),
getSchemaTpl('theme:paddingAndMargin', {
name: 'themeCss.labelClassName.padding-and-margin:default'
@ -468,11 +495,15 @@ setSchemaTpl('theme:form-description', () => {
getSchemaTpl('theme:font', {
label: '文字',
name: 'themeCss.descriptionClassName.font:default',
editorThemePath: 'form.item.default.description.body.font'
editorValueToken: '--Form-description'
}),
getSchemaTpl('theme:paddingAndMargin', {
name: 'themeCss.descriptionClassName.padding-and-margin:default'
})
{
label: '上间距',
type: 'amis-theme-select',
name: 'themeCss.descriptionClassName.margin-top:default',
options: '${sizesOptions}',
editorValueToken: '--Form-description-gap'
}
]
};
});
@ -481,6 +512,7 @@ setSchemaTpl('theme:form-description', () => {
setSchemaTpl('theme:select', (option: any = {}) => {
return {
mode: 'horizontal',
labelAlign: 'left',
type: 'amis-theme-select',
label: '大小',
name: `themeCss.className.select:default`,
@ -579,7 +611,17 @@ setSchemaTpl(
classname?: string;
title?: string;
hiddenOn?: string;
visibleOn?: string;
hidePaddingAndMargin?: boolean;
hideBorder?: boolean;
hideRadius?: boolean;
hideBackground?: boolean;
hideShadow?: boolean;
hideMargin?: boolean;
hidePadding?: boolean;
needState?: boolean;
editorValueToken?: string;
state?: string[];
}) => {
const {
collapsed = false,
@ -587,38 +629,64 @@ setSchemaTpl(
classname = 'baseControlClassName',
title = '基本样式',
hiddenOn,
hidePaddingAndMargin
visibleOn,
hidePaddingAndMargin,
hideBorder,
hideRadius,
hideBackground,
hideShadow,
hideMargin,
hidePadding,
needState = true,
editorValueToken,
state = ['default', 'hover', 'active']
} = option;
const curHidePaddingAndMargin = hidePaddingAndMargin ?? false;
const styleStateFunc = (visibleOn: string, state: string) => {
return [
!curHidePaddingAndMargin
? getSchemaTpl('theme:paddingAndMargin', {
visibleOn: visibleOn,
name: `themeCss.${classname}.padding-and-margin:${state}`
})
: null,
getSchemaTpl('theme:border', {
visibleOn: visibleOn,
name: `themeCss.${classname}.border:${state}`
}),
getSchemaTpl('theme:colorPicker', {
visibleOn: visibleOn,
name: `themeCss.${classname}.background:${state}`,
label: '背景',
needCustom: true,
needGradient: true,
needImage: true,
labelMode: 'input'
}),
getSchemaTpl('theme:radius', {
visibleOn: visibleOn,
name: `themeCss.${classname}.radius:${state}`
}),
getSchemaTpl('theme:shadow', {
visibleOn: visibleOn,
name: `themeCss.${classname}.boxShadow:${state}`
})
!hideBorder &&
getSchemaTpl('theme:border', {
visibleOn: visibleOn,
name: `themeCss.${classname}.border:${state}`,
state,
editorValueToken
}),
!hideRadius &&
getSchemaTpl('theme:radius', {
visibleOn: visibleOn,
name: `themeCss.${classname}.radius:${state}`,
state,
editorValueToken
}),
!hidePaddingAndMargin &&
getSchemaTpl('theme:paddingAndMargin', {
visibleOn: visibleOn,
name: `themeCss.${classname}.padding-and-margin:${state}`,
hideMargin,
hidePadding,
state,
editorValueToken
}),
!hideBackground &&
getSchemaTpl('theme:colorPicker', {
visibleOn: visibleOn,
name: `themeCss.${classname}.background:${state}`,
label: '背景',
needCustom: true,
needGradient: true,
needImage: true,
labelMode: 'input',
state,
editorValueToken: editorValueToken
? `${editorValueToken}-\${__editorState${classname} || 'default'}-bg-color`
: undefined
}),
!hideShadow &&
getSchemaTpl('theme:shadow', {
visibleOn: visibleOn,
name: `themeCss.${classname}.boxShadow:${state}`,
state,
editorValueToken
})
]
.filter(item => item)
.concat(
@ -626,15 +694,19 @@ setSchemaTpl(
return {
...item,
visibleOn: visibleOn,
name: `${item.name}:${state}`
name: `${item.name}:${state}`,
state
};
})
);
};
const styles = [
{
needState && {
type: 'select',
name: '__editorState',
mode: 'horizontal',
labelAlign: 'left',
labelWidth: 80,
name: `__editorState${classname}`,
label: '状态',
selectFirst: true,
options: [
@ -650,21 +722,22 @@ setSchemaTpl(
label: '点击',
value: 'active'
}
]
].filter(item => state.includes(item.value))
},
...styleStateFunc(
"${__editorState == 'default' || !__editorState}",
`\${__editorState${classname} == 'default' || !__editorState${classname}}`,
'default'
),
...styleStateFunc("${__editorState == 'hover'}", 'hover'),
...styleStateFunc("${__editorState == 'active'}", 'active')
];
...styleStateFunc(`\${__editorState${classname} == 'hover'}`, 'hover'),
...styleStateFunc(`\${__editorState${classname} == 'active'}`, 'active')
].filter(Boolean);
return {
title,
collapsed,
body: styles,
hiddenOn
hiddenOn,
visibleOn
};
}
);
@ -734,3 +807,35 @@ setSchemaTpl(
].filter(item => !~exclude.indexOf(item.key || ''));
}
);
setSchemaTpl(
'theme:icon',
(option: {classname?: string; visibleOn?: string; title?: string}) => {
const {
classname = 'iconControlClassName',
visibleOn,
title = '图标样式'
} = option;
return {
title,
visibleOn,
body: [
getSchemaTpl('theme:select', {
label: '图标尺寸',
name: `themeCss.${classname}.iconSize`
}),
getSchemaTpl('theme:colorPicker', {
name: `themeCss.${classname}.color`,
label: '图标颜色',
needCustom: true,
needGradient: true,
labelMode: 'input'
}),
getSchemaTpl('theme:paddingAndMargin', {
label: '图标边距',
name: `themeCss.${classname}.padding-and-margin`
})
]
};
}
);

View File

@ -1,18 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="18px" height="18px" viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="11.1终稿" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="图标" transform="translate(-141.000000, -40.000000)">
<g id="自定义设置" transform="translate(142.000000, 41.000000)">
<g id="编组-28备份">
<rect id="矩形" x="0" y="0" width="16" height="16"></rect>
</g>
<rect id="矩形" fill="#84868C" x="7.11577461" y="2.95100764" width="6.91305466" height="1" rx="0.5"></rect>
<rect id="矩形备份-49" fill="#84868C" x="7.11577461" y="5.98938234" width="5.18768508" height="1" rx="0.5"></rect>
<rect id="矩形备份-51" fill="#84868C" x="7.11577461" y="9.02775704" width="6.91305466" height="1" rx="0.5"></rect>
<rect id="矩形备份-50" fill="#84868C" x="7.11577461" y="12.0661317" width="5.18768508" height="1" rx="0.5"></rect>
<path d="M4.28402126,2.5 L4.28402126,3.98813377 M4.28402126,6.73009787 L4.28402126,8.87389474 M4.28402126,11.9388703 L4.28402126,13.5" id="形状" stroke="#84868C" stroke-linecap="round" stroke-linejoin="bevel"></path>
<path d="M4.25,9.19464196 C4.98767176,9.19464196 5.58567287,9.79264308 5.58567287,10.5303148 C5.58567287,11.2679866 4.98767176,11.8659877 4.25,11.8659877 C3.51232824,11.8659877 2.91432713,11.2679866 2.91432713,10.5303148 C2.91432713,9.79264308 3.51232824,9.19464196 4.25,9.19464196 Z M4.25,4.04216118 C4.98767176,4.04216118 5.58567287,4.6401623 5.58567287,5.37783405 C5.58567287,6.11550581 4.98767176,6.71350693 4.25,6.71350693 C3.51232824,6.71350693 2.91432713,6.11550581 2.91432713,5.37783405 C2.91432713,4.6401623 3.51232824,4.04216118 4.25,4.04216118 Z" id="形状结合" stroke="#84868C" stroke-linejoin="round"></path>
</g>
</g>
</g>
<svg viewBox="0 0 18 18" version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="7.11577461" y="2.95100764" width="6.91305466" height="1"
rx="0.5" fill="currentColor"> </rect>
<rect x="7.11577461" y="5.98938234" width="5.18768508"
height="1" rx="0.5" fill="currentColor"></rect>
<rect x="7.11577461" y="9.02775704" width="6.91305466"
height="1" rx="0.5" fill="currentColor"></rect>
<rect x="7.11577461" y="12.0661317" width="5.18768508"
fill="currentColor"
height="1" rx="0.5"></rect>
<path
d="M4.28402126,2.5 L4.28402126,3.98813377 M4.28402126,6.73009787 L4.28402126,8.87389474 M4.28402126,11.9388703 L4.28402126,13.5"
stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="bevel"></path>
<path
d="M4.25,9.19464196 C4.98767176,9.19464196 5.58567287,9.79264308 5.58567287,10.5303148 C5.58567287,11.2679866 4.98767176,11.8659877 4.25,11.8659877 C3.51232824,11.8659877 2.91432713,11.2679866 2.91432713,10.5303148 C2.91432713,9.79264308 3.51232824,9.19464196 4.25,9.19464196 Z M4.25,4.04216118 C4.98767176,4.04216118 5.58567287,4.6401623 5.58567287,5.37783405 C5.58567287,6.11550581 4.98767176,6.71350693 4.25,6.71350693 C3.51232824,6.71350693 2.91432713,6.11550581 2.91432713,5.37783405 C2.91432713,4.6401623 3.51232824,4.04216118 4.25,4.04216118 Z"
stroke="currentColor" fill="none" stroke-linejoin="round"></path>
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -12,12 +12,7 @@ import {Select} from 'amis-ui';
import ColorPicker from './ColorPicker';
import ThemeSelect from './ThemeSelect';
import {i18n as _i18n} from 'i18n-runtime';
import {
getValueByPath,
getInheritValue,
formatInheritData,
setInheritData
} from '../util';
import {getDefaultValue} from '../util';
interface BorderProps {
custom?: boolean;
@ -104,7 +99,7 @@ function BoxBorder(props: BorderProps & FormControlProps) {
label,
needColorCustom,
state,
editorThemePath
editorValueToken
} = props;
const [borderWidthOptions, setBorderWidthOptions] = useState(
cloneDeep(
@ -122,39 +117,44 @@ function BoxBorder(props: BorderProps & FormControlProps) {
cloneDeep(props.colorOptions || data.colorOptions)
);
const [borderType, setBorderType] = useState<string>('all');
const editorDefaultValue = formatData(getValueByPath(editorThemePath, data));
const editorInheritValue = getInheritValue(editorThemePath, data);
const borderData = formatData(value || {});
useEffect(() => {
if (state && state !== 'default') {
const type = borderType === 'all' ? 'top' : borderType;
const styleOptions = cloneDeep(borderStyleOptions);
if (styleOptions[0].parent) {
styleOptions[0].value = editorThemePath
? 'inherit'
: `var(${data.default.token}${type}-border-style)`;
styleOptions[0].realValue = '继承常规';
} else {
styleOptions.unshift({
label: '继承常规',
value: editorThemePath
? 'inherit'
: `var(${data?.default?.token}${type}-border-style)`,
parent: true,
realValue: '继承常规'
let borderToken: any;
if (editorValueToken) {
borderToken = {
'top-border-color': `${editorValueToken}-top-border-color`,
'top-border-width': `${editorValueToken}-top-border-width`,
'top-border-style': `${editorValueToken}-top-border-style`,
'right-border-color': `${editorValueToken}-right-border-color`,
'right-border-width': `${editorValueToken}-right-border-width`,
'right-border-style': `${editorValueToken}-right-border-style`,
'bottom-border-color': `${editorValueToken}-bottom-border-color`,
'bottom-border-width': `${editorValueToken}-bottom-border-width`,
'bottom-border-style': `${editorValueToken}-bottom-border-style`,
'left-border-color': `${editorValueToken}-left-border-color`,
'left-border-width': `${editorValueToken}-left-border-width`,
'left-border-style': `${editorValueToken}-left-border-style`
};
if (typeof editorValueToken === 'object') {
Object.keys(borderToken).forEach(key => {
// 短横线转驼峰
const tokenKey = key.replace(/-([a-z])/g, function (all, letter) {
return letter.toUpperCase();
});
}
setBorderStyleOptions(styleOptions);
borderToken[key] =
editorValueToken[tokenKey] || `${editorValueToken['*']}-${key}`;
});
}
}, [borderType]);
}
const editorDefaultValue = formatData(getDefaultValue(borderToken, data));
const borderData = formatData(value || {});
function formatData(sourceData: any) {
if (!sourceData) {
return null;
}
const data = formatInheritData(cloneDeep(sourceData));
const data = cloneDeep(sourceData);
const fn = (type: string) => {
if (
@ -174,9 +174,6 @@ function BoxBorder(props: BorderProps & FormControlProps) {
}
function getLabel(value?: string, option?: any) {
if (value === 'inherit') {
return '继承常规';
}
const res = option?.find((item: any) => item.value === value);
if (res) {
return res.label;
@ -222,7 +219,7 @@ function BoxBorder(props: BorderProps & FormControlProps) {
};
}
onChange(setInheritData(changeValue, editorInheritValue));
onChange(changeValue);
};
}
@ -269,7 +266,6 @@ function BoxBorder(props: BorderProps & FormControlProps) {
borderType === 'all' ? 'top' : borderType
}-border-width`}
state={state}
inheritValue={editorThemePath ? 'inherit' : ''}
placeholder={editorDefaultValue?.[getKey('width')] || '边框粗细'}
/>
<div className="Theme-Border-settings-style-color">

View File

@ -28,12 +28,7 @@ import type {GlobalData} from '../helper/getGlobalData';
import cloneDeep from 'lodash/cloneDeep';
import debounce from 'lodash/debounce';
import {i18n as _i18n} from 'i18n-runtime';
import {
getValueByPath,
getInheritValue,
formatInheritData,
setInheritData
} from '../util';
import {getDefaultValue} from '../util';
import {Icon as ThemeIcon} from '../icons/index';
interface ColorPickerProps {
@ -53,7 +48,6 @@ interface ColorPickerProps {
placeholder?: string;
disabled?: boolean;
readOnly?: boolean;
editorInheritValue?: string;
}
interface ColorPickerControlProps extends FormControlProps {
@ -62,7 +56,7 @@ interface ColorPickerControlProps extends FormControlProps {
needImage?: boolean; // 图片
needTheme?: boolean; // 主题色
needCustom?: boolean; // 自定义颜色
editorThemePath?: string;
editorValueToken?: string;
}
interface ColorSelectProps {
@ -112,33 +106,23 @@ function findColor(value: string | undefined, tree: any) {
res = {label: '继承常规', value: value};
}
}
if (value === 'transparent') {
if (value === 'transparent' || value?.includes('none')) {
res = {label: '透明', value: 'transparent'};
}
if (value === 'custom') {
res = {label: '分别配置', value: 'transparent'};
}
if (value === 'inherit') {
res = {label: '继承常规', value: value};
}
return res;
}
function ThemeColorList(props: ThemeColorProps) {
const {themeList, onChange, value, data, itemName, state, editorThemePath} =
props;
const editorInheritValue =
props.editorInheritValue || getInheritValue(editorThemePath, data);
const defaultVar =
state && state !== 'default'
? 'inherit'
: `var(${data?.default?.token}${itemName})`;
const {themeList, onChange, value, data, itemName, state} = props;
const [colorList, setColorList] = useState(cloneDeep(themeList || []));
const [showFlag, setShowFlag] = useState(true);
function setColor(value: string | undefined) {
onChange(setInheritData(value, editorInheritValue));
onChange(value);
}
function searchColor(value: string) {
@ -213,36 +197,6 @@ function ThemeColorList(props: ThemeColorProps) {
</div>
</div>
</TooltipWrapper>
{(data?.state && data.state !== 'default') ||
(state && state !== 'default') ? (
<TooltipWrapper
trigger="hover"
placement="top"
tooltip={{
children: () => <div></div>
}}
>
<div
className={cx(
'ThemeColorList-content-label',
value === defaultVar && 'ThemeColorList-content-label--active'
)}
onClick={() => setColor(defaultVar)}
>
<div
className={cx(
'ThemeColorList-content-label-inner',
'ThemeColor--transparent'
)}
>
<div
className="ThemeColorList-content-label-inner"
style={{background: defaultVar}}
></div>
</div>
</div>
</TooltipWrapper>
) : null}
</div>
<div className="ThemeColorList-content--search">
<SearchBox placeholder="输入色值或名称搜索" onChange={searchColor} />
@ -1472,11 +1426,9 @@ function ColorPicker(props: ColorPickerProps) {
}
function ColorPickerControl(props: ColorPickerControlProps) {
const editorDefaultValue = getValueByPath(
props.editorThemePath || '',
props.data
);
const value = formatInheritData(props.value) || editorDefaultValue;
let editorDefaultValue = getDefaultValue(props.editorValueToken, props.data);
const value = props.value || editorDefaultValue;
return (
<>

View File

@ -14,16 +14,11 @@ import cloneDeep from 'lodash/cloneDeep';
import assign from 'lodash/assign';
import {ThemeWrapperHeader} from './ThemeWrapper';
import ThemeSelect from './ThemeSelect';
import {
getValueByPath,
getInheritValue,
formatInheritData,
setInheritData
} from '../util';
import {getDefaultValue} from '../util';
interface FontEditorProps extends FormControlProps {}
interface SizeDataProps {
interface FontDataProps {
'font-family'?: string;
'fontSize'?: string;
'fontWeight'?: string;
@ -37,7 +32,7 @@ interface SizeDataProps {
interface OptionProps {
label: string;
type: keyof SizeDataProps;
type: keyof FontDataProps;
value: string;
}
@ -921,7 +916,7 @@ function FontEditor(props: FontEditorProps) {
fontWeightOptions = data.fontWeightOptions || defaultFontWeight,
lineHeightOptions = data.lineHeightOptions || defaultlineHeight,
fontFamilyOptions = data.fontFamilyOptions || SYSTEM_FONT_FAMILY,
editorThemePath
editorValueToken
} = props;
const alignOptions = hasVertical
@ -930,24 +925,54 @@ function FontEditor(props: FontEditorProps) {
const [open, toggleOpen] = React.useState(true);
const [senior, toggleSenior] = React.useState(false);
const editorDefaultValue = getValueByPath(editorThemePath, data);
const editorInheritValue = getInheritValue(editorThemePath, data);
const [sizeData, setSizeData] = React.useState<SizeDataProps>(
assign({}, formatInheritData(value))
let fontToken;
if (editorValueToken) {
let color = `${editorValueToken}-color`;
let fontSize = `${editorValueToken}-fontSize`;
let fontWeight = `${editorValueToken}-fontWeight`;
let lineHeight = `${editorValueToken}-lineHeight`;
if (typeof editorValueToken === 'object') {
color = editorValueToken.color || `${editorValueToken['*']}-color`;
fontSize =
editorValueToken.fontSize || `${editorValueToken['*']}-fontSize`;
fontWeight =
editorValueToken.fontWeight || `${editorValueToken['*']}-fontWeight`;
lineHeight =
editorValueToken.lineHeight || `${editorValueToken['*']}-lineHeight`;
}
fontToken = {
color,
fontSize,
fontWeight,
lineHeight
};
}
const editorDefaultValue = getDefaultValue(fontToken, data);
const [fontData, setFontData] = React.useState<FontDataProps>(
assign({}, value)
);
useEffect(() => {
const timer = setTimeout(() => {
if (value) {
setSizeData(assign({}, formatInheritData(value)));
setFontData(assign({}, value));
if (
value['font-style'] ||
value['text-decoration'] ||
value['text-align'] ||
value['vertical-align']
) {
toggleSenior(true);
}
}
}, 100);
return () => clearTimeout(timer);
}, [value]);
function handleEdit(value: string | undefined, type: keyof SizeDataProps) {
const data = cloneDeep(sizeData);
function handleEdit(value: string | undefined, type: keyof FontDataProps) {
const data = cloneDeep(fontData);
if (
[
'font-style',
@ -961,14 +986,11 @@ function FontEditor(props: FontEditorProps) {
} else {
data[type] = value;
}
setSizeData(data);
onChange(setInheritData(data, editorInheritValue));
setFontData(data);
onChange(data);
}
function getLabel(value?: string, option?: any) {
if (value === 'inherit') {
return '继承常规';
}
const res = option?.find((item: any) => item.value === value);
if (res) {
return res.label;
@ -994,7 +1016,7 @@ function FontEditor(props: FontEditorProps) {
<ColorPicker
{...props}
needCustom={needColorCustom ?? false}
value={sizeData.color}
value={fontData.color}
options={colorOptions}
onChange={(value: string) => {
handleEdit(value, 'color');
@ -1002,7 +1024,6 @@ function FontEditor(props: FontEditorProps) {
itemName="color"
state={state}
placeholder={editorDefaultValue?.color || '字体颜色'}
editorInheritValue={editorInheritValue?.color}
/>
</div>
)}
@ -1011,14 +1032,13 @@ function FontEditor(props: FontEditorProps) {
<ThemeSelect
{...props}
options={fontSizeOptions}
value={sizeData['fontSize']}
value={fontData['fontSize']}
onChange={(value: string) => {
handleEdit(value, 'fontSize');
}}
itemName="fontSize"
menuTpl="label"
state={state}
inheritValue={editorThemePath ? 'inherit' : ''}
placeholder={editorDefaultValue?.fontSize || '字体大小'}
/>
</div>
@ -1030,14 +1050,13 @@ function FontEditor(props: FontEditorProps) {
<ThemeSelect
{...props}
options={fontWeightOptions}
value={sizeData['fontWeight']}
value={fontData['fontWeight']}
onChange={(value: string) => {
handleEdit(value, 'fontWeight');
}}
itemName="fontWeight"
menuTpl="label"
state={state}
inheritValue={editorThemePath ? 'inherit' : ''}
placeholder={editorDefaultValue?.fontWeight || '字体字重'}
/>
{(!hideLineHeight || !hideFontFamily) && (
@ -1050,14 +1069,13 @@ function FontEditor(props: FontEditorProps) {
<ThemeSelect
{...props}
options={lineHeightOptions}
value={sizeData['lineHeight']}
value={fontData['lineHeight']}
onChange={(value: string) => {
handleEdit(value, 'lineHeight');
}}
itemName="lineHeight"
menuTpl="label"
state={state}
inheritValue={editorThemePath ? 'inherit' : ''}
placeholder={editorDefaultValue?.lineHeight || '字体行高'}
/>
<div className="Theme-FontEditor-item-label"></div>
@ -1068,14 +1086,14 @@ function FontEditor(props: FontEditorProps) {
<ThemeSelect
{...props}
options={fontFamilyOptions}
value={sizeData['font-family']}
value={fontData['font-family']}
onChange={(value: string) => {
handleEdit(value, 'font-family');
}}
itemName="fontFamily"
menuTpl="label"
state={state}
inheritValue={editorThemePath ? 'inherit' : ''}
placeholder={editorDefaultValue?.fontFamily || '字体'}
/>
<div className="Theme-FontEditor-item-label"></div>
</div>
@ -1092,7 +1110,7 @@ function FontEditor(props: FontEditorProps) {
<div
className={cx(
'Theme-FontEditor-font-style-icon',
sizeData[item.type] === item.value &&
fontData[item.type] === item.value &&
'Theme-FontEditor-font-style-selected'
)}
>
@ -1118,7 +1136,7 @@ function FontEditor(props: FontEditorProps) {
className={cx(
'Theme-FontEditor-font-style-icon',
hasVertical && index === 3 && 'right-line',
sizeData[item.type] === item.value &&
fontData[item.type] === item.value &&
'Theme-FontEditor-font-style-selected'
)}
>

View File

@ -9,12 +9,7 @@ import React, {useEffect, useState} from 'react';
import cx from 'classnames';
import cloneDeep from 'lodash/cloneDeep';
import ThemeSelect from './ThemeSelect';
import {
getValueByPath,
getInheritValue,
formatInheritData,
setInheritData
} from '../util';
import {getDefaultValue} from '../util';
interface PaddingAndMarginProps extends FormControlProps {
custom: boolean;
@ -31,15 +26,13 @@ function PaddingAndMarginDialog(props: PaddingAndMarginProps) {
hideMargin,
hidePadding,
state,
editorThemePath
editorValueToken
} = props;
const [type, setType] = useState('all');
const [customRef, setCustomRef] = useState<Element | null>(null);
const [customShow, setCustomShow] = useState(false);
const [customIndex, setCustomIndex] = useState(0);
const [customKey, setCustomKey] = useState('marginTop');
const [isPaddingInherit, setIsPaddingInherit] = useState<boolean>(false);
const [isMarginInherit, setIsMarginInherit] = useState<boolean>(false);
const LABELS = [
{value: 'marginTop'},
@ -60,8 +53,23 @@ function PaddingAndMarginDialog(props: PaddingAndMarginProps) {
}
});
const editorDefaultValue = formatData(getValueByPath(editorThemePath, data));
const editorInheritValue = getInheritValue(editorThemePath, data);
let paddingAndMarginToken;
if (editorValueToken) {
paddingAndMarginToken = {
paddingTop: `${editorValueToken}-paddingTop`,
paddingBottom: `${editorValueToken}-paddingBottom`,
paddingLeft: `${editorValueToken}-paddingLeft`,
paddingRight: `${editorValueToken}-paddingRight`,
marginTop: `${editorValueToken}-marginTop`,
marginBottom: `${editorValueToken}-marginBottom`,
marginLeft: `${editorValueToken}-marginLeft`,
marginRight: `${editorValueToken}-marginRight`
};
}
const editorDefaultValue = formatData(
getDefaultValue(paddingAndMarginToken, data)
);
const spaceData = formatData(value || {});
const optionsData = options || data.sizesOptions || [];
@ -70,7 +78,7 @@ function PaddingAndMarginDialog(props: PaddingAndMarginProps) {
return null;
}
const data = formatInheritData(cloneDeep(sourceData));
const data = cloneDeep(sourceData);
if (
data?.marginTop === data?.marginRight &&
data?.marginRight === data?.marginBottom &&
@ -102,43 +110,20 @@ function PaddingAndMarginDialog(props: PaddingAndMarginProps) {
}
if (position === 'margin-all') {
if (value?.includes('all')) {
const defaultToken = (key: string) =>
`var(${data.default.token}${key})`;
res.marginTop = defaultToken('marginTop');
res.marginRight = defaultToken('marginRight');
res.marginBottom = defaultToken('marginBottom');
res.marginLeft = defaultToken('marginLeft');
} else {
res.marginTop = value;
res.marginRight = value;
res.marginBottom = value;
res.marginLeft = value;
}
res.marginTop = value;
res.marginRight = value;
res.marginBottom = value;
res.marginLeft = value;
} else if (position === 'padding-all') {
if (value?.includes('all')) {
const defaultToken = (key: string) =>
`var(${data.default.token}${key})`;
res.paddingTop = defaultToken('paddingTop');
res.paddingRight = defaultToken('paddingRight');
res.paddingBottom = defaultToken('paddingBottom');
res.paddingLeft = defaultToken('paddingLeft');
} else {
res.paddingTop = value;
res.paddingRight = value;
res.paddingBottom = value;
res.paddingLeft = value;
}
res.paddingTop = value;
res.paddingRight = value;
res.paddingBottom = value;
res.paddingLeft = value;
} else if (typeof position === 'number') {
const label = LABELS[position].value;
res[label] = value;
if (label.includes('padding')) {
setIsPaddingInherit(false);
} else {
setIsMarginInherit(false);
}
}
onChange(setInheritData(res, editorInheritValue));
onChange(res);
};
}
@ -155,27 +140,6 @@ function PaddingAndMarginDialog(props: PaddingAndMarginProps) {
setCustomKey(key);
}
useEffect(() => {
if (state && state !== 'default' && value) {
if (
value['paddingTop']?.includes('default-paddingTop') &&
value['paddingBottom']?.includes('default-paddingBottom') &&
value['paddingRight']?.includes('default-paddingRight') &&
value['paddingLeft']?.includes('default-paddingLeft')
) {
setIsPaddingInherit(true);
}
if (
value['marginTop']?.includes('default-marginTop') &&
value['marginBottom']?.includes('default-marginBottom') &&
value['marginRight']?.includes('default-marginRight') &&
value['marginLeft']?.includes('default-marginLeft')
) {
setIsMarginInherit(true);
}
}
}, [value]);
useEffect(() => {
if (spaceData.margin === 'custom' || spaceData.padding === 'custom') {
setType('custom');
@ -222,15 +186,10 @@ function PaddingAndMarginDialog(props: PaddingAndMarginProps) {
<ThemeSelect
{...props}
options={optionsData}
value={
isMarginInherit
? `var(${data.default.token}margin-all)`
: spaceData.margin
}
value={spaceData.margin}
onChange={onSpaceChange('margin-all')}
itemName="margin-all"
state={state}
inheritValue={editorThemePath ? 'inherit' : ''}
placeholder={editorDefaultValue?.margin || '外边距'}
/>
<div className="Theme-PaddingAndMargin-input-label"></div>
@ -241,15 +200,10 @@ function PaddingAndMarginDialog(props: PaddingAndMarginProps) {
<ThemeSelect
{...props}
options={optionsData}
value={
isPaddingInherit
? `var(${data.default.token}padding-all)`
: spaceData.padding
}
value={spaceData.padding}
onChange={onSpaceChange('padding-all')}
itemName="padding-all"
state={state}
inheritValue={editorThemePath ? 'inherit' : ''}
placeholder={editorDefaultValue?.padding || '内边距'}
/>
<div className="Theme-PaddingAndMargin-input-label"></div>
@ -298,7 +252,6 @@ function PaddingAndMarginDialog(props: PaddingAndMarginProps) {
onChange={onSpaceChange(customIndex)}
itemName={customKey}
state={state}
inheritValue={editorThemePath ? 'inherit' : ''}
placeholder={editorDefaultValue?.[customKey]}
/>
</div>

View File

@ -9,12 +9,7 @@ import {FormItem, RendererProps, resolveVariableAndFilter} from 'amis-core';
import type {FormControlProps} from 'amis-core';
import cloneDeep from 'lodash/cloneDeep';
import ThemeSelect from './ThemeSelect';
import {
getValueByPath,
getInheritValue,
formatInheritData,
setInheritData
} from '../util';
import {getDefaultValue} from '../util';
interface RadiusProps {
custom: boolean;
@ -48,7 +43,7 @@ function BoxRadius(props: RadiusProps & RendererProps) {
label,
borderRadiusOptions,
state,
editorThemePath
editorValueToken
} = props;
let options = cloneDeep(borderRadiusOptions || data.borderRadiusOptions);
@ -57,10 +52,19 @@ function BoxRadius(props: RadiusProps & RendererProps) {
options = resolveVariableAndFilter(borderRadiusOptions, data, '| raw');
}
const [radiusType, setRadiusType] = useState<string>('all');
const [isInherit, setIsInherit] = useState<boolean>(false);
const editorDefaultValue = formatData(getValueByPath(editorThemePath, data));
const editorInheritValue = getInheritValue(editorThemePath, data);
let radiusToken;
if (editorValueToken) {
radiusToken = {
'top-right-border-radius': `${editorValueToken}-top-right-border-radius`,
'top-left-border-radius': `${editorValueToken}-top-left-border-radius`,
'bottom-right-border-radius': `${editorValueToken}-bottom-right-border-radius`,
'bottom-left-border-radius': `${editorValueToken}-bottom-left-border-radius`
};
}
const editorDefaultValue = formatData(getDefaultValue(radiusToken, data));
const borderData = formatData(value || {});
function formatData(sourceData: any) {
@ -68,7 +72,7 @@ function BoxRadius(props: RadiusProps & RendererProps) {
return null;
}
const data = formatInheritData(cloneDeep(sourceData));
const data = cloneDeep(sourceData);
if (
data[`top-right-border-radius`] === data[`top-left-border-radius`] &&
data[`top-left-border-radius`] === data[`bottom-right-border-radius`] &&
@ -99,17 +103,7 @@ function BoxRadius(props: RadiusProps & RendererProps) {
const items = radiusItems;
items.forEach(item => {
let itemKey = `${item.item}-border-radius`;
if (
state &&
state !== 'default' &&
val?.includes('all-border-radius')
) {
const defaultToken = (key: string) =>
`var(${data.default.token}${key})`;
newValue[itemKey] = defaultToken(itemKey);
} else {
newValue[itemKey] = val;
}
newValue[itemKey] = val;
});
changeValue = {
...value,
@ -121,32 +115,10 @@ function BoxRadius(props: RadiusProps & RendererProps) {
[field]: val
};
}
onChange(setInheritData(changeValue, editorInheritValue));
setIsInherit(false);
onChange(changeValue);
};
}
useEffect(() => {
if (state && state !== 'default') {
if (
value['top-left-border-radius']?.includes(
'default-top-left-border-radius'
) &&
value['top-right-border-radius']?.includes(
'default-top-right-border-radius'
) &&
value['bottom-left-border-radius']?.includes(
'default-bottom-left-border-radius'
) &&
value['bottom-right-border-radius']?.includes(
'default-bottom-right-border-radius'
)
) {
setIsInherit(true);
}
}
}, [value]);
useEffect(() => {
if (borderData['all-border-radius'] === 'custom') {
setRadiusType('custom');
@ -185,17 +157,12 @@ function BoxRadius(props: RadiusProps & RendererProps) {
<ThemeSelect
{...props}
options={options}
value={
isInherit
? `var(${data.default.token}all-border-radius)`
: borderData[getKey('all')]
}
value={borderData[getKey('all')]}
onChange={changeItem('all')}
extraUnit={['px']}
disabled={radiusType === 'custom'}
itemName={'all-border-radius'}
state={state}
inheritValue={editorThemePath ? 'inherit' : ''}
placeholder={editorDefaultValue?.[getKey('all')] || '圆角'}
/>
</div>
@ -217,7 +184,6 @@ function BoxRadius(props: RadiusProps & RendererProps) {
extraUnit={['px']}
itemName={position + '-border-radius'}
state={state}
inheritValue={editorThemePath ? 'inherit' : ''}
placeholder={editorDefaultValue?.[getKey(position)]}
menuTpl="realValue"
/>

View File

@ -16,7 +16,7 @@ import isEqual from 'lodash/isEqual';
import {ThemeWrapperHeader} from './ThemeWrapper';
import ColorPicker from './ColorPicker';
import {Icon as ThemeIcon} from '../icons/index';
import {getValueByPath, getInheritValue, setInheritData} from '../util';
import {getDefaultValue} from '../util';
interface ShadowEditorProps extends FormControlProps {}
@ -81,7 +81,7 @@ function ShadowEditor(props: ShadowEditorProps) {
colorOptions,
state,
itemName,
editorThemePath
editorValueToken
} = props;
const target = React.useRef<HTMLDivElement>(null);
const [open, toggleOpen] = React.useState(true);
@ -107,20 +107,13 @@ function ShadowEditor(props: ShadowEditorProps) {
? [...options, customShadow]
: [...cloneDeep(data.shadowOptions || []), customShadow];
if (state && state !== 'default') {
shadowOptions.unshift({
value: editorThemePath ? 'inherit' : `var(${data.default.token}shadow)`,
label: '继承常规',
realValue: editorThemePath
? ['继承常规']
: [`var(${data.default.token}${itemName})`]
});
let shadowToken;
if (editorValueToken) {
shadowToken = `${editorValueToken}-shadow`;
}
const editorDefaultValue = getValueByPath(editorThemePath, data);
const editorInheritValue = getInheritValue(editorThemePath, data);
const editorDefaultValue = getDefaultValue(shadowToken, data);
const defaultValue = value
? (value.indexOf('inherit:') > -1 && 'inherit') ||
find(cloneDeep(shadowOptions), item => item.value === value) ||
? find(cloneDeep(shadowOptions), item => item.value === value) ||
formateCustomValue(value)
: null;
@ -137,6 +130,20 @@ function ShadowEditor(props: ShadowEditorProps) {
}
}, [defaultValue]);
React.useEffect(() => {
if (shadowData?.value === 'custom') {
toggleSenior(true);
}
}, []);
function getLabel(value?: string, option?: any) {
const res = option?.find((item: any) => item.value === value);
if (res) {
return res.label;
}
return value;
}
function formateCustomValue(value: string) {
const color: PlainObject = {};
let colorIndex = 0;
@ -192,7 +199,7 @@ function ShadowEditor(props: ShadowEditorProps) {
const findItem = find(shadowOptions, item => item.value === value);
if (findItem?.value) {
setShadowData(findItem);
onChange(setInheritData(findItem.value, editorInheritValue));
onChange(findItem.value);
}
} else {
setShadowData(undefined);
@ -303,8 +310,19 @@ function ShadowEditor(props: ShadowEditorProps) {
close={() => {
toggleViewShow(false);
}}
name={shadowData?.label || '无阴影'}
value={formatRealValue(shadowData?.realValue).source}
name={
shadowData?.label ||
getLabel(editorDefaultValue, shadowOptions) ||
'无阴影'
}
value={
formatRealValue(
shadowData?.realValue ||
shadowOptions.find(
item => item.value === editorDefaultValue
)?.realValue
).source
}
/>
<div className="Theme-ShadowEditor-item">
<Select
@ -314,11 +332,13 @@ function ShadowEditor(props: ShadowEditorProps) {
onChange={(res: any) => {
onShadowSelect(res.value);
}}
placeholder={editorDefaultValue || '无阴影'}
placeholder={
getLabel(editorDefaultValue, shadowOptions) || '无阴影'
}
/>
</div>
</div>
{senior || shadowData?.value === 'custom' ? (
{senior ? (
<div className="Theme-ShadowEditor-customContent">
<div className="Theme-Wrapper-header Theme-ShadowEditor-sub-header">
<div className="Theme-Wrapper-header-left"></div>

View File

@ -12,12 +12,7 @@ import cx from 'classnames';
import {Button} from 'amis-ui';
import ThemeSelect from './ThemeSelect';
import {find} from 'lodash';
import {
getValueByPath,
getInheritValue,
formatInheritData,
setInheritData
} from '../util';
import {getDefaultValue} from '../util';
interface SizeEditorProps extends FormControlProps {
hideWidth?: boolean;
@ -34,7 +29,7 @@ function SizeEditor(props: SizeEditorProps) {
hideWidth,
hideHeight,
hideMinWidth,
editorThemePath,
editorValue,
label
} = props;
const [lock, setLockValue] = React.useState(0);
@ -48,13 +43,12 @@ function SizeEditor(props: SizeEditorProps) {
minWidth: ''
});
const RULE = /[0-9\.]*/;
const editorDefaultValue = getValueByPath(editorThemePath, data);
const editorInheritValue = getInheritValue(editorThemePath, data);
const editorDefaultValue = getDefaultValue(editorValue, data);
React.useEffect(() => {
const timer = setTimeout(() => {
if (defaultValue) {
setValue(formatInheritData(defaultValue));
setValue(defaultValue);
}
}, 100);
return () => clearTimeout(timer);
@ -76,7 +70,7 @@ function SizeEditor(props: SizeEditorProps) {
value[key] = sizeFind ? sizeFind.value : valueTemp[key];
}
);
onChange(setInheritData(value, editorInheritValue));
onChange(value);
return;
}
onChange(valueTemp);

View File

@ -10,12 +10,7 @@ import debounce from 'lodash/debounce';
import {FormItem, resolveVariableAndFilter, highlight} from 'amis-core';
import type {FormControlProps} from 'amis-core';
import cx from 'classnames';
import {
getValueByPath,
getInheritValue,
formatInheritData,
setInheritData
} from '../util';
import {getDefaultValue} from '../util';
interface Option {
label: any;
@ -34,10 +29,8 @@ interface ThemeSelectProps {
state?: string;
itemName?: string;
menuTpl?: string;
inheritValue?: string;
placeholder?: string;
editorThemePath?: any;
isEditorTpl?: boolean;
editorValueToken?: string | {[key: string]: string};
}
interface ThemeSelectContentProps extends ThemeSelectProps {
@ -59,19 +52,17 @@ function ThemeSelectContent(props: ThemeSelectContentProps) {
disabled,
menuTpl,
placeholder,
editorThemePath,
data,
isEditorTpl
editorValueToken,
data
} = props;
// 期望value是string类型
const value = String(formatInheritData(props.value));
const value = props.value + '';
const input = useRef<HTMLInputElement>(null);
const [currentItem, setCurrentItem] = useState<Option | undefined>(undefined);
const [options, setOptions] = useState<Option[] | undefined>(originalOptions);
const [showOptions, setShowOptions] = useState(false);
const editorDefaultValue = getValueByPath(editorThemePath, data);
const editorInheritValue = getInheritValue(editorThemePath, data);
const editorDefaultValue = getDefaultValue(editorValueToken, data);
useEffect(() => {
const res = originalOptions?.find(item => item.value === value);
@ -83,9 +74,6 @@ function ThemeSelectContent(props: ThemeSelectContentProps) {
}, [value]);
function getRealValue(value?: string) {
if (value === 'inherit') {
return '继承常规';
}
const res = originalOptions?.find(item => item.value === value);
if (res) {
return res.label;
@ -149,9 +137,7 @@ function ThemeSelectContent(props: ThemeSelectContentProps) {
function valueOnChange(value: string) {
return debounce(() => {
if (value) {
onChange(
isEditorTpl ? setInheritData(value, editorInheritValue) : value
);
onChange(value);
} else {
onChange(undefined);
}
@ -165,9 +151,7 @@ function ThemeSelectContent(props: ThemeSelectContentProps) {
}
function onSelectValue(item: Option) {
onChange(
isEditorTpl ? setInheritData(item.value, editorInheritValue) : item.value
);
onChange(item.value);
setShowOptions(false);
input.current && (input.current.value = item.value);
}
@ -178,15 +162,11 @@ function ThemeSelectContent(props: ThemeSelectContentProps) {
}
}
const tooltipLabel =
value === 'inherit' ? '继承常规' : currentItem?.label || '分别配置';
const tooltipLabel = currentItem?.label || '分别配置';
return (
<>
{currentItem ||
value === 'custom' ||
value?.includes('var') ||
value === 'inherit' ? (
{currentItem || value === 'custom' || value?.includes('var') ? (
<div
onClick={openOptions}
className={cx(
@ -233,9 +213,7 @@ function ThemeSelectContent(props: ThemeSelectContentProps) {
onFocus={openOptions}
ref={input}
disabled={disabled}
placeholder={getRealValue(
isEditorTpl ? editorDefaultValue : placeholder
)}
placeholder={getRealValue(placeholder || editorDefaultValue)}
/>
</div>
)}
@ -279,11 +257,7 @@ function ThemeSelect(props: ThemeSelectProps) {
value: originValue,
options: originOptions,
extraUnit = ['px', 'rem', '%'],
disabled,
state,
itemName,
inheritValue,
isEditorTpl
disabled
} = props;
const themeSelect = useRef<HTMLDivElement>(null);
const [options, setOptions] = React.useState<Option[]>(getOptions());
@ -291,26 +265,10 @@ function ThemeSelect(props: ThemeSelectProps) {
const list = cloneDeep(
typeof originOptions === 'string'
? resolveVariableAndFilter(originOptions, data, '| raw')
: isEditorTpl && !originOptions
: !originOptions
? data?.sizesOptions || []
: originOptions || []
);
if (
state &&
state !== 'default' &&
list[0] &&
list[0].value !== `var(${data?.default?.token}${itemName})`
) {
const name = isEditorTpl
? 'inherit'
: `var(${data?.default?.token}${itemName})`;
list.unshift({
label: '继承常规',
value: inheritValue || name,
realValue: '继承常规'
});
}
return list;
}
useEffect(() => {

View File

@ -45,7 +45,10 @@ export function ThemeWrapperHeader(props: ThemeWrapperProps) {
>
<ThemeIcon
icon="custom"
className="common-icon"
className={cx(
'common-icon',
senior ? 'common-icon--active' : ''
)}
onClick={() => toggleSenior && toggleSenior(!senior)}
/>
</TooltipWrapper>

View File

@ -9,6 +9,7 @@ $ns: cxd-;
padding: 0 px2rem(8px);
display: flex;
align-items: center;
background-color: #fff;
.ThemeSelectContent-theme {
background-color: #f7f8fa;
border-radius: 2px;

View File

@ -7,6 +7,9 @@ $ns: cxd-;
cursor: pointer;
color: #5c5f66;
}
.common-icon--active {
color: #2468f2;
}
&-header {
display: flex;

View File

@ -3912,7 +3912,7 @@ const component: ThemeDefinition['component'] = {
paddingLeft: 'var(--sizes-size-7)',
paddingRight: 'var(--sizes-size-7)'
},
bg: 'var(--colors-neutral-fill-11)',
bg: 'var(--colors-neutral-fill-none)',
border: {
'top-border-color': 'var(--colors-neutral-line-8)',
'top-border-width': 'var(--borders-width-2)',
@ -6006,11 +6006,12 @@ const component: ThemeDefinition['component'] = {
'border-width': 'var(--borders-width-2)',
'border-color': 'var(--colors-neutral-line-8)',
'padding-and-margin': {
paddingTop: 'var(--sizes-size-6)',
paddingTop: 'var(--sizes-size-3)',
paddingRight: 'var(--sizes-size-6)',
paddingBottom: 'var(--sizes-size-6)',
paddingBottom: 'var(--sizes-size-3)',
paddingLeft: 'var(--sizes-size-6)'
},
'line-height': 'var(--sizes-base-20)',
'paddingX': 'var(--sizes-size-7)',
'header-font': {
color: 'var(--colors-neutral-text-2)',

View File

@ -1,64 +1,57 @@
import {PlainObject, filter} from 'amis-core';
import cloneDeep from 'lodash/cloneDeep';
const THEME_CSS_VAR = [
'--colors',
'--sizes',
'--borders',
'--fonts',
'--shadows'
];
function getCssKey(key: string) {
return key?.replace('var(', '').replace(')', '');
}
function isThemeCssVar(key: string) {
// 是否以主题变量开头
return THEME_CSS_VAR.some(item => getCssKey(key)?.startsWith(item));
}
/**
*
*/
export function getValueByPath(path: string | string[], data: any): any {
try {
if (!path || !data) {
return null;
}
let res = {};
const getValue = (p: string) => {
const keys = p.split('.');
let value = cloneDeep(data.themeConfig.component);
let defaultToken = '';
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
const isVar = /\$\{(.*)\}/.exec(key) || [];
if (isVar[1]) {
key = data[isVar[1]] || 'default';
}
if (Array.isArray(value)) {
value = value.find(n => n.type === key);
} else {
if (key === 'hover' || key === 'active' || key === 'disabled') {
defaultToken = value['default'].token;
}
value = value[key];
}
export function getDefaultValue(
editorValue?: string | {[key: string]: string},
data?: any
): any {
if (editorValue) {
if (typeof editorValue === 'string') {
const key = filter(editorValue, data);
const value = data.cssVars[key];
if (!value) {
return value;
}
if (defaultToken && value) {
// 继承default
if (typeof value === 'object') {
for (let key in value) {
if (value[key] === `var(${defaultToken}${key})`) {
value[key] = 'inherit';
}
}
} else {
if (value === `var(${defaultToken}${keys[keys.length - 1]})`) {
value = 'inherit';
}
}
if (isThemeCssVar(value)) {
return value;
} else {
return getDefaultValue(getCssKey(value), data);
}
return value;
};
if (Array.isArray(path)) {
path.forEach(p => {
let value = getValue(p);
if (typeof value === 'string') {
value = {color: value};
}
res = Object.assign(res, value);
});
} else {
res = getValue(path);
const res: PlainObject = {};
Object.keys(editorValue).forEach(k => {
const key = filter(editorValue[k], data);
const value = data.cssVars[key];
if (!value) {
res[k] = value;
}
if (isThemeCssVar(value)) {
res[k] = value;
} else {
res[k] = getDefaultValue(getCssKey(value), data);
}
});
return res;
}
return res;
} catch (e) {
return null;
}
}

View File

@ -1634,7 +1634,6 @@
--Form-item-gap: var(--sizes-base-12);
--Form-item-mobile-gap: var(--sizes-base-4);
--Form-item-color: var(--colors-neutral-text-4);
--Form-item-fontColor: var(--Form-item-color);
--Form-item-fontSize: var(--fonts-size-7);
--Form-item-fontWeight: var(--fonts-weight-6);
--Form-item-lineHeight: var(--fonts-lineHeight-1);
@ -2449,7 +2448,7 @@
--Tabs--chrome-right-border-color: var(--colors-neutral-line-5);
// 面板 Panel
--Panel-bg: var(--colors-neutral-fill-11);
--Panel-bg-color: var(--colors-neutral-fill-11);
--Panel-marginTop: var(--sizes-size-0);
--Panel-marginBottom: var(--sizes-base-10);
--Panel-marginLeft: var(--sizes-size-0);
@ -2476,12 +2475,12 @@
--Panel-borderWidth: var(--Panel-top-border-width)
var(--Panel-right-border-width) var(--Panel-bottom-border-width)
var(--Panel-left-border-width);
--Panel-boxShadow: var(--shadows-shadow-sm);
--Panel-shadow: var(--shadows-shadow-sm);
--Panel-heading-paddingTop: var(--sizes-size-5);
--Panel-heading-paddingBottom: var(--sizes-size-5);
--Panel-heading-paddingLeft: var(--sizes-size-7);
--Panel-heading-paddingRight: var(--sizes-size-7);
--Panel-heading-bg: var(--colors-neutral-fill-10);
--Panel-heading-bg-color: var(--colors-neutral-fill-10);
--Panel-heading-color: var(--colors-neutral-text-3);
--Panel-heading-fontSize: var(--fonts-size-8);
--Panel-heading-fontWeight: var(--fonts-weight-6);
@ -2514,7 +2513,7 @@
--Panel-footer-paddingBottom: var(--sizes-size-5);
--Panel-footer-paddingLeft: var(--sizes-size-7);
--Panel-footer-paddingRight: var(--sizes-size-7);
--Panel-footer-bg: var(--colors-neutral-fill-11);
--Panel-footer-bg-color: var(--colors-neutral-fill-none);
--Panel-footer-top-border-color: var(--colors-neutral-line-8);
--Panel-footer-top-border-width: var(--borders-width-2);
--Panel-footer-top-border-style: var(--borders-style-2);
@ -2529,7 +2528,7 @@
--Panel-footer-left-border-style: var(--borders-style-2);
--Panel-footerBorderRadius: 0 0 var(--Panel-bottom-right-border-radius)
var(--Panel-bottom-left-border-radius);
--Panel-footerBg: var(--Panel-footer-bg);
--Panel-footerBg: var(--Panel-footer-bg-color);
--Panel-footerPadding: var(--Panel-footer-paddingTop)
var(--Panel-footer-paddingRight) var(--Panel-footer-paddingBottom)
var(--Panel-footer-paddingLeft);
@ -3327,7 +3326,7 @@
--image-image-normal-title-marginTop: var(--sizes-size-0);
--image-image-description-color: var(--colors-neutral-text-2);
--image-image-description-fontSize: var(--fonts-size-8);
--image-image-description-marginTop: var(--fonts-size-0);
--image-image-description-marginTop: var(--sizes-size-0);
--image-image-normal-icon: '<svg viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g stroke="none" stroke-width="1" fill="currentColor" fill-rule="evenodd"><g><rect opacity="0" x="0.5" y="0.5" width="15" height="15"></rect><path d="M7.9999,3.0001 C11.9889,3.0001 14.9999,6.8731 14.9999,8.0001 C14.9999,8.8831 11.9889,13.0001 7.9999,13.0001 C3.9609,13.0001 0.9999,8.8831 0.9999,8.0001 C0.9999,6.8731 3.9609,3.0001 7.9999,3.0001 Z M7.9999,4.0001 C4.7329,4.0001 2.2179,7.0861 2.0089,7.9731 C2.2749,8.7711 4.7189,12.0001 7.9999,12.0001 C11.2099,12.0001 13.7339,8.7311 13.9929,7.9631 C13.8069,7.1261 11.2709,4.0001 7.9999,4.0001 Z M7.975,5.879 C9.08,5.879 9.975,6.775 9.975,7.879 C9.975,8.983 9.08,9.879 7.975,9.879 C6.871,9.879 5.975,8.983 5.975,7.879 C5.975,6.775 6.871,5.879 7.975,5.879 Z M7.975,6.879 C7.424,6.879 6.975,7.327 6.975,7.879 C6.975,8.43 7.424,8.879 7.975,8.879 C8.527,8.879 8.975,8.43 8.975,7.879 C8.975,7.327 8.527,6.879 7.975,6.879 Z" ></path></g></g></svg>';
--image-images-item-marginTop: var(--sizes-size-3);
@ -3643,7 +3642,6 @@
--Pick-base-icon-color: #84878c;
--Pick-base-value-hover-icon-color: #b3d7ff;
--Pick-base-value-icon-color: var(--colors-other-5);
--Picker-iconColor: var(--Pick-base-icon-color);
--Picker-onHover-iconColor: var(--icon-onHover-color);
--Picker-tag-height: #{px2rem(24px)};
--Picker-tag-marginBottom: var(--select-multiple-marginBottom);
@ -3659,7 +3657,7 @@
--Pick-status-hover-left-border-color: var(--colors-other-5);
--Pick-status-hover-left-border-width: var(--borders-width-2);
--Pick-status-hover-left-border-style: var(--borders-style-2);
--Pick-status-hover-bgColor: var(--colors-neutral-line-11);
--Pick-status-hover-bgColor: var(--colors-neutral-fill-11);
--Pick-status-focus-top-border-color: var(--colors-other-7);
--Pick-status-focus-top-border-width: var(--borders-width-2);
--Pick-status-focus-top-border-style: var(--borders-style-2);
@ -3673,7 +3671,7 @@
--Pick-status-focus-left-border-width: var(--borders-width-2);
--Pick-status-focus-left-border-style: var(--borders-style-2);
--Pick-status-focus-shadow: var(--shadows-shadow-none);
--Pick-status-focus-bgColor: var(--colors-neutral-line-11);
--Pick-status-focus-bgColor: var(--colors-neutral-fill-11);
--Pick-status-disabled-color: var(--colors-neutral-text-2);
--Pick-status-disabled-fontSize: var(--fonts-size-7);
--Pick-status-disabled-fontWeight: var(--fonts-weight-6);
@ -3689,7 +3687,7 @@
--Pick-status-disabled-left-border-color: var(--colors-neutral-line-8);
--Pick-status-disabled-left-border-width: var(--borders-width-2);
--Pick-status-disabled-left-border-style: var(--borders-style-2);
--Pick-status-disabled-bgColor: var(--colors-neutral-text-10);
--Pick-status-disabled-bgColor: var(--colors-neutral-fill-10);
--Pick-status-disabled-color: var(--colors-neutral-line-6);
--Pick-status-disabled-fontSize: var(--fonts-size-7);
--Pick-status-disabled-fontWeight: var(--fonts-weight-6);
@ -3934,9 +3932,9 @@
--table-border-width: var(--borders-width-2);
--table-border-color: var(--colors-neutral-line-8);
--table-paddingTop: var(--sizes-size-6);
--table-paddingTop: var(--sizes-size-3);
--table-paddingRight: var(--sizes-size-6);
--table-paddingBottom: var(--sizes-size-6);
--table-paddingBottom: var(--sizes-size-3);
--table-paddingLeft: var(--sizes-size-6);
--table-paddingX: var(--sizes-size-7);
--table-header-color: var(--colors-neutral-text-2);
@ -3949,8 +3947,10 @@
--table-body-color: var(--colors-neutral-text-2);
--table-body-fontSize: var(--fonts-size-8);
--table-body-fontWeight: var(--fonts-weight-6);
--table-body-line-height: var(--sizes-base-20);
--table-body-lineHeight: var(--fonts-lineHeight-2);
--table-body-bg-color: var(--colors-neutral-fill-11);
--table-body-default-bg-color: var(--table-body-bg-color);
--table-body-hover-color: var(--colors-neutral-text-2);
--table-body-hover-bg-color: var(--colors-brand-10);
--table-body-hover-border-color: var(--colors-neutral-line-8);
@ -4260,4 +4260,9 @@
--common-popover-border: var(--borders-width-2) solid
var(--colors-neutral-fill-9);
--Form-static-fontSize: var(--fonts-size-7);
--Form-static-color: var(--colors-neutral-text-2);
--Form-static-lineHeight: var(--fonts-lineHeight-2);
--Form-static-fontWeight: var(--fonts-weight-6);
}

View File

@ -611,7 +611,6 @@ $Table-strip-bg: transparent;
--Panel--default-badgeBg: var(--colors-neutral-fill-3);
--Panel--default-badgeColor: var(--colors-neutral-fill-10);
--Picker-iconColor: var(--icon-color);
--Picker-onHover-iconColor: var(--icon-onHover-color);
--PickerColumns-bg: white;
@ -837,7 +836,7 @@ $Table-strip-bg: transparent;
--IconSelect-sm-padding: #{px2rem(6px)};
--IconSelect-base-border-radius: var(--borderRadiusMd);
--IconSelect-border-color: var(--menu-border-color);
--IconSelect-preivew-icon-size: #{px2rem(16px)};
--IconSelect-preview-icon-size: #{px2rem(16px)};
--IconSelect-list-icon-size: #{px2rem(24px)};
--IconSelect-type-font-size: var(--fontSizeSm);
--IconSelect-active-badge-color: var(--colors-neutral-fill-11);

View File

@ -1,7 +1,7 @@
.#{$ns}Panel {
margin: var(--Panel-marginTop) var(--Panel-marginRight)
var(--Panel-marginBottom) var(--Panel-marginLeft);
background: var(--Panel-bg);
background: var(--Panel-bg-color);
border-color: var(--Panel-top-border-color) var(--Panel-right-border-color)
var(--Panel-bottom-border-color) var(--Panel-left-border-color);
border-style: var(--Panel-top-border-style) var(--Panel-right-border-style)
@ -9,7 +9,7 @@
border-width: var(--Panel-borderWidth);
border-radius: var(--Panel-borderRadius);
-webkit-box-shadow: var(--Panel-boxShadow);
box-shadow: var(--Panel-boxShadow);
box-shadow: var(--Panel-shadow);
&-fixedBottom {
position: sticky;
@ -24,7 +24,7 @@
var(--Panel-bottom-border-color) var(--Panel-left-border-color);
> .#{$ns}Panel-heading {
background: var(--Panel-heading-bg);
background: var(--Panel-heading-bg-color);
color: var(--Panel-heading-color);
.badge {

View File

@ -224,6 +224,7 @@
& th,
& td {
text-align: left;
border-color: transparent;
border-bottom: var(--Table-borderWidth) solid var(--Table-borderColor);
&.is-sticky {

View File

@ -193,6 +193,8 @@
> thead > tr {
> th {
height: var(--table-body-line-height);
vertical-align: middle;
background: var(--Table-thead-bg);
padding: var(--table-paddingTop) var(--table-paddingRight)
var(--table-paddingBottom) var(--table-paddingLeft);
@ -259,6 +261,8 @@
> td,
> th {
height: px2rem(40px);
vertical-align: middle;
padding: var(--table-paddingTop) var(--table-paddingRight)
var(--table-paddingBottom) var(--table-paddingLeft);
@ -283,6 +287,16 @@
}
}
.#{$ns}Form-static {
padding: 0;
margin: 0;
min-height: inherit;
line-height: inherit;
font-size: inherit;
color: inherit;
font-weight: inherit;
}
.#{$ns}Table-cell-height-large {
height: var(--TableCell-line-height-large);
line-height: var(--TableCell-line-height-large);

View File

@ -25,6 +25,10 @@
.#{$ns}Form-static {
min-height: var(--Form-input-height);
font-size: var(--Form-static-fontSize);
color: var(--Form-static-color);
line-height: var(--Form-static-lineHeight);
font-weight: var(--Form-static-fontWeight);
&:not(.is-noPaddingY-static) {
padding-top: var(--Form-label-paddingTop);
padding-bottom: var(--Form-label-paddingTop);
@ -57,7 +61,7 @@
margin-bottom: var(--Form-mode-default-labelGap);
position: relative;
font-size: var(--Form-item-fontSize);
color: var(--Form-item-fontColor);
color: var(--Form-item-color);
> span {
position: relative;

View File

@ -51,9 +51,10 @@
&-input {
overflow: hidden;
height: px2rem(32px);
&-area {
min-height: var(--IconSelect-preivew-icon-size);
min-height: var(--IconSelect-preview-icon-size);
display: flex;
align-items: center;
justify-content: flex-start;
@ -83,8 +84,8 @@
&-icon {
&-show {
& > svg {
width: var(--IconSelect-preivew-icon-size);
height: var(--IconSelect-preivew-icon-size);
width: var(--IconSelect-preview-icon-size);
height: var(--IconSelect-preview-icon-size);
margin-right: var(--IconSelect-xs-margin);
top: px2rem(3px);
}
@ -97,6 +98,7 @@
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 12px;
}
&-placeholder {

View File

@ -319,7 +319,7 @@
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.1);
> a {
color: var(--Form-item-fontColor);
color: var(--Form-item-color);
padding: 2px 5px;
cursor: pointer;
font-size: 18px;

View File

@ -120,7 +120,7 @@
&-btn {
cursor: pointer;
color: var(--Picker-iconColor);
color: var(--Pick-base-icon-color);
flex: 30px 0 0;
display: flex;
align-items: center;

View File

@ -185,7 +185,8 @@ export class Textarea extends React.Component<TextAreaProps, TextAreaState> {
maxLength,
showCounter,
clearable,
testIdBuilder
testIdBuilder,
style
} = this.props;
const counter = showCounter ? this.valueToString(value).length : 0;
@ -200,6 +201,7 @@ export class Textarea extends React.Component<TextAreaProps, TextAreaState> {
},
className
)}
style={style}
>
<BaseTextArea
className={cx(`TextareaControl-input`, {

View File

@ -133,7 +133,8 @@ export default class Head extends React.PureComponent<Props> {
onSelectAll,
onFilter,
onResizeMouseDown,
testIdBuilder
testIdBuilder,
className
} = this.props;
const {thColumns, tdColumns} = getBuildColumns(columns);
@ -142,7 +143,7 @@ export default class Head extends React.PureComponent<Props> {
// 获取一行最多th个数
let maxCount = 0;
columns.forEach(cols => {
this.thColumns.forEach(cols => {
if (cols.length > maxCount) {
maxCount = cols.length;
}
@ -174,9 +175,9 @@ export default class Head extends React.PureComponent<Props> {
const selectedKeys = selectedRowKeys.filter((key: number | string) =>
rowKeys.includes(key)
);
let thIndex = 0;
return (
<thead ref={this.domRef} className={cx('Table-thead')}>
<thead ref={this.domRef} className={cx('Table-thead', className)}>
{this.thColumns.map((data, index) => {
return (
<tr key={'th-cell-' + index}>
@ -239,6 +240,7 @@ export default class Head extends React.PureComponent<Props> {
) : null}
{isLeftExpandable && index === 0 ? expandableCell : null}
{data.map((item: any, colIndex: number) => {
thIndex++;
let sort = null;
if (item.sorter) {
sort = (
@ -315,9 +317,7 @@ export default class Head extends React.PureComponent<Props> {
classPrefix={classPrefix}
fixed={item.fixed === true ? 'left' : item.fixed}
className={cx({
'Table-cell-last':
colIndex === maxCount - 1 &&
colIndex === data.length - 1
'Table-cell-last': thIndex === maxCount
})}
depth={item.depth}
col={String(colIndex)}

View File

@ -139,6 +139,9 @@ export interface TableProps extends ThemeProps, LocaleProps, SpinnerExtraProps {
className?: string;
dataSource: Array<any>;
classnames: ClassNamesFn;
headerClassName?: string;
bodyClassname?: string;
rowClassname?: string;
columns: Array<ColumnProps>;
scroll?: ScrollProps;
rowSelection?: RowSelectionProps;
@ -686,7 +689,8 @@ export class Table extends React.PureComponent<TableProps, TableState> {
onSort,
onSelectAll,
onFilter,
testIdBuilder
testIdBuilder,
headerClassName
} = this.props;
const rowSelectionKeyField = this.getRowSelectionKeyField();
@ -721,6 +725,7 @@ export class Table extends React.PureComponent<TableProps, TableState> {
orderBy={this.state.sort?.orderBy}
popOverContainer={this.getPopOverContainer}
classnames={cx}
className={headerClassName}
classPrefix={classPrefix}
onSort={(payload: SortProps, column: ColumnProps) => {
this.setState({
@ -1296,7 +1301,8 @@ export class Table extends React.PureComponent<TableProps, TableState> {
showHeader,
itemActions,
tableLayout,
classnames: cx
classnames: cx,
bodyClassname
} = this.props;
const hasScrollX = scroll && scroll.x;
@ -1326,7 +1332,7 @@ export class Table extends React.PureComponent<TableProps, TableState> {
...tableStyle,
tableLayout: tableLayout === 'fixed' ? 'fixed' : 'auto'
}}
className={cx('Table-table')}
className={cx('Table-table', bodyClassname)}
>
{this.renderColGroup()}
{showHeader ? this.renderHead() : null}
@ -1343,7 +1349,8 @@ export class Table extends React.PureComponent<TableProps, TableState> {
headSummary,
sticky,
showHeader,
classnames: cx
classnames: cx,
headerClassName
} = this.props;
const style = {overflow: 'hidden'};
@ -1354,16 +1361,25 @@ export class Table extends React.PureComponent<TableProps, TableState> {
const tableStyle = {};
if (scroll && (scroll.y || scroll.x)) {
Object.assign(tableStyle, {
width: scroll && scroll.x ? scroll.x + 'px' : '100%'
width:
scroll && scroll.x
? typeof scroll.x === 'number'
? scroll.x + 'px'
: scroll.x
: '100%'
});
}
return (
<div
ref={this.headerDom}
className={cx('Table-header', {
[cx('Table-sticky-holder')]: !!sticky
})}
className={cx(
'Table-header',
{
[cx('Table-sticky-holder')]: !!sticky
},
headerClassName
)}
style={style}
>
<table
@ -1381,7 +1397,7 @@ export class Table extends React.PureComponent<TableProps, TableState> {
}
renderScrollTableBody() {
const {scroll, itemActions, classnames: cx} = this.props;
const {scroll, itemActions, classnames: cx, bodyClassname} = this.props;
const style = {};
const tableStyle = {};
@ -1392,7 +1408,12 @@ export class Table extends React.PureComponent<TableProps, TableState> {
});
Object.assign(tableStyle, {
width: scroll && scroll.x ? scroll.x + 'px' : '100%'
width:
scroll && scroll.x
? typeof scroll.x === 'number'
? scroll.x + 'px'
: scroll.x
: '100%'
});
}
@ -1414,7 +1435,7 @@ export class Table extends React.PureComponent<TableProps, TableState> {
</ItemActionsWrapper>
) : null}
<table
className={cx('Table-table')}
className={cx('Table-table', bodyClassname)}
style={{...tableStyle, tableLayout: 'fixed'}}
>
{this.renderColGroup()}

View File

@ -30,7 +30,7 @@ exports[`1. Renderer:Picker base 1`] = `
class="cxd-Picker cxd-Picker--single is-focused"
>
<div
class="cxd-Picker-input"
class="cxd-Picker-input is-focused"
>
<div
class="cxd-Picker-placeholder"
@ -247,7 +247,7 @@ exports[`1. Renderer:Picker base 2`] = `
class="cxd-Picker cxd-Picker--single is-focused"
>
<div
class="cxd-Picker-input"
class="cxd-Picker-input is-focused"
>
<div
class="cxd-Picker-valueWrap"

View File

@ -1379,7 +1379,8 @@ export default class CRUD2 extends React.Component<CRUD2Props, any> {
type: mode,
columns: mode.startsWith('table')
? store.columns || columns
: undefined
: undefined,
id
},
{
key: 'body',

View File

@ -79,23 +79,36 @@ export default class IconSelectControl extends React.PureComponent<
);
}
getValueBySvg(svg: string | undefined): IconSelectStore.SvgIcon | null {
if (!svg || typeof svg !== 'string') {
getSvgName(value: IconChecked | string) {
if (typeof value === 'string') {
return /data-name="(.*?)"/.exec(value)?.[1] || '';
} else {
return value?.name || value?.id || '';
}
}
getSvgId(value: IconChecked | string) {
if (typeof value === 'string') {
return /data-id="(.*?)"/.exec(value)?.[1] || '';
} else {
return value?.id || '';
}
}
getValueBySvg(
svg: string | IconSelectStore.SvgIcon
): IconSelectStore.SvgIcon | null {
if (!svg) {
return null;
}
let findItem: IconSelectStore.SvgIcon | undefined = undefined;
if (IconSelectStore.svgIcons && IconSelectStore.svgIcons.length) {
for (let i = 0; i < IconSelectStore.svgIcons.length; i++) {
findItem = find(
IconSelectStore.svgIcons[i].children,
i => i.svg === svg
);
if (findItem) {
break;
}
}
if (typeof svg !== 'string') {
return svg;
}
return findItem || {name: svg, id: '', svg: ''};
const svgName = this.getSvgName(svg);
const svgId = this.getSvgId(svg);
return {name: svgName, id: svgId, svg: svg.replace(/'/g, '')};
}
@autobind
@ -124,18 +137,18 @@ export default class IconSelectControl extends React.PureComponent<
clearable
} = this.props;
const iconName = value?.name || value;
const svg = this.getValueBySvg(value);
return (
<div className={cx(`${ns}IconSelectControl-input-area`)}>
<div className={cx(`${ns}IconSelectControl-input-icon-show`)}>
<Icon icon={value} className="icon" />
<Icon icon={svg?.svg} className="icon" />
</div>
<span className={cx(`${ns}IconSelectControl-input-icon-id`)}>
{iconName}
{svg?.name}
</span>
{clearable && !disabled && value ? (
{clearable && !disabled && svg ? (
<a
onClick={this.handleClear}
className={cx(`${ns}IconSelectControl-clear`)}
@ -144,7 +157,7 @@ export default class IconSelectControl extends React.PureComponent<
</a>
) : null}
{(!value && placeholder && (
{(!svg && placeholder && (
<span className={cx(`${ns}IconSelectControl-input-icon-placeholder`)}>
{placeholder}
</span>
@ -191,8 +204,12 @@ export default class IconSelectControl extends React.PureComponent<
handleConfirm() {
const checkedIcon = this.state.tmpCheckIconId;
if (this.props.returnSvg) {
this.props.onChange &&
this.props.onChange((checkedIcon && checkedIcon.svg) || '');
let svg = (checkedIcon && checkedIcon.svg) || '';
svg = svg.replace(
/<svg/,
`<svg data-name="${checkedIcon?.name}" data-id="${checkedIcon?.id}"`
);
this.props.onChange && this.props.onChange(svg);
} else {
this.props.onChange &&
this.props.onChange(

View File

@ -520,6 +520,7 @@ export default class NumberControl extends React.Component<
},
className
)}
style={style}
>
<NumberInput
name={name}
@ -594,11 +595,15 @@ export default class NumberControl extends React.Component<
{
key: 'inputControlClassName',
weights: {
active: {
pre: `inputControlClassName-${id?.replace(
'u:',
''
)}.focused, `
focused: {
pre: `${ns}Number-${
displayMode ? displayMode + '-' : ''
}focused.`
},
disabled: {
pre: `${ns}Number-${
displayMode ? displayMode + '-' : ''
}disabled.`
}
}
}
@ -621,11 +626,16 @@ export default class NumberControl extends React.Component<
hover: {
inner: 'input'
},
active: {
pre: `inputControlClassName-${id?.replace(
'u:',
''
)}.focused, `,
focused: {
pre: `${ns}Number-${
displayMode ? displayMode + '-' : ''
}focused.`,
inner: 'input'
},
disabled: {
pre: `${ns}Number-${
displayMode ? displayMode + '-' : ''
}disabled.`,
inner: 'input'
}
}

View File

@ -1211,7 +1211,7 @@ export default class TextControl extends React.PureComponent<
});
return (
<div className={classNames}>
<div className={classNames} style={style}>
{addOn && addOn.position === 'left' ? addOnDom : null}
{body}
{addOn && addOn.position !== 'left' ? addOnDom : null}
@ -1251,11 +1251,11 @@ export default class TextControl extends React.PureComponent<
{
key: 'inputControlClassName',
weights: {
active: {
pre: `${ns}TextControl.is-focused > .inputControlClassName-${id?.replace(
'u:',
''
)}, `
focused: {
parent: `.${ns}TextControl.is-focused`
},
disabled: {
parent: `.${ns}TextControl.is-disabled`
}
}
}
@ -1278,11 +1278,12 @@ export default class TextControl extends React.PureComponent<
hover: {
inner: 'input'
},
active: {
pre: `${ns}TextControl.is-focused > .inputControlClassName-${id?.replace(
'u:',
''
)}, `,
focused: {
parent: `.${ns}TextControl.is-focused`,
inner: 'input'
},
disabled: {
parent: `.${ns}TextControl.is-disabled`,
inner: 'input'
}
}

View File

@ -770,17 +770,13 @@ export default class PickerControl extends React.PureComponent<
onClick={this.handleClick}
className={cx(
'Picker-input',
disabled && 'is-disabled',
this.state.isFocused && 'is-focused',
setThemeClassName({
...this.props,
name: 'pickControlClassName',
id,
themeCss: themeCss || css
}),
setThemeClassName({
...this.props,
name: 'pickControlDisabledClassName',
id,
themeCss: themeCss || css
})
)}
>
@ -873,24 +869,13 @@ export default class PickerControl extends React.PureComponent<
hover: {
important: true
},
active: {
important: true
focused: {
important: true,
parent: `.${ns}Picker.is-focused >`
},
disabled: {
important: true
}
}
},
{
key: 'pickControlDisabledClassName',
weights: {
default: {
pre: `${ns}Picker.is-disabled> .${setThemeClassName({
...this.props,
name: 'pickControlDisabledClassName',
id,
themeCss: themeCss || css
})}, `
important: true,
parent: `.${ns}Picker.is-disabled >`
}
}
},

View File

@ -16,7 +16,9 @@ import {
createObject,
autobind,
TestIdBuilder,
getVariable
getVariable,
CustomStyle,
setThemeClassName
} from 'amis-core';
import {TransferDropDown, Spinner, Select, SpinnerExtraProps} from 'amis-ui';
import {FormOptionsSchema, SchemaApi} from '../../Schema';
@ -466,6 +468,7 @@ export default class SelectControl extends React.Component<SelectProps, any> {
showInvalidMatch,
options,
className,
popoverClassName,
style,
loading,
value,
@ -489,13 +492,17 @@ export default class SelectControl extends React.Component<SelectProps, any> {
filterOption,
...rest
} = this.props;
const {classPrefix: ns, themeCss} = this.props;
if (noResultsText) {
noResultsText = render('noResultText', noResultsText);
}
return (
<div className={cx(`${classPrefix}SelectControl`, className)}>
<div
className={cx(`${classPrefix}SelectControl`, className)}
style={style}
>
{['table', 'list', 'group', 'tree', 'chained', 'associated'].includes(
selectMode
) ? (
@ -503,6 +510,23 @@ export default class SelectControl extends React.Component<SelectProps, any> {
) : (
<Select
{...rest}
className={cx(
setThemeClassName({
...this.props,
name: 'selectControlClassName',
id,
themeCss: themeCss
})
)}
popoverClassName={cx(
popoverClassName,
setThemeClassName({
...this.props,
name: 'selectPopoverClassName',
id,
themeCss: themeCss
})
)}
mobileUI={mobileUI}
popOverContainer={
mobileUI
@ -535,6 +559,38 @@ export default class SelectControl extends React.Component<SelectProps, any> {
overlay={overlay}
/>
)}
<CustomStyle
{...this.props}
config={{
themeCss: themeCss,
classNames: [
{
key: 'selectControlClassName',
weights: {
focused: {
suf: '.is-opened:not(.is-mobile)'
},
disabled: {
suf: '.is-disabled'
}
}
},
{
key: 'selectPopoverClassName',
weights: {
hover: {
suf: ` .${ns}Select-option.is-highlight`
},
focused: {
inner: `.${ns}Select-option.is-active`
}
}
}
],
id: id
}}
env={env}
/>
</div>
);
}

View File

@ -4,12 +4,15 @@ import {
FormControlProps,
resolveEventData,
autobind,
getVariable
getVariable,
CustomStyle,
setThemeClassName
} from 'amis-core';
import {Textarea} from 'amis-ui';
import type {ListenerAction} from 'amis-core';
import {FormBaseControlSchema} from '../../Schema';
import {supportStatic} from './StaticHoc';
import cx from 'classnames';
/**
* TextArea
@ -193,14 +196,55 @@ export default class TextAreaControl extends React.Component<
@supportStatic()
render() {
const {...rest} = this.props;
const {id, themeCss, env, className, classPrefix: ns} = this.props;
return (
<Textarea
{...rest}
forwardRef={this.inputRef}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
onChange={this.handleChange}
/>
<>
<Textarea
{...rest}
forwardRef={this.inputRef}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
onChange={this.handleChange}
className={cx(
className,
setThemeClassName({
...this.props,
name: 'inputControlClassName',
id,
themeCss: themeCss
})
)}
/>
<CustomStyle
{...this.props}
config={{
themeCss: themeCss,
classNames: [
{
key: 'inputControlClassName',
weights: {
default: {
inner: `.${ns}TextareaControl-input`
},
hover: {
inner: `.${ns}TextareaControl-input`
},
focused: {
suf: '.is-focused',
inner: `.${ns}TextareaControl-input`
},
disabled: {
suf: '.is-disabled',
inner: `.${ns}TextareaControl-input`
}
}
}
],
id: id
}}
env={env}
/>
</>
);
}
}

View File

@ -1,5 +1,6 @@
import React from 'react';
import {
CustomStyle,
RENDERER_TRANSMISSION_OMIT_PROPS,
Renderer,
RendererProps
@ -87,6 +88,13 @@ export interface PanelSchema extends BaseSchema {
*
*/
subFormHorizontal?: FormHorizontal;
/**
* classname
*/
headerControlClassName: string;
bodyControlClassName: string;
actionsControlClassName: string;
}
export interface PanelProps
@ -186,6 +194,10 @@ export default class Panel extends React.Component<PanelProps> {
actionsClassName,
footerClassName,
footerWrapClassName,
headerControlClassName,
headerTitleControlClassName,
bodyControlClassName,
actionsControlClassName,
children,
title,
footer,
@ -207,7 +219,11 @@ export default class Panel extends React.Component<PanelProps> {
footerDoms.push(
<div
key="actions"
className={cx(`Panel-btnToolbar`, actionsClassName || `Panel-footer`)}
className={cx(
`Panel-btnToolbar`,
actionsClassName || `Panel-footer`,
actionsControlClassName
)}
>
{actions}
</div>
@ -215,7 +231,13 @@ export default class Panel extends React.Component<PanelProps> {
footer &&
footerDoms.push(
<div key="footer" className={cx(footerClassName || `Panel-footer`)}>
<div
key="footer"
className={cx(
footerClassName || `Panel-footer`,
actionsControlClassName
)}
>
{render('footer', footer, subProps)}
</div>
);
@ -235,18 +257,30 @@ export default class Panel extends React.Component<PanelProps> {
return (
<div className={cx(`Panel`, className || `Panel--default`)} style={style}>
{header ? (
<div className={cx(headerClassName || `Panel-heading`)}>
<div
className={cx(
headerClassName || `Panel-heading`,
headerControlClassName
)}
>
{render('header', header, subProps)}
</div>
) : title ? (
<div className={cx(headerClassName || `Panel-heading`)}>
<h3 className={cx(`Panel-title`)}>
<div
className={cx(
headerClassName || `Panel-heading`,
headerControlClassName
)}
>
<h3 className={cx(`Panel-title`, headerTitleControlClassName)}>
{render('title', title, subProps)}
</h3>
</div>
) : null}
<div className={bodyClassName || `${ns}Panel-body`}>
<div
className={cx(bodyClassName || `Panel-body`, bodyControlClassName)}
>
{this.renderBody()}
</div>

View File

@ -32,7 +32,9 @@ import {
changedEffect,
evalExpressionWithConditionBuilderAsync,
normalizeApi,
getPropValue
getPropValue,
CustomStyle,
setThemeClassName
} from 'amis-core';
import {Icon, Table, BadgeObject, SpinnerExtraProps} from 'amis-ui';
import type {
@ -929,6 +931,15 @@ export default class Table2 extends React.Component<Table2Props, object> {
[`${column.className}`]: !!column.className,
[`${column.titleClassName}`]: !!column.titleClassName
})}
style={{
justifyContent:
(
{
right: 'flex-end',
center: 'center'
} as any
)[column.align] || 'flex-start'
}}
>
{content}
{remark}
@ -1161,7 +1172,7 @@ export default class Table2 extends React.Component<Table2Props, object> {
@autobind
rowClassName(record: any, rowIndex: number) {
const {rowClassNameExpr, store} = this.props;
const {rowClassNameExpr, store, themeCss, id} = this.props;
const classnames = [];
if (rowClassNameExpr) {
@ -1176,6 +1187,14 @@ export default class Table2 extends React.Component<Table2Props, object> {
if (row?.moved) {
classnames.push('is-moved');
}
classnames.push(
setThemeClassName({
...this.props,
name: 'tableRowClassname',
id,
themeCss
})
);
return classnames.join(' ');
}
@ -1942,6 +1961,8 @@ export default class Table2 extends React.Component<Table2Props, object> {
maxKeepItemSelectionLength,
onRow,
store,
id,
themeCss,
...rest
} = this.props;
@ -1983,6 +2004,18 @@ export default class Table2 extends React.Component<Table2Props, object> {
return (
<Table
{...rest}
headerClassName={setThemeClassName({
...this.props,
name: 'tableHeadClassname',
id,
themeCss
})}
bodyClassname={setThemeClassName({
...this.props,
name: 'tableBodyClassname',
id,
themeCss
})}
onRef={this.getRef}
title={this.renderSchema('title', title, schemaProps)}
footer={this.renderSchema('footer', footer, schemaProps)}
@ -2110,7 +2143,15 @@ export default class Table2 extends React.Component<Table2Props, object> {
}
render() {
const {classnames: cx, style, store} = this.props;
const {
classnames: cx,
style,
store,
themeCss,
wrapperCustomStyle,
id,
env
} = this.props;
this.renderedToolbars = []; // 用来记录哪些 toolbar 已经渲染了
@ -2118,14 +2159,82 @@ export default class Table2 extends React.Component<Table2Props, object> {
return (
<div
className={cx('Table-render-wrapper', {
'Table--unsaved': !!store.modified || !!store.moved
})}
className={cx(
'Table-render-wrapper',
setThemeClassName({
...this.props,
name: 'wrapperCustomStyle',
id,
themeCss: wrapperCustomStyle
}),
{
'Table--unsaved': !!store.modified || !!store.moved
}
)}
style={style}
>
{this.renderActions('header')}
{heading}
{this.renderTable()}
<CustomStyle
{...this.props}
config={{
themeCss,
classNames: [
{
key: 'tableHeadClassname',
weights: {
default: {
inner: `.${cx('Table-table')} > thead > tr > th`,
important: true
}
}
},
{
key: 'tableHeadClassname',
weights: {
default: {
inner: `> tr > th`,
important: true
}
}
},
{
key: 'tableBodyClassname',
weights: {
default: {
inner: `> tbody.${cx('Table-tbody')} > tr td`
},
hover: {
suf: '> tbody > tr',
inner: `td`,
important: true
}
}
},
{
key: 'tableRowClassname',
weights: {
default: {
parent: `.${cx('Table-table')} > tbody.${cx(
'Table-tbody'
)}`,
inner: `td.${cx('Table-cell')}`
},
hover: {
parent: `.${cx('Table-table')} > tbody.${cx(
'Table-tbody'
)}`,
inner: `td.${cx('Table-cell')}`
}
}
}
],
wrapperCustomStyle,
id
}}
env={env}
/>
</div>
);
}