Skip to content

Commit

Permalink
feat(comp:button,checkbox,radio): add waveless prop
Browse files Browse the repository at this point in the history
  • Loading branch information
liuzaijiang committed Nov 24, 2022
1 parent 0a59283 commit ad51683
Show file tree
Hide file tree
Showing 34 changed files with 241 additions and 8 deletions.
13 changes: 13 additions & 0 deletions packages/components/_private/wave/__tests__/wave.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//import { mount, MountingOptions } from '@vue/test-utils'
import { renderWork } from '@tests'

import Wave from '../src/Wave'
import { WaveProps } from '../src/types'

describe('Wave', () => {
//const WaveMount = (options?: MountingOptions<Partial<WaveProps>>) => mount(Wave, { ...(options as MountingOptions<WaveProps>)})

renderWork<WaveProps>(Wave, {
props: {},
})
})
20 changes: 20 additions & 0 deletions packages/components/_private/wave/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* @license
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import type { WaveComponent } from './src/types'

import Wave from './src/Wave'

const ɵWave = Wave as unknown as WaveComponent

export { ɵWave }

export type {
WaveInstance as ɵWaveInstance,
WaveComponent as ɵWaveComponent,
WavePublicProps as ɵWaveProps,
} from './src/types'
57 changes: 57 additions & 0 deletions packages/components/_private/wave/src/Wave.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/**
* @license
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import { computed, defineComponent, nextTick, ref } from 'vue'

import { useGlobalConfig } from '@idux/components/config'

import { waveProps } from './types'

export default defineComponent({
name: 'ɵWave',
props: waveProps,
setup(_, { expose }) {
const common = useGlobalConfig('common')
const mergedPrefixCls = computed(() => `${common.prefixCls}-wave`)

const selfRef = ref<HTMLElement>()

const play = () => {
nextTick(() => {
if (selfRef.value && selfRef.value.parentElement && selfRef.value.animate) {
const borderColor = getComputedStyle(selfRef.value.parentElement).borderColor
selfRef.value.animate(
[
{
// from
opacity: 0.6,
boxShadow: `0 0 1px 0 ${borderColor}`,
zIndex: 1,
easing: 'cubic-bezier(0, 0, 0.2, 1)',
},
{
// to
opacity: 0,
zIndex: 0,
boxShadow: `0 0 1px 5px ${borderColor}`,
},
],
600,
)
}
})
}

expose({
play,
})

return () => {
return <div ref={selfRef} aria-hidden class={mergedPrefixCls.value} />
}
},
})
20 changes: 20 additions & 0 deletions packages/components/_private/wave/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* @license
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import type { ExtractInnerPropTypes, ExtractPublicPropTypes } from '@idux/cdk/utils'
import type { DefineComponent, HTMLAttributes } from 'vue'

export const waveProps = {} as const

export interface WaveBindings {
play: () => void
}

export type WaveProps = ExtractInnerPropTypes<typeof waveProps>
export type WavePublicProps = ExtractPublicPropTypes<typeof waveProps>
export type WaveComponent = DefineComponent<Omit<HTMLAttributes, keyof WavePublicProps> & WavePublicProps>
export type WaveInstance = InstanceType<DefineComponent<WaveProps, WaveBindings>>
9 changes: 9 additions & 0 deletions packages/components/_private/wave/style/index.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.@{wave-prefix} {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
border-radius: inherit;
pointer-events: none;
}
3 changes: 3 additions & 0 deletions packages/components/_private/wave/style/themes/default.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@import './default.variable.less';

@import '../index.less';
2 changes: 2 additions & 0 deletions packages/components/_private/wave/style/themes/default.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// style dependencies
import './default.less'
Empty file.
3 changes: 3 additions & 0 deletions packages/components/_private/wave/style/themes/seer.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@import './seer.variable.less';

@import '../index.less';
2 changes: 2 additions & 0 deletions packages/components/_private/wave/style/themes/seer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// style dependencies
import './seer.less'
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@import './default.variable.less';
27 changes: 27 additions & 0 deletions packages/components/button/__tests__/button.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,31 @@ describe('Button', () => {
})
expect(wrapper.text()).toEqual(text)
})

test('waveless work', async () => {
const text = 'Button'
const wrapper = ButtonMount({
slots: {
default: text,
},
})

expect(wrapper.find('.ix-wave').exists()).toBeTruthy()

await wrapper.setProps({ waveless: true })

expect(wrapper.find('.ix-wave').exists()).toBeFalsy()

await wrapper.setProps({ waveless: false, mode: 'text' })

expect(wrapper.find('.ix-wave').exists()).toBeFalsy()

await wrapper.setProps({ mode: 'link' })

expect(wrapper.find('.ix-wave').exists()).toBeFalsy()

await wrapper.setProps({ mode: 'default' })

expect(wrapper.find('.ix-wave').exists()).toBeTruthy()
})
})
2 changes: 1 addition & 1 deletion packages/components/button/demo/Group.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<IxSpace>
<IxButtonGroup>
<IxButtonGroup vertical>
<IxButton>Default</IxButton>
<IxButton icon="setting" shape="square"></IxButton>
</IxButtonGroup>
Expand Down
1 change: 1 addition & 0 deletions packages/components/button/docs/Api.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
| `shape` | 设置按钮形状 | `'square' \| 'circle' \| 'round'` | - | - |- |
| `size` | 设置按钮大小 | `'xl' \| 'lg' \| 'md' \| 'sm' \| 'xs'` | `'md'` || `seer` 主题下默认为 `'sm'` |
| `type` | 原生 `button``type` 属性 | `'button' \| 'submit' \| 'reset'` | `'button'` | - | 参考 [HTML 标准](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/button#attr-type) |
| `waveless` | 是否关闭按钮点击时波纹动画 | `boolean` | `false` || 不支持`link``text`模式,且存在[浏览器兼容性](https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Animations_API/Keyframe_Formats#%E6%B5%8F%E8%A7%88%E5%99%A8%E5%85%BC%E5%AE%B9%E6%80%A7) |

#### ButtonSlots

Expand Down
13 changes: 12 additions & 1 deletion packages/components/button/src/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
* found in the LICENSE file at https://github.com/IDuxFE/idux/blob/main/LICENSE
*/

