Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow users customize ID generation #2959

Merged
merged 4 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions packages/@headlessui-vue/src/components/combobox/combobox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -779,9 +779,10 @@ export let ComboboxLabel = defineComponent({
name: 'ComboboxLabel',
props: {
as: { type: [Object, String], default: 'label' },
id: { type: String, default: () => `headlessui-combobox-label-${useId()}` },
id: { type: String, default: null },
},
setup(props, { attrs, slots }) {
let id = props.id ?? `headlessui-combobox-label-${useId()}`
thecrypticace marked this conversation as resolved.
Show resolved Hide resolved
let api = useComboboxContext('ComboboxLabel')

function handleClick() {
Expand All @@ -794,7 +795,7 @@ export let ComboboxLabel = defineComponent({
disabled: api.disabled.value,
}

let { id, ...theirProps } = props
let { ...theirProps } = props
let ourProps = { id, ref: api.labelRef, onClick: handleClick }

return render({
Expand All @@ -815,9 +816,10 @@ export let ComboboxButton = defineComponent({
name: 'ComboboxButton',
props: {
as: { type: [Object, String], default: 'button' },
id: { type: String, default: () => `headlessui-combobox-button-${useId()}` },
id: { type: String, default: null },
},
setup(props, { attrs, slots, expose }) {
let id = props.id ?? `headlessui-combobox-button-${useId()}`
let api = useComboboxContext('ComboboxButton')

expose({ el: api.buttonRef, $el: api.buttonRef })
Expand Down Expand Up @@ -884,7 +886,7 @@ export let ComboboxButton = defineComponent({
disabled: api.disabled.value,
value: api.value.value,
}
let { id, ...theirProps } = props
let { ...theirProps } = props
let ourProps = {
ref: api.buttonRef,
id,
Expand Down Expand Up @@ -921,12 +923,13 @@ export let ComboboxInput = defineComponent({
unmount: { type: Boolean, default: true },
displayValue: { type: Function as PropType<(item: unknown) => string> },
defaultValue: { type: String, default: undefined },
id: { type: String, default: () => `headlessui-combobox-input-${useId()}` },
id: { type: String, default: null },
},
emits: {
change: (_value: Event & { target: HTMLInputElement }) => true,
},
setup(props, { emit, attrs, slots, expose }) {
let id = props.id ?? `headlessui-combobox-input-${useId()}`
let api = useComboboxContext('ComboboxInput')
let ownerDocument = computed(() => getOwnerDocument(dom(api.inputRef)))

Expand Down Expand Up @@ -1284,7 +1287,7 @@ export let ComboboxInput = defineComponent({

return () => {
let slot = { open: api.comboboxState.value === ComboboxStates.Open }
let { id, displayValue, onChange: _onChange, ...theirProps } = props
let { displayValue, onChange: _onChange, ...theirProps } = props
let ourProps = {
'aria-controls': api.optionsRef.value?.id,
'aria-expanded': api.comboboxState.value === ComboboxStates.Open,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,18 @@ export let Description = defineComponent({
name: 'Description',
props: {
as: { type: [Object, String], default: 'p' },
id: { type: String, default: () => `headlessui-description-${useId()}` },
id: { type: String, default: null },
},
setup(myProps, { attrs, slots }) {
let id = myProps.id ?? `headlessui-description-${useId()}`
let context = useDescriptionContext()

onMounted(() => onUnmounted(context.register(myProps.id)))
onMounted(() => onUnmounted(context.register(id)))

return () => {
let { name = 'Description', slot = ref({}), props = {} } = context
let { id, ...theirProps } = myProps
let { id: _id, ...theirProps } = myProps

let ourProps = {
...Object.entries(props).reduce(
(acc, [key, value]) => Object.assign(acc, { [key]: unref(value) }),
Expand Down
27 changes: 16 additions & 11 deletions packages/@headlessui-vue/src/components/dialog/dialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,12 @@ export let Dialog = defineComponent({
unmount: { type: Boolean, default: true },
open: { type: [Boolean, String], default: Missing },
initialFocus: { type: Object as PropType<HTMLElement | null>, default: null },
id: { type: String, default: () => `headlessui-dialog-${useId()}` },
id: { type: String, default: null },
role: { type: String as PropType<'dialog' | 'alertdialog'>, default: 'dialog' },
},
emits: { close: (_close: boolean) => true },
setup(props, { emit, attrs, slots, expose }) {
let id = props.id ?? `headlessui-dialog-${useId()}`
let ready = ref(false)
onMounted(() => {
ready.value = true
Expand Down Expand Up @@ -292,7 +293,7 @@ export let Dialog = defineComponent({
})

return () => {
let { id, open: _, initialFocus, ...theirProps } = props
let { open: _, initialFocus, ...theirProps } = props
let ourProps = {
// Manually passthrough the attributes, because Vue can't automatically pass
// it to the underlying div because of all the wrapper components below.
Expand Down Expand Up @@ -352,9 +353,10 @@ export let DialogOverlay = defineComponent({
name: 'DialogOverlay',
props: {
as: { type: [Object, String], default: 'div' },
id: { type: String, default: () => `headlessui-dialog-overlay-${useId()}` },
id: { type: String, default: null },
},
setup(props, { attrs, slots }) {
let id = props.id ?? `headlessui-dialog-overlay-${useId()}`
let api = useDialogContext('DialogOverlay')

function handleClick(event: MouseEvent) {
Expand All @@ -365,7 +367,7 @@ export let DialogOverlay = defineComponent({
}

return () => {
let { id, ...theirProps } = props
let { ...theirProps } = props
let ourProps = {
id,
'aria-hidden': true,
Expand All @@ -390,10 +392,11 @@ export let DialogBackdrop = defineComponent({
name: 'DialogBackdrop',
props: {
as: { type: [Object, String], default: 'div' },
id: { type: String, default: () => `headlessui-dialog-backdrop-${useId()}` },
id: { type: String, default: null },
},
inheritAttrs: false,
setup(props, { attrs, slots, expose }) {
let id = props.id ?? `headlessui-dialog-backdrop-${useId()}`
let api = useDialogContext('DialogBackdrop')
let internalBackdropRef = ref(null)

Expand All @@ -408,7 +411,7 @@ export let DialogBackdrop = defineComponent({
})

return () => {
let { id, ...theirProps } = props
let { ...theirProps } = props
let ourProps = {
id,
ref: internalBackdropRef,
Expand Down Expand Up @@ -437,9 +440,10 @@ export let DialogPanel = defineComponent({
name: 'DialogPanel',
props: {
as: { type: [Object, String], default: 'div' },
id: { type: String, default: () => `headlessui-dialog-panel-${useId()}` },
id: { type: String, default: null },
},
setup(props, { attrs, slots, expose }) {
let id = props.id ?? `headlessui-dialog-panel-${useId()}`
let api = useDialogContext('DialogPanel')

expose({ el: api.panelRef, $el: api.panelRef })
Expand All @@ -449,7 +453,7 @@ export let DialogPanel = defineComponent({
}

return () => {
let { id, ...theirProps } = props
let { ...theirProps } = props
let ourProps = {
id,
ref: api.panelRef,
Expand All @@ -474,18 +478,19 @@ export let DialogTitle = defineComponent({
name: 'DialogTitle',
props: {
as: { type: [Object, String], default: 'h2' },
id: { type: String, default: () => `headlessui-dialog-title-${useId()}` },
id: { type: String, default: null },
},
setup(props, { attrs, slots }) {
let id = props.id ?? `headlessui-dialog-title-${useId()}`
let api = useDialogContext('DialogTitle')

onMounted(() => {
api.setTitleId(props.id)
api.setTitleId(id)
onUnmounted(() => api.setTitleId(null))
})

return () => {
let { id, ...theirProps } = props
let { ...theirProps } = props
let ourProps = { id }

return render({
Expand Down
7 changes: 4 additions & 3 deletions packages/@headlessui-vue/src/components/label/label.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,17 @@ export let Label = defineComponent({
props: {
as: { type: [Object, String], default: 'label' },
passive: { type: [Boolean], default: false },
id: { type: String, default: () => `headlessui-label-${useId()}` },
id: { type: String, default: null },
},
setup(myProps, { slots, attrs }) {
let id = myProps.id ?? `headlessui-label-${useId()}`
let context = useLabelContext()

onMounted(() => onUnmounted(context.register(myProps.id)))
onMounted(() => onUnmounted(context.register(id)))

return () => {
let { name = 'Label', slot = {}, props = {} } = context
let { id, passive, ...theirProps } = myProps
let { id: _id, passive, ...theirProps } = myProps
let ourProps = {
...Object.entries(props).reduce(
(acc, [key, value]) => Object.assign(acc, { [key]: unref(value) }),
Expand Down
36 changes: 20 additions & 16 deletions packages/@headlessui-vue/src/components/listbox/listbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -426,9 +426,10 @@ export let ListboxLabel = defineComponent({
name: 'ListboxLabel',
props: {
as: { type: [Object, String], default: 'label' },
id: { type: String, default: () => `headlessui-listbox-label-${useId()}` },
id: { type: String, default: null },
},
setup(props, { attrs, slots }) {
let id = props.id ?? `headlessui-listbox-label-${useId()}`
let api = useListboxContext('ListboxLabel')

function handleClick() {
Expand All @@ -440,7 +441,7 @@ export let ListboxLabel = defineComponent({
open: api.listboxState.value === ListboxStates.Open,
disabled: api.disabled.value,
}
let { id, ...theirProps } = props
let { ...theirProps } = props
let ourProps = { id, ref: api.labelRef, onClick: handleClick }

return render({
Expand All @@ -461,9 +462,10 @@ export let ListboxButton = defineComponent({
name: 'ListboxButton',
props: {
as: { type: [Object, String], default: 'button' },
id: { type: String, default: () => `headlessui-listbox-button-${useId()}` },
id: { type: String, default: null },
},
setup(props, { attrs, slots, expose }) {
let id = props.id ?? `headlessui-listbox-button-${useId()}`
let api = useListboxContext('ListboxButton')

expose({ el: api.buttonRef, $el: api.buttonRef })
Expand Down Expand Up @@ -529,7 +531,7 @@ export let ListboxButton = defineComponent({
value: api.value.value,
}

let { id, ...theirProps } = props
let { ...theirProps } = props
let ourProps = {
ref: api.buttonRef,
id,
Expand Down Expand Up @@ -564,9 +566,10 @@ export let ListboxOptions = defineComponent({
as: { type: [Object, String], default: 'ul' },
static: { type: Boolean, default: false },
unmount: { type: Boolean, default: true },
id: { type: String, default: () => `headlessui-listbox-options-${useId()}` },
id: { type: String, default: null },
},
setup(props, { attrs, slots, expose }) {
let id = props.id ?? `headlessui-listbox-options-${useId()}`
let api = useListboxContext('ListboxOptions')
let searchDebounce = ref<ReturnType<typeof setTimeout> | null>(null)

Expand Down Expand Up @@ -656,7 +659,7 @@ export let ListboxOptions = defineComponent({

return () => {
let slot = { open: api.listboxState.value === ListboxStates.Open }
let { id, ...theirProps } = props
let { ...theirProps } = props
let ourProps = {
'aria-activedescendant':
api.activeOptionIndex.value === null
Expand Down Expand Up @@ -696,17 +699,18 @@ export let ListboxOption = defineComponent({
>,
},
disabled: { type: Boolean, default: false },
id: { type: String, default: () => `headlessui-listbox.option-${useId()}` },
id: { type: String, default: null },
},
setup(props, { slots, attrs, expose }) {
let id = props.id ?? `headlessui-listbox-option-${useId()}`
let api = useListboxContext('ListboxOption')
let internalOptionRef = ref<HTMLElement | null>(null)

expose({ el: internalOptionRef, $el: internalOptionRef })

let active = computed(() => {
return api.activeOptionIndex.value !== null
? api.options.value[api.activeOptionIndex.value].id === props.id
? api.options.value[api.activeOptionIndex.value].id === id
: false
})

Expand All @@ -727,7 +731,7 @@ export let ListboxOption = defineComponent({
return (
api.options.value.find((option) =>
currentValues.some((value) => api.compare(toRaw(value), toRaw(option.dataRef.value)))
)?.id === props.id
)?.id === id
)
},
[ValueMode.Single]: () => selected.value,
Expand All @@ -744,8 +748,8 @@ export let ListboxOption = defineComponent({
domRef: internalOptionRef,
}))

onMounted(() => api.registerOption(props.id, dataRef))
onUnmounted(() => api.unregisterOption(props.id))
onMounted(() => api.registerOption(id, dataRef))
onUnmounted(() => api.unregisterOption(id))

onMounted(() => {
watch(
Expand All @@ -756,10 +760,10 @@ export let ListboxOption = defineComponent({

match(api.mode.value, {
[ValueMode.Multi]: () => {
if (isFirstSelected.value) api.goToOption(Focus.Specific, props.id)
if (isFirstSelected.value) api.goToOption(Focus.Specific, id)
},
[ValueMode.Single]: () => {
api.goToOption(Focus.Specific, props.id)
api.goToOption(Focus.Specific, id)
},
})
},
Expand All @@ -785,7 +789,7 @@ export let ListboxOption = defineComponent({

function handleFocus() {
if (props.disabled) return api.goToOption(Focus.Nothing)
api.goToOption(Focus.Specific, props.id)
api.goToOption(Focus.Specific, id)
}

let pointer = useTrackedPointer()
Expand All @@ -798,7 +802,7 @@ export let ListboxOption = defineComponent({
if (!pointer.wasMoved(evt)) return
if (props.disabled) return
if (active.value) return
api.goToOption(Focus.Specific, props.id, ActivationTrigger.Pointer)
api.goToOption(Focus.Specific, id, ActivationTrigger.Pointer)
}

function handleLeave(evt: PointerEvent) {
Expand All @@ -811,7 +815,7 @@ export let ListboxOption = defineComponent({
return () => {
let { disabled } = props
let slot = { active: active.value, selected: selected.value, disabled }
let { id, value: _value, disabled: _disabled, ...theirProps } = props
let { value: _value, disabled: _disabled, ...theirProps } = props
let ourProps = {
id,
ref: internalOptionRef,
Expand Down
Loading
Loading