@headlessui/vue
Version:
A set of completely unstyled, fully accessible UI components for Vue 3, designed to integrate beautifully with Tailwind CSS.
1,680 lines (1,435 loc) • 154 kB
JavaScript
import { cloneVNode, h, onUnmounted, ref, watchEffect, onUpdated, inject, provide, defineComponent, Teleport, reactive, computed, unref, onMounted, nextTick, toRaw, watch } from 'vue';
function _extends() {
_extends = Object.assign || function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}
function _objectWithoutPropertiesLoose(source, excluded) {
if (source == null) return {};
var target = {};
var sourceKeys = Object.keys(source);
var key, i;
for (i = 0; i < sourceKeys.length; i++) {
key = sourceKeys[i];
if (excluded.indexOf(key) >= 0) continue;
target[key] = source[key];
}
return target;
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(o);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
return arr2;
}
function _createForOfIteratorHelperLoose(o, allowArrayLike) {
var it;
if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) {
if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
if (it) o = it;
var i = 0;
return function () {
if (i >= o.length) return {
done: true
};
return {
done: false,
value: o[i++]
};
};
}
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
it = o[Symbol.iterator]();
return it.next.bind(it);
}
function match(value, lookup) {
if (value in lookup) {
var returnValue = lookup[value];
for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
args[_key - 2] = arguments[_key];
}
return typeof returnValue === 'function' ? returnValue.apply(void 0, args) : returnValue;
}
var error = new Error("Tried to handle \"" + value + "\" but there is no handler defined. Only defined handlers are: " + Object.keys(lookup).map(function (key) {
return "\"" + key + "\"";
}).join(', ') + ".");
if (Error.captureStackTrace) Error.captureStackTrace(error, match);
throw error;
}
var Features;
(function (Features) {
/** No features at all */
Features[Features["None"] = 0] = "None";
/**
* When used, this will allow us to use one of the render strategies.
*
* **The render strategies are:**
* - **Unmount** _(Will unmount the component.)_
* - **Hidden** _(Will hide the component using the [hidden] attribute.)_
*/
Features[Features["RenderStrategy"] = 1] = "RenderStrategy";
/**
* When used, this will allow the user of our component to be in control. This can be used when
* you want to transition based on some state.
*/
Features[Features["Static"] = 2] = "Static";
})(Features || (Features = {}));
var RenderStrategy;
(function (RenderStrategy) {
RenderStrategy[RenderStrategy["Unmount"] = 0] = "Unmount";
RenderStrategy[RenderStrategy["Hidden"] = 1] = "Hidden";
})(RenderStrategy || (RenderStrategy = {}));
function render(_ref) {
var _ref$visible = _ref.visible,
visible = _ref$visible === void 0 ? true : _ref$visible,
_ref$features = _ref.features,
features = _ref$features === void 0 ? Features.None : _ref$features,
main = _objectWithoutPropertiesLoose(_ref, ["visible", "features"]);
// Visible always render
if (visible) return _render(main);
if (features & Features.Static) {
// When the `static` prop is passed as `true`, then the user is in control, thus we don't care about anything else
if (main.props["static"]) return _render(main);
}
if (features & Features.RenderStrategy) {
var _main$props$unmount, _match;
var strategy = ((_main$props$unmount = main.props.unmount) != null ? _main$props$unmount : true) ? RenderStrategy.Unmount : RenderStrategy.Hidden;
return match(strategy, (_match = {}, _match[RenderStrategy.Unmount] = function () {
return null;
}, _match[RenderStrategy.Hidden] = function () {
return _render(_extends({}, main, {
props: _extends({}, main.props, {
hidden: true,
style: {
display: 'none'
}
})
}));
}, _match));
} // No features enabled, just render
return _render(main);
}
function _render(_ref2) {
var props = _ref2.props,
attrs = _ref2.attrs,
slots = _ref2.slots,
slot = _ref2.slot,
name = _ref2.name;
var _omit = omit(props, ['unmount', 'static']),
as = _omit.as,
passThroughProps = _objectWithoutPropertiesLoose(_omit, ["as"]);
var children = slots["default"] == null ? void 0 : slots["default"](slot);
if (as === 'template') {
if (Object.keys(passThroughProps).length > 0 || Object.keys(attrs).length > 0) {
var _ref3 = children != null ? children : [],
firstChild = _ref3[0],
other = _ref3.slice(1);
if (!isValidElement(firstChild) || other.length > 0) {
throw new Error(['Passing props on "template"!', '', "The current component <" + name + " /> is rendering a \"template\".", "However we need to passthrough the following props:", Object.keys(passThroughProps).concat(Object.keys(attrs)).map(function (line) {
return " - " + line;
}).join('\n'), '', 'You can apply a few solutions:', ['Add an `as="..."` prop, to ensure that we render an actual element instead of a "template".', 'Render a single element as the child so that we can forward the props onto that element.'].map(function (line) {
return " - " + line;
}).join('\n')].join('\n'));
}
return cloneVNode(firstChild, passThroughProps);
}
if (Array.isArray(children) && children.length === 1) {
return children[0];
}
return children;
}
return h(as, passThroughProps, children);
}
function omit(object, keysToOmit) {
if (keysToOmit === void 0) {
keysToOmit = [];
}
var clone = Object.assign({}, object);
for (var _iterator = _createForOfIteratorHelperLoose(keysToOmit), _step; !(_step = _iterator()).done;) {
var key = _step.value;
if (key in clone) delete clone[key];
}
return clone;
}
function isValidElement(input) {
if (input == null) return false; // No children
if (typeof input.type === 'string') return true; // 'div', 'span', ...
if (typeof input.type === 'object') return true; // Other components
if (typeof input.type === 'function') return true; // Built-ins like Transition
return false; // Comments, strings, ...
}
// TODO: This must already exist somewhere, right? 🤔
// Ref: https://www.w3.org/TR/uievents-key/#named-key-attribute-values
var Keys;
(function (Keys) {
Keys["Space"] = " ";
Keys["Enter"] = "Enter";
Keys["Escape"] = "Escape";
Keys["Backspace"] = "Backspace";
Keys["ArrowLeft"] = "ArrowLeft";
Keys["ArrowUp"] = "ArrowUp";
Keys["ArrowRight"] = "ArrowRight";
Keys["ArrowDown"] = "ArrowDown";
Keys["Home"] = "Home";
Keys["End"] = "End";
Keys["PageUp"] = "PageUp";
Keys["PageDown"] = "PageDown";
Keys["Tab"] = "Tab";
})(Keys || (Keys = {}));
var id = 0;
function generateId() {
return ++id;
}
function useId() {
return generateId();
}
// - https://stackoverflow.com/a/30753870
var focusableSelector = /*#__PURE__*/['[contentEditable=true]', '[tabindex]', 'a[href]', 'area[href]', 'button:not([disabled])', 'iframe', 'input:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])'].map(process.env.NODE_ENV === 'test' ? // TODO: Remove this once JSDOM fixes the issue where an element that is
// "hidden" can be the document.activeElement, because this is not possible
// in real browsers.
// TODO: Remove this once JSDOM fixes the issue where an element that is
function (selector) {
return selector + ":not([tabindex='-1']):not([style*='display: none'])";
} : function (selector) {
return selector + ":not([tabindex='-1'])";
}).join(',');
var Focus;
(function (Focus) {
/** Focus the first non-disabled element */
Focus[Focus["First"] = 1] = "First";
/** Focus the previous non-disabled element */
Focus[Focus["Previous"] = 2] = "Previous";
/** Focus the next non-disabled element */
Focus[Focus["Next"] = 4] = "Next";
/** Focus the last non-disabled element */
Focus[Focus["Last"] = 8] = "Last";
/** Wrap tab around */
Focus[Focus["WrapAround"] = 16] = "WrapAround";
/** Prevent scrolling the focusable elements into view */
Focus[Focus["NoScroll"] = 32] = "NoScroll";
})(Focus || (Focus = {}));
var FocusResult;
(function (FocusResult) {
FocusResult[FocusResult["Error"] = 0] = "Error";
FocusResult[FocusResult["Overflow"] = 1] = "Overflow";
FocusResult[FocusResult["Success"] = 2] = "Success";
FocusResult[FocusResult["Underflow"] = 3] = "Underflow";
})(FocusResult || (FocusResult = {}));
var Direction;
(function (Direction) {
Direction[Direction["Previous"] = -1] = "Previous";
Direction[Direction["Next"] = 1] = "Next";
})(Direction || (Direction = {}));
function getFocusableElements(container) {
if (container === void 0) {
container = document.body;
}
if (container == null) return [];
return Array.from(container.querySelectorAll(focusableSelector));
}
var FocusableMode;
(function (FocusableMode) {
/** The element itself must be focusable. */
FocusableMode[FocusableMode["Strict"] = 0] = "Strict";
/** The element should be inside of a focusable element. */
FocusableMode[FocusableMode["Loose"] = 1] = "Loose";
})(FocusableMode || (FocusableMode = {}));
function isFocusableElement(element, mode) {
var _match;
if (mode === void 0) {
mode = FocusableMode.Strict;
}
if (element === document.body) return false;
return match(mode, (_match = {}, _match[FocusableMode.Strict] = function () {
return element.matches(focusableSelector);
}, _match[FocusableMode.Loose] = function () {
var next = element;
while (next !== null) {
if (next.matches(focusableSelector)) return true;
next = next.parentElement;
}
return false;
}, _match));
}
function focusElement(element) {
element == null ? void 0 : element.focus({
preventScroll: true
});
}
function focusIn(container, focus) {
var elements = Array.isArray(container) ? container : getFocusableElements(container);
var active = document.activeElement;
var direction = function () {
if (focus & (Focus.First | Focus.Next)) return Direction.Next;
if (focus & (Focus.Previous | Focus.Last)) return Direction.Previous;
throw new Error('Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last');
}();
var startIndex = function () {
if (focus & Focus.First) return 0;
if (focus & Focus.Previous) return Math.max(0, elements.indexOf(active)) - 1;
if (focus & Focus.Next) return Math.max(0, elements.indexOf(active)) + 1;
if (focus & Focus.Last) return elements.length - 1;
throw new Error('Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last');
}();
var focusOptions = focus & Focus.NoScroll ? {
preventScroll: true
} : {};
var offset = 0;
var total = elements.length;
var next = undefined;
do {
var _next;
// Guard against infinite loops
if (offset >= total || offset + total <= 0) return FocusResult.Error;
var nextIdx = startIndex + offset;
if (focus & Focus.WrapAround) {
nextIdx = (nextIdx + total) % total;
} else {
if (nextIdx < 0) return FocusResult.Underflow;
if (nextIdx >= total) return FocusResult.Overflow;
}
next = elements[nextIdx]; // Try the focus the next element, might not work if it is "hidden" to the user.
(_next = next) == null ? void 0 : _next.focus(focusOptions); // Try the next one in line
offset += direction;
} while (next !== document.activeElement); // This is a little weird, but let me try and explain: There are a few scenario's
// in chrome for example where a focused `<a>` tag does not get the default focus
// styles and sometimes they do. This highly depends on whether you started by
// clicking or by using your keyboard. When you programmatically add focus `anchor.focus()`
// then the active element (document.activeElement) is this anchor, which is expected.
// However in that case the default focus styles are not applied *unless* you
// also add this tabindex.
if (!next.hasAttribute('tabindex')) next.setAttribute('tabindex', '0');
return FocusResult.Success;
}
function useWindowEvent(type, listener, options) {
window.addEventListener(type, listener, options);
onUnmounted(function () {
return window.removeEventListener(type, listener, options);
});
}
function contains(containers, element) {
for (var _iterator = _createForOfIteratorHelperLoose(containers), _step; !(_step = _iterator()).done;) {
var container = _step.value;
if (container.contains(element)) return true;
}
return false;
}
function useFocusTrap(containers, enabled, options) {
if (enabled === void 0) {
enabled = ref(true);
}
if (options === void 0) {
options = ref({});
}
var restoreElement = ref(typeof window !== 'undefined' ? document.activeElement : null);
var previousActiveElement = ref(null);
function handleFocus() {
if (!enabled.value) return;
if (containers.value.size !== 1) return;
var initialFocus = options.value.initialFocus;
var activeElement = document.activeElement;
if (initialFocus) {
if (initialFocus === activeElement) {
return; // Initial focus ref is already the active element
}
} else if (contains(containers.value, activeElement)) {
return; // Already focused within Dialog
}
restoreElement.value = activeElement; // Try to focus the initialFocus ref
if (initialFocus) {
focusElement(initialFocus);
} else {
var couldFocus = false;
for (var _iterator = _createForOfIteratorHelperLoose(containers.value), _step; !(_step = _iterator()).done;) {
var container = _step.value;
var result = focusIn(container, Focus.First);
if (result === FocusResult.Success) {
couldFocus = true;
break;
}
}
if (!couldFocus) console.warn('There are no focusable elements inside the <FocusTrap />');
}
previousActiveElement.value = document.activeElement;
} // Restore when `enabled` becomes false
function restore() {
focusElement(restoreElement.value);
restoreElement.value = null;
previousActiveElement.value = null;
} // Handle initial focus
watchEffect(handleFocus);
onUpdated(function () {
enabled.value ? handleFocus() : restore();
});
onUnmounted(restore); // Handle Tab & Shift+Tab keyboard events
useWindowEvent('keydown', function (event) {
if (!enabled.value) return;
if (event.key !== Keys.Tab) return;
if (!document.activeElement) return;
if (containers.value.size !== 1) return;
event.preventDefault();
for (var _iterator2 = _createForOfIteratorHelperLoose(containers.value), _step2; !(_step2 = _iterator2()).done;) {
var element = _step2.value;
var result = focusIn(element, (event.shiftKey ? Focus.Previous : Focus.Next) | Focus.WrapAround);
if (result === FocusResult.Success) {
previousActiveElement.value = document.activeElement;
break;
}
}
}); // Prevent programmatically escaping
useWindowEvent('focus', function (event) {
if (!enabled.value) return;
if (containers.value.size !== 1) return;
var previous = previousActiveElement.value;
if (!previous) return;
var toElement = event.target;
if (toElement && toElement instanceof HTMLElement) {
if (!contains(containers.value, toElement)) {
event.preventDefault();
event.stopPropagation();
focusElement(previous);
} else {
previousActiveElement.value = toElement;
focusElement(toElement);
}
} else {
focusElement(previousActiveElement.value);
}
}, true);
}
var CHILDREN_SELECTOR = process.env.NODE_ENV === 'test' ? '[data-v-app=""] > *' : 'body > *';
var interactables = /*#__PURE__*/new Set();
var originals = /*#__PURE__*/new Map();
function inert(element) {
element.setAttribute('aria-hidden', 'true'); // @ts-expect-error `inert` does not exist on HTMLElement (yet!)
element.inert = true;
}
function restore(element) {
var original = originals.get(element);
if (!original) return;
if (original['aria-hidden'] === null) element.removeAttribute('aria-hidden');else element.setAttribute('aria-hidden', original['aria-hidden']); // @ts-expect-error `inert` does not exist on HTMLElement (yet!)
element.inert = original.inert;
}
function useInertOthers(container, enabled) {
if (enabled === void 0) {
enabled = ref(true);
}
watchEffect(function (onInvalidate) {
if (!enabled.value) return;
if (!container.value) return;
var element = container.value; // Mark myself as an interactable element
interactables.add(element); // Restore elements that now contain an interactable child
for (var _iterator = _createForOfIteratorHelperLoose(originals.keys()), _step; !(_step = _iterator()).done;) {
var original = _step.value;
if (original.contains(element)) {
restore(original);
originals["delete"](original);
}
} // Collect direct children of the body
document.querySelectorAll(CHILDREN_SELECTOR).forEach(function (child) {
if (!(child instanceof HTMLElement)) return; // Skip non-HTMLElements
// Skip the interactables, and the parents of the interactables
for (var _iterator2 = _createForOfIteratorHelperLoose(interactables), _step2; !(_step2 = _iterator2()).done;) {
var interactable = _step2.value;
if (child.contains(interactable)) return;
} // Keep track of the elements
if (interactables.size === 1) {
originals.set(child, {
'aria-hidden': child.getAttribute('aria-hidden'),
// @ts-expect-error `inert` does not exist on HTMLElement (yet!)
inert: child.inert
}); // Mutate the element
inert(child);
}
});
onInvalidate(function () {
// Inert is disabled on the current element
interactables["delete"](element); // We still have interactable elements, therefore this one and its parent
// will become inert as well.
if (interactables.size > 0) {
// Collect direct children of the body
document.querySelectorAll(CHILDREN_SELECTOR).forEach(function (child) {
if (!(child instanceof HTMLElement)) return; // Skip non-HTMLElements
// Skip already inert parents
if (originals.has(child)) return; // Skip the interactables, and the parents of the interactables
for (var _iterator3 = _createForOfIteratorHelperLoose(interactables), _step3; !(_step3 = _iterator3()).done;) {
var interactable = _step3.value;
if (child.contains(interactable)) return;
}
originals.set(child, {
'aria-hidden': child.getAttribute('aria-hidden'),
// @ts-expect-error `inert` does not exist on HTMLElement (yet!)
inert: child.inert
}); // Mutate the element
inert(child);
});
} else {
for (var _iterator4 = _createForOfIteratorHelperLoose(originals.keys()), _step4; !(_step4 = _iterator4()).done;) {
var _element = _step4.value;
// Restore
restore(_element); // Cleanup
originals["delete"](_element);
}
}
});
});
}
var StackContext = /*#__PURE__*/Symbol('StackContext');
var StackMessage;
(function (StackMessage) {
StackMessage[StackMessage["AddElement"] = 0] = "AddElement";
StackMessage[StackMessage["RemoveElement"] = 1] = "RemoveElement";
})(StackMessage || (StackMessage = {}));
function useStackContext() {
return inject(StackContext, function () {});
}
function useElemenStack(element) {
var notify = useStackContext();
watchEffect(function (onInvalidate) {
var domElement = element == null ? void 0 : element.value;
if (!domElement) return;
notify(StackMessage.AddElement, domElement);
onInvalidate(function () {
return notify(StackMessage.RemoveElement, domElement);
});
});
}
function useStackProvider(onUpdate) {
var parentUpdate = useStackContext();
function notify() {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
// Notify our layer
onUpdate == null ? void 0 : onUpdate.apply(void 0, args); // Notify the parent
parentUpdate.apply(void 0, args);
}
provide(StackContext, notify);
}
var ForcePortalRootContext = /*#__PURE__*/Symbol('ForcePortalRootContext');
function usePortalRoot() {
return inject(ForcePortalRootContext, false);
}
var ForcePortalRoot = /*#__PURE__*/defineComponent({
name: 'ForcePortalRoot',
props: {
as: {
type: [Object, String],
"default": 'template'
},
force: {
type: Boolean,
"default": false
}
},
setup: function setup(props, _ref) {
var slots = _ref.slots,
attrs = _ref.attrs;
provide(ForcePortalRootContext, props.force);
return function () {
var passThroughProps = _objectWithoutPropertiesLoose(props, ["force"]);
return render({
props: passThroughProps,
slot: {},
slots: slots,
attrs: attrs,
name: 'ForcePortalRoot'
});
};
}
});
function getPortalRoot() {
var existingRoot = document.getElementById('headlessui-portal-root');
if (existingRoot) return existingRoot;
var root = document.createElement('div');
root.setAttribute('id', 'headlessui-portal-root');
return document.body.appendChild(root);
}
var Portal = /*#__PURE__*/defineComponent({
name: 'Portal',
props: {
as: {
type: [Object, String],
"default": 'div'
}
},
setup: function setup(props, _ref) {
var slots = _ref.slots,
attrs = _ref.attrs;
var forcePortalRoot = usePortalRoot();
var groupContext = inject(PortalGroupContext, null);
var myTarget = ref(forcePortalRoot === true ? getPortalRoot() : groupContext === null ? getPortalRoot() : groupContext.resolveTarget());
watchEffect(function () {
if (forcePortalRoot) return;
if (groupContext === null) return;
myTarget.value = groupContext.resolveTarget();
});
var element = ref(null);
useElemenStack(element);
onUnmounted(function () {
var root = document.getElementById('headlessui-portal-root');
if (!root) return;
if (myTarget.value !== root) return;
if (myTarget.value.children.length <= 0) {
var _myTarget$value$paren;
(_myTarget$value$paren = myTarget.value.parentElement) == null ? void 0 : _myTarget$value$paren.removeChild(myTarget.value);
}
});
useStackProvider();
return function () {
if (myTarget.value === null) return null;
var propsWeControl = {
ref: element
};
return h( // @ts-expect-error Children can be an object, but TypeScript is not happy
// with it. Once this is fixed upstream we can remove this assertion.
Teleport, {
to: myTarget.value
}, render({
props: _extends({}, props, propsWeControl),
slot: {},
attrs: attrs,
slots: slots,
name: 'Portal'
}));
};
}
}); // ---
var PortalGroupContext = /*#__PURE__*/Symbol('PortalGroupContext');
var PortalGroup = /*#__PURE__*/defineComponent({
name: 'PortalGroup',
props: {
as: {
type: [Object, String],
"default": 'template'
},
target: {
type: Object,
"default": null
}
},
setup: function setup(props, _ref2) {
var attrs = _ref2.attrs,
slots = _ref2.slots;
var api = reactive({
resolveTarget: function resolveTarget() {
return props.target;
}
});
provide(PortalGroupContext, api);
return function () {
var passThroughProps = _objectWithoutPropertiesLoose(props, ["target"]);
return render({
props: passThroughProps,
slot: {},
attrs: attrs,
slots: slots,
name: 'PortalGroup'
});
};
}
});
var DescriptionContext = /*#__PURE__*/Symbol('DescriptionContext');
function useDescriptionContext() {
var context = inject(DescriptionContext, null);
if (context === null) {
throw new Error('Missing parent');
}
return context;
}
function useDescriptions(_temp) {
var _ref = _temp === void 0 ? {} : _temp,
_ref$slot = _ref.slot,
slot = _ref$slot === void 0 ? ref({}) : _ref$slot,
_ref$name = _ref.name,
name = _ref$name === void 0 ? 'Description' : _ref$name,
_ref$props = _ref.props,
props = _ref$props === void 0 ? {} : _ref$props;
var descriptionIds = ref([]);
function register(value) {
descriptionIds.value.push(value);
return function () {
var idx = descriptionIds.value.indexOf(value);
if (idx === -1) return;
descriptionIds.value.splice(idx, 1);
};
}
provide(DescriptionContext, {
register: register,
slot: slot,
name: name,
props: props
}); // The actual id's as string or undefined.
return computed(function () {
return descriptionIds.value.length > 0 ? descriptionIds.value.join(' ') : undefined;
});
} // ---
var Description = /*#__PURE__*/defineComponent({
name: 'Description',
props: {
as: {
type: [Object, String],
"default": 'p'
}
},
render: function render$1() {
var _this$context = this.context,
_this$context$name = _this$context.name,
name = _this$context$name === void 0 ? 'Description' : _this$context$name,
_this$context$slot = _this$context.slot,
slot = _this$context$slot === void 0 ? ref({}) : _this$context$slot,
_this$context$props = _this$context.props,
props = _this$context$props === void 0 ? {} : _this$context$props;
var passThroughProps = this.$props;
var propsWeControl = _extends({}, Object.entries(props).reduce(function (acc, _ref2) {
var _Object$assign;
var key = _ref2[0],
value = _ref2[1];
return Object.assign(acc, (_Object$assign = {}, _Object$assign[key] = unref(value), _Object$assign));
}, {}), {
id: this.id
});
return render({
props: _extends({}, passThroughProps, propsWeControl),
slot: slot.value,
attrs: this.$attrs,
slots: this.$slots,
name: name
});
},
setup: function setup() {
var context = useDescriptionContext();
var id = "headlessui-description-" + useId();
onMounted(function () {
return onUnmounted(context.register(id));
});
return {
id: id,
context: context
};
}
});
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 Context = /*#__PURE__*/Symbol('Context');
var State;
(function (State) {
State[State["Open"] = 0] = "Open";
State[State["Closed"] = 1] = "Closed";
})(State || (State = {}));
function hasOpenClosed() {
return useOpenClosed() !== null;
}
function useOpenClosed() {
return inject(Context, null);
}
function useOpenClosedProvider(value) {
provide(Context, value);
}
var DialogStates;
(function (DialogStates) {
DialogStates[DialogStates["Open"] = 0] = "Open";
DialogStates[DialogStates["Closed"] = 1] = "Closed";
})(DialogStates || (DialogStates = {}));
var DialogContext = /*#__PURE__*/Symbol('DialogContext');
function useDialogContext(component) {
var context = inject(DialogContext, null);
if (context === null) {
var err = new Error("<" + component + " /> is missing a parent <Dialog /> component.");
if (Error.captureStackTrace) Error.captureStackTrace(err, useDialogContext);
throw err;
}
return context;
} // ---
var Missing = 'DC8F892D-2EBD-447C-A4C8-A03058436FF4';
var Dialog = /*#__PURE__*/defineComponent({
name: 'Dialog',
inheritAttrs: false,
props: {
as: {
type: [Object, String],
"default": 'div'
},
"static": {
type: Boolean,
"default": false
},
unmount: {
type: Boolean,
"default": true
},
open: {
type: [Boolean, String],
"default": Missing
},
initialFocus: {
type: Object,
"default": null
}
},
emits: {
close: function close(_close) {
return true;
}
},
render: function render$1() {
var _this = this;
var propsWeControl = _extends({}, this.$attrs, {
ref: 'el',
id: this.id,
role: 'dialog',
'aria-modal': this.dialogState === DialogStates.Open ? true : undefined,
'aria-labelledby': this.titleId,
'aria-describedby': this.describedby,
onClick: this.handleClick
});
var _this$$props = this.$props,
passThroughProps = _objectWithoutPropertiesLoose(_this$$props, ["open", "initialFocus"]);
var slot = {
open: this.dialogState === DialogStates.Open
};
return h(ForcePortalRoot, {
force: true
}, function () {
return h(Portal, function () {
return h(PortalGroup, {
target: _this.dialogRef
}, function () {
return h(ForcePortalRoot, {
force: false
}, function () {
return render({
props: _extends({}, passThroughProps, propsWeControl),
slot: slot,
attrs: _this.$attrs,
slots: _this.$slots,
visible: _this.visible,
features: Features.RenderStrategy | Features.Static,
name: 'Dialog'
});
});
});
});
});
},
setup: function setup(props, _ref) {
var emit = _ref.emit;
var containers = ref(new Set());
var usesOpenClosedState = useOpenClosed();
var open = computed(function () {
if (props.open === Missing && usesOpenClosedState !== null) {
var _match;
// Update the `open` prop based on the open closed state
return match(usesOpenClosedState.value, (_match = {}, _match[State.Open] = true, _match[State.Closed] = false, _match));
}
return props.open;
}); // Validations
var hasOpen = props.open !== Missing || usesOpenClosedState !== null;
if (!hasOpen) {
throw new Error("You forgot to provide an `open` prop to the `Dialog`.");
}
if (typeof open.value !== 'boolean') {
throw new Error("You provided an `open` prop to the `Dialog`, but the value is not a boolean. Received: " + (open.value === Missing ? undefined : props.open));
}
var dialogState = computed(function () {
return props.open ? DialogStates.Open : DialogStates.Closed;
});
var visible = computed(function () {
if (usesOpenClosedState !== null) {
return usesOpenClosedState.value === State.Open;
}
return dialogState.value === DialogStates.Open;
});
var internalDialogRef = ref(null);
var enabled = ref(dialogState.value === DialogStates.Open);
onUpdated(function () {
enabled.value = dialogState.value === DialogStates.Open;
});
var id = "headlessui-dialog-" + useId();
var focusTrapOptions = computed(function () {
return {
initialFocus: props.initialFocus
};
});
useFocusTrap(containers, enabled, focusTrapOptions);
useInertOthers(internalDialogRef, enabled);
useStackProvider(function (message, element) {
var _match2;
return match(message, (_match2 = {}, _match2[StackMessage.AddElement] = function () {
containers.value.add(element);
}, _match2[StackMessage.RemoveElement] = function () {
containers.value["delete"](element);
}, _match2));
});
var describedby = useDescriptions({
name: 'DialogDescription',
slot: computed(function () {
return {
open: open.value
};
})
});
var titleId = ref(null);
var api = {
titleId: titleId,
dialogState: dialogState,
setTitleId: function setTitleId(id) {
if (titleId.value === id) return;
titleId.value = id;
},
close: function close() {
emit('close', false);
}
};
provide(DialogContext, api); // Handle outside click
useWindowEvent('mousedown', function (event) {
var target = event.target;
if (dialogState.value !== DialogStates.Open) return;
if (containers.value.size !== 1) return;
if (contains(containers.value, target)) return;
api.close();
nextTick(function () {
return target == null ? void 0 : target.focus();
});
}); // Handle `Escape` to close
useWindowEvent('keydown', function (event) {
if (event.key !== Keys.Escape) return;
if (dialogState.value !== DialogStates.Open) return;
if (containers.value.size > 1) return; // 1 is myself, otherwise other elements in the Stack
event.preventDefault();
event.stopPropagation();
api.close();
}); // Scroll lock
watchEffect(function (onInvalidate) {
if (dialogState.value !== DialogStates.Open) return;
var overflow = document.documentElement.style.overflow;
var paddingRight = document.documentElement.style.paddingRight;
var scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
document.documentElement.style.overflow = 'hidden';
document.documentElement.style.paddingRight = scrollbarWidth + "px";
onInvalidate(function () {
document.documentElement.style.overflow = overflow;
document.documentElement.style.paddingRight = paddingRight;
});
}); // Trigger close when the FocusTrap gets hidden
watchEffect(function (onInvalidate) {
if (dialogState.value !== DialogStates.Open) return;
var container = dom(internalDialogRef);
if (!container) return;
var observer = new IntersectionObserver(function (entries) {
for (var _iterator = _createForOfIteratorHelperLoose(entries), _step; !(_step = _iterator()).done;) {
var entry = _step.value;
if (entry.boundingClientRect.x === 0 && entry.boundingClientRect.y === 0 && entry.boundingClientRect.width === 0 && entry.boundingClientRect.height === 0) {
api.close();
}
}
});
observer.observe(container);
onInvalidate(function () {
return observer.disconnect();
});
});
return {
id: id,
el: internalDialogRef,
dialogRef: internalDialogRef,
containers: containers,
dialogState: dialogState,
titleId: titleId,
describedby: describedby,
visible: visible,
open: open,
handleClick: function handleClick(event) {
event.stopPropagation();
}
};
}
}); // ---
var DialogOverlay = /*#__PURE__*/defineComponent({
name: 'DialogOverlay',
props: {
as: {
type: [Object, String],
"default": 'div'
}
},
render: function render$1() {
var api = useDialogContext('DialogOverlay');
var propsWeControl = {
ref: 'el',
id: this.id,
'aria-hidden': true,
onClick: this.handleClick
};
var passThroughProps = this.$props;
return render({
props: _extends({}, passThroughProps, propsWeControl),
slot: {
open: api.dialogState.value === DialogStates.Open
},
attrs: this.$attrs,
slots: this.$slots,
name: 'DialogOverlay'
});
},
setup: function setup() {
var api = useDialogContext('DialogOverlay');
var id = "headlessui-dialog-overlay-" + useId();
return {
id: id,
handleClick: function handleClick(event) {
event.preventDefault();
event.stopPropagation();
api.close();
}
};
}
}); // ---
var DialogTitle = /*#__PURE__*/defineComponent({
name: 'DialogTitle',
props: {
as: {
type: [Object, String],
"default": 'h2'
}
},
render: function render$1() {
var api = useDialogContext('DialogTitle');
var propsWeControl = {
id: this.id
};
var passThroughProps = this.$props;
return render({
props: _extends({}, passThroughProps, propsWeControl),
slot: {
open: api.dialogState.value === DialogStates.Open
},
attrs: this.$attrs,
slots: this.$slots,
name: 'DialogTitle'
});
},
setup: function setup() {
var api = useDialogContext('DialogTitle');
var id = "headlessui-dialog-title-" + useId();
onMounted(function () {
api.setTitleId(id);
onUnmounted(function () {
return api.setTitleId(null);
});
});
return {
id: id
};
}
}); // ---
var DialogDescription = Description;
function resolveType(type, as) {
if (type) return type;
var tag = as != null ? as : 'button';
if (typeof tag === 'string' && tag.toLowerCase() === 'button') return 'button';
return undefined;
}
function useResolveButtonType(data, refElement) {
var type = ref(resolveType(data.value.type, data.value.as));
onMounted(function () {
type.value = resolveType(data.value.type, data.value.as);
});
watchEffect(function () {
var _dom;
if (type.value) return;
if (!dom(refElement)) return;
if (dom(refElement) instanceof HTMLButtonElement && !((_dom = dom(refElement)) == null ? void 0 : _dom.hasAttribute('type'))) {
type.value = 'button';
}
});
return type;
}
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 DisclosurePanelContext = /*#__PURE__*/Symbol('DisclosurePanelContext');
function useDisclosurePanelContext() {
return inject(DisclosurePanelContext, null);
} // ---
var Disclosure = /*#__PURE__*/defineComponent({
name: 'Disclosure',
props: {
as: {
type: [Object, String],
"default": 'template'
},
defaultOpen: {
type: [Boolean],
"default": false
}
},
setup: function setup(props, _ref) {
var slots = _ref.slots,
attrs = _ref.attrs;
var buttonId = "headlessui-disclosure-button-" + useId();
var panelId = "headlessui-disclosure-panel-" + useId();
var disclosureState = ref(props.defaultOpen ? DisclosureStates.Open : DisclosureStates.Closed);
var panelRef = ref(null);
var buttonRef = ref(null);
var api = {
buttonId: buttonId,
panelId: panelId,
disclosureState: disclosureState,
panel: panelRef,
button: buttonRef,
toggleDisclosure: function toggleDisclosure() {
var _match;
disclosureState.value = match(disclosureState.value, (_match = {}, _match[DisclosureStates.Open] = DisclosureStates.Closed, _match[DisclosureStates.Closed] = DisclosureStates.Open, _match));
},
closeDisclosure: function closeDisclosure() {
if (disclosureState.value === DisclosureStates.Closed) return;
disclosureState.value = DisclosureStates.Closed;
},
close: function close(focusableElement) {
api.closeDisclosure();
var restoreElement = function () {
if (!focusableElement) return dom(api.button);
if (focusableElement instanceof HTMLElement) return focusableElement;
if (focusableElement.value instanceof HTMLElement) return dom(focusableElement);
return dom(api.button);
}();
restoreElement == null ? void 0 : restoreElement.focus();
}
};
provide(DisclosureContext, api);
useOpenClosedProvider(computed(function () {
var _match2;
return match(disclosureState.value, (_match2 = {}, _match2[DisclosureStates.Open] = State.Open, _match2[DisclosureStates.Closed] = State.Closed, _match2));
}));
return function () {
var passThroughProps = _objectWithoutPropertiesLoose(props, ["defaultOpen"]);
var slot = {
open: disclosureState.value === DisclosureStates.Open,
close: api.close
};
return render({
props: passThroughProps,
slot: slot,
slots: slots,
attrs: attrs,
name: 'Disclosure'
});
};
}
}); // ---
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 = this.isWithinPanel ? {
ref: 'el',
type: this.type,
onClick: this.handleClick,
onKeydown: this.handleKeyDown
} : {
id: this.id,
ref: 'el',
type: this.type,
'aria-expanded': this.$props.disabled ? undefined : api.disclosureState.value === DisclosureStates.Open,
'aria-controls': dom(api.panel) ? api.panelId : undefined,
disabled: this.$props.disabled ? true : undefined,
onClick: this.handleClick,
onKeydown: this.handleKeyDown,
onKeyup: this.handleKeyUp
};
return render({
props: _extends({}, this.$props, propsWeControl),
slot: slot,
attrs: this.$attrs,
slots: this.$slots,
name: 'DisclosureButton'
});
},
setup: function setup(props, _ref2) {
var attrs = _ref2.attrs;
var api = useDisclosureContext('DisclosureButton');
var panelContext = useDisclosurePanelContext();
var isWithinPanel = panelContext === null ? false : panelContext === api.panelId;
var elementRef = ref(null);
if (!isWithinPanel) {
watchEffect(function () {
api.button.value = elementRef.value;
});
}
return {
isWithinPanel: isWithinPanel,
id: api.buttonId,
el: elementRef,
type: useResolveButtonType(computed(function () {
return {
as: props.as,
type: attrs.type
};
}), elementRef),
handleClick: function handleClick() {
if (props.disabled) return;
if (isWithinPanel) {
var _dom;
api.toggleDisclosure();
(_dom = dom(api.button)) == null ? void 0 : _dom.focus();
} else {
api.toggleDisclosure();
}
},
handleKeyDown: function handleKeyDown(event) {
var _dom2;
if (props.disabled) return;
if (isWithinPanel) {
switch (event.key) {
case Keys.Space:
case Keys.Enter:
event.preventDefault();
event.stopPropagation();
api.toggleDisclosure();
(_dom2 = dom(api.button)) == null ? void 0 : _dom2.focus();
break;
}
} else {
switch (event.key) {
case Keys.Space:
case Keys.Enter:
event.preventDefault();
event.stopPropagation();
api.toggleDisclosure();
break;
}
}
},
handleKeyUp: function handleKeyUp(event) {
switch (event.key) {
case Keys.Space:
// Required for firefox, event.preventDefault() in handleKeyDown for
// the Space key doesn't cancel the handleKeyUp, which in turn
// triggers a *click*.
event.preventDefault();
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,
close: api.close
};
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: this.visible,
name: 'DisclosurePanel'
});
},
setup: function setup() {
var api = useDisclosureContext('DisclosurePanel');
provide(DisclosurePanelContext, api.panelId);
var usesOpenClosedState = useOpenClosed();
var visible = computed(function () {
if (usesOpenClosedState !== null) {
return usesOpenClosedState.value === State.Open;
}
return api.disclosureState.value === DisclosureStates.Open;
});
return {
id: api.panelId,
el: api.panel,
visible: visible
};
}
});
var FocusTrap = /*#__PURE__*/defineComponent({
name: 'FocusTrap',
props: {
as: {
type: [Object, String],
"default": 'div'
},
initialFocus: {
type: Object,
"default": null
}
},
render: function render$1() {
var slot = {};
var propsWeControl = {
ref: 'el'
};
var _this$$props = this.$props,
passThroughProps = _objectWithoutPropertiesLoose(_this$$props, ["initialFocus"]);
return render({
props: _extends({}, passThroughProps, propsWeControl),
slot: slot,
attrs: this.$attrs,
slots: this.$slots,
name: 'FocusTrap'
});
},
setup: function setup(props) {
var containers = ref(new Set());
var container = ref(null);
var enabled = ref(true);
var focusTrapOptions = computed(function () {
return {
initialFocus: props.initialFocus
};
});
onMounted(function () {
if (!container.value) return;
containers.value.add(container.value);
useFocusTrap(containers, enabled, focusTrapOptions);
});
onUnmounted(function () {
enabled.value = false;
});
return {
el: container
};
}
});
function assertNever(x) {
throw new Error('Unexpected object: ' + x);
}
var Focus$1;
(function (Focus) {
/** Focus the first non-disabled item. */
Focus[Focus["First"] = 0] = "First";
/** Focus the previous non-disabled item. */
Focus[Focus["Previous"] = 1] = "Previous";
/** Focus the next non-disabled item. */
Focus[Focus["Next"] = 2] = "Next";
/** Focus the last non-disabled item. */
Focus[Focus["Last"] = 3] = "Last";
/** Focus a specific item based on the `id` of the item. */
Focus[Focus["Specific"] = 4] = "Specific";
/** Focus no items at all. */
Focus[Focus["Nothing"] = 5] = "Nothing";
})(Focus$1 || (Focus$1 = {}));
function calculateActiveIndex(action, resolvers) {
var items = resolvers.resolveItems();
if (items.length <= 0) return null;
var currentActiveIndex = resolvers.resolveActiveIndex();
var activeIndex = currentActiveIndex != null ? currentActiveIndex : -1;
var nextActiveIndex = function () {
switch (action.focus) {
case Focus$1.First:
return items.findIndex(function (item) {
return !resolvers.resolveDisabled(item);
});
case Focus$1.Previous:
{
var idx = items.slice().reverse().findIndex(function (item, idx, all) {
if (activeIndex !== -1 && all.length - idx - 1 >= activeIndex) return false;
return !resolvers.resolveDisabled(item);
});
if (idx === -1) return idx;
return items.length - 1 - idx;
}
case Focus$1.Next:
return items.findIndex(function (item, idx) {
if (idx <= activeIndex) return false;
return !resolvers.resolveDisabled(item);
});
case Focus$1.Last:
{
var _idx = items.slice().reverse().findIndex(function (item) {
return !resolvers.resolveDisabled(item);
});
if (_idx === -1) return _idx;
return items.length - 1 - _idx;
}
case Focus$1.Specific:
return items.findIndex(function (item) {
return resolvers.resolveId(item) === action.id;
});
case Focus$1.Nothing:
return null;
default:
assertNever(action);
}
}();
return nextActiveIndex === -1 ? currentActiveIndex : nextActiveIndex;
}
var ListboxStates;
(function (ListboxStates) {
ListboxStates[ListboxStates["Open"] = 0] = "Open";
ListboxStates[ListboxStates["Closed"] = 1] = "Closed";
})(ListboxStates || (ListboxStates = {}));
function nextFrame(cb) {
requestAnimationFrame(function () {
return