import { type VNodeChild, computed, defineComponent, inject, normalizeClass } from 'vue'
import { type VNodeChild, computed, defineComponent, inject, normalizeClass, ref } from 'vue'

import { callEmit } from '@idux/cdk/utils'
import { ɵWave, type ɵWaveInstance } from '@idux/components/_private/wave'
import { useGlobalConfig } from '@idux/components/config'
import { FORM_TOKEN } from '@idux/components/form'
import { IxIcon } from '@idux/components/icon'
Expand All @@ -27,8 +28,13 @@ export default defineComponent({
const groupProps = inject(buttonToken, {})
const formContext = inject(FORM_TOKEN, null)

const waveRef = ref<ɵWaveInstance>()

const mode = computed(() => props.mode ?? groupProps.mode ?? 'default')
const size = computed(() => props.size ?? groupProps.size ?? formContext?.size.value ?? config.size)
const mergedWaveless = computed(
() => mode.value === 'text' || mode.value === 'link' || (props.waveless ?? config.waveless),
)

const classes = computed(() => {
const { block, danger, disabled, ghost, loading, icon, shape = groupProps.shape } = props
Expand All @@ -53,6 +59,10 @@ export default defineComponent({
evt.stopImmediatePropagation()
return
}

if (!mergedWaveless.value && waveRef.value) {
waveRef.value.play()
}
callEmit(props.onClick, evt)
}

Expand Down Expand Up @@ -82,6 +92,7 @@ export default defineComponent({
return (
<button class={classes.value} disabled={disabled || loading} type={type} onClick={handleClick}>
{children}
{!mergedWaveless.value && <ɵWave ref={waveRef} />}
</button>
)
}
Expand Down
4 changes: 3 additions & 1 deletion packages/components/button/src/ButtonGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { buttonGroupProps } from './types'
export default defineComponent({
name: 'IxButtonGroup',
props: buttonGroupProps,
setup(props, { slots }) {
setup(props, { attrs, slots }) {
const common = useGlobalConfig('common')
const mergedPrefixCls = computed(() => `${common.prefixCls}-button-group`)

Expand All @@ -29,6 +29,8 @@ export default defineComponent({
})
})

console.log(attrs)

provide(buttonToken, props)

return () => (
Expand Down
1 change: 1 addition & 0 deletions packages/components/button/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const buttonProps = {
size: String as PropType<ButtonSize>,
shape: String as PropType<ButtonShape>,
type: { type: String as PropType<ButtonType>, default: 'button' },
waveless: { type: Boolean, default: undefined },

onClick: [Function, Array] as PropType<(evt: MouseEvent) => void>,
} as const
Expand Down
2 changes: 1 addition & 1 deletion packages/components/button/style/themes/default.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// style dependencies

import '@idux/components/_private/wave/style/themes/default'
import '@idux/components/icon/style/themes/default'

import './default.less'
2 changes: 1 addition & 1 deletion packages/components/button/style/themes/seer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// style dependencies

import '@idux/components/_private/wave/style/themes/seer'
import '@idux/components/icon/style/themes/seer'

import './seer.less'
14 changes: 14 additions & 0 deletions packages/components/checkbox/__tests__/checkbox.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,18 @@ describe('Checkbox', () => {

expect(blurFn).toBeCalledTimes(1)
})

test('waveless work', async () => {
const wrapper = CheckboxMount()

expect(wrapper.find('.ix-wave').exists()).toBeFalsy()

await wrapper.setProps({ waveless: false, buttoned: true })

expect(wrapper.find('.ix-wave').exists()).toBeTruthy()

await wrapper.setProps({ buttoned: false })

expect(wrapper.find('.ix-wave').exists()).toBeFalsy()
})
})
1 change: 1 addition & 0 deletions packages/components/checkbox/docs/Api.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
| `value` | 设置勾选框的值,与 `IxCheckboxGroup` 配合使用 | `any`| - | - | 不传时使用 `key` 作为 `value` |
| `size` | 按钮大小 | `'sm' \| 'md' \| 'lg'` | `md` | - |`buttoned``true`时生效 |
| `onChange` | 选中状态发生变化后的回调 | `(newChecked: boolean \| string \| number, oldChecked: boolean \| string \| number) => void`| - | - | - |
| `waveless` | 是否关闭按钮点击时波纹动画 | `boolean` | `false` || 仅在`buttoned``true`时生效,且存在[浏览器兼容性](https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Animations_API/Keyframe_Formats#%E6%B5%8F%E8%A7%88%E5%99%A8%E5%85%BC%E5%AE%B9%E6%80%A7) |

#### CheckboxMethods

Expand Down
12 changes: 11 additions & 1 deletion packages/components/checkbox/src/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@

/* eslint-disable vue/no-ref-as-operand */

import { type ComputedRef, computed, defineComponent, inject, normalizeClass, ref } from 'vue'
import { type ComputedRef, Ref, computed, defineComponent, inject, normalizeClass, ref } from 'vue'

import { isNil } from 'lodash-es'

import { useAccessorAndControl } from '@idux/cdk/forms'
import { callEmit } from '@idux/cdk/utils'
import { ɵWave, type ɵWaveInstance } from '@idux/components/_private/wave'
import { useGlobalConfig } from '@idux/components/config'
import { FORM_TOKEN, useFormElement, useFormItemRegister } from '@idux/components/form'
import { convertStringVNode, useKey } from '@idux/components/utils'
Expand All @@ -37,6 +38,7 @@ export default defineComponent({
const config = useGlobalConfig('checkbox')

const { elementRef, focus, blur } = useFormElement()
const waveRef = ref<ɵWaveInstance>()
expose({ focus, blur })

const formContext = inject(FORM_TOKEN, null)
Expand All @@ -52,10 +54,13 @@ export default defineComponent({
})
const isButtoned = computed(() => props.buttoned ?? checkboxGroup?.props.buttoned ?? false)
const size = computed(() => props.size ?? checkboxGroup?.props.size ?? formContext?.size.value ?? config.size)
const mergedWaveless = computed(() => (props.waveless ?? config.waveless) || !isButtoned.value)
const { isChecked, isDisabled, isFocused, handleChange, handleBlur, handleFocus } = useCheckbox(
props,
checkboxGroup,
mergedValue,
waveRef,
mergedWaveless,
)
const classes = computed(() => {
const { indeterminate } = props
Expand Down Expand Up @@ -109,6 +114,7 @@ export default defineComponent({
{!isButtoned.value && <span class={`${prefixCls}-input-box`} tabindex={tabindex as number} />}
</span>
{isButtoned.value && <span class={`${prefixCls}-input-tick`} tabindex={tabindex as number} />}
{!mergedWaveless.value && <ɵWave ref={waveRef} />}
{labelNode && <span class={`${prefixCls}-label`}>{labelNode}</span>}
</label>
)
Expand All @@ -120,6 +126,8 @@ const useCheckbox = (
props: CheckboxProps,
checkboxGroup: CheckboxGroupContext | null,
mergedValue: ComputedRef<unknown>,
waveRef: Ref<ɵWaveInstance | undefined>,
mergedWaveless: ComputedRef<boolean>,
) => {
let isChecked: ComputedRef<boolean>
let isDisabled: ComputedRef<boolean>
Expand Down Expand Up @@ -162,6 +170,7 @@ const useCheckbox = (
accessor.setValue(newValue)
callEmit(props.onChange, checkValue, oldCheckValue)
callEmit(groupProps.onChange, newValue, oldValue)
!mergedWaveless.value && waveRef.value?.play()
}
} else {
const { accessor, control } = useAccessorAndControl<CheckValue>({ valueKey: 'checked' })
Expand All @@ -183,6 +192,7 @@ const useCheckbox = (
const oldChecked = !checked ? trueValue : falseValue
accessor.setValue(newChecked)
callEmit(props.onChange, newChecked, oldChecked)
!mergedWaveless.value && waveRef.value?.play()
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/components/checkbox/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export const checkboxProps = {
falseValue: { type: [String, Number, Boolean] as PropType<CheckValue>, default: false },
value: { type: null, default: undefined },
size: { type: String as PropType<FormSize>, default: undefined },
waveless: { type: Boolean, default: false },

// events
'onUpdate:checked': { type: [Function, Array] as PropType<MaybeArray<(checked: any) => void>> },
Expand Down
2 changes: 1 addition & 1 deletion packages/components/checkbox/style/index.less
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
}

&.@{button-prefix} {
z-index: 2;
z-index: 1;
color: @checkbox-color-active;
border-color: @checkbox-color-active;
}
Expand Down

0 comments on commit ad51683

Please sign in to comment.