@headlessui/vue
Advanced tools
Comparing version 0.3.0-e73d09b to 0.3.0-e8e2770
@@ -209,11 +209,47 @@ 'use strict'; | ||
var id = 0; | ||
var Alert = /*#__PURE__*/vue.defineComponent({ | ||
name: 'Alert', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'div' | ||
}, | ||
importance: { | ||
type: String, | ||
"default": 'polite' | ||
} | ||
}, | ||
setup: function setup(props, _ref) { | ||
var slots = _ref.slots, | ||
attrs = _ref.attrs; | ||
function generateId() { | ||
return ++id; | ||
} | ||
var _props$importance = props.importance, | ||
importance = _props$importance === void 0 ? 'polite' : _props$importance, | ||
passThroughProps = _objectWithoutPropertiesLoose(props, ["importance"]); | ||
function useId() { | ||
return generateId(); | ||
} | ||
var slot = { | ||
importance: importance | ||
}; | ||
var propsWeControl = match(importance, { | ||
polite: function polite() { | ||
return { | ||
role: 'status' | ||
}; | ||
}, | ||
assertive: function assertive() { | ||
return { | ||
role: 'alert' | ||
}; | ||
} | ||
}); | ||
return function () { | ||
return render({ | ||
props: _extends({}, passThroughProps, propsWeControl), | ||
slot: slot, | ||
slots: slots, | ||
attrs: attrs | ||
}); | ||
}; | ||
} | ||
}); | ||
@@ -238,2 +274,189 @@ // TODO: This must already exist somewhere, right? 🤔 | ||
var id = 0; | ||
function generateId() { | ||
return ++id; | ||
} | ||
function useId() { | ||
return generateId(); | ||
} | ||
function dom(ref) { | ||
var _ref$value$$el; | ||
if (ref == null) return null; | ||
if (ref.value == null) return null; | ||
return (_ref$value$$el = ref.value.$el) != null ? _ref$value$$el : ref.value; | ||
} | ||
var DisclosureStates; | ||
(function (DisclosureStates) { | ||
DisclosureStates[DisclosureStates["Open"] = 0] = "Open"; | ||
DisclosureStates[DisclosureStates["Closed"] = 1] = "Closed"; | ||
})(DisclosureStates || (DisclosureStates = {})); | ||
var DisclosureContext = /*#__PURE__*/Symbol('DisclosureContext'); | ||
function useDisclosureContext(component) { | ||
var context = vue.inject(DisclosureContext, null); | ||
if (context === null) { | ||
var err = new Error("<" + component + " /> is missing a parent <Disclosure /> component."); | ||
if (Error.captureStackTrace) Error.captureStackTrace(err, useDisclosureContext); | ||
throw err; | ||
} | ||
return context; | ||
} // --- | ||
var Disclosure = /*#__PURE__*/vue.defineComponent({ | ||
name: 'Disclosure', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'template' | ||
} | ||
}, | ||
setup: function setup(props, _ref) { | ||
var slots = _ref.slots, | ||
attrs = _ref.attrs; | ||
var passThroughProps = _extends({}, props); | ||
var disclosureState = vue.ref(DisclosureStates.Closed); | ||
var panelRef = vue.ref(null); | ||
var api = { | ||
disclosureState: disclosureState, | ||
panelRef: panelRef, | ||
toggleDisclosure: function toggleDisclosure() { | ||
var _match; | ||
disclosureState.value = match(disclosureState.value, (_match = {}, _match[DisclosureStates.Open] = DisclosureStates.Closed, _match[DisclosureStates.Closed] = DisclosureStates.Open, _match)); | ||
} | ||
}; | ||
vue.provide(DisclosureContext, api); | ||
return function () { | ||
var slot = { | ||
open: disclosureState.value === DisclosureStates.Open | ||
}; | ||
return render({ | ||
props: passThroughProps, | ||
slot: slot, | ||
slots: slots, | ||
attrs: attrs | ||
}); | ||
}; | ||
} | ||
}); // --- | ||
var DisclosureButton = /*#__PURE__*/vue.defineComponent({ | ||
name: 'DisclosureButton', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'button' | ||
}, | ||
disabled: { | ||
type: [Boolean], | ||
"default": false | ||
} | ||
}, | ||
render: function render$1() { | ||
var api = useDisclosureContext('DisclosureButton'); | ||
var slot = { | ||
open: api.disclosureState.value === DisclosureStates.Open | ||
}; | ||
var propsWeControl = { | ||
id: this.id, | ||
ref: 'el', | ||
type: 'button', | ||
'aria-expanded': api.disclosureState.value === DisclosureStates.Open ? true : undefined, | ||
'aria-controls': this.ariaControls, | ||
onClick: this.handleClick, | ||
onKeydown: this.handleKeyDown | ||
}; | ||
return render({ | ||
props: _extends({}, this.$props, propsWeControl), | ||
slot: slot, | ||
attrs: this.$attrs, | ||
slots: this.$slots | ||
}); | ||
}, | ||
setup: function setup(props) { | ||
var api = useDisclosureContext('DisclosureButton'); | ||
var buttonId = "headlessui-disclosure-button-" + useId(); | ||
var ariaControls = vue.computed(function () { | ||
var _dom$id, _dom; | ||
return (_dom$id = (_dom = dom(api.panelRef)) == null ? void 0 : _dom.id) != null ? _dom$id : undefined; | ||
}); | ||
return { | ||
id: buttonId, | ||
ariaControls: ariaControls, | ||
handleClick: function handleClick() { | ||
if (props.disabled) return; | ||
api.toggleDisclosure(); | ||
}, | ||
handleKeyDown: function handleKeyDown(event) { | ||
if (props.disabled) return; | ||
switch (event.key) { | ||
case Keys.Space: | ||
case Keys.Enter: | ||
event.preventDefault(); | ||
api.toggleDisclosure(); | ||
break; | ||
} | ||
} | ||
}; | ||
} | ||
}); // --- | ||
var DisclosurePanel = /*#__PURE__*/vue.defineComponent({ | ||
name: 'DisclosurePanel', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'div' | ||
}, | ||
"static": { | ||
type: Boolean, | ||
"default": false | ||
}, | ||
unmount: { | ||
type: Boolean, | ||
"default": true | ||
} | ||
}, | ||
render: function render$1() { | ||
var api = useDisclosureContext('DisclosurePanel'); | ||
var slot = { | ||
open: api.disclosureState.value === DisclosureStates.Open | ||
}; | ||
var propsWeControl = { | ||
id: this.id, | ||
ref: 'el' | ||
}; | ||
return render({ | ||
props: _extends({}, this.$props, propsWeControl), | ||
slot: slot, | ||
attrs: this.$attrs, | ||
slots: this.$slots, | ||
features: Features.RenderStrategy | Features.Static, | ||
visible: slot.open | ||
}); | ||
}, | ||
setup: function setup() { | ||
var api = useDisclosureContext('DisclosurePanel'); | ||
var panelId = "headlessui-disclosure-panel-" + useId(); | ||
return { | ||
id: panelId, | ||
el: api.panelRef | ||
}; | ||
} | ||
}); | ||
function assertNever(x) { | ||
@@ -326,17 +549,9 @@ throw new Error('Unexpected object: ' + x); | ||
function dom(ref) { | ||
var _ref$value$$el; | ||
var ListboxStates; | ||
if (ref == null) return null; | ||
if (ref.value == null) return null; | ||
return (_ref$value$$el = ref.value.$el) != null ? _ref$value$$el : ref.value; | ||
} | ||
(function (ListboxStates) { | ||
ListboxStates[ListboxStates["Open"] = 0] = "Open"; | ||
ListboxStates[ListboxStates["Closed"] = 1] = "Closed"; | ||
})(ListboxStates || (ListboxStates = {})); | ||
var MenuStates; | ||
(function (MenuStates) { | ||
MenuStates[MenuStates["Open"] = 0] = "Open"; | ||
MenuStates[MenuStates["Closed"] = 1] = "Closed"; | ||
})(MenuStates || (MenuStates = {})); | ||
function nextFrame(cb) { | ||
@@ -348,10 +563,10 @@ requestAnimationFrame(function () { | ||
var MenuContext = /*#__PURE__*/Symbol('MenuContext'); | ||
var ListboxContext = /*#__PURE__*/Symbol('ListboxContext'); | ||
function useMenuContext(component) { | ||
var context = vue.inject(MenuContext, null); | ||
function useListboxContext(component) { | ||
var context = vue.inject(ListboxContext, null); | ||
if (context === null) { | ||
var err = new Error("<" + component + " /> is missing a parent <Menu /> component."); | ||
if (Error.captureStackTrace) Error.captureStackTrace(err, useMenuContext); | ||
var err = new Error("<" + component + " /> is missing a parent <Listbox /> component."); | ||
if (Error.captureStackTrace) Error.captureStackTrace(err, useListboxContext); | ||
throw err; | ||
@@ -361,5 +576,8 @@ } | ||
return context; | ||
} | ||
} // --- | ||
var Menu = /*#__PURE__*/vue.defineComponent({ | ||
var Listbox = /*#__PURE__*/vue.defineComponent({ | ||
name: 'Listbox', | ||
emits: ['update:modelValue'], | ||
props: { | ||
@@ -369,2 +587,10 @@ as: { | ||
"default": 'template' | ||
}, | ||
disabled: { | ||
type: [Boolean], | ||
"default": false | ||
}, | ||
modelValue: { | ||
type: [Object, String, Number, Boolean], | ||
"default": null | ||
} | ||
@@ -374,25 +600,43 @@ }, | ||
var slots = _ref.slots, | ||
attrs = _ref.attrs; | ||
var menuState = vue.ref(MenuStates.Closed); | ||
attrs = _ref.attrs, | ||
emit = _ref.emit; | ||
var disabled = props.disabled, | ||
passThroughProps = _objectWithoutPropertiesLoose(props, ["modelValue", "disabled"]); | ||
var listboxState = vue.ref(ListboxStates.Closed); | ||
var labelRef = vue.ref(null); | ||
var buttonRef = vue.ref(null); | ||
var itemsRef = vue.ref(null); | ||
var items = vue.ref([]); | ||
var optionsRef = vue.ref(null); | ||
var options = vue.ref([]); | ||
var searchQuery = vue.ref(''); | ||
var activeItemIndex = vue.ref(null); | ||
var activeOptionIndex = vue.ref(null); | ||
var value = vue.computed(function () { | ||
return props.modelValue; | ||
}); | ||
var api = { | ||
menuState: menuState, | ||
listboxState: listboxState, | ||
value: value, | ||
labelRef: labelRef, | ||
buttonRef: buttonRef, | ||
itemsRef: itemsRef, | ||
items: items, | ||
optionsRef: optionsRef, | ||
disabled: disabled, | ||
options: options, | ||
searchQuery: searchQuery, | ||
activeItemIndex: activeItemIndex, | ||
closeMenu: function closeMenu() { | ||
menuState.value = MenuStates.Closed; | ||
activeItemIndex.value = null; | ||
activeOptionIndex: activeOptionIndex, | ||
closeListbox: function closeListbox() { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
listboxState.value = ListboxStates.Closed; | ||
activeOptionIndex.value = null; | ||
}, | ||
openMenu: function openMenu() { | ||
return menuState.value = MenuStates.Open; | ||
openListbox: function openListbox() { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Open) return; | ||
listboxState.value = ListboxStates.Open; | ||
}, | ||
goToItem: function goToItem(focus, id) { | ||
var nextActiveItemIndex = calculateActiveIndex(focus === Focus.Specific ? { | ||
goToOption: function goToOption(focus, id) { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
var nextActiveOptionIndex = calculateActiveIndex(focus === Focus.Specific ? { | ||
focus: Focus.Specific, | ||
@@ -404,32 +648,37 @@ id: id | ||
resolveItems: function resolveItems() { | ||
return items.value; | ||
return options.value; | ||
}, | ||
resolveActiveIndex: function resolveActiveIndex() { | ||
return activeItemIndex.value; | ||
return activeOptionIndex.value; | ||
}, | ||
resolveId: function resolveId(item) { | ||
return item.id; | ||
resolveId: function resolveId(option) { | ||
return option.id; | ||
}, | ||
resolveDisabled: function resolveDisabled(item) { | ||
return item.dataRef.disabled; | ||
resolveDisabled: function resolveDisabled(option) { | ||
return option.dataRef.disabled; | ||
} | ||
}); | ||
if (searchQuery.value === '' && activeItemIndex.value === nextActiveItemIndex) return; | ||
if (searchQuery.value === '' && activeOptionIndex.value === nextActiveOptionIndex) return; | ||
searchQuery.value = ''; | ||
activeItemIndex.value = nextActiveItemIndex; | ||
activeOptionIndex.value = nextActiveOptionIndex; | ||
}, | ||
search: function search(value) { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
searchQuery.value += value; | ||
var match = items.value.findIndex(function (item) { | ||
return item.dataRef.textValue.startsWith(searchQuery.value) && !item.dataRef.disabled; | ||
var match = options.value.findIndex(function (option) { | ||
return !option.dataRef.disabled && option.dataRef.textValue.startsWith(searchQuery.value); | ||
}); | ||
if (match === -1 || match === activeItemIndex.value) return; | ||
activeItemIndex.value = match; | ||
if (match === -1 || match === activeOptionIndex.value) return; | ||
activeOptionIndex.value = match; | ||
}, | ||
clearSearch: function clearSearch() { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
if (searchQuery.value === '') return; | ||
searchQuery.value = ''; | ||
}, | ||
registerItem: function registerItem(id, dataRef) { | ||
registerOption: function registerOption(id, dataRef) { | ||
// @ts-expect-error The expected type comes from property 'dataRef' which is declared here on type '{ id: string; dataRef: { textValue: string; disabled: boolean; }; }' | ||
items.value.push({ | ||
options.value.push({ | ||
id: id, | ||
@@ -439,18 +688,22 @@ dataRef: dataRef | ||
}, | ||
unregisterItem: function unregisterItem(id) { | ||
var nextItems = items.value.slice(); | ||
var currentActiveItem = activeItemIndex.value !== null ? nextItems[activeItemIndex.value] : null; | ||
var idx = nextItems.findIndex(function (a) { | ||
unregisterOption: function unregisterOption(id) { | ||
var nextOptions = options.value.slice(); | ||
var currentActiveOption = activeOptionIndex.value !== null ? nextOptions[activeOptionIndex.value] : null; | ||
var idx = nextOptions.findIndex(function (a) { | ||
return a.id === id; | ||
}); | ||
if (idx !== -1) nextItems.splice(idx, 1); | ||
items.value = nextItems; | ||
if (idx !== -1) nextOptions.splice(idx, 1); | ||
options.value = nextOptions; | ||
activeItemIndex.value = function () { | ||
if (idx === activeItemIndex.value) return null; | ||
if (currentActiveItem === null) return null; // If we removed the item before the actual active index, then it would be out of sync. To | ||
activeOptionIndex.value = function () { | ||
if (idx === activeOptionIndex.value) return null; | ||
if (currentActiveOption === null) return null; // If we removed the option before the actual active index, then it would be out of sync. To | ||
// fix this, we will find the correct (new) index position. | ||
return nextItems.indexOf(currentActiveItem); | ||
return nextOptions.indexOf(currentActiveOption); | ||
}(); | ||
}, | ||
select: function select(value) { | ||
if (disabled) return; | ||
emit('update:modelValue', value); | ||
} | ||
@@ -464,5 +717,5 @@ }; | ||
var active = document.activeElement; | ||
if (menuState.value !== MenuStates.Open) return; | ||
if (listboxState.value !== ListboxStates.Open) return; | ||
if ((_dom = dom(buttonRef)) == null ? void 0 : _dom.contains(target)) return; | ||
if (!((_dom2 = dom(itemsRef)) == null ? void 0 : _dom2.contains(target))) api.closeMenu(); | ||
if (!((_dom2 = dom(optionsRef)) == null ? void 0 : _dom2.contains(target))) api.closeListbox(); | ||
if (active !== document.body && (active == null ? void 0 : active.contains(target))) return; // Keep focus on newly clicked/focused element | ||
@@ -481,9 +734,10 @@ | ||
vue.provide(MenuContext, api); | ||
vue.provide(ListboxContext, api); | ||
return function () { | ||
var slot = { | ||
open: menuState.value === MenuStates.Open | ||
open: listboxState.value === ListboxStates.Open, | ||
disabled: disabled | ||
}; | ||
return render({ | ||
props: props, | ||
props: passThroughProps, | ||
slot: slot, | ||
@@ -495,11 +749,52 @@ slots: slots, | ||
} | ||
}); | ||
var MenuButton = /*#__PURE__*/vue.defineComponent({ | ||
}); // --- | ||
var ListboxLabel = /*#__PURE__*/vue.defineComponent({ | ||
name: 'ListboxLabel', | ||
props: { | ||
disabled: { | ||
type: Boolean, | ||
"default": false | ||
}, | ||
as: { | ||
type: [Object, String], | ||
"default": 'label' | ||
} | ||
}, | ||
render: function render$1() { | ||
var api = useListboxContext('ListboxLabel'); | ||
var slot = { | ||
open: api.listboxState.value === ListboxStates.Open, | ||
disabled: api.disabled | ||
}; | ||
var propsWeControl = { | ||
id: this.id, | ||
ref: 'el', | ||
onClick: this.handleClick | ||
}; | ||
return render({ | ||
props: _extends({}, this.$props, propsWeControl), | ||
slot: slot, | ||
attrs: this.$attrs, | ||
slots: this.$slots | ||
}); | ||
}, | ||
setup: function setup() { | ||
var api = useListboxContext('ListboxLabel'); | ||
var id = "headlessui-listbox-label-" + useId(); | ||
return { | ||
id: id, | ||
el: api.labelRef, | ||
handleClick: function handleClick() { | ||
var _dom4; | ||
(_dom4 = dom(api.buttonRef)) == null ? void 0 : _dom4.focus({ | ||
preventScroll: true | ||
}); | ||
} | ||
}; | ||
} | ||
}); // --- | ||
var ListboxButton = /*#__PURE__*/vue.defineComponent({ | ||
name: 'ListboxButton', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'button' | ||
@@ -509,7 +804,8 @@ } | ||
render: function render$1() { | ||
var _dom4; | ||
var _dom5, _dom6; | ||
var api = useMenuContext('MenuButton'); | ||
var api = useListboxContext('ListboxButton'); | ||
var slot = { | ||
open: api.menuState.value === MenuStates.Open | ||
open: api.listboxState.value === ListboxStates.Open, | ||
disabled: api.disabled | ||
}; | ||
@@ -521,5 +817,7 @@ var propsWeControl = { | ||
'aria-haspopup': true, | ||
'aria-controls': (_dom4 = dom(api.itemsRef)) == null ? void 0 : _dom4.id, | ||
'aria-expanded': api.menuState.value === MenuStates.Open ? true : undefined, | ||
onKeyDown: this.handleKeyDown, | ||
'aria-controls': (_dom5 = dom(api.optionsRef)) == null ? void 0 : _dom5.id, | ||
'aria-expanded': api.listboxState.value === ListboxStates.Open ? true : undefined, | ||
'aria-labelledby': api.labelRef.value ? [(_dom6 = dom(api.labelRef)) == null ? void 0 : _dom6.id, this.id].join(' ') : undefined, | ||
disabled: api.disabled, | ||
onKeydown: this.handleKeyDown, | ||
onClick: this.handleClick | ||
@@ -534,5 +832,5 @@ }; | ||
}, | ||
setup: function setup(props) { | ||
var api = useMenuContext('MenuButton'); | ||
var id = "headlessui-menu-button-" + useId(); | ||
setup: function setup() { | ||
var api = useListboxContext('ListboxButton'); | ||
var id = "headlessui-listbox-button-" + useId(); | ||
@@ -546,10 +844,10 @@ function handleKeyDown(event) { | ||
event.preventDefault(); | ||
api.openMenu(); | ||
api.openListbox(); | ||
vue.nextTick(function () { | ||
var _dom5; | ||
var _dom7; | ||
(_dom5 = dom(api.itemsRef)) == null ? void 0 : _dom5.focus({ | ||
(_dom7 = dom(api.optionsRef)) == null ? void 0 : _dom7.focus({ | ||
preventScroll: true | ||
}); | ||
api.goToItem(Focus.First); | ||
if (!api.value.value) api.goToOption(Focus.First); | ||
}); | ||
@@ -560,10 +858,10 @@ break; | ||
event.preventDefault(); | ||
api.openMenu(); | ||
api.openListbox(); | ||
vue.nextTick(function () { | ||
var _dom6; | ||
var _dom8; | ||
(_dom6 = dom(api.itemsRef)) == null ? void 0 : _dom6.focus({ | ||
(_dom8 = dom(api.optionsRef)) == null ? void 0 : _dom8.focus({ | ||
preventScroll: true | ||
}); | ||
api.goToItem(Focus.Last); | ||
if (!api.value.value) api.goToOption(Focus.Last); | ||
}); | ||
@@ -575,10 +873,10 @@ break; | ||
function handleClick(event) { | ||
if (props.disabled) return; | ||
if (api.disabled) return; | ||
if (api.menuState.value === MenuStates.Open) { | ||
api.closeMenu(); | ||
if (api.listboxState.value === ListboxStates.Open) { | ||
api.closeListbox(); | ||
vue.nextTick(function () { | ||
var _dom7; | ||
var _dom9; | ||
return (_dom7 = dom(api.buttonRef)) == null ? void 0 : _dom7.focus({ | ||
return (_dom9 = dom(api.buttonRef)) == null ? void 0 : _dom9.focus({ | ||
preventScroll: true | ||
@@ -589,7 +887,7 @@ }); | ||
event.preventDefault(); | ||
api.openMenu(); | ||
api.openListbox(); | ||
nextFrame(function () { | ||
var _dom8; | ||
var _dom10; | ||
return (_dom8 = dom(api.itemsRef)) == null ? void 0 : _dom8.focus({ | ||
return (_dom10 = dom(api.optionsRef)) == null ? void 0 : _dom10.focus({ | ||
preventScroll: true | ||
@@ -608,8 +906,10 @@ }); | ||
} | ||
}); | ||
var MenuItems = /*#__PURE__*/vue.defineComponent({ | ||
}); // --- | ||
var ListboxOptions = /*#__PURE__*/vue.defineComponent({ | ||
name: 'ListboxOptions', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'div' | ||
"default": 'ul' | ||
}, | ||
@@ -626,14 +926,14 @@ "static": { | ||
render: function render$1() { | ||
var _api$items$value$api$, _dom9; | ||
var _api$options$value$ap, _dom$id, _dom11, _dom12; | ||
var api = useMenuContext('MenuItems'); | ||
var api = useListboxContext('ListboxOptions'); | ||
var slot = { | ||
open: api.menuState.value === MenuStates.Open | ||
open: api.listboxState.value === ListboxStates.Open | ||
}; | ||
var propsWeControl = { | ||
'aria-activedescendant': api.activeItemIndex.value === null ? undefined : (_api$items$value$api$ = api.items.value[api.activeItemIndex.value]) == null ? void 0 : _api$items$value$api$.id, | ||
'aria-labelledby': (_dom9 = dom(api.buttonRef)) == null ? void 0 : _dom9.id, | ||
'aria-activedescendant': api.activeOptionIndex.value === null ? undefined : (_api$options$value$ap = api.options.value[api.activeOptionIndex.value]) == null ? void 0 : _api$options$value$ap.id, | ||
'aria-labelledby': (_dom$id = (_dom11 = dom(api.labelRef)) == null ? void 0 : _dom11.id) != null ? _dom$id : (_dom12 = dom(api.buttonRef)) == null ? void 0 : _dom12.id, | ||
id: this.id, | ||
onKeyDown: this.handleKeyDown, | ||
role: 'menu', | ||
onKeydown: this.handleKeyDown, | ||
role: 'listbox', | ||
tabIndex: 0, | ||
@@ -653,22 +953,6 @@ ref: 'el' | ||
setup: function setup() { | ||
var api = useMenuContext('MenuItems'); | ||
var id = "headlessui-menu-items-" + useId(); | ||
var api = useListboxContext('ListboxOptions'); | ||
var id = "headlessui-listbox-options-" + useId(); | ||
var searchDebounce = vue.ref(null); | ||
vue.watchEffect(function () { | ||
var container = dom(api.itemsRef); | ||
if (!container) return; | ||
if (api.menuState.value !== MenuStates.Open) return; | ||
var walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, { | ||
acceptNode: function acceptNode(node) { | ||
if (node.getAttribute('role') === 'menuitem') return NodeFilter.FILTER_REJECT; | ||
if (node.hasAttribute('role')) return NodeFilter.FILTER_SKIP; | ||
return NodeFilter.FILTER_ACCEPT; | ||
} | ||
}); | ||
while (walker.nextNode()) { | ||
walker.currentNode.setAttribute('role', 'none'); | ||
} | ||
}); | ||
function handleKeyDown(event) { | ||
@@ -691,14 +975,12 @@ if (searchDebounce.value) clearTimeout(searchDebounce.value); | ||
if (api.activeItemIndex.value !== null) { | ||
var _document$getElementB; | ||
var _id = api.items.value[api.activeItemIndex.value].id; | ||
(_document$getElementB = document.getElementById(_id)) == null ? void 0 : _document$getElementB.click(); | ||
if (api.activeOptionIndex.value !== null) { | ||
var dataRef = api.options.value[api.activeOptionIndex.value].dataRef; | ||
api.select(dataRef.value); | ||
} | ||
api.closeMenu(); | ||
api.closeListbox(); | ||
vue.nextTick(function () { | ||
var _dom10; | ||
var _dom13; | ||
return (_dom10 = dom(api.buttonRef)) == null ? void 0 : _dom10.focus({ | ||
return (_dom13 = dom(api.buttonRef)) == null ? void 0 : _dom13.focus({ | ||
preventScroll: true | ||
@@ -711,7 +993,7 @@ }); | ||
event.preventDefault(); | ||
return api.goToItem(Focus.Next); | ||
return api.goToOption(Focus.Next); | ||
case Keys.ArrowUp: | ||
event.preventDefault(); | ||
return api.goToItem(Focus.Previous); | ||
return api.goToOption(Focus.Previous); | ||
@@ -721,3 +1003,3 @@ case Keys.Home: | ||
event.preventDefault(); | ||
return api.goToItem(Focus.First); | ||
return api.goToOption(Focus.First); | ||
@@ -727,11 +1009,11 @@ case Keys.End: | ||
event.preventDefault(); | ||
return api.goToItem(Focus.Last); | ||
return api.goToOption(Focus.Last); | ||
case Keys.Escape: | ||
event.preventDefault(); | ||
api.closeMenu(); | ||
api.closeListbox(); | ||
vue.nextTick(function () { | ||
var _dom11; | ||
var _dom14; | ||
return (_dom11 = dom(api.buttonRef)) == null ? void 0 : _dom11.focus({ | ||
return (_dom14 = dom(api.buttonRef)) == null ? void 0 : _dom14.focus({ | ||
preventScroll: true | ||
@@ -759,3 +1041,3 @@ }); | ||
id: id, | ||
el: api.itemsRef, | ||
el: api.optionsRef, | ||
handleKeyDown: handleKeyDown | ||
@@ -765,8 +1047,13 @@ }; | ||
}); | ||
var MenuItem = /*#__PURE__*/vue.defineComponent({ | ||
var ListboxOption = /*#__PURE__*/vue.defineComponent({ | ||
name: 'ListboxOption', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'template' | ||
"default": 'li' | ||
}, | ||
value: { | ||
type: [Object, String], | ||
"default": null | ||
}, | ||
disabled: { | ||
@@ -788,29 +1075,46 @@ type: Boolean, | ||
attrs = _ref2.attrs; | ||
var api = useMenuContext('MenuItem'); | ||
var id = "headlessui-menu-item-" + useId(); | ||
var api = useListboxContext('ListboxOption'); | ||
var id = "headlessui-listbox-option-" + useId(); | ||
var disabled = props.disabled, | ||
defaultClass = props["class"], | ||
_props$className = props.className, | ||
className = _props$className === void 0 ? defaultClass : _props$className; | ||
className = _props$className === void 0 ? defaultClass : _props$className, | ||
value = props.value; | ||
var active = vue.computed(function () { | ||
return api.activeItemIndex.value !== null ? api.items.value[api.activeItemIndex.value].id === id : false; | ||
return api.activeOptionIndex.value !== null ? api.options.value[api.activeOptionIndex.value].id === id : false; | ||
}); | ||
var selected = vue.computed(function () { | ||
return vue.toRaw(api.value.value) === vue.toRaw(value); | ||
}); | ||
var dataRef = vue.ref({ | ||
disabled: disabled, | ||
value: value, | ||
textValue: '' | ||
}); | ||
vue.onMounted(function () { | ||
var _document$getElementB2, _document$getElementB3; | ||
var _document$getElementB, _document$getElementB2; | ||
var textValue = (_document$getElementB2 = document.getElementById(id)) == null ? void 0 : (_document$getElementB3 = _document$getElementB2.textContent) == null ? void 0 : _document$getElementB3.toLowerCase().trim(); | ||
var textValue = (_document$getElementB = document.getElementById(id)) == null ? void 0 : (_document$getElementB2 = _document$getElementB.textContent) == null ? void 0 : _document$getElementB2.toLowerCase().trim(); | ||
if (textValue !== undefined) dataRef.value.textValue = textValue; | ||
}); | ||
vue.onMounted(function () { | ||
return api.registerItem(id, dataRef); | ||
return api.registerOption(id, dataRef); | ||
}); | ||
vue.onUnmounted(function () { | ||
return api.unregisterItem(id); | ||
return api.unregisterOption(id); | ||
}); | ||
vue.onMounted(function () { | ||
vue.watch([api.listboxState, selected], function () { | ||
var _document$getElementB3; | ||
if (api.listboxState.value !== ListboxStates.Open) return; | ||
if (!selected.value) return; | ||
api.goToOption(Focus.Specific, id); | ||
(_document$getElementB3 = document.getElementById(id)) == null ? void 0 : _document$getElementB3.focus == null ? void 0 : _document$getElementB3.focus(); | ||
}, { | ||
immediate: true | ||
}); | ||
}); | ||
vue.watchEffect(function () { | ||
if (api.menuState.value !== MenuStates.Open) return; | ||
if (api.listboxState.value !== ListboxStates.Open) return; | ||
if (!active.value) return; | ||
@@ -828,7 +1132,8 @@ vue.nextTick(function () { | ||
if (disabled) return event.preventDefault(); | ||
api.closeMenu(); | ||
api.select(value); | ||
api.closeListbox(); | ||
vue.nextTick(function () { | ||
var _dom12; | ||
var _dom15; | ||
return (_dom12 = dom(api.buttonRef)) == null ? void 0 : _dom12.focus({ | ||
return (_dom15 = dom(api.buttonRef)) == null ? void 0 : _dom15.focus({ | ||
preventScroll: true | ||
@@ -840,4 +1145,4 @@ }); | ||
function handleFocus() { | ||
if (disabled) return api.goToItem(Focus.Nothing); | ||
api.goToItem(Focus.Specific, id); | ||
if (disabled) return api.goToOption(Focus.Nothing); | ||
api.goToOption(Focus.Specific, id); | ||
} | ||
@@ -848,3 +1153,3 @@ | ||
if (active.value) return; | ||
api.goToItem(Focus.Specific, id); | ||
api.goToOption(Focus.Specific, id); | ||
} | ||
@@ -855,3 +1160,3 @@ | ||
if (!active.value) return; | ||
api.goToItem(Focus.Nothing); | ||
api.goToOption(Focus.Nothing); | ||
} | ||
@@ -862,2 +1167,3 @@ | ||
active: active.value, | ||
selected: selected.value, | ||
disabled: disabled | ||
@@ -867,12 +1173,13 @@ }; | ||
id: id, | ||
role: 'menuitem', | ||
role: 'option', | ||
tabIndex: -1, | ||
"class": resolvePropValue(className, slot), | ||
'aria-disabled': disabled === true ? true : undefined, | ||
'aria-selected': selected.value === true ? selected.value : undefined, | ||
onClick: handleClick, | ||
onFocus: handleFocus, | ||
onPointerMove: handleMove, | ||
onMouseMove: handleMove, | ||
onPointerLeave: handleLeave, | ||
onMouseLeave: handleLeave | ||
onPointermove: handleMove, | ||
onMousemove: handleMove, | ||
onPointerleave: handleLeave, | ||
onMouseleave: handleLeave | ||
}; | ||
@@ -889,8 +1196,8 @@ return render({ | ||
var ListboxStates; | ||
var MenuStates; | ||
(function (ListboxStates) { | ||
ListboxStates[ListboxStates["Open"] = 0] = "Open"; | ||
ListboxStates[ListboxStates["Closed"] = 1] = "Closed"; | ||
})(ListboxStates || (ListboxStates = {})); | ||
(function (MenuStates) { | ||
MenuStates[MenuStates["Open"] = 0] = "Open"; | ||
MenuStates[MenuStates["Closed"] = 1] = "Closed"; | ||
})(MenuStates || (MenuStates = {})); | ||
@@ -903,10 +1210,10 @@ function nextFrame$1(cb) { | ||
var ListboxContext = /*#__PURE__*/Symbol('ListboxContext'); | ||
var MenuContext = /*#__PURE__*/Symbol('MenuContext'); | ||
function useListboxContext(component) { | ||
var context = vue.inject(ListboxContext, null); | ||
function useMenuContext(component) { | ||
var context = vue.inject(MenuContext, null); | ||
if (context === null) { | ||
var err = new Error("<" + component + " /> is missing a parent <Listbox /> component."); | ||
if (Error.captureStackTrace) Error.captureStackTrace(err, useListboxContext); | ||
var err = new Error("<" + component + " /> is missing a parent <Menu /> component."); | ||
if (Error.captureStackTrace) Error.captureStackTrace(err, useMenuContext); | ||
throw err; | ||
@@ -916,8 +1223,5 @@ } | ||
return context; | ||
} // --- | ||
} | ||
var Listbox = /*#__PURE__*/vue.defineComponent({ | ||
name: 'Listbox', | ||
emits: ['update:modelValue'], | ||
var Menu = /*#__PURE__*/vue.defineComponent({ | ||
props: { | ||
@@ -927,10 +1231,2 @@ as: { | ||
"default": 'template' | ||
}, | ||
disabled: { | ||
type: [Boolean], | ||
"default": false | ||
}, | ||
modelValue: { | ||
type: [Object, String, Number, Boolean], | ||
"default": null | ||
} | ||
@@ -940,43 +1236,25 @@ }, | ||
var slots = _ref.slots, | ||
attrs = _ref.attrs, | ||
emit = _ref.emit; | ||
var disabled = props.disabled, | ||
passThroughProps = _objectWithoutPropertiesLoose(props, ["modelValue", "disabled"]); | ||
var listboxState = vue.ref(ListboxStates.Closed); | ||
var labelRef = vue.ref(null); | ||
attrs = _ref.attrs; | ||
var menuState = vue.ref(MenuStates.Closed); | ||
var buttonRef = vue.ref(null); | ||
var optionsRef = vue.ref(null); | ||
var options = vue.ref([]); | ||
var itemsRef = vue.ref(null); | ||
var items = vue.ref([]); | ||
var searchQuery = vue.ref(''); | ||
var activeOptionIndex = vue.ref(null); | ||
var value = vue.computed(function () { | ||
return props.modelValue; | ||
}); | ||
var activeItemIndex = vue.ref(null); | ||
var api = { | ||
listboxState: listboxState, | ||
value: value, | ||
labelRef: labelRef, | ||
menuState: menuState, | ||
buttonRef: buttonRef, | ||
optionsRef: optionsRef, | ||
disabled: disabled, | ||
options: options, | ||
itemsRef: itemsRef, | ||
items: items, | ||
searchQuery: searchQuery, | ||
activeOptionIndex: activeOptionIndex, | ||
closeListbox: function closeListbox() { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
listboxState.value = ListboxStates.Closed; | ||
activeOptionIndex.value = null; | ||
activeItemIndex: activeItemIndex, | ||
closeMenu: function closeMenu() { | ||
menuState.value = MenuStates.Closed; | ||
activeItemIndex.value = null; | ||
}, | ||
openListbox: function openListbox() { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Open) return; | ||
listboxState.value = ListboxStates.Open; | ||
openMenu: function openMenu() { | ||
return menuState.value = MenuStates.Open; | ||
}, | ||
goToOption: function goToOption(focus, id) { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
var nextActiveOptionIndex = calculateActiveIndex(focus === Focus.Specific ? { | ||
goToItem: function goToItem(focus, id) { | ||
var nextActiveItemIndex = calculateActiveIndex(focus === Focus.Specific ? { | ||
focus: Focus.Specific, | ||
@@ -988,37 +1266,32 @@ id: id | ||
resolveItems: function resolveItems() { | ||
return options.value; | ||
return items.value; | ||
}, | ||
resolveActiveIndex: function resolveActiveIndex() { | ||
return activeOptionIndex.value; | ||
return activeItemIndex.value; | ||
}, | ||
resolveId: function resolveId(option) { | ||
return option.id; | ||
resolveId: function resolveId(item) { | ||
return item.id; | ||
}, | ||
resolveDisabled: function resolveDisabled(option) { | ||
return option.dataRef.disabled; | ||
resolveDisabled: function resolveDisabled(item) { | ||
return item.dataRef.disabled; | ||
} | ||
}); | ||
if (searchQuery.value === '' && activeOptionIndex.value === nextActiveOptionIndex) return; | ||
if (searchQuery.value === '' && activeItemIndex.value === nextActiveItemIndex) return; | ||
searchQuery.value = ''; | ||
activeOptionIndex.value = nextActiveOptionIndex; | ||
activeItemIndex.value = nextActiveItemIndex; | ||
}, | ||
search: function search(value) { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
searchQuery.value += value; | ||
var match = options.value.findIndex(function (option) { | ||
return !option.dataRef.disabled && option.dataRef.textValue.startsWith(searchQuery.value); | ||
var match = items.value.findIndex(function (item) { | ||
return item.dataRef.textValue.startsWith(searchQuery.value) && !item.dataRef.disabled; | ||
}); | ||
if (match === -1 || match === activeOptionIndex.value) return; | ||
activeOptionIndex.value = match; | ||
if (match === -1 || match === activeItemIndex.value) return; | ||
activeItemIndex.value = match; | ||
}, | ||
clearSearch: function clearSearch() { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
if (searchQuery.value === '') return; | ||
searchQuery.value = ''; | ||
}, | ||
registerOption: function registerOption(id, dataRef) { | ||
registerItem: function registerItem(id, dataRef) { | ||
// @ts-expect-error The expected type comes from property 'dataRef' which is declared here on type '{ id: string; dataRef: { textValue: string; disabled: boolean; }; }' | ||
options.value.push({ | ||
items.value.push({ | ||
id: id, | ||
@@ -1028,22 +1301,18 @@ dataRef: dataRef | ||
}, | ||
unregisterOption: function unregisterOption(id) { | ||
var nextOptions = options.value.slice(); | ||
var currentActiveOption = activeOptionIndex.value !== null ? nextOptions[activeOptionIndex.value] : null; | ||
var idx = nextOptions.findIndex(function (a) { | ||
unregisterItem: function unregisterItem(id) { | ||
var nextItems = items.value.slice(); | ||
var currentActiveItem = activeItemIndex.value !== null ? nextItems[activeItemIndex.value] : null; | ||
var idx = nextItems.findIndex(function (a) { | ||
return a.id === id; | ||
}); | ||
if (idx !== -1) nextOptions.splice(idx, 1); | ||
options.value = nextOptions; | ||
if (idx !== -1) nextItems.splice(idx, 1); | ||
items.value = nextItems; | ||
activeOptionIndex.value = function () { | ||
if (idx === activeOptionIndex.value) return null; | ||
if (currentActiveOption === null) return null; // If we removed the option before the actual active index, then it would be out of sync. To | ||
activeItemIndex.value = function () { | ||
if (idx === activeItemIndex.value) return null; | ||
if (currentActiveItem === null) return null; // If we removed the item before the actual active index, then it would be out of sync. To | ||
// fix this, we will find the correct (new) index position. | ||
return nextOptions.indexOf(currentActiveOption); | ||
return nextItems.indexOf(currentActiveItem); | ||
}(); | ||
}, | ||
select: function select(value) { | ||
if (disabled) return; | ||
emit('update:modelValue', value); | ||
} | ||
@@ -1057,5 +1326,5 @@ }; | ||
var active = document.activeElement; | ||
if (listboxState.value !== ListboxStates.Open) return; | ||
if (menuState.value !== MenuStates.Open) return; | ||
if ((_dom = dom(buttonRef)) == null ? void 0 : _dom.contains(target)) return; | ||
if (!((_dom2 = dom(optionsRef)) == null ? void 0 : _dom2.contains(target))) api.closeListbox(); | ||
if (!((_dom2 = dom(itemsRef)) == null ? void 0 : _dom2.contains(target))) api.closeMenu(); | ||
if (active !== document.body && (active == null ? void 0 : active.contains(target))) return; // Keep focus on newly clicked/focused element | ||
@@ -1074,10 +1343,9 @@ | ||
vue.provide(ListboxContext, api); | ||
vue.provide(MenuContext, api); | ||
return function () { | ||
var slot = { | ||
open: listboxState.value === ListboxStates.Open, | ||
disabled: disabled | ||
open: menuState.value === MenuStates.Open | ||
}; | ||
return render({ | ||
props: passThroughProps, | ||
props: props, | ||
slot: slot, | ||
@@ -1089,52 +1357,11 @@ slots: slots, | ||
} | ||
}); // --- | ||
var ListboxLabel = /*#__PURE__*/vue.defineComponent({ | ||
name: 'ListboxLabel', | ||
}); | ||
var MenuButton = /*#__PURE__*/vue.defineComponent({ | ||
props: { | ||
disabled: { | ||
type: Boolean, | ||
"default": false | ||
}, | ||
as: { | ||
type: [Object, String], | ||
"default": 'label' | ||
} | ||
}, | ||
render: function render$1() { | ||
var api = useListboxContext('ListboxLabel'); | ||
var slot = { | ||
open: api.listboxState.value === ListboxStates.Open, | ||
disabled: api.disabled | ||
}; | ||
var propsWeControl = { | ||
id: this.id, | ||
ref: 'el', | ||
onClick: this.handleClick | ||
}; | ||
return render({ | ||
props: _extends({}, this.$props, propsWeControl), | ||
slot: slot, | ||
attrs: this.$attrs, | ||
slots: this.$slots | ||
}); | ||
}, | ||
setup: function setup() { | ||
var api = useListboxContext('ListboxLabel'); | ||
var id = "headlessui-listbox-label-" + useId(); | ||
return { | ||
id: id, | ||
el: api.labelRef, | ||
handleClick: function handleClick() { | ||
var _dom4; | ||
(_dom4 = dom(api.buttonRef)) == null ? void 0 : _dom4.focus({ | ||
preventScroll: true | ||
}); | ||
} | ||
}; | ||
} | ||
}); // --- | ||
var ListboxButton = /*#__PURE__*/vue.defineComponent({ | ||
name: 'ListboxButton', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'button' | ||
@@ -1144,8 +1371,7 @@ } | ||
render: function render$1() { | ||
var _dom5, _dom6; | ||
var _dom4; | ||
var api = useListboxContext('ListboxButton'); | ||
var api = useMenuContext('MenuButton'); | ||
var slot = { | ||
open: api.listboxState.value === ListboxStates.Open, | ||
disabled: api.disabled | ||
open: api.menuState.value === MenuStates.Open | ||
}; | ||
@@ -1157,7 +1383,5 @@ var propsWeControl = { | ||
'aria-haspopup': true, | ||
'aria-controls': (_dom5 = dom(api.optionsRef)) == null ? void 0 : _dom5.id, | ||
'aria-expanded': api.listboxState.value === ListboxStates.Open ? true : undefined, | ||
'aria-labelledby': api.labelRef.value ? [(_dom6 = dom(api.labelRef)) == null ? void 0 : _dom6.id, this.id].join(' ') : undefined, | ||
disabled: api.disabled, | ||
onKeyDown: this.handleKeyDown, | ||
'aria-controls': (_dom4 = dom(api.itemsRef)) == null ? void 0 : _dom4.id, | ||
'aria-expanded': api.menuState.value === MenuStates.Open ? true : undefined, | ||
onKeydown: this.handleKeyDown, | ||
onClick: this.handleClick | ||
@@ -1172,5 +1396,5 @@ }; | ||
}, | ||
setup: function setup() { | ||
var api = useListboxContext('ListboxButton'); | ||
var id = "headlessui-listbox-button-" + useId(); | ||
setup: function setup(props) { | ||
var api = useMenuContext('MenuButton'); | ||
var id = "headlessui-menu-button-" + useId(); | ||
@@ -1184,10 +1408,10 @@ function handleKeyDown(event) { | ||
event.preventDefault(); | ||
api.openListbox(); | ||
api.openMenu(); | ||
vue.nextTick(function () { | ||
var _dom7; | ||
var _dom5; | ||
(_dom7 = dom(api.optionsRef)) == null ? void 0 : _dom7.focus({ | ||
(_dom5 = dom(api.itemsRef)) == null ? void 0 : _dom5.focus({ | ||
preventScroll: true | ||
}); | ||
if (!api.value.value) api.goToOption(Focus.First); | ||
api.goToItem(Focus.First); | ||
}); | ||
@@ -1198,10 +1422,10 @@ break; | ||
event.preventDefault(); | ||
api.openListbox(); | ||
api.openMenu(); | ||
vue.nextTick(function () { | ||
var _dom8; | ||
var _dom6; | ||
(_dom8 = dom(api.optionsRef)) == null ? void 0 : _dom8.focus({ | ||
(_dom6 = dom(api.itemsRef)) == null ? void 0 : _dom6.focus({ | ||
preventScroll: true | ||
}); | ||
if (!api.value.value) api.goToOption(Focus.Last); | ||
api.goToItem(Focus.Last); | ||
}); | ||
@@ -1213,10 +1437,10 @@ break; | ||
function handleClick(event) { | ||
if (api.disabled) return; | ||
if (props.disabled) return; | ||
if (api.listboxState.value === ListboxStates.Open) { | ||
api.closeListbox(); | ||
if (api.menuState.value === MenuStates.Open) { | ||
api.closeMenu(); | ||
vue.nextTick(function () { | ||
var _dom9; | ||
var _dom7; | ||
return (_dom9 = dom(api.buttonRef)) == null ? void 0 : _dom9.focus({ | ||
return (_dom7 = dom(api.buttonRef)) == null ? void 0 : _dom7.focus({ | ||
preventScroll: true | ||
@@ -1227,7 +1451,7 @@ }); | ||
event.preventDefault(); | ||
api.openListbox(); | ||
api.openMenu(); | ||
nextFrame$1(function () { | ||
var _dom10; | ||
var _dom8; | ||
return (_dom10 = dom(api.optionsRef)) == null ? void 0 : _dom10.focus({ | ||
return (_dom8 = dom(api.itemsRef)) == null ? void 0 : _dom8.focus({ | ||
preventScroll: true | ||
@@ -1246,10 +1470,8 @@ }); | ||
} | ||
}); // --- | ||
var ListboxOptions = /*#__PURE__*/vue.defineComponent({ | ||
name: 'ListboxOptions', | ||
}); | ||
var MenuItems = /*#__PURE__*/vue.defineComponent({ | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'ul' | ||
"default": 'div' | ||
}, | ||
@@ -1266,14 +1488,14 @@ "static": { | ||
render: function render$1() { | ||
var _api$options$value$ap, _dom$id, _dom11, _dom12; | ||
var _api$items$value$api$, _dom9; | ||
var api = useListboxContext('ListboxOptions'); | ||
var api = useMenuContext('MenuItems'); | ||
var slot = { | ||
open: api.listboxState.value === ListboxStates.Open | ||
open: api.menuState.value === MenuStates.Open | ||
}; | ||
var propsWeControl = { | ||
'aria-activedescendant': api.activeOptionIndex.value === null ? undefined : (_api$options$value$ap = api.options.value[api.activeOptionIndex.value]) == null ? void 0 : _api$options$value$ap.id, | ||
'aria-labelledby': (_dom$id = (_dom11 = dom(api.labelRef)) == null ? void 0 : _dom11.id) != null ? _dom$id : (_dom12 = dom(api.buttonRef)) == null ? void 0 : _dom12.id, | ||
'aria-activedescendant': api.activeItemIndex.value === null ? undefined : (_api$items$value$api$ = api.items.value[api.activeItemIndex.value]) == null ? void 0 : _api$items$value$api$.id, | ||
'aria-labelledby': (_dom9 = dom(api.buttonRef)) == null ? void 0 : _dom9.id, | ||
id: this.id, | ||
onKeyDown: this.handleKeyDown, | ||
role: 'listbox', | ||
onKeydown: this.handleKeyDown, | ||
role: 'menu', | ||
tabIndex: 0, | ||
@@ -1293,6 +1515,22 @@ ref: 'el' | ||
setup: function setup() { | ||
var api = useListboxContext('ListboxOptions'); | ||
var id = "headlessui-listbox-options-" + useId(); | ||
var api = useMenuContext('MenuItems'); | ||
var id = "headlessui-menu-items-" + useId(); | ||
var searchDebounce = vue.ref(null); | ||
vue.watchEffect(function () { | ||
var container = dom(api.itemsRef); | ||
if (!container) return; | ||
if (api.menuState.value !== MenuStates.Open) return; | ||
var walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, { | ||
acceptNode: function acceptNode(node) { | ||
if (node.getAttribute('role') === 'menuitem') return NodeFilter.FILTER_REJECT; | ||
if (node.hasAttribute('role')) return NodeFilter.FILTER_SKIP; | ||
return NodeFilter.FILTER_ACCEPT; | ||
} | ||
}); | ||
while (walker.nextNode()) { | ||
walker.currentNode.setAttribute('role', 'none'); | ||
} | ||
}); | ||
function handleKeyDown(event) { | ||
@@ -1315,12 +1553,14 @@ if (searchDebounce.value) clearTimeout(searchDebounce.value); | ||
if (api.activeOptionIndex.value !== null) { | ||
var dataRef = api.options.value[api.activeOptionIndex.value].dataRef; | ||
api.select(dataRef.value); | ||
if (api.activeItemIndex.value !== null) { | ||
var _document$getElementB; | ||
var _id = api.items.value[api.activeItemIndex.value].id; | ||
(_document$getElementB = document.getElementById(_id)) == null ? void 0 : _document$getElementB.click(); | ||
} | ||
api.closeListbox(); | ||
api.closeMenu(); | ||
vue.nextTick(function () { | ||
var _dom13; | ||
var _dom10; | ||
return (_dom13 = dom(api.buttonRef)) == null ? void 0 : _dom13.focus({ | ||
return (_dom10 = dom(api.buttonRef)) == null ? void 0 : _dom10.focus({ | ||
preventScroll: true | ||
@@ -1333,7 +1573,7 @@ }); | ||
event.preventDefault(); | ||
return api.goToOption(Focus.Next); | ||
return api.goToItem(Focus.Next); | ||
case Keys.ArrowUp: | ||
event.preventDefault(); | ||
return api.goToOption(Focus.Previous); | ||
return api.goToItem(Focus.Previous); | ||
@@ -1343,3 +1583,3 @@ case Keys.Home: | ||
event.preventDefault(); | ||
return api.goToOption(Focus.First); | ||
return api.goToItem(Focus.First); | ||
@@ -1349,11 +1589,11 @@ case Keys.End: | ||
event.preventDefault(); | ||
return api.goToOption(Focus.Last); | ||
return api.goToItem(Focus.Last); | ||
case Keys.Escape: | ||
event.preventDefault(); | ||
api.closeListbox(); | ||
api.closeMenu(); | ||
vue.nextTick(function () { | ||
var _dom14; | ||
var _dom11; | ||
return (_dom14 = dom(api.buttonRef)) == null ? void 0 : _dom14.focus({ | ||
return (_dom11 = dom(api.buttonRef)) == null ? void 0 : _dom11.focus({ | ||
preventScroll: true | ||
@@ -1381,3 +1621,3 @@ }); | ||
id: id, | ||
el: api.optionsRef, | ||
el: api.itemsRef, | ||
handleKeyDown: handleKeyDown | ||
@@ -1387,13 +1627,8 @@ }; | ||
}); | ||
var ListboxOption = /*#__PURE__*/vue.defineComponent({ | ||
name: 'ListboxOption', | ||
var MenuItem = /*#__PURE__*/vue.defineComponent({ | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'li' | ||
"default": 'template' | ||
}, | ||
value: { | ||
type: [Object, String], | ||
"default": null | ||
}, | ||
disabled: { | ||
@@ -1415,46 +1650,29 @@ type: Boolean, | ||
attrs = _ref2.attrs; | ||
var api = useListboxContext('ListboxOption'); | ||
var id = "headlessui-listbox-option-" + useId(); | ||
var api = useMenuContext('MenuItem'); | ||
var id = "headlessui-menu-item-" + useId(); | ||
var disabled = props.disabled, | ||
defaultClass = props["class"], | ||
_props$className = props.className, | ||
className = _props$className === void 0 ? defaultClass : _props$className, | ||
value = props.value; | ||
className = _props$className === void 0 ? defaultClass : _props$className; | ||
var active = vue.computed(function () { | ||
return api.activeOptionIndex.value !== null ? api.options.value[api.activeOptionIndex.value].id === id : false; | ||
return api.activeItemIndex.value !== null ? api.items.value[api.activeItemIndex.value].id === id : false; | ||
}); | ||
var selected = vue.computed(function () { | ||
return vue.toRaw(api.value.value) === vue.toRaw(value); | ||
}); | ||
var dataRef = vue.ref({ | ||
disabled: disabled, | ||
value: value, | ||
textValue: '' | ||
}); | ||
vue.onMounted(function () { | ||
var _document$getElementB, _document$getElementB2; | ||
var _document$getElementB2, _document$getElementB3; | ||
var textValue = (_document$getElementB = document.getElementById(id)) == null ? void 0 : (_document$getElementB2 = _document$getElementB.textContent) == null ? void 0 : _document$getElementB2.toLowerCase().trim(); | ||
var textValue = (_document$getElementB2 = document.getElementById(id)) == null ? void 0 : (_document$getElementB3 = _document$getElementB2.textContent) == null ? void 0 : _document$getElementB3.toLowerCase().trim(); | ||
if (textValue !== undefined) dataRef.value.textValue = textValue; | ||
}); | ||
vue.onMounted(function () { | ||
return api.registerOption(id, dataRef); | ||
return api.registerItem(id, dataRef); | ||
}); | ||
vue.onUnmounted(function () { | ||
return api.unregisterOption(id); | ||
return api.unregisterItem(id); | ||
}); | ||
vue.onMounted(function () { | ||
vue.watch([api.listboxState, selected], function () { | ||
var _document$getElementB3; | ||
if (api.listboxState.value !== ListboxStates.Open) return; | ||
if (!selected.value) return; | ||
api.goToOption(Focus.Specific, id); | ||
(_document$getElementB3 = document.getElementById(id)) == null ? void 0 : _document$getElementB3.focus == null ? void 0 : _document$getElementB3.focus(); | ||
}, { | ||
immediate: true | ||
}); | ||
}); | ||
vue.watchEffect(function () { | ||
if (api.listboxState.value !== ListboxStates.Open) return; | ||
if (api.menuState.value !== MenuStates.Open) return; | ||
if (!active.value) return; | ||
@@ -1472,8 +1690,7 @@ vue.nextTick(function () { | ||
if (disabled) return event.preventDefault(); | ||
api.select(value); | ||
api.closeListbox(); | ||
api.closeMenu(); | ||
vue.nextTick(function () { | ||
var _dom15; | ||
var _dom12; | ||
return (_dom15 = dom(api.buttonRef)) == null ? void 0 : _dom15.focus({ | ||
return (_dom12 = dom(api.buttonRef)) == null ? void 0 : _dom12.focus({ | ||
preventScroll: true | ||
@@ -1485,4 +1702,4 @@ }); | ||
function handleFocus() { | ||
if (disabled) return api.goToOption(Focus.Nothing); | ||
api.goToOption(Focus.Specific, id); | ||
if (disabled) return api.goToItem(Focus.Nothing); | ||
api.goToItem(Focus.Specific, id); | ||
} | ||
@@ -1493,3 +1710,3 @@ | ||
if (active.value) return; | ||
api.goToOption(Focus.Specific, id); | ||
api.goToItem(Focus.Specific, id); | ||
} | ||
@@ -1500,3 +1717,3 @@ | ||
if (!active.value) return; | ||
api.goToOption(Focus.Nothing); | ||
api.goToItem(Focus.Nothing); | ||
} | ||
@@ -1507,3 +1724,2 @@ | ||
active: active.value, | ||
selected: selected.value, | ||
disabled: disabled | ||
@@ -1513,13 +1729,12 @@ }; | ||
id: id, | ||
role: 'option', | ||
role: 'menuitem', | ||
tabIndex: -1, | ||
"class": resolvePropValue(className, slot), | ||
'aria-disabled': disabled === true ? true : undefined, | ||
'aria-selected': selected.value === true ? selected.value : undefined, | ||
onClick: handleClick, | ||
onFocus: handleFocus, | ||
onPointerMove: handleMove, | ||
onMouseMove: handleMove, | ||
onPointerLeave: handleLeave, | ||
onMouseLeave: handleLeave | ||
onPointermove: handleMove, | ||
onMousemove: handleMove, | ||
onPointerleave: handleLeave, | ||
onMouseleave: handleLeave | ||
}; | ||
@@ -1632,4 +1847,4 @@ return render({ | ||
onClick: this.handleClick, | ||
onKeyUp: this.handleKeyUp, | ||
onKeyPress: this.handleKeyPress | ||
onKeyup: this.handleKeyUp, | ||
onKeypress: this.handleKeyPress | ||
}; | ||
@@ -1746,2 +1961,6 @@ | ||
exports.Alert = Alert; | ||
exports.Disclosure = Disclosure; | ||
exports.DisclosureButton = DisclosureButton; | ||
exports.DisclosurePanel = DisclosurePanel; | ||
exports.Listbox = Listbox; | ||
@@ -1748,0 +1967,0 @@ exports.ListboxButton = ListboxButton; |
@@ -1,2 +0,2 @@ | ||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e,t,n=require("vue");function o(){return(o=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e}).apply(this,arguments)}function r(e,t){if(null==e)return{};var n,o,r={},l=Object.keys(e);for(o=0;o<l.length;o++)t.indexOf(n=l[o])>=0||(r[n]=e[n]);return r}function l(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=new Array(t);n<t;n++)o[n]=e[n];return o}function i(e,t){if(e in t){for(var n=t[e],o=arguments.length,r=new Array(o>2?o-2:0),l=2;l<o;l++)r[l-2]=arguments[l];return"function"==typeof n?n.apply(void 0,r):n}var u=new Error('Tried to handle "'+e+'" but there is no handler defined. Only defined handlers are: '+Object.keys(t).map((function(e){return'"'+e+'"'})).join(", ")+".");throw Error.captureStackTrace&&Error.captureStackTrace(u,i),u}function u(n){var l,u,s=n.visible,c=void 0===s||s,d=n.features,p=void 0===d?e.None:d,f=r(n,["visible","features"]);return c||p&e.Static&&f.props.static?a(f):p&e.RenderStrategy?i(null==(l=f.props.unmount)||l?t.Unmount:t.Hidden,((u={})[t.Unmount]=function(){return null},u[t.Hidden]=function(){return a(o({},f,{props:o({},f.props,{hidden:!0,style:{display:"none"}})}))},u)):a(f)}function a(e){var t=e.attrs,o=e.slots,i=e.slot,u=function(e,t){void 0===t&&(t=[]);for(var n,o=Object.assign({},e),r=function(e,t){var n;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return l(e,void 0);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?l(e,void 0):void 0}}(e))){n&&(e=n);var o=0;return function(){return o>=e.length?{done:!0}:{done:!1,value:e[o++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}return(n=e[Symbol.iterator]()).next.bind(n)}(t);!(n=r()).done;){var i=n.value;i in o&&delete o[i]}return o}(e.props,["unmount","static"]),a=u.as,s=r(u,["as"]),c=null==o.default?void 0:o.default(i);if("template"===a){if(Object.keys(s).length>0||"class"in t){var d=null!=c?c:[],p=d[0];if(d.slice(1).length>0)throw new Error('You should only render 1 child or use the `as="..."` prop');return n.cloneVNode(p,s)}return c}return n.h(a,s,c)}!function(e){e[e.None=0]="None",e[e.RenderStrategy=1]="RenderStrategy",e[e.Static=2]="Static"}(e||(e={})),function(e){e[e.Unmount=0]="Unmount",e[e.Hidden=1]="Hidden"}(t||(t={}));var s,c,d,p=0;function f(){return++p}function v(e,t){var n=t.resolveItems();if(n.length<=0)return null;var o=t.resolveActiveIndex(),r=null!=o?o:-1,l=function(){switch(e.focus){case c.First:return n.findIndex((function(e){return!t.resolveDisabled(e)}));case c.Previous:var o=n.slice().reverse().findIndex((function(e,n,o){return!(-1!==r&&o.length-n-1>=r||t.resolveDisabled(e))}));return-1===o?o:n.length-1-o;case c.Next:return n.findIndex((function(e,n){return!(n<=r||t.resolveDisabled(e))}));case c.Last:var l=n.slice().reverse().findIndex((function(e){return!t.resolveDisabled(e)}));return-1===l?l:n.length-1-l;case c.Specific:return n.findIndex((function(n){return t.resolveId(n)===e.id}));case c.Nothing:return null;default:!function(e){throw new Error("Unexpected object: "+e)}(e)}}();return-1===l?o:l}function b(e,t){if(void 0!==e)return"function"==typeof e?e(t):e}function m(e){var t;return null==e||null==e.value?null:null!=(t=e.value.$el)?t:e.value}!function(e){e.Space=" ",e.Enter="Enter",e.Escape="Escape",e.Backspace="Backspace",e.ArrowUp="ArrowUp",e.ArrowDown="ArrowDown",e.Home="Home",e.End="End",e.PageUp="PageUp",e.PageDown="PageDown",e.Tab="Tab"}(s||(s={})),function(e){e[e.First=0]="First",e[e.Previous=1]="Previous",e[e.Next=2]="Next",e[e.Last=3]="Last",e[e.Specific=4]="Specific",e[e.Nothing=5]="Nothing"}(c||(c={})),function(e){e[e.Open=0]="Open",e[e.Closed=1]="Closed"}(d||(d={}));var h=Symbol("MenuContext");function x(e){var t=n.inject(h,null);if(null===t){var o=new Error("<"+e+" /> is missing a parent <Menu /> component.");throw Error.captureStackTrace&&Error.captureStackTrace(o,x),o}return t}var y,S=n.defineComponent({props:{as:{type:[Object,String],default:"template"}},setup:function(e,t){var o=t.slots,r=t.attrs,l=n.ref(d.Closed),i=n.ref(null),a=n.ref(null),s=n.ref([]),p=n.ref(""),f=n.ref(null),b={menuState:l,buttonRef:i,itemsRef:a,items:s,searchQuery:p,activeItemIndex:f,closeMenu:function(){l.value=d.Closed,f.value=null},openMenu:function(){return l.value=d.Open},goToItem:function(e,t){var n=v(e===c.Specific?{focus:c.Specific,id:t}:{focus:e},{resolveItems:function(){return s.value},resolveActiveIndex:function(){return f.value},resolveId:function(e){return e.id},resolveDisabled:function(e){return e.dataRef.disabled}});""===p.value&&f.value===n||(p.value="",f.value=n)},search:function(e){p.value+=e;var t=s.value.findIndex((function(e){return e.dataRef.textValue.startsWith(p.value)&&!e.dataRef.disabled}));-1!==t&&t!==f.value&&(f.value=t)},clearSearch:function(){p.value=""},registerItem:function(e,t){s.value.push({id:e,dataRef:t})},unregisterItem:function(e){var t=s.value.slice(),n=null!==f.value?t[f.value]:null,o=t.findIndex((function(t){return t.id===e}));-1!==o&&t.splice(o,1),s.value=t,f.value=o===f.value||null===n?null:t.indexOf(n)}};return n.onMounted((function(){function e(e){var t,n,o,r=e.target,u=document.activeElement;l.value===d.Open&&((null==(t=m(i))?void 0:t.contains(r))||((null==(n=m(a))?void 0:n.contains(r))||b.closeMenu(),u!==document.body&&(null==u?void 0:u.contains(r))||e.defaultPrevented||null==(o=m(i))||o.focus({preventScroll:!0})))}window.addEventListener("mousedown",e),n.onUnmounted((function(){return window.removeEventListener("mousedown",e)}))})),n.provide(h,b),function(){return u({props:e,slot:{open:l.value===d.Open},slots:o,attrs:r})}}}),g=n.defineComponent({props:{disabled:{type:Boolean,default:!1},as:{type:[Object,String],default:"button"}},render:function(){var e,t=x("MenuButton"),n={open:t.menuState.value===d.Open},r={ref:"el",id:this.id,type:"button","aria-haspopup":!0,"aria-controls":null==(e=m(t.itemsRef))?void 0:e.id,"aria-expanded":t.menuState.value===d.Open||void 0,onKeyDown:this.handleKeyDown,onClick:this.handleClick};return u({props:o({},this.$props,r),slot:n,attrs:this.$attrs,slots:this.$slots})},setup:function(e){var t=x("MenuButton");return{id:"headlessui-menu-button-"+f(),el:t.buttonRef,handleKeyDown:function(e){switch(e.key){case s.Space:case s.Enter:case s.ArrowDown:e.preventDefault(),t.openMenu(),n.nextTick((function(){var e;null==(e=m(t.itemsRef))||e.focus({preventScroll:!0}),t.goToItem(c.First)}));break;case s.ArrowUp:e.preventDefault(),t.openMenu(),n.nextTick((function(){var e;null==(e=m(t.itemsRef))||e.focus({preventScroll:!0}),t.goToItem(c.Last)}))}},handleClick:function(o){var r;e.disabled||(t.menuState.value===d.Open?(t.closeMenu(),n.nextTick((function(){var e;return null==(e=m(t.buttonRef))?void 0:e.focus({preventScroll:!0})}))):(o.preventDefault(),t.openMenu(),r=function(){var e;return null==(e=m(t.itemsRef))?void 0:e.focus({preventScroll:!0})},requestAnimationFrame((function(){return requestAnimationFrame(r)}))))}}}}),w=n.defineComponent({props:{as:{type:[Object,String],default:"div"},static:{type:Boolean,default:!1},unmount:{type:Boolean,default:!0}},render:function(){var t,n,r=x("MenuItems"),l={open:r.menuState.value===d.Open},i={"aria-activedescendant":null===r.activeItemIndex.value||null==(t=r.items.value[r.activeItemIndex.value])?void 0:t.id,"aria-labelledby":null==(n=m(r.buttonRef))?void 0:n.id,id:this.id,onKeyDown:this.handleKeyDown,role:"menu",tabIndex:0,ref:"el"};return u({props:o({},this.$props,i),slot:l,attrs:this.$attrs,slots:this.$slots,features:e.RenderStrategy|e.Static,visible:l.open})},setup:function(){var e=x("MenuItems"),t="headlessui-menu-items-"+f(),o=n.ref(null);return n.watchEffect((function(){var t=m(e.itemsRef);if(t&&e.menuState.value===d.Open)for(var n=document.createTreeWalker(t,NodeFilter.SHOW_ELEMENT,{acceptNode:function(e){return"menuitem"===e.getAttribute("role")?NodeFilter.FILTER_REJECT:e.hasAttribute("role")?NodeFilter.FILTER_SKIP:NodeFilter.FILTER_ACCEPT}});n.nextNode();)n.currentNode.setAttribute("role","none")})),{id:t,el:e.itemsRef,handleKeyDown:function(t){switch(o.value&&clearTimeout(o.value),t.key){case s.Space:if(""!==e.searchQuery.value)return t.preventDefault(),e.search(t.key);case s.Enter:var r;t.preventDefault(),null!==e.activeItemIndex.value&&(null==(r=document.getElementById(e.items.value[e.activeItemIndex.value].id))||r.click()),e.closeMenu(),n.nextTick((function(){var t;return null==(t=m(e.buttonRef))?void 0:t.focus({preventScroll:!0})}));break;case s.ArrowDown:return t.preventDefault(),e.goToItem(c.Next);case s.ArrowUp:return t.preventDefault(),e.goToItem(c.Previous);case s.Home:case s.PageUp:return t.preventDefault(),e.goToItem(c.First);case s.End:case s.PageDown:return t.preventDefault(),e.goToItem(c.Last);case s.Escape:t.preventDefault(),e.closeMenu(),n.nextTick((function(){var t;return null==(t=m(e.buttonRef))?void 0:t.focus({preventScroll:!0})}));break;case s.Tab:return t.preventDefault();default:1===t.key.length&&(e.search(t.key),o.value=setTimeout((function(){return e.clearSearch()}),350))}}}}}),O=n.defineComponent({props:{as:{type:[Object,String],default:"template"},disabled:{type:Boolean,default:!1},class:{type:[String,Function],required:!1},className:{type:[String,Function],required:!1}},setup:function(e,t){var r=t.slots,l=t.attrs,i=x("MenuItem"),a="headlessui-menu-item-"+f(),s=e.disabled,p=e.className,v=void 0===p?e.class:p,h=n.computed((function(){return null!==i.activeItemIndex.value&&i.items.value[i.activeItemIndex.value].id===a})),y=n.ref({disabled:s,textValue:""});function S(e){if(s)return e.preventDefault();i.closeMenu(),n.nextTick((function(){var e;return null==(e=m(i.buttonRef))?void 0:e.focus({preventScroll:!0})}))}function g(){if(s)return i.goToItem(c.Nothing);i.goToItem(c.Specific,a)}function w(){s||h.value||i.goToItem(c.Specific,a)}function O(){s||h.value&&i.goToItem(c.Nothing)}return n.onMounted((function(){var e,t,n=null==(e=document.getElementById(a))||null==(t=e.textContent)?void 0:t.toLowerCase().trim();void 0!==n&&(y.value.textValue=n)})),n.onMounted((function(){return i.registerItem(a,y)})),n.onUnmounted((function(){return i.unregisterItem(a)})),n.watchEffect((function(){i.menuState.value===d.Open&&h.value&&n.nextTick((function(){var e;return null==(e=document.getElementById(a))||null==e.scrollIntoView?void 0:e.scrollIntoView({block:"nearest"})}))})),function(){var t={active:h.value,disabled:s},n={id:a,role:"menuitem",tabIndex:-1,class:b(v,t),"aria-disabled":!0===s||void 0,onClick:S,onFocus:g,onPointerMove:w,onMouseMove:w,onPointerLeave:O,onMouseLeave:O};return u({props:o({},e,n),slot:t,attrs:l,slots:r})}}});!function(e){e[e.Open=0]="Open",e[e.Closed=1]="Closed"}(y||(y={}));var I=Symbol("ListboxContext");function k(e){var t=n.inject(I,null);if(null===t){var o=new Error("<"+e+" /> is missing a parent <Listbox /> component.");throw Error.captureStackTrace&&Error.captureStackTrace(o,k),o}return t}var R=n.defineComponent({name:"Listbox",emits:["update:modelValue"],props:{as:{type:[Object,String],default:"template"},disabled:{type:[Boolean],default:!1},modelValue:{type:[Object,String,Number,Boolean],default:null}},setup:function(e,t){var o=t.slots,l=t.attrs,i=t.emit,a=e.disabled,s=r(e,["modelValue","disabled"]),d=n.ref(y.Closed),p=n.ref(null),f=n.ref(null),b=n.ref(null),h=n.ref([]),x=n.ref(""),S=n.ref(null),g=n.computed((function(){return e.modelValue})),w={listboxState:d,value:g,labelRef:p,buttonRef:f,optionsRef:b,disabled:a,options:h,searchQuery:x,activeOptionIndex:S,closeListbox:function(){a||d.value!==y.Closed&&(d.value=y.Closed,S.value=null)},openListbox:function(){a||d.value!==y.Open&&(d.value=y.Open)},goToOption:function(e,t){if(!a&&d.value!==y.Closed){var n=v(e===c.Specific?{focus:c.Specific,id:t}:{focus:e},{resolveItems:function(){return h.value},resolveActiveIndex:function(){return S.value},resolveId:function(e){return e.id},resolveDisabled:function(e){return e.dataRef.disabled}});""===x.value&&S.value===n||(x.value="",S.value=n)}},search:function(e){if(!a&&d.value!==y.Closed){x.value+=e;var t=h.value.findIndex((function(e){return!e.dataRef.disabled&&e.dataRef.textValue.startsWith(x.value)}));-1!==t&&t!==S.value&&(S.value=t)}},clearSearch:function(){a||d.value!==y.Closed&&""!==x.value&&(x.value="")},registerOption:function(e,t){h.value.push({id:e,dataRef:t})},unregisterOption:function(e){var t=h.value.slice(),n=null!==S.value?t[S.value]:null,o=t.findIndex((function(t){return t.id===e}));-1!==o&&t.splice(o,1),h.value=t,S.value=o===S.value||null===n?null:t.indexOf(n)},select:function(e){a||i("update:modelValue",e)}};return n.onMounted((function(){function e(e){var t,n,o,r=e.target,l=document.activeElement;d.value===y.Open&&((null==(t=m(f))?void 0:t.contains(r))||((null==(n=m(b))?void 0:n.contains(r))||w.closeListbox(),l!==document.body&&(null==l?void 0:l.contains(r))||e.defaultPrevented||null==(o=m(f))||o.focus({preventScroll:!0})))}window.addEventListener("mousedown",e),n.onUnmounted((function(){return window.removeEventListener("mousedown",e)}))})),n.provide(I,w),function(){return u({props:s,slot:{open:d.value===y.Open,disabled:a},slots:o,attrs:l})}}}),T=n.defineComponent({name:"ListboxLabel",props:{as:{type:[Object,String],default:"label"}},render:function(){var e=k("ListboxLabel"),t={open:e.listboxState.value===y.Open,disabled:e.disabled};return u({props:o({},this.$props,{id:this.id,ref:"el",onClick:this.handleClick}),slot:t,attrs:this.$attrs,slots:this.$slots})},setup:function(){var e=k("ListboxLabel");return{id:"headlessui-listbox-label-"+f(),el:e.labelRef,handleClick:function(){var t;null==(t=m(e.buttonRef))||t.focus({preventScroll:!0})}}}}),D=n.defineComponent({name:"ListboxButton",props:{as:{type:[Object,String],default:"button"}},render:function(){var e,t,n=k("ListboxButton"),r={open:n.listboxState.value===y.Open,disabled:n.disabled},l={ref:"el",id:this.id,type:"button","aria-haspopup":!0,"aria-controls":null==(e=m(n.optionsRef))?void 0:e.id,"aria-expanded":n.listboxState.value===y.Open||void 0,"aria-labelledby":n.labelRef.value?[null==(t=m(n.labelRef))?void 0:t.id,this.id].join(" "):void 0,disabled:n.disabled,onKeyDown:this.handleKeyDown,onClick:this.handleClick};return u({props:o({},this.$props,l),slot:r,attrs:this.$attrs,slots:this.$slots})},setup:function(){var e=k("ListboxButton");return{id:"headlessui-listbox-button-"+f(),el:e.buttonRef,handleKeyDown:function(t){switch(t.key){case s.Space:case s.Enter:case s.ArrowDown:t.preventDefault(),e.openListbox(),n.nextTick((function(){var t;null==(t=m(e.optionsRef))||t.focus({preventScroll:!0}),e.value.value||e.goToOption(c.First)}));break;case s.ArrowUp:t.preventDefault(),e.openListbox(),n.nextTick((function(){var t;null==(t=m(e.optionsRef))||t.focus({preventScroll:!0}),e.value.value||e.goToOption(c.Last)}))}},handleClick:function(t){var o;e.disabled||(e.listboxState.value===y.Open?(e.closeListbox(),n.nextTick((function(){var t;return null==(t=m(e.buttonRef))?void 0:t.focus({preventScroll:!0})}))):(t.preventDefault(),e.openListbox(),o=function(){var t;return null==(t=m(e.optionsRef))?void 0:t.focus({preventScroll:!0})},requestAnimationFrame((function(){return requestAnimationFrame(o)}))))}}}}),L=n.defineComponent({name:"ListboxOptions",props:{as:{type:[Object,String],default:"ul"},static:{type:Boolean,default:!1},unmount:{type:Boolean,default:!0}},render:function(){var t,n,r,l,i=k("ListboxOptions"),a={open:i.listboxState.value===y.Open},s={"aria-activedescendant":null===i.activeOptionIndex.value||null==(t=i.options.value[i.activeOptionIndex.value])?void 0:t.id,"aria-labelledby":null!=(n=null==(r=m(i.labelRef))?void 0:r.id)?n:null==(l=m(i.buttonRef))?void 0:l.id,id:this.id,onKeyDown:this.handleKeyDown,role:"listbox",tabIndex:0,ref:"el"};return u({props:o({},this.$props,s),slot:a,attrs:this.$attrs,slots:this.$slots,features:e.RenderStrategy|e.Static,visible:a.open})},setup:function(){var e=k("ListboxOptions"),t="headlessui-listbox-options-"+f(),o=n.ref(null);return{id:t,el:e.optionsRef,handleKeyDown:function(t){switch(o.value&&clearTimeout(o.value),t.key){case s.Space:if(""!==e.searchQuery.value)return t.preventDefault(),e.search(t.key);case s.Enter:t.preventDefault(),null!==e.activeOptionIndex.value&&e.select(e.options.value[e.activeOptionIndex.value].dataRef.value),e.closeListbox(),n.nextTick((function(){var t;return null==(t=m(e.buttonRef))?void 0:t.focus({preventScroll:!0})}));break;case s.ArrowDown:return t.preventDefault(),e.goToOption(c.Next);case s.ArrowUp:return t.preventDefault(),e.goToOption(c.Previous);case s.Home:case s.PageUp:return t.preventDefault(),e.goToOption(c.First);case s.End:case s.PageDown:return t.preventDefault(),e.goToOption(c.Last);case s.Escape:t.preventDefault(),e.closeListbox(),n.nextTick((function(){var t;return null==(t=m(e.buttonRef))?void 0:t.focus({preventScroll:!0})}));break;case s.Tab:return t.preventDefault();default:1===t.key.length&&(e.search(t.key),o.value=setTimeout((function(){return e.clearSearch()}),350))}}}}}),C=n.defineComponent({name:"ListboxOption",props:{as:{type:[Object,String],default:"li"},value:{type:[Object,String],default:null},disabled:{type:Boolean,default:!1},class:{type:[String,Function],required:!1},className:{type:[String,Function],required:!1}},setup:function(e,t){var r=t.slots,l=t.attrs,i=k("ListboxOption"),a="headlessui-listbox-option-"+f(),s=e.disabled,d=e.className,p=void 0===d?e.class:d,v=e.value,h=n.computed((function(){return null!==i.activeOptionIndex.value&&i.options.value[i.activeOptionIndex.value].id===a})),x=n.computed((function(){return n.toRaw(i.value.value)===n.toRaw(v)})),S=n.ref({disabled:s,value:v,textValue:""});function g(e){if(s)return e.preventDefault();i.select(v),i.closeListbox(),n.nextTick((function(){var e;return null==(e=m(i.buttonRef))?void 0:e.focus({preventScroll:!0})}))}function w(){if(s)return i.goToOption(c.Nothing);i.goToOption(c.Specific,a)}function O(){s||h.value||i.goToOption(c.Specific,a)}function I(){s||h.value&&i.goToOption(c.Nothing)}return n.onMounted((function(){var e,t,n=null==(e=document.getElementById(a))||null==(t=e.textContent)?void 0:t.toLowerCase().trim();void 0!==n&&(S.value.textValue=n)})),n.onMounted((function(){return i.registerOption(a,S)})),n.onUnmounted((function(){return i.unregisterOption(a)})),n.onMounted((function(){n.watch([i.listboxState,x],(function(){var e;i.listboxState.value===y.Open&&x.value&&(i.goToOption(c.Specific,a),null==(e=document.getElementById(a))||null==e.focus||e.focus())}),{immediate:!0})})),n.watchEffect((function(){i.listboxState.value===y.Open&&h.value&&n.nextTick((function(){var e;return null==(e=document.getElementById(a))||null==e.scrollIntoView?void 0:e.scrollIntoView({block:"nearest"})}))})),function(){var t={active:h.value,selected:x.value,disabled:s},n={id:a,role:"option",tabIndex:-1,class:b(p,t),"aria-disabled":!0===s||void 0,"aria-selected":!0===x.value?x.value:void 0,onClick:g,onFocus:w,onPointerMove:O,onMouseMove:O,onPointerLeave:I,onMouseLeave:I};return u({props:o({},e,n),slot:t,attrs:l,slots:r})}}}),E=Symbol("GroupContext");function M(e){var t=n.inject(E,null);if(null===t){var o=new Error("<"+e+" /> is missing a parent <SwitchGroup /> component.");throw Error.captureStackTrace&&Error.captureStackTrace(o,M),o}return t}var j=n.defineComponent({name:"SwitchGroup",props:{as:{type:[Object,String],default:"template"}},setup:function(e,t){var o=t.slots,r=t.attrs,l=n.ref(null),i=n.ref(null),a=n.ref(null);return n.provide(E,{switchRef:l,labelRef:i,descriptionRef:a}),function(){return u({props:e,slot:{},slots:o,attrs:r})}}}),N=n.defineComponent({name:"Switch",emits:["update:modelValue"],props:{as:{type:[Object,String],default:"button"},modelValue:{type:[Object,Boolean],default:null},class:{type:[String,Function],required:!1},className:{type:[String,Function],required:!1}},render:function(){var e=n.inject(E,null),t=this.$props,r=t.className,l=void 0===r?t.class:r,i=n.computed((function(){var t;return null==(t=m(null==e?void 0:e.labelRef))?void 0:t.id})),a=n.computed((function(){var t;return null==(t=m(null==e?void 0:e.descriptionRef))?void 0:t.id})),s={checked:this.$props.modelValue},c={id:this.id,ref:null===e?void 0:e.switchRef,role:"switch",tabIndex:0,class:b(l,s),"aria-checked":this.$props.modelValue,"aria-labelledby":i.value,"aria-describedby":a.value,onClick:this.handleClick,onKeyUp:this.handleKeyUp,onKeyPress:this.handleKeyPress};return"button"===this.$props.as&&Object.assign(c,{type:"button"}),u({props:o({},this.$props,c),slot:s,attrs:this.$attrs,slots:this.$slots})},setup:function(e,t){var o=t.emit,r=n.inject(E,null);function l(){o("update:modelValue",!e.modelValue)}return{id:"headlessui-switch-"+f(),el:null==r?void 0:r.switchRef,handleClick:function(e){e.preventDefault(),l()},handleKeyUp:function(e){e.key!==s.Tab&&e.preventDefault(),e.key===s.Space&&l()},handleKeyPress:function(e){e.preventDefault()}}}}),A=n.defineComponent({name:"SwitchLabel",props:{as:{type:[Object,String],default:"label"}},render:function(){return u({props:o({},this.$props,{id:this.id,ref:"el",onClick:this.handleClick}),slot:{},attrs:this.$attrs,slots:this.$slots})},setup:function(){var e=M("SwitchLabel");return{id:"headlessui-switch-label-"+f(),el:e.labelRef,handleClick:function(){var t=m(e.switchRef);null==t||t.click(),null==t||t.focus({preventScroll:!0})}}}}),$=n.defineComponent({name:"SwitchDescription",props:{as:{type:[Object,String],default:"p"}},render:function(){return u({props:o({},this.$props,{id:this.id,ref:"el"}),slot:{},attrs:this.$attrs,slots:this.$slots})},setup:function(){var e=M("SwitchDescription");return{id:"headlessui-switch-description-"+f(),el:e.descriptionRef}}});exports.Listbox=R,exports.ListboxButton=D,exports.ListboxLabel=T,exports.ListboxOption=C,exports.ListboxOptions=L,exports.Menu=S,exports.MenuButton=g,exports.MenuItem=O,exports.MenuItems=w,exports.Switch=N,exports.SwitchDescription=$,exports.SwitchGroup=j,exports.SwitchLabel=A; | ||
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e,t,n=require("vue");function o(){return(o=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e}).apply(this,arguments)}function r(e,t){if(null==e)return{};var n,o,r={},l=Object.keys(e);for(o=0;o<l.length;o++)t.indexOf(n=l[o])>=0||(r[n]=e[n]);return r}function l(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=new Array(t);n<t;n++)o[n]=e[n];return o}function i(e,t){if(e in t){for(var n=t[e],o=arguments.length,r=new Array(o>2?o-2:0),l=2;l<o;l++)r[l-2]=arguments[l];return"function"==typeof n?n.apply(void 0,r):n}var u=new Error('Tried to handle "'+e+'" but there is no handler defined. Only defined handlers are: '+Object.keys(t).map((function(e){return'"'+e+'"'})).join(", ")+".");throw Error.captureStackTrace&&Error.captureStackTrace(u,i),u}function u(n){var l,u,s=n.visible,c=void 0===s||s,d=n.features,p=void 0===d?e.None:d,f=r(n,["visible","features"]);return c||p&e.Static&&f.props.static?a(f):p&e.RenderStrategy?i(null==(l=f.props.unmount)||l?t.Unmount:t.Hidden,((u={})[t.Unmount]=function(){return null},u[t.Hidden]=function(){return a(o({},f,{props:o({},f.props,{hidden:!0,style:{display:"none"}})}))},u)):a(f)}function a(e){var t=e.attrs,o=e.slots,i=e.slot,u=function(e,t){void 0===t&&(t=[]);for(var n,o=Object.assign({},e),r=function(e,t){var n;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return l(e,void 0);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?l(e,void 0):void 0}}(e))){n&&(e=n);var o=0;return function(){return o>=e.length?{done:!0}:{done:!1,value:e[o++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}return(n=e[Symbol.iterator]()).next.bind(n)}(t);!(n=r()).done;){var i=n.value;i in o&&delete o[i]}return o}(e.props,["unmount","static"]),a=u.as,s=r(u,["as"]),c=null==o.default?void 0:o.default(i);if("template"===a){if(Object.keys(s).length>0||"class"in t){var d=null!=c?c:[],p=d[0];if(d.slice(1).length>0)throw new Error('You should only render 1 child or use the `as="..."` prop');return n.cloneVNode(p,s)}return c}return n.h(a,s,c)}!function(e){e[e.None=0]="None",e[e.RenderStrategy=1]="RenderStrategy",e[e.Static=2]="Static"}(e||(e={})),function(e){e[e.Unmount=0]="Unmount",e[e.Hidden=1]="Hidden"}(t||(t={}));var s,c=n.defineComponent({name:"Alert",props:{as:{type:[Object,String],default:"div"},importance:{type:String,default:"polite"}},setup:function(e,t){var n=t.slots,l=t.attrs,a=e.importance,s=void 0===a?"polite":a,c=r(e,["importance"]),d={importance:s},p=i(s,{polite:function(){return{role:"status"}},assertive:function(){return{role:"alert"}}});return function(){return u({props:o({},c,p),slot:d,slots:n,attrs:l})}}});!function(e){e.Space=" ",e.Enter="Enter",e.Escape="Escape",e.Backspace="Backspace",e.ArrowUp="ArrowUp",e.ArrowDown="ArrowDown",e.Home="Home",e.End="End",e.PageUp="PageUp",e.PageDown="PageDown",e.Tab="Tab"}(s||(s={}));var d,p=0;function f(){return++p}function v(e){var t;return null==e||null==e.value?null:null!=(t=e.value.$el)?t:e.value}!function(e){e[e.Open=0]="Open",e[e.Closed=1]="Closed"}(d||(d={}));var m=Symbol("DisclosureContext");function b(e){var t=n.inject(m,null);if(null===t){var o=new Error("<"+e+" /> is missing a parent <Disclosure /> component.");throw Error.captureStackTrace&&Error.captureStackTrace(o,b),o}return t}var h,x,y=n.defineComponent({name:"Disclosure",props:{as:{type:[Object,String],default:"template"}},setup:function(e,t){var r=t.slots,l=t.attrs,a=o({},e),s=n.ref(d.Closed),c=n.ref(null);return n.provide(m,{disclosureState:s,panelRef:c,toggleDisclosure:function(){var e;s.value=i(s.value,((e={})[d.Open]=d.Closed,e[d.Closed]=d.Open,e))}}),function(){return u({props:a,slot:{open:s.value===d.Open},slots:r,attrs:l})}}}),S=n.defineComponent({name:"DisclosureButton",props:{as:{type:[Object,String],default:"button"},disabled:{type:[Boolean],default:!1}},render:function(){var e=b("DisclosureButton"),t={open:e.disclosureState.value===d.Open};return u({props:o({},this.$props,{id:this.id,ref:"el",type:"button","aria-expanded":e.disclosureState.value===d.Open||void 0,"aria-controls":this.ariaControls,onClick:this.handleClick,onKeydown:this.handleKeyDown}),slot:t,attrs:this.$attrs,slots:this.$slots})},setup:function(e){var t=b("DisclosureButton");return{id:"headlessui-disclosure-button-"+f(),ariaControls:n.computed((function(){var e,n;return null!=(e=null==(n=v(t.panelRef))?void 0:n.id)?e:void 0})),handleClick:function(){e.disabled||t.toggleDisclosure()},handleKeyDown:function(n){if(!e.disabled)switch(n.key){case s.Space:case s.Enter:n.preventDefault(),t.toggleDisclosure()}}}}}),g=n.defineComponent({name:"DisclosurePanel",props:{as:{type:[Object,String],default:"div"},static:{type:Boolean,default:!1},unmount:{type:Boolean,default:!0}},render:function(){var t={open:b("DisclosurePanel").disclosureState.value===d.Open};return u({props:o({},this.$props,{id:this.id,ref:"el"}),slot:t,attrs:this.$attrs,slots:this.$slots,features:e.RenderStrategy|e.Static,visible:t.open})},setup:function(){var e=b("DisclosurePanel");return{id:"headlessui-disclosure-panel-"+f(),el:e.panelRef}}});function w(e,t){var n=t.resolveItems();if(n.length<=0)return null;var o=t.resolveActiveIndex(),r=null!=o?o:-1,l=function(){switch(e.focus){case h.First:return n.findIndex((function(e){return!t.resolveDisabled(e)}));case h.Previous:var o=n.slice().reverse().findIndex((function(e,n,o){return!(-1!==r&&o.length-n-1>=r||t.resolveDisabled(e))}));return-1===o?o:n.length-1-o;case h.Next:return n.findIndex((function(e,n){return!(n<=r||t.resolveDisabled(e))}));case h.Last:var l=n.slice().reverse().findIndex((function(e){return!t.resolveDisabled(e)}));return-1===l?l:n.length-1-l;case h.Specific:return n.findIndex((function(n){return t.resolveId(n)===e.id}));case h.Nothing:return null;default:!function(e){throw new Error("Unexpected object: "+e)}(e)}}();return-1===l?o:l}function O(e,t){if(void 0!==e)return"function"==typeof e?e(t):e}!function(e){e[e.First=0]="First",e[e.Previous=1]="Previous",e[e.Next=2]="Next",e[e.Last=3]="Last",e[e.Specific=4]="Specific",e[e.Nothing=5]="Nothing"}(h||(h={})),function(e){e[e.Open=0]="Open",e[e.Closed=1]="Closed"}(x||(x={}));var I=Symbol("ListboxContext");function k(e){var t=n.inject(I,null);if(null===t){var o=new Error("<"+e+" /> is missing a parent <Listbox /> component.");throw Error.captureStackTrace&&Error.captureStackTrace(o,k),o}return t}var D,C=n.defineComponent({name:"Listbox",emits:["update:modelValue"],props:{as:{type:[Object,String],default:"template"},disabled:{type:[Boolean],default:!1},modelValue:{type:[Object,String,Number,Boolean],default:null}},setup:function(e,t){var o=t.slots,l=t.attrs,i=t.emit,a=e.disabled,s=r(e,["modelValue","disabled"]),c=n.ref(x.Closed),d=n.ref(null),p=n.ref(null),f=n.ref(null),m=n.ref([]),b=n.ref(""),y=n.ref(null),S=n.computed((function(){return e.modelValue})),g={listboxState:c,value:S,labelRef:d,buttonRef:p,optionsRef:f,disabled:a,options:m,searchQuery:b,activeOptionIndex:y,closeListbox:function(){a||c.value!==x.Closed&&(c.value=x.Closed,y.value=null)},openListbox:function(){a||c.value!==x.Open&&(c.value=x.Open)},goToOption:function(e,t){if(!a&&c.value!==x.Closed){var n=w(e===h.Specific?{focus:h.Specific,id:t}:{focus:e},{resolveItems:function(){return m.value},resolveActiveIndex:function(){return y.value},resolveId:function(e){return e.id},resolveDisabled:function(e){return e.dataRef.disabled}});""===b.value&&y.value===n||(b.value="",y.value=n)}},search:function(e){if(!a&&c.value!==x.Closed){b.value+=e;var t=m.value.findIndex((function(e){return!e.dataRef.disabled&&e.dataRef.textValue.startsWith(b.value)}));-1!==t&&t!==y.value&&(y.value=t)}},clearSearch:function(){a||c.value!==x.Closed&&""!==b.value&&(b.value="")},registerOption:function(e,t){m.value.push({id:e,dataRef:t})},unregisterOption:function(e){var t=m.value.slice(),n=null!==y.value?t[y.value]:null,o=t.findIndex((function(t){return t.id===e}));-1!==o&&t.splice(o,1),m.value=t,y.value=o===y.value||null===n?null:t.indexOf(n)},select:function(e){a||i("update:modelValue",e)}};return n.onMounted((function(){function e(e){var t,n,o,r=e.target,l=document.activeElement;c.value===x.Open&&((null==(t=v(p))?void 0:t.contains(r))||((null==(n=v(f))?void 0:n.contains(r))||g.closeListbox(),l!==document.body&&(null==l?void 0:l.contains(r))||e.defaultPrevented||null==(o=v(p))||o.focus({preventScroll:!0})))}window.addEventListener("mousedown",e),n.onUnmounted((function(){return window.removeEventListener("mousedown",e)}))})),n.provide(I,g),function(){return u({props:s,slot:{open:c.value===x.Open,disabled:a},slots:o,attrs:l})}}}),R=n.defineComponent({name:"ListboxLabel",props:{as:{type:[Object,String],default:"label"}},render:function(){var e=k("ListboxLabel"),t={open:e.listboxState.value===x.Open,disabled:e.disabled};return u({props:o({},this.$props,{id:this.id,ref:"el",onClick:this.handleClick}),slot:t,attrs:this.$attrs,slots:this.$slots})},setup:function(){var e=k("ListboxLabel");return{id:"headlessui-listbox-label-"+f(),el:e.labelRef,handleClick:function(){var t;null==(t=v(e.buttonRef))||t.focus({preventScroll:!0})}}}}),T=n.defineComponent({name:"ListboxButton",props:{as:{type:[Object,String],default:"button"}},render:function(){var e,t,n=k("ListboxButton"),r={open:n.listboxState.value===x.Open,disabled:n.disabled},l={ref:"el",id:this.id,type:"button","aria-haspopup":!0,"aria-controls":null==(e=v(n.optionsRef))?void 0:e.id,"aria-expanded":n.listboxState.value===x.Open||void 0,"aria-labelledby":n.labelRef.value?[null==(t=v(n.labelRef))?void 0:t.id,this.id].join(" "):void 0,disabled:n.disabled,onKeydown:this.handleKeyDown,onClick:this.handleClick};return u({props:o({},this.$props,l),slot:r,attrs:this.$attrs,slots:this.$slots})},setup:function(){var e=k("ListboxButton");return{id:"headlessui-listbox-button-"+f(),el:e.buttonRef,handleKeyDown:function(t){switch(t.key){case s.Space:case s.Enter:case s.ArrowDown:t.preventDefault(),e.openListbox(),n.nextTick((function(){var t;null==(t=v(e.optionsRef))||t.focus({preventScroll:!0}),e.value.value||e.goToOption(h.First)}));break;case s.ArrowUp:t.preventDefault(),e.openListbox(),n.nextTick((function(){var t;null==(t=v(e.optionsRef))||t.focus({preventScroll:!0}),e.value.value||e.goToOption(h.Last)}))}},handleClick:function(t){var o;e.disabled||(e.listboxState.value===x.Open?(e.closeListbox(),n.nextTick((function(){var t;return null==(t=v(e.buttonRef))?void 0:t.focus({preventScroll:!0})}))):(t.preventDefault(),e.openListbox(),o=function(){var t;return null==(t=v(e.optionsRef))?void 0:t.focus({preventScroll:!0})},requestAnimationFrame((function(){return requestAnimationFrame(o)}))))}}}}),E=n.defineComponent({name:"ListboxOptions",props:{as:{type:[Object,String],default:"ul"},static:{type:Boolean,default:!1},unmount:{type:Boolean,default:!0}},render:function(){var t,n,r,l,i=k("ListboxOptions"),a={open:i.listboxState.value===x.Open},s={"aria-activedescendant":null===i.activeOptionIndex.value||null==(t=i.options.value[i.activeOptionIndex.value])?void 0:t.id,"aria-labelledby":null!=(n=null==(r=v(i.labelRef))?void 0:r.id)?n:null==(l=v(i.buttonRef))?void 0:l.id,id:this.id,onKeydown:this.handleKeyDown,role:"listbox",tabIndex:0,ref:"el"};return u({props:o({},this.$props,s),slot:a,attrs:this.$attrs,slots:this.$slots,features:e.RenderStrategy|e.Static,visible:a.open})},setup:function(){var e=k("ListboxOptions"),t="headlessui-listbox-options-"+f(),o=n.ref(null);return{id:t,el:e.optionsRef,handleKeyDown:function(t){switch(o.value&&clearTimeout(o.value),t.key){case s.Space:if(""!==e.searchQuery.value)return t.preventDefault(),e.search(t.key);case s.Enter:t.preventDefault(),null!==e.activeOptionIndex.value&&e.select(e.options.value[e.activeOptionIndex.value].dataRef.value),e.closeListbox(),n.nextTick((function(){var t;return null==(t=v(e.buttonRef))?void 0:t.focus({preventScroll:!0})}));break;case s.ArrowDown:return t.preventDefault(),e.goToOption(h.Next);case s.ArrowUp:return t.preventDefault(),e.goToOption(h.Previous);case s.Home:case s.PageUp:return t.preventDefault(),e.goToOption(h.First);case s.End:case s.PageDown:return t.preventDefault(),e.goToOption(h.Last);case s.Escape:t.preventDefault(),e.closeListbox(),n.nextTick((function(){var t;return null==(t=v(e.buttonRef))?void 0:t.focus({preventScroll:!0})}));break;case s.Tab:return t.preventDefault();default:1===t.key.length&&(e.search(t.key),o.value=setTimeout((function(){return e.clearSearch()}),350))}}}}}),L=n.defineComponent({name:"ListboxOption",props:{as:{type:[Object,String],default:"li"},value:{type:[Object,String],default:null},disabled:{type:Boolean,default:!1},class:{type:[String,Function],required:!1},className:{type:[String,Function],required:!1}},setup:function(e,t){var r=t.slots,l=t.attrs,i=k("ListboxOption"),a="headlessui-listbox-option-"+f(),s=e.disabled,c=e.className,d=void 0===c?e.class:c,p=e.value,m=n.computed((function(){return null!==i.activeOptionIndex.value&&i.options.value[i.activeOptionIndex.value].id===a})),b=n.computed((function(){return n.toRaw(i.value.value)===n.toRaw(p)})),y=n.ref({disabled:s,value:p,textValue:""});function S(e){if(s)return e.preventDefault();i.select(p),i.closeListbox(),n.nextTick((function(){var e;return null==(e=v(i.buttonRef))?void 0:e.focus({preventScroll:!0})}))}function g(){if(s)return i.goToOption(h.Nothing);i.goToOption(h.Specific,a)}function w(){s||m.value||i.goToOption(h.Specific,a)}function I(){s||m.value&&i.goToOption(h.Nothing)}return n.onMounted((function(){var e,t,n=null==(e=document.getElementById(a))||null==(t=e.textContent)?void 0:t.toLowerCase().trim();void 0!==n&&(y.value.textValue=n)})),n.onMounted((function(){return i.registerOption(a,y)})),n.onUnmounted((function(){return i.unregisterOption(a)})),n.onMounted((function(){n.watch([i.listboxState,b],(function(){var e;i.listboxState.value===x.Open&&b.value&&(i.goToOption(h.Specific,a),null==(e=document.getElementById(a))||null==e.focus||e.focus())}),{immediate:!0})})),n.watchEffect((function(){i.listboxState.value===x.Open&&m.value&&n.nextTick((function(){var e;return null==(e=document.getElementById(a))||null==e.scrollIntoView?void 0:e.scrollIntoView({block:"nearest"})}))})),function(){var t={active:m.value,selected:b.value,disabled:s},n={id:a,role:"option",tabIndex:-1,class:O(d,t),"aria-disabled":!0===s||void 0,"aria-selected":!0===b.value?b.value:void 0,onClick:S,onFocus:g,onPointermove:w,onMousemove:w,onPointerleave:I,onMouseleave:I};return u({props:o({},e,n),slot:t,attrs:l,slots:r})}}});!function(e){e[e.Open=0]="Open",e[e.Closed=1]="Closed"}(D||(D={}));var j=Symbol("MenuContext");function $(e){var t=n.inject(j,null);if(null===t){var o=new Error("<"+e+" /> is missing a parent <Menu /> component.");throw Error.captureStackTrace&&Error.captureStackTrace(o,$),o}return t}var M=n.defineComponent({props:{as:{type:[Object,String],default:"template"}},setup:function(e,t){var o=t.slots,r=t.attrs,l=n.ref(D.Closed),i=n.ref(null),a=n.ref(null),s=n.ref([]),c=n.ref(""),d=n.ref(null),p={menuState:l,buttonRef:i,itemsRef:a,items:s,searchQuery:c,activeItemIndex:d,closeMenu:function(){l.value=D.Closed,d.value=null},openMenu:function(){return l.value=D.Open},goToItem:function(e,t){var n=w(e===h.Specific?{focus:h.Specific,id:t}:{focus:e},{resolveItems:function(){return s.value},resolveActiveIndex:function(){return d.value},resolveId:function(e){return e.id},resolveDisabled:function(e){return e.dataRef.disabled}});""===c.value&&d.value===n||(c.value="",d.value=n)},search:function(e){c.value+=e;var t=s.value.findIndex((function(e){return e.dataRef.textValue.startsWith(c.value)&&!e.dataRef.disabled}));-1!==t&&t!==d.value&&(d.value=t)},clearSearch:function(){c.value=""},registerItem:function(e,t){s.value.push({id:e,dataRef:t})},unregisterItem:function(e){var t=s.value.slice(),n=null!==d.value?t[d.value]:null,o=t.findIndex((function(t){return t.id===e}));-1!==o&&t.splice(o,1),s.value=t,d.value=o===d.value||null===n?null:t.indexOf(n)}};return n.onMounted((function(){function e(e){var t,n,o,r=e.target,u=document.activeElement;l.value===D.Open&&((null==(t=v(i))?void 0:t.contains(r))||((null==(n=v(a))?void 0:n.contains(r))||p.closeMenu(),u!==document.body&&(null==u?void 0:u.contains(r))||e.defaultPrevented||null==(o=v(i))||o.focus({preventScroll:!0})))}window.addEventListener("mousedown",e),n.onUnmounted((function(){return window.removeEventListener("mousedown",e)}))})),n.provide(j,p),function(){return u({props:e,slot:{open:l.value===D.Open},slots:o,attrs:r})}}}),A=n.defineComponent({props:{disabled:{type:Boolean,default:!1},as:{type:[Object,String],default:"button"}},render:function(){var e,t=$("MenuButton"),n={open:t.menuState.value===D.Open},r={ref:"el",id:this.id,type:"button","aria-haspopup":!0,"aria-controls":null==(e=v(t.itemsRef))?void 0:e.id,"aria-expanded":t.menuState.value===D.Open||void 0,onKeydown:this.handleKeyDown,onClick:this.handleClick};return u({props:o({},this.$props,r),slot:n,attrs:this.$attrs,slots:this.$slots})},setup:function(e){var t=$("MenuButton");return{id:"headlessui-menu-button-"+f(),el:t.buttonRef,handleKeyDown:function(e){switch(e.key){case s.Space:case s.Enter:case s.ArrowDown:e.preventDefault(),t.openMenu(),n.nextTick((function(){var e;null==(e=v(t.itemsRef))||e.focus({preventScroll:!0}),t.goToItem(h.First)}));break;case s.ArrowUp:e.preventDefault(),t.openMenu(),n.nextTick((function(){var e;null==(e=v(t.itemsRef))||e.focus({preventScroll:!0}),t.goToItem(h.Last)}))}},handleClick:function(o){var r;e.disabled||(t.menuState.value===D.Open?(t.closeMenu(),n.nextTick((function(){var e;return null==(e=v(t.buttonRef))?void 0:e.focus({preventScroll:!0})}))):(o.preventDefault(),t.openMenu(),r=function(){var e;return null==(e=v(t.itemsRef))?void 0:e.focus({preventScroll:!0})},requestAnimationFrame((function(){return requestAnimationFrame(r)}))))}}}}),B=n.defineComponent({props:{as:{type:[Object,String],default:"div"},static:{type:Boolean,default:!1},unmount:{type:Boolean,default:!0}},render:function(){var t,n,r=$("MenuItems"),l={open:r.menuState.value===D.Open},i={"aria-activedescendant":null===r.activeItemIndex.value||null==(t=r.items.value[r.activeItemIndex.value])?void 0:t.id,"aria-labelledby":null==(n=v(r.buttonRef))?void 0:n.id,id:this.id,onKeydown:this.handleKeyDown,role:"menu",tabIndex:0,ref:"el"};return u({props:o({},this.$props,i),slot:l,attrs:this.$attrs,slots:this.$slots,features:e.RenderStrategy|e.Static,visible:l.open})},setup:function(){var e=$("MenuItems"),t="headlessui-menu-items-"+f(),o=n.ref(null);return n.watchEffect((function(){var t=v(e.itemsRef);if(t&&e.menuState.value===D.Open)for(var n=document.createTreeWalker(t,NodeFilter.SHOW_ELEMENT,{acceptNode:function(e){return"menuitem"===e.getAttribute("role")?NodeFilter.FILTER_REJECT:e.hasAttribute("role")?NodeFilter.FILTER_SKIP:NodeFilter.FILTER_ACCEPT}});n.nextNode();)n.currentNode.setAttribute("role","none")})),{id:t,el:e.itemsRef,handleKeyDown:function(t){switch(o.value&&clearTimeout(o.value),t.key){case s.Space:if(""!==e.searchQuery.value)return t.preventDefault(),e.search(t.key);case s.Enter:var r;t.preventDefault(),null!==e.activeItemIndex.value&&(null==(r=document.getElementById(e.items.value[e.activeItemIndex.value].id))||r.click()),e.closeMenu(),n.nextTick((function(){var t;return null==(t=v(e.buttonRef))?void 0:t.focus({preventScroll:!0})}));break;case s.ArrowDown:return t.preventDefault(),e.goToItem(h.Next);case s.ArrowUp:return t.preventDefault(),e.goToItem(h.Previous);case s.Home:case s.PageUp:return t.preventDefault(),e.goToItem(h.First);case s.End:case s.PageDown:return t.preventDefault(),e.goToItem(h.Last);case s.Escape:t.preventDefault(),e.closeMenu(),n.nextTick((function(){var t;return null==(t=v(e.buttonRef))?void 0:t.focus({preventScroll:!0})}));break;case s.Tab:return t.preventDefault();default:1===t.key.length&&(e.search(t.key),o.value=setTimeout((function(){return e.clearSearch()}),350))}}}}}),N=n.defineComponent({props:{as:{type:[Object,String],default:"template"},disabled:{type:Boolean,default:!1},class:{type:[String,Function],required:!1},className:{type:[String,Function],required:!1}},setup:function(e,t){var r=t.slots,l=t.attrs,i=$("MenuItem"),a="headlessui-menu-item-"+f(),s=e.disabled,c=e.className,d=void 0===c?e.class:c,p=n.computed((function(){return null!==i.activeItemIndex.value&&i.items.value[i.activeItemIndex.value].id===a})),m=n.ref({disabled:s,textValue:""});function b(e){if(s)return e.preventDefault();i.closeMenu(),n.nextTick((function(){var e;return null==(e=v(i.buttonRef))?void 0:e.focus({preventScroll:!0})}))}function x(){if(s)return i.goToItem(h.Nothing);i.goToItem(h.Specific,a)}function y(){s||p.value||i.goToItem(h.Specific,a)}function S(){s||p.value&&i.goToItem(h.Nothing)}return n.onMounted((function(){var e,t,n=null==(e=document.getElementById(a))||null==(t=e.textContent)?void 0:t.toLowerCase().trim();void 0!==n&&(m.value.textValue=n)})),n.onMounted((function(){return i.registerItem(a,m)})),n.onUnmounted((function(){return i.unregisterItem(a)})),n.watchEffect((function(){i.menuState.value===D.Open&&p.value&&n.nextTick((function(){var e;return null==(e=document.getElementById(a))||null==e.scrollIntoView?void 0:e.scrollIntoView({block:"nearest"})}))})),function(){var t={active:p.value,disabled:s},n={id:a,role:"menuitem",tabIndex:-1,class:O(d,t),"aria-disabled":!0===s||void 0,onClick:b,onFocus:x,onPointermove:y,onMousemove:y,onPointerleave:S,onMouseleave:S};return u({props:o({},e,n),slot:t,attrs:l,slots:r})}}}),P=Symbol("GroupContext");function F(e){var t=n.inject(P,null);if(null===t){var o=new Error("<"+e+" /> is missing a parent <SwitchGroup /> component.");throw Error.captureStackTrace&&Error.captureStackTrace(o,F),o}return t}var K=n.defineComponent({name:"SwitchGroup",props:{as:{type:[Object,String],default:"template"}},setup:function(e,t){var o=t.slots,r=t.attrs,l=n.ref(null),i=n.ref(null),a=n.ref(null);return n.provide(P,{switchRef:l,labelRef:i,descriptionRef:a}),function(){return u({props:e,slot:{},slots:o,attrs:r})}}}),U=n.defineComponent({name:"Switch",emits:["update:modelValue"],props:{as:{type:[Object,String],default:"button"},modelValue:{type:[Object,Boolean],default:null},class:{type:[String,Function],required:!1},className:{type:[String,Function],required:!1}},render:function(){var e=n.inject(P,null),t=this.$props,r=t.className,l=void 0===r?t.class:r,i=n.computed((function(){var t;return null==(t=v(null==e?void 0:e.labelRef))?void 0:t.id})),a=n.computed((function(){var t;return null==(t=v(null==e?void 0:e.descriptionRef))?void 0:t.id})),s={checked:this.$props.modelValue},c={id:this.id,ref:null===e?void 0:e.switchRef,role:"switch",tabIndex:0,class:O(l,s),"aria-checked":this.$props.modelValue,"aria-labelledby":i.value,"aria-describedby":a.value,onClick:this.handleClick,onKeyup:this.handleKeyUp,onKeypress:this.handleKeyPress};return"button"===this.$props.as&&Object.assign(c,{type:"button"}),u({props:o({},this.$props,c),slot:s,attrs:this.$attrs,slots:this.$slots})},setup:function(e,t){var o=t.emit,r=n.inject(P,null);function l(){o("update:modelValue",!e.modelValue)}return{id:"headlessui-switch-"+f(),el:null==r?void 0:r.switchRef,handleClick:function(e){e.preventDefault(),l()},handleKeyUp:function(e){e.key!==s.Tab&&e.preventDefault(),e.key===s.Space&&l()},handleKeyPress:function(e){e.preventDefault()}}}}),V=n.defineComponent({name:"SwitchLabel",props:{as:{type:[Object,String],default:"label"}},render:function(){return u({props:o({},this.$props,{id:this.id,ref:"el",onClick:this.handleClick}),slot:{},attrs:this.$attrs,slots:this.$slots})},setup:function(){var e=F("SwitchLabel");return{id:"headlessui-switch-label-"+f(),el:e.labelRef,handleClick:function(){var t=v(e.switchRef);null==t||t.click(),null==t||t.focus({preventScroll:!0})}}}}),q=n.defineComponent({name:"SwitchDescription",props:{as:{type:[Object,String],default:"p"}},render:function(){return u({props:o({},this.$props,{id:this.id,ref:"el"}),slot:{},attrs:this.$attrs,slots:this.$slots})},setup:function(){var e=F("SwitchDescription");return{id:"headlessui-switch-description-"+f(),el:e.descriptionRef}}});exports.Alert=c,exports.Disclosure=y,exports.DisclosureButton=S,exports.DisclosurePanel=g,exports.Listbox=C,exports.ListboxButton=T,exports.ListboxLabel=R,exports.ListboxOption=L,exports.ListboxOptions=E,exports.Menu=M,exports.MenuButton=A,exports.MenuItem=N,exports.MenuItems=B,exports.Switch=U,exports.SwitchDescription=q,exports.SwitchGroup=K,exports.SwitchLabel=V; | ||
//# sourceMappingURL=headlessui.cjs.production.min.js.map |
@@ -1,2 +0,2 @@ | ||
import { cloneVNode, h, defineComponent, ref, onMounted, onUnmounted, provide, watchEffect, computed, nextTick, inject, toRaw, watch } from 'vue'; | ||
import { cloneVNode, h, defineComponent, ref, provide, computed, inject, onMounted, onUnmounted, toRaw, watch, watchEffect, nextTick } from 'vue'; | ||
@@ -205,11 +205,47 @@ function _extends() { | ||
var id = 0; | ||
var Alert = /*#__PURE__*/defineComponent({ | ||
name: 'Alert', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'div' | ||
}, | ||
importance: { | ||
type: String, | ||
"default": 'polite' | ||
} | ||
}, | ||
setup: function setup(props, _ref) { | ||
var slots = _ref.slots, | ||
attrs = _ref.attrs; | ||
function generateId() { | ||
return ++id; | ||
} | ||
var _props$importance = props.importance, | ||
importance = _props$importance === void 0 ? 'polite' : _props$importance, | ||
passThroughProps = _objectWithoutPropertiesLoose(props, ["importance"]); | ||
function useId() { | ||
return generateId(); | ||
} | ||
var slot = { | ||
importance: importance | ||
}; | ||
var propsWeControl = match(importance, { | ||
polite: function polite() { | ||
return { | ||
role: 'status' | ||
}; | ||
}, | ||
assertive: function assertive() { | ||
return { | ||
role: 'alert' | ||
}; | ||
} | ||
}); | ||
return function () { | ||
return render({ | ||
props: _extends({}, passThroughProps, propsWeControl), | ||
slot: slot, | ||
slots: slots, | ||
attrs: attrs | ||
}); | ||
}; | ||
} | ||
}); | ||
@@ -234,2 +270,189 @@ // TODO: This must already exist somewhere, right? 🤔 | ||
var id = 0; | ||
function generateId() { | ||
return ++id; | ||
} | ||
function useId() { | ||
return generateId(); | ||
} | ||
function dom(ref) { | ||
var _ref$value$$el; | ||
if (ref == null) return null; | ||
if (ref.value == null) return null; | ||
return (_ref$value$$el = ref.value.$el) != null ? _ref$value$$el : ref.value; | ||
} | ||
var DisclosureStates; | ||
(function (DisclosureStates) { | ||
DisclosureStates[DisclosureStates["Open"] = 0] = "Open"; | ||
DisclosureStates[DisclosureStates["Closed"] = 1] = "Closed"; | ||
})(DisclosureStates || (DisclosureStates = {})); | ||
var DisclosureContext = /*#__PURE__*/Symbol('DisclosureContext'); | ||
function useDisclosureContext(component) { | ||
var context = inject(DisclosureContext, null); | ||
if (context === null) { | ||
var err = new Error("<" + component + " /> is missing a parent <Disclosure /> component."); | ||
if (Error.captureStackTrace) Error.captureStackTrace(err, useDisclosureContext); | ||
throw err; | ||
} | ||
return context; | ||
} // --- | ||
var Disclosure = /*#__PURE__*/defineComponent({ | ||
name: 'Disclosure', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'template' | ||
} | ||
}, | ||
setup: function setup(props, _ref) { | ||
var slots = _ref.slots, | ||
attrs = _ref.attrs; | ||
var passThroughProps = _extends({}, props); | ||
var disclosureState = ref(DisclosureStates.Closed); | ||
var panelRef = ref(null); | ||
var api = { | ||
disclosureState: disclosureState, | ||
panelRef: panelRef, | ||
toggleDisclosure: function toggleDisclosure() { | ||
var _match; | ||
disclosureState.value = match(disclosureState.value, (_match = {}, _match[DisclosureStates.Open] = DisclosureStates.Closed, _match[DisclosureStates.Closed] = DisclosureStates.Open, _match)); | ||
} | ||
}; | ||
provide(DisclosureContext, api); | ||
return function () { | ||
var slot = { | ||
open: disclosureState.value === DisclosureStates.Open | ||
}; | ||
return render({ | ||
props: passThroughProps, | ||
slot: slot, | ||
slots: slots, | ||
attrs: attrs | ||
}); | ||
}; | ||
} | ||
}); // --- | ||
var DisclosureButton = /*#__PURE__*/defineComponent({ | ||
name: 'DisclosureButton', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'button' | ||
}, | ||
disabled: { | ||
type: [Boolean], | ||
"default": false | ||
} | ||
}, | ||
render: function render$1() { | ||
var api = useDisclosureContext('DisclosureButton'); | ||
var slot = { | ||
open: api.disclosureState.value === DisclosureStates.Open | ||
}; | ||
var propsWeControl = { | ||
id: this.id, | ||
ref: 'el', | ||
type: 'button', | ||
'aria-expanded': api.disclosureState.value === DisclosureStates.Open ? true : undefined, | ||
'aria-controls': this.ariaControls, | ||
onClick: this.handleClick, | ||
onKeydown: this.handleKeyDown | ||
}; | ||
return render({ | ||
props: _extends({}, this.$props, propsWeControl), | ||
slot: slot, | ||
attrs: this.$attrs, | ||
slots: this.$slots | ||
}); | ||
}, | ||
setup: function setup(props) { | ||
var api = useDisclosureContext('DisclosureButton'); | ||
var buttonId = "headlessui-disclosure-button-" + useId(); | ||
var ariaControls = computed(function () { | ||
var _dom$id, _dom; | ||
return (_dom$id = (_dom = dom(api.panelRef)) == null ? void 0 : _dom.id) != null ? _dom$id : undefined; | ||
}); | ||
return { | ||
id: buttonId, | ||
ariaControls: ariaControls, | ||
handleClick: function handleClick() { | ||
if (props.disabled) return; | ||
api.toggleDisclosure(); | ||
}, | ||
handleKeyDown: function handleKeyDown(event) { | ||
if (props.disabled) return; | ||
switch (event.key) { | ||
case Keys.Space: | ||
case Keys.Enter: | ||
event.preventDefault(); | ||
api.toggleDisclosure(); | ||
break; | ||
} | ||
} | ||
}; | ||
} | ||
}); // --- | ||
var DisclosurePanel = /*#__PURE__*/defineComponent({ | ||
name: 'DisclosurePanel', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'div' | ||
}, | ||
"static": { | ||
type: Boolean, | ||
"default": false | ||
}, | ||
unmount: { | ||
type: Boolean, | ||
"default": true | ||
} | ||
}, | ||
render: function render$1() { | ||
var api = useDisclosureContext('DisclosurePanel'); | ||
var slot = { | ||
open: api.disclosureState.value === DisclosureStates.Open | ||
}; | ||
var propsWeControl = { | ||
id: this.id, | ||
ref: 'el' | ||
}; | ||
return render({ | ||
props: _extends({}, this.$props, propsWeControl), | ||
slot: slot, | ||
attrs: this.$attrs, | ||
slots: this.$slots, | ||
features: Features.RenderStrategy | Features.Static, | ||
visible: slot.open | ||
}); | ||
}, | ||
setup: function setup() { | ||
var api = useDisclosureContext('DisclosurePanel'); | ||
var panelId = "headlessui-disclosure-panel-" + useId(); | ||
return { | ||
id: panelId, | ||
el: api.panelRef | ||
}; | ||
} | ||
}); | ||
function assertNever(x) { | ||
@@ -322,17 +545,9 @@ throw new Error('Unexpected object: ' + x); | ||
function dom(ref) { | ||
var _ref$value$$el; | ||
var ListboxStates; | ||
if (ref == null) return null; | ||
if (ref.value == null) return null; | ||
return (_ref$value$$el = ref.value.$el) != null ? _ref$value$$el : ref.value; | ||
} | ||
(function (ListboxStates) { | ||
ListboxStates[ListboxStates["Open"] = 0] = "Open"; | ||
ListboxStates[ListboxStates["Closed"] = 1] = "Closed"; | ||
})(ListboxStates || (ListboxStates = {})); | ||
var MenuStates; | ||
(function (MenuStates) { | ||
MenuStates[MenuStates["Open"] = 0] = "Open"; | ||
MenuStates[MenuStates["Closed"] = 1] = "Closed"; | ||
})(MenuStates || (MenuStates = {})); | ||
function nextFrame(cb) { | ||
@@ -344,10 +559,10 @@ requestAnimationFrame(function () { | ||
var MenuContext = /*#__PURE__*/Symbol('MenuContext'); | ||
var ListboxContext = /*#__PURE__*/Symbol('ListboxContext'); | ||
function useMenuContext(component) { | ||
var context = inject(MenuContext, null); | ||
function useListboxContext(component) { | ||
var context = inject(ListboxContext, null); | ||
if (context === null) { | ||
var err = new Error("<" + component + " /> is missing a parent <Menu /> component."); | ||
if (Error.captureStackTrace) Error.captureStackTrace(err, useMenuContext); | ||
var err = new Error("<" + component + " /> is missing a parent <Listbox /> component."); | ||
if (Error.captureStackTrace) Error.captureStackTrace(err, useListboxContext); | ||
throw err; | ||
@@ -357,5 +572,8 @@ } | ||
return context; | ||
} | ||
} // --- | ||
var Menu = /*#__PURE__*/defineComponent({ | ||
var Listbox = /*#__PURE__*/defineComponent({ | ||
name: 'Listbox', | ||
emits: ['update:modelValue'], | ||
props: { | ||
@@ -365,2 +583,10 @@ as: { | ||
"default": 'template' | ||
}, | ||
disabled: { | ||
type: [Boolean], | ||
"default": false | ||
}, | ||
modelValue: { | ||
type: [Object, String, Number, Boolean], | ||
"default": null | ||
} | ||
@@ -370,25 +596,43 @@ }, | ||
var slots = _ref.slots, | ||
attrs = _ref.attrs; | ||
var menuState = ref(MenuStates.Closed); | ||
attrs = _ref.attrs, | ||
emit = _ref.emit; | ||
var disabled = props.disabled, | ||
passThroughProps = _objectWithoutPropertiesLoose(props, ["modelValue", "disabled"]); | ||
var listboxState = ref(ListboxStates.Closed); | ||
var labelRef = ref(null); | ||
var buttonRef = ref(null); | ||
var itemsRef = ref(null); | ||
var items = ref([]); | ||
var optionsRef = ref(null); | ||
var options = ref([]); | ||
var searchQuery = ref(''); | ||
var activeItemIndex = ref(null); | ||
var activeOptionIndex = ref(null); | ||
var value = computed(function () { | ||
return props.modelValue; | ||
}); | ||
var api = { | ||
menuState: menuState, | ||
listboxState: listboxState, | ||
value: value, | ||
labelRef: labelRef, | ||
buttonRef: buttonRef, | ||
itemsRef: itemsRef, | ||
items: items, | ||
optionsRef: optionsRef, | ||
disabled: disabled, | ||
options: options, | ||
searchQuery: searchQuery, | ||
activeItemIndex: activeItemIndex, | ||
closeMenu: function closeMenu() { | ||
menuState.value = MenuStates.Closed; | ||
activeItemIndex.value = null; | ||
activeOptionIndex: activeOptionIndex, | ||
closeListbox: function closeListbox() { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
listboxState.value = ListboxStates.Closed; | ||
activeOptionIndex.value = null; | ||
}, | ||
openMenu: function openMenu() { | ||
return menuState.value = MenuStates.Open; | ||
openListbox: function openListbox() { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Open) return; | ||
listboxState.value = ListboxStates.Open; | ||
}, | ||
goToItem: function goToItem(focus, id) { | ||
var nextActiveItemIndex = calculateActiveIndex(focus === Focus.Specific ? { | ||
goToOption: function goToOption(focus, id) { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
var nextActiveOptionIndex = calculateActiveIndex(focus === Focus.Specific ? { | ||
focus: Focus.Specific, | ||
@@ -400,32 +644,37 @@ id: id | ||
resolveItems: function resolveItems() { | ||
return items.value; | ||
return options.value; | ||
}, | ||
resolveActiveIndex: function resolveActiveIndex() { | ||
return activeItemIndex.value; | ||
return activeOptionIndex.value; | ||
}, | ||
resolveId: function resolveId(item) { | ||
return item.id; | ||
resolveId: function resolveId(option) { | ||
return option.id; | ||
}, | ||
resolveDisabled: function resolveDisabled(item) { | ||
return item.dataRef.disabled; | ||
resolveDisabled: function resolveDisabled(option) { | ||
return option.dataRef.disabled; | ||
} | ||
}); | ||
if (searchQuery.value === '' && activeItemIndex.value === nextActiveItemIndex) return; | ||
if (searchQuery.value === '' && activeOptionIndex.value === nextActiveOptionIndex) return; | ||
searchQuery.value = ''; | ||
activeItemIndex.value = nextActiveItemIndex; | ||
activeOptionIndex.value = nextActiveOptionIndex; | ||
}, | ||
search: function search(value) { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
searchQuery.value += value; | ||
var match = items.value.findIndex(function (item) { | ||
return item.dataRef.textValue.startsWith(searchQuery.value) && !item.dataRef.disabled; | ||
var match = options.value.findIndex(function (option) { | ||
return !option.dataRef.disabled && option.dataRef.textValue.startsWith(searchQuery.value); | ||
}); | ||
if (match === -1 || match === activeItemIndex.value) return; | ||
activeItemIndex.value = match; | ||
if (match === -1 || match === activeOptionIndex.value) return; | ||
activeOptionIndex.value = match; | ||
}, | ||
clearSearch: function clearSearch() { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
if (searchQuery.value === '') return; | ||
searchQuery.value = ''; | ||
}, | ||
registerItem: function registerItem(id, dataRef) { | ||
registerOption: function registerOption(id, dataRef) { | ||
// @ts-expect-error The expected type comes from property 'dataRef' which is declared here on type '{ id: string; dataRef: { textValue: string; disabled: boolean; }; }' | ||
items.value.push({ | ||
options.value.push({ | ||
id: id, | ||
@@ -435,18 +684,22 @@ dataRef: dataRef | ||
}, | ||
unregisterItem: function unregisterItem(id) { | ||
var nextItems = items.value.slice(); | ||
var currentActiveItem = activeItemIndex.value !== null ? nextItems[activeItemIndex.value] : null; | ||
var idx = nextItems.findIndex(function (a) { | ||
unregisterOption: function unregisterOption(id) { | ||
var nextOptions = options.value.slice(); | ||
var currentActiveOption = activeOptionIndex.value !== null ? nextOptions[activeOptionIndex.value] : null; | ||
var idx = nextOptions.findIndex(function (a) { | ||
return a.id === id; | ||
}); | ||
if (idx !== -1) nextItems.splice(idx, 1); | ||
items.value = nextItems; | ||
if (idx !== -1) nextOptions.splice(idx, 1); | ||
options.value = nextOptions; | ||
activeItemIndex.value = function () { | ||
if (idx === activeItemIndex.value) return null; | ||
if (currentActiveItem === null) return null; // If we removed the item before the actual active index, then it would be out of sync. To | ||
activeOptionIndex.value = function () { | ||
if (idx === activeOptionIndex.value) return null; | ||
if (currentActiveOption === null) return null; // If we removed the option before the actual active index, then it would be out of sync. To | ||
// fix this, we will find the correct (new) index position. | ||
return nextItems.indexOf(currentActiveItem); | ||
return nextOptions.indexOf(currentActiveOption); | ||
}(); | ||
}, | ||
select: function select(value) { | ||
if (disabled) return; | ||
emit('update:modelValue', value); | ||
} | ||
@@ -460,5 +713,5 @@ }; | ||
var active = document.activeElement; | ||
if (menuState.value !== MenuStates.Open) return; | ||
if (listboxState.value !== ListboxStates.Open) return; | ||
if ((_dom = dom(buttonRef)) == null ? void 0 : _dom.contains(target)) return; | ||
if (!((_dom2 = dom(itemsRef)) == null ? void 0 : _dom2.contains(target))) api.closeMenu(); | ||
if (!((_dom2 = dom(optionsRef)) == null ? void 0 : _dom2.contains(target))) api.closeListbox(); | ||
if (active !== document.body && (active == null ? void 0 : active.contains(target))) return; // Keep focus on newly clicked/focused element | ||
@@ -477,9 +730,10 @@ | ||
provide(MenuContext, api); | ||
provide(ListboxContext, api); | ||
return function () { | ||
var slot = { | ||
open: menuState.value === MenuStates.Open | ||
open: listboxState.value === ListboxStates.Open, | ||
disabled: disabled | ||
}; | ||
return render({ | ||
props: props, | ||
props: passThroughProps, | ||
slot: slot, | ||
@@ -491,11 +745,52 @@ slots: slots, | ||
} | ||
}); | ||
var MenuButton = /*#__PURE__*/defineComponent({ | ||
}); // --- | ||
var ListboxLabel = /*#__PURE__*/defineComponent({ | ||
name: 'ListboxLabel', | ||
props: { | ||
disabled: { | ||
type: Boolean, | ||
"default": false | ||
}, | ||
as: { | ||
type: [Object, String], | ||
"default": 'label' | ||
} | ||
}, | ||
render: function render$1() { | ||
var api = useListboxContext('ListboxLabel'); | ||
var slot = { | ||
open: api.listboxState.value === ListboxStates.Open, | ||
disabled: api.disabled | ||
}; | ||
var propsWeControl = { | ||
id: this.id, | ||
ref: 'el', | ||
onClick: this.handleClick | ||
}; | ||
return render({ | ||
props: _extends({}, this.$props, propsWeControl), | ||
slot: slot, | ||
attrs: this.$attrs, | ||
slots: this.$slots | ||
}); | ||
}, | ||
setup: function setup() { | ||
var api = useListboxContext('ListboxLabel'); | ||
var id = "headlessui-listbox-label-" + useId(); | ||
return { | ||
id: id, | ||
el: api.labelRef, | ||
handleClick: function handleClick() { | ||
var _dom4; | ||
(_dom4 = dom(api.buttonRef)) == null ? void 0 : _dom4.focus({ | ||
preventScroll: true | ||
}); | ||
} | ||
}; | ||
} | ||
}); // --- | ||
var ListboxButton = /*#__PURE__*/defineComponent({ | ||
name: 'ListboxButton', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'button' | ||
@@ -505,7 +800,8 @@ } | ||
render: function render$1() { | ||
var _dom4; | ||
var _dom5, _dom6; | ||
var api = useMenuContext('MenuButton'); | ||
var api = useListboxContext('ListboxButton'); | ||
var slot = { | ||
open: api.menuState.value === MenuStates.Open | ||
open: api.listboxState.value === ListboxStates.Open, | ||
disabled: api.disabled | ||
}; | ||
@@ -517,5 +813,7 @@ var propsWeControl = { | ||
'aria-haspopup': true, | ||
'aria-controls': (_dom4 = dom(api.itemsRef)) == null ? void 0 : _dom4.id, | ||
'aria-expanded': api.menuState.value === MenuStates.Open ? true : undefined, | ||
onKeyDown: this.handleKeyDown, | ||
'aria-controls': (_dom5 = dom(api.optionsRef)) == null ? void 0 : _dom5.id, | ||
'aria-expanded': api.listboxState.value === ListboxStates.Open ? true : undefined, | ||
'aria-labelledby': api.labelRef.value ? [(_dom6 = dom(api.labelRef)) == null ? void 0 : _dom6.id, this.id].join(' ') : undefined, | ||
disabled: api.disabled, | ||
onKeydown: this.handleKeyDown, | ||
onClick: this.handleClick | ||
@@ -530,5 +828,5 @@ }; | ||
}, | ||
setup: function setup(props) { | ||
var api = useMenuContext('MenuButton'); | ||
var id = "headlessui-menu-button-" + useId(); | ||
setup: function setup() { | ||
var api = useListboxContext('ListboxButton'); | ||
var id = "headlessui-listbox-button-" + useId(); | ||
@@ -542,10 +840,10 @@ function handleKeyDown(event) { | ||
event.preventDefault(); | ||
api.openMenu(); | ||
api.openListbox(); | ||
nextTick(function () { | ||
var _dom5; | ||
var _dom7; | ||
(_dom5 = dom(api.itemsRef)) == null ? void 0 : _dom5.focus({ | ||
(_dom7 = dom(api.optionsRef)) == null ? void 0 : _dom7.focus({ | ||
preventScroll: true | ||
}); | ||
api.goToItem(Focus.First); | ||
if (!api.value.value) api.goToOption(Focus.First); | ||
}); | ||
@@ -556,10 +854,10 @@ break; | ||
event.preventDefault(); | ||
api.openMenu(); | ||
api.openListbox(); | ||
nextTick(function () { | ||
var _dom6; | ||
var _dom8; | ||
(_dom6 = dom(api.itemsRef)) == null ? void 0 : _dom6.focus({ | ||
(_dom8 = dom(api.optionsRef)) == null ? void 0 : _dom8.focus({ | ||
preventScroll: true | ||
}); | ||
api.goToItem(Focus.Last); | ||
if (!api.value.value) api.goToOption(Focus.Last); | ||
}); | ||
@@ -571,10 +869,10 @@ break; | ||
function handleClick(event) { | ||
if (props.disabled) return; | ||
if (api.disabled) return; | ||
if (api.menuState.value === MenuStates.Open) { | ||
api.closeMenu(); | ||
if (api.listboxState.value === ListboxStates.Open) { | ||
api.closeListbox(); | ||
nextTick(function () { | ||
var _dom7; | ||
var _dom9; | ||
return (_dom7 = dom(api.buttonRef)) == null ? void 0 : _dom7.focus({ | ||
return (_dom9 = dom(api.buttonRef)) == null ? void 0 : _dom9.focus({ | ||
preventScroll: true | ||
@@ -585,7 +883,7 @@ }); | ||
event.preventDefault(); | ||
api.openMenu(); | ||
api.openListbox(); | ||
nextFrame(function () { | ||
var _dom8; | ||
var _dom10; | ||
return (_dom8 = dom(api.itemsRef)) == null ? void 0 : _dom8.focus({ | ||
return (_dom10 = dom(api.optionsRef)) == null ? void 0 : _dom10.focus({ | ||
preventScroll: true | ||
@@ -604,8 +902,10 @@ }); | ||
} | ||
}); | ||
var MenuItems = /*#__PURE__*/defineComponent({ | ||
}); // --- | ||
var ListboxOptions = /*#__PURE__*/defineComponent({ | ||
name: 'ListboxOptions', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'div' | ||
"default": 'ul' | ||
}, | ||
@@ -622,14 +922,14 @@ "static": { | ||
render: function render$1() { | ||
var _api$items$value$api$, _dom9; | ||
var _api$options$value$ap, _dom$id, _dom11, _dom12; | ||
var api = useMenuContext('MenuItems'); | ||
var api = useListboxContext('ListboxOptions'); | ||
var slot = { | ||
open: api.menuState.value === MenuStates.Open | ||
open: api.listboxState.value === ListboxStates.Open | ||
}; | ||
var propsWeControl = { | ||
'aria-activedescendant': api.activeItemIndex.value === null ? undefined : (_api$items$value$api$ = api.items.value[api.activeItemIndex.value]) == null ? void 0 : _api$items$value$api$.id, | ||
'aria-labelledby': (_dom9 = dom(api.buttonRef)) == null ? void 0 : _dom9.id, | ||
'aria-activedescendant': api.activeOptionIndex.value === null ? undefined : (_api$options$value$ap = api.options.value[api.activeOptionIndex.value]) == null ? void 0 : _api$options$value$ap.id, | ||
'aria-labelledby': (_dom$id = (_dom11 = dom(api.labelRef)) == null ? void 0 : _dom11.id) != null ? _dom$id : (_dom12 = dom(api.buttonRef)) == null ? void 0 : _dom12.id, | ||
id: this.id, | ||
onKeyDown: this.handleKeyDown, | ||
role: 'menu', | ||
onKeydown: this.handleKeyDown, | ||
role: 'listbox', | ||
tabIndex: 0, | ||
@@ -649,22 +949,6 @@ ref: 'el' | ||
setup: function setup() { | ||
var api = useMenuContext('MenuItems'); | ||
var id = "headlessui-menu-items-" + useId(); | ||
var api = useListboxContext('ListboxOptions'); | ||
var id = "headlessui-listbox-options-" + useId(); | ||
var searchDebounce = ref(null); | ||
watchEffect(function () { | ||
var container = dom(api.itemsRef); | ||
if (!container) return; | ||
if (api.menuState.value !== MenuStates.Open) return; | ||
var walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, { | ||
acceptNode: function acceptNode(node) { | ||
if (node.getAttribute('role') === 'menuitem') return NodeFilter.FILTER_REJECT; | ||
if (node.hasAttribute('role')) return NodeFilter.FILTER_SKIP; | ||
return NodeFilter.FILTER_ACCEPT; | ||
} | ||
}); | ||
while (walker.nextNode()) { | ||
walker.currentNode.setAttribute('role', 'none'); | ||
} | ||
}); | ||
function handleKeyDown(event) { | ||
@@ -687,14 +971,12 @@ if (searchDebounce.value) clearTimeout(searchDebounce.value); | ||
if (api.activeItemIndex.value !== null) { | ||
var _document$getElementB; | ||
var _id = api.items.value[api.activeItemIndex.value].id; | ||
(_document$getElementB = document.getElementById(_id)) == null ? void 0 : _document$getElementB.click(); | ||
if (api.activeOptionIndex.value !== null) { | ||
var dataRef = api.options.value[api.activeOptionIndex.value].dataRef; | ||
api.select(dataRef.value); | ||
} | ||
api.closeMenu(); | ||
api.closeListbox(); | ||
nextTick(function () { | ||
var _dom10; | ||
var _dom13; | ||
return (_dom10 = dom(api.buttonRef)) == null ? void 0 : _dom10.focus({ | ||
return (_dom13 = dom(api.buttonRef)) == null ? void 0 : _dom13.focus({ | ||
preventScroll: true | ||
@@ -707,7 +989,7 @@ }); | ||
event.preventDefault(); | ||
return api.goToItem(Focus.Next); | ||
return api.goToOption(Focus.Next); | ||
case Keys.ArrowUp: | ||
event.preventDefault(); | ||
return api.goToItem(Focus.Previous); | ||
return api.goToOption(Focus.Previous); | ||
@@ -717,3 +999,3 @@ case Keys.Home: | ||
event.preventDefault(); | ||
return api.goToItem(Focus.First); | ||
return api.goToOption(Focus.First); | ||
@@ -723,11 +1005,11 @@ case Keys.End: | ||
event.preventDefault(); | ||
return api.goToItem(Focus.Last); | ||
return api.goToOption(Focus.Last); | ||
case Keys.Escape: | ||
event.preventDefault(); | ||
api.closeMenu(); | ||
api.closeListbox(); | ||
nextTick(function () { | ||
var _dom11; | ||
var _dom14; | ||
return (_dom11 = dom(api.buttonRef)) == null ? void 0 : _dom11.focus({ | ||
return (_dom14 = dom(api.buttonRef)) == null ? void 0 : _dom14.focus({ | ||
preventScroll: true | ||
@@ -755,3 +1037,3 @@ }); | ||
id: id, | ||
el: api.itemsRef, | ||
el: api.optionsRef, | ||
handleKeyDown: handleKeyDown | ||
@@ -761,8 +1043,13 @@ }; | ||
}); | ||
var MenuItem = /*#__PURE__*/defineComponent({ | ||
var ListboxOption = /*#__PURE__*/defineComponent({ | ||
name: 'ListboxOption', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'template' | ||
"default": 'li' | ||
}, | ||
value: { | ||
type: [Object, String], | ||
"default": null | ||
}, | ||
disabled: { | ||
@@ -784,29 +1071,46 @@ type: Boolean, | ||
attrs = _ref2.attrs; | ||
var api = useMenuContext('MenuItem'); | ||
var id = "headlessui-menu-item-" + useId(); | ||
var api = useListboxContext('ListboxOption'); | ||
var id = "headlessui-listbox-option-" + useId(); | ||
var disabled = props.disabled, | ||
defaultClass = props["class"], | ||
_props$className = props.className, | ||
className = _props$className === void 0 ? defaultClass : _props$className; | ||
className = _props$className === void 0 ? defaultClass : _props$className, | ||
value = props.value; | ||
var active = computed(function () { | ||
return api.activeItemIndex.value !== null ? api.items.value[api.activeItemIndex.value].id === id : false; | ||
return api.activeOptionIndex.value !== null ? api.options.value[api.activeOptionIndex.value].id === id : false; | ||
}); | ||
var selected = computed(function () { | ||
return toRaw(api.value.value) === toRaw(value); | ||
}); | ||
var dataRef = ref({ | ||
disabled: disabled, | ||
value: value, | ||
textValue: '' | ||
}); | ||
onMounted(function () { | ||
var _document$getElementB2, _document$getElementB3; | ||
var _document$getElementB, _document$getElementB2; | ||
var textValue = (_document$getElementB2 = document.getElementById(id)) == null ? void 0 : (_document$getElementB3 = _document$getElementB2.textContent) == null ? void 0 : _document$getElementB3.toLowerCase().trim(); | ||
var textValue = (_document$getElementB = document.getElementById(id)) == null ? void 0 : (_document$getElementB2 = _document$getElementB.textContent) == null ? void 0 : _document$getElementB2.toLowerCase().trim(); | ||
if (textValue !== undefined) dataRef.value.textValue = textValue; | ||
}); | ||
onMounted(function () { | ||
return api.registerItem(id, dataRef); | ||
return api.registerOption(id, dataRef); | ||
}); | ||
onUnmounted(function () { | ||
return api.unregisterItem(id); | ||
return api.unregisterOption(id); | ||
}); | ||
onMounted(function () { | ||
watch([api.listboxState, selected], function () { | ||
var _document$getElementB3; | ||
if (api.listboxState.value !== ListboxStates.Open) return; | ||
if (!selected.value) return; | ||
api.goToOption(Focus.Specific, id); | ||
(_document$getElementB3 = document.getElementById(id)) == null ? void 0 : _document$getElementB3.focus == null ? void 0 : _document$getElementB3.focus(); | ||
}, { | ||
immediate: true | ||
}); | ||
}); | ||
watchEffect(function () { | ||
if (api.menuState.value !== MenuStates.Open) return; | ||
if (api.listboxState.value !== ListboxStates.Open) return; | ||
if (!active.value) return; | ||
@@ -824,7 +1128,8 @@ nextTick(function () { | ||
if (disabled) return event.preventDefault(); | ||
api.closeMenu(); | ||
api.select(value); | ||
api.closeListbox(); | ||
nextTick(function () { | ||
var _dom12; | ||
var _dom15; | ||
return (_dom12 = dom(api.buttonRef)) == null ? void 0 : _dom12.focus({ | ||
return (_dom15 = dom(api.buttonRef)) == null ? void 0 : _dom15.focus({ | ||
preventScroll: true | ||
@@ -836,4 +1141,4 @@ }); | ||
function handleFocus() { | ||
if (disabled) return api.goToItem(Focus.Nothing); | ||
api.goToItem(Focus.Specific, id); | ||
if (disabled) return api.goToOption(Focus.Nothing); | ||
api.goToOption(Focus.Specific, id); | ||
} | ||
@@ -844,3 +1149,3 @@ | ||
if (active.value) return; | ||
api.goToItem(Focus.Specific, id); | ||
api.goToOption(Focus.Specific, id); | ||
} | ||
@@ -851,3 +1156,3 @@ | ||
if (!active.value) return; | ||
api.goToItem(Focus.Nothing); | ||
api.goToOption(Focus.Nothing); | ||
} | ||
@@ -858,2 +1163,3 @@ | ||
active: active.value, | ||
selected: selected.value, | ||
disabled: disabled | ||
@@ -863,12 +1169,13 @@ }; | ||
id: id, | ||
role: 'menuitem', | ||
role: 'option', | ||
tabIndex: -1, | ||
"class": resolvePropValue(className, slot), | ||
'aria-disabled': disabled === true ? true : undefined, | ||
'aria-selected': selected.value === true ? selected.value : undefined, | ||
onClick: handleClick, | ||
onFocus: handleFocus, | ||
onPointerMove: handleMove, | ||
onMouseMove: handleMove, | ||
onPointerLeave: handleLeave, | ||
onMouseLeave: handleLeave | ||
onPointermove: handleMove, | ||
onMousemove: handleMove, | ||
onPointerleave: handleLeave, | ||
onMouseleave: handleLeave | ||
}; | ||
@@ -885,8 +1192,8 @@ return render({ | ||
var ListboxStates; | ||
var MenuStates; | ||
(function (ListboxStates) { | ||
ListboxStates[ListboxStates["Open"] = 0] = "Open"; | ||
ListboxStates[ListboxStates["Closed"] = 1] = "Closed"; | ||
})(ListboxStates || (ListboxStates = {})); | ||
(function (MenuStates) { | ||
MenuStates[MenuStates["Open"] = 0] = "Open"; | ||
MenuStates[MenuStates["Closed"] = 1] = "Closed"; | ||
})(MenuStates || (MenuStates = {})); | ||
@@ -899,10 +1206,10 @@ function nextFrame$1(cb) { | ||
var ListboxContext = /*#__PURE__*/Symbol('ListboxContext'); | ||
var MenuContext = /*#__PURE__*/Symbol('MenuContext'); | ||
function useListboxContext(component) { | ||
var context = inject(ListboxContext, null); | ||
function useMenuContext(component) { | ||
var context = inject(MenuContext, null); | ||
if (context === null) { | ||
var err = new Error("<" + component + " /> is missing a parent <Listbox /> component."); | ||
if (Error.captureStackTrace) Error.captureStackTrace(err, useListboxContext); | ||
var err = new Error("<" + component + " /> is missing a parent <Menu /> component."); | ||
if (Error.captureStackTrace) Error.captureStackTrace(err, useMenuContext); | ||
throw err; | ||
@@ -912,8 +1219,5 @@ } | ||
return context; | ||
} // --- | ||
} | ||
var Listbox = /*#__PURE__*/defineComponent({ | ||
name: 'Listbox', | ||
emits: ['update:modelValue'], | ||
var Menu = /*#__PURE__*/defineComponent({ | ||
props: { | ||
@@ -923,10 +1227,2 @@ as: { | ||
"default": 'template' | ||
}, | ||
disabled: { | ||
type: [Boolean], | ||
"default": false | ||
}, | ||
modelValue: { | ||
type: [Object, String, Number, Boolean], | ||
"default": null | ||
} | ||
@@ -936,43 +1232,25 @@ }, | ||
var slots = _ref.slots, | ||
attrs = _ref.attrs, | ||
emit = _ref.emit; | ||
var disabled = props.disabled, | ||
passThroughProps = _objectWithoutPropertiesLoose(props, ["modelValue", "disabled"]); | ||
var listboxState = ref(ListboxStates.Closed); | ||
var labelRef = ref(null); | ||
attrs = _ref.attrs; | ||
var menuState = ref(MenuStates.Closed); | ||
var buttonRef = ref(null); | ||
var optionsRef = ref(null); | ||
var options = ref([]); | ||
var itemsRef = ref(null); | ||
var items = ref([]); | ||
var searchQuery = ref(''); | ||
var activeOptionIndex = ref(null); | ||
var value = computed(function () { | ||
return props.modelValue; | ||
}); | ||
var activeItemIndex = ref(null); | ||
var api = { | ||
listboxState: listboxState, | ||
value: value, | ||
labelRef: labelRef, | ||
menuState: menuState, | ||
buttonRef: buttonRef, | ||
optionsRef: optionsRef, | ||
disabled: disabled, | ||
options: options, | ||
itemsRef: itemsRef, | ||
items: items, | ||
searchQuery: searchQuery, | ||
activeOptionIndex: activeOptionIndex, | ||
closeListbox: function closeListbox() { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
listboxState.value = ListboxStates.Closed; | ||
activeOptionIndex.value = null; | ||
activeItemIndex: activeItemIndex, | ||
closeMenu: function closeMenu() { | ||
menuState.value = MenuStates.Closed; | ||
activeItemIndex.value = null; | ||
}, | ||
openListbox: function openListbox() { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Open) return; | ||
listboxState.value = ListboxStates.Open; | ||
openMenu: function openMenu() { | ||
return menuState.value = MenuStates.Open; | ||
}, | ||
goToOption: function goToOption(focus, id) { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
var nextActiveOptionIndex = calculateActiveIndex(focus === Focus.Specific ? { | ||
goToItem: function goToItem(focus, id) { | ||
var nextActiveItemIndex = calculateActiveIndex(focus === Focus.Specific ? { | ||
focus: Focus.Specific, | ||
@@ -984,37 +1262,32 @@ id: id | ||
resolveItems: function resolveItems() { | ||
return options.value; | ||
return items.value; | ||
}, | ||
resolveActiveIndex: function resolveActiveIndex() { | ||
return activeOptionIndex.value; | ||
return activeItemIndex.value; | ||
}, | ||
resolveId: function resolveId(option) { | ||
return option.id; | ||
resolveId: function resolveId(item) { | ||
return item.id; | ||
}, | ||
resolveDisabled: function resolveDisabled(option) { | ||
return option.dataRef.disabled; | ||
resolveDisabled: function resolveDisabled(item) { | ||
return item.dataRef.disabled; | ||
} | ||
}); | ||
if (searchQuery.value === '' && activeOptionIndex.value === nextActiveOptionIndex) return; | ||
if (searchQuery.value === '' && activeItemIndex.value === nextActiveItemIndex) return; | ||
searchQuery.value = ''; | ||
activeOptionIndex.value = nextActiveOptionIndex; | ||
activeItemIndex.value = nextActiveItemIndex; | ||
}, | ||
search: function search(value) { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
searchQuery.value += value; | ||
var match = options.value.findIndex(function (option) { | ||
return !option.dataRef.disabled && option.dataRef.textValue.startsWith(searchQuery.value); | ||
var match = items.value.findIndex(function (item) { | ||
return item.dataRef.textValue.startsWith(searchQuery.value) && !item.dataRef.disabled; | ||
}); | ||
if (match === -1 || match === activeOptionIndex.value) return; | ||
activeOptionIndex.value = match; | ||
if (match === -1 || match === activeItemIndex.value) return; | ||
activeItemIndex.value = match; | ||
}, | ||
clearSearch: function clearSearch() { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
if (searchQuery.value === '') return; | ||
searchQuery.value = ''; | ||
}, | ||
registerOption: function registerOption(id, dataRef) { | ||
registerItem: function registerItem(id, dataRef) { | ||
// @ts-expect-error The expected type comes from property 'dataRef' which is declared here on type '{ id: string; dataRef: { textValue: string; disabled: boolean; }; }' | ||
options.value.push({ | ||
items.value.push({ | ||
id: id, | ||
@@ -1024,22 +1297,18 @@ dataRef: dataRef | ||
}, | ||
unregisterOption: function unregisterOption(id) { | ||
var nextOptions = options.value.slice(); | ||
var currentActiveOption = activeOptionIndex.value !== null ? nextOptions[activeOptionIndex.value] : null; | ||
var idx = nextOptions.findIndex(function (a) { | ||
unregisterItem: function unregisterItem(id) { | ||
var nextItems = items.value.slice(); | ||
var currentActiveItem = activeItemIndex.value !== null ? nextItems[activeItemIndex.value] : null; | ||
var idx = nextItems.findIndex(function (a) { | ||
return a.id === id; | ||
}); | ||
if (idx !== -1) nextOptions.splice(idx, 1); | ||
options.value = nextOptions; | ||
if (idx !== -1) nextItems.splice(idx, 1); | ||
items.value = nextItems; | ||
activeOptionIndex.value = function () { | ||
if (idx === activeOptionIndex.value) return null; | ||
if (currentActiveOption === null) return null; // If we removed the option before the actual active index, then it would be out of sync. To | ||
activeItemIndex.value = function () { | ||
if (idx === activeItemIndex.value) return null; | ||
if (currentActiveItem === null) return null; // If we removed the item before the actual active index, then it would be out of sync. To | ||
// fix this, we will find the correct (new) index position. | ||
return nextOptions.indexOf(currentActiveOption); | ||
return nextItems.indexOf(currentActiveItem); | ||
}(); | ||
}, | ||
select: function select(value) { | ||
if (disabled) return; | ||
emit('update:modelValue', value); | ||
} | ||
@@ -1053,5 +1322,5 @@ }; | ||
var active = document.activeElement; | ||
if (listboxState.value !== ListboxStates.Open) return; | ||
if (menuState.value !== MenuStates.Open) return; | ||
if ((_dom = dom(buttonRef)) == null ? void 0 : _dom.contains(target)) return; | ||
if (!((_dom2 = dom(optionsRef)) == null ? void 0 : _dom2.contains(target))) api.closeListbox(); | ||
if (!((_dom2 = dom(itemsRef)) == null ? void 0 : _dom2.contains(target))) api.closeMenu(); | ||
if (active !== document.body && (active == null ? void 0 : active.contains(target))) return; // Keep focus on newly clicked/focused element | ||
@@ -1070,10 +1339,9 @@ | ||
provide(ListboxContext, api); | ||
provide(MenuContext, api); | ||
return function () { | ||
var slot = { | ||
open: listboxState.value === ListboxStates.Open, | ||
disabled: disabled | ||
open: menuState.value === MenuStates.Open | ||
}; | ||
return render({ | ||
props: passThroughProps, | ||
props: props, | ||
slot: slot, | ||
@@ -1085,52 +1353,11 @@ slots: slots, | ||
} | ||
}); // --- | ||
var ListboxLabel = /*#__PURE__*/defineComponent({ | ||
name: 'ListboxLabel', | ||
}); | ||
var MenuButton = /*#__PURE__*/defineComponent({ | ||
props: { | ||
disabled: { | ||
type: Boolean, | ||
"default": false | ||
}, | ||
as: { | ||
type: [Object, String], | ||
"default": 'label' | ||
} | ||
}, | ||
render: function render$1() { | ||
var api = useListboxContext('ListboxLabel'); | ||
var slot = { | ||
open: api.listboxState.value === ListboxStates.Open, | ||
disabled: api.disabled | ||
}; | ||
var propsWeControl = { | ||
id: this.id, | ||
ref: 'el', | ||
onClick: this.handleClick | ||
}; | ||
return render({ | ||
props: _extends({}, this.$props, propsWeControl), | ||
slot: slot, | ||
attrs: this.$attrs, | ||
slots: this.$slots | ||
}); | ||
}, | ||
setup: function setup() { | ||
var api = useListboxContext('ListboxLabel'); | ||
var id = "headlessui-listbox-label-" + useId(); | ||
return { | ||
id: id, | ||
el: api.labelRef, | ||
handleClick: function handleClick() { | ||
var _dom4; | ||
(_dom4 = dom(api.buttonRef)) == null ? void 0 : _dom4.focus({ | ||
preventScroll: true | ||
}); | ||
} | ||
}; | ||
} | ||
}); // --- | ||
var ListboxButton = /*#__PURE__*/defineComponent({ | ||
name: 'ListboxButton', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'button' | ||
@@ -1140,8 +1367,7 @@ } | ||
render: function render$1() { | ||
var _dom5, _dom6; | ||
var _dom4; | ||
var api = useListboxContext('ListboxButton'); | ||
var api = useMenuContext('MenuButton'); | ||
var slot = { | ||
open: api.listboxState.value === ListboxStates.Open, | ||
disabled: api.disabled | ||
open: api.menuState.value === MenuStates.Open | ||
}; | ||
@@ -1153,7 +1379,5 @@ var propsWeControl = { | ||
'aria-haspopup': true, | ||
'aria-controls': (_dom5 = dom(api.optionsRef)) == null ? void 0 : _dom5.id, | ||
'aria-expanded': api.listboxState.value === ListboxStates.Open ? true : undefined, | ||
'aria-labelledby': api.labelRef.value ? [(_dom6 = dom(api.labelRef)) == null ? void 0 : _dom6.id, this.id].join(' ') : undefined, | ||
disabled: api.disabled, | ||
onKeyDown: this.handleKeyDown, | ||
'aria-controls': (_dom4 = dom(api.itemsRef)) == null ? void 0 : _dom4.id, | ||
'aria-expanded': api.menuState.value === MenuStates.Open ? true : undefined, | ||
onKeydown: this.handleKeyDown, | ||
onClick: this.handleClick | ||
@@ -1168,5 +1392,5 @@ }; | ||
}, | ||
setup: function setup() { | ||
var api = useListboxContext('ListboxButton'); | ||
var id = "headlessui-listbox-button-" + useId(); | ||
setup: function setup(props) { | ||
var api = useMenuContext('MenuButton'); | ||
var id = "headlessui-menu-button-" + useId(); | ||
@@ -1180,10 +1404,10 @@ function handleKeyDown(event) { | ||
event.preventDefault(); | ||
api.openListbox(); | ||
api.openMenu(); | ||
nextTick(function () { | ||
var _dom7; | ||
var _dom5; | ||
(_dom7 = dom(api.optionsRef)) == null ? void 0 : _dom7.focus({ | ||
(_dom5 = dom(api.itemsRef)) == null ? void 0 : _dom5.focus({ | ||
preventScroll: true | ||
}); | ||
if (!api.value.value) api.goToOption(Focus.First); | ||
api.goToItem(Focus.First); | ||
}); | ||
@@ -1194,10 +1418,10 @@ break; | ||
event.preventDefault(); | ||
api.openListbox(); | ||
api.openMenu(); | ||
nextTick(function () { | ||
var _dom8; | ||
var _dom6; | ||
(_dom8 = dom(api.optionsRef)) == null ? void 0 : _dom8.focus({ | ||
(_dom6 = dom(api.itemsRef)) == null ? void 0 : _dom6.focus({ | ||
preventScroll: true | ||
}); | ||
if (!api.value.value) api.goToOption(Focus.Last); | ||
api.goToItem(Focus.Last); | ||
}); | ||
@@ -1209,10 +1433,10 @@ break; | ||
function handleClick(event) { | ||
if (api.disabled) return; | ||
if (props.disabled) return; | ||
if (api.listboxState.value === ListboxStates.Open) { | ||
api.closeListbox(); | ||
if (api.menuState.value === MenuStates.Open) { | ||
api.closeMenu(); | ||
nextTick(function () { | ||
var _dom9; | ||
var _dom7; | ||
return (_dom9 = dom(api.buttonRef)) == null ? void 0 : _dom9.focus({ | ||
return (_dom7 = dom(api.buttonRef)) == null ? void 0 : _dom7.focus({ | ||
preventScroll: true | ||
@@ -1223,7 +1447,7 @@ }); | ||
event.preventDefault(); | ||
api.openListbox(); | ||
api.openMenu(); | ||
nextFrame$1(function () { | ||
var _dom10; | ||
var _dom8; | ||
return (_dom10 = dom(api.optionsRef)) == null ? void 0 : _dom10.focus({ | ||
return (_dom8 = dom(api.itemsRef)) == null ? void 0 : _dom8.focus({ | ||
preventScroll: true | ||
@@ -1242,10 +1466,8 @@ }); | ||
} | ||
}); // --- | ||
var ListboxOptions = /*#__PURE__*/defineComponent({ | ||
name: 'ListboxOptions', | ||
}); | ||
var MenuItems = /*#__PURE__*/defineComponent({ | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'ul' | ||
"default": 'div' | ||
}, | ||
@@ -1262,14 +1484,14 @@ "static": { | ||
render: function render$1() { | ||
var _api$options$value$ap, _dom$id, _dom11, _dom12; | ||
var _api$items$value$api$, _dom9; | ||
var api = useListboxContext('ListboxOptions'); | ||
var api = useMenuContext('MenuItems'); | ||
var slot = { | ||
open: api.listboxState.value === ListboxStates.Open | ||
open: api.menuState.value === MenuStates.Open | ||
}; | ||
var propsWeControl = { | ||
'aria-activedescendant': api.activeOptionIndex.value === null ? undefined : (_api$options$value$ap = api.options.value[api.activeOptionIndex.value]) == null ? void 0 : _api$options$value$ap.id, | ||
'aria-labelledby': (_dom$id = (_dom11 = dom(api.labelRef)) == null ? void 0 : _dom11.id) != null ? _dom$id : (_dom12 = dom(api.buttonRef)) == null ? void 0 : _dom12.id, | ||
'aria-activedescendant': api.activeItemIndex.value === null ? undefined : (_api$items$value$api$ = api.items.value[api.activeItemIndex.value]) == null ? void 0 : _api$items$value$api$.id, | ||
'aria-labelledby': (_dom9 = dom(api.buttonRef)) == null ? void 0 : _dom9.id, | ||
id: this.id, | ||
onKeyDown: this.handleKeyDown, | ||
role: 'listbox', | ||
onKeydown: this.handleKeyDown, | ||
role: 'menu', | ||
tabIndex: 0, | ||
@@ -1289,6 +1511,22 @@ ref: 'el' | ||
setup: function setup() { | ||
var api = useListboxContext('ListboxOptions'); | ||
var id = "headlessui-listbox-options-" + useId(); | ||
var api = useMenuContext('MenuItems'); | ||
var id = "headlessui-menu-items-" + useId(); | ||
var searchDebounce = ref(null); | ||
watchEffect(function () { | ||
var container = dom(api.itemsRef); | ||
if (!container) return; | ||
if (api.menuState.value !== MenuStates.Open) return; | ||
var walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, { | ||
acceptNode: function acceptNode(node) { | ||
if (node.getAttribute('role') === 'menuitem') return NodeFilter.FILTER_REJECT; | ||
if (node.hasAttribute('role')) return NodeFilter.FILTER_SKIP; | ||
return NodeFilter.FILTER_ACCEPT; | ||
} | ||
}); | ||
while (walker.nextNode()) { | ||
walker.currentNode.setAttribute('role', 'none'); | ||
} | ||
}); | ||
function handleKeyDown(event) { | ||
@@ -1311,12 +1549,14 @@ if (searchDebounce.value) clearTimeout(searchDebounce.value); | ||
if (api.activeOptionIndex.value !== null) { | ||
var dataRef = api.options.value[api.activeOptionIndex.value].dataRef; | ||
api.select(dataRef.value); | ||
if (api.activeItemIndex.value !== null) { | ||
var _document$getElementB; | ||
var _id = api.items.value[api.activeItemIndex.value].id; | ||
(_document$getElementB = document.getElementById(_id)) == null ? void 0 : _document$getElementB.click(); | ||
} | ||
api.closeListbox(); | ||
api.closeMenu(); | ||
nextTick(function () { | ||
var _dom13; | ||
var _dom10; | ||
return (_dom13 = dom(api.buttonRef)) == null ? void 0 : _dom13.focus({ | ||
return (_dom10 = dom(api.buttonRef)) == null ? void 0 : _dom10.focus({ | ||
preventScroll: true | ||
@@ -1329,7 +1569,7 @@ }); | ||
event.preventDefault(); | ||
return api.goToOption(Focus.Next); | ||
return api.goToItem(Focus.Next); | ||
case Keys.ArrowUp: | ||
event.preventDefault(); | ||
return api.goToOption(Focus.Previous); | ||
return api.goToItem(Focus.Previous); | ||
@@ -1339,3 +1579,3 @@ case Keys.Home: | ||
event.preventDefault(); | ||
return api.goToOption(Focus.First); | ||
return api.goToItem(Focus.First); | ||
@@ -1345,11 +1585,11 @@ case Keys.End: | ||
event.preventDefault(); | ||
return api.goToOption(Focus.Last); | ||
return api.goToItem(Focus.Last); | ||
case Keys.Escape: | ||
event.preventDefault(); | ||
api.closeListbox(); | ||
api.closeMenu(); | ||
nextTick(function () { | ||
var _dom14; | ||
var _dom11; | ||
return (_dom14 = dom(api.buttonRef)) == null ? void 0 : _dom14.focus({ | ||
return (_dom11 = dom(api.buttonRef)) == null ? void 0 : _dom11.focus({ | ||
preventScroll: true | ||
@@ -1377,3 +1617,3 @@ }); | ||
id: id, | ||
el: api.optionsRef, | ||
el: api.itemsRef, | ||
handleKeyDown: handleKeyDown | ||
@@ -1383,13 +1623,8 @@ }; | ||
}); | ||
var ListboxOption = /*#__PURE__*/defineComponent({ | ||
name: 'ListboxOption', | ||
var MenuItem = /*#__PURE__*/defineComponent({ | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'li' | ||
"default": 'template' | ||
}, | ||
value: { | ||
type: [Object, String], | ||
"default": null | ||
}, | ||
disabled: { | ||
@@ -1411,46 +1646,29 @@ type: Boolean, | ||
attrs = _ref2.attrs; | ||
var api = useListboxContext('ListboxOption'); | ||
var id = "headlessui-listbox-option-" + useId(); | ||
var api = useMenuContext('MenuItem'); | ||
var id = "headlessui-menu-item-" + useId(); | ||
var disabled = props.disabled, | ||
defaultClass = props["class"], | ||
_props$className = props.className, | ||
className = _props$className === void 0 ? defaultClass : _props$className, | ||
value = props.value; | ||
className = _props$className === void 0 ? defaultClass : _props$className; | ||
var active = computed(function () { | ||
return api.activeOptionIndex.value !== null ? api.options.value[api.activeOptionIndex.value].id === id : false; | ||
return api.activeItemIndex.value !== null ? api.items.value[api.activeItemIndex.value].id === id : false; | ||
}); | ||
var selected = computed(function () { | ||
return toRaw(api.value.value) === toRaw(value); | ||
}); | ||
var dataRef = ref({ | ||
disabled: disabled, | ||
value: value, | ||
textValue: '' | ||
}); | ||
onMounted(function () { | ||
var _document$getElementB, _document$getElementB2; | ||
var _document$getElementB2, _document$getElementB3; | ||
var textValue = (_document$getElementB = document.getElementById(id)) == null ? void 0 : (_document$getElementB2 = _document$getElementB.textContent) == null ? void 0 : _document$getElementB2.toLowerCase().trim(); | ||
var textValue = (_document$getElementB2 = document.getElementById(id)) == null ? void 0 : (_document$getElementB3 = _document$getElementB2.textContent) == null ? void 0 : _document$getElementB3.toLowerCase().trim(); | ||
if (textValue !== undefined) dataRef.value.textValue = textValue; | ||
}); | ||
onMounted(function () { | ||
return api.registerOption(id, dataRef); | ||
return api.registerItem(id, dataRef); | ||
}); | ||
onUnmounted(function () { | ||
return api.unregisterOption(id); | ||
return api.unregisterItem(id); | ||
}); | ||
onMounted(function () { | ||
watch([api.listboxState, selected], function () { | ||
var _document$getElementB3; | ||
if (api.listboxState.value !== ListboxStates.Open) return; | ||
if (!selected.value) return; | ||
api.goToOption(Focus.Specific, id); | ||
(_document$getElementB3 = document.getElementById(id)) == null ? void 0 : _document$getElementB3.focus == null ? void 0 : _document$getElementB3.focus(); | ||
}, { | ||
immediate: true | ||
}); | ||
}); | ||
watchEffect(function () { | ||
if (api.listboxState.value !== ListboxStates.Open) return; | ||
if (api.menuState.value !== MenuStates.Open) return; | ||
if (!active.value) return; | ||
@@ -1468,8 +1686,7 @@ nextTick(function () { | ||
if (disabled) return event.preventDefault(); | ||
api.select(value); | ||
api.closeListbox(); | ||
api.closeMenu(); | ||
nextTick(function () { | ||
var _dom15; | ||
var _dom12; | ||
return (_dom15 = dom(api.buttonRef)) == null ? void 0 : _dom15.focus({ | ||
return (_dom12 = dom(api.buttonRef)) == null ? void 0 : _dom12.focus({ | ||
preventScroll: true | ||
@@ -1481,4 +1698,4 @@ }); | ||
function handleFocus() { | ||
if (disabled) return api.goToOption(Focus.Nothing); | ||
api.goToOption(Focus.Specific, id); | ||
if (disabled) return api.goToItem(Focus.Nothing); | ||
api.goToItem(Focus.Specific, id); | ||
} | ||
@@ -1489,3 +1706,3 @@ | ||
if (active.value) return; | ||
api.goToOption(Focus.Specific, id); | ||
api.goToItem(Focus.Specific, id); | ||
} | ||
@@ -1496,3 +1713,3 @@ | ||
if (!active.value) return; | ||
api.goToOption(Focus.Nothing); | ||
api.goToItem(Focus.Nothing); | ||
} | ||
@@ -1503,3 +1720,2 @@ | ||
active: active.value, | ||
selected: selected.value, | ||
disabled: disabled | ||
@@ -1509,13 +1725,12 @@ }; | ||
id: id, | ||
role: 'option', | ||
role: 'menuitem', | ||
tabIndex: -1, | ||
"class": resolvePropValue(className, slot), | ||
'aria-disabled': disabled === true ? true : undefined, | ||
'aria-selected': selected.value === true ? selected.value : undefined, | ||
onClick: handleClick, | ||
onFocus: handleFocus, | ||
onPointerMove: handleMove, | ||
onMouseMove: handleMove, | ||
onPointerLeave: handleLeave, | ||
onMouseLeave: handleLeave | ||
onPointermove: handleMove, | ||
onMousemove: handleMove, | ||
onPointerleave: handleLeave, | ||
onMouseleave: handleLeave | ||
}; | ||
@@ -1628,4 +1843,4 @@ return render({ | ||
onClick: this.handleClick, | ||
onKeyUp: this.handleKeyUp, | ||
onKeyPress: this.handleKeyPress | ||
onKeyup: this.handleKeyUp, | ||
onKeypress: this.handleKeyPress | ||
}; | ||
@@ -1742,3 +1957,3 @@ | ||
export { Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions, Menu, MenuButton, MenuItem, MenuItems, Switch, SwitchDescription, SwitchGroup, SwitchLabel }; | ||
export { Alert, Disclosure, DisclosureButton, DisclosurePanel, Listbox, ListboxButton, ListboxLabel, ListboxOption, ListboxOptions, Menu, MenuButton, MenuItem, MenuItems, Switch, SwitchDescription, SwitchGroup, SwitchLabel }; | ||
//# sourceMappingURL=headlessui.esm.js.map |
@@ -209,11 +209,47 @@ (function (global, factory) { | ||
var id = 0; | ||
var Alert = /*#__PURE__*/vue.defineComponent({ | ||
name: 'Alert', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'div' | ||
}, | ||
importance: { | ||
type: String, | ||
"default": 'polite' | ||
} | ||
}, | ||
setup: function setup(props, _ref) { | ||
var slots = _ref.slots, | ||
attrs = _ref.attrs; | ||
function generateId() { | ||
return ++id; | ||
} | ||
var _props$importance = props.importance, | ||
importance = _props$importance === void 0 ? 'polite' : _props$importance, | ||
passThroughProps = _objectWithoutPropertiesLoose(props, ["importance"]); | ||
function useId() { | ||
return generateId(); | ||
} | ||
var slot = { | ||
importance: importance | ||
}; | ||
var propsWeControl = match(importance, { | ||
polite: function polite() { | ||
return { | ||
role: 'status' | ||
}; | ||
}, | ||
assertive: function assertive() { | ||
return { | ||
role: 'alert' | ||
}; | ||
} | ||
}); | ||
return function () { | ||
return render({ | ||
props: _extends({}, passThroughProps, propsWeControl), | ||
slot: slot, | ||
slots: slots, | ||
attrs: attrs | ||
}); | ||
}; | ||
} | ||
}); | ||
@@ -238,2 +274,189 @@ // TODO: This must already exist somewhere, right? 🤔 | ||
var id = 0; | ||
function generateId() { | ||
return ++id; | ||
} | ||
function useId() { | ||
return generateId(); | ||
} | ||
function dom(ref) { | ||
var _ref$value$$el; | ||
if (ref == null) return null; | ||
if (ref.value == null) return null; | ||
return (_ref$value$$el = ref.value.$el) != null ? _ref$value$$el : ref.value; | ||
} | ||
var DisclosureStates; | ||
(function (DisclosureStates) { | ||
DisclosureStates[DisclosureStates["Open"] = 0] = "Open"; | ||
DisclosureStates[DisclosureStates["Closed"] = 1] = "Closed"; | ||
})(DisclosureStates || (DisclosureStates = {})); | ||
var DisclosureContext = /*#__PURE__*/Symbol('DisclosureContext'); | ||
function useDisclosureContext(component) { | ||
var context = vue.inject(DisclosureContext, null); | ||
if (context === null) { | ||
var err = new Error("<" + component + " /> is missing a parent <Disclosure /> component."); | ||
if (Error.captureStackTrace) Error.captureStackTrace(err, useDisclosureContext); | ||
throw err; | ||
} | ||
return context; | ||
} // --- | ||
var Disclosure = /*#__PURE__*/vue.defineComponent({ | ||
name: 'Disclosure', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'template' | ||
} | ||
}, | ||
setup: function setup(props, _ref) { | ||
var slots = _ref.slots, | ||
attrs = _ref.attrs; | ||
var passThroughProps = _extends({}, props); | ||
var disclosureState = vue.ref(DisclosureStates.Closed); | ||
var panelRef = vue.ref(null); | ||
var api = { | ||
disclosureState: disclosureState, | ||
panelRef: panelRef, | ||
toggleDisclosure: function toggleDisclosure() { | ||
var _match; | ||
disclosureState.value = match(disclosureState.value, (_match = {}, _match[DisclosureStates.Open] = DisclosureStates.Closed, _match[DisclosureStates.Closed] = DisclosureStates.Open, _match)); | ||
} | ||
}; | ||
vue.provide(DisclosureContext, api); | ||
return function () { | ||
var slot = { | ||
open: disclosureState.value === DisclosureStates.Open | ||
}; | ||
return render({ | ||
props: passThroughProps, | ||
slot: slot, | ||
slots: slots, | ||
attrs: attrs | ||
}); | ||
}; | ||
} | ||
}); // --- | ||
var DisclosureButton = /*#__PURE__*/vue.defineComponent({ | ||
name: 'DisclosureButton', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'button' | ||
}, | ||
disabled: { | ||
type: [Boolean], | ||
"default": false | ||
} | ||
}, | ||
render: function render$1() { | ||
var api = useDisclosureContext('DisclosureButton'); | ||
var slot = { | ||
open: api.disclosureState.value === DisclosureStates.Open | ||
}; | ||
var propsWeControl = { | ||
id: this.id, | ||
ref: 'el', | ||
type: 'button', | ||
'aria-expanded': api.disclosureState.value === DisclosureStates.Open ? true : undefined, | ||
'aria-controls': this.ariaControls, | ||
onClick: this.handleClick, | ||
onKeydown: this.handleKeyDown | ||
}; | ||
return render({ | ||
props: _extends({}, this.$props, propsWeControl), | ||
slot: slot, | ||
attrs: this.$attrs, | ||
slots: this.$slots | ||
}); | ||
}, | ||
setup: function setup(props) { | ||
var api = useDisclosureContext('DisclosureButton'); | ||
var buttonId = "headlessui-disclosure-button-" + useId(); | ||
var ariaControls = vue.computed(function () { | ||
var _dom$id, _dom; | ||
return (_dom$id = (_dom = dom(api.panelRef)) == null ? void 0 : _dom.id) != null ? _dom$id : undefined; | ||
}); | ||
return { | ||
id: buttonId, | ||
ariaControls: ariaControls, | ||
handleClick: function handleClick() { | ||
if (props.disabled) return; | ||
api.toggleDisclosure(); | ||
}, | ||
handleKeyDown: function handleKeyDown(event) { | ||
if (props.disabled) return; | ||
switch (event.key) { | ||
case Keys.Space: | ||
case Keys.Enter: | ||
event.preventDefault(); | ||
api.toggleDisclosure(); | ||
break; | ||
} | ||
} | ||
}; | ||
} | ||
}); // --- | ||
var DisclosurePanel = /*#__PURE__*/vue.defineComponent({ | ||
name: 'DisclosurePanel', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'div' | ||
}, | ||
"static": { | ||
type: Boolean, | ||
"default": false | ||
}, | ||
unmount: { | ||
type: Boolean, | ||
"default": true | ||
} | ||
}, | ||
render: function render$1() { | ||
var api = useDisclosureContext('DisclosurePanel'); | ||
var slot = { | ||
open: api.disclosureState.value === DisclosureStates.Open | ||
}; | ||
var propsWeControl = { | ||
id: this.id, | ||
ref: 'el' | ||
}; | ||
return render({ | ||
props: _extends({}, this.$props, propsWeControl), | ||
slot: slot, | ||
attrs: this.$attrs, | ||
slots: this.$slots, | ||
features: Features.RenderStrategy | Features.Static, | ||
visible: slot.open | ||
}); | ||
}, | ||
setup: function setup() { | ||
var api = useDisclosureContext('DisclosurePanel'); | ||
var panelId = "headlessui-disclosure-panel-" + useId(); | ||
return { | ||
id: panelId, | ||
el: api.panelRef | ||
}; | ||
} | ||
}); | ||
function assertNever(x) { | ||
@@ -326,17 +549,9 @@ throw new Error('Unexpected object: ' + x); | ||
function dom(ref) { | ||
var _ref$value$$el; | ||
var ListboxStates; | ||
if (ref == null) return null; | ||
if (ref.value == null) return null; | ||
return (_ref$value$$el = ref.value.$el) != null ? _ref$value$$el : ref.value; | ||
} | ||
(function (ListboxStates) { | ||
ListboxStates[ListboxStates["Open"] = 0] = "Open"; | ||
ListboxStates[ListboxStates["Closed"] = 1] = "Closed"; | ||
})(ListboxStates || (ListboxStates = {})); | ||
var MenuStates; | ||
(function (MenuStates) { | ||
MenuStates[MenuStates["Open"] = 0] = "Open"; | ||
MenuStates[MenuStates["Closed"] = 1] = "Closed"; | ||
})(MenuStates || (MenuStates = {})); | ||
function nextFrame(cb) { | ||
@@ -348,10 +563,10 @@ requestAnimationFrame(function () { | ||
var MenuContext = /*#__PURE__*/Symbol('MenuContext'); | ||
var ListboxContext = /*#__PURE__*/Symbol('ListboxContext'); | ||
function useMenuContext(component) { | ||
var context = vue.inject(MenuContext, null); | ||
function useListboxContext(component) { | ||
var context = vue.inject(ListboxContext, null); | ||
if (context === null) { | ||
var err = new Error("<" + component + " /> is missing a parent <Menu /> component."); | ||
if (Error.captureStackTrace) Error.captureStackTrace(err, useMenuContext); | ||
var err = new Error("<" + component + " /> is missing a parent <Listbox /> component."); | ||
if (Error.captureStackTrace) Error.captureStackTrace(err, useListboxContext); | ||
throw err; | ||
@@ -361,5 +576,8 @@ } | ||
return context; | ||
} | ||
} // --- | ||
var Menu = /*#__PURE__*/vue.defineComponent({ | ||
var Listbox = /*#__PURE__*/vue.defineComponent({ | ||
name: 'Listbox', | ||
emits: ['update:modelValue'], | ||
props: { | ||
@@ -369,2 +587,10 @@ as: { | ||
"default": 'template' | ||
}, | ||
disabled: { | ||
type: [Boolean], | ||
"default": false | ||
}, | ||
modelValue: { | ||
type: [Object, String, Number, Boolean], | ||
"default": null | ||
} | ||
@@ -374,25 +600,43 @@ }, | ||
var slots = _ref.slots, | ||
attrs = _ref.attrs; | ||
var menuState = vue.ref(MenuStates.Closed); | ||
attrs = _ref.attrs, | ||
emit = _ref.emit; | ||
var disabled = props.disabled, | ||
passThroughProps = _objectWithoutPropertiesLoose(props, ["modelValue", "disabled"]); | ||
var listboxState = vue.ref(ListboxStates.Closed); | ||
var labelRef = vue.ref(null); | ||
var buttonRef = vue.ref(null); | ||
var itemsRef = vue.ref(null); | ||
var items = vue.ref([]); | ||
var optionsRef = vue.ref(null); | ||
var options = vue.ref([]); | ||
var searchQuery = vue.ref(''); | ||
var activeItemIndex = vue.ref(null); | ||
var activeOptionIndex = vue.ref(null); | ||
var value = vue.computed(function () { | ||
return props.modelValue; | ||
}); | ||
var api = { | ||
menuState: menuState, | ||
listboxState: listboxState, | ||
value: value, | ||
labelRef: labelRef, | ||
buttonRef: buttonRef, | ||
itemsRef: itemsRef, | ||
items: items, | ||
optionsRef: optionsRef, | ||
disabled: disabled, | ||
options: options, | ||
searchQuery: searchQuery, | ||
activeItemIndex: activeItemIndex, | ||
closeMenu: function closeMenu() { | ||
menuState.value = MenuStates.Closed; | ||
activeItemIndex.value = null; | ||
activeOptionIndex: activeOptionIndex, | ||
closeListbox: function closeListbox() { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
listboxState.value = ListboxStates.Closed; | ||
activeOptionIndex.value = null; | ||
}, | ||
openMenu: function openMenu() { | ||
return menuState.value = MenuStates.Open; | ||
openListbox: function openListbox() { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Open) return; | ||
listboxState.value = ListboxStates.Open; | ||
}, | ||
goToItem: function goToItem(focus, id) { | ||
var nextActiveItemIndex = calculateActiveIndex(focus === Focus.Specific ? { | ||
goToOption: function goToOption(focus, id) { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
var nextActiveOptionIndex = calculateActiveIndex(focus === Focus.Specific ? { | ||
focus: Focus.Specific, | ||
@@ -404,32 +648,37 @@ id: id | ||
resolveItems: function resolveItems() { | ||
return items.value; | ||
return options.value; | ||
}, | ||
resolveActiveIndex: function resolveActiveIndex() { | ||
return activeItemIndex.value; | ||
return activeOptionIndex.value; | ||
}, | ||
resolveId: function resolveId(item) { | ||
return item.id; | ||
resolveId: function resolveId(option) { | ||
return option.id; | ||
}, | ||
resolveDisabled: function resolveDisabled(item) { | ||
return item.dataRef.disabled; | ||
resolveDisabled: function resolveDisabled(option) { | ||
return option.dataRef.disabled; | ||
} | ||
}); | ||
if (searchQuery.value === '' && activeItemIndex.value === nextActiveItemIndex) return; | ||
if (searchQuery.value === '' && activeOptionIndex.value === nextActiveOptionIndex) return; | ||
searchQuery.value = ''; | ||
activeItemIndex.value = nextActiveItemIndex; | ||
activeOptionIndex.value = nextActiveOptionIndex; | ||
}, | ||
search: function search(value) { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
searchQuery.value += value; | ||
var match = items.value.findIndex(function (item) { | ||
return item.dataRef.textValue.startsWith(searchQuery.value) && !item.dataRef.disabled; | ||
var match = options.value.findIndex(function (option) { | ||
return !option.dataRef.disabled && option.dataRef.textValue.startsWith(searchQuery.value); | ||
}); | ||
if (match === -1 || match === activeItemIndex.value) return; | ||
activeItemIndex.value = match; | ||
if (match === -1 || match === activeOptionIndex.value) return; | ||
activeOptionIndex.value = match; | ||
}, | ||
clearSearch: function clearSearch() { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
if (searchQuery.value === '') return; | ||
searchQuery.value = ''; | ||
}, | ||
registerItem: function registerItem(id, dataRef) { | ||
registerOption: function registerOption(id, dataRef) { | ||
// @ts-expect-error The expected type comes from property 'dataRef' which is declared here on type '{ id: string; dataRef: { textValue: string; disabled: boolean; }; }' | ||
items.value.push({ | ||
options.value.push({ | ||
id: id, | ||
@@ -439,18 +688,22 @@ dataRef: dataRef | ||
}, | ||
unregisterItem: function unregisterItem(id) { | ||
var nextItems = items.value.slice(); | ||
var currentActiveItem = activeItemIndex.value !== null ? nextItems[activeItemIndex.value] : null; | ||
var idx = nextItems.findIndex(function (a) { | ||
unregisterOption: function unregisterOption(id) { | ||
var nextOptions = options.value.slice(); | ||
var currentActiveOption = activeOptionIndex.value !== null ? nextOptions[activeOptionIndex.value] : null; | ||
var idx = nextOptions.findIndex(function (a) { | ||
return a.id === id; | ||
}); | ||
if (idx !== -1) nextItems.splice(idx, 1); | ||
items.value = nextItems; | ||
if (idx !== -1) nextOptions.splice(idx, 1); | ||
options.value = nextOptions; | ||
activeItemIndex.value = function () { | ||
if (idx === activeItemIndex.value) return null; | ||
if (currentActiveItem === null) return null; // If we removed the item before the actual active index, then it would be out of sync. To | ||
activeOptionIndex.value = function () { | ||
if (idx === activeOptionIndex.value) return null; | ||
if (currentActiveOption === null) return null; // If we removed the option before the actual active index, then it would be out of sync. To | ||
// fix this, we will find the correct (new) index position. | ||
return nextItems.indexOf(currentActiveItem); | ||
return nextOptions.indexOf(currentActiveOption); | ||
}(); | ||
}, | ||
select: function select(value) { | ||
if (disabled) return; | ||
emit('update:modelValue', value); | ||
} | ||
@@ -464,5 +717,5 @@ }; | ||
var active = document.activeElement; | ||
if (menuState.value !== MenuStates.Open) return; | ||
if (listboxState.value !== ListboxStates.Open) return; | ||
if ((_dom = dom(buttonRef)) == null ? void 0 : _dom.contains(target)) return; | ||
if (!((_dom2 = dom(itemsRef)) == null ? void 0 : _dom2.contains(target))) api.closeMenu(); | ||
if (!((_dom2 = dom(optionsRef)) == null ? void 0 : _dom2.contains(target))) api.closeListbox(); | ||
if (active !== document.body && (active == null ? void 0 : active.contains(target))) return; // Keep focus on newly clicked/focused element | ||
@@ -481,9 +734,10 @@ | ||
vue.provide(MenuContext, api); | ||
vue.provide(ListboxContext, api); | ||
return function () { | ||
var slot = { | ||
open: menuState.value === MenuStates.Open | ||
open: listboxState.value === ListboxStates.Open, | ||
disabled: disabled | ||
}; | ||
return render({ | ||
props: props, | ||
props: passThroughProps, | ||
slot: slot, | ||
@@ -495,11 +749,52 @@ slots: slots, | ||
} | ||
}); | ||
var MenuButton = /*#__PURE__*/vue.defineComponent({ | ||
}); // --- | ||
var ListboxLabel = /*#__PURE__*/vue.defineComponent({ | ||
name: 'ListboxLabel', | ||
props: { | ||
disabled: { | ||
type: Boolean, | ||
"default": false | ||
}, | ||
as: { | ||
type: [Object, String], | ||
"default": 'label' | ||
} | ||
}, | ||
render: function render$1() { | ||
var api = useListboxContext('ListboxLabel'); | ||
var slot = { | ||
open: api.listboxState.value === ListboxStates.Open, | ||
disabled: api.disabled | ||
}; | ||
var propsWeControl = { | ||
id: this.id, | ||
ref: 'el', | ||
onClick: this.handleClick | ||
}; | ||
return render({ | ||
props: _extends({}, this.$props, propsWeControl), | ||
slot: slot, | ||
attrs: this.$attrs, | ||
slots: this.$slots | ||
}); | ||
}, | ||
setup: function setup() { | ||
var api = useListboxContext('ListboxLabel'); | ||
var id = "headlessui-listbox-label-" + useId(); | ||
return { | ||
id: id, | ||
el: api.labelRef, | ||
handleClick: function handleClick() { | ||
var _dom4; | ||
(_dom4 = dom(api.buttonRef)) == null ? void 0 : _dom4.focus({ | ||
preventScroll: true | ||
}); | ||
} | ||
}; | ||
} | ||
}); // --- | ||
var ListboxButton = /*#__PURE__*/vue.defineComponent({ | ||
name: 'ListboxButton', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'button' | ||
@@ -509,7 +804,8 @@ } | ||
render: function render$1() { | ||
var _dom4; | ||
var _dom5, _dom6; | ||
var api = useMenuContext('MenuButton'); | ||
var api = useListboxContext('ListboxButton'); | ||
var slot = { | ||
open: api.menuState.value === MenuStates.Open | ||
open: api.listboxState.value === ListboxStates.Open, | ||
disabled: api.disabled | ||
}; | ||
@@ -521,5 +817,7 @@ var propsWeControl = { | ||
'aria-haspopup': true, | ||
'aria-controls': (_dom4 = dom(api.itemsRef)) == null ? void 0 : _dom4.id, | ||
'aria-expanded': api.menuState.value === MenuStates.Open ? true : undefined, | ||
onKeyDown: this.handleKeyDown, | ||
'aria-controls': (_dom5 = dom(api.optionsRef)) == null ? void 0 : _dom5.id, | ||
'aria-expanded': api.listboxState.value === ListboxStates.Open ? true : undefined, | ||
'aria-labelledby': api.labelRef.value ? [(_dom6 = dom(api.labelRef)) == null ? void 0 : _dom6.id, this.id].join(' ') : undefined, | ||
disabled: api.disabled, | ||
onKeydown: this.handleKeyDown, | ||
onClick: this.handleClick | ||
@@ -534,5 +832,5 @@ }; | ||
}, | ||
setup: function setup(props) { | ||
var api = useMenuContext('MenuButton'); | ||
var id = "headlessui-menu-button-" + useId(); | ||
setup: function setup() { | ||
var api = useListboxContext('ListboxButton'); | ||
var id = "headlessui-listbox-button-" + useId(); | ||
@@ -546,10 +844,10 @@ function handleKeyDown(event) { | ||
event.preventDefault(); | ||
api.openMenu(); | ||
api.openListbox(); | ||
vue.nextTick(function () { | ||
var _dom5; | ||
var _dom7; | ||
(_dom5 = dom(api.itemsRef)) == null ? void 0 : _dom5.focus({ | ||
(_dom7 = dom(api.optionsRef)) == null ? void 0 : _dom7.focus({ | ||
preventScroll: true | ||
}); | ||
api.goToItem(Focus.First); | ||
if (!api.value.value) api.goToOption(Focus.First); | ||
}); | ||
@@ -560,10 +858,10 @@ break; | ||
event.preventDefault(); | ||
api.openMenu(); | ||
api.openListbox(); | ||
vue.nextTick(function () { | ||
var _dom6; | ||
var _dom8; | ||
(_dom6 = dom(api.itemsRef)) == null ? void 0 : _dom6.focus({ | ||
(_dom8 = dom(api.optionsRef)) == null ? void 0 : _dom8.focus({ | ||
preventScroll: true | ||
}); | ||
api.goToItem(Focus.Last); | ||
if (!api.value.value) api.goToOption(Focus.Last); | ||
}); | ||
@@ -575,10 +873,10 @@ break; | ||
function handleClick(event) { | ||
if (props.disabled) return; | ||
if (api.disabled) return; | ||
if (api.menuState.value === MenuStates.Open) { | ||
api.closeMenu(); | ||
if (api.listboxState.value === ListboxStates.Open) { | ||
api.closeListbox(); | ||
vue.nextTick(function () { | ||
var _dom7; | ||
var _dom9; | ||
return (_dom7 = dom(api.buttonRef)) == null ? void 0 : _dom7.focus({ | ||
return (_dom9 = dom(api.buttonRef)) == null ? void 0 : _dom9.focus({ | ||
preventScroll: true | ||
@@ -589,7 +887,7 @@ }); | ||
event.preventDefault(); | ||
api.openMenu(); | ||
api.openListbox(); | ||
nextFrame(function () { | ||
var _dom8; | ||
var _dom10; | ||
return (_dom8 = dom(api.itemsRef)) == null ? void 0 : _dom8.focus({ | ||
return (_dom10 = dom(api.optionsRef)) == null ? void 0 : _dom10.focus({ | ||
preventScroll: true | ||
@@ -608,8 +906,10 @@ }); | ||
} | ||
}); | ||
var MenuItems = /*#__PURE__*/vue.defineComponent({ | ||
}); // --- | ||
var ListboxOptions = /*#__PURE__*/vue.defineComponent({ | ||
name: 'ListboxOptions', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'div' | ||
"default": 'ul' | ||
}, | ||
@@ -626,14 +926,14 @@ "static": { | ||
render: function render$1() { | ||
var _api$items$value$api$, _dom9; | ||
var _api$options$value$ap, _dom$id, _dom11, _dom12; | ||
var api = useMenuContext('MenuItems'); | ||
var api = useListboxContext('ListboxOptions'); | ||
var slot = { | ||
open: api.menuState.value === MenuStates.Open | ||
open: api.listboxState.value === ListboxStates.Open | ||
}; | ||
var propsWeControl = { | ||
'aria-activedescendant': api.activeItemIndex.value === null ? undefined : (_api$items$value$api$ = api.items.value[api.activeItemIndex.value]) == null ? void 0 : _api$items$value$api$.id, | ||
'aria-labelledby': (_dom9 = dom(api.buttonRef)) == null ? void 0 : _dom9.id, | ||
'aria-activedescendant': api.activeOptionIndex.value === null ? undefined : (_api$options$value$ap = api.options.value[api.activeOptionIndex.value]) == null ? void 0 : _api$options$value$ap.id, | ||
'aria-labelledby': (_dom$id = (_dom11 = dom(api.labelRef)) == null ? void 0 : _dom11.id) != null ? _dom$id : (_dom12 = dom(api.buttonRef)) == null ? void 0 : _dom12.id, | ||
id: this.id, | ||
onKeyDown: this.handleKeyDown, | ||
role: 'menu', | ||
onKeydown: this.handleKeyDown, | ||
role: 'listbox', | ||
tabIndex: 0, | ||
@@ -653,22 +953,6 @@ ref: 'el' | ||
setup: function setup() { | ||
var api = useMenuContext('MenuItems'); | ||
var id = "headlessui-menu-items-" + useId(); | ||
var api = useListboxContext('ListboxOptions'); | ||
var id = "headlessui-listbox-options-" + useId(); | ||
var searchDebounce = vue.ref(null); | ||
vue.watchEffect(function () { | ||
var container = dom(api.itemsRef); | ||
if (!container) return; | ||
if (api.menuState.value !== MenuStates.Open) return; | ||
var walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, { | ||
acceptNode: function acceptNode(node) { | ||
if (node.getAttribute('role') === 'menuitem') return NodeFilter.FILTER_REJECT; | ||
if (node.hasAttribute('role')) return NodeFilter.FILTER_SKIP; | ||
return NodeFilter.FILTER_ACCEPT; | ||
} | ||
}); | ||
while (walker.nextNode()) { | ||
walker.currentNode.setAttribute('role', 'none'); | ||
} | ||
}); | ||
function handleKeyDown(event) { | ||
@@ -691,14 +975,12 @@ if (searchDebounce.value) clearTimeout(searchDebounce.value); | ||
if (api.activeItemIndex.value !== null) { | ||
var _document$getElementB; | ||
var _id = api.items.value[api.activeItemIndex.value].id; | ||
(_document$getElementB = document.getElementById(_id)) == null ? void 0 : _document$getElementB.click(); | ||
if (api.activeOptionIndex.value !== null) { | ||
var dataRef = api.options.value[api.activeOptionIndex.value].dataRef; | ||
api.select(dataRef.value); | ||
} | ||
api.closeMenu(); | ||
api.closeListbox(); | ||
vue.nextTick(function () { | ||
var _dom10; | ||
var _dom13; | ||
return (_dom10 = dom(api.buttonRef)) == null ? void 0 : _dom10.focus({ | ||
return (_dom13 = dom(api.buttonRef)) == null ? void 0 : _dom13.focus({ | ||
preventScroll: true | ||
@@ -711,7 +993,7 @@ }); | ||
event.preventDefault(); | ||
return api.goToItem(Focus.Next); | ||
return api.goToOption(Focus.Next); | ||
case Keys.ArrowUp: | ||
event.preventDefault(); | ||
return api.goToItem(Focus.Previous); | ||
return api.goToOption(Focus.Previous); | ||
@@ -721,3 +1003,3 @@ case Keys.Home: | ||
event.preventDefault(); | ||
return api.goToItem(Focus.First); | ||
return api.goToOption(Focus.First); | ||
@@ -727,11 +1009,11 @@ case Keys.End: | ||
event.preventDefault(); | ||
return api.goToItem(Focus.Last); | ||
return api.goToOption(Focus.Last); | ||
case Keys.Escape: | ||
event.preventDefault(); | ||
api.closeMenu(); | ||
api.closeListbox(); | ||
vue.nextTick(function () { | ||
var _dom11; | ||
var _dom14; | ||
return (_dom11 = dom(api.buttonRef)) == null ? void 0 : _dom11.focus({ | ||
return (_dom14 = dom(api.buttonRef)) == null ? void 0 : _dom14.focus({ | ||
preventScroll: true | ||
@@ -759,3 +1041,3 @@ }); | ||
id: id, | ||
el: api.itemsRef, | ||
el: api.optionsRef, | ||
handleKeyDown: handleKeyDown | ||
@@ -765,8 +1047,13 @@ }; | ||
}); | ||
var MenuItem = /*#__PURE__*/vue.defineComponent({ | ||
var ListboxOption = /*#__PURE__*/vue.defineComponent({ | ||
name: 'ListboxOption', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'template' | ||
"default": 'li' | ||
}, | ||
value: { | ||
type: [Object, String], | ||
"default": null | ||
}, | ||
disabled: { | ||
@@ -788,29 +1075,46 @@ type: Boolean, | ||
attrs = _ref2.attrs; | ||
var api = useMenuContext('MenuItem'); | ||
var id = "headlessui-menu-item-" + useId(); | ||
var api = useListboxContext('ListboxOption'); | ||
var id = "headlessui-listbox-option-" + useId(); | ||
var disabled = props.disabled, | ||
defaultClass = props["class"], | ||
_props$className = props.className, | ||
className = _props$className === void 0 ? defaultClass : _props$className; | ||
className = _props$className === void 0 ? defaultClass : _props$className, | ||
value = props.value; | ||
var active = vue.computed(function () { | ||
return api.activeItemIndex.value !== null ? api.items.value[api.activeItemIndex.value].id === id : false; | ||
return api.activeOptionIndex.value !== null ? api.options.value[api.activeOptionIndex.value].id === id : false; | ||
}); | ||
var selected = vue.computed(function () { | ||
return vue.toRaw(api.value.value) === vue.toRaw(value); | ||
}); | ||
var dataRef = vue.ref({ | ||
disabled: disabled, | ||
value: value, | ||
textValue: '' | ||
}); | ||
vue.onMounted(function () { | ||
var _document$getElementB2, _document$getElementB3; | ||
var _document$getElementB, _document$getElementB2; | ||
var textValue = (_document$getElementB2 = document.getElementById(id)) == null ? void 0 : (_document$getElementB3 = _document$getElementB2.textContent) == null ? void 0 : _document$getElementB3.toLowerCase().trim(); | ||
var textValue = (_document$getElementB = document.getElementById(id)) == null ? void 0 : (_document$getElementB2 = _document$getElementB.textContent) == null ? void 0 : _document$getElementB2.toLowerCase().trim(); | ||
if (textValue !== undefined) dataRef.value.textValue = textValue; | ||
}); | ||
vue.onMounted(function () { | ||
return api.registerItem(id, dataRef); | ||
return api.registerOption(id, dataRef); | ||
}); | ||
vue.onUnmounted(function () { | ||
return api.unregisterItem(id); | ||
return api.unregisterOption(id); | ||
}); | ||
vue.onMounted(function () { | ||
vue.watch([api.listboxState, selected], function () { | ||
var _document$getElementB3; | ||
if (api.listboxState.value !== ListboxStates.Open) return; | ||
if (!selected.value) return; | ||
api.goToOption(Focus.Specific, id); | ||
(_document$getElementB3 = document.getElementById(id)) == null ? void 0 : _document$getElementB3.focus == null ? void 0 : _document$getElementB3.focus(); | ||
}, { | ||
immediate: true | ||
}); | ||
}); | ||
vue.watchEffect(function () { | ||
if (api.menuState.value !== MenuStates.Open) return; | ||
if (api.listboxState.value !== ListboxStates.Open) return; | ||
if (!active.value) return; | ||
@@ -828,7 +1132,8 @@ vue.nextTick(function () { | ||
if (disabled) return event.preventDefault(); | ||
api.closeMenu(); | ||
api.select(value); | ||
api.closeListbox(); | ||
vue.nextTick(function () { | ||
var _dom12; | ||
var _dom15; | ||
return (_dom12 = dom(api.buttonRef)) == null ? void 0 : _dom12.focus({ | ||
return (_dom15 = dom(api.buttonRef)) == null ? void 0 : _dom15.focus({ | ||
preventScroll: true | ||
@@ -840,4 +1145,4 @@ }); | ||
function handleFocus() { | ||
if (disabled) return api.goToItem(Focus.Nothing); | ||
api.goToItem(Focus.Specific, id); | ||
if (disabled) return api.goToOption(Focus.Nothing); | ||
api.goToOption(Focus.Specific, id); | ||
} | ||
@@ -848,3 +1153,3 @@ | ||
if (active.value) return; | ||
api.goToItem(Focus.Specific, id); | ||
api.goToOption(Focus.Specific, id); | ||
} | ||
@@ -855,3 +1160,3 @@ | ||
if (!active.value) return; | ||
api.goToItem(Focus.Nothing); | ||
api.goToOption(Focus.Nothing); | ||
} | ||
@@ -862,2 +1167,3 @@ | ||
active: active.value, | ||
selected: selected.value, | ||
disabled: disabled | ||
@@ -867,12 +1173,13 @@ }; | ||
id: id, | ||
role: 'menuitem', | ||
role: 'option', | ||
tabIndex: -1, | ||
"class": resolvePropValue(className, slot), | ||
'aria-disabled': disabled === true ? true : undefined, | ||
'aria-selected': selected.value === true ? selected.value : undefined, | ||
onClick: handleClick, | ||
onFocus: handleFocus, | ||
onPointerMove: handleMove, | ||
onMouseMove: handleMove, | ||
onPointerLeave: handleLeave, | ||
onMouseLeave: handleLeave | ||
onPointermove: handleMove, | ||
onMousemove: handleMove, | ||
onPointerleave: handleLeave, | ||
onMouseleave: handleLeave | ||
}; | ||
@@ -889,8 +1196,8 @@ return render({ | ||
var ListboxStates; | ||
var MenuStates; | ||
(function (ListboxStates) { | ||
ListboxStates[ListboxStates["Open"] = 0] = "Open"; | ||
ListboxStates[ListboxStates["Closed"] = 1] = "Closed"; | ||
})(ListboxStates || (ListboxStates = {})); | ||
(function (MenuStates) { | ||
MenuStates[MenuStates["Open"] = 0] = "Open"; | ||
MenuStates[MenuStates["Closed"] = 1] = "Closed"; | ||
})(MenuStates || (MenuStates = {})); | ||
@@ -903,10 +1210,10 @@ function nextFrame$1(cb) { | ||
var ListboxContext = /*#__PURE__*/Symbol('ListboxContext'); | ||
var MenuContext = /*#__PURE__*/Symbol('MenuContext'); | ||
function useListboxContext(component) { | ||
var context = vue.inject(ListboxContext, null); | ||
function useMenuContext(component) { | ||
var context = vue.inject(MenuContext, null); | ||
if (context === null) { | ||
var err = new Error("<" + component + " /> is missing a parent <Listbox /> component."); | ||
if (Error.captureStackTrace) Error.captureStackTrace(err, useListboxContext); | ||
var err = new Error("<" + component + " /> is missing a parent <Menu /> component."); | ||
if (Error.captureStackTrace) Error.captureStackTrace(err, useMenuContext); | ||
throw err; | ||
@@ -916,8 +1223,5 @@ } | ||
return context; | ||
} // --- | ||
} | ||
var Listbox = /*#__PURE__*/vue.defineComponent({ | ||
name: 'Listbox', | ||
emits: ['update:modelValue'], | ||
var Menu = /*#__PURE__*/vue.defineComponent({ | ||
props: { | ||
@@ -927,10 +1231,2 @@ as: { | ||
"default": 'template' | ||
}, | ||
disabled: { | ||
type: [Boolean], | ||
"default": false | ||
}, | ||
modelValue: { | ||
type: [Object, String, Number, Boolean], | ||
"default": null | ||
} | ||
@@ -940,43 +1236,25 @@ }, | ||
var slots = _ref.slots, | ||
attrs = _ref.attrs, | ||
emit = _ref.emit; | ||
var disabled = props.disabled, | ||
passThroughProps = _objectWithoutPropertiesLoose(props, ["modelValue", "disabled"]); | ||
var listboxState = vue.ref(ListboxStates.Closed); | ||
var labelRef = vue.ref(null); | ||
attrs = _ref.attrs; | ||
var menuState = vue.ref(MenuStates.Closed); | ||
var buttonRef = vue.ref(null); | ||
var optionsRef = vue.ref(null); | ||
var options = vue.ref([]); | ||
var itemsRef = vue.ref(null); | ||
var items = vue.ref([]); | ||
var searchQuery = vue.ref(''); | ||
var activeOptionIndex = vue.ref(null); | ||
var value = vue.computed(function () { | ||
return props.modelValue; | ||
}); | ||
var activeItemIndex = vue.ref(null); | ||
var api = { | ||
listboxState: listboxState, | ||
value: value, | ||
labelRef: labelRef, | ||
menuState: menuState, | ||
buttonRef: buttonRef, | ||
optionsRef: optionsRef, | ||
disabled: disabled, | ||
options: options, | ||
itemsRef: itemsRef, | ||
items: items, | ||
searchQuery: searchQuery, | ||
activeOptionIndex: activeOptionIndex, | ||
closeListbox: function closeListbox() { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
listboxState.value = ListboxStates.Closed; | ||
activeOptionIndex.value = null; | ||
activeItemIndex: activeItemIndex, | ||
closeMenu: function closeMenu() { | ||
menuState.value = MenuStates.Closed; | ||
activeItemIndex.value = null; | ||
}, | ||
openListbox: function openListbox() { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Open) return; | ||
listboxState.value = ListboxStates.Open; | ||
openMenu: function openMenu() { | ||
return menuState.value = MenuStates.Open; | ||
}, | ||
goToOption: function goToOption(focus, id) { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
var nextActiveOptionIndex = calculateActiveIndex(focus === Focus.Specific ? { | ||
goToItem: function goToItem(focus, id) { | ||
var nextActiveItemIndex = calculateActiveIndex(focus === Focus.Specific ? { | ||
focus: Focus.Specific, | ||
@@ -988,37 +1266,32 @@ id: id | ||
resolveItems: function resolveItems() { | ||
return options.value; | ||
return items.value; | ||
}, | ||
resolveActiveIndex: function resolveActiveIndex() { | ||
return activeOptionIndex.value; | ||
return activeItemIndex.value; | ||
}, | ||
resolveId: function resolveId(option) { | ||
return option.id; | ||
resolveId: function resolveId(item) { | ||
return item.id; | ||
}, | ||
resolveDisabled: function resolveDisabled(option) { | ||
return option.dataRef.disabled; | ||
resolveDisabled: function resolveDisabled(item) { | ||
return item.dataRef.disabled; | ||
} | ||
}); | ||
if (searchQuery.value === '' && activeOptionIndex.value === nextActiveOptionIndex) return; | ||
if (searchQuery.value === '' && activeItemIndex.value === nextActiveItemIndex) return; | ||
searchQuery.value = ''; | ||
activeOptionIndex.value = nextActiveOptionIndex; | ||
activeItemIndex.value = nextActiveItemIndex; | ||
}, | ||
search: function search(value) { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
searchQuery.value += value; | ||
var match = options.value.findIndex(function (option) { | ||
return !option.dataRef.disabled && option.dataRef.textValue.startsWith(searchQuery.value); | ||
var match = items.value.findIndex(function (item) { | ||
return item.dataRef.textValue.startsWith(searchQuery.value) && !item.dataRef.disabled; | ||
}); | ||
if (match === -1 || match === activeOptionIndex.value) return; | ||
activeOptionIndex.value = match; | ||
if (match === -1 || match === activeItemIndex.value) return; | ||
activeItemIndex.value = match; | ||
}, | ||
clearSearch: function clearSearch() { | ||
if (disabled) return; | ||
if (listboxState.value === ListboxStates.Closed) return; | ||
if (searchQuery.value === '') return; | ||
searchQuery.value = ''; | ||
}, | ||
registerOption: function registerOption(id, dataRef) { | ||
registerItem: function registerItem(id, dataRef) { | ||
// @ts-expect-error The expected type comes from property 'dataRef' which is declared here on type '{ id: string; dataRef: { textValue: string; disabled: boolean; }; }' | ||
options.value.push({ | ||
items.value.push({ | ||
id: id, | ||
@@ -1028,22 +1301,18 @@ dataRef: dataRef | ||
}, | ||
unregisterOption: function unregisterOption(id) { | ||
var nextOptions = options.value.slice(); | ||
var currentActiveOption = activeOptionIndex.value !== null ? nextOptions[activeOptionIndex.value] : null; | ||
var idx = nextOptions.findIndex(function (a) { | ||
unregisterItem: function unregisterItem(id) { | ||
var nextItems = items.value.slice(); | ||
var currentActiveItem = activeItemIndex.value !== null ? nextItems[activeItemIndex.value] : null; | ||
var idx = nextItems.findIndex(function (a) { | ||
return a.id === id; | ||
}); | ||
if (idx !== -1) nextOptions.splice(idx, 1); | ||
options.value = nextOptions; | ||
if (idx !== -1) nextItems.splice(idx, 1); | ||
items.value = nextItems; | ||
activeOptionIndex.value = function () { | ||
if (idx === activeOptionIndex.value) return null; | ||
if (currentActiveOption === null) return null; // If we removed the option before the actual active index, then it would be out of sync. To | ||
activeItemIndex.value = function () { | ||
if (idx === activeItemIndex.value) return null; | ||
if (currentActiveItem === null) return null; // If we removed the item before the actual active index, then it would be out of sync. To | ||
// fix this, we will find the correct (new) index position. | ||
return nextOptions.indexOf(currentActiveOption); | ||
return nextItems.indexOf(currentActiveItem); | ||
}(); | ||
}, | ||
select: function select(value) { | ||
if (disabled) return; | ||
emit('update:modelValue', value); | ||
} | ||
@@ -1057,5 +1326,5 @@ }; | ||
var active = document.activeElement; | ||
if (listboxState.value !== ListboxStates.Open) return; | ||
if (menuState.value !== MenuStates.Open) return; | ||
if ((_dom = dom(buttonRef)) == null ? void 0 : _dom.contains(target)) return; | ||
if (!((_dom2 = dom(optionsRef)) == null ? void 0 : _dom2.contains(target))) api.closeListbox(); | ||
if (!((_dom2 = dom(itemsRef)) == null ? void 0 : _dom2.contains(target))) api.closeMenu(); | ||
if (active !== document.body && (active == null ? void 0 : active.contains(target))) return; // Keep focus on newly clicked/focused element | ||
@@ -1074,10 +1343,9 @@ | ||
vue.provide(ListboxContext, api); | ||
vue.provide(MenuContext, api); | ||
return function () { | ||
var slot = { | ||
open: listboxState.value === ListboxStates.Open, | ||
disabled: disabled | ||
open: menuState.value === MenuStates.Open | ||
}; | ||
return render({ | ||
props: passThroughProps, | ||
props: props, | ||
slot: slot, | ||
@@ -1089,52 +1357,11 @@ slots: slots, | ||
} | ||
}); // --- | ||
var ListboxLabel = /*#__PURE__*/vue.defineComponent({ | ||
name: 'ListboxLabel', | ||
}); | ||
var MenuButton = /*#__PURE__*/vue.defineComponent({ | ||
props: { | ||
disabled: { | ||
type: Boolean, | ||
"default": false | ||
}, | ||
as: { | ||
type: [Object, String], | ||
"default": 'label' | ||
} | ||
}, | ||
render: function render$1() { | ||
var api = useListboxContext('ListboxLabel'); | ||
var slot = { | ||
open: api.listboxState.value === ListboxStates.Open, | ||
disabled: api.disabled | ||
}; | ||
var propsWeControl = { | ||
id: this.id, | ||
ref: 'el', | ||
onClick: this.handleClick | ||
}; | ||
return render({ | ||
props: _extends({}, this.$props, propsWeControl), | ||
slot: slot, | ||
attrs: this.$attrs, | ||
slots: this.$slots | ||
}); | ||
}, | ||
setup: function setup() { | ||
var api = useListboxContext('ListboxLabel'); | ||
var id = "headlessui-listbox-label-" + useId(); | ||
return { | ||
id: id, | ||
el: api.labelRef, | ||
handleClick: function handleClick() { | ||
var _dom4; | ||
(_dom4 = dom(api.buttonRef)) == null ? void 0 : _dom4.focus({ | ||
preventScroll: true | ||
}); | ||
} | ||
}; | ||
} | ||
}); // --- | ||
var ListboxButton = /*#__PURE__*/vue.defineComponent({ | ||
name: 'ListboxButton', | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'button' | ||
@@ -1144,8 +1371,7 @@ } | ||
render: function render$1() { | ||
var _dom5, _dom6; | ||
var _dom4; | ||
var api = useListboxContext('ListboxButton'); | ||
var api = useMenuContext('MenuButton'); | ||
var slot = { | ||
open: api.listboxState.value === ListboxStates.Open, | ||
disabled: api.disabled | ||
open: api.menuState.value === MenuStates.Open | ||
}; | ||
@@ -1157,7 +1383,5 @@ var propsWeControl = { | ||
'aria-haspopup': true, | ||
'aria-controls': (_dom5 = dom(api.optionsRef)) == null ? void 0 : _dom5.id, | ||
'aria-expanded': api.listboxState.value === ListboxStates.Open ? true : undefined, | ||
'aria-labelledby': api.labelRef.value ? [(_dom6 = dom(api.labelRef)) == null ? void 0 : _dom6.id, this.id].join(' ') : undefined, | ||
disabled: api.disabled, | ||
onKeyDown: this.handleKeyDown, | ||
'aria-controls': (_dom4 = dom(api.itemsRef)) == null ? void 0 : _dom4.id, | ||
'aria-expanded': api.menuState.value === MenuStates.Open ? true : undefined, | ||
onKeydown: this.handleKeyDown, | ||
onClick: this.handleClick | ||
@@ -1172,5 +1396,5 @@ }; | ||
}, | ||
setup: function setup() { | ||
var api = useListboxContext('ListboxButton'); | ||
var id = "headlessui-listbox-button-" + useId(); | ||
setup: function setup(props) { | ||
var api = useMenuContext('MenuButton'); | ||
var id = "headlessui-menu-button-" + useId(); | ||
@@ -1184,10 +1408,10 @@ function handleKeyDown(event) { | ||
event.preventDefault(); | ||
api.openListbox(); | ||
api.openMenu(); | ||
vue.nextTick(function () { | ||
var _dom7; | ||
var _dom5; | ||
(_dom7 = dom(api.optionsRef)) == null ? void 0 : _dom7.focus({ | ||
(_dom5 = dom(api.itemsRef)) == null ? void 0 : _dom5.focus({ | ||
preventScroll: true | ||
}); | ||
if (!api.value.value) api.goToOption(Focus.First); | ||
api.goToItem(Focus.First); | ||
}); | ||
@@ -1198,10 +1422,10 @@ break; | ||
event.preventDefault(); | ||
api.openListbox(); | ||
api.openMenu(); | ||
vue.nextTick(function () { | ||
var _dom8; | ||
var _dom6; | ||
(_dom8 = dom(api.optionsRef)) == null ? void 0 : _dom8.focus({ | ||
(_dom6 = dom(api.itemsRef)) == null ? void 0 : _dom6.focus({ | ||
preventScroll: true | ||
}); | ||
if (!api.value.value) api.goToOption(Focus.Last); | ||
api.goToItem(Focus.Last); | ||
}); | ||
@@ -1213,10 +1437,10 @@ break; | ||
function handleClick(event) { | ||
if (api.disabled) return; | ||
if (props.disabled) return; | ||
if (api.listboxState.value === ListboxStates.Open) { | ||
api.closeListbox(); | ||
if (api.menuState.value === MenuStates.Open) { | ||
api.closeMenu(); | ||
vue.nextTick(function () { | ||
var _dom9; | ||
var _dom7; | ||
return (_dom9 = dom(api.buttonRef)) == null ? void 0 : _dom9.focus({ | ||
return (_dom7 = dom(api.buttonRef)) == null ? void 0 : _dom7.focus({ | ||
preventScroll: true | ||
@@ -1227,7 +1451,7 @@ }); | ||
event.preventDefault(); | ||
api.openListbox(); | ||
api.openMenu(); | ||
nextFrame$1(function () { | ||
var _dom10; | ||
var _dom8; | ||
return (_dom10 = dom(api.optionsRef)) == null ? void 0 : _dom10.focus({ | ||
return (_dom8 = dom(api.itemsRef)) == null ? void 0 : _dom8.focus({ | ||
preventScroll: true | ||
@@ -1246,10 +1470,8 @@ }); | ||
} | ||
}); // --- | ||
var ListboxOptions = /*#__PURE__*/vue.defineComponent({ | ||
name: 'ListboxOptions', | ||
}); | ||
var MenuItems = /*#__PURE__*/vue.defineComponent({ | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'ul' | ||
"default": 'div' | ||
}, | ||
@@ -1266,14 +1488,14 @@ "static": { | ||
render: function render$1() { | ||
var _api$options$value$ap, _dom$id, _dom11, _dom12; | ||
var _api$items$value$api$, _dom9; | ||
var api = useListboxContext('ListboxOptions'); | ||
var api = useMenuContext('MenuItems'); | ||
var slot = { | ||
open: api.listboxState.value === ListboxStates.Open | ||
open: api.menuState.value === MenuStates.Open | ||
}; | ||
var propsWeControl = { | ||
'aria-activedescendant': api.activeOptionIndex.value === null ? undefined : (_api$options$value$ap = api.options.value[api.activeOptionIndex.value]) == null ? void 0 : _api$options$value$ap.id, | ||
'aria-labelledby': (_dom$id = (_dom11 = dom(api.labelRef)) == null ? void 0 : _dom11.id) != null ? _dom$id : (_dom12 = dom(api.buttonRef)) == null ? void 0 : _dom12.id, | ||
'aria-activedescendant': api.activeItemIndex.value === null ? undefined : (_api$items$value$api$ = api.items.value[api.activeItemIndex.value]) == null ? void 0 : _api$items$value$api$.id, | ||
'aria-labelledby': (_dom9 = dom(api.buttonRef)) == null ? void 0 : _dom9.id, | ||
id: this.id, | ||
onKeyDown: this.handleKeyDown, | ||
role: 'listbox', | ||
onKeydown: this.handleKeyDown, | ||
role: 'menu', | ||
tabIndex: 0, | ||
@@ -1293,6 +1515,22 @@ ref: 'el' | ||
setup: function setup() { | ||
var api = useListboxContext('ListboxOptions'); | ||
var id = "headlessui-listbox-options-" + useId(); | ||
var api = useMenuContext('MenuItems'); | ||
var id = "headlessui-menu-items-" + useId(); | ||
var searchDebounce = vue.ref(null); | ||
vue.watchEffect(function () { | ||
var container = dom(api.itemsRef); | ||
if (!container) return; | ||
if (api.menuState.value !== MenuStates.Open) return; | ||
var walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT, { | ||
acceptNode: function acceptNode(node) { | ||
if (node.getAttribute('role') === 'menuitem') return NodeFilter.FILTER_REJECT; | ||
if (node.hasAttribute('role')) return NodeFilter.FILTER_SKIP; | ||
return NodeFilter.FILTER_ACCEPT; | ||
} | ||
}); | ||
while (walker.nextNode()) { | ||
walker.currentNode.setAttribute('role', 'none'); | ||
} | ||
}); | ||
function handleKeyDown(event) { | ||
@@ -1315,12 +1553,14 @@ if (searchDebounce.value) clearTimeout(searchDebounce.value); | ||
if (api.activeOptionIndex.value !== null) { | ||
var dataRef = api.options.value[api.activeOptionIndex.value].dataRef; | ||
api.select(dataRef.value); | ||
if (api.activeItemIndex.value !== null) { | ||
var _document$getElementB; | ||
var _id = api.items.value[api.activeItemIndex.value].id; | ||
(_document$getElementB = document.getElementById(_id)) == null ? void 0 : _document$getElementB.click(); | ||
} | ||
api.closeListbox(); | ||
api.closeMenu(); | ||
vue.nextTick(function () { | ||
var _dom13; | ||
var _dom10; | ||
return (_dom13 = dom(api.buttonRef)) == null ? void 0 : _dom13.focus({ | ||
return (_dom10 = dom(api.buttonRef)) == null ? void 0 : _dom10.focus({ | ||
preventScroll: true | ||
@@ -1333,7 +1573,7 @@ }); | ||
event.preventDefault(); | ||
return api.goToOption(Focus.Next); | ||
return api.goToItem(Focus.Next); | ||
case Keys.ArrowUp: | ||
event.preventDefault(); | ||
return api.goToOption(Focus.Previous); | ||
return api.goToItem(Focus.Previous); | ||
@@ -1343,3 +1583,3 @@ case Keys.Home: | ||
event.preventDefault(); | ||
return api.goToOption(Focus.First); | ||
return api.goToItem(Focus.First); | ||
@@ -1349,11 +1589,11 @@ case Keys.End: | ||
event.preventDefault(); | ||
return api.goToOption(Focus.Last); | ||
return api.goToItem(Focus.Last); | ||
case Keys.Escape: | ||
event.preventDefault(); | ||
api.closeListbox(); | ||
api.closeMenu(); | ||
vue.nextTick(function () { | ||
var _dom14; | ||
var _dom11; | ||
return (_dom14 = dom(api.buttonRef)) == null ? void 0 : _dom14.focus({ | ||
return (_dom11 = dom(api.buttonRef)) == null ? void 0 : _dom11.focus({ | ||
preventScroll: true | ||
@@ -1381,3 +1621,3 @@ }); | ||
id: id, | ||
el: api.optionsRef, | ||
el: api.itemsRef, | ||
handleKeyDown: handleKeyDown | ||
@@ -1387,13 +1627,8 @@ }; | ||
}); | ||
var ListboxOption = /*#__PURE__*/vue.defineComponent({ | ||
name: 'ListboxOption', | ||
var MenuItem = /*#__PURE__*/vue.defineComponent({ | ||
props: { | ||
as: { | ||
type: [Object, String], | ||
"default": 'li' | ||
"default": 'template' | ||
}, | ||
value: { | ||
type: [Object, String], | ||
"default": null | ||
}, | ||
disabled: { | ||
@@ -1415,46 +1650,29 @@ type: Boolean, | ||
attrs = _ref2.attrs; | ||
var api = useListboxContext('ListboxOption'); | ||
var id = "headlessui-listbox-option-" + useId(); | ||
var api = useMenuContext('MenuItem'); | ||
var id = "headlessui-menu-item-" + useId(); | ||
var disabled = props.disabled, | ||
defaultClass = props["class"], | ||
_props$className = props.className, | ||
className = _props$className === void 0 ? defaultClass : _props$className, | ||
value = props.value; | ||
className = _props$className === void 0 ? defaultClass : _props$className; | ||
var active = vue.computed(function () { | ||
return api.activeOptionIndex.value !== null ? api.options.value[api.activeOptionIndex.value].id === id : false; | ||
return api.activeItemIndex.value !== null ? api.items.value[api.activeItemIndex.value].id === id : false; | ||
}); | ||
var selected = vue.computed(function () { | ||
return vue.toRaw(api.value.value) === vue.toRaw(value); | ||
}); | ||
var dataRef = vue.ref({ | ||
disabled: disabled, | ||
value: value, | ||
textValue: '' | ||
}); | ||
vue.onMounted(function () { | ||
var _document$getElementB, _document$getElementB2; | ||
var _document$getElementB2, _document$getElementB3; | ||
var textValue = (_document$getElementB = document.getElementById(id)) == null ? void 0 : (_document$getElementB2 = _document$getElementB.textContent) == null ? void 0 : _document$getElementB2.toLowerCase().trim(); | ||
var textValue = (_document$getElementB2 = document.getElementById(id)) == null ? void 0 : (_document$getElementB3 = _document$getElementB2.textContent) == null ? void 0 : _document$getElementB3.toLowerCase().trim(); | ||
if (textValue !== undefined) dataRef.value.textValue = textValue; | ||
}); | ||
vue.onMounted(function () { | ||
return api.registerOption(id, dataRef); | ||
return api.registerItem(id, dataRef); | ||
}); | ||
vue.onUnmounted(function () { | ||
return api.unregisterOption(id); | ||
return api.unregisterItem(id); | ||
}); | ||
vue.onMounted(function () { | ||
vue.watch([api.listboxState, selected], function () { | ||
var _document$getElementB3; | ||
if (api.listboxState.value !== ListboxStates.Open) return; | ||
if (!selected.value) return; | ||
api.goToOption(Focus.Specific, id); | ||
(_document$getElementB3 = document.getElementById(id)) == null ? void 0 : _document$getElementB3.focus == null ? void 0 : _document$getElementB3.focus(); | ||
}, { | ||
immediate: true | ||
}); | ||
}); | ||
vue.watchEffect(function () { | ||
if (api.listboxState.value !== ListboxStates.Open) return; | ||
if (api.menuState.value !== MenuStates.Open) return; | ||
if (!active.value) return; | ||
@@ -1472,8 +1690,7 @@ vue.nextTick(function () { | ||
if (disabled) return event.preventDefault(); | ||
api.select(value); | ||
api.closeListbox(); | ||
api.closeMenu(); | ||
vue.nextTick(function () { | ||
var _dom15; | ||
var _dom12; | ||
return (_dom15 = dom(api.buttonRef)) == null ? void 0 : _dom15.focus({ | ||
return (_dom12 = dom(api.buttonRef)) == null ? void 0 : _dom12.focus({ | ||
preventScroll: true | ||
@@ -1485,4 +1702,4 @@ }); | ||
function handleFocus() { | ||
if (disabled) return api.goToOption(Focus.Nothing); | ||
api.goToOption(Focus.Specific, id); | ||
if (disabled) return api.goToItem(Focus.Nothing); | ||
api.goToItem(Focus.Specific, id); | ||
} | ||
@@ -1493,3 +1710,3 @@ | ||
if (active.value) return; | ||
api.goToOption(Focus.Specific, id); | ||
api.goToItem(Focus.Specific, id); | ||
} | ||
@@ -1500,3 +1717,3 @@ | ||
if (!active.value) return; | ||
api.goToOption(Focus.Nothing); | ||
api.goToItem(Focus.Nothing); | ||
} | ||
@@ -1507,3 +1724,2 @@ | ||
active: active.value, | ||
selected: selected.value, | ||
disabled: disabled | ||
@@ -1513,13 +1729,12 @@ }; | ||
id: id, | ||
role: 'option', | ||
role: 'menuitem', | ||
tabIndex: -1, | ||
"class": resolvePropValue(className, slot), | ||
'aria-disabled': disabled === true ? true : undefined, | ||
'aria-selected': selected.value === true ? selected.value : undefined, | ||
onClick: handleClick, | ||
onFocus: handleFocus, | ||
onPointerMove: handleMove, | ||
onMouseMove: handleMove, | ||
onPointerLeave: handleLeave, | ||
onMouseLeave: handleLeave | ||
onPointermove: handleMove, | ||
onMousemove: handleMove, | ||
onPointerleave: handleLeave, | ||
onMouseleave: handleLeave | ||
}; | ||
@@ -1632,4 +1847,4 @@ return render({ | ||
onClick: this.handleClick, | ||
onKeyUp: this.handleKeyUp, | ||
onKeyPress: this.handleKeyPress | ||
onKeyup: this.handleKeyUp, | ||
onKeypress: this.handleKeyPress | ||
}; | ||
@@ -1746,2 +1961,6 @@ | ||
exports.Alert = Alert; | ||
exports.Disclosure = Disclosure; | ||
exports.DisclosureButton = DisclosureButton; | ||
exports.DisclosurePanel = DisclosurePanel; | ||
exports.Listbox = Listbox; | ||
@@ -1748,0 +1967,0 @@ exports.ListboxButton = ListboxButton; |
@@ -1,2 +0,2 @@ | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("vue")):"function"==typeof define&&define.amd?define(["exports","vue"],t):t((e=e||self).headlessui={},e.Vue)}(this,(function(e,t){"use strict";function n(){return(n=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e}).apply(this,arguments)}function o(e,t){if(null==e)return{};var n,o,r={},i=Object.keys(e);for(o=0;o<i.length;o++)t.indexOf(n=i[o])>=0||(r[n]=e[n]);return r}function r(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=new Array(t);n<t;n++)o[n]=e[n];return o}function i(e,t){if(e in t){for(var n=t[e],o=arguments.length,r=new Array(o>2?o-2:0),l=2;l<o;l++)r[l-2]=arguments[l];return"function"==typeof n?n.apply(void 0,r):n}var u=new Error('Tried to handle "'+e+'" but there is no handler defined. Only defined handlers are: '+Object.keys(t).map((function(e){return'"'+e+'"'})).join(", ")+".");throw Error.captureStackTrace&&Error.captureStackTrace(u,i),u}var l,u;function a(e){var t,r,a=e.visible,c=void 0===a||a,d=e.features,f=void 0===d?l.None:d,p=o(e,["visible","features"]);return c||f&l.Static&&p.props.static?s(p):f&l.RenderStrategy?i(null==(t=p.props.unmount)||t?u.Unmount:u.Hidden,((r={})[u.Unmount]=function(){return null},r[u.Hidden]=function(){return s(n({},p,{props:n({},p.props,{hidden:!0,style:{display:"none"}})}))},r)):s(p)}function s(e){var n=e.attrs,i=e.slots,l=e.slot,u=function(e,t){void 0===t&&(t=[]);for(var n,o=Object.assign({},e),i=function(e,t){var n;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return r(e,void 0);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?r(e,void 0):void 0}}(e))){n&&(e=n);var o=0;return function(){return o>=e.length?{done:!0}:{done:!1,value:e[o++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}return(n=e[Symbol.iterator]()).next.bind(n)}(t);!(n=i()).done;){var l=n.value;l in o&&delete o[l]}return o}(e.props,["unmount","static"]),a=u.as,s=o(u,["as"]),c=null==i.default?void 0:i.default(l);if("template"===a){if(Object.keys(s).length>0||"class"in n){var d=null!=c?c:[],f=d[0];if(d.slice(1).length>0)throw new Error('You should only render 1 child or use the `as="..."` prop');return t.cloneVNode(f,s)}return c}return t.h(a,s,c)}!function(e){e[e.None=0]="None",e[e.RenderStrategy=1]="RenderStrategy",e[e.Static=2]="Static"}(l||(l={})),function(e){e[e.Unmount=0]="Unmount",e[e.Hidden=1]="Hidden"}(u||(u={}));var c,d,f,p=0;function v(){return++p}function b(e,t){var n=t.resolveItems();if(n.length<=0)return null;var o=t.resolveActiveIndex(),r=null!=o?o:-1,i=function(){switch(e.focus){case d.First:return n.findIndex((function(e){return!t.resolveDisabled(e)}));case d.Previous:var o=n.slice().reverse().findIndex((function(e,n,o){return!(-1!==r&&o.length-n-1>=r||t.resolveDisabled(e))}));return-1===o?o:n.length-1-o;case d.Next:return n.findIndex((function(e,n){return!(n<=r||t.resolveDisabled(e))}));case d.Last:var i=n.slice().reverse().findIndex((function(e){return!t.resolveDisabled(e)}));return-1===i?i:n.length-1-i;case d.Specific:return n.findIndex((function(n){return t.resolveId(n)===e.id}));case d.Nothing:return null;default:!function(e){throw new Error("Unexpected object: "+e)}(e)}}();return-1===i?o:i}function m(e,t){if(void 0!==e)return"function"==typeof e?e(t):e}function h(e){var t;return null==e||null==e.value?null:null!=(t=e.value.$el)?t:e.value}!function(e){e.Space=" ",e.Enter="Enter",e.Escape="Escape",e.Backspace="Backspace",e.ArrowUp="ArrowUp",e.ArrowDown="ArrowDown",e.Home="Home",e.End="End",e.PageUp="PageUp",e.PageDown="PageDown",e.Tab="Tab"}(c||(c={})),function(e){e[e.First=0]="First",e[e.Previous=1]="Previous",e[e.Next=2]="Next",e[e.Last=3]="Last",e[e.Specific=4]="Specific",e[e.Nothing=5]="Nothing"}(d||(d={})),function(e){e[e.Open=0]="Open",e[e.Closed=1]="Closed"}(f||(f={}));var y=Symbol("MenuContext");function S(e){var n=t.inject(y,null);if(null===n){var o=new Error("<"+e+" /> is missing a parent <Menu /> component.");throw Error.captureStackTrace&&Error.captureStackTrace(o,S),o}return n}var x,g=t.defineComponent({props:{as:{type:[Object,String],default:"template"}},setup:function(e,n){var o=n.slots,r=n.attrs,i=t.ref(f.Closed),l=t.ref(null),u=t.ref(null),s=t.ref([]),c=t.ref(""),p=t.ref(null),v={menuState:i,buttonRef:l,itemsRef:u,items:s,searchQuery:c,activeItemIndex:p,closeMenu:function(){i.value=f.Closed,p.value=null},openMenu:function(){return i.value=f.Open},goToItem:function(e,t){var n=b(e===d.Specific?{focus:d.Specific,id:t}:{focus:e},{resolveItems:function(){return s.value},resolveActiveIndex:function(){return p.value},resolveId:function(e){return e.id},resolveDisabled:function(e){return e.dataRef.disabled}});""===c.value&&p.value===n||(c.value="",p.value=n)},search:function(e){c.value+=e;var t=s.value.findIndex((function(e){return e.dataRef.textValue.startsWith(c.value)&&!e.dataRef.disabled}));-1!==t&&t!==p.value&&(p.value=t)},clearSearch:function(){c.value=""},registerItem:function(e,t){s.value.push({id:e,dataRef:t})},unregisterItem:function(e){var t=s.value.slice(),n=null!==p.value?t[p.value]:null,o=t.findIndex((function(t){return t.id===e}));-1!==o&&t.splice(o,1),s.value=t,p.value=o===p.value||null===n?null:t.indexOf(n)}};return t.onMounted((function(){function e(e){var t,n,o,r=e.target,a=document.activeElement;i.value===f.Open&&((null==(t=h(l))?void 0:t.contains(r))||((null==(n=h(u))?void 0:n.contains(r))||v.closeMenu(),a!==document.body&&(null==a?void 0:a.contains(r))||e.defaultPrevented||null==(o=h(l))||o.focus({preventScroll:!0})))}window.addEventListener("mousedown",e),t.onUnmounted((function(){return window.removeEventListener("mousedown",e)}))})),t.provide(y,v),function(){return a({props:e,slot:{open:i.value===f.Open},slots:o,attrs:r})}}}),w=t.defineComponent({props:{disabled:{type:Boolean,default:!1},as:{type:[Object,String],default:"button"}},render:function(){var e,t=S("MenuButton"),o={open:t.menuState.value===f.Open},r={ref:"el",id:this.id,type:"button","aria-haspopup":!0,"aria-controls":null==(e=h(t.itemsRef))?void 0:e.id,"aria-expanded":t.menuState.value===f.Open||void 0,onKeyDown:this.handleKeyDown,onClick:this.handleClick};return a({props:n({},this.$props,r),slot:o,attrs:this.$attrs,slots:this.$slots})},setup:function(e){var n=S("MenuButton");return{id:"headlessui-menu-button-"+v(),el:n.buttonRef,handleKeyDown:function(e){switch(e.key){case c.Space:case c.Enter:case c.ArrowDown:e.preventDefault(),n.openMenu(),t.nextTick((function(){var e;null==(e=h(n.itemsRef))||e.focus({preventScroll:!0}),n.goToItem(d.First)}));break;case c.ArrowUp:e.preventDefault(),n.openMenu(),t.nextTick((function(){var e;null==(e=h(n.itemsRef))||e.focus({preventScroll:!0}),n.goToItem(d.Last)}))}},handleClick:function(o){var r;e.disabled||(n.menuState.value===f.Open?(n.closeMenu(),t.nextTick((function(){var e;return null==(e=h(n.buttonRef))?void 0:e.focus({preventScroll:!0})}))):(o.preventDefault(),n.openMenu(),r=function(){var e;return null==(e=h(n.itemsRef))?void 0:e.focus({preventScroll:!0})},requestAnimationFrame((function(){return requestAnimationFrame(r)}))))}}}}),O=t.defineComponent({props:{as:{type:[Object,String],default:"div"},static:{type:Boolean,default:!1},unmount:{type:Boolean,default:!0}},render:function(){var e,t,o=S("MenuItems"),r={open:o.menuState.value===f.Open},i={"aria-activedescendant":null===o.activeItemIndex.value||null==(e=o.items.value[o.activeItemIndex.value])?void 0:e.id,"aria-labelledby":null==(t=h(o.buttonRef))?void 0:t.id,id:this.id,onKeyDown:this.handleKeyDown,role:"menu",tabIndex:0,ref:"el"};return a({props:n({},this.$props,i),slot:r,attrs:this.$attrs,slots:this.$slots,features:l.RenderStrategy|l.Static,visible:r.open})},setup:function(){var e=S("MenuItems"),n="headlessui-menu-items-"+v(),o=t.ref(null);return t.watchEffect((function(){var t=h(e.itemsRef);if(t&&e.menuState.value===f.Open)for(var n=document.createTreeWalker(t,NodeFilter.SHOW_ELEMENT,{acceptNode:function(e){return"menuitem"===e.getAttribute("role")?NodeFilter.FILTER_REJECT:e.hasAttribute("role")?NodeFilter.FILTER_SKIP:NodeFilter.FILTER_ACCEPT}});n.nextNode();)n.currentNode.setAttribute("role","none")})),{id:n,el:e.itemsRef,handleKeyDown:function(n){switch(o.value&&clearTimeout(o.value),n.key){case c.Space:if(""!==e.searchQuery.value)return n.preventDefault(),e.search(n.key);case c.Enter:var r;n.preventDefault(),null!==e.activeItemIndex.value&&(null==(r=document.getElementById(e.items.value[e.activeItemIndex.value].id))||r.click()),e.closeMenu(),t.nextTick((function(){var t;return null==(t=h(e.buttonRef))?void 0:t.focus({preventScroll:!0})}));break;case c.ArrowDown:return n.preventDefault(),e.goToItem(d.Next);case c.ArrowUp:return n.preventDefault(),e.goToItem(d.Previous);case c.Home:case c.PageUp:return n.preventDefault(),e.goToItem(d.First);case c.End:case c.PageDown:return n.preventDefault(),e.goToItem(d.Last);case c.Escape:n.preventDefault(),e.closeMenu(),t.nextTick((function(){var t;return null==(t=h(e.buttonRef))?void 0:t.focus({preventScroll:!0})}));break;case c.Tab:return n.preventDefault();default:1===n.key.length&&(e.search(n.key),o.value=setTimeout((function(){return e.clearSearch()}),350))}}}}}),I=t.defineComponent({props:{as:{type:[Object,String],default:"template"},disabled:{type:Boolean,default:!1},class:{type:[String,Function],required:!1},className:{type:[String,Function],required:!1}},setup:function(e,o){var r=o.slots,i=o.attrs,l=S("MenuItem"),u="headlessui-menu-item-"+v(),s=e.disabled,c=e.className,p=void 0===c?e.class:c,b=t.computed((function(){return null!==l.activeItemIndex.value&&l.items.value[l.activeItemIndex.value].id===u})),y=t.ref({disabled:s,textValue:""});function x(e){if(s)return e.preventDefault();l.closeMenu(),t.nextTick((function(){var e;return null==(e=h(l.buttonRef))?void 0:e.focus({preventScroll:!0})}))}function g(){if(s)return l.goToItem(d.Nothing);l.goToItem(d.Specific,u)}function w(){s||b.value||l.goToItem(d.Specific,u)}function O(){s||b.value&&l.goToItem(d.Nothing)}return t.onMounted((function(){var e,t,n=null==(e=document.getElementById(u))||null==(t=e.textContent)?void 0:t.toLowerCase().trim();void 0!==n&&(y.value.textValue=n)})),t.onMounted((function(){return l.registerItem(u,y)})),t.onUnmounted((function(){return l.unregisterItem(u)})),t.watchEffect((function(){l.menuState.value===f.Open&&b.value&&t.nextTick((function(){var e;return null==(e=document.getElementById(u))||null==e.scrollIntoView?void 0:e.scrollIntoView({block:"nearest"})}))})),function(){var t={active:b.value,disabled:s},o={id:u,role:"menuitem",tabIndex:-1,class:m(p,t),"aria-disabled":!0===s||void 0,onClick:x,onFocus:g,onPointerMove:w,onMouseMove:w,onPointerLeave:O,onMouseLeave:O};return a({props:n({},e,o),slot:t,attrs:i,slots:r})}}});!function(e){e[e.Open=0]="Open",e[e.Closed=1]="Closed"}(x||(x={}));var k=Symbol("ListboxContext");function R(e){var n=t.inject(k,null);if(null===n){var o=new Error("<"+e+" /> is missing a parent <Listbox /> component.");throw Error.captureStackTrace&&Error.captureStackTrace(o,R),o}return n}var T=t.defineComponent({name:"Listbox",emits:["update:modelValue"],props:{as:{type:[Object,String],default:"template"},disabled:{type:[Boolean],default:!1},modelValue:{type:[Object,String,Number,Boolean],default:null}},setup:function(e,n){var r=n.slots,i=n.attrs,l=n.emit,u=e.disabled,s=o(e,["modelValue","disabled"]),c=t.ref(x.Closed),f=t.ref(null),p=t.ref(null),v=t.ref(null),m=t.ref([]),y=t.ref(""),S=t.ref(null),g=t.computed((function(){return e.modelValue})),w={listboxState:c,value:g,labelRef:f,buttonRef:p,optionsRef:v,disabled:u,options:m,searchQuery:y,activeOptionIndex:S,closeListbox:function(){u||c.value!==x.Closed&&(c.value=x.Closed,S.value=null)},openListbox:function(){u||c.value!==x.Open&&(c.value=x.Open)},goToOption:function(e,t){if(!u&&c.value!==x.Closed){var n=b(e===d.Specific?{focus:d.Specific,id:t}:{focus:e},{resolveItems:function(){return m.value},resolveActiveIndex:function(){return S.value},resolveId:function(e){return e.id},resolveDisabled:function(e){return e.dataRef.disabled}});""===y.value&&S.value===n||(y.value="",S.value=n)}},search:function(e){if(!u&&c.value!==x.Closed){y.value+=e;var t=m.value.findIndex((function(e){return!e.dataRef.disabled&&e.dataRef.textValue.startsWith(y.value)}));-1!==t&&t!==S.value&&(S.value=t)}},clearSearch:function(){u||c.value!==x.Closed&&""!==y.value&&(y.value="")},registerOption:function(e,t){m.value.push({id:e,dataRef:t})},unregisterOption:function(e){var t=m.value.slice(),n=null!==S.value?t[S.value]:null,o=t.findIndex((function(t){return t.id===e}));-1!==o&&t.splice(o,1),m.value=t,S.value=o===S.value||null===n?null:t.indexOf(n)},select:function(e){u||l("update:modelValue",e)}};return t.onMounted((function(){function e(e){var t,n,o,r=e.target,i=document.activeElement;c.value===x.Open&&((null==(t=h(p))?void 0:t.contains(r))||((null==(n=h(v))?void 0:n.contains(r))||w.closeListbox(),i!==document.body&&(null==i?void 0:i.contains(r))||e.defaultPrevented||null==(o=h(p))||o.focus({preventScroll:!0})))}window.addEventListener("mousedown",e),t.onUnmounted((function(){return window.removeEventListener("mousedown",e)}))})),t.provide(k,w),function(){return a({props:s,slot:{open:c.value===x.Open,disabled:u},slots:r,attrs:i})}}}),D=t.defineComponent({name:"ListboxLabel",props:{as:{type:[Object,String],default:"label"}},render:function(){var e=R("ListboxLabel"),t={open:e.listboxState.value===x.Open,disabled:e.disabled};return a({props:n({},this.$props,{id:this.id,ref:"el",onClick:this.handleClick}),slot:t,attrs:this.$attrs,slots:this.$slots})},setup:function(){var e=R("ListboxLabel");return{id:"headlessui-listbox-label-"+v(),el:e.labelRef,handleClick:function(){var t;null==(t=h(e.buttonRef))||t.focus({preventScroll:!0})}}}}),L=t.defineComponent({name:"ListboxButton",props:{as:{type:[Object,String],default:"button"}},render:function(){var e,t,o=R("ListboxButton"),r={open:o.listboxState.value===x.Open,disabled:o.disabled},i={ref:"el",id:this.id,type:"button","aria-haspopup":!0,"aria-controls":null==(e=h(o.optionsRef))?void 0:e.id,"aria-expanded":o.listboxState.value===x.Open||void 0,"aria-labelledby":o.labelRef.value?[null==(t=h(o.labelRef))?void 0:t.id,this.id].join(" "):void 0,disabled:o.disabled,onKeyDown:this.handleKeyDown,onClick:this.handleClick};return a({props:n({},this.$props,i),slot:r,attrs:this.$attrs,slots:this.$slots})},setup:function(){var e=R("ListboxButton");return{id:"headlessui-listbox-button-"+v(),el:e.buttonRef,handleKeyDown:function(n){switch(n.key){case c.Space:case c.Enter:case c.ArrowDown:n.preventDefault(),e.openListbox(),t.nextTick((function(){var t;null==(t=h(e.optionsRef))||t.focus({preventScroll:!0}),e.value.value||e.goToOption(d.First)}));break;case c.ArrowUp:n.preventDefault(),e.openListbox(),t.nextTick((function(){var t;null==(t=h(e.optionsRef))||t.focus({preventScroll:!0}),e.value.value||e.goToOption(d.Last)}))}},handleClick:function(n){var o;e.disabled||(e.listboxState.value===x.Open?(e.closeListbox(),t.nextTick((function(){var t;return null==(t=h(e.buttonRef))?void 0:t.focus({preventScroll:!0})}))):(n.preventDefault(),e.openListbox(),o=function(){var t;return null==(t=h(e.optionsRef))?void 0:t.focus({preventScroll:!0})},requestAnimationFrame((function(){return requestAnimationFrame(o)}))))}}}}),C=t.defineComponent({name:"ListboxOptions",props:{as:{type:[Object,String],default:"ul"},static:{type:Boolean,default:!1},unmount:{type:Boolean,default:!0}},render:function(){var e,t,o,r,i=R("ListboxOptions"),u={open:i.listboxState.value===x.Open},s={"aria-activedescendant":null===i.activeOptionIndex.value||null==(e=i.options.value[i.activeOptionIndex.value])?void 0:e.id,"aria-labelledby":null!=(t=null==(o=h(i.labelRef))?void 0:o.id)?t:null==(r=h(i.buttonRef))?void 0:r.id,id:this.id,onKeyDown:this.handleKeyDown,role:"listbox",tabIndex:0,ref:"el"};return a({props:n({},this.$props,s),slot:u,attrs:this.$attrs,slots:this.$slots,features:l.RenderStrategy|l.Static,visible:u.open})},setup:function(){var e=R("ListboxOptions"),n="headlessui-listbox-options-"+v(),o=t.ref(null);return{id:n,el:e.optionsRef,handleKeyDown:function(n){switch(o.value&&clearTimeout(o.value),n.key){case c.Space:if(""!==e.searchQuery.value)return n.preventDefault(),e.search(n.key);case c.Enter:n.preventDefault(),null!==e.activeOptionIndex.value&&e.select(e.options.value[e.activeOptionIndex.value].dataRef.value),e.closeListbox(),t.nextTick((function(){var t;return null==(t=h(e.buttonRef))?void 0:t.focus({preventScroll:!0})}));break;case c.ArrowDown:return n.preventDefault(),e.goToOption(d.Next);case c.ArrowUp:return n.preventDefault(),e.goToOption(d.Previous);case c.Home:case c.PageUp:return n.preventDefault(),e.goToOption(d.First);case c.End:case c.PageDown:return n.preventDefault(),e.goToOption(d.Last);case c.Escape:n.preventDefault(),e.closeListbox(),t.nextTick((function(){var t;return null==(t=h(e.buttonRef))?void 0:t.focus({preventScroll:!0})}));break;case c.Tab:return n.preventDefault();default:1===n.key.length&&(e.search(n.key),o.value=setTimeout((function(){return e.clearSearch()}),350))}}}}}),E=t.defineComponent({name:"ListboxOption",props:{as:{type:[Object,String],default:"li"},value:{type:[Object,String],default:null},disabled:{type:Boolean,default:!1},class:{type:[String,Function],required:!1},className:{type:[String,Function],required:!1}},setup:function(e,o){var r=o.slots,i=o.attrs,l=R("ListboxOption"),u="headlessui-listbox-option-"+v(),s=e.disabled,c=e.className,f=void 0===c?e.class:c,p=e.value,b=t.computed((function(){return null!==l.activeOptionIndex.value&&l.options.value[l.activeOptionIndex.value].id===u})),y=t.computed((function(){return t.toRaw(l.value.value)===t.toRaw(p)})),S=t.ref({disabled:s,value:p,textValue:""});function g(e){if(s)return e.preventDefault();l.select(p),l.closeListbox(),t.nextTick((function(){var e;return null==(e=h(l.buttonRef))?void 0:e.focus({preventScroll:!0})}))}function w(){if(s)return l.goToOption(d.Nothing);l.goToOption(d.Specific,u)}function O(){s||b.value||l.goToOption(d.Specific,u)}function I(){s||b.value&&l.goToOption(d.Nothing)}return t.onMounted((function(){var e,t,n=null==(e=document.getElementById(u))||null==(t=e.textContent)?void 0:t.toLowerCase().trim();void 0!==n&&(S.value.textValue=n)})),t.onMounted((function(){return l.registerOption(u,S)})),t.onUnmounted((function(){return l.unregisterOption(u)})),t.onMounted((function(){t.watch([l.listboxState,y],(function(){var e;l.listboxState.value===x.Open&&y.value&&(l.goToOption(d.Specific,u),null==(e=document.getElementById(u))||null==e.focus||e.focus())}),{immediate:!0})})),t.watchEffect((function(){l.listboxState.value===x.Open&&b.value&&t.nextTick((function(){var e;return null==(e=document.getElementById(u))||null==e.scrollIntoView?void 0:e.scrollIntoView({block:"nearest"})}))})),function(){var t={active:b.value,selected:y.value,disabled:s},o={id:u,role:"option",tabIndex:-1,class:m(f,t),"aria-disabled":!0===s||void 0,"aria-selected":!0===y.value?y.value:void 0,onClick:g,onFocus:w,onPointerMove:O,onMouseMove:O,onPointerLeave:I,onMouseLeave:I};return a({props:n({},e,o),slot:t,attrs:i,slots:r})}}}),M=Symbol("GroupContext");function j(e){var n=t.inject(M,null);if(null===n){var o=new Error("<"+e+" /> is missing a parent <SwitchGroup /> component.");throw Error.captureStackTrace&&Error.captureStackTrace(o,j),o}return n}var N=t.defineComponent({name:"SwitchGroup",props:{as:{type:[Object,String],default:"template"}},setup:function(e,n){var o=n.slots,r=n.attrs,i=t.ref(null),l=t.ref(null),u=t.ref(null);return t.provide(M,{switchRef:i,labelRef:l,descriptionRef:u}),function(){return a({props:e,slot:{},slots:o,attrs:r})}}}),A=t.defineComponent({name:"Switch",emits:["update:modelValue"],props:{as:{type:[Object,String],default:"button"},modelValue:{type:[Object,Boolean],default:null},class:{type:[String,Function],required:!1},className:{type:[String,Function],required:!1}},render:function(){var e=t.inject(M,null),o=this.$props,r=o.className,i=void 0===r?o.class:r,l=t.computed((function(){var t;return null==(t=h(null==e?void 0:e.labelRef))?void 0:t.id})),u=t.computed((function(){var t;return null==(t=h(null==e?void 0:e.descriptionRef))?void 0:t.id})),s={checked:this.$props.modelValue},c={id:this.id,ref:null===e?void 0:e.switchRef,role:"switch",tabIndex:0,class:m(i,s),"aria-checked":this.$props.modelValue,"aria-labelledby":l.value,"aria-describedby":u.value,onClick:this.handleClick,onKeyUp:this.handleKeyUp,onKeyPress:this.handleKeyPress};return"button"===this.$props.as&&Object.assign(c,{type:"button"}),a({props:n({},this.$props,c),slot:s,attrs:this.$attrs,slots:this.$slots})},setup:function(e,n){var o=n.emit,r=t.inject(M,null);function i(){o("update:modelValue",!e.modelValue)}return{id:"headlessui-switch-"+v(),el:null==r?void 0:r.switchRef,handleClick:function(e){e.preventDefault(),i()},handleKeyUp:function(e){e.key!==c.Tab&&e.preventDefault(),e.key===c.Space&&i()},handleKeyPress:function(e){e.preventDefault()}}}}),$=t.defineComponent({name:"SwitchLabel",props:{as:{type:[Object,String],default:"label"}},render:function(){return a({props:n({},this.$props,{id:this.id,ref:"el",onClick:this.handleClick}),slot:{},attrs:this.$attrs,slots:this.$slots})},setup:function(){var e=j("SwitchLabel");return{id:"headlessui-switch-label-"+v(),el:e.labelRef,handleClick:function(){var t=h(e.switchRef);null==t||t.click(),null==t||t.focus({preventScroll:!0})}}}}),F=t.defineComponent({name:"SwitchDescription",props:{as:{type:[Object,String],default:"p"}},render:function(){return a({props:n({},this.$props,{id:this.id,ref:"el"}),slot:{},attrs:this.$attrs,slots:this.$slots})},setup:function(){var e=j("SwitchDescription");return{id:"headlessui-switch-description-"+v(),el:e.descriptionRef}}});e.Listbox=T,e.ListboxButton=L,e.ListboxLabel=D,e.ListboxOption=E,e.ListboxOptions=C,e.Menu=g,e.MenuButton=w,e.MenuItem=I,e.MenuItems=O,e.Switch=A,e.SwitchDescription=F,e.SwitchGroup=N,e.SwitchLabel=$,Object.defineProperty(e,"__esModule",{value:!0})})); | ||
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("vue")):"function"==typeof define&&define.amd?define(["exports","vue"],t):t((e=e||self).headlessui={},e.Vue)}(this,(function(e,t){"use strict";function n(){return(n=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&(e[o]=n[o])}return e}).apply(this,arguments)}function o(e,t){if(null==e)return{};var n,o,r={},l=Object.keys(e);for(o=0;o<l.length;o++)t.indexOf(n=l[o])>=0||(r[n]=e[n]);return r}function r(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,o=new Array(t);n<t;n++)o[n]=e[n];return o}function l(e,t){if(e in t){for(var n=t[e],o=arguments.length,r=new Array(o>2?o-2:0),i=2;i<o;i++)r[i-2]=arguments[i];return"function"==typeof n?n.apply(void 0,r):n}var u=new Error('Tried to handle "'+e+'" but there is no handler defined. Only defined handlers are: '+Object.keys(t).map((function(e){return'"'+e+'"'})).join(", ")+".");throw Error.captureStackTrace&&Error.captureStackTrace(u,l),u}var i,u;function a(e){var t,r,a=e.visible,c=void 0===a||a,d=e.features,p=void 0===d?i.None:d,f=o(e,["visible","features"]);return c||p&i.Static&&f.props.static?s(f):p&i.RenderStrategy?l(null==(t=f.props.unmount)||t?u.Unmount:u.Hidden,((r={})[u.Unmount]=function(){return null},r[u.Hidden]=function(){return s(n({},f,{props:n({},f.props,{hidden:!0,style:{display:"none"}})}))},r)):s(f)}function s(e){var n=e.attrs,l=e.slots,i=e.slot,u=function(e,t){void 0===t&&(t=[]);for(var n,o=Object.assign({},e),l=function(e,t){var n;if("undefined"==typeof Symbol||null==e[Symbol.iterator]){if(Array.isArray(e)||(n=function(e,t){if(e){if("string"==typeof e)return r(e,void 0);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?r(e,void 0):void 0}}(e))){n&&(e=n);var o=0;return function(){return o>=e.length?{done:!0}:{done:!1,value:e[o++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}return(n=e[Symbol.iterator]()).next.bind(n)}(t);!(n=l()).done;){var i=n.value;i in o&&delete o[i]}return o}(e.props,["unmount","static"]),a=u.as,s=o(u,["as"]),c=null==l.default?void 0:l.default(i);if("template"===a){if(Object.keys(s).length>0||"class"in n){var d=null!=c?c:[],p=d[0];if(d.slice(1).length>0)throw new Error('You should only render 1 child or use the `as="..."` prop');return t.cloneVNode(p,s)}return c}return t.h(a,s,c)}!function(e){e[e.None=0]="None",e[e.RenderStrategy=1]="RenderStrategy",e[e.Static=2]="Static"}(i||(i={})),function(e){e[e.Unmount=0]="Unmount",e[e.Hidden=1]="Hidden"}(u||(u={}));var c,d=t.defineComponent({name:"Alert",props:{as:{type:[Object,String],default:"div"},importance:{type:String,default:"polite"}},setup:function(e,t){var r=t.slots,i=t.attrs,u=e.importance,s=void 0===u?"polite":u,c=o(e,["importance"]),d={importance:s},p=l(s,{polite:function(){return{role:"status"}},assertive:function(){return{role:"alert"}}});return function(){return a({props:n({},c,p),slot:d,slots:r,attrs:i})}}});!function(e){e.Space=" ",e.Enter="Enter",e.Escape="Escape",e.Backspace="Backspace",e.ArrowUp="ArrowUp",e.ArrowDown="ArrowDown",e.Home="Home",e.End="End",e.PageUp="PageUp",e.PageDown="PageDown",e.Tab="Tab"}(c||(c={}));var p,f=0;function v(){return++f}function m(e){var t;return null==e||null==e.value?null:null!=(t=e.value.$el)?t:e.value}!function(e){e[e.Open=0]="Open",e[e.Closed=1]="Closed"}(p||(p={}));var b=Symbol("DisclosureContext");function h(e){var n=t.inject(b,null);if(null===n){var o=new Error("<"+e+" /> is missing a parent <Disclosure /> component.");throw Error.captureStackTrace&&Error.captureStackTrace(o,h),o}return n}var y,S,g=t.defineComponent({name:"Disclosure",props:{as:{type:[Object,String],default:"template"}},setup:function(e,o){var r=o.slots,i=o.attrs,u=n({},e),s=t.ref(p.Closed),c=t.ref(null);return t.provide(b,{disclosureState:s,panelRef:c,toggleDisclosure:function(){var e;s.value=l(s.value,((e={})[p.Open]=p.Closed,e[p.Closed]=p.Open,e))}}),function(){return a({props:u,slot:{open:s.value===p.Open},slots:r,attrs:i})}}}),x=t.defineComponent({name:"DisclosureButton",props:{as:{type:[Object,String],default:"button"},disabled:{type:[Boolean],default:!1}},render:function(){var e=h("DisclosureButton"),t={open:e.disclosureState.value===p.Open};return a({props:n({},this.$props,{id:this.id,ref:"el",type:"button","aria-expanded":e.disclosureState.value===p.Open||void 0,"aria-controls":this.ariaControls,onClick:this.handleClick,onKeydown:this.handleKeyDown}),slot:t,attrs:this.$attrs,slots:this.$slots})},setup:function(e){var n=h("DisclosureButton");return{id:"headlessui-disclosure-button-"+v(),ariaControls:t.computed((function(){var e,t;return null!=(e=null==(t=m(n.panelRef))?void 0:t.id)?e:void 0})),handleClick:function(){e.disabled||n.toggleDisclosure()},handleKeyDown:function(t){if(!e.disabled)switch(t.key){case c.Space:case c.Enter:t.preventDefault(),n.toggleDisclosure()}}}}}),w=t.defineComponent({name:"DisclosurePanel",props:{as:{type:[Object,String],default:"div"},static:{type:Boolean,default:!1},unmount:{type:Boolean,default:!0}},render:function(){var e={open:h("DisclosurePanel").disclosureState.value===p.Open};return a({props:n({},this.$props,{id:this.id,ref:"el"}),slot:e,attrs:this.$attrs,slots:this.$slots,features:i.RenderStrategy|i.Static,visible:e.open})},setup:function(){var e=h("DisclosurePanel");return{id:"headlessui-disclosure-panel-"+v(),el:e.panelRef}}});function O(e,t){var n=t.resolveItems();if(n.length<=0)return null;var o=t.resolveActiveIndex(),r=null!=o?o:-1,l=function(){switch(e.focus){case y.First:return n.findIndex((function(e){return!t.resolveDisabled(e)}));case y.Previous:var o=n.slice().reverse().findIndex((function(e,n,o){return!(-1!==r&&o.length-n-1>=r||t.resolveDisabled(e))}));return-1===o?o:n.length-1-o;case y.Next:return n.findIndex((function(e,n){return!(n<=r||t.resolveDisabled(e))}));case y.Last:var l=n.slice().reverse().findIndex((function(e){return!t.resolveDisabled(e)}));return-1===l?l:n.length-1-l;case y.Specific:return n.findIndex((function(n){return t.resolveId(n)===e.id}));case y.Nothing:return null;default:!function(e){throw new Error("Unexpected object: "+e)}(e)}}();return-1===l?o:l}function I(e,t){if(void 0!==e)return"function"==typeof e?e(t):e}!function(e){e[e.First=0]="First",e[e.Previous=1]="Previous",e[e.Next=2]="Next",e[e.Last=3]="Last",e[e.Specific=4]="Specific",e[e.Nothing=5]="Nothing"}(y||(y={})),function(e){e[e.Open=0]="Open",e[e.Closed=1]="Closed"}(S||(S={}));var k=Symbol("ListboxContext");function D(e){var n=t.inject(k,null);if(null===n){var o=new Error("<"+e+" /> is missing a parent <Listbox /> component.");throw Error.captureStackTrace&&Error.captureStackTrace(o,D),o}return n}var C,R=t.defineComponent({name:"Listbox",emits:["update:modelValue"],props:{as:{type:[Object,String],default:"template"},disabled:{type:[Boolean],default:!1},modelValue:{type:[Object,String,Number,Boolean],default:null}},setup:function(e,n){var r=n.slots,l=n.attrs,i=n.emit,u=e.disabled,s=o(e,["modelValue","disabled"]),c=t.ref(S.Closed),d=t.ref(null),p=t.ref(null),f=t.ref(null),v=t.ref([]),b=t.ref(""),h=t.ref(null),g=t.computed((function(){return e.modelValue})),x={listboxState:c,value:g,labelRef:d,buttonRef:p,optionsRef:f,disabled:u,options:v,searchQuery:b,activeOptionIndex:h,closeListbox:function(){u||c.value!==S.Closed&&(c.value=S.Closed,h.value=null)},openListbox:function(){u||c.value!==S.Open&&(c.value=S.Open)},goToOption:function(e,t){if(!u&&c.value!==S.Closed){var n=O(e===y.Specific?{focus:y.Specific,id:t}:{focus:e},{resolveItems:function(){return v.value},resolveActiveIndex:function(){return h.value},resolveId:function(e){return e.id},resolveDisabled:function(e){return e.dataRef.disabled}});""===b.value&&h.value===n||(b.value="",h.value=n)}},search:function(e){if(!u&&c.value!==S.Closed){b.value+=e;var t=v.value.findIndex((function(e){return!e.dataRef.disabled&&e.dataRef.textValue.startsWith(b.value)}));-1!==t&&t!==h.value&&(h.value=t)}},clearSearch:function(){u||c.value!==S.Closed&&""!==b.value&&(b.value="")},registerOption:function(e,t){v.value.push({id:e,dataRef:t})},unregisterOption:function(e){var t=v.value.slice(),n=null!==h.value?t[h.value]:null,o=t.findIndex((function(t){return t.id===e}));-1!==o&&t.splice(o,1),v.value=t,h.value=o===h.value||null===n?null:t.indexOf(n)},select:function(e){u||i("update:modelValue",e)}};return t.onMounted((function(){function e(e){var t,n,o,r=e.target,l=document.activeElement;c.value===S.Open&&((null==(t=m(p))?void 0:t.contains(r))||((null==(n=m(f))?void 0:n.contains(r))||x.closeListbox(),l!==document.body&&(null==l?void 0:l.contains(r))||e.defaultPrevented||null==(o=m(p))||o.focus({preventScroll:!0})))}window.addEventListener("mousedown",e),t.onUnmounted((function(){return window.removeEventListener("mousedown",e)}))})),t.provide(k,x),function(){return a({props:s,slot:{open:c.value===S.Open,disabled:u},slots:r,attrs:l})}}}),T=t.defineComponent({name:"ListboxLabel",props:{as:{type:[Object,String],default:"label"}},render:function(){var e=D("ListboxLabel"),t={open:e.listboxState.value===S.Open,disabled:e.disabled};return a({props:n({},this.$props,{id:this.id,ref:"el",onClick:this.handleClick}),slot:t,attrs:this.$attrs,slots:this.$slots})},setup:function(){var e=D("ListboxLabel");return{id:"headlessui-listbox-label-"+v(),el:e.labelRef,handleClick:function(){var t;null==(t=m(e.buttonRef))||t.focus({preventScroll:!0})}}}}),E=t.defineComponent({name:"ListboxButton",props:{as:{type:[Object,String],default:"button"}},render:function(){var e,t,o=D("ListboxButton"),r={open:o.listboxState.value===S.Open,disabled:o.disabled},l={ref:"el",id:this.id,type:"button","aria-haspopup":!0,"aria-controls":null==(e=m(o.optionsRef))?void 0:e.id,"aria-expanded":o.listboxState.value===S.Open||void 0,"aria-labelledby":o.labelRef.value?[null==(t=m(o.labelRef))?void 0:t.id,this.id].join(" "):void 0,disabled:o.disabled,onKeydown:this.handleKeyDown,onClick:this.handleClick};return a({props:n({},this.$props,l),slot:r,attrs:this.$attrs,slots:this.$slots})},setup:function(){var e=D("ListboxButton");return{id:"headlessui-listbox-button-"+v(),el:e.buttonRef,handleKeyDown:function(n){switch(n.key){case c.Space:case c.Enter:case c.ArrowDown:n.preventDefault(),e.openListbox(),t.nextTick((function(){var t;null==(t=m(e.optionsRef))||t.focus({preventScroll:!0}),e.value.value||e.goToOption(y.First)}));break;case c.ArrowUp:n.preventDefault(),e.openListbox(),t.nextTick((function(){var t;null==(t=m(e.optionsRef))||t.focus({preventScroll:!0}),e.value.value||e.goToOption(y.Last)}))}},handleClick:function(n){var o;e.disabled||(e.listboxState.value===S.Open?(e.closeListbox(),t.nextTick((function(){var t;return null==(t=m(e.buttonRef))?void 0:t.focus({preventScroll:!0})}))):(n.preventDefault(),e.openListbox(),o=function(){var t;return null==(t=m(e.optionsRef))?void 0:t.focus({preventScroll:!0})},requestAnimationFrame((function(){return requestAnimationFrame(o)}))))}}}}),L=t.defineComponent({name:"ListboxOptions",props:{as:{type:[Object,String],default:"ul"},static:{type:Boolean,default:!1},unmount:{type:Boolean,default:!0}},render:function(){var e,t,o,r,l=D("ListboxOptions"),u={open:l.listboxState.value===S.Open},s={"aria-activedescendant":null===l.activeOptionIndex.value||null==(e=l.options.value[l.activeOptionIndex.value])?void 0:e.id,"aria-labelledby":null!=(t=null==(o=m(l.labelRef))?void 0:o.id)?t:null==(r=m(l.buttonRef))?void 0:r.id,id:this.id,onKeydown:this.handleKeyDown,role:"listbox",tabIndex:0,ref:"el"};return a({props:n({},this.$props,s),slot:u,attrs:this.$attrs,slots:this.$slots,features:i.RenderStrategy|i.Static,visible:u.open})},setup:function(){var e=D("ListboxOptions"),n="headlessui-listbox-options-"+v(),o=t.ref(null);return{id:n,el:e.optionsRef,handleKeyDown:function(n){switch(o.value&&clearTimeout(o.value),n.key){case c.Space:if(""!==e.searchQuery.value)return n.preventDefault(),e.search(n.key);case c.Enter:n.preventDefault(),null!==e.activeOptionIndex.value&&e.select(e.options.value[e.activeOptionIndex.value].dataRef.value),e.closeListbox(),t.nextTick((function(){var t;return null==(t=m(e.buttonRef))?void 0:t.focus({preventScroll:!0})}));break;case c.ArrowDown:return n.preventDefault(),e.goToOption(y.Next);case c.ArrowUp:return n.preventDefault(),e.goToOption(y.Previous);case c.Home:case c.PageUp:return n.preventDefault(),e.goToOption(y.First);case c.End:case c.PageDown:return n.preventDefault(),e.goToOption(y.Last);case c.Escape:n.preventDefault(),e.closeListbox(),t.nextTick((function(){var t;return null==(t=m(e.buttonRef))?void 0:t.focus({preventScroll:!0})}));break;case c.Tab:return n.preventDefault();default:1===n.key.length&&(e.search(n.key),o.value=setTimeout((function(){return e.clearSearch()}),350))}}}}}),j=t.defineComponent({name:"ListboxOption",props:{as:{type:[Object,String],default:"li"},value:{type:[Object,String],default:null},disabled:{type:Boolean,default:!1},class:{type:[String,Function],required:!1},className:{type:[String,Function],required:!1}},setup:function(e,o){var r=o.slots,l=o.attrs,i=D("ListboxOption"),u="headlessui-listbox-option-"+v(),s=e.disabled,c=e.className,d=void 0===c?e.class:c,p=e.value,f=t.computed((function(){return null!==i.activeOptionIndex.value&&i.options.value[i.activeOptionIndex.value].id===u})),b=t.computed((function(){return t.toRaw(i.value.value)===t.toRaw(p)})),h=t.ref({disabled:s,value:p,textValue:""});function g(e){if(s)return e.preventDefault();i.select(p),i.closeListbox(),t.nextTick((function(){var e;return null==(e=m(i.buttonRef))?void 0:e.focus({preventScroll:!0})}))}function x(){if(s)return i.goToOption(y.Nothing);i.goToOption(y.Specific,u)}function w(){s||f.value||i.goToOption(y.Specific,u)}function O(){s||f.value&&i.goToOption(y.Nothing)}return t.onMounted((function(){var e,t,n=null==(e=document.getElementById(u))||null==(t=e.textContent)?void 0:t.toLowerCase().trim();void 0!==n&&(h.value.textValue=n)})),t.onMounted((function(){return i.registerOption(u,h)})),t.onUnmounted((function(){return i.unregisterOption(u)})),t.onMounted((function(){t.watch([i.listboxState,b],(function(){var e;i.listboxState.value===S.Open&&b.value&&(i.goToOption(y.Specific,u),null==(e=document.getElementById(u))||null==e.focus||e.focus())}),{immediate:!0})})),t.watchEffect((function(){i.listboxState.value===S.Open&&f.value&&t.nextTick((function(){var e;return null==(e=document.getElementById(u))||null==e.scrollIntoView?void 0:e.scrollIntoView({block:"nearest"})}))})),function(){var t={active:f.value,selected:b.value,disabled:s},o={id:u,role:"option",tabIndex:-1,class:I(d,t),"aria-disabled":!0===s||void 0,"aria-selected":!0===b.value?b.value:void 0,onClick:g,onFocus:x,onPointermove:w,onMousemove:w,onPointerleave:O,onMouseleave:O};return a({props:n({},e,o),slot:t,attrs:l,slots:r})}}});!function(e){e[e.Open=0]="Open",e[e.Closed=1]="Closed"}(C||(C={}));var $=Symbol("MenuContext");function M(e){var n=t.inject($,null);if(null===n){var o=new Error("<"+e+" /> is missing a parent <Menu /> component.");throw Error.captureStackTrace&&Error.captureStackTrace(o,M),o}return n}var A=t.defineComponent({props:{as:{type:[Object,String],default:"template"}},setup:function(e,n){var o=n.slots,r=n.attrs,l=t.ref(C.Closed),i=t.ref(null),u=t.ref(null),s=t.ref([]),c=t.ref(""),d=t.ref(null),p={menuState:l,buttonRef:i,itemsRef:u,items:s,searchQuery:c,activeItemIndex:d,closeMenu:function(){l.value=C.Closed,d.value=null},openMenu:function(){return l.value=C.Open},goToItem:function(e,t){var n=O(e===y.Specific?{focus:y.Specific,id:t}:{focus:e},{resolveItems:function(){return s.value},resolveActiveIndex:function(){return d.value},resolveId:function(e){return e.id},resolveDisabled:function(e){return e.dataRef.disabled}});""===c.value&&d.value===n||(c.value="",d.value=n)},search:function(e){c.value+=e;var t=s.value.findIndex((function(e){return e.dataRef.textValue.startsWith(c.value)&&!e.dataRef.disabled}));-1!==t&&t!==d.value&&(d.value=t)},clearSearch:function(){c.value=""},registerItem:function(e,t){s.value.push({id:e,dataRef:t})},unregisterItem:function(e){var t=s.value.slice(),n=null!==d.value?t[d.value]:null,o=t.findIndex((function(t){return t.id===e}));-1!==o&&t.splice(o,1),s.value=t,d.value=o===d.value||null===n?null:t.indexOf(n)}};return t.onMounted((function(){function e(e){var t,n,o,r=e.target,a=document.activeElement;l.value===C.Open&&((null==(t=m(i))?void 0:t.contains(r))||((null==(n=m(u))?void 0:n.contains(r))||p.closeMenu(),a!==document.body&&(null==a?void 0:a.contains(r))||e.defaultPrevented||null==(o=m(i))||o.focus({preventScroll:!0})))}window.addEventListener("mousedown",e),t.onUnmounted((function(){return window.removeEventListener("mousedown",e)}))})),t.provide($,p),function(){return a({props:e,slot:{open:l.value===C.Open},slots:o,attrs:r})}}}),B=t.defineComponent({props:{disabled:{type:Boolean,default:!1},as:{type:[Object,String],default:"button"}},render:function(){var e,t=M("MenuButton"),o={open:t.menuState.value===C.Open},r={ref:"el",id:this.id,type:"button","aria-haspopup":!0,"aria-controls":null==(e=m(t.itemsRef))?void 0:e.id,"aria-expanded":t.menuState.value===C.Open||void 0,onKeydown:this.handleKeyDown,onClick:this.handleClick};return a({props:n({},this.$props,r),slot:o,attrs:this.$attrs,slots:this.$slots})},setup:function(e){var n=M("MenuButton");return{id:"headlessui-menu-button-"+v(),el:n.buttonRef,handleKeyDown:function(e){switch(e.key){case c.Space:case c.Enter:case c.ArrowDown:e.preventDefault(),n.openMenu(),t.nextTick((function(){var e;null==(e=m(n.itemsRef))||e.focus({preventScroll:!0}),n.goToItem(y.First)}));break;case c.ArrowUp:e.preventDefault(),n.openMenu(),t.nextTick((function(){var e;null==(e=m(n.itemsRef))||e.focus({preventScroll:!0}),n.goToItem(y.Last)}))}},handleClick:function(o){var r;e.disabled||(n.menuState.value===C.Open?(n.closeMenu(),t.nextTick((function(){var e;return null==(e=m(n.buttonRef))?void 0:e.focus({preventScroll:!0})}))):(o.preventDefault(),n.openMenu(),r=function(){var e;return null==(e=m(n.itemsRef))?void 0:e.focus({preventScroll:!0})},requestAnimationFrame((function(){return requestAnimationFrame(r)}))))}}}}),N=t.defineComponent({props:{as:{type:[Object,String],default:"div"},static:{type:Boolean,default:!1},unmount:{type:Boolean,default:!0}},render:function(){var e,t,o=M("MenuItems"),r={open:o.menuState.value===C.Open},l={"aria-activedescendant":null===o.activeItemIndex.value||null==(e=o.items.value[o.activeItemIndex.value])?void 0:e.id,"aria-labelledby":null==(t=m(o.buttonRef))?void 0:t.id,id:this.id,onKeydown:this.handleKeyDown,role:"menu",tabIndex:0,ref:"el"};return a({props:n({},this.$props,l),slot:r,attrs:this.$attrs,slots:this.$slots,features:i.RenderStrategy|i.Static,visible:r.open})},setup:function(){var e=M("MenuItems"),n="headlessui-menu-items-"+v(),o=t.ref(null);return t.watchEffect((function(){var t=m(e.itemsRef);if(t&&e.menuState.value===C.Open)for(var n=document.createTreeWalker(t,NodeFilter.SHOW_ELEMENT,{acceptNode:function(e){return"menuitem"===e.getAttribute("role")?NodeFilter.FILTER_REJECT:e.hasAttribute("role")?NodeFilter.FILTER_SKIP:NodeFilter.FILTER_ACCEPT}});n.nextNode();)n.currentNode.setAttribute("role","none")})),{id:n,el:e.itemsRef,handleKeyDown:function(n){switch(o.value&&clearTimeout(o.value),n.key){case c.Space:if(""!==e.searchQuery.value)return n.preventDefault(),e.search(n.key);case c.Enter:var r;n.preventDefault(),null!==e.activeItemIndex.value&&(null==(r=document.getElementById(e.items.value[e.activeItemIndex.value].id))||r.click()),e.closeMenu(),t.nextTick((function(){var t;return null==(t=m(e.buttonRef))?void 0:t.focus({preventScroll:!0})}));break;case c.ArrowDown:return n.preventDefault(),e.goToItem(y.Next);case c.ArrowUp:return n.preventDefault(),e.goToItem(y.Previous);case c.Home:case c.PageUp:return n.preventDefault(),e.goToItem(y.First);case c.End:case c.PageDown:return n.preventDefault(),e.goToItem(y.Last);case c.Escape:n.preventDefault(),e.closeMenu(),t.nextTick((function(){var t;return null==(t=m(e.buttonRef))?void 0:t.focus({preventScroll:!0})}));break;case c.Tab:return n.preventDefault();default:1===n.key.length&&(e.search(n.key),o.value=setTimeout((function(){return e.clearSearch()}),350))}}}}}),P=t.defineComponent({props:{as:{type:[Object,String],default:"template"},disabled:{type:Boolean,default:!1},class:{type:[String,Function],required:!1},className:{type:[String,Function],required:!1}},setup:function(e,o){var r=o.slots,l=o.attrs,i=M("MenuItem"),u="headlessui-menu-item-"+v(),s=e.disabled,c=e.className,d=void 0===c?e.class:c,p=t.computed((function(){return null!==i.activeItemIndex.value&&i.items.value[i.activeItemIndex.value].id===u})),f=t.ref({disabled:s,textValue:""});function b(e){if(s)return e.preventDefault();i.closeMenu(),t.nextTick((function(){var e;return null==(e=m(i.buttonRef))?void 0:e.focus({preventScroll:!0})}))}function h(){if(s)return i.goToItem(y.Nothing);i.goToItem(y.Specific,u)}function S(){s||p.value||i.goToItem(y.Specific,u)}function g(){s||p.value&&i.goToItem(y.Nothing)}return t.onMounted((function(){var e,t,n=null==(e=document.getElementById(u))||null==(t=e.textContent)?void 0:t.toLowerCase().trim();void 0!==n&&(f.value.textValue=n)})),t.onMounted((function(){return i.registerItem(u,f)})),t.onUnmounted((function(){return i.unregisterItem(u)})),t.watchEffect((function(){i.menuState.value===C.Open&&p.value&&t.nextTick((function(){var e;return null==(e=document.getElementById(u))||null==e.scrollIntoView?void 0:e.scrollIntoView({block:"nearest"})}))})),function(){var t={active:p.value,disabled:s},o={id:u,role:"menuitem",tabIndex:-1,class:I(d,t),"aria-disabled":!0===s||void 0,onClick:b,onFocus:h,onPointermove:S,onMousemove:S,onPointerleave:g,onMouseleave:g};return a({props:n({},e,o),slot:t,attrs:l,slots:r})}}}),F=Symbol("GroupContext");function V(e){var n=t.inject(F,null);if(null===n){var o=new Error("<"+e+" /> is missing a parent <SwitchGroup /> component.");throw Error.captureStackTrace&&Error.captureStackTrace(o,V),o}return n}var K=t.defineComponent({name:"SwitchGroup",props:{as:{type:[Object,String],default:"template"}},setup:function(e,n){var o=n.slots,r=n.attrs,l=t.ref(null),i=t.ref(null),u=t.ref(null);return t.provide(F,{switchRef:l,labelRef:i,descriptionRef:u}),function(){return a({props:e,slot:{},slots:o,attrs:r})}}}),U=t.defineComponent({name:"Switch",emits:["update:modelValue"],props:{as:{type:[Object,String],default:"button"},modelValue:{type:[Object,Boolean],default:null},class:{type:[String,Function],required:!1},className:{type:[String,Function],required:!1}},render:function(){var e=t.inject(F,null),o=this.$props,r=o.className,l=void 0===r?o.class:r,i=t.computed((function(){var t;return null==(t=m(null==e?void 0:e.labelRef))?void 0:t.id})),u=t.computed((function(){var t;return null==(t=m(null==e?void 0:e.descriptionRef))?void 0:t.id})),s={checked:this.$props.modelValue},c={id:this.id,ref:null===e?void 0:e.switchRef,role:"switch",tabIndex:0,class:I(l,s),"aria-checked":this.$props.modelValue,"aria-labelledby":i.value,"aria-describedby":u.value,onClick:this.handleClick,onKeyup:this.handleKeyUp,onKeypress:this.handleKeyPress};return"button"===this.$props.as&&Object.assign(c,{type:"button"}),a({props:n({},this.$props,c),slot:s,attrs:this.$attrs,slots:this.$slots})},setup:function(e,n){var o=n.emit,r=t.inject(F,null);function l(){o("update:modelValue",!e.modelValue)}return{id:"headlessui-switch-"+v(),el:null==r?void 0:r.switchRef,handleClick:function(e){e.preventDefault(),l()},handleKeyUp:function(e){e.key!==c.Tab&&e.preventDefault(),e.key===c.Space&&l()},handleKeyPress:function(e){e.preventDefault()}}}}),q=t.defineComponent({name:"SwitchLabel",props:{as:{type:[Object,String],default:"label"}},render:function(){return a({props:n({},this.$props,{id:this.id,ref:"el",onClick:this.handleClick}),slot:{},attrs:this.$attrs,slots:this.$slots})},setup:function(){var e=V("SwitchLabel");return{id:"headlessui-switch-label-"+v(),el:e.labelRef,handleClick:function(){var t=m(e.switchRef);null==t||t.click(),null==t||t.focus({preventScroll:!0})}}}}),H=t.defineComponent({name:"SwitchDescription",props:{as:{type:[Object,String],default:"p"}},render:function(){return a({props:n({},this.$props,{id:this.id,ref:"el"}),slot:{},attrs:this.$attrs,slots:this.$slots})},setup:function(){var e=V("SwitchDescription");return{id:"headlessui-switch-description-"+v(),el:e.descriptionRef}}});e.Alert=d,e.Disclosure=g,e.DisclosureButton=x,e.DisclosurePanel=w,e.Listbox=R,e.ListboxButton=E,e.ListboxLabel=T,e.ListboxOption=j,e.ListboxOptions=L,e.Menu=A,e.MenuButton=B,e.MenuItem=P,e.MenuItems=N,e.Switch=U,e.SwitchDescription=H,e.SwitchGroup=K,e.SwitchLabel=q,Object.defineProperty(e,"__esModule",{value:!0})})); | ||
//# sourceMappingURL=headlessui.umd.production.min.js.map |
@@ -0,3 +1,5 @@ | ||
export * from './components/alert/alert'; | ||
export * from './components/disclosure/disclosure'; | ||
export * from './components/listbox/listbox'; | ||
export * from './components/menu/menu'; | ||
export * from './components/listbox/listbox'; | ||
export * from './components/switch/switch'; |
@@ -84,2 +84,22 @@ export declare function getMenuButton(): HTMLElement | null; | ||
}, switchElement?: HTMLElement | null): void; | ||
export declare function getDisclosureButton(): HTMLElement | null; | ||
export declare function getDisclosurePanel(): HTMLElement | null; | ||
export declare enum DisclosureState { | ||
/** The disclosure is visible to the user. */ | ||
Visible = 0, | ||
/** The disclosure is **not** visible to the user. It's still in the DOM, but it is hidden. */ | ||
InvisibleHidden = 1, | ||
/** The disclosure is **not** visible to the user. It's not in the DOM, it is unmounted. */ | ||
InvisibleUnmounted = 2 | ||
} | ||
export declare function assertDisclosureButton(options: { | ||
attributes?: Record<string, string | null>; | ||
textContent?: string; | ||
state: DisclosureState; | ||
}, button?: HTMLElement | null): void; | ||
export declare function assertDisclosurePanel(options: { | ||
attributes?: Record<string, string | null>; | ||
textContent?: string; | ||
state: DisclosureState; | ||
}, panel?: HTMLElement | null): void; | ||
export declare function assertLabelValue(element: HTMLElement | null, value: string): void; | ||
@@ -90,1 +110,2 @@ export declare function assertDescriptionValue(element: HTMLElement | null, value: string): void; | ||
export declare function assertVisible(element: HTMLElement | null): void; | ||
export declare function getByText(text: string): HTMLElement | null; |
{ | ||
"name": "@headlessui/vue", | ||
"version": "0.3.0-e73d09b", | ||
"version": "0.3.0-e8e2770", | ||
"main": "dist/index.js", | ||
@@ -23,3 +23,3 @@ "typings": "dist/index.d.ts", | ||
"scripts": { | ||
"playground": "vite serve examples", | ||
"playground": "vite --config ./vite.config.js serve examples", | ||
"playground:build": "NODE_ENV=production vite build examples", | ||
@@ -33,3 +33,3 @@ "prepublishOnly": "npm run build", | ||
"peerDependencies": { | ||
"vue": "^3.0.0-rc.13" | ||
"vue": "^3.0.0" | ||
}, | ||
@@ -43,5 +43,5 @@ "devDependencies": { | ||
"vite": "^1.0.0-rc.4", | ||
"vue": "^3.0.0-rc.13", | ||
"vue": "^3.0.0", | ||
"vue-router": "^4.0.0-beta.13" | ||
} | ||
} |
1205
README.md
@@ -32,5 +32,5 @@ <h3 align="center"> | ||
- [Menu Button (Dropdown)](#menu-button-dropdown) | ||
- [Listbox (Select)](#listbox-select) | ||
- [Switch (Toggle)](#switch-toggle) | ||
- [Menu Button (Dropdown)](./src/components/menu/README.md) | ||
- [Listbox (Select)](./src/components/listbox/README.md) | ||
- [Switch (Toggle)](./src/components/switch/README.md) | ||
@@ -52,1200 +52,1 @@ ### Roadmap | ||
We'll be continuing to develop new components on an on-going basis, with a goal of reaching a pretty fleshed out v1.0 by the end of the year. | ||
--- | ||
## Menu Button (Dropdown) | ||
[View live demo on CodeSandbox](https://codesandbox.io/s/headlessuivue-menu-example-70br3?file=/src/App.vue) | ||
The `Menu` component and related child components are used to quickly build custom dropdown components that are fully accessible out of the box, including correct ARIA attribute management and robust keyboard navigation support. | ||
- [Basic example](#basic-example) | ||
- [Styling](#styling) | ||
- [Transitions](#transitions) | ||
- [Component API](#component-api) | ||
### Basic example | ||
Menu Buttons are built using the `Menu`, `MenuButton`, `MenuItems`, and `MenuItem` components. | ||
The `MenuButton` will automatically open/close the `MenuItems` when clicked, and when the menu is open, the list of items receives focus and is automatically navigable via the keyboard. | ||
```vue | ||
<template> | ||
<Menu> | ||
<MenuButton> More </MenuButton> | ||
<MenuItems> | ||
<MenuItem v-slot="{ active }"> | ||
<a :class="{ 'bg-blue-500': active }" href="/account-settings"> Account settings </a> | ||
</MenuItem> | ||
<MenuItem v-slot="{ active }"> | ||
<a :class="{ 'bg-blue-500': active }" href="/account-settings"> Documentation </a> | ||
</MenuItem> | ||
<MenuItem v-slot="{ active }" disabled> | ||
<span :class="{ 'bg-blue-500': active }"> Invite a friend (coming soon!) </span> | ||
</MenuItem> | ||
</MenuItems> | ||
</Menu> | ||
</template> | ||
<script> | ||
import { Menu, MenuButton, MenuItems, MenuItem } from '@headlessui/vue' | ||
export default { | ||
components: { | ||
Menu, | ||
MenuButton, | ||
MenuItems, | ||
MenuItem, | ||
}, | ||
} | ||
</script> | ||
``` | ||
### Styling the active item | ||
This is a headless component so there are no styles included by default. Instead, the components expose useful information via [scoped slots](https://v3.vuejs.org/guide/component-slots.html#scoped-slots) that you can use to apply the styles you'd like to apply yourself. | ||
To style the active `MenuItem` you can read the `active` slot prop, which tells you whether or not that menu item is the item that is currently focused via the mouse or keyboard. | ||
You can use this state to conditionally apply whatever active/focus styles you like, for instance a blue background like is typical in most operating systems. | ||
```vue | ||
<template> | ||
<Menu> | ||
<MenuButton> More </MenuButton> | ||
<MenuItems> | ||
<!-- Use the `active` state to conditionally style the active item. --> | ||
<MenuItem v-slot="{ active }"> | ||
<a href="/settings" :class="active ? 'bg-blue-500 text-white' : 'bg-white text-black'"> | ||
Settings | ||
</a> | ||
</MenuItem> | ||
<!-- ... --> | ||
</MenuItems> | ||
</Menu> | ||
</template> | ||
``` | ||
### Showing/hiding the menu | ||
By default, your `MenuItems` instance will be shown/hidden automatically based on the internal `open` state tracked within the `Menu` component itself. | ||
```vue | ||
<template> | ||
<Menu> | ||
<MenuButton> More </MenuButton> | ||
<!-- By default, this will automatically show/hide when the MenuButton is pressed. --> | ||
<MenuItems> | ||
<MenuItem v-slot="{ active }"> | ||
<a :class="{ 'bg-blue-500': active }" href="/account-settings"> Account settings </a> | ||
</MenuItem> | ||
<!-- ... --> | ||
</MenuItems> | ||
</Menu> | ||
</template> | ||
``` | ||
If you'd rather handle this yourself (perhaps because you need to add an extra wrapper element for one reason or another), you can add a `static` prop to the `MenuItems` instance to tell it to always render, and inspect the `open` slot prop provided by the `Menu` to control which element is shown/hidden yourself. | ||
```vue | ||
<template> | ||
<Menu v-slot="{ open }"> | ||
<MenuButton> More </MenuButton> | ||
<div v-show="open"> | ||
<!-- Using `static`, `MenuItems` is always rendered and ignores the `open` state. --> | ||
<MenuItems static> | ||
<MenuItem v-slot="{ active }"> | ||
<a :class="{ 'bg-blue-500': active }" href="/account-settings"> Account settings </a> | ||
</MenuItem> | ||
<!-- ... --> | ||
</MenuItems> | ||
</div> | ||
</Menu> | ||
</template> | ||
``` | ||
### Disabling an item | ||
Use the `disabled` prop to disable a `MenuItem`. This will make it unselectable via keyboard navigation, and it will be skipped when pressing the up/down arrows. | ||
```vue | ||
<template> | ||
<Menu> | ||
<MenuButton> More </MenuButton> | ||
<MenuItems> | ||
<MenuItem disabled> | ||
<span class="opacity-75">Invite a friend (coming soon!)</span> | ||
</MenuItem> | ||
<!-- ... --> | ||
</MenuItems> | ||
</Menu> | ||
</template> | ||
``` | ||
### Transitions | ||
To animate the opening/closing of the menu panel, use Vue's built-in `transition` component. All you need to do is wrap your `MenuItems` instance in a `<transition>` element and the transition will be applied automatically. | ||
```vue | ||
<template> | ||
<Menu> | ||
<MenuButton> More </MenuButton> | ||
<transition | ||
enter-active-class="transition duration-100 ease-out" | ||
enter-from-class="transform scale-95 opacity-0" | ||
enter-to-class="transform scale-100 opacity-100" | ||
leave-active-class="transition duration-75 ease-out" | ||
leave-from-class="transform scale-100 opacity-100" | ||
leave-to-class="transform scale-95 opacity-0" | ||
> | ||
<MenuItems> | ||
<MenuItem v-slot="{ active }"> | ||
<a :class="{ 'bg-blue-500': active }" href="/account-settings"> Account settings </a> | ||
</MenuItem> | ||
<!-- ... --> | ||
</MenuItems> | ||
</transition> | ||
</Menu> | ||
</template> | ||
``` | ||
### Rendering additional content | ||
The `Menu` component is not limited to rendering only its related subcomponents. You can render anything you like within a menu, which gives you complete control over exactly what you are building. | ||
For example, if you'd like to add a little header section to the menu with some extra information in it, just render an extra `div` with your content in it. | ||
```vue | ||
<template> | ||
<Menu> | ||
<MenuButton> More </MenuButton> | ||
<MenuItems> | ||
<div class="px-4 py-3"> | ||
<p class="text-sm leading-5">Signed in as</p> | ||
<p class="text-sm font-medium leading-5 text-gray-900 truncate">tom@example.com</p> | ||
</div> | ||
<MenuItem v-slot="{ active }"> | ||
<a :class="{ 'bg-blue-500': active }" href="/account-settings"> Account settings </a> | ||
</MenuItem> | ||
<!-- ... --> | ||
</MenuItems> | ||
</Menu> | ||
</template> | ||
``` | ||
Note that only `MenuItem` instances will be navigable via the keyboard. | ||
### Rendering a different element for a component | ||
By default, the `Menu` and its subcomponents each render a default element that is sensible for that component. | ||
For example, `MenuButton` renders a `button` by default, and `MenuItems` renders a `div`. `Menu` and `MenuItem` interestingly _do not render an extra element_, and instead render their children directly by default. | ||
This is easy to change using the `as` prop, which exists on every component. | ||
```vue | ||
<template> | ||
<!-- Render a `div` instead of no wrapper element --> | ||
<Menu as="div"> | ||
<MenuButton> More </MenuButton> | ||
<!-- Render a `ul` instead of a `div` --> | ||
<MenuItems as="ul"> | ||
<!-- Render an `li` instead of no wrapper element --> | ||
<MenuItem as="li" v-slot="{ active }"> | ||
<a href="/account-settings" :class="{ 'bg-blue-500': active }"> Account settings </a> | ||
</MenuItem> | ||
<!-- ... --> | ||
</MenuItems> | ||
</Menu> | ||
</template> | ||
``` | ||
To tell an element to render its children directly with no wrapper element, use `as="template"`. | ||
```vue | ||
<template> | ||
<Menu> | ||
<!-- Render no wrapper, instead pass in a button manually --> | ||
<MenuButton as="template"> | ||
<button>More</button> | ||
</MenuButton> | ||
<MenuItems> | ||
<MenuItem v-slot="{ active }"> | ||
<a href="/account-settings" :class="{ 'bg-blue-500': active }"> Account settings </a> | ||
</MenuItem> | ||
<!-- ... --> | ||
</MenuItems> | ||
</Menu> | ||
</template> | ||
``` | ||
### Component API | ||
#### Menu | ||
```vue | ||
<Menu v-slot="{ open }"> | ||
<MenuButton>More options</MenuButton> | ||
<MenuItems> | ||
<MenuItem><!-- ... --></MenuItem> | ||
<!-- ... --> | ||
</MenuItems> | ||
</Menu> | ||
``` | ||
##### Props | ||
| Prop | Type | Default | Description | | ||
| :--- | :------------------ | :-------------------------------- | :---------------------------------------------------- | | ||
| `as` | String \| Component | `template` _(no wrapper element_) | The element or component the `Menu` should render as. | | ||
##### Slot props | ||
| Prop | Type | Description | | ||
| :----- | :------ | :------------------------------- | | ||
| `open` | Boolean | Whether or not the menu is open. | | ||
#### MenuButton | ||
```vue | ||
<MenuButton v-slot="{ open }"> | ||
<span>More options</span> | ||
<ChevronRightIcon :class="open ? 'transform rotate-90' : ''" /> | ||
</MenuButton> | ||
``` | ||
##### Props | ||
| Prop | Type | Default | Description | | ||
| :--- | :------------------ | :------- | :---------------------------------------------------------- | | ||
| `as` | String \| Component | `button` | The element or component the `MenuButton` should render as. | | ||
##### Slot props | ||
| Prop | Type | Description | | ||
| :----- | :------ | :------------------------------- | | ||
| `open` | Boolean | Whether or not the menu is open. | | ||
#### MenuItems | ||
```vue | ||
<MenuItems> | ||
<MenuItem><!-- ... --></MenuItem> | ||
<!-- ... --> | ||
</MenuItem> | ||
``` | ||
##### Props | ||
| Prop | Type | Default | Description | | ||
| :-------- | :------------------ | :------ | :-------------------------------------------------------------------------------- | | ||
| `as` | String \| Component | `div` | The element or component the `MenuItems` should render as. | | ||
| `static` | Boolean | `false` | Whether the element should ignore the internally managed open/closed state. | | ||
| `unmount` | Boolean | `true` | Whether the element should be unmounted or hidden based on the open/closed state. | | ||
##### Slot props | ||
| Prop | Type | Description | | ||
| :----- | :------ | :------------------------------- | | ||
| `open` | Boolean | Whether or not the menu is open. | | ||
#### MenuItem | ||
```vue | ||
<MenuItem v-slot="{ active }"> | ||
<a href="/settings" :class="active ? 'bg-blue-500 text-white' : 'bg-white text-black'"> | ||
Settings | ||
</a> | ||
</MenuItem> | ||
``` | ||
##### Props | ||
| Prop | Type | Default | Description | | ||
| :--------- | :------------------ | :-------------------------------- | :------------------------------------------------------------------------------------ | | ||
| `as` | String \| Component | `template` _(no wrapper element)_ | The element or component the `MenuItem` should render as. | | ||
| `disabled` | Boolean | `false` | Whether or not the item should be disabled for keyboard navigation and ARIA purposes. | | ||
##### Slot props | ||
| Prop | Type | Description | | ||
| :--------- | :------ | :--------------------------------------------------------------------------------- | | ||
| `active` | Boolean | Whether or not the item is the active/focused item in the list. | | ||
| `disabled` | Boolean | Whether or not the item is the disabled for keyboard navigation and ARIA purposes. | | ||
## Listbox (Select) | ||
[View live demo on CodeSandbox](https://codesandbox.io/s/headlessuivue-listbox-example-mi67g?file=/src/App.vue) | ||
The `Listbox` component and related child components are used to quickly build custom listbox components that are fully accessible out of the box, including correct ARIA attribute management and robust keyboard navigation support. | ||
- [Basic example](#basic-example-1) | ||
- [Styling the active and selected option](#styling-the-active-and-selected-option) | ||
- [Showing/hiding the listbox](#showinghiding-the-listbox) | ||
- [Using a custom label](#using-a-custom-label) | ||
- [Disabling an option](#disabling-an-option) | ||
- [Transitions](#transitions-1) | ||
- [Rendering additional content](#rendering-additional-content-1) | ||
- [Rendering a different element for a component](#rendering-a-different-element-for-a-component-1) | ||
- [Component API](#component-api-1) | ||
### Basic example | ||
Listboxes are built using the `Listbox`, `ListboxButton`, `ListboxOptions`, `ListboxOption` and `ListboxLabel` components. | ||
The `ListboxButton` will automatically open/close the `ListboxOptions` when clicked, and when the menu is open, the list of items receives focus and is automatically navigable via the keyboard. | ||
```vue | ||
<template> | ||
<Listbox v-model="selectedPerson"> | ||
<ListboxButton>{{ selectedPerson.name }}</ListboxButton> | ||
<ListboxOptions> | ||
<ListboxOption | ||
v-for="person in people" | ||
:key="person.id" | ||
:value="person" | ||
:disabled="person.unavailable" | ||
> | ||
{{ person.name }} | ||
</ListboxOption> | ||
</ListboxOptions> | ||
</Listbox> | ||
</template> | ||
<script> | ||
import { ref } from 'vue' | ||
import { | ||
Listbox, | ||
ListboxLabel, | ||
ListboxButton, | ||
ListboxOptions, | ||
ListboxOption, | ||
} from '@headlessui/vue' | ||
export default { | ||
components: { | ||
Listbox, | ||
ListboxLabel, | ||
ListboxButton, | ||
ListboxOptions, | ||
ListboxOption, | ||
}, | ||
setup() { | ||
const people = [ | ||
{ id: 1, name: 'Durward Reynolds', unavailable: false }, | ||
{ id: 2, name: 'Kenton Towne', unavailable: false }, | ||
{ id: 3, name: 'Therese Wunsch', unavailable: false }, | ||
{ id: 4, name: 'Benedict Kessler', unavailable: true }, | ||
{ id: 5, name: 'Katelyn Rohan', unavailable: false }, | ||
] | ||
const selectedPerson = ref(people[0]) | ||
return { | ||
people, | ||
selectedPerson, | ||
} | ||
}, | ||
} | ||
</script> | ||
``` | ||
### Styling the active and selected option | ||
This is a headless component so there are no styles included by default. Instead, the components expose useful information via [scoped slots](https://v3.vuejs.org/guide/component-slots.html#scoped-slots) that you can use to apply the styles you'd like to apply yourself. | ||
To style the active `ListboxOption` you can read the `active` slot prop, which tells you whether or not that listbox option is the option that is currently focused via the mouse or keyboard. | ||
To style the selected `ListboxOption` you can read the `selected` slot prop, which tells you whether or not that listbox option is the option that is currently the `value` passed to the `Listbox`. | ||
You can use this state to conditionally apply whatever active/focus styles you like, for instance a blue background like is typical in most operating systems. For the selected state, you might conditionally render a checkmark next to the seleted option. | ||
```vue | ||
<template> | ||
<Listbox v-model="selectedPerson"> | ||
<ListboxButton>{{ selectedPerson.name }}</ListboxButton> | ||
<ListboxOptions> | ||
<!-- Use the `active` state to conditionally style the active option. --> | ||
<!-- Use the `selected` state to conditionally style the selected option. --> | ||
<ListboxOption | ||
as="template" | ||
v-slot="{ active, selected }" | ||
v-for="person in people" | ||
:key="person.id" | ||
:value="person" | ||
> | ||
<li :class="{ 'bg-blue-500 text-white': active, 'bg-white text-black': !active }"> | ||
<CheckmarkIcon v-show="selected" /> | ||
{{ person.name }} | ||
</li> | ||
</ListboxOption> | ||
</ListboxOptions> | ||
</Listbox> | ||
</template> | ||
<script> | ||
import { ref } from 'vue' | ||
import { | ||
Listbox, | ||
ListboxLabel, | ||
ListboxButton, | ||
ListboxOptions, | ||
ListboxOption, | ||
} from '@headlessui/vue' | ||
import CheckmarkIcon from './CheckmarkIcon' | ||
export default { | ||
components: { | ||
Listbox, | ||
ListboxLabel, | ||
ListboxButton, | ||
ListboxOptions, | ||
ListboxOption, | ||
CheckmarkIcon, | ||
}, | ||
setup() { | ||
const people = [ | ||
{ id: 1, name: 'Durward Reynolds' }, | ||
{ id: 2, name: 'Kenton Towne' }, | ||
{ id: 3, name: 'Therese Wunsch' }, | ||
{ id: 4, name: 'Benedict Kessler' }, | ||
{ id: 5, name: 'Katelyn Rohan' }, | ||
] | ||
const selectedPerson = ref(people[0]) | ||
return { | ||
people, | ||
selectedPerson, | ||
} | ||
}, | ||
} | ||
</script> | ||
``` | ||
### Using a custom label | ||
By default the `Listbox` will use the button contents as the label for screenreaders. However you can also render a custom `ListboxLabel` which will be automatically linked to the listbox with a generated ID. | ||
```vue | ||
<template> | ||
<Listbox v-model="country"> | ||
<ListboxLabel>Country:</ListboxLabel> | ||
<ListboxButton>{country}</ListboxButton> | ||
<ListboxOptions> | ||
<ListboxOption value="australia">Australia</ListboxOption> | ||
<ListboxOption value="belgium">Belgium</ListboxOption> | ||
<ListboxOption value="canada">Canada</ListboxOption> | ||
<ListboxOption value="england">England</ListboxOption> | ||
<!-- ... --> | ||
</ListboxOptions> | ||
</Listbox> | ||
</template> | ||
<script> | ||
import { ref } from 'vue' | ||
import { | ||
Listbox, | ||
ListboxLabel, | ||
ListboxButton, | ||
ListboxOptions, | ||
ListboxOption, | ||
} from '@headlessui/vue' | ||
export default { | ||
components: { | ||
Listbox, | ||
ListboxLabel, | ||
ListboxButton, | ||
ListboxOptions, | ||
ListboxOption, | ||
}, | ||
setup() { | ||
const country = ref('belgium') | ||
return { | ||
country, | ||
} | ||
}, | ||
} | ||
</script> | ||
``` | ||
### Showing/hiding the listbox | ||
By default, your `ListboxOptions` instance will be shown/hidden automatically based on the internal `open` state tracked within the `Listbox` component itself. | ||
```vue | ||
<template> | ||
<Listbox v-model="option"> | ||
<ListboxButton>{{ person.name }}</ListboxButton> | ||
<!-- By default, this will automatically show/hide when the ListboxButton is pressed. --> | ||
<ListboxOptions> | ||
<ListboxOption v-for="person in people" :key="person.id" :value="person"> | ||
{{ person.name }} | ||
</ListboxOption> | ||
</ListboxOptions> | ||
</Listbox> | ||
</template> | ||
<script> | ||
import { ref } from 'vue' | ||
import { | ||
Listbox, | ||
ListboxLabel, | ||
ListboxButton, | ||
ListboxOptions, | ||
ListboxOption, | ||
} from '@headlessui/vue' | ||
export default { | ||
components: { | ||
Listbox, | ||
ListboxLabel, | ||
ListboxButton, | ||
ListboxOptions, | ||
ListboxOption, | ||
}, | ||
setup() { | ||
const people = [ | ||
{ id: 1, name: 'Durward Reynolds' }, | ||
{ id: 2, name: 'Kenton Towne' }, | ||
{ id: 3, name: 'Therese Wunsch' }, | ||
{ id: 4, name: 'Benedict Kessler' }, | ||
{ id: 5, name: 'Katelyn Rohan' }, | ||
] | ||
const selectedPerson = ref(people[0]) | ||
return { | ||
people, | ||
selectedPerson, | ||
} | ||
}, | ||
} | ||
</script> | ||
``` | ||
If you'd rather handle this yourself (perhaps because you need to add an extra wrapper element for one reason or another), you can add a `static` prop to the `ListboxOptions` instance to tell it to always render, and inspect the `open` slot prop provided by the `Listbox` to control which element is shown/hidden yourself. | ||
```vue | ||
<template> | ||
<Listbox v-model="option" v-slot="{ open }"> | ||
<ListboxButton>{{ person.name }}</ListboxButton> | ||
<div v-show="open"> | ||
<!-- Using `static`, `ListboxOptions` is always rendered and ignores the `open` state. --> | ||
<ListboxOptions static> | ||
<ListboxOption v-for="person in people" :key="person.id" :value="person"> | ||
{{ person.name }} | ||
</ListboxOption> | ||
</ListboxOptions> | ||
</div> | ||
</Listbox> | ||
</template> | ||
<script> | ||
import { ref } from 'vue' | ||
import { | ||
Listbox, | ||
ListboxLabel, | ||
ListboxButton, | ||
ListboxOptions, | ||
ListboxOption, | ||
} from '@headlessui/vue' | ||
export default { | ||
components: { | ||
Listbox, | ||
ListboxLabel, | ||
ListboxButton, | ||
ListboxOptions, | ||
ListboxOption, | ||
}, | ||
setup() { | ||
const people = [ | ||
{ id: 1, name: 'Durward Reynolds' }, | ||
{ id: 2, name: 'Kenton Towne' }, | ||
{ id: 3, name: 'Therese Wunsch' }, | ||
{ id: 4, name: 'Benedict Kessler' }, | ||
{ id: 5, name: 'Katelyn Rohan' }, | ||
] | ||
const selectedPerson = ref(people[0]) | ||
return { | ||
people, | ||
selectedPerson, | ||
} | ||
}, | ||
} | ||
</script> | ||
``` | ||
### Disabling an option | ||
Use the `disabled` prop to disable a `ListboxOption`. This will make it unselectable via keyboard navigation, and it will be skipped when pressing the up/down arrows. | ||
```vue | ||
<template> | ||
<Listbox v-model="selectedPerson"> | ||
<ListboxButton>{{ selectedPerson.name }}</ListboxButton> | ||
<ListboxOptions> | ||
<!-- Disabled options will be skipped by keyboard navigation. --> | ||
<ListboxOption | ||
v-for="person in people" | ||
:key="person.id" | ||
:value="person" | ||
:disabled="person.unavailable" | ||
> | ||
<span :class="{ 'opacity-75': person.unavailable }">{{ person.name }}</span> | ||
</ListboxOption> | ||
</ListboxOptions> | ||
</Listbox> | ||
</template> | ||
<script> | ||
import { ref } from 'vue' | ||
import { | ||
Listbox, | ||
ListboxLabel, | ||
ListboxButton, | ||
ListboxOptions, | ||
ListboxOption, | ||
} from '@headlessui/vue' | ||
export default { | ||
components: { | ||
Listbox, | ||
ListboxLabel, | ||
ListboxButton, | ||
ListboxOptions, | ||
ListboxOption, | ||
}, | ||
setup() { | ||
const people = [ | ||
{ id: 1, name: 'Durward Reynolds', unavailable: false }, | ||
{ id: 2, name: 'Kenton Towne', unavailable: false }, | ||
{ id: 3, name: 'Therese Wunsch', unavailable: false }, | ||
{ id: 4, name: 'Benedict Kessler', unavailable: true }, | ||
{ id: 5, name: 'Katelyn Rohan', unavailable: false }, | ||
] | ||
const selectedPerson = ref(people[0]) | ||
return { | ||
people, | ||
selectedPerson, | ||
} | ||
}, | ||
} | ||
</script> | ||
``` | ||
### Transitions | ||
To animate the opening/closing of the menu panel, use Vue's built-in `transition` component. All you need to do is wrap your `ListboxOptions` instance in a `<transition>` element and the transition will be applied automatically. | ||
```vue | ||
<template> | ||
<Listbox v-model="selectedPerson"> | ||
<ListboxButton>{{ selectedPerson.name }}</ListboxButton> | ||
<transition | ||
enter-active-class="transition duration-100 ease-out" | ||
enter-from-class="transform scale-95 opacity-0" | ||
enter-to-class="transform scale-100 opacity-100" | ||
leave-active-class="transition duration-75 ease-out" | ||
leave-from-class="transform scale-100 opacity-100" | ||
leave-to-class="transform scale-95 opacity-0" | ||
> | ||
<ListboxOptions> | ||
<ListboxOption | ||
v-for="person in people" | ||
:key="person.id" | ||
:value="person" | ||
:disabled="person.unavailable" | ||
> | ||
{{ person.name }} | ||
</ListboxOption> | ||
</ListboxOptions> | ||
</transition> | ||
</Listbox> | ||
</template> | ||
<script> | ||
import { ref } from 'vue' | ||
import { | ||
Listbox, | ||
ListboxLabel, | ||
ListboxButton, | ||
ListboxOptions, | ||
ListboxOption, | ||
} from '@headlessui/vue' | ||
export default { | ||
components: { | ||
Listbox, | ||
ListboxLabel, | ||
ListboxButton, | ||
ListboxOptions, | ||
ListboxOption, | ||
}, | ||
setup() { | ||
const people = [ | ||
{ id: 1, name: 'Durward Reynolds', unavailable: false }, | ||
{ id: 2, name: 'Kenton Towne', unavailable: false }, | ||
{ id: 3, name: 'Therese Wunsch', unavailable: false }, | ||
{ id: 4, name: 'Benedict Kessler', unavailable: true }, | ||
{ id: 5, name: 'Katelyn Rohan', unavailable: false }, | ||
] | ||
const selectedPerson = ref(people[0]) | ||
return { | ||
people, | ||
selectedPerson, | ||
} | ||
}, | ||
} | ||
</script> | ||
``` | ||
### Rendering a different element for a component | ||
By default, the `Listbox` and its subcomponents each render a default element that is sensible for that component. | ||
For example, `ListboxLabel` renders a `label` by default, `ListboxButton` renders a `button` by default, `ListboxOptions` renders a `ul` and `ListboxOption` renders a `li` by default. `Listbox` interestingly _does not render an extra element_, and instead renders its children directly by default. | ||
This is easy to change using the `as` prop, which exists on every component. | ||
```vue | ||
<template> | ||
<Listbox as="div" v-model="selectedPerson"> | ||
<ListboxButton>{{ selectedPerson.name }}</ListboxButton> | ||
<ListboxOptions as="div"> | ||
<ListboxOption as="span" v-for="person in people" :key="person.id" :value="person"> | ||
{{ person.name }} | ||
</ListboxOption> | ||
</ListboxOptions> | ||
</Listbox> | ||
</template> | ||
<script> | ||
import { ref } from 'vue' | ||
import { | ||
Listbox, | ||
ListboxLabel, | ||
ListboxButton, | ||
ListboxOptions, | ||
ListboxOption, | ||
} from '@headlessui/vue' | ||
export default { | ||
components: { | ||
Listbox, | ||
ListboxLabel, | ||
ListboxButton, | ||
ListboxOptions, | ||
ListboxOption, | ||
}, | ||
setup() { | ||
const people = [ | ||
{ id: 1, name: 'Durward Reynolds' }, | ||
{ id: 2, name: 'Kenton Towne' }, | ||
{ id: 3, name: 'Therese Wunsch' }, | ||
{ id: 4, name: 'Benedict Kessler' }, | ||
{ id: 5, name: 'Katelyn Rohan' }, | ||
] | ||
const selectedPerson = ref(people[0]) | ||
return { | ||
people, | ||
selectedPerson, | ||
} | ||
}, | ||
} | ||
</script> | ||
``` | ||
To tell an element to render its children directly with no wrapper element, use `as="template"`. | ||
```vue | ||
<template> | ||
<Listbox v-model="selectedPerson"> | ||
<!-- Render no wrapper, instead pass in a button manually. --> | ||
<ListboxButton as="template"> | ||
<button>{{ selectedPerson.name }}</button> | ||
</ListboxButton> | ||
<ListboxOptions> | ||
<ListboxOption v-for="person in people" :key="person.id" :value="person"> | ||
{{ person.name }} | ||
</ListboxOption> | ||
</ListboxOptions> | ||
</Listbox> | ||
</template> | ||
<script> | ||
import { ref } from 'vue' | ||
import { | ||
Listbox, | ||
ListboxLabel, | ||
ListboxButton, | ||
ListboxOptions, | ||
ListboxOption, | ||
} from '@headlessui/vue' | ||
export default { | ||
components: { | ||
Listbox, | ||
ListboxLabel, | ||
ListboxButton, | ||
ListboxOptions, | ||
ListboxOption, | ||
}, | ||
setup() { | ||
const people = [ | ||
{ id: 1, name: 'Durward Reynolds' }, | ||
{ id: 2, name: 'Kenton Towne' }, | ||
{ id: 3, name: 'Therese Wunsch' }, | ||
{ id: 4, name: 'Benedict Kessler' }, | ||
{ id: 5, name: 'Katelyn Rohan' }, | ||
] | ||
const selectedPerson = ref(people[0]) | ||
return { | ||
people, | ||
selectedPerson, | ||
} | ||
}, | ||
} | ||
</script> | ||
``` | ||
### Component API | ||
#### Listbox | ||
```vue | ||
<template> | ||
<Listbox v-model="selectedPerson"> | ||
<ListboxButton> | ||
{{ selectedPerson.name }} | ||
</ListboxButton> | ||
<ListboxOptions> | ||
<ListboxOption v-for="person in people" :key="person.id" :value="person"> | ||
{{ person.name }} | ||
</ListboxOption> | ||
</ListboxOptions> | ||
</Listbox> | ||
</template> | ||
<script> | ||
import { ref } from 'vue' | ||
import { | ||
Listbox, | ||
ListboxLabel, | ||
ListboxButton, | ||
ListboxOptions, | ||
ListboxOption, | ||
} from '@headlessui/vue' | ||
export default { | ||
components: { | ||
Listbox, | ||
ListboxLabel, | ||
ListboxButton, | ||
ListboxOptions, | ||
ListboxOption, | ||
}, | ||
setup() { | ||
const people = [ | ||
{ id: 1, name: 'Durward Reynolds' }, | ||
{ id: 2, name: 'Kenton Towne' }, | ||
{ id: 3, name: 'Therese Wunsch' }, | ||
{ id: 4, name: 'Benedict Kessler' }, | ||
{ id: 5, name: 'Katelyn Rohan' }, | ||
] | ||
const selectedPerson = ref(people[0]) | ||
return { | ||
people, | ||
selectedPerson, | ||
} | ||
}, | ||
} | ||
</script> | ||
``` | ||
##### Props | ||
| Prop | Type | Default | Description | | ||
| :--------- | :------------------ | :-------------------------------- | :------------------------------------------------------- | | ||
| `as` | String \| Component | `template` _(no wrapper element_) | The element or component the `Listbox` should render as. | | ||
| `v-model` | `T` | - | The selected value. | | ||
| `disabled` | Boolean | `false` | Enable/Disable the `Listbox` component. | | ||
##### Slot props | ||
| Prop | Type | Description | | ||
| :--------- | :------ | :-------------------------------------- | | ||
| `open` | Boolean | Whether or not the listbox is open. | | ||
| `disabled` | Boolean | Whether or not the listbox is disabled. | | ||
#### ListboxButton | ||
```vue | ||
<ListboxButton v-slot="{ open }"> | ||
<span>{{ selectedPerson.name }}</span> | ||
<ChevronRightIcon class={`${open ? 'transform rotate-90' : ''}`} /> | ||
</ListboxButton> | ||
``` | ||
##### Props | ||
| Prop | Type | Default | Description | | ||
| :--- | :------------------ | :------- | :------------------------------------------------------------- | | ||
| `as` | String \| Component | `button` | The element or component the `ListboxButton` should render as. | | ||
##### Slot props | ||
| Prop | Type | Description | | ||
| :--------- | :------ | :-------------------------------------- | | ||
| `open` | Boolean | Whether or not the listbox is open. | | ||
| `disabled` | Boolean | Whether or not the listbox is disabled. | | ||
#### ListboxLabel | ||
```vue | ||
<ListboxLabel>Enable notifications</ListboxLabel> | ||
``` | ||
##### Props | ||
| Prop | Type | Default | Description | | ||
| :--- | :------------------ | :------ | :------------------------------------------------------------ | | ||
| `as` | String \| Component | `label` | The element or component the `ListboxLabel` should render as. | | ||
##### Slot props | ||
| Prop | Type | Description | | ||
| :--------- | :------ | :-------------------------------------- | | ||
| `open` | Boolean | Whether or not the listbox is open. | | ||
| `disabled` | Boolean | Whether or not the listbox is disabled. | | ||
#### ListboxOptions | ||
```vue | ||
<ListboxOptions> | ||
<ListboxOption value="option-a"><!-- ... --></ListboxOption> | ||
<!-- ... --> | ||
</ListboxOptions> | ||
``` | ||
##### Props | ||
| Prop | Type | Default | Description | | ||
| :-------- | :------------------ | :------ | :-------------------------------------------------------------------------------- | | ||
| `as` | String \| Component | `ul` | The element or component the `ListboxOptions` should render as. | | ||
| `static` | Boolean | `false` | Whether the element should ignore the internally managed open/closed state. | | ||
| `unmount` | Boolean | `true` | Whether the element should be unmounted or hidden based on the open/closed state. | | ||
##### Slot props | ||
| Prop | Type | Description | | ||
| :----- | :------ | :---------------------------------- | | ||
| `open` | Boolean | Whether or not the listbox is open. | | ||
#### ListboxOption | ||
```vue | ||
<ListboxOption value="option-a">Option A</ListboxOption> | ||
``` | ||
##### Props | ||
| Prop | Type | Default | Description | | ||
| :--------- | :------------------ | :------ | :-------------------------------------------------------------------------------------- | | ||
| `as` | String \| Component | `li` | The element or component the `ListboxOption` should render as. | | ||
| `value` | `T` | - | The option value. | | ||
| `disabled` | Boolean | `false` | Whether or not the option should be disabled for keyboard navigation and ARIA purposes. | | ||
##### Slot props | ||
| Prop | Type | Description | | ||
| :--------- | :------ | :----------------------------------------------------------------------------------- | | ||
| `active` | Boolean | Whether or not the option is the active/focused option in the list. | | ||
| `selected` | Boolean | Whether or not the option is the selected option in the list. | | ||
| `disabled` | Boolean | Whether or not the option is the disabled for keyboard navigation and ARIA purposes. | | ||
## Switch (Toggle) | ||
[View live demo on CodeSandbox](https://codesandbox.io/s/headlessuivue-switch-example-8ycp6?file=/src/App.vue) | ||
The `Switch` component and related child components are used to quickly build custom switch/toggle components that are fully accessible out of the box, including correct ARIA attribute management and robust keyboard support. | ||
- [Basic example](#basic-example-2) | ||
- [Using a custom label](#using-a-custom-label-1) | ||
- [Component API](#component-api-2) | ||
### Basic example | ||
Switches are built using the `Switch` component. Optionally you can also use the `SwitchGroup`, `SwitchLabel` and `SwitchDescription` components. | ||
```vue | ||
<template> | ||
<Switch | ||
as="button" | ||
v-model="switchValue" | ||
class="relative inline-flex items-center h-6 rounded-full w-11" | ||
:class="switchValue ? 'bg-blue-600' : 'bg-gray-200'" | ||
v-slot="{ checked }" | ||
> | ||
<span | ||
class="inline-block w-4 h-4 transform bg-white rounded-full" | ||
:class="{ 'translate-x-6': checked, 'translate-x-1': !checked }" | ||
/> | ||
</Switch> | ||
</template> | ||
<script> | ||
import { ref } from 'vue' | ||
import { SwitchGroup, Switch, SwitchLabel, SwitchDescription } from '@headlessui/vue' | ||
export default { | ||
components: { | ||
SwitchGroup, | ||
Switch, | ||
SwitchLabel, | ||
SwitchDescription, | ||
}, | ||
setup() { | ||
const switchValue = ref(false) | ||
return { | ||
switchValue, | ||
} | ||
}, | ||
} | ||
</script> | ||
``` | ||
### Using a custom label | ||
By default the `Switch` will use the contents as the label for screenreaders. If you need more control, you can render a `SwitchLabel` outside of the `Switch`, as long as both the switch and label are within a parent `SwitchGroup`. | ||
Clicking the label will toggle the switch state, like you'd expect from a native checkbox. | ||
```vue | ||
<template> | ||
<SwitchGroup as="div" class="flex items-center space-x-4"> | ||
<SwitchLabel>Enable notifications</SwitchLabel> | ||
<Switch | ||
as="button" | ||
v-model="switchValue" | ||
class="relative inline-flex items-center h-6 rounded-full w-11" | ||
:class="switchValue ? 'bg-blue-600' : 'bg-gray-200'" | ||
v-slot="{ checked }" | ||
> | ||
<span | ||
class="inline-block w-4 h-4 transform bg-white rounded-full" | ||
:class="{ 'translate-x-6': checked, 'translate-x-1': !checked }" | ||
/> | ||
</Switch> | ||
</SwitchGroup> | ||
</template> | ||
<script> | ||
import { ref } from 'vue' | ||
import { SwitchGroup, Switch, SwitchLabel } from '@headlessui/vue' | ||
export default { | ||
components: { | ||
SwitchGroup, | ||
Switch, | ||
SwitchLabel, | ||
}, | ||
setup() { | ||
const switchValue = ref(false) | ||
return { | ||
switchValue, | ||
} | ||
}, | ||
} | ||
</script> | ||
``` | ||
### Component API | ||
#### Switch | ||
```html | ||
<Switch v-model="switchState"> | ||
<span class="sr-only">Enable notifications</span> | ||
<!-- ... --> | ||
</Switch> | ||
``` | ||
##### Props | ||
| Prop | Type | Default | Description | | ||
| :-------- | :------------------ | :------- | :------------------------------------------------------ | | ||
| `as` | String \| Component | `button` | The element or component the `Switch` should render as. | | ||
| `v-model` | `T` | - | The switch value. | | ||
##### Slot props | ||
| Prop | Type | Description | | ||
| :-------- | :------ | :------------------------------------ | | ||
| `checked` | Boolean | Whether or not the switch is checked. | | ||
#### SwitchLabel | ||
```html | ||
<SwitchGroup> | ||
<SwitchLabel>Enable notifications</SwitchLabel> | ||
<Switch v-model="switchState"> | ||
<!-- ... --> | ||
</Switch> | ||
</SwitchGroup> | ||
``` | ||
##### Props | ||
| Prop | Type | Default | Description | | ||
| :--- | :------------------ | :------ | :----------------------------------------------------------- | | ||
| `as` | String \| Component | `label` | The element or component the `SwitchLabel` should render as. | | ||
#### SwitchDescription | ||
```html | ||
<SwitchGroup> | ||
<SwitchDescription>Enable notifications</SwitchDescription> | ||
<Switch v-model="switchState"> | ||
<!-- ... --> | ||
</Switch> | ||
</SwitchGroup> | ||
``` | ||
##### Props | ||
| Prop | Type | Default | Description | | ||
| :--- | :------------------ | :------ | :----------------------------------------------------------------- | | ||
| `as` | String \| Component | `p` | The element or component the `SwitchDescription` should render as. | | ||
#### SwitchGroup | ||
```html | ||
<SwitchGroup> | ||
<SwitchLabel>Enable notifications</SwitchLabel> | ||
<Switch v-model="switchState"> | ||
<!-- ... --> | ||
</Switch> | ||
</SwitchGroup> | ||
``` | ||
##### Props | ||
| Prop | Type | Default | Description | | ||
| :--- | :------------------ | :-------------------------------- | :----------------------------------------------------------- | | ||
| `as` | String \| Component | `template` _(no wrapper element)_ | The element or component the `SwitchGroup` should render as. | |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
689190
31
5907
51