From 72241e913eed8a73084fdb48b231523e483f8c0e Mon Sep 17 00:00:00 2001 From: shifeng1993 Date: Sun, 19 Feb 2023 19:38:04 +0800 Subject: [PATCH 1/6] feat: add float-button components --- components/components.ts | 7 +- components/float-button/BackTop.tsx | 147 ++++++++ components/float-button/FloatButton.tsx | 127 +++++++ .../float-button/FloatButtonContent.tsx | 42 +++ components/float-button/FloatButtonGroup.tsx | 132 +++++++ .../__tests__/__snapshots__/demo.test.js.snap | 256 ++++++++++++++ .../__snapshots__/index.test.js.snap | 65 ++++ .../float-button/__tests__/demo.test.js | 3 + .../float-button/__tests__/index.test.js | 48 +++ .../float-button/__tests__/wave.test.js | 79 +++++ components/float-button/context.ts | 10 + components/float-button/demo/back-top.vue | 43 +++ components/float-button/demo/basic.vue | 36 ++ components/float-button/demo/description.vue | 69 ++++ components/float-button/demo/group-menu.vue | 56 +++ components/float-button/demo/group.vue | 57 +++ components/float-button/demo/index.vue | 86 +++++ components/float-button/demo/shape.vue | 62 ++++ components/float-button/demo/tooltip.vue | 47 +++ components/float-button/demo/type.vue | 54 +++ components/float-button/index.en-US.md | 48 +++ components/float-button/index.ts | 42 +++ components/float-button/index.zh-CN.md | 49 +++ components/float-button/interface.ts | 66 ++++ components/float-button/style/index.ts | 333 ++++++++++++++++++ components/theme/interface/components.ts | 4 +- site/src/router/index.js | 17 + 27 files changed, 1981 insertions(+), 4 deletions(-) create mode 100644 components/float-button/BackTop.tsx create mode 100644 components/float-button/FloatButton.tsx create mode 100644 components/float-button/FloatButtonContent.tsx create mode 100644 components/float-button/FloatButtonGroup.tsx create mode 100644 components/float-button/__tests__/__snapshots__/demo.test.js.snap create mode 100644 components/float-button/__tests__/__snapshots__/index.test.js.snap create mode 100644 components/float-button/__tests__/demo.test.js create mode 100644 components/float-button/__tests__/index.test.js create mode 100644 components/float-button/__tests__/wave.test.js create mode 100644 components/float-button/context.ts create mode 100644 components/float-button/demo/back-top.vue create mode 100644 components/float-button/demo/basic.vue create mode 100644 components/float-button/demo/description.vue create mode 100644 components/float-button/demo/group-menu.vue create mode 100644 components/float-button/demo/group.vue create mode 100644 components/float-button/demo/index.vue create mode 100644 components/float-button/demo/shape.vue create mode 100644 components/float-button/demo/tooltip.vue create mode 100644 components/float-button/demo/type.vue create mode 100644 components/float-button/index.en-US.md create mode 100644 components/float-button/index.ts create mode 100644 components/float-button/index.zh-CN.md create mode 100644 components/float-button/interface.ts create mode 100644 components/float-button/style/index.ts diff --git a/components/components.ts b/components/components.ts index 020c5add2a..24b4325e90 100644 --- a/components/components.ts +++ b/components/components.ts @@ -13,8 +13,8 @@ export { default as Alert } from './alert'; export type { AvatarProps } from './avatar'; export { default as Avatar, AvatarGroup } from './avatar'; -export type { BackTopProps } from './back-top'; -export { default as BackTop } from './back-top'; +// export type { BackTopProps } from './back-top'; +// export { default as BackTop } from './back-top'; export type { BadgeProps } from './badge'; export { default as Badge, BadgeRibbon } from './badge'; @@ -76,6 +76,9 @@ export { default as Drawer } from './drawer'; export type { EmptyProps } from './empty'; export { default as Empty } from './empty'; +export type { FloatButtonProps, FloatButtonGroupProps } from './float-button/interface'; +export { default as FloatButton } from './float-button'; + export type { FormProps, FormItemProps, FormInstance, FormItemInstance } from './form'; export { default as Form, FormItem, FormItemRest } from './form'; diff --git a/components/float-button/BackTop.tsx b/components/float-button/BackTop.tsx new file mode 100644 index 0000000000..0cd818f1da --- /dev/null +++ b/components/float-button/BackTop.tsx @@ -0,0 +1,147 @@ +import VerticalAlignTopOutlined from '@ant-design/icons-vue/VerticalAlignTopOutlined'; +import { getTransitionProps, Transition } from '../_util/transition'; +import { + defineComponent, + nextTick, + onActivated, + onBeforeUnmount, + onMounted, + reactive, + ref, + watch, + onDeactivated, +} from 'vue'; +import FloatButton from './FloatButton'; +import useConfigInject from '../config-provider/hooks/useConfigInject'; +import getScroll from '../_util/getScroll'; +import scrollTo from '../_util/scrollTo'; +import throttleByAnimationFrame from '../_util/throttleByAnimationFrame'; +import { initDefaultProps } from '../_util/props-util'; +import { backTopProps } from './interface'; +import { floatButtonPrefixCls } from './FloatButton'; + +import useStyle from './style'; + +const BackTop = defineComponent({ + compatConfig: { MODE: 3 }, + name: 'ABackTop', + inheritAttrs: false, + props: initDefaultProps(backTopProps(), { + visibilityHeight: 400, + target: () => window, + duration: 450, + }), + // emits: ['click'], + setup(props, { slots, attrs, emit }) { + const { prefixCls, direction } = useConfigInject(floatButtonPrefixCls, props); + + const [wrapSSR] = useStyle(prefixCls); + + const domRef = ref(); + const state = reactive({ + visible: false, + scrollEvent: null, + }); + + const getDefaultTarget = () => + domRef.value && domRef.value.ownerDocument ? domRef.value.ownerDocument : window; + + const scrollToTop = (e: Event) => { + const { target = getDefaultTarget, duration } = props; + scrollTo(0, { + getContainer: target, + duration, + }); + emit('click', e); + }; + + const handleScroll = throttleByAnimationFrame((e: Event | { target: any }) => { + const { visibilityHeight } = props; + const scrollTop = getScroll(e.target, true); + state.visible = scrollTop >= visibilityHeight; + }); + + const bindScrollEvent = () => { + const { target } = props; + const getTarget = target || getDefaultTarget; + const container = getTarget(); + handleScroll({ target: container }); + container?.addEventListener('scroll', handleScroll); + }; + + const scrollRemove = () => { + const { target } = props; + const getTarget = target || getDefaultTarget; + const container = getTarget(); + handleScroll.cancel(); + container?.removeEventListener('scroll', handleScroll); + }; + + watch( + () => props.target, + () => { + scrollRemove(); + nextTick(() => { + bindScrollEvent(); + }); + }, + ); + + onMounted(() => { + nextTick(() => { + bindScrollEvent(); + }); + }); + + onActivated(() => { + nextTick(() => { + bindScrollEvent(); + }); + }); + + onDeactivated(() => { + scrollRemove(); + }); + + onBeforeUnmount(() => { + scrollRemove(); + }); + + return () => { + const defaultElement = ( +
+
+ +
+
+ ); + const divProps = { + ...attrs, + onClick: scrollToTop, + class: { + [`${prefixCls.value}`]: true, + [`${attrs.class}`]: attrs.class, + [`${prefixCls.value}-rtl`]: direction.value === 'rtl', + }, + }; + + const transitionProps = getTransitionProps('fade'); + return wrapSSR( + + + {{ + icon: () => , + default: () => slots.default?.() || defaultElement, + }} + + , + ); + }; + }, +}); + +if (process.env.NODE_ENV !== 'production') { + BackTop.displayName = 'BackTop'; +} + +export default BackTop; diff --git a/components/float-button/FloatButton.tsx b/components/float-button/FloatButton.tsx new file mode 100644 index 0000000000..827bfd7826 --- /dev/null +++ b/components/float-button/FloatButton.tsx @@ -0,0 +1,127 @@ +import classNames from '../_util/classNames'; +import { defineComponent, computed, CSSProperties, ref } from 'vue'; +import Tooltip from '../tooltip'; +import Content from './FloatButtonContent'; +import type { FloatButtonContentProps } from './interface'; +import useConfigInject from '../config-provider/hooks/useConfigInject'; +import FloatButtonGroupContext from './context'; +import warning from '../_util/warning'; +import { initDefaultProps } from '../_util/props-util'; +import { floatButtonProps } from './interface'; +// import { useCompactItemContext } from '../space/Compact'; + +// CSSINJS +import useStyle from './style'; + +export const floatButtonPrefixCls = 'float-btn'; + +const FloatButton = defineComponent({ + compatConfig: { MODE: 3 }, + name: 'AFloatButton', + inheritAttrs: false, + props: initDefaultProps(floatButtonProps(), { type: 'default', shape: 'circle' }), + setup(props, { attrs, slots, expose }) { + const { prefixCls, direction } = useConfigInject(floatButtonPrefixCls, props); + const [wrapSSR, hashId] = useStyle(prefixCls); + const { shape: groupShape } = FloatButtonGroupContext.useInject(); + + const floatButtonRef = ref(null); + + const mergeShape = computed(() => { + return groupShape || props.shape; + }); + + const classString = computed(() => { + return classNames( + hashId.value, + prefixCls.value, + attrs.class, + `${prefixCls.value}-${props.type}`, + `${prefixCls.value}-${mergeShape.value}`, + { + [`${prefixCls.value}-rtl`]: direction.value === 'rtl', + }, + ); + }); + + expose({ + floatButtonEl: floatButtonRef, + }); + + return () => { + const { + prefixCls: customPrefixCls, + type = 'default', + shape = 'circle', + description, + tooltip, + ...restProps + } = props; + + const contentProps: FloatButtonContentProps = { + prefixCls: prefixCls.value, + description, + }; + + const buttonNode = ( + + {{ + title: + slots.tooltip || tooltip + ? () => (slots.tooltip && slots.tooltip()) || tooltip + : undefined, + default: () => ( +
+ + {{ + icon: slots.icon, + description: slots.description, + }} + +
+ ), + }} +
+ ); + + if (process.env.NODE_ENV !== 'production') { + warning( + !(shape === 'circle' && description), + 'FloatButton', + 'supported only when `shape` is `square`. Due to narrow space for text, short sentence is recommended.', + ); + } + + return wrapSSR( + props.href ? ( + + {buttonNode} + + ) : ( + + ), + ); + }; + }, +}); + +if (process.env.NODE_ENV !== 'production') { + FloatButton.displayName = 'FloatButton'; +} + +export default FloatButton; diff --git a/components/float-button/FloatButtonContent.tsx b/components/float-button/FloatButtonContent.tsx new file mode 100644 index 0000000000..95b6d3bfb9 --- /dev/null +++ b/components/float-button/FloatButtonContent.tsx @@ -0,0 +1,42 @@ +import { defineComponent } from 'vue'; +import FileTextOutlined from '@ant-design/icons-vue/FileTextOutlined'; +import classNames from '../_util/classNames'; +import { initDefaultProps } from '../_util/props-util'; +import { floatButtonContentProps } from './interface'; + +const FloatButtonContent = defineComponent({ + compatConfig: { MODE: 3 }, + name: 'AFloatButtonContent', + inheritAttrs: false, + props: initDefaultProps(floatButtonContentProps(), {}), + setup(props, { attrs, slots }) { + return () => { + const { description, prefixCls } = props; + + const defaultElement = ( +
+ +
+ ); + + return ( +
+ {slots.icon || description ? ( + <> + {slots.icon &&
{slots.icon()}
} + {(slots.description || description) && ( +
+ {(slots.description && slots.description()) || description} +
+ )} + + ) : ( + defaultElement + )} +
+ ); + }; + }, +}); + +export default FloatButtonContent; diff --git a/components/float-button/FloatButtonGroup.tsx b/components/float-button/FloatButtonGroup.tsx new file mode 100644 index 0000000000..f80c6bc94b --- /dev/null +++ b/components/float-button/FloatButtonGroup.tsx @@ -0,0 +1,132 @@ +import { defineComponent, ref, computed, watch } from 'vue'; +import CloseOutlined from '@ant-design/icons-vue/CloseOutlined'; +import FileTextOutlined from '@ant-design/icons-vue/FileTextOutlined'; +import classNames from '../_util/classNames'; +import { getTransitionProps, Transition } from '../_util/transition'; +import FloatButton, { floatButtonPrefixCls } from './FloatButton'; +import useConfigInject from '../config-provider/hooks/useConfigInject'; +import FloatButtonGroupContext from './context'; +import { initDefaultProps } from '../_util/props-util'; +import { floatButtonGroupProps } from './interface'; +import type { FloatButtonGroupProps } from './interface'; + +// CSSINJS +import useStyle from './style'; + +const FloatButtonGroup = defineComponent({ + compatConfig: { MODE: 3 }, + name: 'AFloatButtonGroup', + inheritAttrs: false, + props: initDefaultProps(floatButtonGroupProps(), { + type: 'default', + shape: 'circle', + } as FloatButtonGroupProps), + setup(props, { attrs, slots }) { + const { prefixCls, direction } = useConfigInject(floatButtonPrefixCls, props); + + // style + const [wrapSSR, hashId] = useStyle(prefixCls); + + const open = ref(props.open); + + const floatButtonGroupRef = ref(null); + const floatButtonRef = ref(null); + + FloatButtonGroupContext.useProvide({ + shape: computed(() => props.shape), + }); + + const hoverAction = computed(() => { + const hoverTypeAction = { + onMouseenter() { + open.value = true; + props.onOpenChange?.(true); + }, + onMouseleave() { + open.value = false; + props.onOpenChange?.(false); + }, + }; + return props.trigger === 'hover' ? hoverTypeAction : {}; + }); + + const handleOpenChange = () => { + open.value = !open.value; + props.onOpenChange?.(!open.value); + }; + + const onClick = (e: MouseEvent) => { + if (floatButtonGroupRef.value?.contains(e.target as Node)) { + if ((floatButtonRef.value as any)?.floatButtonEl?.contains(e.target as Node)) { + handleOpenChange(); + } + return; + } + open.value = false; + props.onOpenChange?.(false); + }; + + watch( + computed(() => props.trigger), + value => { + if (value === 'click') { + document.addEventListener('click', onClick); + return () => { + document.removeEventListener('click', onClick); + }; + } + }, + { immediate: true }, + ); + + return () => { + const { shape = 'circle', type = 'default', tooltip, description, trigger } = props; + + const groupPrefixCls = `${prefixCls.value}-group`; + + const groupCls = classNames(groupPrefixCls, hashId.value, attrs.class, { + [`${groupPrefixCls}-rtl`]: direction.value === 'rtl', + [`${groupPrefixCls}-${shape}`]: shape, + [`${groupPrefixCls}-${shape}-shadow`]: !trigger, + }); + + const wrapperCls = classNames(hashId.value, `${groupPrefixCls}-wrap`); + + const transitionProps = getTransitionProps(`${groupPrefixCls}-wrap`); + + return wrapSSR( +
+ {trigger && ['click', 'hover'].includes(trigger) ? ( + <> + +
+ {slots.default && slots.default()} +
+
+ + {{ + icon: () => + open.value + ? (slots.closeIcon && slots.closeIcon()) || + : (slots.icon && slots.icon()) || , + tooltip: slots.tooltip, + description: slots.description, + }} + + + ) : ( + slots.default && slots.default() + )} +
, + ); + }; + }, +}); + +export default FloatButtonGroup; diff --git a/components/float-button/__tests__/__snapshots__/demo.test.js.snap b/components/float-button/__tests__/__snapshots__/demo.test.js.snap new file mode 100644 index 0000000000..25dbe26448 --- /dev/null +++ b/components/float-button/__tests__/__snapshots__/demo.test.js.snap @@ -0,0 +1,256 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders ./components/button/demo/basic.vue correctly 1`] = ` + + + + + +`; + +exports[`renders ./components/button/demo/block.vue correctly 1`] = ` + + + + + +`; + +exports[`renders ./components/button/demo/button-group.vue correctly 1`] = ` +
+

