From 4775732760310603ffe6ddd247faaf2b172b80fb Mon Sep 17 00:00:00 2001 From: productdevbook Date: Sat, 9 Sep 2023 17:44:23 +0300 Subject: [PATCH 1/3] fix(toogle-group): tabindex --- .../roving-focus/src/RovingFocusGroup.ts | 20 +-- .../roving-focus/src/RovingFocusGroupImpl.ts | 31 ++-- .../roving-focus/src/RovingFocusGroupItem.ts | 13 +- .../toggle-group/src/ToggleGroup.ts | 86 ++++++++-- .../toggle-group/src/ToggleGroupImpl.ts | 44 +++-- .../src/ToggleGroupImplMultiple.ts | 121 ++++++++++++++ .../toggle-group/src/ToggleGroupImplSingle.ts | 116 +++++++++++++ .../toggle-group/src/ToggleGroupItem.ts | 25 ++- .../toggle-group/src/ToggleGroupItemImpl.ts | 47 +++--- .../toggle-group/src/ToggleGroupVariant.ts | 157 ------------------ packages/components/toggle-group/src/index.ts | 15 +- .../src/stories/ToggleGroupDemo.vue | 11 +- packages/components/toggle/src/toggle.ts | 26 +-- .../use-composable/src/useControllable.ts | 4 +- 14 files changed, 424 insertions(+), 292 deletions(-) create mode 100644 packages/components/toggle-group/src/ToggleGroupImplMultiple.ts create mode 100644 packages/components/toggle-group/src/ToggleGroupImplSingle.ts delete mode 100644 packages/components/toggle-group/src/ToggleGroupVariant.ts diff --git a/packages/components/roving-focus/src/RovingFocusGroup.ts b/packages/components/roving-focus/src/RovingFocusGroup.ts index 56d879286..11c13cdf3 100644 --- a/packages/components/roving-focus/src/RovingFocusGroup.ts +++ b/packages/components/roving-focus/src/RovingFocusGroup.ts @@ -2,7 +2,7 @@ import { createProvideScope } from '@oku-ui/provide' import type { CollectionPropsType } from '@oku-ui/collection' import { createCollection } from '@oku-ui/collection' import type { Ref } from 'vue' -import { defineComponent, h, mergeProps, toRefs } from 'vue' +import { defineComponent, h, mergeProps } from 'vue' import { useForwardRef } from '@oku-ui/use-composable' import { primitiveProps } from '@oku-ui/primitive' import { OkuRovingFocusGroupImpl, rovingFocusGroupImplProps } from './RovingFocusGroupImpl' @@ -90,16 +90,6 @@ const rovingFocusGroup = defineComponent({ }, emits: rovingFocusGroupProps.emits, setup(props, { slots, attrs }) { - const { - currentTabStopId, - dir, - loop, - orientation, - defaultCurrentTabStopId, - asChild, - ...rest - } = toRefs(props) - const forwardedRef = useForwardRef() return () => { return h(CollectionProvider, { @@ -109,13 +99,7 @@ const rovingFocusGroup = defineComponent({ scope: props.scopeOkuRovingFocusGroup, }, { default: () => h(OkuRovingFocusGroupImpl, { - ...mergeProps(rest, attrs), - asChild: asChild.value, - currentTabStopId: currentTabStopId?.value, - defaultCurrentTabStopId: defaultCurrentTabStopId?.value, - dir: dir?.value, - loop: loop?.value, - orientation: orientation?.value, + ...mergeProps(attrs, props), ref: forwardedRef, }, slots), }), diff --git a/packages/components/roving-focus/src/RovingFocusGroupImpl.ts b/packages/components/roving-focus/src/RovingFocusGroupImpl.ts index f1ba38488..ce121212e 100644 --- a/packages/components/roving-focus/src/RovingFocusGroupImpl.ts +++ b/packages/components/roving-focus/src/RovingFocusGroupImpl.ts @@ -1,6 +1,6 @@ import type { ComputedRef, PropType } from 'vue' -import { computed, defineComponent, h, mergeProps, ref, toRefs, unref, watchEffect } from 'vue' -import { useCallbackRef, useComposedRefs, useControllable, useForwardRef } from '@oku-ui/use-composable' +import { computed, defineComponent, h, mergeProps, reactive, ref, toRefs, watchEffect } from 'vue' +import { useComposedRefs, useControllable, useForwardRef } from '@oku-ui/use-composable' import type { OkuElement } from '@oku-ui/primitive' @@ -60,17 +60,14 @@ const RovingFocusGroupImpl = defineComponent({ }, emits: rovingFocusGroupImplProps.emits, setup(props, { attrs, slots, emit }) { - const _attrs = attrs as Omit const { + scopeOkuRovingFocusGroup, orientation, loop, + dir, currentTabStopId: currentTabStopIdProp, defaultCurrentTabStopId, - onEntryFocus, - asChild, - scopeOkuRovingFocusGroup, - dir, - ...propsData + ...groupProps } = toRefs(props) const buttonRef = ref(null) @@ -87,7 +84,6 @@ const RovingFocusGroupImpl = defineComponent({ }) const isTabbingBackOut = ref(false) - const handleEntryFocus = useCallbackRef(onEntryFocus?.value || undefined) const getItems = useCollection(scopeOkuRovingFocusGroup.value) const isClickFocusRef = ref(false) const focusableItemsCount = ref(0) @@ -95,8 +91,12 @@ const RovingFocusGroupImpl = defineComponent({ watchEffect(() => { const node = buttonRef.value if (node) { - node.addEventListener(ENTRY_FOCUS, handleEntryFocus.value) - return () => node.removeEventListener(ENTRY_FOCUS, handleEntryFocus.value) + node.addEventListener(ENTRY_FOCUS, (event) => { + emit('entryFocus', event) + }) + return () => node.removeEventListener(ENTRY_FOCUS, (event) => { + emit('entryFocus', event) + }) } }) @@ -105,7 +105,7 @@ const RovingFocusGroupImpl = defineComponent({ orientation, dir, loop, - currentTabStopId: currentTabStopId || null, + currentTabStopId, onItemFocus: (tabStopId: string) => { updateCurrentTabStopId(tabStopId) }, @@ -120,17 +120,18 @@ const RovingFocusGroupImpl = defineComponent({ }, }) + const _reactiveProupProps = reactive(groupProps) + return () => { return h(Primitive.div, { 'tabindex': isTabbingBackOut.value || focusableItemsCount.value === 0 ? -1 : 0, 'data-orientation': orientation?.value, - ...unref(mergeProps(propsData, _attrs)), + ...mergeProps(attrs, _reactiveProupProps), 'ref': composedRefs, 'style': { outline: 'none', - ..._attrs.style as any, + ...attrs.style as any, }, - 'asChild': asChild.value, 'onMousedown': composeEventHandlers((e) => { emit('mousedown', e) }, () => { diff --git a/packages/components/roving-focus/src/RovingFocusGroupItem.ts b/packages/components/roving-focus/src/RovingFocusGroupItem.ts index e697a6983..49d484174 100644 --- a/packages/components/roving-focus/src/RovingFocusGroupItem.ts +++ b/packages/components/roving-focus/src/RovingFocusGroupItem.ts @@ -1,4 +1,4 @@ -import { computed, defineComponent, h, nextTick, toRefs, watchEffect } from 'vue' +import { computed, defineComponent, h, mergeProps, nextTick, reactive, toRefs, watchEffect } from 'vue' import { useForwardRef, useId } from '@oku-ui/use-composable' import { Primitive, primitiveProps } from '@oku-ui/primitive' @@ -63,15 +63,13 @@ const rovingFocusGroupItem = defineComponent({ }, emits: rovingFocusItemProps.emits, setup(props, { attrs, slots, emit }) { - const _attrs = attrs as any const { + scopeOkuRovingFocusGroup, focusable, active, tabStopId, - scopeOkuRovingFocusGroup, - asChild, + ...itemProps } = toRefs(props) - const attrsItems = _attrs const autoId = useId() const id = computed(() => tabStopId.value || autoId) @@ -91,7 +89,7 @@ const rovingFocusGroupItem = defineComponent({ }) }) }) - + const reactiveItemProps = reactive(itemProps) return () => { return h(CollectionItemSlot, { id: id.value, @@ -102,10 +100,9 @@ const rovingFocusGroupItem = defineComponent({ default: () => { return h(Primitive.span, { 'tabindex': isCurrentTabStop.value ? 0 : -1, - ...attrsItems, + ...mergeProps(attrs, reactiveItemProps), 'data-orientation': inject.orientation?.value, 'ref': forwardedRef, - 'asChild': asChild.value, 'onMousedown': composeEventHandlers((e) => { emit('mousedown', e) diff --git a/packages/components/toggle-group/src/ToggleGroup.ts b/packages/components/toggle-group/src/ToggleGroup.ts index 15fa35c63..b10c030c0 100644 --- a/packages/components/toggle-group/src/ToggleGroup.ts +++ b/packages/components/toggle-group/src/ToggleGroup.ts @@ -1,6 +1,6 @@ import { primitiveProps } from '@oku-ui/primitive' -import { defineComponent, h, mergeProps } from 'vue' -import type { Ref } from 'vue' +import { defineComponent, h, mergeProps, reactive } from 'vue' +import type { PropType, Ref } from 'vue' import { useForwardRef } from '@oku-ui/use-composable' import { createProvideScope } from '@oku-ui/provide' @@ -8,9 +8,10 @@ import { createRovingFocusGroupScope } from '@oku-ui/roving-focus' import { scopeToggleGroupProps } from './utils' -import type { ToggleGroupImplElement, ToggleGroupImplNaviteElement } from './ToggleGroupImpl' -import type { ToggleGroupVariantEmits, ToggleGroupVariantProps } from './ToggleGroupVariant' -import { OkuToggleGroupVariant, toggleGroupVariantProps } from './ToggleGroupVariant' +import { toggleGroupImplProps } from './ToggleGroupImpl' +import type { ToggleGroupImplElement, ToggleGroupImplNaviteElement, ToggleGroupImplProps } from './ToggleGroupImpl' +import { OkuToggleGroupImplSingle } from './ToggleGroupImplSingle' +import { OkuToggleGroupImplMultiple } from './ToggleGroupImplMultiple' export const TOGGLE_GROUP_NAME = 'OkuToggleGroup' @@ -19,7 +20,7 @@ export const [createToggleGroupProvide, createToggleGroupScope] = createProvideS ]) type ToggleGroupValueProvide = { - type: Ref<'single' | 'multiple'> + type: 'single' | 'multiple' value: Ref onItemActivate(value: string): void onItemDeactivate(value: string): void @@ -44,6 +45,52 @@ export type ToggleGroupEmits = ToggleGroupVariantEmits export type ToggleGroupElement = ToggleGroupImplElement export type ToggleGroupNaviteElement = ToggleGroupImplNaviteElement +export interface ToggleGroupVariantProps extends ToggleGroupImplProps { + type: 'single' | 'multiple' + /** + * The controlled stateful value of the item that is pressed. + */ + value?: string | string[] + /** + * The value of the item that is pressed when initially rendered. Use + * `defaultValue` if you do not need to control the state of a toggle group. + */ + defaultValue?: string | string[] +} + +export type ToggleGroupVariantEmits = { + /** + * The callback that fires when the value of the toggle group changes. + */ + 'valueChange': [value: string | string[]] +} + +export const toggleGroupVariantProps = { + props: { + type: { + type: [String] as PropType<'single' | 'multiple'>, + required: true, + }, + modelValue: { + type: [String, Array] as PropType, + default: undefined, + }, + value: { + type: [String, Array] as PropType, + default: undefined, + }, + defaultValue: { + type: [String, Array] as PropType, + default: undefined, + }, + ...toggleGroupImplProps.props, + }, + emits: { + // eslint-disable-next-line unused-imports/no-unused-vars + valueChange: (value: string | string[]) => true, + }, +} + export const toggleGroupProps = { props: { ...toggleGroupVariantProps.props, @@ -55,6 +102,10 @@ export const toggleGroupProps = { const toggleGroup = defineComponent({ name: TOGGLE_GROUP_NAME, + components: { + OkuToggleGroupImplSingle, + OkuToggleGroupImplMultiple, + }, inheritAttrs: false, props: { ...toggleGroupProps.props, @@ -63,12 +114,27 @@ const toggleGroup = defineComponent({ }, emits: toggleGroupProps.emits, setup(props, { slots, attrs }) { + const { type, ...toggleGroupProps } = props + const forwardedRef = useForwardRef() return () => { - return h(OkuToggleGroupVariant, { - ...mergeProps(attrs, props), - ref: forwardedRef, - }, slots) + if (type === 'single') { + const singleProps = reactive(toggleGroupProps) + return h(OkuToggleGroupImplSingle, { + ...mergeProps(attrs, singleProps), + ref: forwardedRef, + }, slots) + } + + if (type === 'multiple') { + const multipleProps = reactive(toggleGroupProps) + return h(OkuToggleGroupImplMultiple, { + ...mergeProps(attrs, multipleProps), + ref: forwardedRef, + }, slots) + } + + throw new Error(`Missing prop \`type\` expected on \`${TOGGLE_GROUP_NAME}\``) } }, }) diff --git a/packages/components/toggle-group/src/ToggleGroupImpl.ts b/packages/components/toggle-group/src/ToggleGroupImpl.ts index 6eeb03ef5..2c2bfa2dc 100644 --- a/packages/components/toggle-group/src/ToggleGroupImpl.ts +++ b/packages/components/toggle-group/src/ToggleGroupImpl.ts @@ -1,6 +1,6 @@ import type { OkuElement, PrimitiveProps } from '@oku-ui/primitive' import { Primitive, primitiveProps } from '@oku-ui/primitive' -import { defineComponent, h, toRefs } from 'vue' +import { defineComponent, h, mergeProps, reactive, toRef, toRefs } from 'vue' import type { PropType } from 'vue' import { useForwardRef } from '@oku-ui/use-composable' import { OkuRovingFocusGroup, type RovingFocusGroupProps } from '@oku-ui/roving-focus' @@ -41,6 +41,7 @@ export const toggleGroupImplProps = { disabled: { type: [Boolean] as PropType, default: false, + required: false, }, /** * Whether the group should maintain roving focus of its buttons. @@ -49,18 +50,22 @@ export const toggleGroupImplProps = { rovingFocus: { type: [Boolean] as PropType, default: true, + required: false, }, loop: { type: [Boolean, String] as PropType, default: true, + required: false, }, orientation: { type: [String] as PropType, default: undefined, + required: false, }, dir: { type: [String] as PropType, default: undefined, + required: false, }, ...primitiveProps, }, @@ -68,6 +73,10 @@ export const toggleGroupImplProps = { const toggleGroupImpl = defineComponent({ name: TOGGLE_GROUP_IMPL_NAME, + components: { + Primitive, + OkuRovingFocusGroup, + }, inheritAttrs: false, props: { ...toggleGroupImplProps.props, @@ -75,19 +84,27 @@ const toggleGroupImpl = defineComponent({ ...primitiveProps, }, setup(props, { slots, attrs }) { - const { dir, disabled, loop, orientation, rovingFocus } = toRefs(props) - const rovingFocusGroupScope = useRovingFocusGroupScope(props.scopeOkuToggleGroup) + const { + scopeOkuToggleGroup, + disabled, + rovingFocus, + orientation, + dir, + loop, + ...toggleGroupProps + } = toRefs(props) + const rovingFocusGroupScope = useRovingFocusGroupScope(scopeOkuToggleGroup.value) const direction = useDirection(dir.value) const forwardedRef = useForwardRef() toggleGroupProvide({ - scope: props.scopeOkuToggleGroup, - rovingFocus, - disabled, + scope: scopeOkuToggleGroup.value, + rovingFocus: toRef(props, 'rovingFocus'), + disabled: toRef(() => disabled.value), }) - - return () => rovingFocus.value + const _toggleGroupProps = reactive(toggleGroupProps) + return () => rovingFocus ? h(OkuRovingFocusGroup, { asChild: true, ...rovingFocusGroupScope, @@ -96,19 +113,16 @@ const toggleGroupImpl = defineComponent({ loop: loop.value, }, { default: () => h(Primitive.div, { - role: 'group', + role: 'radiogroup', dir: direction.value, - asChild: props.asChild, - ...attrs, - scopeOkuToggleGroup: props.scopeOkuToggleGroup, + ...mergeProps(attrs, _toggleGroupProps), ref: forwardedRef, }, slots), }) : h(Primitive.div, { - role: 'group', + role: 'radiogroup', dir: direction.value, - ...attrs, - asChild: props.asChild, + ...mergeProps(attrs, _toggleGroupProps), ref: forwardedRef, }, slots) }, diff --git a/packages/components/toggle-group/src/ToggleGroupImplMultiple.ts b/packages/components/toggle-group/src/ToggleGroupImplMultiple.ts new file mode 100644 index 000000000..5c3bddcd6 --- /dev/null +++ b/packages/components/toggle-group/src/ToggleGroupImplMultiple.ts @@ -0,0 +1,121 @@ +import { computed, defineComponent, h, mergeProps, reactive, toRefs, useModel } from 'vue' +import type { PropType } from 'vue' +import { useControllable, useForwardRef } from '@oku-ui/use-composable' + +import { scopeToggleGroupProps } from './utils' +import { OkuToggleGroupImpl, type ToggleGroupImplElement, type ToggleGroupImplNaviteElement, type ToggleGroupImplProps, toggleGroupImplProps } from './ToggleGroupImpl' +import { toggleGroupValueProvider } from './ToggleGroup' + +const TOGGLE_GROUP_NAME = 'OkuToggleGroupImplSingle' + +export type ToggleGroupVariantNaviteElement = ToggleGroupImplNaviteElement +export type ToggleGroupVariantElement = ToggleGroupImplElement + +export interface ToggleGroupImplMultipleProps extends ToggleGroupImplProps { + /** + * The controlled stateful value of the item that is pressed. + */ + value?: string[] + /** + * The value of the item that is pressed when initially rendered. Use + * `defaultValue` if you do not need to control the state of a toggle group. + */ + defaultValue?: string[] +} + +export type ToggleGroupVariantEmits = { + 'update:modelValue': [value: string[]] + /** + * The callback that fires when the value of the toggle group changes. + */ + 'valueChange': [value: string[]] +} + +export const toggleGroupImplMultipleProps = { + props: { + modelValue: { + type: [Array] as PropType, + default: undefined, + }, + value: { + type: [Array] as PropType, + default: undefined, + }, + defaultValue: { + type: [Array] as PropType, + default: undefined, + }, + ...toggleGroupImplProps.props, + }, + emits: { + // eslint-disable-next-line unused-imports/no-unused-vars + 'update:modelValue': (value: string[]) => true, + // eslint-disable-next-line unused-imports/no-unused-vars + 'valueChange': (value: string[]) => true, + }, +} + +const toggleGroupImplMultiple = defineComponent({ + name: TOGGLE_GROUP_NAME, + inheritAttrs: false, + props: { + ...toggleGroupImplMultipleProps.props, + ...scopeToggleGroupProps, + }, + emits: toggleGroupImplMultipleProps.emits, + setup(props, { slots, emit, attrs }) { + const { + value: valueProp, + defaultValue, + onValueChange: _onValueChange, + ...toggleGroupMultipleProps + } = toRefs(props) + const reactiveToggleGroupMultipleProps = reactive(toggleGroupMultipleProps) + const forwardedRef = useForwardRef() + + const modelValue = useModel(props, 'modelValue') + const proxyChecked = computed({ + get: () => modelValue.value !== undefined + ? modelValue.value + : valueProp.value !== undefined ? valueProp.value : undefined, + set: () => { + }, + }) + + const { state, updateValue } = useControllable>({ + prop: computed(() => proxyChecked.value), + defaultProp: computed(() => defaultValue.value), + onChange: (result: any) => { + emit('valueChange', result) + modelValue.value = result + }, + initialValue: [], + }) + + const handleButtonActivate = (itemValue: string) => { + updateValue([...state.value, itemValue]) + } + + const handleButtonDeactivate = (itemValue: string) => { + updateValue(state.value.filter(value => value !== itemValue)) + } + + toggleGroupValueProvider({ + scope: props.scopeOkuToggleGroup, + value: state, + onItemActivate: handleButtonActivate, + onItemDeactivate: handleButtonDeactivate, + type: 'multiple', + }) + + return () => h(OkuToggleGroupImpl, { + ...mergeProps(attrs, reactiveToggleGroupMultipleProps), + ref: forwardedRef, + }, slots) + }, +}) + +export const OkuToggleGroupImplMultiple = toggleGroupImplMultiple as typeof toggleGroupImplMultiple & +(new () => { + $props: ToggleGroupVariantNaviteElement +}) diff --git a/packages/components/toggle-group/src/ToggleGroupImplSingle.ts b/packages/components/toggle-group/src/ToggleGroupImplSingle.ts new file mode 100644 index 000000000..f17bb5fc2 --- /dev/null +++ b/packages/components/toggle-group/src/ToggleGroupImplSingle.ts @@ -0,0 +1,116 @@ +import { computed, defineComponent, h, mergeProps, reactive, toRefs, useModel } from 'vue' +import type { PropType } from 'vue' +import { useControllable, useForwardRef } from '@oku-ui/use-composable' + +import { scopeToggleGroupProps } from './utils' +import { OkuToggleGroupImpl, type ToggleGroupImplElement, type ToggleGroupImplNaviteElement, type ToggleGroupImplProps, toggleGroupImplProps } from './ToggleGroupImpl' +import { toggleGroupValueProvider } from './ToggleGroup' + +const TOGGLE_GROUP_NAME = 'OkuToggleGroupImplSingle' + +export type ToggleGroupVariantNaviteElement = ToggleGroupImplNaviteElement +export type ToggleGroupVariantElement = ToggleGroupImplElement + +export interface ToggleGroupImplSingleProps extends ToggleGroupImplProps { + /** + * The controlled stateful value of the item that is pressed. + */ + value?: string + /** + * The value of the item that is pressed when initially rendered. Use + * `defaultValue` if you do not need to control the state of a toggle group. + */ + defaultValue?: string +} + +export type ToggleGroupVariantEmits = { + /** + * The callback that fires when the value of the toggle group changes. + */ + 'valueChange': [value: string] +} + +export const toggleGroupImplSingleProps = { + props: { + modelValue: { + type: [String] as PropType, + default: undefined, + }, + value: { + type: [String] as PropType, + default: undefined, + }, + defaultValue: { + type: [String] as PropType, + default: undefined, + }, + ...toggleGroupImplProps.props, + }, + emits: { + // eslint-disable-next-line unused-imports/no-unused-vars + 'update:modelValue': (value: string) => true, + // eslint-disable-next-line unused-imports/no-unused-vars + 'valueChange': (value: string) => true, + }, +} + +const groupImplSingle = defineComponent({ + name: TOGGLE_GROUP_NAME, + inheritAttrs: false, + props: { + ...toggleGroupImplSingleProps.props, + ...scopeToggleGroupProps, + }, + emits: toggleGroupImplSingleProps.emits, + setup(props, { slots, emit, attrs }) { + const { + value: valueProp, + defaultValue, + onValueChange: _onValueChange, + ...toggleGroupVariant + } = toRefs(props) + const reactiveToggleGroupVariant = reactive(toggleGroupVariant) + const forwardedRef = useForwardRef() + + const modelValue = useModel(props, 'modelValue') + + const proxyChecked = computed({ + get: () => modelValue.value !== undefined + ? modelValue.value + : valueProp.value !== undefined ? valueProp.value : undefined, + set: () => { + }, + }) + + const { state, updateValue } = useControllable({ + prop: computed(() => proxyChecked.value), + defaultProp: computed(() => defaultValue.value), + onChange: (result) => { + emit('valueChange', result) + modelValue.value = result + }, + }) + + toggleGroupValueProvider({ + scope: props.scopeOkuToggleGroup, + value: computed(() => state.value ? [state.value] : []), + onItemActivate: (value: string) => { + updateValue(value) + }, + onItemDeactivate: () => { + updateValue('') + }, + type: 'single', + }) + + return () => h(OkuToggleGroupImpl, { + ...mergeProps(attrs, reactiveToggleGroupVariant), + ref: forwardedRef, + }, slots) + }, +}) + +export const OkuToggleGroupImplSingle = groupImplSingle as typeof groupImplSingle & +(new () => { + $props: ToggleGroupVariantNaviteElement +}) diff --git a/packages/components/toggle-group/src/ToggleGroupItem.ts b/packages/components/toggle-group/src/ToggleGroupItem.ts index f32b52c2d..e77581fbd 100644 --- a/packages/components/toggle-group/src/ToggleGroupItem.ts +++ b/packages/components/toggle-group/src/ToggleGroupItem.ts @@ -1,5 +1,5 @@ import { primitiveProps, propsOmit } from '@oku-ui/primitive' -import { computed, defineComponent, h, mergeProps, ref, toRefs } from 'vue' +import { computed, defineComponent, h, mergeProps, ref } from 'vue' import { useForwardRef } from '@oku-ui/use-composable' import { OkuRovingFocusGroupItem } from '@oku-ui/roving-focus' @@ -28,6 +28,10 @@ export const toggleGroupItemProps = { const toggleGroupItem = defineComponent({ name: TOGGLE_ITEM_NAME, + components: { + OkuToggleGroupItemImpl, + OkuRovingFocusGroupItem, + }, inheritAttrs: false, props: { ...toggleGroupItemProps.props, @@ -35,16 +39,11 @@ const toggleGroupItem = defineComponent({ }, emits: toggleGroupItemProps.emits, setup(props, { slots, attrs }) { - const { - value, - disabled, - scopeOkuToggleGroup, - } = toRefs(props) const valueInject = useToggleGroupValueInject(TOGGLE_ITEM_NAME, props.scopeOkuToggleGroup) - const inject = useToggleGroupInject(TOGGLE_ITEM_NAME, scopeOkuToggleGroup.value) + const inject = useToggleGroupInject(TOGGLE_ITEM_NAME, props.scopeOkuToggleGroup) const rovingFocusGroupScope = useRovingFocusGroupScope(props.scopeOkuToggleGroup) - const pressed = computed(() => valueInject?.value?.value.includes(value.value!)) - const _disabled = computed(() => inject.disabled.value || disabled.value) + const pressed = computed(() => valueInject?.value.value.includes(props.value!)) + const disable = computed(() => inject.disabled.value || props.disabled) const forwardedRef = useForwardRef() const _ref = ref(null) @@ -53,21 +52,21 @@ const toggleGroupItem = defineComponent({ ? h(OkuRovingFocusGroupItem, { asChild: true, ...rovingFocusGroupScope, - focusable: !_disabled.value, - active: true, + focusable: !disable.value, + active: pressed.value, ref: _ref, }, { default: () => h(OkuToggleGroupItemImpl, { ...mergeProps(attrs, props), pressed: pressed.value, - disabled: _disabled.value, + disabled: disable.value, ref: forwardedRef, }, slots), }) : h(OkuToggleGroupItemImpl, { ...mergeProps(attrs, props), pressed: pressed.value, - disabled: _disabled.value, + disabled: disable.value, ref: forwardedRef, }, slots) }, diff --git a/packages/components/toggle-group/src/ToggleGroupItemImpl.ts b/packages/components/toggle-group/src/ToggleGroupItemImpl.ts index 899d543d1..ea5ee6b0f 100644 --- a/packages/components/toggle-group/src/ToggleGroupItemImpl.ts +++ b/packages/components/toggle-group/src/ToggleGroupItemImpl.ts @@ -1,5 +1,5 @@ import { primitiveProps, propsOmit } from '@oku-ui/primitive' -import { computed, defineComponent, h, toRefs } from 'vue' +import { computed, defineComponent, h, mergeProps, reactive, toRefs } from 'vue' import type { PropType } from 'vue' import { useForwardRef } from '@oku-ui/use-composable' import { OkuToggle, toggleProps } from '@oku-ui/toggle' @@ -36,46 +36,45 @@ export const toggleGroupItemImplProps = { ...primitiveProps, }, emits: { - ...propsOmit(toggleProps.emits, ['update:modelValue', 'pressedChange']), + ...propsOmit(toggleProps.emits, ['pressedChange']), }, } const toggleGroupItemImpl = defineComponent({ name: TOGGLE_GROUP_NAME, + components: { + OkuToggle, + }, inheritAttrs: false, props: { ...toggleGroupItemImplProps.props, ...scopeToggleGroupProps, }, emits: toggleGroupItemImplProps.emits, - setup(props, { slots, emit, attrs }) { - const { pressed, disabled, value, scopeOkuToggleGroup, asChild } = toRefs(props) + setup(props, { slots, attrs }) { + const { scopeOkuToggleGroup, value, ...itemProps } = toRefs(props) + const valueInject = useToggleGroupValueInject(TOGGLE_ITEM_NAME, scopeOkuToggleGroup.value) const singleProps = computed(() => { - return { 'role': 'radio', 'ariaChecked': pressed.value, 'aria-pressed': undefined } as ToggleElementNaviteElement + return { 'role': 'radio', 'ariaChecked': itemProps.pressed.value, 'aria-pressed': undefined } as ToggleElementNaviteElement }) - const typeProps = computed(() => valueInject.type.value === 'single' ? singleProps.value : undefined) + const typeProps = computed(() => valueInject.type === 'single' ? singleProps.value : undefined) const forwardedRef = useForwardRef() + const _itemProps = reactive(itemProps) + return () => + h(OkuToggle, { + ...typeProps.value, + ...mergeProps(attrs, _itemProps), + ref: forwardedRef, + onPressedChange: (pressed: boolean) => { + if (pressed) + valueInject.onItemActivate(value.value!) - return () => h(OkuToggle, { - ...attrs, - ...typeProps.value as any, - pressed: pressed.value, - disabled: disabled.value, - asChild: asChild.value, - ref: forwardedRef, - onClick: (e: ToggleEmits['click'][0]) => { - emit('click', e) - }, - onPressedChange: (pressed: boolean) => { - if (pressed) - valueInject.onItemActivate(value.value!) - - else - valueInject.onItemDeactivate(value.value!) - }, - }, slots) + else + valueInject.onItemDeactivate(value.value!) + }, + }, slots) }, }) diff --git a/packages/components/toggle-group/src/ToggleGroupVariant.ts b/packages/components/toggle-group/src/ToggleGroupVariant.ts deleted file mode 100644 index 6c8f128dc..000000000 --- a/packages/components/toggle-group/src/ToggleGroupVariant.ts +++ /dev/null @@ -1,157 +0,0 @@ -import { computed, defineComponent, h, toRefs, useModel } from 'vue' -import type { PropType } from 'vue' -import { useControllable, useForwardRef } from '@oku-ui/use-composable' - -import { scopeToggleGroupProps } from './utils' -import { OkuToggleGroupImpl, type ToggleGroupImplElement, type ToggleGroupImplNaviteElement, type ToggleGroupImplProps, toggleGroupImplProps } from './ToggleGroupImpl' -import { toggleGroupValueProvider } from './ToggleGroup' - -const TOGGLE_GROUP_NAME = 'OkuToggleGroupImplSingle' - -export type ToggleGroupVariantNaviteElement = ToggleGroupImplNaviteElement -export type ToggleGroupVariantElement = ToggleGroupImplElement - -export interface ToggleGroupVariantProps extends ToggleGroupImplProps { - type: 'single' | 'multiple' - /** - * The controlled stateful value of the item that is pressed. - */ - value?: string | string[] - /** - * The value of the item that is pressed when initially rendered. Use - * `defaultValue` if you do not need to control the state of a toggle group. - */ - defaultValue?: string | string[] -} - -export type ToggleGroupVariantEmits = { - 'update:modelValue': [value: string | string[]] - /** - * The callback that fires when the value of the toggle group changes. - */ - 'valueChange': [value: string | string[]] -} - -export const toggleGroupVariantProps = { - props: { - type: { - type: [String] as PropType<'single' | 'multiple'>, - required: true, - }, - modelValue: { - type: [String, Array] as PropType, - default: undefined, - required: false, - }, - value: { - type: [String, Array] as PropType, - default: undefined, - required: false, - }, - defaultValue: { - type: [String, Array] as PropType, - default: undefined, - }, - ...toggleGroupImplProps.props, - }, - emits: { - // eslint-disable-next-line unused-imports/no-unused-vars - 'update:modelValue': (value: string | string[]) => true, - // eslint-disable-next-line unused-imports/no-unused-vars - 'valueChange': (value: string | string[]) => true, - }, -} - -const toggleGroupVariant = defineComponent({ - name: TOGGLE_GROUP_NAME, - inheritAttrs: false, - props: { - ...toggleGroupVariantProps.props, - ...scopeToggleGroupProps, - }, - emits: toggleGroupVariantProps.emits, - setup(props, { slots, emit, attrs }) { - const { - value: valueProp, - defaultValue, - type, - dir, - disabled, - loop, - asChild, - orientation, - rovingFocus, - } = toRefs(props) - const forwardedRef = useForwardRef() - const modelValue = useModel(props, 'modelValue') - const proxyChecked = computed({ - get: () => modelValue.value !== undefined - ? modelValue.value - : valueProp.value !== undefined ? valueProp.value : undefined, - set: () => { - }, - }) - - const { state, updateValue } = useControllable({ - prop: computed(() => proxyChecked.value), - defaultProp: computed(() => defaultValue.value), - onChange: (result: any) => { - emit('update:modelValue', result) - emit('valueChange', result) - }, - }) - - const handleButtonActivate = (itemValue: string) => { - if (type.value === 'multiple') - updateValue([...state.value || [], itemValue]) - else - updateValue(itemValue) - } - - const handleButtonDeactivate = (itemValue: string) => { - if (type.value === 'multiple' && typeof state.value === 'object') - updateValue(state.value?.filter(value => value !== itemValue)) - else - updateValue('') - } - - toggleGroupValueProvider({ - scope: props.scopeOkuToggleGroup, - value: computed(() => { - if (type.value === 'single' && typeof state.value === 'string') - return state.value ? [state.value] : [] - else if (typeof state.value === 'object') - return state.value || [] - else - return [] - }), - onItemActivate: (value: string) => { - handleButtonActivate(value) - }, - onItemDeactivate: (value: string) => { - if (type.value === 'single') - handleButtonDeactivate('') - else if (type.value === 'multiple') - handleButtonDeactivate(value) - }, - type: computed(() => type.value || 'single'), - }) - - return () => h(OkuToggleGroupImpl, { - ...attrs, - dir: dir.value, - disabled: disabled.value, - loop: loop.value, - asChild: asChild.value, - orientation: orientation.value, - rovingFocus: rovingFocus.value, - scopeOkuToggleGroup: props.scopeOkuToggleGroup, - ref: forwardedRef, - }, slots) - }, -}) - -export const OkuToggleGroupVariant = toggleGroupVariant as typeof toggleGroupVariant & -(new () => { - $props: ToggleGroupVariantNaviteElement -}) diff --git a/packages/components/toggle-group/src/index.ts b/packages/components/toggle-group/src/index.ts index 26fd87016..79a0fecc4 100644 --- a/packages/components/toggle-group/src/index.ts +++ b/packages/components/toggle-group/src/index.ts @@ -2,12 +2,15 @@ export { OkuToggleGroup, createToggleGroupScope, toggleGroupProps, + toggleGroupVariantProps, } from './ToggleGroup' export type { ToggleGroupProps, ToggleGroupNaviteElement, ToggleGroupElement, + ToggleGroupVariantEmits, + ToggleGroupVariantProps, } from './ToggleGroup' export { @@ -20,15 +23,3 @@ export type { ToggleGroupItemNaviteElement, ToggleGroupItemProps, } from './ToggleGroupItem' - -export { - OkuToggleGroupVariant, - toggleGroupVariantProps, -} from './ToggleGroupVariant' - -export type { - ToggleGroupVariantElement, - ToggleGroupVariantEmits, - ToggleGroupVariantNaviteElement, - ToggleGroupVariantProps, -} from './ToggleGroupVariant' diff --git a/packages/components/toggle-group/src/stories/ToggleGroupDemo.vue b/packages/components/toggle-group/src/stories/ToggleGroupDemo.vue index 25c928579..34c455efc 100644 --- a/packages/components/toggle-group/src/stories/ToggleGroupDemo.vue +++ b/packages/components/toggle-group/src/stories/ToggleGroupDemo.vue @@ -12,13 +12,14 @@ export interface ITabsProps { defineProps() -const value = ref() +const testValue = ref() -