Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: 自定义样式抽象成装饰器 #11139

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 66 additions & 7 deletions packages/amis-core/src/components/CustomStyle.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import {useEffect, useRef} from 'react';
import type {RendererEnv} from '../env';
import {
removeCustomStyle,
type InsertCustomStyle,
insertCustomStyle,
insertEditCustomStyle
insertEditCustomStyle,
CustomStyleClassName,
setThemeClassName
} from '../utils/style-helper';
import React from 'react';
import {PlainObject} from '../types';
import cx from 'classnames';

interface CustomStyleProps {
config: {
wrapperCustomStyle?: any;
componentId?: string;
} & InsertCustomStyle;
[propName: string]: any;
}

export const styleIdCount = new Map();

export default function (props: CustomStyleProps) {
const {config, env, data} = props;
export default function CustomStyle(props: CustomStyleProps) {
const {config, env, data, children, classPrefix} = props;
const {themeCss, classNames, defaultData, wrapperCustomStyle} = config;
const id = config.id ? `${config.id}` : config.id;

Expand Down Expand Up @@ -47,7 +50,8 @@ export default function (props: CustomStyleProps) {
defaultData,
customStyleClassPrefix: env?.customStyleClassPrefix,
doc: env?.getModalContainer?.()?.ownerDocument,
data
data,
classPrefix
});
}

Expand Down Expand Up @@ -85,5 +89,60 @@ export default function (props: CustomStyleProps) {
};
}, [wrapperCustomStyle, id]);

return null;
return children;
}

/**
* 自定义样式装饰器
*/
export function CustomStyleWrapper(config: {
classNames: CustomStyleClassName[];
wrapperCustomStyle?: boolean;
themeCss?: string;
id?: string;
}) {
return function <T extends React.ComponentType>(Component: T): T {
const WrappedComponent = (props: any) => {
const id = config.id || props.id;
const themeCss = props[config.themeCss || 'themeCss'];

const [className, setClassName] = React.useState<PlainObject>({});

useEffect(() => {
const className: PlainObject = {};
config.classNames.forEach(item => {
if (item.name) {
className[item.name] = cx(
setThemeClassName({
props,
name: item.key,
id,
themeCss
}),
props[item.name]
);
}
});
setClassName(className);
}, [props]);

return (
<>
<CustomStyle
config={{
classNames: config.classNames,
id,
themeCss,
wrapperCustomStyle: config.wrapperCustomStyle
? props.wrapperCustomStyle
: null
}}
{...props}
/>
<Component {...props} {...className} />
</>
);
};
return WrappedComponent as unknown as T;
};
}
3 changes: 2 additions & 1 deletion packages/amis-core/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ import {
SchemaRenderer
} from './SchemaRenderer';
import type {IItem} from './store/list';
import CustomStyle from './components/CustomStyle';
import CustomStyle, {CustomStyleWrapper} from './components/CustomStyle';
import {StatusScoped} from './StatusScoped';

