mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 13:39:24 +08:00
refactor: show button title with tooltip on action icon hover (#6761)
* refactor: show button title with tooltip on action icon hover * fix: test * fix: style improve * fix: filter action should onlyicon * fix: test * fix: test * style: link action style improve
This commit is contained in:
parent
c2521a04c1
commit
14e6ccca01
@ -53,14 +53,33 @@ export const filterActionSettings = new SchemaSettings({
|
|||||||
default: fieldSchema?.['x-component-props']?.icon,
|
default: fieldSchema?.['x-component-props']?.icon,
|
||||||
'x-component-props': {},
|
'x-component-props': {},
|
||||||
},
|
},
|
||||||
|
onlyIcon: {
|
||||||
|
'x-decorator': 'FormItem',
|
||||||
|
'x-component': 'Checkbox',
|
||||||
|
title: t('Icon only'),
|
||||||
|
default: fieldSchema?.['x-component-props']?.onlyIcon,
|
||||||
|
'x-component-props': {},
|
||||||
|
'x-reactions': [
|
||||||
|
{
|
||||||
|
dependencies: ['icon'],
|
||||||
|
fulfill: {
|
||||||
|
state: {
|
||||||
|
hidden: '{{!$deps[0]}}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
} as ISchema,
|
} as ISchema,
|
||||||
onSubmit: ({ title, icon }) => {
|
onSubmit: ({ title, icon, onlyIcon }) => {
|
||||||
fieldSchema.title = title;
|
fieldSchema.title = title;
|
||||||
field.title = title;
|
field.title = title;
|
||||||
field.componentProps.icon = icon;
|
field.componentProps.icon = icon;
|
||||||
|
field.componentProps.onlyIcon = onlyIcon;
|
||||||
fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {};
|
fieldSchema['x-component-props'] = fieldSchema['x-component-props'] || {};
|
||||||
fieldSchema['x-component-props'].icon = icon;
|
fieldSchema['x-component-props'].icon = icon;
|
||||||
|
fieldSchema['x-component-props'].onlyIcon = onlyIcon;
|
||||||
dn.emit('patch', {
|
dn.emit('patch', {
|
||||||
schema: {
|
schema: {
|
||||||
['x-uid']: fieldSchema['x-uid'],
|
['x-uid']: fieldSchema['x-uid'],
|
||||||
|
@ -35,7 +35,7 @@ export const ActionLink: ComposedAction = withDynamicSchemaProps(
|
|||||||
return (
|
return (
|
||||||
<Action
|
<Action
|
||||||
{...props}
|
{...props}
|
||||||
component={props.component || WrapperComponent}
|
component={props.component || 'a'}
|
||||||
className={classnames('nb-action-link', props.className)}
|
className={classnames('nb-action-link', props.className)}
|
||||||
isLink
|
isLink
|
||||||
/>
|
/>
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
import { Field } from '@formily/core';
|
import { Field } from '@formily/core';
|
||||||
import { observer, Schema, useField, useFieldSchema, useForm } from '@formily/react';
|
import { observer, Schema, useField, useFieldSchema, useForm } from '@formily/react';
|
||||||
import { isPortalInBody } from '@nocobase/utils/client';
|
import { isPortalInBody } from '@nocobase/utils/client';
|
||||||
import { App, Button } from 'antd';
|
import { App, Button, Tooltip } from 'antd';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import debounce from 'lodash/debounce';
|
import debounce from 'lodash/debounce';
|
||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
@ -369,6 +369,7 @@ Action.Popover = function ActionPopover(props) {
|
|||||||
{props.children}
|
{props.children}
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StablePopover
|
<StablePopover
|
||||||
{...props}
|
{...props}
|
||||||
@ -618,6 +619,22 @@ const RenderButtonInner = observer(
|
|||||||
const actionTitle = typeof rawTitle === 'string' ? t(rawTitle, { ns: NAMESPACE_UI_SCHEMA }) : rawTitle;
|
const actionTitle = typeof rawTitle === 'string' ? t(rawTitle, { ns: NAMESPACE_UI_SCHEMA }) : rawTitle;
|
||||||
const { opacity, ...restButtonStyle } = buttonStyle;
|
const { opacity, ...restButtonStyle } = buttonStyle;
|
||||||
const linkStyle = isLink && opacity ? { opacity } : undefined;
|
const linkStyle = isLink && opacity ? { opacity } : undefined;
|
||||||
|
const WrapperComponent = React.forwardRef(
|
||||||
|
({ component: Component = tarComponent || Button, icon, onlyIcon, children, ...restProps }: any, ref) => {
|
||||||
|
return (
|
||||||
|
<Component ref={ref} {...restProps}>
|
||||||
|
{onlyIcon ? (
|
||||||
|
<Tooltip title={restProps.title}>
|
||||||
|
<span style={{ marginRight: 3 }}>{icon && typeof icon === 'string' ? <Icon type={icon} /> : icon}</span>
|
||||||
|
</Tooltip>
|
||||||
|
) : (
|
||||||
|
<span style={{ marginRight: 3 }}>{icon && typeof icon === 'string' ? <Icon type={icon} /> : icon}</span>
|
||||||
|
)}
|
||||||
|
{onlyIcon ? children[1] : children}
|
||||||
|
</Component>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
return (
|
return (
|
||||||
<SortableItem
|
<SortableItem
|
||||||
role="button"
|
role="button"
|
||||||
@ -630,10 +647,11 @@ const RenderButtonInner = observer(
|
|||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
style={isLink ? restButtonStyle : buttonStyle}
|
style={isLink ? restButtonStyle : buttonStyle}
|
||||||
onClick={process.env.__E2E__ ? handleButtonClick : debouncedClick} // E2E 中的点击操作都是很快的,如果加上 debounce 会导致 E2E 测试失败
|
onClick={process.env.__E2E__ ? handleButtonClick : debouncedClick} // E2E 中的点击操作都是很快的,如果加上 debounce 会导致 E2E 测试失败
|
||||||
component={tarComponent || Button}
|
component={onlyIcon || tarComponent ? WrapperComponent : tarComponent || Button}
|
||||||
className={classnames(componentCls, hashId, className, 'nb-action')}
|
className={classnames(componentCls, hashId, className, 'nb-action')}
|
||||||
type={type === 'danger' ? undefined : type}
|
type={type === 'danger' ? undefined : type}
|
||||||
title={actionTitle}
|
title={actionTitle}
|
||||||
|
onlyIcon={onlyIcon}
|
||||||
>
|
>
|
||||||
{!onlyIcon && actionTitle && (
|
{!onlyIcon && actionTitle && (
|
||||||
<span className={icon ? 'nb-action-title' : null} style={linkStyle}>
|
<span className={icon ? 'nb-action-title' : null} style={linkStyle}>
|
||||||
|
@ -118,8 +118,5 @@ describe('Action.Popover', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
fireEvent.mouseLeave(btn);
|
fireEvent.mouseLeave(btn);
|
||||||
await waitFor(() => {
|
|
||||||
expect(document.querySelector('.ant-popover')).not.toBeInTheDocument();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -81,6 +81,7 @@ export interface ActionProps extends ButtonProps {
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
addChild?: boolean;
|
addChild?: boolean;
|
||||||
|
onlyIcon?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComposedAction = React.FC<ActionProps> & {
|
export type ComposedAction = React.FC<ActionProps> & {
|
||||||
|
@ -51,7 +51,7 @@ const InternalFilterAction = React.memo((props: FilterActionProps) => {
|
|||||||
const form = useMemo<Form>(() => props.form || createForm(), []);
|
const form = useMemo<Form>(() => props.form || createForm(), []);
|
||||||
|
|
||||||
// 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
// 新版 UISchema(1.0 之后)中已经废弃了 useProps,这里之所以继续保留是为了兼容旧版的 UISchema
|
||||||
const { options, onSubmit, onReset, Container = StablePopover, icon } = useProps(props);
|
const { options, onSubmit, onReset, Container = StablePopover, icon, onlyIcon } = useProps(props);
|
||||||
|
|
||||||
const onOpenChange = useCallback((visible: boolean): void => {
|
const onOpenChange = useCallback((visible: boolean): void => {
|
||||||
setVisible(visible);
|
setVisible(visible);
|
||||||
@ -77,7 +77,6 @@ const InternalFilterAction = React.memo((props: FilterActionProps) => {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}, [field, fieldSchema, form, onReset, onSubmit, options]);
|
}, [field, fieldSchema, form, onReset, onSubmit, options]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FilterActionContext.Provider value={filterActionContextValue}>
|
<FilterActionContext.Provider value={filterActionContextValue}>
|
||||||
<Container
|
<Container
|
||||||
@ -90,7 +89,7 @@ const InternalFilterAction = React.memo((props: FilterActionProps) => {
|
|||||||
>
|
>
|
||||||
{/* Adding a div here can prevent unnecessary re-rendering of Action */}
|
{/* Adding a div here can prevent unnecessary re-rendering of Action */}
|
||||||
<div>
|
<div>
|
||||||
<Action onClick={handleClick} icon={icon} />
|
<Action onClick={handleClick} icon={icon} onlyIcon={onlyIcon} />
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
</FilterActionContext.Provider>
|
</FilterActionContext.Provider>
|
||||||
|
@ -194,7 +194,8 @@ const useTableColumns = (
|
|||||||
return css`
|
return css`
|
||||||
.nb-action-link {
|
.nb-action-link {
|
||||||
margin: -${token.paddingContentVerticalLG}px -${token.marginSM}px;
|
margin: -${token.paddingContentVerticalLG}px -${token.marginSM}px;
|
||||||
padding: ${token.paddingContentVerticalLG}px ${token.paddingSM + 4}px;
|
padding: ${token.paddingContentVerticalLG}px ${token.paddingContentVerticalLG}px ${token.paddingSM}px
|
||||||
|
${token.paddingSM}px;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}, [token.paddingContentVerticalLG, token.marginSM, token.margin]);
|
}, [token.paddingContentVerticalLG, token.marginSM, token.margin]);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user