mirror of
https://github.com/ant-design/ant-design.git
synced 2025-01-18 06:03:38 +08:00
refactor: tree support cssinjs (#34258)
* chore: part of tree * chore: tree cssinjs * chore: fix lint * refactor: RTL support * fix: checkbox hover logic * chore: rtl use same style * fix: check style priority
This commit is contained in:
parent
20ae690b14
commit
989a27ada8
@ -20,6 +20,8 @@ const defaultDesignToken: DesignToken = {
|
||||
textColor: new TinyColor('#000').setAlpha(0.85).toRgbString(),
|
||||
textColorDisabled: new TinyColor('#000').setAlpha(0.25).toRgbString(),
|
||||
|
||||
itemHoverBackground: '#f5f5f5',
|
||||
|
||||
height: 32,
|
||||
|
||||
padding: 16,
|
||||
|
@ -22,6 +22,8 @@ export interface DesignToken {
|
||||
textColor: string;
|
||||
textColorDisabled: string;
|
||||
|
||||
itemHoverBackground: string;
|
||||
|
||||
height: number;
|
||||
|
||||
padding: number;
|
||||
@ -40,6 +42,7 @@ export interface DerivativeToken extends Omit<DesignToken, 'duration'> {
|
||||
primaryActiveColor: string;
|
||||
errorHoverColor: string;
|
||||
errorActiveColor: string;
|
||||
itemActiveBackground: string;
|
||||
|
||||
linkColor: string;
|
||||
fontSizeSM: number;
|
||||
@ -51,6 +54,9 @@ export interface DerivativeToken extends Omit<DesignToken, 'duration'> {
|
||||
|
||||
duration: string;
|
||||
durationFast: string;
|
||||
|
||||
// TMP
|
||||
tmpPrimaryHoverColorWeak: string;
|
||||
}
|
||||
|
||||
export { useStyleRegister };
|
||||
@ -63,12 +69,15 @@ function derivative(designToken: DesignToken): DerivativeToken {
|
||||
return {
|
||||
...designToken,
|
||||
|
||||
tmpPrimaryHoverColorWeak: primaryColors[0],
|
||||
primaryHoverColor: primaryColors[4],
|
||||
primaryActiveColor: primaryColors[6],
|
||||
|
||||
errorHoverColor: errorColors[4],
|
||||
errorActiveColor: errorColors[6],
|
||||
|
||||
itemActiveBackground: primaryColors[1],
|
||||
|
||||
linkColor: designToken.primaryColor,
|
||||
fontSizeSM: designToken.fontSize - 2,
|
||||
fontSizeLG: designToken.fontSize + 2,
|
||||
|
@ -1,6 +1,3 @@
|
||||
// import '../../style/index.less';
|
||||
// import './index.less';
|
||||
|
||||
// deps-lint-skip-all
|
||||
import { CSSObject, Keyframes } from '@ant-design/cssinjs';
|
||||
import { DerivativeToken, useStyleRegister, useToken, resetComponent } from '../../_util/theme';
|
||||
@ -66,6 +63,7 @@ export const genCheckboxStyle = (
|
||||
position: 'relative',
|
||||
whiteSpace: 'nowrap',
|
||||
lineHeight: 1,
|
||||
cursor: 'pointer',
|
||||
|
||||
// Wrapper > Checkbox > input
|
||||
'&-input': {
|
||||
@ -135,12 +133,25 @@ export const genCheckboxStyle = (
|
||||
},
|
||||
},
|
||||
|
||||
// ==================== Checked ====================
|
||||
// ===================== Hover =====================
|
||||
// Wrapper
|
||||
[`${wrapperCls}:hover ${checkboxCls}:after`]: {
|
||||
visibility: 'visible',
|
||||
},
|
||||
|
||||
// Wrapper & Wrapper > Checkbox
|
||||
[`
|
||||
${wrapperCls}:hover:not(${wrapperCls}-disabled),
|
||||
${checkboxCls}:hover:not(${checkboxCls}-disabled),
|
||||
${checkboxCls}-input:focus +
|
||||
`]: {
|
||||
[`${checkboxCls}-inner`]: {
|
||||
borderColor: token.primaryColor,
|
||||
},
|
||||
},
|
||||
|
||||
// ==================== Checked ====================
|
||||
|
||||
// Wrapper > Checkbox
|
||||
[`${checkboxCls}-checked`]: {
|
||||
[`${checkboxCls}-inner`]: {
|
||||
@ -172,7 +183,7 @@ export const genCheckboxStyle = (
|
||||
|
||||
// ==================== Disable ====================
|
||||
// Wrapper
|
||||
[`${wrapperCls}:disabled`]: {
|
||||
[`${wrapperCls}-disabled`]: {
|
||||
cursor: 'not-allowed',
|
||||
},
|
||||
|
||||
@ -181,7 +192,6 @@ export const genCheckboxStyle = (
|
||||
// Wrapper > Checkbox > input
|
||||
[`&, ${checkboxCls}-input`]: {
|
||||
cursor: 'not-allowed',
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
|
||||
// Wrapper > Checkbox > inner
|
||||
@ -206,11 +216,14 @@ export const genCheckboxStyle = (
|
||||
};
|
||||
|
||||
// ============================== Export ==============================
|
||||
export function getStyle(prefixCls: string, token: DerivativeToken, hashId: string) {
|
||||
return [genCheckboxStyle(prefixCls, token, hashId), antCheckboxEffect];
|
||||
}
|
||||
|
||||
export default function useStyle(prefixCls: string) {
|
||||
const [theme, token, hashId] = useToken();
|
||||
|
||||
return useStyleRegister({ theme, token, hashId, path: [prefixCls] }, () => [
|
||||
genCheckboxStyle(prefixCls, token, hashId),
|
||||
antCheckboxEffect,
|
||||
]);
|
||||
return useStyleRegister({ theme, token, hashId, path: [prefixCls] }, () =>
|
||||
getStyle(prefixCls, token, hashId),
|
||||
);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import { ConfigContext } from '../config-provider';
|
||||
import collapseMotion from '../_util/motion';
|
||||
import renderSwitcherIcon from './utils/iconUtil';
|
||||
import dropIndicatorRender from './utils/dropIndicator';
|
||||
import useStyle from './style';
|
||||
|
||||
export interface AntdTreeNodeAttribute {
|
||||
eventKey: string;
|
||||
@ -172,6 +173,8 @@ const Tree = React.forwardRef<RcTree, TreeProps>((props, ref) => {
|
||||
dropIndicatorRender,
|
||||
};
|
||||
|
||||
const wrapSSR = useStyle(prefixCls);
|
||||
|
||||
const draggableConfig = React.useMemo(() => {
|
||||
if (!draggable) {
|
||||
return false;
|
||||
@ -198,7 +201,7 @@ const Tree = React.forwardRef<RcTree, TreeProps>((props, ref) => {
|
||||
return mergedDraggable;
|
||||
}, [draggable]);
|
||||
|
||||
return (
|
||||
return wrapSSR(
|
||||
<RcTree
|
||||
itemHeight={20}
|
||||
ref={ref}
|
||||
@ -223,7 +226,7 @@ const Tree = React.forwardRef<RcTree, TreeProps>((props, ref) => {
|
||||
draggable={draggableConfig as any}
|
||||
>
|
||||
{children}
|
||||
</RcTree>
|
||||
</RcTree>,
|
||||
);
|
||||
}) as unknown as CompoundedComponent;
|
||||
|
||||
|
@ -1,16 +1,16 @@
|
||||
@import '../../style/themes/index';
|
||||
@import '../../style/mixins/index';
|
||||
@import '../../checkbox/style/mixin';
|
||||
@import './mixin';
|
||||
@import './directory';
|
||||
// @import '../../style/themes/index';
|
||||
// @import '../../style/mixins/index';
|
||||
// @import '../../checkbox/style/mixin';
|
||||
// @import './mixin';
|
||||
// @import './directory';
|
||||
|
||||
@tree-prefix-cls: ~'@{ant-prefix}-tree';
|
||||
@tree-node-prefix-cls: ~'@{tree-prefix-cls}-treenode';
|
||||
// @tree-prefix-cls: ~'@{ant-prefix}-tree';
|
||||
// @tree-node-prefix-cls: ~'@{tree-prefix-cls}-treenode';
|
||||
|
||||
.antCheckboxFn(@checkbox-prefix-cls: ~'@{ant-prefix}-tree-checkbox');
|
||||
// .antCheckboxFn(@checkbox-prefix-cls: ~'@{ant-prefix}-tree-checkbox');
|
||||
|
||||
.@{tree-prefix-cls} {
|
||||
.antTreeFn(@tree-prefix-cls);
|
||||
}
|
||||
// .@{tree-prefix-cls} {
|
||||
// .antTreeFn(@tree-prefix-cls);
|
||||
// }
|
||||
|
||||
@import './rtl';
|
||||
// @import './rtl';
|
||||
|
@ -1,2 +1,438 @@
|
||||
import '../../style/index.less';
|
||||
import './index.less';
|
||||
// import '../../style/index.less';
|
||||
// import './index.less';
|
||||
|
||||
// deps-lint-skip-all
|
||||
import { CSSObject, CSSInterpolation, Keyframes } from '@ant-design/cssinjs';
|
||||
import { DerivativeToken, useStyleRegister, useToken, resetComponent } from '../../_util/theme';
|
||||
import { getStyle as getCheckboxStyle } from '../../checkbox/style';
|
||||
|
||||
// ============================ Keyframes =============================
|
||||
const treeNodeFX = new Keyframes('ant-tree-node-fx-do-not-use', {
|
||||
'0%': {
|
||||
opacity: 0,
|
||||
},
|
||||
'100%': {
|
||||
opacity: 1,
|
||||
},
|
||||
});
|
||||
|
||||
// ============================== Switch ==============================
|
||||
const getSwitchStyle = (prefixCls: string, token: DerivativeToken): CSSObject => ({
|
||||
[`.${prefixCls}-switcher-icon`]: {
|
||||
display: 'inline-block',
|
||||
fontSize: 10, // FIXME: missing token
|
||||
verticalAlign: 'baseline',
|
||||
|
||||
svg: {
|
||||
transition: `transform ${token.duration}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// =============================== Drop ===============================
|
||||
const getDropIndicatorStyle = (prefixCls: string, token: DerivativeToken) => ({
|
||||
[`.${prefixCls}-drop-indicator`]: {
|
||||
position: 'absolute',
|
||||
// it should displayed over the following node
|
||||
zIndex: 1,
|
||||
height: 2,
|
||||
backgroundColor: token.primaryColor,
|
||||
borderRadius: 1,
|
||||
pointerEvents: 'none',
|
||||
|
||||
'&:after': {
|
||||
position: 'absolute',
|
||||
top: -3,
|
||||
left: -6,
|
||||
width: 8,
|
||||
height: 8,
|
||||
backgroundColor: 'transparent',
|
||||
border: `2px solid ${token.primaryColor}`,
|
||||
borderRadius: '50%',
|
||||
content: '""',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// =============================== Base ===============================
|
||||
type TreeToken = DerivativeToken & {
|
||||
treeCls: string;
|
||||
treeNodeCls: string;
|
||||
treeNodePadding: number;
|
||||
treeTitleHeight: number;
|
||||
};
|
||||
|
||||
export const genBaseStyle = (prefixCls: string, token: TreeToken, hashId: string): CSSObject => {
|
||||
const { treeCls, treeNodeCls, treeNodePadding, treeTitleHeight } = token;
|
||||
|
||||
const treeCheckBoxMarginVertical = (treeTitleHeight - token.fontSizeLG) / 2;
|
||||
const treeCheckBoxMarginHorizontal = token.paddingXS;
|
||||
|
||||
return {
|
||||
[treeCls]: {
|
||||
...resetComponent(token),
|
||||
background: token.componentBackground,
|
||||
borderRadius: token.borderRadius,
|
||||
transition: `background-color ${token.duration}`,
|
||||
|
||||
'&-focused:not(:hover):not(&-active-focused)': {
|
||||
background: token.tmpPrimaryHoverColorWeak,
|
||||
},
|
||||
|
||||
// =================== Virtual List ===================
|
||||
'&-list-holder-inner': {
|
||||
alignItems: 'flex-start',
|
||||
},
|
||||
|
||||
[`&${treeCls}-block-node`]: {
|
||||
[`${treeCls}-list-holder-inner`]: {
|
||||
alignItems: 'stretch',
|
||||
|
||||
// >>> Title
|
||||
[`${treeCls}-node-content-wrapper`]: {
|
||||
flex: 'auto',
|
||||
display: 'flex',
|
||||
flexWrap: 'nowrap',
|
||||
},
|
||||
|
||||
// >>> Drag
|
||||
[`${treeNodeCls}.dragging`]: {
|
||||
position: 'relative',
|
||||
|
||||
'&:after': {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: treeNodePadding,
|
||||
left: 0,
|
||||
border: `1px solid ${token.primaryColor}`,
|
||||
opacity: 0,
|
||||
animation: `${treeNodeFX.getName(hashId)} ${token.duration}`,
|
||||
animationPlayState: 'running',
|
||||
animationFillMode: 'forwards',
|
||||
content: '""',
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// ===================== TreeNode =====================
|
||||
[`${treeNodeCls}`]: {
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
padding: `0 0 ${treeNodePadding}px 0`,
|
||||
outline: 'none',
|
||||
|
||||
// Disabled
|
||||
'&-disabled': {
|
||||
// >>> Title
|
||||
[`${treeCls}-node-content-wrapper`]: {
|
||||
color: token.textColorDisabled,
|
||||
cursor: 'not-allowed',
|
||||
'&:hover': {
|
||||
background: 'transparent',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
[`&-active ${treeCls}-node-content-wrapper`]: {
|
||||
background: token.itemHoverBackground,
|
||||
},
|
||||
|
||||
[`&:not(&-disabled).filter-node ${treeCls}-title`]: {
|
||||
color: 'inherit',
|
||||
fontWeight: 500,
|
||||
},
|
||||
},
|
||||
|
||||
// >>> Indent
|
||||
'&-indent': {
|
||||
alignSelf: 'stretch',
|
||||
whiteSpace: 'nowrap',
|
||||
userSelect: 'none',
|
||||
'&-unit': {
|
||||
display: 'inline-block',
|
||||
width: treeTitleHeight,
|
||||
},
|
||||
},
|
||||
|
||||
// >>> Drag Handler
|
||||
'&-draggable-icon': {
|
||||
width: treeTitleHeight,
|
||||
lineHeight: `${treeTitleHeight}px`,
|
||||
textAlign: 'center',
|
||||
opacity: 0.2,
|
||||
transition: `opacity ${token.duration}`,
|
||||
|
||||
[`${treeNodeCls}:hover &`]: {
|
||||
opacity: 0.45,
|
||||
},
|
||||
},
|
||||
|
||||
// >>> Switcher
|
||||
'&-switcher': {
|
||||
...getSwitchStyle(prefixCls, token),
|
||||
position: 'relative',
|
||||
flex: 'none',
|
||||
alignSelf: 'stretch',
|
||||
width: treeTitleHeight,
|
||||
margin: 0,
|
||||
lineHeight: `${treeTitleHeight}px`,
|
||||
textAlign: 'center',
|
||||
cursor: 'pointer',
|
||||
userSelect: 'none',
|
||||
|
||||
'&-noop': {
|
||||
cursor: 'default',
|
||||
},
|
||||
|
||||
'&_close': {
|
||||
[`${treeCls}-switcher-icon`]: {
|
||||
svg: {
|
||||
transform: 'rotate(-90deg)',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
'&-loading-icon': {
|
||||
color: token.primaryColor,
|
||||
},
|
||||
|
||||
'&-leaf-line': {
|
||||
position: 'relative',
|
||||
zIndex: 1,
|
||||
display: 'inline-block',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/31884
|
||||
'&:before': {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: treeTitleHeight / 2,
|
||||
bottom: -treeNodePadding,
|
||||
marginLeft: -1,
|
||||
borderRight: `1px solid ${token.borderColor}`,
|
||||
content: '""',
|
||||
},
|
||||
|
||||
'&:after': {
|
||||
position: 'absolute',
|
||||
width: (treeTitleHeight / 2) * 0.8,
|
||||
height: treeTitleHeight / 2,
|
||||
borderBottom: `1px solid ${token.borderColor}`,
|
||||
content: '""',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// >>> Checkbox
|
||||
'&-checkbox': {
|
||||
top: 'initial',
|
||||
marginInlineEnd: treeCheckBoxMarginHorizontal,
|
||||
marginBlockStart: treeCheckBoxMarginVertical,
|
||||
},
|
||||
|
||||
// >>> Title
|
||||
'& &-node-content-wrapper': {
|
||||
position: 'relative',
|
||||
zIndex: 'auto',
|
||||
minHeight: treeTitleHeight,
|
||||
margin: 0,
|
||||
padding: `0 ${token.paddingXS / 2}px`,
|
||||
color: 'inherit',
|
||||
lineHeight: `${treeTitleHeight}px`,
|
||||
background: 'transparent',
|
||||
borderRadius: token.borderRadius,
|
||||
cursor: 'pointer',
|
||||
transition: `all ${token.duration}, border 0s, line-height 0s, box-shadow 0s`,
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: token.itemHoverBackground,
|
||||
},
|
||||
|
||||
[`&${treeCls}-node-selected`]: {
|
||||
backgroundColor: token.tmpPrimaryHoverColorWeak,
|
||||
},
|
||||
|
||||
// Icon
|
||||
[`${treeCls}-iconEle`]: {
|
||||
display: 'inline-block',
|
||||
width: treeTitleHeight,
|
||||
height: treeTitleHeight,
|
||||
lineHeight: `${treeTitleHeight}px`,
|
||||
textAlign: 'center',
|
||||
verticalAlign: 'top',
|
||||
|
||||
'&:empty': {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// https://github.com/ant-design/ant-design/issues/28217
|
||||
'&-unselectable &-node-content-wrapper:hover': {
|
||||
backgroundColor: 'transparent',
|
||||
},
|
||||
|
||||
// ==================== Draggable =====================
|
||||
'&-node-content-wrapper': {
|
||||
lineHeight: `${treeTitleHeight}px`,
|
||||
userSelect: 'none',
|
||||
|
||||
...getDropIndicatorStyle(prefixCls, token),
|
||||
},
|
||||
|
||||
[`${treeNodeCls}.drop-container`]: {
|
||||
'> [draggable]': {
|
||||
boxShadow: `0 0 0 2px ${token.primaryColor}`,
|
||||
},
|
||||
},
|
||||
|
||||
// ==================== Show Line =====================
|
||||
'&-show-line': {
|
||||
// ================ Indent lines ================
|
||||
[`${treeCls}-indent`]: {
|
||||
'&-unit': {
|
||||
position: 'relative',
|
||||
height: '100%',
|
||||
|
||||
'&:before': {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: treeTitleHeight / 2,
|
||||
bottom: -treeNodePadding,
|
||||
borderRight: `1px solid ${token.borderColor}`,
|
||||
content: '""',
|
||||
},
|
||||
|
||||
'&-end': {
|
||||
'&:before': {
|
||||
display: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// ============== Cover Background ==============
|
||||
[`${treeCls}-switcher`]: {
|
||||
background: token.componentBackground,
|
||||
|
||||
'&-line-icon': {
|
||||
// https://github.com/ant-design/ant-design/issues/32813
|
||||
verticalAlign: '-0.15em',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
[`${treeNodeCls}-leaf-last`]: {
|
||||
[`${treeCls}-switcher`]: {
|
||||
'&-leaf-line': {
|
||||
'&:before': {
|
||||
top: 'auto !important',
|
||||
bottom: 'auto !important',
|
||||
height: `${treeTitleHeight / 2}px !important`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
// =============================== RTL ================================
|
||||
export const genRTLStyle = (token: TreeToken): CSSObject => {
|
||||
const { treeCls, treeNodeCls, treeTitleHeight } = token;
|
||||
|
||||
return {
|
||||
[treeCls]: {
|
||||
'&-rtl': {
|
||||
direction: 'rtl',
|
||||
[`${treeCls}-node-content-wrapper[draggable='true']`]: {
|
||||
[`${treeCls}-drop-indicator`]: {
|
||||
'&:after': {
|
||||
right: -6,
|
||||
left: 'unset',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// ===================== TreeNode =====================
|
||||
[`${treeNodeCls}`]: {
|
||||
'&-rtl': {
|
||||
direction: 'rtl',
|
||||
},
|
||||
},
|
||||
|
||||
// >>> Switcher
|
||||
'&-switcher': {
|
||||
'&_close': {
|
||||
[`${treeCls}-switcher-icon`]: {
|
||||
svg: {
|
||||
[`${treeCls}-rtl &`]: {
|
||||
transform: 'rotate(90deg)',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
// ==================== Show Line =====================
|
||||
'&-show-line': {
|
||||
// ================ Indent lines ================
|
||||
[`${treeCls}-indent`]: {
|
||||
'&-unit': {
|
||||
'&:before': {
|
||||
[`${treeCls}-rtl&`]: {
|
||||
right: 'auto',
|
||||
left: -(treeTitleHeight / 2) - 1,
|
||||
borderRight: 'none',
|
||||
borderLeft: `1px solid ${token.borderColor}`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
// ============================== Merged ==============================
|
||||
export const genTreeStyle = (
|
||||
prefixCls: string,
|
||||
token: DerivativeToken,
|
||||
hashId: string,
|
||||
): CSSInterpolation => {
|
||||
const treeCls = `.${prefixCls}`;
|
||||
const treeNodeCls = `${treeCls}-treenode`;
|
||||
|
||||
const treeNodePadding = token.paddingXS / 2;
|
||||
const treeTitleHeight = token.heightSM;
|
||||
|
||||
const treeToken = {
|
||||
...token,
|
||||
treeCls,
|
||||
treeNodeCls,
|
||||
treeNodePadding,
|
||||
treeTitleHeight,
|
||||
};
|
||||
|
||||
return [
|
||||
// Basic
|
||||
genBaseStyle(prefixCls, treeToken, hashId),
|
||||
// RTL
|
||||
genRTLStyle(treeToken),
|
||||
];
|
||||
};
|
||||
|
||||
// ============================== Export ==============================
|
||||
export default function useStyle(prefixCls: string) {
|
||||
const [theme, token, hashId] = useToken();
|
||||
|
||||
return useStyleRegister({ theme, token, hashId, path: [prefixCls] }, () => [
|
||||
getCheckboxStyle(`${prefixCls}-checkbox`, token, hashId),
|
||||
genTreeStyle(prefixCls, token, hashId),
|
||||
treeNodeFX,
|
||||
]);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user