import styleManager from './StyleManager';
Expand Down Expand Up @@ -231,6 +231,7 @@ export {
filterTarget,
splitTarget,
CustomStyle,
CustomStyleWrapper,
enableDebug,
disableDebug,
envOverwrite,
Expand Down
45 changes: 35 additions & 10 deletions packages/amis-core/src/utils/style-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ export const inheritValueMap: PlainObject = {

interface extra {
important?: boolean;
parent?: string;
inner?: string;
pre?: string;
suf?: string;
parent?: string | ((ns: string) => string);
inner?: string | ((ns: string) => string);
pre?: string | ((ns: string) => string);
suf?: string | ((ns: string) => string);
}

/**
Expand Down Expand Up @@ -169,7 +169,8 @@ export function formatStyle(
classNames?: CustomStyleClassName[],
id?: string,
defaultData?: any,
data?: any
data?: any,
classPrefix?: string
) {
// 没有具体的样式,或者没有对应的classname
if (!themeCss || !classNames) {
Expand Down Expand Up @@ -272,9 +273,23 @@ export function formatStyle(
}
});
if (styles.length > 0) {
const cx = (weights?.pre || '') + className + (weights?.suf || '');
const inner = weights?.inner || '';
const parent = weights?.parent || '';
const pre =
typeof weights?.pre === 'function'
? weights.pre(classPrefix)
: weights?.pre || '';
const suf =
typeof weights?.suf === 'function'
? weights.suf(classPrefix)
: weights?.suf || '';
const cx = pre + className + suf;
const inner =
typeof weights?.inner === 'function'
? weights.inner(classPrefix)
: weights?.inner || '';
const parent =
weights?.parent === 'function'
? weights.parent(classPrefix)
: weights?.parent || '';

res.push({
className: parent + cx + status2string[status] + inner,
Expand All @@ -300,6 +315,7 @@ export function formatStyle(

export interface CustomStyleClassName {
key: string;
name?: string;
weights?: {
default?: extra;
hover?: extra;
Expand All @@ -316,6 +332,7 @@ export function insertCustomStyle(params: {
defaultData?: any;
customStyleClassPrefix?: string;
doc?: Document;
classPrefix?: string;
[propName: string]: any;
}) {
const {
Expand All @@ -325,13 +342,21 @@ export function insertCustomStyle(params: {
defaultData,
customStyleClassPrefix,
doc,
data
data,
classPrefix
} = params;
if (!themeCss) {
return;
}

let {value} = formatStyle(themeCss, classNames, id, defaultData, data);
let {value} = formatStyle(
themeCss,
classNames,
id,
defaultData,
data,
classPrefix
);
value = customStyleClassPrefix ? `${customStyleClassPrefix} ${value}` : value;
let classId = id?.replace?.('u:', '') || id + '';
if (typeof data?.index === 'number') {
Expand Down
93 changes: 38 additions & 55 deletions packages/amis/src/renderers/Form/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ import {
autobind,
TestIdBuilder,
getVariable,
CustomStyle,
setThemeClassName
CustomStyleWrapper
} from 'amis-core';
import {TransferDropDown, Spinner, Select, SpinnerExtraProps} from 'amis-ui';
import {FormOptionsSchema, SchemaApi} from '../../Schema';
Expand Down Expand Up @@ -526,6 +525,7 @@ export default class SelectControl extends React.Component<SelectProps, any> {
showInvalidMatch,
options,
className,
controlClassName,
popoverClassName,
style,
loading,
Expand All @@ -550,7 +550,6 @@ export default class SelectControl extends React.Component<SelectProps, any> {
filterOption,
...rest
} = this.props;
const {classPrefix: ns, themeCss} = this.props;

if (noResultsText) {
noResultsText = render('noResultText', noResultsText);
Expand All @@ -571,23 +570,8 @@ export default class SelectControl extends React.Component<SelectProps, any> {
onAdd={this.handleOptionAdd}
onEdit={this.handleOptionEdit}
onDelete={this.handleOptionDelete}
className={cx(
setThemeClassName({
...this.props,
name: 'selectControlClassName',
id,
themeCss: themeCss
})
)}
popoverClassName={cx(
popoverClassName,
setThemeClassName({
...this.props,
name: 'selectPopoverClassName',
id,
themeCss: themeCss
})
)}
className={controlClassName}
popoverClassName={popoverClassName}
mobileUI={mobileUI}
popOverContainer={
mobileUI
Expand Down Expand Up @@ -620,41 +604,6 @@ export default class SelectControl extends React.Component<SelectProps, any> {
overlay={overlay}
/>
)}
<CustomStyle
{...this.props}
config={{
themeCss: themeCss,
classNames: [
{
key: 'selectControlClassName',
weights: {
focused: {
suf: '.is-opened:not(.is-mobile)'
},
disabled: {
suf: '.is-disabled'
}
}
},
{
key: 'selectPopoverClassName',
weights: {
default: {
suf: ` .${ns}Select-option`
},
hover: {
suf: ` .${ns}Select-option.is-highlight`
},
focused: {
inner: `.${ns}Select-option.is-active`
}
}
}
],
id: id
}}
env={env}
/>
</div>
);
}
Expand Down Expand Up @@ -789,14 +738,48 @@ class TransferDropdownRenderer extends BaseTransferRenderer<TransferDropDownProp
}
}

const customStyleOptions = {
classNames: [
{
key: 'selectControlClassName',
name: 'controlClassName',
weights: {
focused: {
suf: '.is-opened:not(.is-mobile)'
},
disabled: {
suf: '.is-disabled'
}
}
},
{
key: 'selectPopoverClassName',
name: 'popoverClassName',
weights: {
default: {
suf: (ns: string) => ` .${ns}Select-option`
},
hover: {
suf: (ns: string) => ` .${ns}Select-option.is-highlight`
},
focused: {
inner: (ns: string) => `.${ns}Select-option.is-active`
}
}
}
]
};

@OptionsControl({
type: 'select'
})
@CustomStyleWrapper(customStyleOptions)
export class SelectControlRenderer extends SelectControl {}

@OptionsControl({
type: 'multi-select'
})
@CustomStyleWrapper(customStyleOptions)
export class MultiSelectControlRenderer extends SelectControl {
static defaultProps = {
multiple: true
Expand Down
Loading