Basic

+
+
+
+

With Icon

+
+
+
+`; + +exports[`renders ./components/button/demo/danger.vue correctly 1`] = ` +
+`; + +exports[`renders ./components/button/demo/disabled.vue correctly 1`] = ` + + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+
+`; + +exports[`renders ./components/button/demo/ghost.vue correctly 1`] = ` +
+`; + +exports[`renders ./components/button/demo/icon.vue correctly 1`] = ` + + + + + + + + + + +
+
+ + + + + +
+ + + + + +`; + +exports[`renders ./components/button/demo/loading.vue correctly 1`] = ` +
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+ +
+`; + +exports[`renders ./components/button/demo/multiple.vue correctly 1`] = ` + + + +`; + +exports[`renders ./components/button/demo/size.vue correctly 1`] = ` +
+
+
+ + + + + +
+ + + + + +
+`; diff --git a/components/float-button/__tests__/__snapshots__/index.test.js.snap b/components/float-button/__tests__/__snapshots__/index.test.js.snap new file mode 100644 index 0000000000..00b3398d8a --- /dev/null +++ b/components/float-button/__tests__/__snapshots__/index.test.js.snap @@ -0,0 +1,65 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Button fixbug renders {0} , 0 and {false} 1`] = ` + +`; + +exports[`Button fixbug renders {0} , 0 and {false} 2`] = ` + +`; + +exports[`Button fixbug renders {0} , 0 and {false} 3`] = ` + +`; + +exports[`Button renders Chinese characters correctly 1`] = ` + +`; + +exports[`Button renders Chinese characters correctly 2`] = ` + +`; + +exports[`Button renders Chinese characters correctly 3`] = ` + +`; + +exports[`Button renders Chinese characters correctly 4`] = ``; + +exports[`Button renders Chinese characters correctly 5`] = ``; + +exports[`Button renders Chinese characters correctly 6`] = ` + +`; + +exports[`Button renders correctly 1`] = ` + +`; + +exports[`Button should not render as link button when href is undefined 1`] = ` + +`; + +exports[`Button should support link button 1`] = ` + + link button + +`; diff --git a/components/float-button/__tests__/demo.test.js b/components/float-button/__tests__/demo.test.js new file mode 100644 index 0000000000..fdcb0c780c --- /dev/null +++ b/components/float-button/__tests__/demo.test.js @@ -0,0 +1,3 @@ +import demoTest from '../../../tests/shared/demoTest'; + +demoTest('float-button'); diff --git a/components/float-button/__tests__/index.test.js b/components/float-button/__tests__/index.test.js new file mode 100644 index 0000000000..d5bc3c4dc1 --- /dev/null +++ b/components/float-button/__tests__/index.test.js @@ -0,0 +1,48 @@ +import FloatButton from '../index'; +import { mount } from '@vue/test-utils'; +import mountTest from '../../../tests/shared/mountTest'; + +describe('FloatButton', () => { + mountTest(FloatButton); + mountTest(FloatButton.Group); + it('renders correctly', () => { + const wrapper = mount({ + render() { + return ; + }, + }); + expect(wrapper.html()).toMatchSnapshot(); + }); + + it('create primary button', () => { + const wrapper = mount({ + render() { + return 按钮; + }, + }); + expect(wrapper.find('.ant-float-btn-primary').exists()).toBe(true); + }); + + it('fixbug renders {0} , 0 and {false}', () => { + const wrapper = mount({ + render() { + return {0}; + }, + }); + expect(wrapper.html()).toMatchSnapshot(); + + const wrapper1 = mount({ + render() { + return 0; + }, + }); + expect(wrapper1.html()).toMatchSnapshot(); + + const wrapper2 = mount({ + render() { + return {false}; + }, + }); + expect(wrapper2.html()).toMatchSnapshot(); + }); +}); diff --git a/components/float-button/__tests__/wave.test.js b/components/float-button/__tests__/wave.test.js new file mode 100644 index 0000000000..3102ccf3df --- /dev/null +++ b/components/float-button/__tests__/wave.test.js @@ -0,0 +1,79 @@ +import FloatButton from '../index'; +import { mount } from '@vue/test-utils'; +import { asyncExpect, sleep } from '../../../tests/utils'; + +describe('click wave effect', () => { + async function clickFloatButton(wrapper) { + await asyncExpect(() => { + wrapper.find('.ant-float-btn').trigger('click'); + }); + wrapper.find('.ant-float-btn').element.dispatchEvent(new Event('transitionstart')); + await sleep(20); + wrapper.find('.ant-float-btn').element.dispatchEvent(new Event('animationend')); + await sleep(20); + } + + it('should have click wave effect for primary button', async () => { + const wrapper = mount({ + render() { + return ; + }, + }); + await clickFloatButton(wrapper); + expect( + wrapper.find('.ant-float-btn').attributes('ant-click-animating-without-extra-node'), + ).toBe('true'); + }); + + it('should have click wave effect for default button', async () => { + const wrapper = mount({ + render() { + return button; + }, + }); + await clickFloatButton(wrapper); + expect( + wrapper.find('.ant-float-btn').attributes('ant-click-animating-without-extra-node'), + ).toBe('true'); + }); + + it('should not have click wave effect for link type button', async () => { + const wrapper = mount({ + render() { + return button; + }, + }); + await clickFloatButton(wrapper); + expect( + wrapper.find('.ant-float-btn').attributes('ant-click-animating-without-extra-node'), + ).toBe(undefined); + }); + + it('should not have click wave effect for text type button', async () => { + const wrapper = mount({ + render() { + return button; + }, + }); + await clickFloatButton(wrapper); + expect( + wrapper.find('.ant-float-btn').attributes('ant-click-animating-without-extra-node'), + ).toBe(undefined); + }); + + it('should handle transitionstart', async () => { + const wrapper = mount({ + render() { + return button; + }, + }); + await clickFloatButton(wrapper); + const buttonNode = wrapper.find('.ant-float-btn').element; + buttonNode.dispatchEvent(new Event('transitionstart')); + expect( + wrapper.find('.ant-float-btn').attributes('ant-click-animating-without-extra-node'), + ).toBe('true'); + wrapper.unmount(); + buttonNode.dispatchEvent(new Event('transitionstart')); + }); +}); diff --git a/components/float-button/context.ts b/components/float-button/context.ts new file mode 100644 index 0000000000..45864a7b75 --- /dev/null +++ b/components/float-button/context.ts @@ -0,0 +1,10 @@ +import type { Ref } from 'vue'; +import createContext from '../_util/createContext'; + +import type { FloatButtonShape } from './interface'; + +const FloatButtonGroupContext = createContext<{ shape: Ref } | undefined>( + undefined, +); + +export default FloatButtonGroupContext; diff --git a/components/float-button/demo/back-top.vue b/components/float-button/demo/back-top.vue new file mode 100644 index 0000000000..f0befadd5c --- /dev/null +++ b/components/float-button/demo/back-top.vue @@ -0,0 +1,43 @@ + +--- +order: 7 +iframe: 360 +title: + zh-CN: 回到顶部 + en-US: BackTop +--- + +## zh-CN + +返回页面顶部的操作按钮。 + +## en-US + +`BackTop` makes it easy to go back to the top of the page. + + + + + + diff --git a/components/float-button/demo/basic.vue b/components/float-button/demo/basic.vue new file mode 100644 index 0000000000..eadc0b3076 --- /dev/null +++ b/components/float-button/demo/basic.vue @@ -0,0 +1,36 @@ + +--- +order: 0 +iframe: 360 +title: + zh-CN: 基本 + en-US: Basic Usage +--- + +## zh-CN + +最简单的用法。 + +## en-US + +The most basic usage. + + + + + + diff --git a/components/float-button/demo/description.vue b/components/float-button/demo/description.vue new file mode 100644 index 0000000000..c8ab9936af --- /dev/null +++ b/components/float-button/demo/description.vue @@ -0,0 +1,69 @@ + +--- +order: 3 +iframe: 360 +title: + zh-CN: 描述 + en-US: Description +--- + +## zh-CN + +可以通过 `description` 设置文字内容。 + +> 仅当 `shape` 属性为 `square` 时支持。由于空间较小,推荐使用比较精简的双数文字。 + +## en-US + +Setting `description` prop to show FloatButton with description. + +> supported only when `shape` is `square`. Due to narrow space for text, short sentence is recommended. + + + + + + + diff --git a/components/float-button/demo/group-menu.vue b/components/float-button/demo/group-menu.vue new file mode 100644 index 0000000000..7ebc58c968 --- /dev/null +++ b/components/float-button/demo/group-menu.vue @@ -0,0 +1,56 @@ + +--- +order: 6 +iframe: 360 +title: + zh-CN: 菜单模式 + en-US: Menu mode +--- + +## zh-CN + +设置 `trigger` 属性即可开启菜单模式。提供 `hover` 和 `click` 两种触发方式。 + +## en-US + +Open menu mode with `trigger`, which could be `hover` or `click`. + + + + + + + diff --git a/components/float-button/demo/group.vue b/components/float-button/demo/group.vue new file mode 100644 index 0000000000..fddf870981 --- /dev/null +++ b/components/float-button/demo/group.vue @@ -0,0 +1,57 @@ + +--- +order: 5 +iframe: 360 +title: + zh-CN: 浮动按钮组 + en-US: FloatButton Group +--- + +## zh-CN + +按钮组合使用时,推荐使用 ``,并通过设置 `shape` 属性改变悬浮按钮组的形状。悬浮按钮组的 `shape` 会覆盖内部 FloatButton 的 `shape` 属性。 + +## en-US + +When multiple buttons are used together, `` is recommended. By setting `shape` of FloatButton.Group, you can change the shape of group. `shape` of FloatButton.Group will override `shape` of FloatButton inside. + + + + + + diff --git a/components/float-button/demo/index.vue b/components/float-button/demo/index.vue new file mode 100644 index 0000000000..caa7ba7822 --- /dev/null +++ b/components/float-button/demo/index.vue @@ -0,0 +1,86 @@ + + + diff --git a/components/float-button/demo/shape.vue b/components/float-button/demo/shape.vue new file mode 100644 index 0000000000..5797ad97fe --- /dev/null +++ b/components/float-button/demo/shape.vue @@ -0,0 +1,62 @@ + +--- +order: 2 +iframe: 360 +title: + zh-CN: 形状 + en-US: Shape +--- + +## zh-CN + +最简单的用法。 + +## en-US + +The most basic usage. + + + + + + diff --git a/components/float-button/demo/tooltip.vue b/components/float-button/demo/tooltip.vue new file mode 100644 index 0000000000..ae05d4648d --- /dev/null +++ b/components/float-button/demo/tooltip.vue @@ -0,0 +1,47 @@ + +--- +order: 4 +iframe: 360 +title: + zh-CN: 含有气泡卡片的悬浮按钮 + en-US: FloatButton with tooltip +--- + +## zh-CN + +设置 tooltip 属性,即可开启气泡卡片。 + +## en-US + +Setting `tooltip` prop to show FloatButton with tooltip. + + + + + + diff --git a/components/float-button/demo/type.vue b/components/float-button/demo/type.vue new file mode 100644 index 0000000000..8523180f38 --- /dev/null +++ b/components/float-button/demo/type.vue @@ -0,0 +1,54 @@ + +--- +order: 1 +iframe: 360 +title: + zh-CN: 类型 + en-US: Type +--- + +## zh-CN + +通过 `type` 改变悬浮按钮的类型。 + +## en-US + +Change the type of the FloatButton with `type`. + + + + + + diff --git a/components/float-button/index.en-US.md b/components/float-button/index.en-US.md new file mode 100644 index 0000000000..7c2226d3a2 --- /dev/null +++ b/components/float-button/index.en-US.md @@ -0,0 +1,48 @@ +--- +category: Components +type: Other +title: FloatButton +cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*HS-wTIIwu0kAAAAAAAAAAAAADrJ8AQ/original +--- + +FloatButton. Available since `4.0.0`. + +## When To Use + +- For global functionality on the site. +- Buttons that can be seen wherever you browse. + +## API + +> This component is available since `ant-design-vue@4.0.0`. + +### common API + +| Property | Description | Type | Default | Version | +| --- | --- | --- | --- | --- | +| icon | Set the icon component of button | slot | - | | +| description | Text and other | string \| slot | - | | +| tooltip | The text shown in the tooltip | string \| slot | | | +| type | Setting button type | `default` \| `primary` | `default` | | +| shape | Setting button shape | `circle` \| `square` | `circle` | | +| onClick | Set the handler to handle `click` event | (event) => void | - | | +| href | The target of hyperlink | string | - | | +| target | Specifies where to display the linked URL | string | - | | + +### FloatButton.Group + +| Property | Description | Type | Default | Version | +| --- | --- | --- | --- | --- | +| shape | Setting button shape of children | `circle` \| `square` | `circle` | | +| trigger | Which action can trigger menu open/close | `click` \| `hover` | - | | +| open | Whether the menu is visible or not | boolean | - | | +| onOpenChange | Callback executed when active menu is changed | (open: boolean) => void | - | | + +### FloatButton.BackTop + +| Property | Description | Type | Default | Version | +| --- | --- | --- | --- | --- | +| duration | Time to return to top(ms) | number | 450 | | +| target | Specifies the scrollable area dom node | () => HTMLElement | () => window | | +| visibilityHeight | The BackTop button will not show until the scroll height reaches this value | number | 400 | | +| onClick | A callback function, which can be executed when you click the button | () => void | - | | diff --git a/components/float-button/index.ts b/components/float-button/index.ts new file mode 100644 index 0000000000..be156477e6 --- /dev/null +++ b/components/float-button/index.ts @@ -0,0 +1,42 @@ +import type { App, Plugin } from 'vue'; +import FloatButton from './FloatButton'; +import FloatButtonGroup from './FloatButtonGroup'; +import BackTop from './BackTop'; + +import type { + FloatButtonProps, + FloatButtonShape, + FloatButtonType, + FloatButtonGroupProps, + BackTopProps, +} from './interface'; + +import type { SizeType as FloatButtonSize } from '../config-provider'; + +export type { + FloatButtonProps, + FloatButtonShape, + FloatButtonType, + FloatButtonGroupProps, + BackTopProps, + FloatButtonSize, +}; + +FloatButton.Group = FloatButtonGroup; +FloatButton.BackTop = BackTop; + +/* istanbul ignore next */ +FloatButton.install = function (app: App) { + app.component(FloatButton.name, FloatButton); + app.component(FloatButtonGroup.name, FloatButtonGroup); + app.component(BackTop.name, BackTop); + return app; +}; + +export { FloatButtonGroup }; + +export default FloatButton as typeof FloatButton & + Plugin & { + readonly Group: typeof FloatButtonGroup; + readonly BackTop: typeof BackTop; + }; diff --git a/components/float-button/index.zh-CN.md b/components/float-button/index.zh-CN.md new file mode 100644 index 0000000000..bd00f5eab2 --- /dev/null +++ b/components/float-button/index.zh-CN.md @@ -0,0 +1,49 @@ +--- +category: Components +subtitle: 悬浮按钮 +type: 其他 +title: FloatButton +cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*HS-wTIIwu0kAAAAAAAAAAAAADrJ8AQ/original +--- + +悬浮按钮。自 `4.0.0` 版本开始提供该组件。 + +## 何时使用 + +- 用于网站上的全局功能; +- 无论浏览到何处都可以看见的按钮。 + +## API + +> 自 `ant-design-vue@4.0.0` 版本开始提供该组件。 + +### 共同的 API + +| 参数 | 说明 | 类型 | 默认值 | 版本 | +| --- | --- | --- | --- | --- | +| icon | 自定义图标 | slot | - | | +| description | 文字及其它内容 | string \| slot | - | | +| tooltip | 气泡卡片的内容 | string \| slot | - | | +| type | 设置按钮类型 | `default` \| `primary` | `default` | | +| shape | 设置按钮形状 | `circle` \| `square` | `circle` | | +| onClick | 点击按钮时的回调 | (event) => void | - | | +| href | 点击跳转的地址,指定此属性 button 的行为和 a 链接一致 | string | - | | +| target | 相当于 a 标签的 target 属性,href 存在时生效 | string | - | | + +### FloatButton.Group + +| 参数 | 说明 | 类型 | 默认值 | 版本 | +| ------------ | -------------------------------- | ----------------------- | -------- | ---- | +| shape | 设置包含的 FloatButton 按钮形状 | `circle` \| `square` | `circle` | | +| trigger | 触发方式(有触发方式为菜单模式) | `click` \| `hover` | - | | +| open | 受控展开 | boolean | - | | +| onOpenChange | 展开收起时的回调 | (open: boolean) => void | - | | + +### FloatButton.BackTop + +| 参数 | 说明 | 类型 | 默认值 | 版本 | +| ---------------- | ---------------------------------- | ----------------- | ------------ | ---- | +| duration | 回到顶部所需时间(ms) | number | 450 | | +| target | 设置需要监听其滚动事件的元素 | () => HTMLElement | () => window | | +| visibilityHeight | 滚动高度达到此参数值才出现 BackTop | number | 400 | | +| onClick | 点击按钮的回调函数 | () => void | - | | diff --git a/components/float-button/interface.ts b/components/float-button/interface.ts new file mode 100644 index 0000000000..ceffd260f0 --- /dev/null +++ b/components/float-button/interface.ts @@ -0,0 +1,66 @@ +import type { ExtractPropTypes } from 'vue'; +import PropTypes from '../_util/vue-types'; +import type { MouseEventHandler } from '../_util/EventInterface'; +import { stringType, booleanType, functionType } from '../_util/type'; + +export type FloatButtonType = 'default' | 'primary'; + +export type FloatButtonShape = 'circle' | 'square'; + +export type FloatButtonGroupTrigger = 'click' | 'hover'; + +export const floatButtonProps = () => { + return { + prefixCls: String, + description: PropTypes.any, + type: stringType('default'), + shape: stringType('circle'), + tooltip: PropTypes.any, + href: String, + target: functionType<() => Window | HTMLElement | null>(), + onClick: functionType(), + }; +}; + +export type FloatButtonProps = Partial>>; + +export const floatButtonContentProps = () => { + return { + prefixCls: stringType(), + description: PropTypes.any, + }; +}; + +export type FloatButtonContentProps = Partial< + ExtractPropTypes> +>; + +export const floatButtonGroupProps = () => { + return { + ...floatButtonProps(), + // 包含的 Float Button + // 触发方式 (有触发方式为菜单模式) + trigger: stringType(), + // 受控展开 + open: booleanType(false), + // 展开收起的回调 + onOpenChange: functionType<(open: boolean) => void>(), + }; +}; + +export type FloatButtonGroupProps = Partial< + ExtractPropTypes> +>; + +export const backTopProps = () => { + return { + ...floatButtonProps(), + prefixCls: String, + duration: Number, + target: functionType<() => HTMLElement | Window | Document>(), + visibilityHeight: Number, + onClick: functionType(), + }; +}; + +export type BackTopProps = Partial>>; diff --git a/components/float-button/style/index.ts b/components/float-button/style/index.ts new file mode 100644 index 0000000000..61c390c7da --- /dev/null +++ b/components/float-button/style/index.ts @@ -0,0 +1,333 @@ +import type { CSSObject } from '../../_util/cssinjs'; +import { Keyframes } from '../../_util/cssinjs'; +import type { FullToken, GenerateStyle } from '../../theme/internal'; +import { genComponentStyleHook, mergeToken } from '../../theme/internal'; +import { initFadeMotion } from '../../_style/motion/fade'; +import { resetComponent } from '../../_style'; +import { initMotion } from '../../_style/motion/motion'; + +/** Component only token. Which will handle additional calculation of alias token */ +export interface ComponentToken { + zIndexPopup: number; +} + +type FloatButtonToken = FullToken<'FloatButton'> & { + floatButtonColor: string; + floatButtonBackgroundColor: string; + floatButtonHoverBackgroundColor: string; + floatButtonFontSize: number; + floatButtonSize: number; + floatButtonIconSize: number; + + // Position + floatButtonInsetBlockEnd: number; + floatButtonInsetInlineEnd: number; +}; + +const initFloatButtonGroupMotion = (token: FloatButtonToken) => { + const { componentCls, floatButtonSize, motionDurationSlow, motionEaseInOutCirc } = token; + const groupPrefixCls = `${componentCls}-group`; + const moveDownIn = new Keyframes('antFloatButtonMoveDownIn', { + '0%': { + transform: `translate3d(0, ${floatButtonSize}px, 0)`, + transformOrigin: '0 0', + opacity: 0, + }, + + '100%': { + transform: 'translate3d(0, 0, 0)', + transformOrigin: '0 0', + opacity: 1, + }, + }); + const moveDownOut = new Keyframes('antFloatButtonMoveDownOut', { + '0%': { + transform: 'translate3d(0, 0, 0)', + transformOrigin: '0 0', + opacity: 1, + }, + + '100%': { + transform: `translate3d(0, ${floatButtonSize}px, 0)`, + transformOrigin: '0 0', + opacity: 0, + }, + }); + + return [ + { + [`${groupPrefixCls}-wrap`]: { + ...initMotion(`${groupPrefixCls}-wrap`, moveDownIn, moveDownOut, motionDurationSlow, true), + }, + }, + { + [`${groupPrefixCls}-wrap`]: { + [` + &${groupPrefixCls}-wrap-enter, + &${groupPrefixCls}-wrap-appear + `]: { + opacity: 0, + animationTimingFunction: motionEaseInOutCirc, + }, + + [`&${groupPrefixCls}-wrap-leave`]: { + animationTimingFunction: motionEaseInOutCirc, + }, + }, + }, + ]; +}; + +// ============================== Group ============================== +const floatButtonGroupStyle: GenerateStyle = token => { + const { componentCls, floatButtonSize, margin, borderRadiusLG } = token; + const groupPrefixCls = `${componentCls}-group`; + return { + [groupPrefixCls]: { + ...resetComponent(token), + zIndex: 99, + display: 'block', + border: 'none', + position: 'fixed', + width: floatButtonSize, + height: 'auto', + boxShadow: 'none', + minHeight: floatButtonSize, + insetInlineEnd: token.floatButtonInsetInlineEnd, + insetBlockEnd: token.floatButtonInsetBlockEnd, + borderRadius: borderRadiusLG, + + [`${groupPrefixCls}-wrap`]: { + zIndex: -1, + display: 'block', + position: 'relative', + marginBottom: margin, + }, + [`&${groupPrefixCls}-rtl`]: { + direction: 'rtl', + }, + [componentCls]: { + position: 'static', + }, + }, + [`${groupPrefixCls}-circle`]: { + [`${componentCls}-circle:not(:last-child)`]: { + marginBottom: token.margin, + [`${componentCls}-body`]: { + width: floatButtonSize, + height: floatButtonSize, + }, + }, + }, + [`${groupPrefixCls}-square`]: { + [`${componentCls}-square`]: { + borderRadius: 0, + padding: 0, + '&:first-child': { + borderStartStartRadius: borderRadiusLG, + borderStartEndRadius: borderRadiusLG, + }, + '&:last-child': { + borderEndStartRadius: borderRadiusLG, + borderEndEndRadius: borderRadiusLG, + }, + '&:not(:last-child)': { + borderBottom: `${token.lineWidth}px ${token.lineType} ${token.colorSplit}`, + }, + }, + [`${groupPrefixCls}-wrap`]: { + display: 'block', + borderRadius: borderRadiusLG, + boxShadow: token.boxShadowSecondary, + overflow: 'hidden', + [`${componentCls}-square`]: { + boxShadow: 'none', + marginTop: 0, + borderRadius: 0, + padding: token.paddingXXS, + '&:first-child': { + borderStartStartRadius: borderRadiusLG, + borderStartEndRadius: borderRadiusLG, + }, + '&:last-child': { + borderEndStartRadius: borderRadiusLG, + borderEndEndRadius: borderRadiusLG, + }, + '&:not(:last-child)': { + borderBottom: `${token.lineWidth}px ${token.lineType} ${token.colorSplit}`, + }, + [`${componentCls}-body`]: { + width: floatButtonSize - token.paddingXXS * 2, + height: floatButtonSize - token.paddingXXS * 2, + }, + }, + }, + }, + + [`${groupPrefixCls}-circle-shadow`]: { + boxShadow: 'none', + }, + [`${groupPrefixCls}-square-shadow`]: { + boxShadow: token.boxShadowSecondary, + [`${componentCls}-square`]: { + boxShadow: 'none', + padding: token.paddingXXS, + [`${componentCls}-body`]: { + width: floatButtonSize - token.paddingXXS * 2, + height: floatButtonSize - token.paddingXXS * 2, + }, + }, + }, + }; +}; + +// ============================== Shared ============================== +const sharedFloatButtonStyle: GenerateStyle = token => { + const { componentCls, floatButtonIconSize, floatButtonSize, borderRadiusLG } = token; + return { + [componentCls]: { + ...resetComponent(token), + border: 'none', + position: 'fixed', + cursor: 'pointer', + overflow: 'hidden', + zIndex: 99, + display: 'block', + justifyContent: 'center', + alignItems: 'center', + width: floatButtonSize, + height: floatButtonSize, + insetInlineEnd: token.floatButtonInsetInlineEnd, + insetBlockEnd: token.floatButtonInsetBlockEnd, + boxShadow: token.boxShadowSecondary, + + // Pure Panel + '&-pure': { + position: 'relative', + inset: 'auto', + }, + + '&:empty': { + display: 'none', + }, + + [`${componentCls}-body`]: { + width: '100%', + height: '100%', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + transition: `all ${token.motionDurationMid}`, + [`${componentCls}-content`]: { + overflow: 'hidden', + textAlign: 'center', + minHeight: floatButtonSize, + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + padding: `2px 4px`, + [`${componentCls}-icon`]: { + textAlign: 'center', + margin: 'auto', + width: floatButtonIconSize, + fontSize: floatButtonIconSize, + lineHeight: 1, + }, + }, + }, + }, + [`${componentCls}-circle`]: { + height: floatButtonSize, + borderRadius: '50%', + [`${componentCls}-body`]: { + borderRadius: '50%', + }, + }, + [`${componentCls}-square`]: { + height: 'auto', + minHeight: floatButtonSize, + borderRadius: borderRadiusLG, + [`${componentCls}-body`]: { + height: 'auto', + borderRadius: token.borderRadiusSM, + }, + }, + [`${componentCls}-default`]: { + backgroundColor: token.floatButtonBackgroundColor, + transition: `background-color ${token.motionDurationMid}`, + [`${componentCls}-body`]: { + backgroundColor: token.floatButtonBackgroundColor, + transition: `background-color ${token.motionDurationMid}`, + '&:hover': { + backgroundColor: token.colorFillContent, + }, + [`${componentCls}-content`]: { + [`${componentCls}-icon`]: { + color: token.colorText, + }, + [`${componentCls}-description`]: { + display: 'flex', + alignItems: 'center', + lineHeight: `${token.fontSizeLG}px`, + color: token.colorText, + fontSize: token.fontSizeSM, + }, + }, + }, + }, + [`${componentCls}-primary`]: { + backgroundColor: token.colorPrimary, + [`${componentCls}-body`]: { + backgroundColor: token.colorPrimary, + transition: `background-color ${token.motionDurationMid}`, + '&:hover': { + backgroundColor: token.colorPrimaryHover, + }, + [`${componentCls}-content`]: { + [`${componentCls}-icon`]: { + color: token.colorTextLightSolid, + }, + [`${componentCls}-description`]: { + display: 'flex', + alignItems: 'center', + lineHeight: `${token.fontSizeLG}px`, + color: token.colorTextLightSolid, + fontSize: token.fontSizeSM, + }, + }, + }, + }, + }; +}; + +// ============================== Export ============================== +export default genComponentStyleHook<'FloatButton'>('FloatButton', token => { + const { + colorTextLightSolid, + colorBgElevated, + controlHeightLG, + marginXXL, + marginLG, + fontSize, + fontSizeIcon, + controlItemBgHover, + } = token; + const floatButtonToken = mergeToken(token, { + floatButtonBackgroundColor: colorBgElevated, + floatButtonColor: colorTextLightSolid, + floatButtonHoverBackgroundColor: controlItemBgHover, + floatButtonFontSize: fontSize, + floatButtonIconSize: fontSizeIcon * 1.5, + floatButtonSize: controlHeightLG, + + floatButtonInsetBlockEnd: marginXXL, + floatButtonInsetInlineEnd: marginLG, + }); + return [ + floatButtonGroupStyle(floatButtonToken), + sharedFloatButtonStyle(floatButtonToken), + initFadeMotion(token), + initFloatButtonGroupMotion(floatButtonToken), + ]; +}); diff --git a/components/theme/interface/components.ts b/components/theme/interface/components.ts index c62e7e4984..eb42af5609 100644 --- a/components/theme/interface/components.ts +++ b/components/theme/interface/components.ts @@ -3,7 +3,7 @@ import type { ComponentToken as AnchorComponentToken } from '../../anchor/style' import type { ComponentToken as AvatarComponentToken } from '../../avatar/style'; import type { ComponentToken as BackTopComponentToken } from '../../back-top/style'; import type { ComponentToken as ButtonComponentToken } from '../../button/style'; -// import type { ComponentToken as FloatButtonComponentToken } from '../../float-button/style'; +import type { ComponentToken as FloatButtonComponentToken } from '../../float-button/style'; import type { ComponentToken as CalendarComponentToken } from '../../calendar/style'; import type { ComponentToken as CardComponentToken } from '../../card/style'; import type { ComponentToken as CarouselComponentToken } from '../../carousel/style'; @@ -71,7 +71,7 @@ export interface ComponentTokenMap { // Drawer?: DrawerComponentToken; Dropdown?: DropdownComponentToken; Empty?: EmptyComponentToken; - // FloatButton?: FloatButtonComponentToken; + FloatButton?: FloatButtonComponentToken; Form?: {}; Grid?: {}; // Image?: ImageComponentToken; diff --git a/site/src/router/index.js b/site/src/router/index.js index a5f286ae8a..1058a8d00b 100644 --- a/site/src/router/index.js +++ b/site/src/router/index.js @@ -38,6 +38,23 @@ const routes = [ }, component: () => import('../../../components/layout/demo/index.vue'), }, + { + path: 'float-button:lang(.*)', + meta: { + category: 'Components', + subtitle: '悬浮按钮', + type: '悬浮按钮', + cols: 1, + title: 'FloatButton', + cover: + 'https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*HS-wTIIwu0kAAAAAAAAAAAAADrJ8AQ/original', + }, + props: route => { + const hash = route.hash.replace('#', ''); + return { iframeName: hash }; + }, + component: () => import('../../../components/float-button/demo/index.vue'), + }, ], }, { From 4bb685360672883de6efea7a06df2d03be6a2b6e Mon Sep 17 00:00:00 2001 From: shifeng1993 Date: Sun, 19 Feb 2023 19:59:51 +0800 Subject: [PATCH 2/6] fix type & demo display --- components/components.ts | 6 +++++- site/index.html | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/components/components.ts b/components/components.ts index 24b4325e90..47b3a66074 100644 --- a/components/components.ts +++ b/components/components.ts @@ -76,7 +76,11 @@ export { default as Drawer } from './drawer'; export type { EmptyProps } from './empty'; export { default as Empty } from './empty'; -export type { FloatButtonProps, FloatButtonGroupProps } from './float-button/interface'; +export type { + FloatButtonProps, + FloatButtonGroupProps, + BackTopProps, +} from './float-button/interface'; export { default as FloatButton } from './float-button'; export type { FormProps, FormItemProps, FormInstance, FormItemInstance } from './form'; diff --git a/site/index.html b/site/index.html index dd15d468fd..8e5cf5c598 100644 --- a/site/index.html +++ b/site/index.html @@ -63,7 +63,7 @@ gtag('config', 'UA-151755889-1'); -
+ > --> From fc46ccd469ee9379b7357aa3a213bbbdca5edaec Mon Sep 17 00:00:00 2001 From: shifeng1993 Date: Sat, 25 Feb 2023 00:18:42 +0800 Subject: [PATCH 3/6] fix components entry --- components/components.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/components/components.ts b/components/components.ts index d806cf824d..0e1f54f4a7 100644 --- a/components/components.ts +++ b/components/components.ts @@ -13,8 +13,8 @@ export { default as Alert } from './alert'; export type { AvatarProps } from './avatar'; export { default as Avatar, AvatarGroup } from './avatar'; -// export type { BackTopProps } from './back-top'; -// export { default as BackTop } from './back-top'; +export type { BackTopProps } from './back-top'; +export { default as BackTop } from './back-top'; export type { BadgeProps } from './badge'; export { default as Badge, BadgeRibbon } from './badge'; @@ -76,11 +76,7 @@ export { default as Drawer } from './drawer'; export type { EmptyProps } from './empty'; export { default as Empty } from './empty'; -export type { - FloatButtonProps, - FloatButtonGroupProps, - BackTopProps, -} from './float-button/interface'; +export type { FloatButtonProps, FloatButtonGroupProps } from './float-button/interface'; export { default as FloatButton } from './float-button'; export type { FormProps, FormItemProps, FormInstance, FormItemInstance } from './form'; From 1160fd3045eb41148d54c699f920f079abdbbb13 Mon Sep 17 00:00:00 2001 From: shifeng1993 Date: Tue, 28 Feb 2023 21:59:28 +0800 Subject: [PATCH 4/6] fix review bug --- components/components.ts | 2 +- components/float-button/BackTop.tsx | 4 --- components/float-button/FloatButton.tsx | 34 ++++++++----------- .../float-button/FloatButtonContent.tsx | 3 +- site/src/router/index.js | 17 ---------- 5 files changed, 16 insertions(+), 44 deletions(-) diff --git a/components/components.ts b/components/components.ts index 125b907332..6e42fc16d2 100644 --- a/components/components.ts +++ b/components/components.ts @@ -77,7 +77,7 @@ export type { EmptyProps } from './empty'; export { default as Empty } from './empty'; export type { FloatButtonProps, FloatButtonGroupProps } from './float-button/interface'; -export { default as FloatButton } from './float-button'; +export { default as FloatButton, FloatButtonGroup } from './float-button'; export type { FormProps, FormItemProps, FormInstance, FormItemInstance } from './form'; export { default as Form, FormItem, FormItemRest } from './form'; diff --git a/components/float-button/BackTop.tsx b/components/float-button/BackTop.tsx index 0cd818f1da..3e231cec26 100644 --- a/components/float-button/BackTop.tsx +++ b/components/float-button/BackTop.tsx @@ -140,8 +140,4 @@ const BackTop = defineComponent({ }, }); -if (process.env.NODE_ENV !== 'production') { - BackTop.displayName = 'BackTop'; -} - export default BackTop; diff --git a/components/float-button/FloatButton.tsx b/components/float-button/FloatButton.tsx index 827bfd7826..b201ea7fa6 100644 --- a/components/float-button/FloatButton.tsx +++ b/components/float-button/FloatButton.tsx @@ -28,20 +28,7 @@ const FloatButton = defineComponent({ const floatButtonRef = ref(null); const mergeShape = computed(() => { - return groupShape || props.shape; - }); - - const classString = computed(() => { - return classNames( - hashId.value, - prefixCls.value, - attrs.class, - `${prefixCls.value}-${props.type}`, - `${prefixCls.value}-${mergeShape.value}`, - { - [`${prefixCls.value}-rtl`]: direction.value === 'rtl', - }, - ); + return groupShape.value || props.shape; }); expose({ @@ -63,6 +50,17 @@ const FloatButton = defineComponent({ description, }; + const classString = classNames( + prefixCls.value, + `${prefixCls.value}-${props.type}`, + `${prefixCls.value}-${mergeShape.value}`, + { + [`${prefixCls.value}-rtl`]: direction.value === 'rtl', + }, + attrs.class, + hashId.value, + ); + const buttonNode = ( {{ @@ -98,7 +96,7 @@ const FloatButton = defineComponent({ ref={floatButtonRef} {...attrs} {...(restProps as any)} - class={classString.value} + class={classString} style={attrs.style as CSSProperties} > {buttonNode} @@ -108,7 +106,7 @@ const FloatButton = defineComponent({ ref={floatButtonRef} {...attrs} {...restProps} - class={classString.value} + class={classString} style={attrs.style as CSSProperties} type="button" > @@ -120,8 +118,4 @@ const FloatButton = defineComponent({ }, }); -if (process.env.NODE_ENV !== 'production') { - FloatButton.displayName = 'FloatButton'; -} - export default FloatButton; diff --git a/components/float-button/FloatButtonContent.tsx b/components/float-button/FloatButtonContent.tsx index 95b6d3bfb9..67a5591acf 100644 --- a/components/float-button/FloatButtonContent.tsx +++ b/components/float-button/FloatButtonContent.tsx @@ -1,14 +1,13 @@ import { defineComponent } from 'vue'; import FileTextOutlined from '@ant-design/icons-vue/FileTextOutlined'; import classNames from '../_util/classNames'; -import { initDefaultProps } from '../_util/props-util'; import { floatButtonContentProps } from './interface'; const FloatButtonContent = defineComponent({ compatConfig: { MODE: 3 }, name: 'AFloatButtonContent', inheritAttrs: false, - props: initDefaultProps(floatButtonContentProps(), {}), + props: floatButtonContentProps(), setup(props, { attrs, slots }) { return () => { const { description, prefixCls } = props; diff --git a/site/src/router/index.js b/site/src/router/index.js index dfa10dbe62..648a0bedce 100644 --- a/site/src/router/index.js +++ b/site/src/router/index.js @@ -38,23 +38,6 @@ const routes = [ }, component: () => import('../../../components/layout/demo/index.vue'), }, - { - path: 'float-button:lang(.*)', - meta: { - category: 'Components', - subtitle: '悬浮按钮', - type: '悬浮按钮', - cols: 1, - title: 'FloatButton', - cover: - 'https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*HS-wTIIwu0kAAAAAAAAAAAAADrJ8AQ/original', - }, - props: route => { - const hash = route.hash.replace('#', ''); - return { iframeName: hash }; - }, - component: () => import('../../../components/float-button/demo/index.vue'), - }, ], }, { From 557f082b937b96b82c54eab271261689681ae835 Mon Sep 17 00:00:00 2001 From: shifeng1993 Date: Tue, 28 Feb 2023 22:59:13 +0800 Subject: [PATCH 5/6] fix bug --- components/float-button/FloatButton.tsx | 2 +- components/float-button/FloatButtonGroup.tsx | 2 +- components/float-button/context.ts | 5 +---- components/float-button/style/index.ts | 6 +++--- site/src/router/index.js | 17 +++++++++++++++++ 5 files changed, 23 insertions(+), 9 deletions(-) diff --git a/components/float-button/FloatButton.tsx b/components/float-button/FloatButton.tsx index b201ea7fa6..2594b9bdfa 100644 --- a/components/float-button/FloatButton.tsx +++ b/components/float-button/FloatButton.tsx @@ -28,7 +28,7 @@ const FloatButton = defineComponent({ const floatButtonRef = ref(null); const mergeShape = computed(() => { - return groupShape.value || props.shape; + return groupShape || props.shape; }); expose({ diff --git a/components/float-button/FloatButtonGroup.tsx b/components/float-button/FloatButtonGroup.tsx index f80c6bc94b..24703d0d04 100644 --- a/components/float-button/FloatButtonGroup.tsx +++ b/components/float-button/FloatButtonGroup.tsx @@ -33,7 +33,7 @@ const FloatButtonGroup = defineComponent({ const floatButtonRef = ref(null); FloatButtonGroupContext.useProvide({ - shape: computed(() => props.shape), + shape: props.shape, }); const hoverAction = computed(() => { diff --git a/components/float-button/context.ts b/components/float-button/context.ts index 45864a7b75..e71883d545 100644 --- a/components/float-button/context.ts +++ b/components/float-button/context.ts @@ -1,10 +1,7 @@ -import type { Ref } from 'vue'; import createContext from '../_util/createContext'; import type { FloatButtonShape } from './interface'; -const FloatButtonGroupContext = createContext<{ shape: Ref } | undefined>( - undefined, -); +const FloatButtonGroupContext = createContext<{ shape: FloatButtonShape } | undefined>(undefined); export default FloatButtonGroupContext; diff --git a/components/float-button/style/index.ts b/components/float-button/style/index.ts index 61c390c7da..86d786ef35 100644 --- a/components/float-button/style/index.ts +++ b/components/float-button/style/index.ts @@ -2,9 +2,9 @@ import type { CSSObject } from '../../_util/cssinjs'; import { Keyframes } from '../../_util/cssinjs'; import type { FullToken, GenerateStyle } from '../../theme/internal'; import { genComponentStyleHook, mergeToken } from '../../theme/internal'; -import { initFadeMotion } from '../../_style/motion/fade'; -import { resetComponent } from '../../_style'; -import { initMotion } from '../../_style/motion/motion'; +import { initFadeMotion } from '../../style/motion/fade'; +import { resetComponent } from '../../style'; +import { initMotion } from '../../style/motion/motion'; /** Component only token. Which will handle additional calculation of alias token */ export interface ComponentToken { diff --git a/site/src/router/index.js b/site/src/router/index.js index 648a0bedce..dfa10dbe62 100644 --- a/site/src/router/index.js +++ b/site/src/router/index.js @@ -38,6 +38,23 @@ const routes = [ }, component: () => import('../../../components/layout/demo/index.vue'), }, + { + path: 'float-button:lang(.*)', + meta: { + category: 'Components', + subtitle: '悬浮按钮', + type: '悬浮按钮', + cols: 1, + title: 'FloatButton', + cover: + 'https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*HS-wTIIwu0kAAAAAAAAAAAAADrJ8AQ/original', + }, + props: route => { + const hash = route.hash.replace('#', ''); + return { iframeName: hash }; + }, + component: () => import('../../../components/float-button/demo/index.vue'), + }, ], }, { From a426af912eaa45debb75fecfd15d52de6e37cf2b Mon Sep 17 00:00:00 2001 From: shifeng1993 Date: Wed, 1 Mar 2023 23:47:47 +0800 Subject: [PATCH 6/6] fix .value --- components/float-button/FloatButton.tsx | 2 +- components/float-button/FloatButtonGroup.tsx | 2 +- components/float-button/context.ts | 26 ++++++++++++++++++-- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/components/float-button/FloatButton.tsx b/components/float-button/FloatButton.tsx index 2594b9bdfa..a0f4404526 100644 --- a/components/float-button/FloatButton.tsx +++ b/components/float-button/FloatButton.tsx @@ -28,7 +28,7 @@ const FloatButton = defineComponent({ const floatButtonRef = ref(null); const mergeShape = computed(() => { - return groupShape || props.shape; + return groupShape?.value || props.shape; }); expose({ diff --git a/components/float-button/FloatButtonGroup.tsx b/components/float-button/FloatButtonGroup.tsx index 24703d0d04..f80c6bc94b 100644 --- a/components/float-button/FloatButtonGroup.tsx +++ b/components/float-button/FloatButtonGroup.tsx @@ -33,7 +33,7 @@ const FloatButtonGroup = defineComponent({ const floatButtonRef = ref(null); FloatButtonGroupContext.useProvide({ - shape: props.shape, + shape: computed(() => props.shape), }); const hoverAction = computed(() => { diff --git a/components/float-button/context.ts b/components/float-button/context.ts index e71883d545..ef9aeeeb1f 100644 --- a/components/float-button/context.ts +++ b/components/float-button/context.ts @@ -1,7 +1,29 @@ -import createContext from '../_util/createContext'; +import type { Ref } from 'vue'; +import { inject, provide } from 'vue'; import type { FloatButtonShape } from './interface'; -const FloatButtonGroupContext = createContext<{ shape: FloatButtonShape } | undefined>(undefined); +function createContext>(defaultValue?: T) { + const contextKey = Symbol('floatButtonGroupContext'); + + const useProvide = (props: T) => { + provide(contextKey, props); + + return props; + }; + + const useInject = () => { + return inject(contextKey, defaultValue as T) || ({} as T); + }; + + return { + useProvide, + useInject, + }; +} + +const FloatButtonGroupContext = createContext<{ shape: Ref } | undefined>( + undefined, +); export default FloatButtonGroupContext;