From 97820a10755a4b286ac01f31217e792542a45802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E5=A4=9A=E7=9B=8A?= Date: Tue, 15 Feb 2022 16:23:46 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20page=20=E7=9A=84=20style=20=E9=87=8C?= =?UTF-8?q?=E9=92=88=E5=AF=B9=20backgroundImage=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=8A=A0=E4=B8=8A=20url=20(#3581)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __mocks__/monaco.ts | 23 ++++++++ .../__snapshots__/Page.test.tsx.snap | 16 ------ __tests__/utils/style.test.ts | 41 ++++++++++++++ src/components/Badge.tsx | 9 ++- src/components/Card.tsx | 7 ++- src/renderers/Container.tsx | 9 ++- src/renderers/Page.tsx | 6 +- src/renderers/Property.tsx | 19 ++++--- src/renderers/Tpl.tsx | 6 +- src/renderers/Wrapper.tsx | 8 +-- src/utils/style.ts | 56 +++++++++++++++++++ 11 files changed, 158 insertions(+), 42 deletions(-) create mode 100644 __mocks__/monaco.ts create mode 100644 __tests__/utils/style.test.ts create mode 100644 src/utils/style.ts diff --git a/__mocks__/monaco.ts b/__mocks__/monaco.ts new file mode 100644 index 000000000..a441da336 --- /dev/null +++ b/__mocks__/monaco.ts @@ -0,0 +1,23 @@ +/** + * monaco 加载没搞定,可能后续会先 mock + */ + +export const languages = { + register: function (language: any) {}, + setMonarchTokensProvider: function (name: any, tokens: any) {}, + registerCompletionItemProvider: function (name: any, provider: any) {} +}; + +export const editor = { + defineTheme: function (name: any, theme: any) {}, + create: function (name: any, theme: any) { + return { + onDidChangeModelDecorations: () => { + return { + dispose: () => {} + }; + }, + dispose: () => {} + }; + } +}; diff --git a/__tests__/renderers/__snapshots__/Page.test.tsx.snap b/__tests__/renderers/__snapshots__/Page.test.tsx.snap index fcd25a5e4..294d2c0f4 100644 --- a/__tests__/renderers/__snapshots__/Page.test.tsx.snap +++ b/__tests__/renderers/__snapshots__/Page.test.tsx.snap @@ -4,7 +4,6 @@ exports[`Renderer:Page 1`] = `
{ + expect( + buildStyle('${style}', { + style: { + backgroundImage: 'http://www.example.com/a.png' + } + }) + ).toEqual({ + backgroundImage: 'url("http://www.example.com/a.png")' + }); + + expect( + buildStyle( + { + backgroundImage: 'http://www.example.com/a.png' + }, + {} + ) + ).toEqual({ + backgroundImage: 'url("http://www.example.com/a.png")' + }); +}); + +test('style case', () => { + expect( + buildStyle( + { + 'font-size': '10' + }, + {} + ) + ).toEqual({ + fontSize: '10' + }); +}); diff --git a/src/components/Badge.tsx b/src/components/Badge.tsx index 1f96525d4..213e64f14 100644 --- a/src/components/Badge.tsx +++ b/src/components/Badge.tsx @@ -8,6 +8,7 @@ import {BaseSchema, SchemaExpression} from '../Schema'; import {evalExpression} from '../utils/tpl'; import {resolveVariable, resolveVariableAndFilter} from '../utils/tpl-builtin'; import {ClassNamesFn} from '../theme'; +import {buildStyle} from '../utils/style'; /** * Badge 角标。 @@ -91,6 +92,8 @@ export class Badge extends React.Component { const {classnames: cx, badge, data} = this.props; let {mode = 'dot', level = 'danger', style} = badge as BadgeSchema; + const customStyle = buildStyle(style, data); + if (typeof level === 'string' && level[0] === '$') { level = resolveVariableAndFilter(level, data); } @@ -100,7 +103,7 @@ export class Badge extends React.Component { return ( {animationElement} @@ -113,7 +116,7 @@ export class Badge extends React.Component { `Badge--${position}`, `Badge--${level}` )} - style={{...offsetStyle, ...sizeStyle, ...style}} + style={{...offsetStyle, ...sizeStyle, ...customStyle}} > {text} {animationElement} @@ -132,7 +135,7 @@ export class Badge extends React.Component { `Badge-ribbon--${position}`, `Badge--${level}` )} - style={{...sizeStyle, ...style}} + style={{...sizeStyle, ...customStyle}} > {text} {animationElement} diff --git a/src/components/Card.tsx b/src/components/Card.tsx index d16db0299..bcfa8c6c4 100644 --- a/src/components/Card.tsx +++ b/src/components/Card.tsx @@ -1,6 +1,7 @@ import React from 'react'; import {isClickOnInput} from '../utils/helper'; import {ClassNamesFn, themeable, ThemeProps} from '../theme'; +import {buildStyle} from '../utils/style'; export interface CardProps extends ThemeProps { className?: string; headerClassName?: string; @@ -29,6 +30,7 @@ export interface CardProps extends ThemeProps { secondary?: string | JSX.Element; onClick?: (e: React.MouseEvent) => void; classnames: ClassNamesFn; + data?: any; } export class Card extends React.Component { @@ -88,7 +90,8 @@ export class Card extends React.Component { descriptionPlaceholder, secondary, avatar, - avatarText + avatarText, + data } = this.props; let heading = null; @@ -111,7 +114,7 @@ export class Card extends React.Component { ) : avatarText ? ( {avatarText} diff --git a/src/renderers/Container.tsx b/src/renderers/Container.tsx index 060c3994c..22648ee07 100644 --- a/src/renderers/Container.tsx +++ b/src/renderers/Container.tsx @@ -2,6 +2,7 @@ import React from 'react'; import {Renderer, RendererProps} from '../factory'; import {BaseSchema, SchemaClassName, SchemaCollection} from '../Schema'; import {SchemaNode} from '../types'; +import {buildStyle} from '../utils/style'; /** * Container 容器渲染器。 @@ -80,14 +81,18 @@ export default class Container extends React.Component< wrapperComponent, size, classnames: cx, - style + style, + data } = this.props; const Component = (wrapperComponent as keyof JSX.IntrinsicElements) || 'div'; return ( - + {this.renderBody()} ); diff --git a/src/renderers/Page.tsx b/src/renderers/Page.tsx index d472f0f36..c027add47 100644 --- a/src/renderers/Page.tsx +++ b/src/renderers/Page.tsx @@ -31,6 +31,7 @@ import {SchemaRemark} from './Remark'; import {onAction} from 'mobx-state-tree'; import mapValues from 'lodash/mapValues'; import {resolveVariable} from '../utils/tpl-builtin'; +import {buildStyle} from '../utils/style'; /** * 样式属性名及值 @@ -722,10 +723,7 @@ export default class Page extends React.Component { ? ~regions.indexOf('aside') : aside && (!Array.isArray(aside) || aside.length); - let styleVar = - typeof style === 'string' - ? resolveVariable(style, data) || {} - : mapValues(style, s => resolveVariable(s, data) || s); + const styleVar = buildStyle(style, data); return (
{ contentStyle, labelStyle, separator = ': ', - mode = 'table' + mode = 'table', + data } = this.props; return rows.map((row, key) => { return ( @@ -175,10 +177,12 @@ export default class Property extends React.Component { {row.map((property, index) => { return mode === 'table' ? ( - {render('label', property.label)} + + {render('label', property.label)} + {render('content', property.content)} @@ -186,10 +190,10 @@ export default class Property extends React.Component { ) : ( - + {render('label', property.label)} {separator} @@ -210,6 +214,7 @@ export default class Property extends React.Component { classnames: cx, className, titleStyle, + data, mode = 'table' } = this.props; @@ -218,7 +223,7 @@ export default class Property extends React.Component { return (
{title ? ( @@ -226,7 +231,7 @@ export default class Property extends React.Component { diff --git a/src/renderers/Tpl.tsx b/src/renderers/Tpl.tsx index dc2291785..7b039853f 100644 --- a/src/renderers/Tpl.tsx +++ b/src/renderers/Tpl.tsx @@ -6,6 +6,7 @@ import {anyChanged, getPropValue} from '../utils/helper'; import {escapeHtml} from '../utils/tpl-builtin'; import {BaseSchema, SchemaTpl} from '../Schema'; import {BadgeSchema, withBadge} from '../components/Badge'; +import {buildStyle} from '../utils/style'; /** * tpl 渲染器 @@ -115,7 +116,8 @@ export class Tpl extends React.Component { wrapperComponent, inline, classnames: cx, - style + style, + data } = this.props; const Component = wrapperComponent || (inline ? 'span' : 'div'); @@ -123,7 +125,7 @@ export class Tpl extends React.Component { {this.getContent()} diff --git a/src/renderers/Wrapper.tsx b/src/renderers/Wrapper.tsx index adb445dbe..efe2e1a59 100644 --- a/src/renderers/Wrapper.tsx +++ b/src/renderers/Wrapper.tsx @@ -4,6 +4,7 @@ import {BaseSchema, SchemaCollection} from '../Schema'; import {resolveVariable} from '../utils/tpl-builtin'; import {SchemaNode} from '../types'; import mapValues from 'lodash/mapValues'; +import {buildStyle} from '../utils/style'; /** * Wrapper 容器渲染器。 @@ -65,11 +66,6 @@ export default class Wrapper extends React.Component { return this.renderBody(); } - let styleVar = - typeof style === 'string' - ? resolveVariable(style, data) || {} - : mapValues(style, s => resolveVariable(s, data) || s); - return (
{ size && size !== 'none' ? `Wrapper--${size}` : '', className )} - style={styleVar} + style={buildStyle(style, data)} > {this.renderBody()}
diff --git a/src/utils/style.ts b/src/utils/style.ts new file mode 100644 index 000000000..d11351269 --- /dev/null +++ b/src/utils/style.ts @@ -0,0 +1,56 @@ +/** + * 处理样式相关的工具方法,不放 helper 里是为了避免循环依赖 + */ + +import {resolveVariableAndFilter} from './tpl-builtin'; +import mapValues from 'lodash/mapValues'; +import camelCase from 'lodash/camelCase'; + +function autoAddImageURL(image: string) { + // 只支持单个的情况,并简单滤掉 linear-gradient 等情况 + if ( + typeof image === 'string' && + image.indexOf(',') === -1 && + image.indexOf('(') === -1 + ) { + return `url("${image}")`; + } + return image; +} + +/** + * 处理配置中的 style,主要做三件事: + * 1. 变量解析 + * 2. 将 font-size 之类的错误写法转成 fontSize + * 3. 针对 image 自动加 url + */ +export function buildStyle(style: any, data: any) { + if (!style) { + return style; + } + let styleVar = + typeof style === 'string' + ? resolveVariableAndFilter(style, data, '| raw') || {} + : mapValues(style, s => resolveVariableAndFilter(s, data, '| raw') || s); + + Object.keys(styleVar).forEach((key: string) => { + if (key.indexOf('-') !== -1) { + styleVar[camelCase(key)] = styleVar[key]; + delete styleVar[key]; + } + }); + + if (styleVar.backgroundImage) { + styleVar.backgroundImage = autoAddImageURL(styleVar.backgroundImage); + } + + if (styleVar.borderImage) { + styleVar.borderImage = autoAddImageURL(styleVar.borderImage); + } + + if (styleVar.listStyleImage) { + styleVar.listStyleImage = autoAddImageURL(styleVar.listStyleImage); + } + + return styleVar; +}
{title}