Skip to content

Commit

Permalink
fix(toogle-group): tabindex (#345)
Browse files Browse the repository at this point in the history
* fix(toogle-group): tabindex

* chore: lint fix

* chore: fix controllable
  • Loading branch information
productdevbook authored Sep 9, 2023
1 parent 807042a commit 15a6d44
Show file tree
Hide file tree
Showing 16 changed files with 435 additions and 297 deletions.
13 changes: 9 additions & 4 deletions packages/components/checkbox/src/checkbox.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createProvideScope } from '@oku-ui/provide'
import type { PropType, Ref } from 'vue'
import { computed, defineComponent, h, ref, toRefs, useModel, watchEffect } from 'vue'
import { computed, defineComponent, h, mergeProps, reactive, ref, toRefs, useModel, watchEffect } from 'vue'

import { composeEventHandlers } from '@oku-ui/utils'
import { useComposedRefs, useControllable, useForwardRef } from '@oku-ui/use-composable'
Expand Down Expand Up @@ -103,12 +103,15 @@ const Checkbox = defineComponent({
emits: checkboxProps.emits,
setup(props, { attrs, slots, emit }) {
const {
modelValue: _modelValue,
scopeOkuCheckbox,
checked: checkedProp,
defaultChecked,
required,
disabled,
name,
value,
...checkboxProps
} = toRefs(props)

const buttonRef = ref<HTMLButtonElement | null>(null)
Expand Down Expand Up @@ -154,23 +157,25 @@ const Checkbox = defineComponent({
})

CheckboxProvider({
scope: props.scopeOkuCheckbox,
scope: scopeOkuCheckbox.value,
state,
disabled,
})

const reactiveCheckboxProps = reactive(checkboxProps)

const originalReturn = () =>
[h(Primitive.button, {
'type': 'button',
'role': 'checkbox',
'aria-checked': isIndeterminate(state.value) ? 'mixed' : state.value as any,
'aria-checked': computed(() => isIndeterminate(state.value) ? 'mixed' : state.value).value as any,
'aria-required': required.value,
'data-state': computed(() => getState(state.value)).value,
'data-disabled': disabled.value ? '' : undefined,
'disabled': disabled.value,
'value': value.value,
'asChild': props.asChild,
...checkboxAttrs,
...mergeProps(checkboxAttrs, reactiveCheckboxProps),
'ref': composedRefs,
'onKeyDown': composeEventHandlers<KeyboardEvent>((e) => {
emit('keydown', e)
Expand Down
20 changes: 2 additions & 18 deletions packages/components/roving-focus/src/RovingFocusGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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, {
Expand All @@ -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),
}),
Expand Down
31 changes: 16 additions & 15 deletions packages/components/roving-focus/src/RovingFocusGroupImpl.ts
Original file line number Diff line number Diff line change
@@ -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'

Expand Down Expand Up @@ -60,17 +60,14 @@ const RovingFocusGroupImpl = defineComponent({
},
emits: rovingFocusGroupImplProps.emits,
setup(props, { attrs, slots, emit }) {
const _attrs = attrs as Omit<RovingFocusGroupImplNaviteElement, 'dir'>
const {
scopeOkuRovingFocusGroup,
orientation,
loop,
dir,
currentTabStopId: currentTabStopIdProp,
defaultCurrentTabStopId,
onEntryFocus,
asChild,
scopeOkuRovingFocusGroup,
dir,
...propsData
...groupProps
} = toRefs(props)

const buttonRef = ref<HTMLDivElement | null>(null)
Expand All @@ -87,16 +84,19 @@ 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)

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)
})
}
})

Expand All @@ -105,7 +105,7 @@ const RovingFocusGroupImpl = defineComponent({
orientation,
dir,
loop,
currentTabStopId: currentTabStopId || null,
currentTabStopId,
onItemFocus: (tabStopId: string) => {
updateCurrentTabStopId(tabStopId)
},
Expand All @@ -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<MouseEvent>((e) => {
emit('mousedown', e)
}, () => {
Expand Down
13 changes: 5 additions & 8 deletions packages/components/roving-focus/src/RovingFocusGroupItem.ts
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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)
Expand All @@ -91,7 +89,7 @@ const rovingFocusGroupItem = defineComponent({
})
})
})

const reactiveItemProps = reactive(itemProps)
return () => {
return h(CollectionItemSlot, {
id: id.value,
Expand All @@ -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<MouseEvent>((e) => {
emit('mousedown', e)
Expand Down
86 changes: 76 additions & 10 deletions packages/components/toggle-group/src/ToggleGroup.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
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'
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'

Expand All @@ -19,7 +20,7 @@ export const [createToggleGroupProvide, createToggleGroupScope] = createProvideS
])

type ToggleGroupValueProvide = {
type: Ref<'single' | 'multiple'>
type: 'single' | 'multiple'
value: Ref<string[]>
onItemActivate(value: string): void
onItemDeactivate(value: string): void
Expand All @@ -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<string | string[] | undefined>,
default: undefined,
},
value: {
type: [String, Array] as PropType<string | string[] | undefined>,
default: undefined,
},
defaultValue: {
type: [String, Array] as PropType<string | string[] | undefined>,
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,
Expand All @@ -55,6 +102,10 @@ export const toggleGroupProps = {

const toggleGroup = defineComponent({
name: TOGGLE_GROUP_NAME,
components: {
OkuToggleGroupImplSingle,
OkuToggleGroupImplMultiple,
},
inheritAttrs: false,
props: {
...toggleGroupProps.props,
Expand All @@ -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}\``)
}
},
})
Expand Down
Loading

0 comments on commit 15a6d44

Please sign in to comment.