Skip to content

Commit

Permalink
fix(vue): add id props and improve state exposition
Browse files Browse the repository at this point in the history
BREAKING CHANGE: Update exposed context types and remove unnecessary expose calls

Signed-off-by: Shyrro <zsahmane@gmail.com>
  • Loading branch information
Shyrro committed May 8, 2023
1 parent cf784a8 commit 53e7c13
Show file tree
Hide file tree
Showing 59 changed files with 825 additions and 758 deletions.
19 changes: 12 additions & 7 deletions packages/vue/src/accordion/accordion.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import { type Context } from '@zag-js/accordion'
import { defineComponent, type PropType } from 'vue'
import { ark, type HTMLArkProps } from '../factory'
import { type Assign } from '../types'
import { type ComponentWithProps } from '../utils'
import { createVueProps, type ComponentWithProps } from '../utils'
import { AccordionProvider } from './accordion-context'
import { useAccordion, type UseAccordionContext } from './use-accordion'
import { useAccordion } from './use-accordion'

export type AccordionProps = Assign<HTMLArkProps<'div'>, UseAccordionContext>
export type AccordionContext = Context & { modelValue?: AccordionContext['value'] }
export type AccordionProps = Assign<HTMLArkProps<'div'>, AccordionContext>

const VueAccordionProps = {
const VueAccordionProps = createVueProps<AccordionProps>({
id: {
type: String as PropType<AccordionProps['id']>,
},
modelValue: {
type: [String, Object] as PropType<AccordionProps['modelValue']>,
},
Expand All @@ -32,9 +37,9 @@ const VueAccordionProps = {
orientation: {
type: String as PropType<AccordionProps['orientation']>,
},
}
})

export const Accordion: ComponentWithProps<AccordionProps> = defineComponent({
export const Accordion: ComponentWithProps<Partial<AccordionProps>> = defineComponent({
name: 'Accordion',
emits: ['change', 'update:modelValue'],
props: VueAccordionProps,
Expand All @@ -45,7 +50,7 @@ export const Accordion: ComponentWithProps<AccordionProps> = defineComponent({

return () => (
<ark.div {...api.value.rootProps} {...attrs}>
{slots?.default?.()}
{slots?.default?.(api.value)}
</ark.div>
)
},
Expand Down
18 changes: 8 additions & 10 deletions packages/vue/src/accordion/use-accordion.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { connect, machine, type Context as AccordionContext } from '@zag-js/accordion'
import { connect, machine } from '@zag-js/accordion'
import { normalizeProps, useMachine } from '@zag-js/vue'
import { computed, reactive, watch } from 'vue'
import { computed, reactive, watch, type ExtractPropTypes } from 'vue'
import { useId } from '../utils'
import type { AccordionContext } from './accordion'

export interface UseAccordionContext extends Omit<AccordionContext, 'id'> {
modelValue?: AccordionContext['value']
}

export const useAccordion = (emit: CallableFunction, context: UseAccordionContext) => {
export const useAccordion = <T extends ExtractPropTypes<AccordionContext>>(
emit: CallableFunction,
context: T,
) => {
const reactiveContext = reactive(context)

const [state, send] = useMachine(
machine({
...reactiveContext,
value: reactiveContext.modelValue ?? reactiveContext.value,
id: useId().value,
id: reactiveContext.id || useId().value,
onChange: (details) => {
emit('change', details.value)
emit(
Expand All @@ -37,5 +37,3 @@ export const useAccordion = (emit: CallableFunction, context: UseAccordionContex

return { api }
}

export type UseAccordionReturn = ReturnType<typeof useAccordion>
21 changes: 14 additions & 7 deletions packages/vue/src/checkbox/checkbox.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import { type Context } from '@zag-js/checkbox'
import { defineComponent, type PropType } from 'vue'
import { ark, type HTMLArkProps } from '../factory'
import { type Assign } from '../types'
import { type ComponentWithProps } from '../utils'
import { createVueProps, type ComponentWithProps } from '../utils'
import { CheckboxProvider } from './checkbox-context'
import { useCheckbox, type UseCheckboxContext } from './use-checkbox'
import { useCheckbox } from './use-checkbox'

export type CheckboxProps = Assign<HTMLArkProps<'label'>, UseCheckboxContext>
export type CheckboxContext = Context & {
modelValue?: Context['checked']
}
export type CheckboxProps = Assign<HTMLArkProps<'label'>, CheckboxContext>

const VueCheckboxProps = {
export const VueCheckboxProps = createVueProps<CheckboxProps>({
id: {
type: String as PropType<CheckboxProps['id']>,
},
'aria-describedby': {
type: String as PropType<CheckboxProps['aria-describedby']>,
},
Expand Down Expand Up @@ -46,7 +53,7 @@ const VueCheckboxProps = {
type: Boolean as PropType<CheckboxProps['invalid']>,
},
modelValue: {
type: Boolean as PropType<CheckboxProps['modelValue']>,
type: [Boolean, String] as PropType<CheckboxProps['modelValue']>,
default: undefined,
},
name: {
Expand All @@ -61,9 +68,9 @@ const VueCheckboxProps = {
value: {
type: String as PropType<CheckboxProps['value']>,
},
}
})

export const Checkbox: ComponentWithProps<CheckboxProps> = defineComponent({
export const Checkbox: ComponentWithProps<Partial<CheckboxProps>> = defineComponent({
name: 'Checkbox',
emits: ['change', 'update:modelValue'],
props: VueCheckboxProps,
Expand Down
4 changes: 3 additions & 1 deletion packages/vue/src/checkbox/stories/basic.stories.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { ref } from 'vue'
import { Checkbox, CheckboxControl, CheckboxInput, CheckboxLabel } from '..'
import '../checkbox.css'
const checkboxRef = ref(true)
const props = defineProps(['modelValue'])
const checkboxRef = ref(props.modelValue || true)
</script>
<template>
<Checkbox v-model="checkboxRef">
Expand Down
20 changes: 9 additions & 11 deletions packages/vue/src/checkbox/use-checkbox.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { connect, machine, type Context as CheckboxContext } from '@zag-js/checkbox'
import { connect, machine } from '@zag-js/checkbox'
import { normalizeProps, useMachine } from '@zag-js/vue'
import { computed, reactive, watch } from 'vue'
import { computed, reactive, watch, type ExtractPropTypes } from 'vue'
import { useId } from '../utils'
import type { CheckboxContext } from './checkbox'

export interface UseCheckboxContext extends Omit<CheckboxContext, 'id'> {
modelValue?: CheckboxContext['checked']
}

export const useCheckbox = (emit: CallableFunction, context: UseCheckboxContext) => {
export const useCheckbox = <T extends ExtractPropTypes<CheckboxContext>>(
emit: CallableFunction,
context: T,
) => {
const reactiveContext = reactive(context)

const [state, send] = useMachine(
machine({
...reactiveContext,
id: useId().value,
id: reactiveContext.id || useId().value,
checked: reactiveContext.modelValue ?? reactiveContext.checked,
onChange(details) {
emit('change', details.checked)
Expand All @@ -30,12 +30,10 @@ export const useCheckbox = (emit: CallableFunction, context: UseCheckboxContext)
if (value == undefined) return

if (value !== api.value.isChecked) {
api.value.setChecked(value)
api.value.setChecked(value as boolean)
}
},
)

return api
}

export type UseCheckboxReturn = ReturnType<typeof useCheckbox>
2 changes: 1 addition & 1 deletion packages/vue/src/color-picker/color-picker.stories.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const colorPickerValue = ref('hsl(10, 81%, 59%)')
<ColorPickerChannelInput :channel="lightness" />

<ColorPickerChannelInput channel="alpha" />
<ColorPickerChannelInput channel="hex" />
<ColorPickerChannelInput channel="hue" />

<ColorPickerSwatchGroup>
<ColorPickerSwatch value="#123123" />
Expand Down
64 changes: 36 additions & 28 deletions packages/vue/src/color-picker/color-picker.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,49 @@
import { type Context } from '@zag-js/color-picker'
import { defineComponent, type PropType } from 'vue'
import type { ComponentWithProps } from '../utils'
import type { HTMLArkProps } from '../factory'
import type { Assign } from '../types'
import { createVueProps, type ComponentWithProps } from '../utils'
import { ColorPickerProvider } from './color-picker-context'
import { useColorPicker, type UseColorPickerContext } from './use-color-picker'
import { useColorPicker } from './use-color-picker'

export type ColorPickerProps = UseColorPickerContext
export type ColorPickerContext = Context & {
modelValue?: Context['value']
}
export type ColorPickerProps = Assign<HTMLArkProps<'div'>, ColorPickerContext>

export const ColorPicker: ComponentWithProps<ColorPickerProps> = defineComponent({
name: 'ColorPicker',
props: {
dir: {
type: String as PropType<ColorPickerProps['dir']>,
},
id: {
type: String as PropType<ColorPickerProps['id']>,
},
getRootNode: {
type: Function as PropType<ColorPickerProps['getRootNode']>,
},
modelValue: {
type: String as PropType<ColorPickerProps['modelValue']>,
},
value: {
type: String as PropType<ColorPickerProps['value']>,
},
disabled: {
type: Boolean as PropType<ColorPickerProps['disabled']>,
},
readOnly: {
type: Boolean as PropType<ColorPickerProps['readOnly']>,
},
const VueColorPickerProps = createVueProps<ColorPickerProps>({
dir: {
type: String as PropType<ColorPickerProps['dir']>,
},
id: {
type: String as PropType<ColorPickerProps['id']>,
},
getRootNode: {
type: Function as PropType<ColorPickerProps['getRootNode']>,
},
modelValue: {
type: String as PropType<ColorPickerProps['modelValue']>,
},
value: {
type: String as PropType<ColorPickerProps['value']>,
},
disabled: {
type: Boolean as PropType<ColorPickerProps['disabled']>,
},
readOnly: {
type: Boolean as PropType<ColorPickerProps['readOnly']>,
},
})

export const ColorPicker: ComponentWithProps<Partial<ColorPickerContext>> = defineComponent({
name: 'ColorPicker',
props: VueColorPickerProps,
emits: ['change', 'change-end', 'update:modelValue'],
setup(props, { slots, emit }) {
const api = useColorPicker(emit, props)

ColorPickerProvider(api)

return () => slots.default?.({ ...api.value })
return () => slots.default?.(api.value)
},
})
17 changes: 7 additions & 10 deletions packages/vue/src/color-picker/use-color-picker.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import * as colorPicker from '@zag-js/color-picker'
import { normalizeProps, useMachine } from '@zag-js/vue'
import { computed, reactive } from 'vue'
import type { Optional } from '../types'
import { computed, reactive, type ExtractPropTypes } from 'vue'
import { useId } from '../utils'
import type { ColorPickerContext } from './color-picker'

export interface UseColorPickerContext extends Optional<colorPicker.Context, 'id'> {
modelValue?: colorPicker.Context['value']
}

export const useColorPicker = (emit: CallableFunction, context: UseColorPickerContext) => {
export const useColorPicker = <T extends ExtractPropTypes<ColorPickerContext>>(
emit: CallableFunction,
context: T,
) => {
const reactiveContext = reactive(context)

const [state, send] = useMachine(
colorPicker.machine({
...reactiveContext,
id: useId().value,
id: reactiveContext.id || useId().value,
value: reactiveContext.modelValue ?? reactiveContext.value,
onChange(details) {
emit('change', details)
Expand All @@ -28,5 +27,3 @@ export const useColorPicker = (emit: CallableFunction, context: UseColorPickerCo

return computed(() => colorPicker.connect(state.value, send, normalizeProps))
}

export type UseColorPickerReturn = ReturnType<typeof useColorPicker>
7 changes: 2 additions & 5 deletions packages/vue/src/combobox/combobox.stories.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
ComboboxTrigger,
} from './'
import './combobox.css'
import type { UseComboboxReturn } from './use-combobox'
type ComboboxData = Pick<ComboboxOptionProps, 'label' | 'value' | 'disabled'>[]
Expand All @@ -26,8 +25,6 @@ const comboboxData: ComboboxData = [
const options = ref(comboboxData)
const comboboxRef = ref<UseComboboxReturn | null>(null)
const handleInputChange: ComboboxProps['onInputChange'] = ({ value }) => {
const filtered = comboboxData.filter((item) =>
item.label.toLowerCase().includes(value.toLowerCase()),
Expand All @@ -45,10 +42,10 @@ const defaultVal = ref(comboboxData[0].label)
</script>
<template>
<Combobox
ref="comboboxRef"
@input-change="handleInputChange"
@select="handleOnSelect"
v-model="defaultVal"
v-slot="{ isInputValueEmpty, isOpen }"
>
<ComboboxLabel>JS Frameworks</ComboboxLabel>
<ComboboxControl>
Expand All @@ -57,7 +54,7 @@ const defaultVal = ref(comboboxData[0].label)
<button>▼</button>
</ComboboxTrigger>
</ComboboxControl>
<div v-show="comboboxRef?.isInputValueEmpty && !comboboxRef?.isOpen">
<div v-show="isInputValueEmpty && !isOpen">
Give me you favorite framework!
</div>
<Teleport to="body">
Expand Down
25 changes: 15 additions & 10 deletions packages/vue/src/combobox/combobox.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import { type Context } from '@zag-js/combobox'
import { defineComponent, type PropType } from 'vue'
import { ark, type HTMLArkProps } from '../factory'
import { type Assign } from '../types'
import { type ComponentWithProps } from '../utils'
import { createVueProps, type ComponentWithProps } from '../utils'
import { ComboboxProvider } from './combobox-context'
import { useCombobox, type UseComboboxContext } from './use-combobox'
import { useCombobox } from './use-combobox'

export type ComboboxProps = Assign<HTMLArkProps<'div'>, UseComboboxContext>
export type ComboboxContext = Context & {
modelValue?: ComboboxContext['inputValue']
}
export type ComboboxProps = Assign<HTMLArkProps<any>, ComboboxContext>

const VueComboboxProps = {
const VueComboboxProps = createVueProps<ComboboxProps>({
id: {
type: String as PropType<ComboboxProps['id']>,
},
modelValue: {
type: String as PropType<ComboboxProps['modelValue']>,
},
Expand Down Expand Up @@ -86,22 +93,20 @@ const VueComboboxProps = {
translations: {
type: Object as PropType<ComboboxProps['translations']>,
},
}
})

export const Combobox: ComponentWithProps<ComboboxProps> = defineComponent({
export const Combobox: ComponentWithProps<Partial<ComboboxProps>> = defineComponent({
name: 'Combobox',
props: VueComboboxProps,
emits: ['close', 'open', 'highlight', 'input-change', 'update:modelValue', 'select'],
setup(props, { slots, attrs, emit, expose }) {
setup(props, { slots, attrs, emit }) {
const api = useCombobox(emit, props)

ComboboxProvider(api)

expose({ ...api.value })

return () => (
<ark.div {...api.value.rootProps} {...attrs}>
{() => slots.default?.()}
{() => slots.default?.(api.value)}
</ark.div>
)
},
Expand Down

0 comments on commit 53e7c13

Please sign in to comment.