mirror of
https://gitee.com/baidu/amis.git
synced 2024-11-29 18:48:45 +08:00
Feat:主题外观功能优化 (#7988)
* feat: 外观自定义样式改版优化 (#7741) * feat: 外观自定义样式改版优化 * fix: 代码优化 * fix: 代码优化&单测修复 * pref: 去掉language配置文件 --------- Co-authored-by: hongyang03 <hongyang03@baidu.com> * feat: 分割线样式支持可视化配置 * feat: 容器/图片/page外观配置 (#7984) * feat: 容器/图片/page外观配置 * fix: 单测修复 * fix: 单测修复 --------- Co-authored-by: hongyang03 <hongyang03@baidu.com> * 更新快照 --------- Co-authored-by: HongYang <33488114+hy993658052@users.noreply.github.com> Co-authored-by: hongyang03 <hongyang03@baidu.com> Co-authored-by: qinhaoyan <30946345+qinhaoyan@users.noreply.github.com>
This commit is contained in:
parent
df59d92a4d
commit
912cb83c53
@ -32,8 +32,11 @@ order: 42
|
||||
|
||||
## 属性表
|
||||
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
| --------- | -------- | ---------- | ----------------------------------- |
|
||||
| type | `string` | | `"divider"` 指定为 分割线 渲染器 |
|
||||
| className | `string` | | 外层 Dom 的类名 |
|
||||
| lineStyle | `string` | `"dashed"` | 分割线的样式,支持`dashed`和`solid` |
|
||||
| 属性名 | 类型 | 默认值 | 说明 |
|
||||
| --------- | -------- | ------------ | ------------------------------------------ |
|
||||
| type | `string` | | `"divider"` 指定为 分割线 渲染器 |
|
||||
| className | `string` | | 外层 Dom 的类名 |
|
||||
| lineStyle | `string` | `dashed` | 分割线的样式,支持`dashed`和`solid` |
|
||||
| direction | `string` | `horizontal` | 分割线的方向,支持`horizontal`和`vertical` |
|
||||
| color | `string` | | 分割线的颜色 |
|
||||
| rotate | `number` | | 分割线的旋转角度 |
|
||||
|
@ -138,4 +138,4 @@
|
||||
"printBasicPrototype": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,29 @@
|
||||
import {useEffect} from 'react';
|
||||
import type {RendererEnv} from '../env';
|
||||
import type {CustomStyleClassName} from '../utils/style-helper';
|
||||
import {insertCustomStyle} from '../utils/style-helper';
|
||||
import {insertCustomStyle, insertEditCustomStyle} from '../utils/style-helper';
|
||||
|
||||
interface CustomStyleProps {
|
||||
config: {
|
||||
themeCss: any;
|
||||
classNames: CustomStyleClassName[];
|
||||
themeCss?: any;
|
||||
classNames?: CustomStyleClassName[];
|
||||
id?: string;
|
||||
defaultData?: any;
|
||||
wrapperCustomStyle?: any;
|
||||
componentId?: string;
|
||||
};
|
||||
env: RendererEnv;
|
||||
}
|
||||
|
||||
export default function (props: CustomStyleProps) {
|
||||
const {themeCss, classNames, id, defaultData} = props.config;
|
||||
const {
|
||||
themeCss,
|
||||
classNames,
|
||||
id,
|
||||
defaultData,
|
||||
wrapperCustomStyle,
|
||||
componentId
|
||||
} = props.config;
|
||||
useEffect(() => {
|
||||
insertCustomStyle(
|
||||
themeCss,
|
||||
@ -25,5 +34,9 @@ export default function (props: CustomStyleProps) {
|
||||
);
|
||||
}, [props.config.themeCss]);
|
||||
|
||||
useEffect(() => {
|
||||
insertEditCustomStyle(wrapperCustomStyle, componentId);
|
||||
}, [props.config.wrapperCustomStyle]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
@ -954,7 +954,9 @@ export class FormItemWrap extends React.Component<FormItemProps> {
|
||||
mobileUI,
|
||||
translate: __,
|
||||
static: isStatic,
|
||||
staticClassName
|
||||
staticClassName,
|
||||
id,
|
||||
wrapperCustomStyle
|
||||
} = props;
|
||||
|
||||
// 强制不渲染 label 的话
|
||||
@ -980,7 +982,10 @@ export class FormItemWrap extends React.Component<FormItemProps> {
|
||||
[`is-error`]: model && !model.valid,
|
||||
[`is-required`]: required
|
||||
},
|
||||
model?.errClassNames
|
||||
model?.errClassNames,
|
||||
wrapperCustomStyle
|
||||
? `wrapperCustomStyle-${id?.replace('u:', '')}`
|
||||
: ''
|
||||
)}
|
||||
style={style}
|
||||
>
|
||||
@ -1493,6 +1498,7 @@ export class FormItemWrap extends React.Component<FormItemProps> {
|
||||
id,
|
||||
labelClassName,
|
||||
descriptionClassName,
|
||||
wrapperCustomStyle,
|
||||
env
|
||||
} = this.props;
|
||||
const mode = this.props.mode || formMode;
|
||||
@ -1532,22 +1538,15 @@ export class FormItemWrap extends React.Component<FormItemProps> {
|
||||
{
|
||||
key: 'labelClassName',
|
||||
value: labelClassName
|
||||
}
|
||||
],
|
||||
id: id + '-label'
|
||||
}}
|
||||
env={env}
|
||||
/>
|
||||
<CustomStyle
|
||||
config={{
|
||||
themeCss: themeCss || css,
|
||||
classNames: [
|
||||
},
|
||||
{
|
||||
key: 'descriptionClassName',
|
||||
value: descriptionClassName
|
||||
}
|
||||
],
|
||||
id: id + '-description'
|
||||
wrapperCustomStyle,
|
||||
componentId: id,
|
||||
id: id + '-item'
|
||||
}}
|
||||
env={env}
|
||||
/>
|
||||
|
@ -1,6 +1,9 @@
|
||||
import {PlainObject} from '../types';
|
||||
import {uuid} from './helper';
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import isObject from 'lodash/isObject';
|
||||
import map from 'lodash/map';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
|
||||
export const valueMap: PlainObject = {
|
||||
'marginTop': 'margin-top',
|
||||
@ -55,6 +58,10 @@ export function insertStyle(style: string, id: string) {
|
||||
|
||||
// bca-disable-line
|
||||
varStyleTag.innerHTML = style;
|
||||
|
||||
if (!style) {
|
||||
varStyleTag.remove();
|
||||
}
|
||||
}
|
||||
|
||||
export function addStyle(style: string, id: string) {
|
||||
@ -100,7 +107,7 @@ function handleInheritData(statusMap: any, data: any) {
|
||||
|
||||
export function formatStyle(
|
||||
themeCss: any,
|
||||
classNames: {
|
||||
classNames?: {
|
||||
key: string;
|
||||
value?: string;
|
||||
weights?: {
|
||||
@ -113,7 +120,7 @@ export function formatStyle(
|
||||
id?: string,
|
||||
defaultData?: any
|
||||
) {
|
||||
if (!themeCss) {
|
||||
if (!themeCss || !classNames) {
|
||||
return {value: '', origin: []};
|
||||
}
|
||||
const res = [];
|
||||
@ -135,12 +142,11 @@ export function formatStyle(
|
||||
|
||||
list?.forEach(n => {
|
||||
if (
|
||||
/(\S*[C|c]lassName-\S*)/.test(n) &&
|
||||
n.includes('lassName-') &&
|
||||
!!~n.indexOf(
|
||||
id
|
||||
?.replace('u:', '')
|
||||
.replace('-label', '')
|
||||
.replace('-description', '')
|
||||
.replace('-item', '')
|
||||
.replace('-addOn', '')
|
||||
.replace('-icon', '')
|
||||
.replace('-inner', '') || ''
|
||||
@ -263,8 +269,8 @@ export interface CustomStyleClassName {
|
||||
}
|
||||
|
||||
export function insertCustomStyle(
|
||||
themeCss: any,
|
||||
classNames: CustomStyleClassName[],
|
||||
themeCss?: any,
|
||||
classNames?: CustomStyleClassName[],
|
||||
id?: string,
|
||||
defaultData?: any,
|
||||
customStyleClassPrefix?: string
|
||||
@ -300,3 +306,51 @@ export function getValueByPath(path: string, data: any) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 递归处理嵌套的样式,转化成一维对象
|
||||
function traverseStyle(style: any, path: string, result: any) {
|
||||
for (let key in style) {
|
||||
if (style.hasOwnProperty(key)) {
|
||||
if (key === '$$id') {
|
||||
continue;
|
||||
}
|
||||
if (isObject(style[key])) {
|
||||
const nowPath = path ? `${path} ${key}` : key;
|
||||
traverseStyle(style[key], nowPath, result);
|
||||
} else if (path === '') {
|
||||
!result[key] && (result[key] = {});
|
||||
result[key] = style[key];
|
||||
} else {
|
||||
!result[path] && (result[path] = {});
|
||||
result[path][key] = style[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置源码编辑自定义样式
|
||||
*/
|
||||
export function insertEditCustomStyle(customStyle: any, id?: string) {
|
||||
let styles: any = {};
|
||||
traverseStyle(customStyle, '', styles);
|
||||
|
||||
let content = '';
|
||||
if (!isEmpty(styles)) {
|
||||
const className = `wrapperCustomStyle-${id?.replace('u:', '')}`;
|
||||
for (let key in styles) {
|
||||
if (styles.hasOwnProperty(key)) {
|
||||
if (isObject(styles[key])) {
|
||||
const res = map(styles[key], (value, key) => `${key}: ${value};`);
|
||||
content += `\n.${className} ${key} {\n ${res.join('\n ')}\n}`;
|
||||
} else {
|
||||
content += `\n.${className} {\n ${key}: ${styles[key]}\n}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
insertStyle(
|
||||
content,
|
||||
'wrapperCustomStyle-' + (id?.replace('u:', '') || uuid())
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,17 @@
|
||||
.ThemeCssCode {
|
||||
position: relative;
|
||||
&-button {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 12px;
|
||||
z-index: 100;
|
||||
}
|
||||
&-icon {
|
||||
cursor: pointer;
|
||||
color: #5c5f66;
|
||||
&:hover {
|
||||
color: #151b26;
|
||||
}
|
||||
}
|
||||
.is-group {
|
||||
overflow: auto;
|
||||
@ -68,6 +78,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
&-wrap {
|
||||
border: 1px solid #e8e9eb;
|
||||
.cxd-MonacoEditor-placeholder {
|
||||
left: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
.editorPanel-inner {
|
||||
.Theme-FontEditor {
|
||||
|
@ -211,9 +211,9 @@ export function JSONPipeOut(
|
||||
* 如果存在themeCss属性,则给对应的className加上name
|
||||
*/
|
||||
export function addStyleClassName(obj: Schema) {
|
||||
const themeCss = obj.themeCss || obj.css;
|
||||
const themeCss = obj.type === 'page' ? obj.themeCss : obj.themeCss || obj.css;
|
||||
// page暂时不做处理
|
||||
if (!themeCss || obj.type === 'page') {
|
||||
if (!themeCss) {
|
||||
return obj;
|
||||
}
|
||||
let toUpdate: any = {};
|
||||
|
@ -168,7 +168,7 @@ export class ButtonPlugin extends BasePlugin {
|
||||
visibleOn: visibleOn,
|
||||
editorThemePath: `button1.size.\${size}.body.border`
|
||||
}),
|
||||
getSchemaTpl('theme:size', {
|
||||
getSchemaTpl('theme:select', {
|
||||
label: '图标尺寸',
|
||||
name: `themeCss.iconClassName.iconSize:${state}`,
|
||||
visibleOn: visibleOn,
|
||||
@ -364,7 +364,7 @@ export class ButtonPlugin extends BasePlugin {
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '自定义样式',
|
||||
title: '基本样式',
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
|
@ -216,131 +216,140 @@ export class CarouselPlugin extends BasePlugin {
|
||||
},
|
||||
{
|
||||
title: '外观',
|
||||
body: [
|
||||
getSchemaTpl('switch', {
|
||||
name: 'auto',
|
||||
label: '自动轮播',
|
||||
pipeIn: defaultValue(true)
|
||||
}),
|
||||
getSchemaTpl('valueFormula', {
|
||||
rendererSchema: {
|
||||
type: 'input-number'
|
||||
},
|
||||
name: 'interval',
|
||||
label: '动画间隔(ms)',
|
||||
valueType: 'number',
|
||||
pipeIn: defaultValue(5000)
|
||||
}),
|
||||
body: getSchemaTpl('collapseGroup', [
|
||||
{
|
||||
name: 'duration',
|
||||
type: 'input-number',
|
||||
label: '动画时长(ms)',
|
||||
min: 100,
|
||||
step: 10,
|
||||
size: 'sm',
|
||||
pipeIn: defaultValue(500)
|
||||
},
|
||||
{
|
||||
name: 'animation',
|
||||
label: '动画效果',
|
||||
type: 'button-group-select',
|
||||
pipeIn: defaultValue('fade'),
|
||||
options: [
|
||||
title: '基本',
|
||||
body: [
|
||||
getSchemaTpl('switch', {
|
||||
name: 'auto',
|
||||
label: '自动轮播',
|
||||
pipeIn: defaultValue(true)
|
||||
}),
|
||||
getSchemaTpl('valueFormula', {
|
||||
rendererSchema: {
|
||||
type: 'input-number'
|
||||
},
|
||||
name: 'interval',
|
||||
label: '动画间隔(ms)',
|
||||
valueType: 'number',
|
||||
pipeIn: defaultValue(5000)
|
||||
}),
|
||||
{
|
||||
label: 'fade',
|
||||
value: 'fade'
|
||||
name: 'duration',
|
||||
type: 'input-number',
|
||||
label: '动画时长(ms)',
|
||||
min: 100,
|
||||
step: 10,
|
||||
size: 'sm',
|
||||
pipeIn: defaultValue(500)
|
||||
},
|
||||
{
|
||||
label: 'slide',
|
||||
value: 'slide'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'controlsTheme',
|
||||
label: '控制按钮主题',
|
||||
type: 'button-group-select',
|
||||
pipeIn: defaultValue('light'),
|
||||
options: [
|
||||
{
|
||||
label: 'light',
|
||||
value: 'light'
|
||||
name: 'animation',
|
||||
label: '动画效果',
|
||||
type: 'button-group-select',
|
||||
pipeIn: defaultValue('fade'),
|
||||
options: [
|
||||
{
|
||||
label: 'fade',
|
||||
value: 'fade'
|
||||
},
|
||||
{
|
||||
label: 'slide',
|
||||
value: 'slide'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'dark',
|
||||
value: 'dark'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'controls',
|
||||
label: '控制显示',
|
||||
type: 'button-group-select',
|
||||
pipeIn: defaultValue('dots,arrows'),
|
||||
multiple: true,
|
||||
options: [
|
||||
{
|
||||
label: '底部圆点',
|
||||
value: 'dots'
|
||||
name: 'controlsTheme',
|
||||
label: '控制按钮主题',
|
||||
type: 'button-group-select',
|
||||
pipeIn: defaultValue('light'),
|
||||
options: [
|
||||
{
|
||||
label: 'light',
|
||||
value: 'light'
|
||||
},
|
||||
{
|
||||
label: 'dark',
|
||||
value: 'dark'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '左右箭头',
|
||||
value: 'arrows'
|
||||
}
|
||||
]
|
||||
},
|
||||
getSchemaTpl('switch', {
|
||||
name: 'alwaysShowArrow',
|
||||
label: '箭头一直显示',
|
||||
clearValueOnHidden: true,
|
||||
hiddenOn: '!~this.controls.indexOf("arrows")',
|
||||
pipeIn: defaultValue(false)
|
||||
}),
|
||||
{
|
||||
type: 'ae-switch-more',
|
||||
bulk: true,
|
||||
mode: 'normal',
|
||||
name: 'multiple',
|
||||
label: '多图展示',
|
||||
formType: 'extend',
|
||||
form: {
|
||||
body: [
|
||||
{
|
||||
name: 'multiple.count',
|
||||
label: '数量',
|
||||
type: 'input-number',
|
||||
min: 2,
|
||||
step: 1
|
||||
name: 'controls',
|
||||
label: '控制显示',
|
||||
type: 'button-group-select',
|
||||
pipeIn: defaultValue('dots,arrows'),
|
||||
multiple: true,
|
||||
options: [
|
||||
{
|
||||
label: '底部圆点',
|
||||
value: 'dots'
|
||||
},
|
||||
{
|
||||
label: '左右箭头',
|
||||
value: 'arrows'
|
||||
}
|
||||
]
|
||||
},
|
||||
getSchemaTpl('switch', {
|
||||
name: 'alwaysShowArrow',
|
||||
label: '箭头一直显示',
|
||||
clearValueOnHidden: true,
|
||||
hiddenOn: '!~this.controls.indexOf("arrows")',
|
||||
pipeIn: defaultValue(false)
|
||||
}),
|
||||
{
|
||||
type: 'ae-switch-more',
|
||||
bulk: true,
|
||||
mode: 'normal',
|
||||
name: 'multiple',
|
||||
label: '多图展示',
|
||||
formType: 'extend',
|
||||
form: {
|
||||
body: [
|
||||
{
|
||||
name: 'multiple.count',
|
||||
label: '数量',
|
||||
type: 'input-number',
|
||||
min: 2,
|
||||
step: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'width',
|
||||
type: 'input-text',
|
||||
label: '宽度',
|
||||
validations: 'isNumeric',
|
||||
addOn: {
|
||||
type: 'button',
|
||||
label: 'px'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'height',
|
||||
type: 'input-text',
|
||||
label: '高度',
|
||||
validations: 'isNumeric',
|
||||
addOn: {
|
||||
type: 'button',
|
||||
label: 'px'
|
||||
}
|
||||
},
|
||||
getSchemaTpl('className')
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'width',
|
||||
type: 'input-text',
|
||||
label: '宽度',
|
||||
validations: 'isNumeric',
|
||||
addOn: {
|
||||
type: 'button',
|
||||
label: 'px'
|
||||
}
|
||||
title: '显隐',
|
||||
body: [getSchemaTpl('ref'), getSchemaTpl('visible')]
|
||||
},
|
||||
{
|
||||
name: 'height',
|
||||
type: 'input-text',
|
||||
label: '高度',
|
||||
validations: 'isNumeric',
|
||||
addOn: {
|
||||
type: 'button',
|
||||
label: 'px'
|
||||
}
|
||||
},
|
||||
getSchemaTpl('className')
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '显隐',
|
||||
body: [getSchemaTpl('ref'), getSchemaTpl('visible')]
|
||||
getSchemaTpl('theme:base', {
|
||||
title: '轮播图'
|
||||
}),
|
||||
getSchemaTpl('theme:cssCode')
|
||||
])
|
||||
}
|
||||
])
|
||||
];
|
||||
|
@ -30,14 +30,92 @@ export class DividerPlugin extends BasePlugin {
|
||||
panelBody = getSchemaTpl('tabs', [
|
||||
{
|
||||
title: '外观',
|
||||
body: [
|
||||
getSchemaTpl('layout:originPosition', {value: 'left-top'}),
|
||||
getSchemaTpl('layout:width:v2', {
|
||||
visibleOn:
|
||||
'data.style && data.style.position && (data.style.position === "fixed" || data.style.position === "absolute")'
|
||||
}),
|
||||
getSchemaTpl('className')
|
||||
]
|
||||
body: getSchemaTpl('collapseGroup', [
|
||||
{
|
||||
title: '基本样式',
|
||||
body: [
|
||||
getSchemaTpl('layout:originPosition', {value: 'left-top'}),
|
||||
getSchemaTpl('layout:width:v2', {
|
||||
visibleOn:
|
||||
'data.style && data.style.position && (data.style.position === "fixed" || data.style.position === "absolute")'
|
||||
}),
|
||||
{
|
||||
mode: 'horizontal',
|
||||
type: 'button-group-select',
|
||||
label: '类型',
|
||||
name: 'lineStyle',
|
||||
value: 'dashed',
|
||||
options: [
|
||||
{
|
||||
value: 'dashed',
|
||||
label: '虚线'
|
||||
},
|
||||
{
|
||||
value: 'solid',
|
||||
label: '实线'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
mode: 'horizontal',
|
||||
type: 'button-group-select',
|
||||
label: '方向',
|
||||
name: 'direction',
|
||||
value: 'horizontal',
|
||||
options: [
|
||||
{
|
||||
value: 'horizontal',
|
||||
label: '水平'
|
||||
},
|
||||
{
|
||||
value: 'vertical',
|
||||
label: '垂直'
|
||||
}
|
||||
]
|
||||
},
|
||||
getSchemaTpl('theme:select', {
|
||||
mode: 'horizontal',
|
||||
label: '长度',
|
||||
name: 'style.width',
|
||||
placeholder: '100%',
|
||||
visibleOn: 'direction !== "vertical"'
|
||||
}),
|
||||
getSchemaTpl('theme:select', {
|
||||
mode: 'horizontal',
|
||||
label: '长度',
|
||||
name: 'style.height',
|
||||
placeholder: 'var(--sizes-base-15)',
|
||||
visibleOn: 'direction === "vertical"'
|
||||
}),
|
||||
getSchemaTpl('theme:select', {
|
||||
mode: 'horizontal',
|
||||
label: '宽度',
|
||||
name: 'style.borderWidth',
|
||||
placeholder: '1px'
|
||||
}),
|
||||
|
||||
getSchemaTpl('theme:colorPicker', {
|
||||
mode: 'horizontal',
|
||||
label: '颜色',
|
||||
name: 'color',
|
||||
placeholder: 'var(--colors-neutral-line-8)',
|
||||
labelMode: 'input',
|
||||
needGradient: true
|
||||
}),
|
||||
getSchemaTpl('theme:paddingAndMargin', {
|
||||
name: 'style',
|
||||
hidePadding: true
|
||||
}),
|
||||
{
|
||||
mode: 'horizontal',
|
||||
type: 'input-number',
|
||||
label: '角度',
|
||||
name: 'rotate',
|
||||
value: 0
|
||||
}
|
||||
]
|
||||
}
|
||||
])
|
||||
},
|
||||
{
|
||||
title: '显隐',
|
||||
|
@ -459,7 +459,7 @@ export class ImageControlPlugin extends BasePlugin {
|
||||
[
|
||||
getSchemaTpl('style:formItem', {renderer: context.info.renderer}),
|
||||
{
|
||||
title: '自定义样式',
|
||||
title: '基本样式',
|
||||
body: [
|
||||
{
|
||||
type: 'select',
|
||||
@ -498,12 +498,12 @@ export class ImageControlPlugin extends BasePlugin {
|
||||
type: 'icon-select',
|
||||
returnSvg: true
|
||||
},
|
||||
getSchemaTpl('theme:size', {
|
||||
getSchemaTpl('theme:select', {
|
||||
name: `${IconCssClassName}.font-size`,
|
||||
label: '图标大小',
|
||||
editorThemePath: `${editorPath}.default.body.icon-size`
|
||||
}),
|
||||
getSchemaTpl('theme:size', {
|
||||
getSchemaTpl('theme:select', {
|
||||
name: `${IconCssClassName}.margin-bottom`,
|
||||
label: '图标底边距',
|
||||
editorThemePath: `${editorPath}.default.body.icon-margin`
|
||||
|
@ -126,9 +126,9 @@ export class IconPlugin extends BasePlugin {
|
||||
title: '外观',
|
||||
body: getSchemaTpl('collapseGroup', [
|
||||
{
|
||||
title: '自定义样式',
|
||||
title: '基本样式',
|
||||
body: [
|
||||
getSchemaTpl('theme:size', {
|
||||
getSchemaTpl('theme:select', {
|
||||
label: '尺寸',
|
||||
name: 'themeCss.className.font.fontSize'
|
||||
}),
|
||||
|
@ -139,11 +139,17 @@ export class ImagePlugin extends BasePlugin {
|
||||
// name: 'showDimensions',
|
||||
// label: '显示图片尺寸'
|
||||
// }),
|
||||
getSchemaTpl('layout:display', {
|
||||
flexHide: true,
|
||||
value: 'inline-block',
|
||||
label: '显示类型'
|
||||
}),
|
||||
|
||||
{
|
||||
name: 'thumbMode',
|
||||
visibleOn: 'imageMode === "thumb"',
|
||||
type: 'select',
|
||||
label: '缩略图展示模式',
|
||||
label: '展示模式',
|
||||
mode: 'horizontal',
|
||||
labelAlign: 'left',
|
||||
horizontal: {
|
||||
@ -173,31 +179,42 @@ export class ImagePlugin extends BasePlugin {
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
getSchemaTpl('theme:size', {
|
||||
label: '尺寸',
|
||||
name: 'themeCss.imageControlClassName.size:default'
|
||||
})
|
||||
]
|
||||
},
|
||||
getSchemaTpl('theme:base', {
|
||||
classname: 'imageControlClassName',
|
||||
title: '图片'
|
||||
}),
|
||||
{
|
||||
title: '其他',
|
||||
body: [
|
||||
getSchemaTpl('theme:font', {
|
||||
label: '标题文字',
|
||||
name: 'themeCss.titleControlClassName.font:default',
|
||||
editorThemePath: 'image.image.default.normal.body.font'
|
||||
}),
|
||||
getSchemaTpl('theme:paddingAndMargin', {
|
||||
label: '标题边距',
|
||||
name: 'themeCss.titleControlClassName.padding-and-margin:default'
|
||||
}),
|
||||
getSchemaTpl('theme:paddingAndMargin', {
|
||||
label: '描述边距',
|
||||
name: 'themeCss.desControlClassName.padding-and-margin:default'
|
||||
}),
|
||||
{
|
||||
name: 'thumbRatio',
|
||||
type: 'button-group-select',
|
||||
label: '缩略图比率',
|
||||
size: 'sm',
|
||||
pipeIn: defaultValue('1:1'),
|
||||
options: [
|
||||
{
|
||||
label: '1:1',
|
||||
value: '1:1'
|
||||
},
|
||||
|
||||
{
|
||||
label: '4:3',
|
||||
value: '4:3'
|
||||
},
|
||||
|
||||
{
|
||||
label: '16:9',
|
||||
value: '16:9'
|
||||
}
|
||||
]
|
||||
name: 'themeCss.iconControlClassName.--image-image-normal-icon',
|
||||
label: '放大图标',
|
||||
type: 'icon-select',
|
||||
returnSvg: true
|
||||
}
|
||||
]
|
||||
},
|
||||
getSchemaTpl('theme:cssCode'),
|
||||
{
|
||||
title: 'CSS类名',
|
||||
body: [
|
||||
|
@ -211,6 +211,11 @@ export class ImagesPlugin extends BasePlugin {
|
||||
}
|
||||
]
|
||||
},
|
||||
getSchemaTpl('theme:base', {
|
||||
classname: 'imagesControlClassName',
|
||||
title: '图片集'
|
||||
}),
|
||||
getSchemaTpl('theme:cssCode'),
|
||||
{
|
||||
title: 'CSS类名',
|
||||
body: [
|
||||
|
@ -293,7 +293,31 @@ export class PagePlugin extends BasePlugin {
|
||||
className: 'p-none',
|
||||
body: [
|
||||
getSchemaTpl('collapseGroup', [
|
||||
...getSchemaTpl('theme:common', {exclude: ['layout']})
|
||||
...getSchemaTpl('theme:common', {
|
||||
exclude: ['layout'],
|
||||
classname: 'bodyControlClassName',
|
||||
baseTitle: '内容区样式',
|
||||
extra: [
|
||||
getSchemaTpl('theme:base', {
|
||||
classname: 'headerControlClassName',
|
||||
title: '标题栏样式',
|
||||
extra: [
|
||||
getSchemaTpl('theme:font', {
|
||||
label: '文字',
|
||||
name: 'font'
|
||||
})
|
||||
]
|
||||
}),
|
||||
getSchemaTpl('theme:base', {
|
||||
classname: 'toolbarControlClassName',
|
||||
title: '工具栏样式'
|
||||
}),
|
||||
getSchemaTpl('theme:base', {
|
||||
classname: 'asideControlClassName',
|
||||
title: '边栏样式'
|
||||
})
|
||||
]
|
||||
})
|
||||
])
|
||||
]
|
||||
},
|
||||
|
@ -265,7 +265,20 @@ export class TooltipWrapperPlugin extends BasePlugin {
|
||||
title: '外观',
|
||||
className: 'p-none',
|
||||
body: getSchemaTpl('collapseGroup', [
|
||||
...getSchemaTpl('style:common'),
|
||||
...getSchemaTpl('theme:common', {
|
||||
layoutExtra: [
|
||||
getSchemaTpl('theme:size', {
|
||||
label: '尺寸',
|
||||
name: 'themeCss.baseControlClassName.size:default'
|
||||
})
|
||||
],
|
||||
extra: [
|
||||
getSchemaTpl('theme:base', {
|
||||
classname: 'tooltipControlClassName',
|
||||
title: '浮层样式'
|
||||
})
|
||||
]
|
||||
}),
|
||||
{
|
||||
title: 'CSS 类名',
|
||||
body: [
|
||||
|
@ -242,7 +242,12 @@ export class TplPlugin extends BasePlugin {
|
||||
body: getSchemaTpl('collapseGroup', [
|
||||
...getSchemaTpl('theme:common', {
|
||||
exclude: ['layout'],
|
||||
include: ['font']
|
||||
baseExtra: [
|
||||
getSchemaTpl('theme:font', {
|
||||
label: '文字',
|
||||
name: 'font'
|
||||
})
|
||||
]
|
||||
})
|
||||
])
|
||||
},
|
||||
|
@ -2,366 +2,118 @@
|
||||
* 类名输入框 + 自定义样式源码编辑器
|
||||
*/
|
||||
import React, {useEffect, useRef, useState} from 'react';
|
||||
import {Button, Editor, Overlay, PopOver} from 'amis-ui';
|
||||
import {FormControlProps, FormItem, styleMap} from 'amis-core';
|
||||
import {Editor, Overlay, PopOver} from 'amis-ui';
|
||||
import {FormControlProps, FormItem} from 'amis-core';
|
||||
// @ts-ignore
|
||||
import {parse as cssParse} from 'amis-postcss';
|
||||
import {PlainObject} from './types';
|
||||
import isObject from 'lodash/isObject';
|
||||
import debounce from 'lodash/debounce';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import {Icon} from '../../icons/index';
|
||||
import editorFactory from './themeLanguage';
|
||||
import cx from 'classnames';
|
||||
|
||||
const valueMap: PlainObject = {};
|
||||
|
||||
for (let key in styleMap) {
|
||||
valueMap[styleMap[key]] = key;
|
||||
const editorPlaceholder = `自定义样式仅对当前组件生效。示例:
|
||||
.text-color: {
|
||||
color: #fff;
|
||||
}
|
||||
const fontStyle = [
|
||||
'color',
|
||||
'font-weight',
|
||||
'font-size',
|
||||
'font-style',
|
||||
'text-decoration',
|
||||
'text-align',
|
||||
'vertical-align',
|
||||
'font-family',
|
||||
'line-height'
|
||||
];
|
||||
`;
|
||||
|
||||
interface CssNode {
|
||||
value: string;
|
||||
selector: string;
|
||||
}
|
||||
|
||||
interface CssNodeTab {
|
||||
name: string;
|
||||
children: CssNode[];
|
||||
}
|
||||
|
||||
function AmisThemeCssCodeEditor(props: FormControlProps) {
|
||||
const {themeClass, data} = props;
|
||||
const id = data.id.replace('u:', '');
|
||||
const [cssNodes, setCssNodes] = useState<CssNodeTab[]>([]);
|
||||
const [tabId, setTabId] = useState(0);
|
||||
function getCssAndSetValue(themeClass: any[]) {
|
||||
try {
|
||||
const newCssNodes: CssNodeTab[] = [];
|
||||
themeClass?.forEach(n => {
|
||||
const classId = n.value ? id + '-' + n.value : id;
|
||||
const state = n.state || ['default'];
|
||||
const className = n.className || 'className';
|
||||
const dom = document.getElementById(classId || '') || null;
|
||||
const content = dom?.innerHTML || '';
|
||||
const ast = cssParse(content);
|
||||
const nodes: any[] = [];
|
||||
ast.nodes.forEach((node: any) => {
|
||||
const selector = node.selector;
|
||||
if (!selector.endsWith('.hover') && !selector.endsWith('.active')) {
|
||||
nodes.push(node);
|
||||
}
|
||||
});
|
||||
|
||||
const css: {selector: string; value: string; state: string}[] = [];
|
||||
state.forEach((s: string) => {
|
||||
css.push({
|
||||
selector: `.${className}-${id}${s === 'default' ? '' : ':' + s}`,
|
||||
state: s,
|
||||
value: ''
|
||||
});
|
||||
});
|
||||
nodes.forEach(node => {
|
||||
const style = node.nodes.map((n: any) => `${n.prop}: ${n.value};`);
|
||||
const item = css.find(c => {
|
||||
if (
|
||||
c.selector === node.selector ||
|
||||
node.selector.endsWith(`:${c.state}`) ||
|
||||
node.selector.split(' ').indexOf(c.selector) > -1
|
||||
) {
|
||||
return c;
|
||||
}
|
||||
return false;
|
||||
})!;
|
||||
item.value = style.join('\n');
|
||||
});
|
||||
|
||||
newCssNodes.push({
|
||||
name: n.name || '自定义样式',
|
||||
children: css
|
||||
});
|
||||
});
|
||||
setCssNodes(newCssNodes);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getCssAndSetValue(themeClass);
|
||||
}, []);
|
||||
|
||||
const editorChange = debounce((nodeTabs: CssNodeTab[]) => {
|
||||
try {
|
||||
const {data, onBulkChange} = props;
|
||||
const sourceCss = data.themeCss || data.css || {};
|
||||
const newCss: any = {};
|
||||
nodeTabs.forEach(tab => {
|
||||
tab.children.forEach(node => {
|
||||
const nodes = cssParse(node.value)
|
||||
.nodes.map((node: any) => {
|
||||
const {prop, value} = node;
|
||||
return {
|
||||
prop,
|
||||
value
|
||||
};
|
||||
})
|
||||
.filter((n: any) => n.value);
|
||||
const selector = node.selector;
|
||||
const nameEtr = /\.(.*)\-/.exec(selector);
|
||||
const cssCode: PlainObject = {};
|
||||
let name = nameEtr ? nameEtr[1] : '';
|
||||
let state = 'default';
|
||||
if (!!~selector.indexOf(':active')) {
|
||||
state = 'active';
|
||||
} else if (!!~selector.indexOf(':hover')) {
|
||||
state = 'hover';
|
||||
}
|
||||
nodes.forEach((item: any) => {
|
||||
const prop = item.prop;
|
||||
const cssValue = item.value;
|
||||
if (!!~prop.indexOf('radius')) {
|
||||
const type = 'radius:' + state;
|
||||
!cssCode[type] && (cssCode[type] = {});
|
||||
const radius = cssValue.split(' ');
|
||||
|
||||
cssCode[type]['top-left-border-radius'] = radius[0];
|
||||
cssCode[type]['top-right-border-radius'] = radius[1];
|
||||
cssCode[type]['bottom-right-border-radius'] = radius[2];
|
||||
cssCode[type]['bottom-left-border-radius'] = radius[3];
|
||||
} else if (!!~prop.indexOf('border')) {
|
||||
!cssCode['border:' + state] && (cssCode['border:' + state] = {});
|
||||
cssCode['border:' + state][valueMap[prop] || prop] = cssValue;
|
||||
} else if (
|
||||
!!~prop.indexOf('padding') ||
|
||||
!!~prop.indexOf('margin')
|
||||
) {
|
||||
!cssCode['padding-and-margin:' + state] &&
|
||||
(cssCode['padding-and-margin:' + state] = {});
|
||||
cssCode['padding-and-margin:' + state][valueMap[prop] || prop] =
|
||||
cssValue;
|
||||
} else if (fontStyle.includes(prop)) {
|
||||
!cssCode['font:' + state] && (cssCode['font:' + state] = {});
|
||||
cssCode['font:' + state][valueMap[prop] || prop] = cssValue;
|
||||
} else {
|
||||
cssCode[(valueMap[prop] || prop) + ':' + state] = cssValue;
|
||||
}
|
||||
});
|
||||
if (newCss[name]) {
|
||||
newCss[name] = Object.assign(newCss[name], cssCode);
|
||||
} else {
|
||||
newCss[name] = cssCode;
|
||||
}
|
||||
});
|
||||
});
|
||||
onBulkChange &&
|
||||
onBulkChange({
|
||||
themeCss: {
|
||||
...sourceCss,
|
||||
...newCss
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
|
||||
function handleChange(value: string, i: number, j: number) {
|
||||
const newCssNodes = cssNodes;
|
||||
newCssNodes[i].children[j].value = value;
|
||||
setCssNodes(newCssNodes); // 好像不需要这个?
|
||||
editorChange(newCssNodes);
|
||||
}
|
||||
function formateTitle(title: string) {
|
||||
if (title.endsWith('hover')) {
|
||||
return '悬浮态样式';
|
||||
} else if (title.endsWith('active')) {
|
||||
return '点击态样式';
|
||||
} else if (title.endsWith('disabled')) {
|
||||
return '禁用态样式';
|
||||
}
|
||||
return '常规态样式';
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="ThemeCssCode-editor">
|
||||
<div className="ThemeCssCode-editor-title">编辑样式源码</div>
|
||||
<div className="ThemeCssCode-editor-close">
|
||||
<Button onClick={props.onHide} level="link">
|
||||
<Icon icon="close" className="icon" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="ThemeCssCode-editor-content">
|
||||
<div className="ThemeCssCode-editor-content-header">
|
||||
{cssNodes.map((node, index) => {
|
||||
return (
|
||||
<div
|
||||
key={index}
|
||||
onClick={() => setTabId(index)}
|
||||
className={cx(
|
||||
'ThemeCssCode-editor-content-header-title',
|
||||
index === tabId &&
|
||||
'ThemeCssCode-editor-content-header-title--active'
|
||||
)}
|
||||
>
|
||||
{node.name}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="ThemeCssCode-editor-content-main">
|
||||
{cssNodes.map((node, i) => {
|
||||
const children = node.children;
|
||||
return (
|
||||
<div
|
||||
key={i}
|
||||
className={cx(
|
||||
i !== tabId && 'ThemeCssCode-editor-content-body--hidden'
|
||||
)}
|
||||
>
|
||||
{children.map((css, j) => {
|
||||
return (
|
||||
<div
|
||||
className="ThemeCssCode-editor-content-body"
|
||||
key={`${i}-${j}-${css.selector}`}
|
||||
id={`${i}-${j}-${css.selector}`}
|
||||
>
|
||||
{children.length > 1 ? (
|
||||
<div className="ThemeCssCode-editor-content-body-title">
|
||||
{formateTitle(css.selector)}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="ThemeCssCode-editor-content-body-editor">
|
||||
<Editor
|
||||
value={css.value}
|
||||
editorFactory={editorFactory}
|
||||
options={{
|
||||
onChange: (value: string) =>
|
||||
handleChange(value, i, j)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function AmisStyleCodeEditor(props: FormControlProps) {
|
||||
function ThemeCssCode(props: FormControlProps) {
|
||||
const {data, onBulkChange} = props;
|
||||
const {style} = data;
|
||||
const {wrapperCustomStyle} = data;
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const [showEditor, setShowEditor] = useState(false);
|
||||
const [value, setValue] = useState('');
|
||||
|
||||
function getCssAndSetValue(data: any) {
|
||||
// 前面加上空格
|
||||
function getSpaceByDep(dep: number) {
|
||||
let spaces = '';
|
||||
for (let i = 0; i < dep; i++) {
|
||||
spaces += ' ';
|
||||
}
|
||||
return spaces;
|
||||
}
|
||||
|
||||
function getCssAndSetValue(data: any, str: string, dep: number) {
|
||||
if (isEmpty(data)) {
|
||||
return '';
|
||||
}
|
||||
let str = '';
|
||||
for (let key in data) {
|
||||
if (key === 'radius') {
|
||||
str += `border-radius: ${
|
||||
data.radius['top-left-border-radius'] +
|
||||
' ' +
|
||||
data.radius['top-right-border-radius'] +
|
||||
' ' +
|
||||
data.radius['bottom-right-border-radius'] +
|
||||
' ' +
|
||||
data.radius['bottom-left-border-radius']
|
||||
};\n`;
|
||||
} else {
|
||||
str += `${styleMap[key] || key}: ${data[key]};\n`;
|
||||
if (data.hasOwnProperty(key)) {
|
||||
if (isObject(data[key])) {
|
||||
str += getSpaceByDep(dep) + `${key} {\n`;
|
||||
str += getCssAndSetValue(data[key], '', dep + 1);
|
||||
str += getSpaceByDep(dep) + `}\n`;
|
||||
if (dep === 0) {
|
||||
str += '\n';
|
||||
}
|
||||
} else {
|
||||
str += getSpaceByDep(dep) + `${key}: ${data[key]};\n`;
|
||||
}
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const res = getCssAndSetValue(style);
|
||||
setValue(res);
|
||||
setValue(getCssAndSetValue(wrapperCustomStyle, '', 0));
|
||||
}, []);
|
||||
|
||||
function handleShowEditor() {
|
||||
setShowEditor(true);
|
||||
}
|
||||
|
||||
// 递归获取自定义样式
|
||||
function getStyle(style: any, newStyle: PlainObject) {
|
||||
if (isEmpty(style)) {
|
||||
return;
|
||||
}
|
||||
style.nodes.forEach((node: any) => {
|
||||
const {prop, value, selector} = node;
|
||||
if (value) {
|
||||
newStyle[prop] = value;
|
||||
}
|
||||
if (node.nodes) {
|
||||
!newStyle[selector] && (newStyle[selector] = {});
|
||||
getStyle(node, newStyle[selector]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const editorChange = debounce((value: string) => {
|
||||
const newStyle: PlainObject = {};
|
||||
try {
|
||||
const style = cssParse(value);
|
||||
style.nodes.forEach((node: any) => {
|
||||
const {prop, value} = node;
|
||||
if (value) {
|
||||
if (prop === 'border-radius') {
|
||||
const radius = value.split(' ');
|
||||
newStyle['radius'] = {
|
||||
'top-left-border-radius': radius[0] || '',
|
||||
'top-right-border-radius': radius[1] || '',
|
||||
'bottom-right-border-radius': radius[2] || '',
|
||||
'bottom-left-border-radius': radius[3] || ''
|
||||
};
|
||||
} else {
|
||||
newStyle[valueMap[prop] || prop] = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
getStyle(style, newStyle);
|
||||
onBulkChange &&
|
||||
onBulkChange({
|
||||
style: newStyle
|
||||
wrapperCustomStyle: newStyle
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
} catch (error) {}
|
||||
});
|
||||
|
||||
function handleChange(value: string) {
|
||||
editorChange(value);
|
||||
setValue(value);
|
||||
}
|
||||
return (
|
||||
<div className="ThemeCssCode-editor">
|
||||
<div className="ThemeCssCode-editor-title">编辑样式源码</div>
|
||||
<div className="ThemeCssCode-editor-close">
|
||||
<Button onClick={props.onHide} level="link">
|
||||
<Icon icon="close" className="icon" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="ThemeCssCode-editor-content">
|
||||
<Editor
|
||||
value={value}
|
||||
editorFactory={editorFactory}
|
||||
options={{
|
||||
onChange: handleChange
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ThemeCssCode(props: FormControlProps) {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const [showEditor, setShowEditor] = useState(false);
|
||||
function handleShowEditor() {
|
||||
setShowEditor(true);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div ref={ref} className="ThemeCssCode">
|
||||
<Button onClick={handleShowEditor} className=":ThemeCssCode-button">
|
||||
<Icon icon="theme-css" className="icon" /> 编辑样式源码
|
||||
</Button>
|
||||
<a
|
||||
onClick={handleShowEditor}
|
||||
className="ThemeCssCode-button ThemeCssCode-icon"
|
||||
>
|
||||
<Icon icon="expand-alt" className="icon" />
|
||||
</a>
|
||||
<div className="ThemeCssCode-editor-wrap" style={{height: '120px'}}>
|
||||
<Editor
|
||||
value={value}
|
||||
placeholder={editorPlaceholder}
|
||||
language="scss"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Overlay
|
||||
container={document.body}
|
||||
@ -371,17 +123,30 @@ function ThemeCssCode(props: FormControlProps) {
|
||||
rootClose={false}
|
||||
>
|
||||
<PopOver overlay onHide={() => setShowEditor(false)}>
|
||||
{props.isLayout ? (
|
||||
<AmisStyleCodeEditor
|
||||
{...props}
|
||||
onHide={() => setShowEditor(false)}
|
||||
/>
|
||||
) : (
|
||||
<AmisThemeCssCodeEditor
|
||||
{...props}
|
||||
onHide={() => setShowEditor(false)}
|
||||
/>
|
||||
)}
|
||||
<div className="ThemeCssCode-editor">
|
||||
<div className="ThemeCssCode-editor-title">编辑样式</div>
|
||||
<div className="ThemeCssCode-editor-close">
|
||||
<a
|
||||
onClick={() => setShowEditor(false)}
|
||||
className="ThemeCssCode-icon"
|
||||
>
|
||||
<Icon icon="close" className="icon" />
|
||||
</a>
|
||||
</div>
|
||||
<div className="ThemeCssCode-editor-content">
|
||||
<div
|
||||
className="ThemeCssCode-editor-wrap"
|
||||
style={{height: '460px'}}
|
||||
>
|
||||
<Editor
|
||||
value={value}
|
||||
placeholder={editorPlaceholder}
|
||||
language="scss"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PopOver>
|
||||
</Overlay>
|
||||
</>
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -6,6 +6,7 @@ import {
|
||||
} from 'amis-editor-core';
|
||||
import isNumber from 'lodash/isNumber';
|
||||
import isString from 'lodash/isString';
|
||||
import compact from 'lodash/compact';
|
||||
|
||||
/**
|
||||
* 布局相关配置项
|
||||
@ -232,8 +233,9 @@ setSchemaTpl(
|
||||
isFlexItem?: boolean;
|
||||
pipeIn?: (value: any, data: any) => void;
|
||||
pipeOut?: (value: any, data: any) => void;
|
||||
flexHide?: boolean;
|
||||
}) => {
|
||||
const configOptions = [
|
||||
const configOptions = compact([
|
||||
{
|
||||
label: '块级(block)',
|
||||
icon: 'block-display',
|
||||
@ -249,12 +251,12 @@ setSchemaTpl(
|
||||
icon: 'inline-display',
|
||||
value: 'inline'
|
||||
},
|
||||
{
|
||||
!config?.flexHide && {
|
||||
label: '弹性布局(flex)',
|
||||
icon: 'flex-display',
|
||||
value: 'flex'
|
||||
}
|
||||
];
|
||||
]);
|
||||
const configSchema = {
|
||||
type: 'icon-button-group',
|
||||
label:
|
||||
|
@ -426,39 +426,17 @@ export const styleTpl = {
|
||||
*/
|
||||
|
||||
// css类名
|
||||
setSchemaTpl(
|
||||
'theme:cssCode',
|
||||
({
|
||||
themeClass = [],
|
||||
isFormItem
|
||||
}: {
|
||||
themeClass?: any[];
|
||||
isFormItem?: boolean;
|
||||
} = {}) => {
|
||||
if (isFormItem) {
|
||||
themeClass.push(
|
||||
...[
|
||||
{
|
||||
name: 'description',
|
||||
value: 'description',
|
||||
className: 'descriptionClassName'
|
||||
},
|
||||
{name: 'label', value: 'label', className: 'labelClassName'}
|
||||
]
|
||||
);
|
||||
}
|
||||
return {
|
||||
title: '样式源码',
|
||||
body: [
|
||||
{
|
||||
type: 'theme-cssCode',
|
||||
label: false,
|
||||
themeClass
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
);
|
||||
setSchemaTpl('theme:cssCode', () => {
|
||||
return {
|
||||
title: '自定义样式',
|
||||
body: [
|
||||
{
|
||||
type: 'theme-cssCode',
|
||||
label: false
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
|
||||
// form label
|
||||
setSchemaTpl('theme:form-label', () => {
|
||||
@ -499,13 +477,13 @@ setSchemaTpl('theme:form-description', () => {
|
||||
};
|
||||
});
|
||||
|
||||
// 尺寸选择器
|
||||
// 带提示的值输入框
|
||||
setSchemaTpl('theme:select', (option: any = {}) => {
|
||||
return {
|
||||
mode: 'default',
|
||||
type: 'amis-theme-select',
|
||||
label: '尺寸',
|
||||
name: `themeCss.className.size:default`,
|
||||
label: '大小',
|
||||
name: `themeCss.className.select:default`,
|
||||
options: '${sizesOptions}',
|
||||
...option
|
||||
};
|
||||
@ -583,56 +561,131 @@ setSchemaTpl('theme:shadow', (option: any = {}) => {
|
||||
// 尺寸选择器
|
||||
setSchemaTpl('theme:size', (option: any = {}) => {
|
||||
return {
|
||||
type: 'amis-theme-select',
|
||||
mode: 'default',
|
||||
type: 'amis-theme-size-editor',
|
||||
label: false,
|
||||
name: `css.className.size`,
|
||||
name: `themeCss.className.size:default`,
|
||||
options: '${sizesOptions}',
|
||||
hideMinWidth: true,
|
||||
...option
|
||||
};
|
||||
});
|
||||
|
||||
setSchemaTpl(
|
||||
'theme:base',
|
||||
(option: {
|
||||
collapsed?: boolean;
|
||||
extra?: any[];
|
||||
classname?: string;
|
||||
title?: string;
|
||||
}) => {
|
||||
const {
|
||||
collapsed = false,
|
||||
extra = [],
|
||||
classname = 'baseControlClassName',
|
||||
title = '基本样式'
|
||||
} = option;
|
||||
const styleStateFunc = (visibleOn: string, state: string) => {
|
||||
return [
|
||||
getSchemaTpl('theme:border', {
|
||||
visibleOn: visibleOn,
|
||||
name: `themeCss.${classname}.border:${state}`
|
||||
}),
|
||||
getSchemaTpl('theme:radius', {
|
||||
visibleOn: visibleOn,
|
||||
name: `themeCss.${classname}.radius:${state}`
|
||||
}),
|
||||
getSchemaTpl('theme:paddingAndMargin', {
|
||||
visibleOn: visibleOn,
|
||||
name: `themeCss.${classname}.padding-and-margin:${state}`
|
||||
}),
|
||||
getSchemaTpl('theme:colorPicker', {
|
||||
visibleOn: visibleOn,
|
||||
name: `themeCss.${classname}.background:${state}`,
|
||||
label: '背景',
|
||||
needCustom: true,
|
||||
needGradient: true,
|
||||
needImage: true,
|
||||
labelMode: 'input'
|
||||
}),
|
||||
getSchemaTpl('theme:shadow', {
|
||||
visibleOn: visibleOn,
|
||||
name: `themeCss.${classname}.boxShadow:${state}`
|
||||
})
|
||||
].concat(
|
||||
extra.map(item => {
|
||||
return {
|
||||
...item,
|
||||
visibleOn: visibleOn,
|
||||
name: `themeCss.${classname}.${item.name}:${state}`
|
||||
};
|
||||
})
|
||||
);
|
||||
};
|
||||
const styles = [
|
||||
{
|
||||
type: 'select',
|
||||
name: 'editorState',
|
||||
label: '状态',
|
||||
selectFirst: true,
|
||||
options: [
|
||||
{
|
||||
label: '常规',
|
||||
value: 'default'
|
||||
},
|
||||
{
|
||||
label: '悬浮',
|
||||
value: 'hover'
|
||||
},
|
||||
{
|
||||
label: '点击',
|
||||
value: 'active'
|
||||
}
|
||||
]
|
||||
},
|
||||
...styleStateFunc(
|
||||
"${editorState == 'default' || !editorState}",
|
||||
'default'
|
||||
),
|
||||
...styleStateFunc("${editorState == 'hover'}", 'hover'),
|
||||
...styleStateFunc("${editorState == 'active'}", 'active')
|
||||
];
|
||||
|
||||
return {
|
||||
title,
|
||||
collapsed,
|
||||
body: styles
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
setSchemaTpl(
|
||||
'theme:common',
|
||||
(option: {
|
||||
exclude: string[] | string;
|
||||
include: string[];
|
||||
collapsed?: boolean;
|
||||
extra?: any[];
|
||||
baseExtra?: any[];
|
||||
layoutExtra?: any[];
|
||||
classname?: string;
|
||||
baseTitle?: string;
|
||||
}) => {
|
||||
let {exclude, include, collapsed} = option || {};
|
||||
let {
|
||||
exclude,
|
||||
collapsed,
|
||||
extra = [],
|
||||
baseExtra,
|
||||
layoutExtra,
|
||||
classname,
|
||||
baseTitle
|
||||
} = option || {};
|
||||
|
||||
const curCollapsed = collapsed ?? false; // 默认都展开
|
||||
// key统一转换成Kebab case,eg: boxShadow => bos-shadow
|
||||
exclude = (
|
||||
exclude ? (Array.isArray(exclude) ? exclude : [exclude]) : []
|
||||
).map((key: string) => kebabCase(key));
|
||||
|
||||
const moreStyle =
|
||||
include?.map(key =>
|
||||
getSchemaTpl(`theme:${key}`, {
|
||||
name: 'style'
|
||||
})
|
||||
) || [];
|
||||
const styles = moreStyle.concat([
|
||||
getSchemaTpl('theme:border', {
|
||||
name: 'style'
|
||||
}),
|
||||
getSchemaTpl('theme:radius', {
|
||||
name: 'style.radius'
|
||||
}),
|
||||
getSchemaTpl('theme:paddingAndMargin', {
|
||||
name: 'style'
|
||||
}),
|
||||
getSchemaTpl('theme:colorPicker', {
|
||||
name: 'style.background',
|
||||
label: '背景',
|
||||
needCustom: true,
|
||||
needGradient: true,
|
||||
needImage: true,
|
||||
labelMode: 'input'
|
||||
}),
|
||||
getSchemaTpl('theme:shadow', {
|
||||
name: 'style.boxShadow'
|
||||
})
|
||||
]);
|
||||
return [
|
||||
{
|
||||
header: '布局',
|
||||
@ -644,21 +697,24 @@ setSchemaTpl(
|
||||
label: false,
|
||||
name: 'style'
|
||||
}
|
||||
].filter(comp => !~exclude.indexOf(comp.type.replace(/^style-/i, '')))
|
||||
]
|
||||
.filter(comp => !~exclude.indexOf(comp.type.replace(/^style-/i, '')))
|
||||
.concat(layoutExtra || [])
|
||||
},
|
||||
getSchemaTpl('theme:base', {
|
||||
collapsed: curCollapsed,
|
||||
extra: baseExtra,
|
||||
classname,
|
||||
title: baseTitle
|
||||
}),
|
||||
...extra,
|
||||
{
|
||||
title: '自定义样式',
|
||||
collapsed: curCollapsed,
|
||||
body: styles
|
||||
},
|
||||
{
|
||||
title: '样式源码',
|
||||
collapsed: curCollapsed,
|
||||
body: [
|
||||
{
|
||||
type: 'theme-cssCode',
|
||||
label: false,
|
||||
isLayout: true
|
||||
label: false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -3192,7 +3192,7 @@
|
||||
--steps-dot-process-bg-color: var(--colors-brand-5);
|
||||
--steps-dot-finish-bg-color: var(--colors-neutral-fill-11);
|
||||
--steps-dot-error-bg-color: var(--colors-error-5);
|
||||
--steps-simple-icon: '';
|
||||
--steps-simple-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"><polyline stroke="currentColor" transform="translate(5.496854, 8.006854) scale(-1, 1) rotate(-135.000000) translate(-5.496854, -8.006854) " points="1.49685425 4.00685425 9.49685425 4.00685425 9.49685425 12.0068542" stroke-width="1" fill="none" stroke-linecap="butt" stroke-linejoin="round"/></g></svg>';
|
||||
--steps-simple-icon-size: var(--sizes-size-8);
|
||||
|
||||
--Steps-bg: var(--steps-status-wait-color);
|
||||
@ -3305,6 +3305,7 @@
|
||||
--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-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);
|
||||
--image-images-item-marginBottom: var(--sizes-size-3);
|
||||
|
@ -1,17 +1,38 @@
|
||||
.#{$ns}Divider {
|
||||
margin: var(--Divider-marginTop) var(--Divider-marginRight)
|
||||
var(--Divider-marginBottom) var(--Divider-marginLeft);
|
||||
border-bottom: var(--Divider-width) var(--Divider-style) var(--Divider-color);
|
||||
height: px2rem(2px);
|
||||
font-size: 0;
|
||||
|
||||
&--solid {
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
|
||||
&--dashed {
|
||||
border-bottom-style: dashed;
|
||||
}
|
||||
|
||||
&--horizontal.#{$ns}Divider--solid {
|
||||
border-bottom-style: solid;
|
||||
}
|
||||
|
||||
&--horizontal.#{$ns}Divider--dashed {
|
||||
border-bottom-style: dashed;
|
||||
}
|
||||
|
||||
&--vertical.#{$ns}Divider--solid {
|
||||
border-left-style: solid;
|
||||
}
|
||||
&--vertical.#{$ns}Divider--dashed {
|
||||
border-left-style: dashed;
|
||||
}
|
||||
|
||||
&--horizontal {
|
||||
border-bottom: var(--Divider-width) var(--Divider-style)
|
||||
var(--Divider-color);
|
||||
height: px2rem(2px);
|
||||
}
|
||||
|
||||
&--vertical {
|
||||
border-left: var(--Divider-width) var(--Divider-style) var(--Divider-color);
|
||||
height: var(--sizes-base-15);
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
/* 移动端样式调整 */
|
||||
|
@ -176,6 +176,11 @@
|
||||
padding: 0 5px;
|
||||
line-height: 1;
|
||||
font-size: px2rem(16px);
|
||||
|
||||
svg {
|
||||
width: px2rem(16px);
|
||||
height: px2rem(16px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -196,3 +201,7 @@
|
||||
position: relative;
|
||||
@include clearfix();
|
||||
}
|
||||
|
||||
.Image-view-icon {
|
||||
content: var(--image-image-normal-icon);
|
||||
}
|
||||
|
@ -22,54 +22,6 @@
|
||||
border-color: var(--Form-input-onFocused-borderColor);
|
||||
}
|
||||
|
||||
.#{$ns}MonacoEditor {
|
||||
position: relative;
|
||||
|
||||
&-header {
|
||||
position: absolute;
|
||||
right: px2rem(14px);
|
||||
top: 0;
|
||||
padding: 0;
|
||||
width: px2rem(18px);
|
||||
height: px2rem(18px);
|
||||
z-index: 5;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: var(--Modal-header-bg);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
&-fullscreen {
|
||||
cursor: pointer;
|
||||
color: var(--Modal-close-color);
|
||||
line-height: inherit;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
|
||||
&:hover {
|
||||
color: var(--Model-close-onHover-color);
|
||||
|
||||
svg {
|
||||
fill: var(--Model-close-onHover-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-placeholder {
|
||||
pointer-events: none;
|
||||
top: 0;
|
||||
position: absolute;
|
||||
left: px2rem(65px);
|
||||
right: px2rem(16px);
|
||||
white-space: break-spaces;
|
||||
line-break: anywhere;
|
||||
color: var(--Form-input-placeholderColor);
|
||||
}
|
||||
}
|
||||
|
||||
&--sm {
|
||||
min-height: 100px;
|
||||
|
||||
@ -115,6 +67,54 @@
|
||||
}
|
||||
}
|
||||
|
||||
.#{$ns}MonacoEditor {
|
||||
position: relative;
|
||||
|
||||
&-header {
|
||||
position: absolute;
|
||||
right: px2rem(14px);
|
||||
top: 0;
|
||||
padding: 0;
|
||||
width: px2rem(18px);
|
||||
height: px2rem(18px);
|
||||
z-index: 5;
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: var(--Modal-header-bg);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
&-fullscreen {
|
||||
cursor: pointer;
|
||||
color: var(--Modal-close-color);
|
||||
line-height: inherit;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
|
||||
&:hover {
|
||||
color: var(--Model-close-onHover-color);
|
||||
|
||||
svg {
|
||||
fill: var(--Model-close-onHover-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-placeholder {
|
||||
pointer-events: none;
|
||||
top: 0;
|
||||
position: absolute;
|
||||
left: px2rem(65px);
|
||||
right: px2rem(16px);
|
||||
white-space: break-spaces;
|
||||
line-break: anywhere;
|
||||
color: var(--Form-input-placeholderColor);
|
||||
}
|
||||
}
|
||||
|
||||
.monaco-inputbox > .wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
@ -290,8 +290,8 @@ export function Icon({
|
||||
// 从css变量中获取icon
|
||||
function refFn(dom: any) {
|
||||
if (dom) {
|
||||
const style = getComputedStyle(dom);
|
||||
const svgStr = style.getPropertyValue('content');
|
||||
const domStyle = getComputedStyle(dom);
|
||||
const svgStr = domStyle.getPropertyValue('content');
|
||||
const svg = /(<svg.*<\/svg>)/.exec(svgStr);
|
||||
|
||||
if (svg) {
|
||||
|
@ -223,7 +223,7 @@ test('Picker filter1', async () => {
|
||||
|
||||
fireEvent.click(pickerBtn);
|
||||
|
||||
await wait(500);
|
||||
await wait(1000);
|
||||
const a = container.querySelector('input[name="a"]')!;
|
||||
const b = container.querySelector('input[name="b"]')!;
|
||||
|
||||
|
@ -1631,7 +1631,7 @@ exports[`Renderer:combo with items & multiLine 1`] = `
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="cxd-Divider cxd-Divider--dashed"
|
||||
class="cxd-Divider cxd-Divider--dashed cxd-Divider--horizontal"
|
||||
/>
|
||||
<div
|
||||
class="cxd-Form-item cxd-Form-item--horizontal"
|
||||
|
@ -180,7 +180,7 @@ exports[`Renderers:Action all levels 1`] = `
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
class="cxd-Divider cxd-Divider--dashed"
|
||||
class="cxd-Divider cxd-Divider--dashed cxd-Divider--horizontal"
|
||||
/>
|
||||
<div
|
||||
class="cxd-Button cxd-Button--link cxd-Button--size-default is-disabled"
|
||||
|
@ -3,6 +3,7 @@
|
||||
exports[`Renderer:flex 1`] = `
|
||||
<div>
|
||||
<div
|
||||
class="cxd-Flex"
|
||||
style="display: flex; flex-direction: row; justify-content: center; align-items: stretch; align-content: center;"
|
||||
>
|
||||
<span
|
||||
@ -62,6 +63,7 @@ exports[`Renderer:flex justify 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
<div
|
||||
class="cxd-Flex"
|
||||
style="display: flex; flex-direction: row; justify-content: center; align-items: stretch; align-content: center;"
|
||||
>
|
||||
<span
|
||||
@ -103,6 +105,7 @@ exports[`Renderer:flex justify 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
<div
|
||||
class="cxd-Flex"
|
||||
style="display: flex; flex-direction: row; justify-content: flex-start; align-items: stretch; align-content: center;"
|
||||
>
|
||||
<span
|
||||
@ -144,6 +147,7 @@ exports[`Renderer:flex justify 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
<div
|
||||
class="cxd-Flex"
|
||||
style="display: flex; flex-direction: row; justify-content: flex-end; align-items: stretch; align-content: center;"
|
||||
>
|
||||
<span
|
||||
@ -185,6 +189,7 @@ exports[`Renderer:flex justify 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
<div
|
||||
class="cxd-Flex"
|
||||
style="display: flex; flex-direction: row; justify-content: space-around; align-items: stretch; align-content: center;"
|
||||
>
|
||||
<span
|
||||
@ -226,6 +231,7 @@ exports[`Renderer:flex justify 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
<div
|
||||
class="cxd-Flex"
|
||||
style="display: flex; flex-direction: row; justify-content: space-between; align-items: stretch; align-content: center;"
|
||||
>
|
||||
<span
|
||||
@ -267,6 +273,7 @@ exports[`Renderer:flex justify 1`] = `
|
||||
</span>
|
||||
</span>
|
||||
<div
|
||||
class="cxd-Flex"
|
||||
style="display: flex; flex-direction: row; justify-content: space-evenly; align-items: stretch; align-content: center;"
|
||||
>
|
||||
<span
|
||||
|
@ -108,7 +108,7 @@ exports[`Renderer:gridnav 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="cxd-Divider cxd-Divider--dashed"
|
||||
class="cxd-Divider cxd-Divider--dashed cxd-Divider--horizontal"
|
||||
/>
|
||||
<div
|
||||
class="cxd-GridNav cxd-GridNav-top u-hairline"
|
||||
|
@ -3,7 +3,7 @@
|
||||
exports[`Renderer:iframe 1`] = `
|
||||
<div>
|
||||
<iframe
|
||||
class="b-a"
|
||||
class="cxd-IFrame b-a"
|
||||
frameborder="0"
|
||||
src="https://www.baidu.com"
|
||||
style="width: 500px; height: 500px;"
|
||||
@ -14,7 +14,7 @@ exports[`Renderer:iframe 1`] = `
|
||||
exports[`Renderer:iframe-escape 1`] = `
|
||||
<div>
|
||||
<iframe
|
||||
class="b-a"
|
||||
class="cxd-IFrame b-a"
|
||||
frameborder="0"
|
||||
src="https://www.baidu.com/?s=%25f"
|
||||
style="width: 500px; height: 500px;"
|
||||
@ -38,7 +38,7 @@ exports[`Renderer:iframe-var 1`] = `
|
||||
role="page-body"
|
||||
>
|
||||
<iframe
|
||||
class="b-a"
|
||||
class="cxd-IFrame b-a"
|
||||
frameborder="0"
|
||||
src="https://www.baidu.com"
|
||||
style="width: 500px; height: 500px;"
|
||||
|
@ -472,7 +472,7 @@ exports[`Renderer:images images:basic 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="cxd-Divider cxd-Divider--dashed"
|
||||
class="cxd-Divider cxd-Divider--dashed cxd-Divider--horizontal"
|
||||
/>
|
||||
<div
|
||||
class="cxd-ImagesField"
|
||||
|
@ -641,6 +641,7 @@ test('api:responseData2', async () => {
|
||||
expect(fetcher).toHaveBeenCalledTimes(1);
|
||||
expect(container.querySelector('input[name="id"]')).toBeInTheDocument();
|
||||
expect((container.querySelector('input[name="id"]') as any).value).toBe('1');
|
||||
await wait(500);
|
||||
|
||||
expect(container.querySelector('input[name="id2"]')).toBeInTheDocument();
|
||||
expect((container.querySelector('input[name="id2"]') as any).value).toBe('2');
|
||||
|
@ -792,6 +792,7 @@ export class Action extends React.Component<ActionProps, ActionState> {
|
||||
classPrefix: ns,
|
||||
loadingConfig,
|
||||
themeCss,
|
||||
wrapperCustomStyle,
|
||||
css,
|
||||
id,
|
||||
env
|
||||
@ -860,7 +861,8 @@ export class Action extends React.Component<ActionProps, ActionState> {
|
||||
<Button
|
||||
loadingConfig={loadingConfig}
|
||||
className={cx(className, {
|
||||
[activeClassName || 'is-active']: isActive
|
||||
[activeClassName || 'is-active']: isActive,
|
||||
[`wrapperCustomStyle-${id?.replace('u:', '')}`]: wrapperCustomStyle
|
||||
})}
|
||||
style={style}
|
||||
size={size}
|
||||
@ -905,17 +907,7 @@ export class Action extends React.Component<ActionProps, ActionState> {
|
||||
},
|
||||
active: {suf: ':not(:disabled):not(.is-disabled)'}
|
||||
}
|
||||
}
|
||||
],
|
||||
id
|
||||
}}
|
||||
env={env}
|
||||
/>
|
||||
{/* button图标自定义样式 */}
|
||||
<CustomStyle
|
||||
config={{
|
||||
themeCss: themeCss || css,
|
||||
classNames: [
|
||||
},
|
||||
{
|
||||
key: 'iconClassName',
|
||||
value: iconClassName,
|
||||
@ -934,6 +926,8 @@ export class Action extends React.Component<ActionProps, ActionState> {
|
||||
}
|
||||
}
|
||||
],
|
||||
wrapperCustomStyle,
|
||||
componentId: id,
|
||||
id
|
||||
}}
|
||||
env={env}
|
||||
|
@ -12,7 +12,8 @@ import {
|
||||
createObject,
|
||||
isObject,
|
||||
isArrayChildrenModified,
|
||||
getPropValue
|
||||
getPropValue,
|
||||
CustomStyle
|
||||
} from 'amis-core';
|
||||
import {ActionObject} from 'amis-core';
|
||||
import {Icon} from 'amis-ui';
|
||||
@ -457,7 +458,12 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
|
||||
duration,
|
||||
multiple,
|
||||
alwaysShowArrow,
|
||||
icons
|
||||
icons,
|
||||
id,
|
||||
wrapperCustomStyle,
|
||||
env,
|
||||
themeCss,
|
||||
baseControlClassName
|
||||
} = this.props;
|
||||
const {options, current, nextAnimation} = this.state;
|
||||
|
||||
@ -608,7 +614,11 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
|
||||
className={cx(
|
||||
`Carousel Carousel--${controlsTheme}`,
|
||||
{['Carousel-arrow--always']: !!alwaysShowArrow},
|
||||
className
|
||||
className,
|
||||
baseControlClassName,
|
||||
wrapperCustomStyle
|
||||
? `wrapperCustomStyle-${id?.replace('u:', '')}`
|
||||
: ''
|
||||
)}
|
||||
style={carouselStyles}
|
||||
>
|
||||
@ -641,6 +651,20 @@ export class Carousel extends React.Component<CarouselProps, CarouselState> {
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
<CustomStyle
|
||||
config={{
|
||||
wrapperCustomStyle,
|
||||
componentId: id,
|
||||
themeCss,
|
||||
classNames: [
|
||||
{
|
||||
key: 'baseControlClassName',
|
||||
value: baseControlClassName
|
||||
}
|
||||
]
|
||||
}}
|
||||
env={env}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -6,7 +6,8 @@ import {
|
||||
Renderer,
|
||||
RendererProps,
|
||||
loadScript,
|
||||
buildStyle
|
||||
buildStyle,
|
||||
CustomStyle
|
||||
} from 'amis-core';
|
||||
import {ServiceStore, IServiceStore} from 'amis-core';
|
||||
|
||||
@ -593,7 +594,12 @@ export class Chart extends React.Component<ChartProps> {
|
||||
height,
|
||||
classPrefix: ns,
|
||||
unMountOnHidden,
|
||||
data
|
||||
data,
|
||||
id,
|
||||
wrapperCustomStyle,
|
||||
env,
|
||||
themeCss,
|
||||
baseControlClassName
|
||||
} = this.props;
|
||||
let style = this.props.style || {};
|
||||
style.width = style.width || width || '100%';
|
||||
@ -601,7 +607,17 @@ export class Chart extends React.Component<ChartProps> {
|
||||
const styleVar = buildStyle(style, data);
|
||||
|
||||
return (
|
||||
<div className={cx(`${ns}Chart`, className)} style={styleVar}>
|
||||
<div
|
||||
className={cx(
|
||||
`${ns}Chart`,
|
||||
className,
|
||||
baseControlClassName,
|
||||
wrapperCustomStyle
|
||||
? `wrapperCustomStyle-${id?.replace('u:', '')}`
|
||||
: ''
|
||||
)}
|
||||
style={styleVar}
|
||||
>
|
||||
<LazyComponent
|
||||
unMountOnHidden={unMountOnHidden}
|
||||
placeholder="..." // 之前那个 spinner 会导致 sensor 失效
|
||||
@ -609,6 +625,20 @@ export class Chart extends React.Component<ChartProps> {
|
||||
<div className={`${ns}Chart-content`} ref={this.refFn}></div>
|
||||
)}
|
||||
/>
|
||||
<CustomStyle
|
||||
config={{
|
||||
wrapperCustomStyle,
|
||||
componentId: id,
|
||||
themeCss,
|
||||
classNames: [
|
||||
{
|
||||
key: 'baseControlClassName',
|
||||
value: baseControlClassName
|
||||
}
|
||||
]
|
||||
}}
|
||||
env={env}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -6,7 +6,8 @@ import {
|
||||
autobind,
|
||||
buildStyle,
|
||||
isPureVariable,
|
||||
resolveVariableAndFilter
|
||||
resolveVariableAndFilter,
|
||||
CustomStyle
|
||||
} from 'amis-core';
|
||||
import {DndContainer as DndWrapper} from 'amis-ui';
|
||||
import {BaseSchema, SchemaClassName, SchemaCollection} from '../Schema';
|
||||
@ -188,7 +189,12 @@ export default class Container<T> extends React.Component<
|
||||
style,
|
||||
data,
|
||||
draggable,
|
||||
draggableConfig
|
||||
draggableConfig,
|
||||
id,
|
||||
wrapperCustomStyle,
|
||||
env,
|
||||
themeCss,
|
||||
baseControlClassName
|
||||
} = this.props;
|
||||
const finalDraggable: boolean = isPureVariable(draggable)
|
||||
? resolveVariableAndFilter(draggable, data, '| raw')
|
||||
@ -206,7 +212,11 @@ export default class Container<T> extends React.Component<
|
||||
className={cx(
|
||||
'Container',
|
||||
size && size !== 'none' ? `Container--${size}` : '',
|
||||
className
|
||||
className,
|
||||
baseControlClassName,
|
||||
wrapperCustomStyle
|
||||
? `wrapperCustomStyle-${id?.replace('u:', '')}`
|
||||
: ''
|
||||
)}
|
||||
onClick={this.handleClick}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
@ -214,6 +224,20 @@ export default class Container<T> extends React.Component<
|
||||
style={buildStyle(style, data)}
|
||||
>
|
||||
{this.renderBody()}
|
||||
<CustomStyle
|
||||
config={{
|
||||
wrapperCustomStyle,
|
||||
componentId: id,
|
||||
themeCss,
|
||||
classNames: [
|
||||
{
|
||||
key: 'baseControlClassName',
|
||||
value: baseControlClassName
|
||||
}
|
||||
]
|
||||
}}
|
||||
env={env}
|
||||
/>
|
||||
</Component>
|
||||
);
|
||||
|
||||
|
@ -9,6 +9,9 @@ import {BaseSchema} from '../Schema';
|
||||
export interface DividerSchema extends BaseSchema {
|
||||
type: 'divider';
|
||||
lineStyle?: 'dashed' | 'solid';
|
||||
direction?: 'horizontal' | 'vertical';
|
||||
color?: string;
|
||||
rotate?: number;
|
||||
[propName: string]: any;
|
||||
}
|
||||
|
||||
@ -23,15 +26,41 @@ export default class Divider extends React.Component<DividerProps, object> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const {classnames: cx, className, style, lineStyle} = this.props;
|
||||
const {
|
||||
classnames: cx,
|
||||
className,
|
||||
style = {},
|
||||
lineStyle,
|
||||
direction,
|
||||
color,
|
||||
rotate
|
||||
} = this.props;
|
||||
|
||||
const borderColor: any = {};
|
||||
if (color) {
|
||||
// 处理渐变色的情况
|
||||
if (~color?.indexOf('linear-gradient')) {
|
||||
borderColor.borderImage = color + ' 10';
|
||||
} else {
|
||||
borderColor.borderColor = color;
|
||||
}
|
||||
}
|
||||
|
||||
let transform;
|
||||
if (rotate) {
|
||||
transform = `${style?.transform || ''} rotate(${rotate}deg)`;
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
'Divider',
|
||||
lineStyle ? `Divider--${lineStyle}` : '',
|
||||
direction === 'vertical'
|
||||
? 'Divider--vertical'
|
||||
: 'Divider--horizontal',
|
||||
className
|
||||
)}
|
||||
style={style}
|
||||
style={{...style, ...borderColor, transform}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {buildStyle, Renderer, RendererProps} from 'amis-core';
|
||||
import {buildStyle, Renderer, RendererProps, CustomStyle} from 'amis-core';
|
||||
import {Schema} from 'amis-core';
|
||||
import {BaseSchema, SchemaCollection, SchemaObject} from '../Schema';
|
||||
|
||||
@ -100,7 +100,13 @@ export default class Flex extends React.Component<FlexProps, object> {
|
||||
className,
|
||||
render,
|
||||
disabled,
|
||||
data
|
||||
data,
|
||||
id,
|
||||
wrapperCustomStyle,
|
||||
env,
|
||||
themeCss,
|
||||
baseControlClassName,
|
||||
classnames: cx
|
||||
} = this.props;
|
||||
const styleVar = buildStyle(style, data);
|
||||
const flexStyle = {
|
||||
@ -121,7 +127,17 @@ export default class Flex extends React.Component<FlexProps, object> {
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={flexStyle} className={className}>
|
||||
<div
|
||||
style={flexStyle}
|
||||
className={cx(
|
||||
'Flex',
|
||||
className,
|
||||
baseControlClassName,
|
||||
wrapperCustomStyle
|
||||
? ` wrapperCustomStyle-${id?.replace('u:', '')}`
|
||||
: ''
|
||||
)}
|
||||
>
|
||||
{(Array.isArray(items) ? items : items ? [items] : []).map(
|
||||
(item, key) =>
|
||||
render(`flexItem/${key}`, item, {
|
||||
@ -129,6 +145,20 @@ export default class Flex extends React.Component<FlexProps, object> {
|
||||
disabled: (item as SchemaObject)?.disabled ?? disabled
|
||||
})
|
||||
)}
|
||||
<CustomStyle
|
||||
config={{
|
||||
wrapperCustomStyle,
|
||||
componentId: id,
|
||||
themeCss,
|
||||
classNames: [
|
||||
{
|
||||
key: 'baseControlClassName',
|
||||
value: baseControlClassName
|
||||
}
|
||||
]
|
||||
}}
|
||||
env={env}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -2001,16 +2001,7 @@ export default class ImageControl extends React.Component<
|
||||
{
|
||||
key: 'inputImageControlClassName',
|
||||
value: inputImageControlClassName
|
||||
}
|
||||
],
|
||||
id
|
||||
}}
|
||||
env={env}
|
||||
/>
|
||||
<CustomStyle
|
||||
config={{
|
||||
themeCss,
|
||||
classNames: [
|
||||
},
|
||||
{
|
||||
key: 'addBtnControlClassName',
|
||||
value: addBtnControlClassName,
|
||||
@ -2022,16 +2013,7 @@ export default class ImageControl extends React.Component<
|
||||
suf: ':not(:disabled):not(.is-disabled)'
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
id: id + '-addOn'
|
||||
}}
|
||||
env={env}
|
||||
/>
|
||||
<CustomStyle
|
||||
config={{
|
||||
themeCss,
|
||||
classNames: [
|
||||
},
|
||||
{
|
||||
key: 'iconControlClassName',
|
||||
value: iconControlClassName,
|
||||
@ -2042,7 +2024,7 @@ export default class ImageControl extends React.Component<
|
||||
}
|
||||
}
|
||||
],
|
||||
id: id + '-icon'
|
||||
id
|
||||
}}
|
||||
env={env}
|
||||
/>
|
||||
|
@ -1,5 +1,11 @@
|
||||
import React from 'react';
|
||||
import {FormHorizontal, Renderer, RendererProps, buildStyle} from 'amis-core';
|
||||
import {
|
||||
FormHorizontal,
|
||||
Renderer,
|
||||
RendererProps,
|
||||
buildStyle,
|
||||
CustomStyle
|
||||
} from 'amis-core';
|
||||
import pick from 'lodash/pick';
|
||||
import {BaseSchema, SchemaClassName, SchemaCollection} from '../Schema';
|
||||
|
||||
@ -201,7 +207,12 @@ export default class Grid<T> extends React.Component<GridProps & T, object> {
|
||||
align: hAlign,
|
||||
loading = false,
|
||||
loadingConfig,
|
||||
data
|
||||
data,
|
||||
id,
|
||||
wrapperCustomStyle,
|
||||
env,
|
||||
themeCss,
|
||||
baseControlClassName
|
||||
} = this.props;
|
||||
const styleVar = buildStyle(style, data);
|
||||
return (
|
||||
@ -213,12 +224,30 @@ export default class Grid<T> extends React.Component<GridProps & T, object> {
|
||||
[`Grid--v${ucFirst(vAlign)}`]: vAlign,
|
||||
[`Grid--h${ucFirst(hAlign)}`]: hAlign
|
||||
},
|
||||
className
|
||||
className,
|
||||
baseControlClassName,
|
||||
wrapperCustomStyle
|
||||
? `wrapperCustomStyle-${id?.replace('u:', '')}`
|
||||
: ''
|
||||
)}
|
||||
style={styleVar}
|
||||
>
|
||||
{this.renderColumns(this.props.columns)}
|
||||
<Spinner loadingConfig={loadingConfig} overlay show={loading} />
|
||||
<CustomStyle
|
||||
config={{
|
||||
wrapperCustomStyle,
|
||||
componentId: id,
|
||||
themeCss,
|
||||
classNames: [
|
||||
{
|
||||
key: 'baseControlClassName',
|
||||
value: baseControlClassName
|
||||
}
|
||||
]
|
||||
}}
|
||||
env={env}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -4,7 +4,8 @@ import {
|
||||
OnEventProps,
|
||||
Renderer,
|
||||
RendererProps,
|
||||
runActions
|
||||
runActions,
|
||||
CustomStyle
|
||||
} from 'amis-core';
|
||||
import {filter} from 'amis-core';
|
||||
import {autobind, createObject} from 'amis-core';
|
||||
@ -222,7 +223,12 @@ export default class IFrame extends React.Component<IFrameProps, object> {
|
||||
sandbox,
|
||||
referrerpolicy,
|
||||
translate: __,
|
||||
env
|
||||
id,
|
||||
wrapperCustomStyle,
|
||||
env,
|
||||
themeCss,
|
||||
baseControlClassName,
|
||||
classnames: cx
|
||||
} = this.props;
|
||||
|
||||
let tempStyle: any = {};
|
||||
@ -252,18 +258,41 @@ export default class IFrame extends React.Component<IFrameProps, object> {
|
||||
}
|
||||
|
||||
return (
|
||||
<iframe
|
||||
name={name}
|
||||
className={className}
|
||||
frameBorder={frameBorder}
|
||||
style={style}
|
||||
ref={this.IFrameRef}
|
||||
onLoad={this.onLoad}
|
||||
src={finalSrc}
|
||||
allow={allow}
|
||||
referrerPolicy={referrerpolicy}
|
||||
sandbox={sandbox}
|
||||
/>
|
||||
<>
|
||||
<iframe
|
||||
name={name}
|
||||
className={cx(
|
||||
'IFrame',
|
||||
className,
|
||||
baseControlClassName,
|
||||
wrapperCustomStyle
|
||||
? ` wrapperCustomStyle-${id?.replace('u:', '')}`
|
||||
: ''
|
||||
)}
|
||||
frameBorder={frameBorder}
|
||||
style={style}
|
||||
ref={this.IFrameRef}
|
||||
onLoad={this.onLoad}
|
||||
src={finalSrc}
|
||||
allow={allow}
|
||||
referrerPolicy={referrerpolicy}
|
||||
sandbox={sandbox}
|
||||
/>
|
||||
<CustomStyle
|
||||
config={{
|
||||
wrapperCustomStyle,
|
||||
componentId: id,
|
||||
themeCss,
|
||||
classNames: [
|
||||
{
|
||||
key: 'baseControlClassName',
|
||||
value: baseControlClassName
|
||||
}
|
||||
]
|
||||
}}
|
||||
env={env}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import {Renderer, RendererProps} from 'amis-core';
|
||||
import {Renderer, RendererProps, CustomStyle} from 'amis-core';
|
||||
import {filter} from 'amis-core';
|
||||
import {themeable, ThemeProps} from 'amis-core';
|
||||
import {autobind, getPropValue} from 'amis-core';
|
||||
@ -165,6 +165,10 @@ export interface ImageThumbProps
|
||||
index?: number;
|
||||
onLoad?: React.EventHandler<any>;
|
||||
overlays?: JSX.Element;
|
||||
imageControlClassName?: string;
|
||||
titleControlClassName?: string;
|
||||
desControlClassName?: string;
|
||||
iconControlClassName?: string;
|
||||
}
|
||||
|
||||
interface ImageThumbState {
|
||||
@ -235,7 +239,11 @@ export class ImageThumb extends React.Component<
|
||||
enlargeAble,
|
||||
translate: __,
|
||||
overlays,
|
||||
imageMode
|
||||
imageMode,
|
||||
imageControlClassName,
|
||||
titleControlClassName,
|
||||
desControlClassName,
|
||||
iconControlClassName
|
||||
} = this.props;
|
||||
|
||||
const {imageLoading} = this.state;
|
||||
@ -270,8 +278,13 @@ export class ImageThumb extends React.Component<
|
||||
data-position="bottom"
|
||||
target="_blank"
|
||||
onClick={this.handleEnlarge}
|
||||
className={iconControlClassName}
|
||||
>
|
||||
<Icon icon="view" className="icon" />
|
||||
<Icon
|
||||
icon="view"
|
||||
className="icon"
|
||||
iconContent="Image-view-icon"
|
||||
/>
|
||||
</a>
|
||||
) : null}
|
||||
{overlays}
|
||||
@ -283,7 +296,8 @@ export class ImageThumb extends React.Component<
|
||||
className={cx(
|
||||
'Image',
|
||||
imageMode === 'original' ? 'Image--original' : 'Image--thumb',
|
||||
className
|
||||
className,
|
||||
imageControlClassName
|
||||
)}
|
||||
style={href ? undefined : style} // 避免重复设置style
|
||||
>
|
||||
@ -320,12 +334,18 @@ export class ImageThumb extends React.Component<
|
||||
{title || caption ? (
|
||||
<div key="caption" className={cx('Image-info')}>
|
||||
{title ? (
|
||||
<div className={cx('Image-title')} title={title}>
|
||||
<div
|
||||
className={cx('Image-title', titleControlClassName)}
|
||||
title={title}
|
||||
>
|
||||
{title}
|
||||
</div>
|
||||
) : null}
|
||||
{caption ? (
|
||||
<div className={cx('Image-caption')} title={caption}>
|
||||
<div
|
||||
className={cx('Image-caption', desControlClassName)}
|
||||
title={caption}
|
||||
>
|
||||
{caption}
|
||||
</div>
|
||||
) : null}
|
||||
@ -467,7 +487,15 @@ export class ImageField extends React.Component<ImageFieldProps, object> {
|
||||
placeholder,
|
||||
originalSrc,
|
||||
enlargeAble,
|
||||
imageMode
|
||||
imageMode,
|
||||
wrapperCustomStyle,
|
||||
id,
|
||||
themeCss,
|
||||
imageControlClassName,
|
||||
titleControlClassName,
|
||||
desControlClassName,
|
||||
iconControlClassName,
|
||||
env
|
||||
} = this.props;
|
||||
|
||||
const finnalSrc = src ? filter(src, data, '| raw') : '';
|
||||
@ -481,7 +509,10 @@ export class ImageField extends React.Component<ImageFieldProps, object> {
|
||||
imageMode === 'original'
|
||||
? 'ImageField--original'
|
||||
: 'ImageField--thumb',
|
||||
className
|
||||
className,
|
||||
wrapperCustomStyle
|
||||
? `wrapperCustomStyle-${id?.replace('u:', '')}`
|
||||
: ''
|
||||
)}
|
||||
style={style}
|
||||
onClick={this.handleClick}
|
||||
@ -507,6 +538,32 @@ export class ImageField extends React.Component<ImageFieldProps, object> {
|
||||
) : (
|
||||
<span className="text-muted">{placeholder}</span>
|
||||
)}
|
||||
<CustomStyle
|
||||
config={{
|
||||
wrapperCustomStyle,
|
||||
componentId: id,
|
||||
themeCss,
|
||||
classNames: [
|
||||
{
|
||||
key: 'imageControlClassName',
|
||||
value: imageControlClassName
|
||||
},
|
||||
{
|
||||
key: 'titleControlClassName',
|
||||
value: titleControlClassName
|
||||
},
|
||||
{
|
||||
key: 'desControlClassName',
|
||||
value: desControlClassName
|
||||
},
|
||||
{
|
||||
key: 'iconControlClassName',
|
||||
value: iconControlClassName
|
||||
}
|
||||
]
|
||||
}}
|
||||
env={env}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import {Renderer, RendererProps} from 'amis-core';
|
||||
import {Renderer, RendererProps, CustomStyle} from 'amis-core';
|
||||
import {filter} from 'amis-core';
|
||||
import {
|
||||
resolveVariable,
|
||||
@ -191,7 +191,12 @@ export class ImagesField extends React.Component<ImagesProps> {
|
||||
options,
|
||||
showToolbar,
|
||||
toolbarActions,
|
||||
imageGallaryClassName
|
||||
imageGallaryClassName,
|
||||
id,
|
||||
wrapperCustomStyle,
|
||||
env,
|
||||
themeCss,
|
||||
imagesControlClassName
|
||||
} = this.props;
|
||||
|
||||
let value: any;
|
||||
@ -217,7 +222,17 @@ export class ImagesField extends React.Component<ImagesProps> {
|
||||
this.list = list;
|
||||
|
||||
return (
|
||||
<div className={cx('ImagesField', className)} style={style}>
|
||||
<div
|
||||
className={cx(
|
||||
'ImagesField',
|
||||
className,
|
||||
imagesControlClassName,
|
||||
wrapperCustomStyle
|
||||
? `wrapperCustomStyle-${id?.replace('u:', '')}`
|
||||
: ''
|
||||
)}
|
||||
style={style}
|
||||
>
|
||||
{Array.isArray(list) ? (
|
||||
<div className={cx('Images', listClassName)}>
|
||||
{list.map((item: any, index: number) => (
|
||||
@ -259,6 +274,20 @@ export class ImagesField extends React.Component<ImagesProps> {
|
||||
) : (
|
||||
placeholder
|
||||
)}
|
||||
<CustomStyle
|
||||
config={{
|
||||
wrapperCustomStyle,
|
||||
componentId: id,
|
||||
themeCss,
|
||||
classNames: [
|
||||
{
|
||||
key: 'imagesControlClassName',
|
||||
value: imagesControlClassName
|
||||
}
|
||||
]
|
||||
}}
|
||||
env={env}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -9,7 +9,8 @@ import {
|
||||
ActionObject,
|
||||
Location,
|
||||
ApiObject,
|
||||
FunctionPropertyNames
|
||||
FunctionPropertyNames,
|
||||
CustomStyle
|
||||
} from 'amis-core';
|
||||
import {filter, evalExpression} from 'amis-core';
|
||||
import {
|
||||
@ -781,6 +782,8 @@ export default class Page extends React.Component<PageProps> {
|
||||
remark,
|
||||
remarkPlacement,
|
||||
headerClassName,
|
||||
headerControlClassName,
|
||||
toolbarControlClassName,
|
||||
toolbarClassName,
|
||||
toolbar,
|
||||
render,
|
||||
@ -803,7 +806,9 @@ export default class Page extends React.Component<PageProps> {
|
||||
Array.isArray(regions) ? ~regions.indexOf('header') : title || subTitle
|
||||
) {
|
||||
header = (
|
||||
<div className={cx(`Page-header`, headerClassName)}>
|
||||
<div
|
||||
className={cx(`Page-header`, headerClassName, headerControlClassName)}
|
||||
>
|
||||
{title ? (
|
||||
<h2 className={cx('Page-title')}>
|
||||
{render('title', title, subProps)}
|
||||
@ -828,7 +833,13 @@ export default class Page extends React.Component<PageProps> {
|
||||
|
||||
if (Array.isArray(regions) ? ~regions.indexOf('toolbar') : toolbar) {
|
||||
right = (
|
||||
<div className={cx(`Page-toolbar`, toolbarClassName)}>
|
||||
<div
|
||||
className={cx(
|
||||
`Page-toolbar`,
|
||||
toolbarClassName,
|
||||
toolbarControlClassName
|
||||
)}
|
||||
>
|
||||
{render('toolbar', toolbar || '', subProps)}
|
||||
</div>
|
||||
);
|
||||
@ -865,7 +876,15 @@ export default class Page extends React.Component<PageProps> {
|
||||
pullRefresh,
|
||||
mobileUI,
|
||||
translate: __,
|
||||
loadingConfig
|
||||
loadingConfig,
|
||||
id,
|
||||
wrapperCustomStyle,
|
||||
env,
|
||||
themeCss,
|
||||
bodyControlClassName,
|
||||
headerControlClassName,
|
||||
toolbarControlClassName,
|
||||
asideControlClassName
|
||||
} = this.props;
|
||||
|
||||
const subProps = {
|
||||
@ -887,7 +906,10 @@ export default class Page extends React.Component<PageProps> {
|
||||
<div className={cx('Page-main')}>
|
||||
{this.renderHeader()}
|
||||
{/* role 用于 editor 定位 Spinner */}
|
||||
<div className={cx(`Page-body`, bodyClassName)} role="page-body">
|
||||
<div
|
||||
className={cx(`Page-body`, bodyClassName, bodyControlClassName)}
|
||||
role="page-body"
|
||||
>
|
||||
<Spinner
|
||||
size="lg"
|
||||
overlay
|
||||
@ -916,7 +938,14 @@ export default class Page extends React.Component<PageProps> {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx(`Page`, hasAside ? `Page--withSidebar` : '', className)}
|
||||
className={cx(
|
||||
`Page`,
|
||||
hasAside ? `Page--withSidebar` : '',
|
||||
className,
|
||||
wrapperCustomStyle
|
||||
? `wrapperCustomStyle-${id?.replace('u:', '')}`
|
||||
: ''
|
||||
)}
|
||||
onClick={this.handleClick}
|
||||
style={styleVar}
|
||||
>
|
||||
@ -925,7 +954,8 @@ export default class Page extends React.Component<PageProps> {
|
||||
className={cx(
|
||||
`Page-aside`,
|
||||
asideResizor ? 'relative' : 'Page-aside--withWidth',
|
||||
asideClassName
|
||||
asideClassName,
|
||||
asideControlClassName
|
||||
)}
|
||||
>
|
||||
<div className={cx(`Page-asideInner`)} ref={this.asideInner}>
|
||||
@ -995,6 +1025,32 @@ export default class Page extends React.Component<PageProps> {
|
||||
onQuery: initApi ? this.handleQuery : undefined
|
||||
}
|
||||
)}
|
||||
<CustomStyle
|
||||
config={{
|
||||
wrapperCustomStyle,
|
||||
componentId: id,
|
||||
themeCss,
|
||||
classNames: [
|
||||
{
|
||||
key: 'bodyControlClassName',
|
||||
value: bodyControlClassName
|
||||
},
|
||||
{
|
||||
key: 'headerControlClassName',
|
||||
value: headerControlClassName
|
||||
},
|
||||
{
|
||||
key: 'toolbarControlClassName',
|
||||
value: toolbarControlClassName
|
||||
},
|
||||
{
|
||||
key: 'asideControlClassName',
|
||||
value: asideControlClassName
|
||||
}
|
||||
]
|
||||
}}
|
||||
env={env}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -3,7 +3,12 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {Renderer, RendererProps, resolveMappingObject} from 'amis-core';
|
||||
import {
|
||||
Renderer,
|
||||
RendererProps,
|
||||
resolveMappingObject,
|
||||
CustomStyle
|
||||
} from 'amis-core';
|
||||
import {BaseSchema, SchemaObject} from '../Schema';
|
||||
|
||||
// 为了方便编辑器,目前考虑不区分 th 和 td,但因为可以控制展现,所以能实现一样的效果,同时后续这个组件还承担复杂布局的功能,不适合用 th
|
||||
@ -261,16 +266,47 @@ export default class TableView extends React.Component<TableViewProps, object> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const {width, trs = [], classnames: cx, className} = this.props;
|
||||
const {
|
||||
width,
|
||||
trs = [],
|
||||
classnames: cx,
|
||||
className,
|
||||
id,
|
||||
wrapperCustomStyle,
|
||||
env,
|
||||
themeCss,
|
||||
baseControlClassName
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<table
|
||||
className={cx('TableView', className)}
|
||||
className={cx(
|
||||
'TableView',
|
||||
className,
|
||||
baseControlClassName,
|
||||
wrapperCustomStyle
|
||||
? `wrapperCustomStyle-${id?.replace('u:', '')}`
|
||||
: ''
|
||||
)}
|
||||
style={{width: width, borderCollapse: 'collapse'}}
|
||||
>
|
||||
{this.renderCaption()}
|
||||
{this.renderCols()}
|
||||
<tbody>{this.renderTrs(trs)}</tbody>
|
||||
<CustomStyle
|
||||
config={{
|
||||
wrapperCustomStyle,
|
||||
componentId: id,
|
||||
themeCss,
|
||||
classNames: [
|
||||
{
|
||||
key: 'baseControlClassName',
|
||||
value: baseControlClassName
|
||||
}
|
||||
]
|
||||
}}
|
||||
env={env}
|
||||
/>
|
||||
</table>
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
import {Renderer, RendererProps} from 'amis-core';
|
||||
import {Renderer, RendererProps, CustomStyle} from 'amis-core';
|
||||
import {BaseSchema, SchemaCollection} from '../Schema';
|
||||
import {filter} from 'amis-core';
|
||||
import {escapeHtml} from 'amis-core';
|
||||
@ -191,7 +191,10 @@ export default class TooltipWrapper extends React.Component<
|
||||
inline,
|
||||
style,
|
||||
data,
|
||||
wrap
|
||||
wrap,
|
||||
baseControlClassName,
|
||||
wrapperCustomStyle,
|
||||
id
|
||||
} = this.props;
|
||||
const Comp =
|
||||
(wrapperComponent as keyof JSX.IntrinsicElements) ||
|
||||
@ -199,9 +202,17 @@ export default class TooltipWrapper extends React.Component<
|
||||
|
||||
return (
|
||||
<Comp
|
||||
className={cx('TooltipWrapper', className, {
|
||||
'TooltipWrapper--inline': inline
|
||||
})}
|
||||
className={cx(
|
||||
'TooltipWrapper',
|
||||
className,
|
||||
{
|
||||
'TooltipWrapper--inline': inline
|
||||
},
|
||||
baseControlClassName,
|
||||
wrapperCustomStyle
|
||||
? `wrapperCustomStyle-${id?.replace('u:', '')}`
|
||||
: ''
|
||||
)}
|
||||
style={buildStyle(style, data)}
|
||||
>
|
||||
{render('body', body)}
|
||||
@ -231,7 +242,12 @@ export default class TooltipWrapper extends React.Component<
|
||||
enterable,
|
||||
data,
|
||||
env,
|
||||
popOverContainer
|
||||
popOverContainer,
|
||||
wrapperCustomStyle,
|
||||
id,
|
||||
themeCss,
|
||||
baseControlClassName,
|
||||
tooltipControlClassName
|
||||
} = this.props;
|
||||
|
||||
const tooltipObj: TooltipObject = {
|
||||
@ -246,7 +262,9 @@ export default class TooltipWrapper extends React.Component<
|
||||
? container
|
||||
: popOverContainer || env?.getModalContainer,
|
||||
tooltipTheme,
|
||||
tooltipClassName,
|
||||
tooltipClassName: tooltipControlClassName
|
||||
? tooltipClassName + ' ' + tooltipControlClassName
|
||||
: tooltipClassName,
|
||||
mouseEnterDelay,
|
||||
mouseLeaveDelay,
|
||||
offset,
|
||||
@ -257,9 +275,33 @@ export default class TooltipWrapper extends React.Component<
|
||||
};
|
||||
|
||||
return (
|
||||
<TooltipWrapperComp classPrefix={ns} classnames={cx} tooltip={tooltipObj}>
|
||||
{this.renderBody()}
|
||||
</TooltipWrapperComp>
|
||||
<>
|
||||
<TooltipWrapperComp
|
||||
classPrefix={ns}
|
||||
classnames={cx}
|
||||
tooltip={tooltipObj}
|
||||
>
|
||||
{this.renderBody()}
|
||||
</TooltipWrapperComp>
|
||||
<CustomStyle
|
||||
config={{
|
||||
wrapperCustomStyle,
|
||||
componentId: id,
|
||||
themeCss,
|
||||
classNames: [
|
||||
{
|
||||
key: 'baseControlClassName',
|
||||
value: baseControlClassName
|
||||
},
|
||||
{
|
||||
key: 'tooltipControlClassName',
|
||||
value: tooltipControlClassName
|
||||
}
|
||||
]
|
||||
}}
|
||||
env={env}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,11 @@
|
||||
import React from 'react';
|
||||
import {autobind, createObject, Renderer, RendererProps} from 'amis-core';
|
||||
import {
|
||||
autobind,
|
||||
createObject,
|
||||
Renderer,
|
||||
RendererProps,
|
||||
CustomStyle
|
||||
} from 'amis-core';
|
||||
import {filter, asyncFilter} from 'amis-core';
|
||||
import cx from 'classnames';
|
||||
import {anyChanged, getPropValue} from 'amis-core';
|
||||
@ -189,14 +195,25 @@ export class Tpl extends React.Component<TplProps, TplState> {
|
||||
style,
|
||||
showNativeTitle,
|
||||
data,
|
||||
env
|
||||
id,
|
||||
wrapperCustomStyle,
|
||||
env,
|
||||
themeCss,
|
||||
baseControlClassName
|
||||
} = this.props;
|
||||
const Component = wrapperComponent || (inline ? 'span' : 'div');
|
||||
const {content} = this.state;
|
||||
|
||||
return (
|
||||
<Component
|
||||
className={cx('TplField', className)}
|
||||
className={cx(
|
||||
'TplField',
|
||||
className,
|
||||
baseControlClassName,
|
||||
wrapperCustomStyle
|
||||
? `wrapperCustomStyle-${id?.replace('u:', '')}`
|
||||
: ''
|
||||
)}
|
||||
style={buildStyle(style, data)}
|
||||
{...(showNativeTitle ? {title: this.getTitle(content)} : {})}
|
||||
onClick={this.handleClick}
|
||||
@ -206,6 +223,20 @@ export class Tpl extends React.Component<TplProps, TplState> {
|
||||
<span
|
||||
dangerouslySetInnerHTML={{__html: env.filterHtml(content)}}
|
||||
></span>
|
||||
<CustomStyle
|
||||
config={{
|
||||
wrapperCustomStyle,
|
||||
componentId: id,
|
||||
themeCss,
|
||||
classNames: [
|
||||
{
|
||||
key: 'baseControlClassName',
|
||||
value: baseControlClassName
|
||||
}
|
||||
]
|
||||
}}
|
||||
env={env}
|
||||
/>
|
||||
</Component>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user