8023 lines
260 KiB
JavaScript
8023 lines
260 KiB
JavaScript
"use strict";
|
|
var __create = Object.create;
|
|
var __defProp = Object.defineProperty;
|
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
var __getProtoOf = Object.getPrototypeOf;
|
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
var __export = (target, all) => {
|
|
for (var name in all)
|
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
};
|
|
var __copyProps = (to, from, except, desc) => {
|
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
for (let key of __getOwnPropNames(from))
|
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
}
|
|
return to;
|
|
};
|
|
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
// If the importer is in node compatibility mode or this is not an ESM
|
|
// file that has been converted to a CommonJS file using a Babel-
|
|
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
mod
|
|
));
|
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
var __publicField = (obj, key, value) => {
|
|
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
return value;
|
|
};
|
|
|
|
// src/index.ts
|
|
var src_exports = {};
|
|
__export(src_exports, {
|
|
Combobox: () => Combobox,
|
|
ComboboxButton: () => ComboboxButton,
|
|
ComboboxInput: () => ComboboxInput,
|
|
ComboboxLabel: () => ComboboxLabel,
|
|
ComboboxOption: () => ComboboxOption,
|
|
ComboboxOptions: () => ComboboxOptions,
|
|
Dialog: () => Dialog,
|
|
DialogBackdrop: () => DialogBackdrop,
|
|
DialogDescription: () => DialogDescription,
|
|
DialogOverlay: () => DialogOverlay,
|
|
DialogPanel: () => DialogPanel,
|
|
DialogTitle: () => DialogTitle,
|
|
Disclosure: () => Disclosure,
|
|
DisclosureButton: () => DisclosureButton,
|
|
DisclosurePanel: () => DisclosurePanel,
|
|
FocusTrap: () => FocusTrap,
|
|
Listbox: () => Listbox,
|
|
ListboxButton: () => ListboxButton,
|
|
ListboxLabel: () => ListboxLabel,
|
|
ListboxOption: () => ListboxOption,
|
|
ListboxOptions: () => ListboxOptions,
|
|
Menu: () => Menu,
|
|
MenuButton: () => MenuButton,
|
|
MenuItem: () => MenuItem,
|
|
MenuItems: () => MenuItems,
|
|
Popover: () => Popover,
|
|
PopoverButton: () => PopoverButton,
|
|
PopoverGroup: () => PopoverGroup,
|
|
PopoverOverlay: () => PopoverOverlay,
|
|
PopoverPanel: () => PopoverPanel,
|
|
Portal: () => Portal,
|
|
PortalGroup: () => PortalGroup,
|
|
RadioGroup: () => RadioGroup,
|
|
RadioGroupDescription: () => RadioGroupDescription,
|
|
RadioGroupLabel: () => RadioGroupLabel,
|
|
RadioGroupOption: () => RadioGroupOption,
|
|
Switch: () => Switch,
|
|
SwitchDescription: () => SwitchDescription,
|
|
SwitchGroup: () => SwitchGroup,
|
|
SwitchLabel: () => SwitchLabel,
|
|
Tab: () => Tab,
|
|
TabGroup: () => TabGroup,
|
|
TabList: () => TabList,
|
|
TabPanel: () => TabPanel,
|
|
TabPanels: () => TabPanels,
|
|
TransitionChild: () => TransitionChild,
|
|
TransitionRoot: () => TransitionRoot,
|
|
provideUseId: () => provideUseId
|
|
});
|
|
module.exports = __toCommonJS(src_exports);
|
|
|
|
// ../../node_modules/@tanstack/vue-virtual/build/lib/_virtual/_rollupPluginBabelHelpers.mjs
|
|
function _extends() {
|
|
_extends = Object.assign ? Object.assign.bind() : 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);
|
|
}
|
|
|
|
// ../../node_modules/@tanstack/virtual-core/build/lib/_virtual/_rollupPluginBabelHelpers.mjs
|
|
function _extends2() {
|
|
_extends2 = Object.assign ? Object.assign.bind() : 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 _extends2.apply(this, arguments);
|
|
}
|
|
|
|
// ../../node_modules/@tanstack/virtual-core/build/lib/utils.mjs
|
|
function memo(getDeps, fn, opts) {
|
|
var _opts$initialDeps;
|
|
var deps = (_opts$initialDeps = opts.initialDeps) != null ? _opts$initialDeps : [];
|
|
var result;
|
|
return function() {
|
|
var depTime;
|
|
if (opts.key && opts.debug != null && opts.debug())
|
|
depTime = Date.now();
|
|
var newDeps = getDeps();
|
|
var depsChanged = newDeps.length !== deps.length || newDeps.some(function(dep, index) {
|
|
return deps[index] !== dep;
|
|
});
|
|
if (!depsChanged) {
|
|
return result;
|
|
}
|
|
deps = newDeps;
|
|
var resultTime;
|
|
if (opts.key && opts.debug != null && opts.debug())
|
|
resultTime = Date.now();
|
|
result = fn.apply(void 0, newDeps);
|
|
if (opts.key && opts.debug != null && opts.debug()) {
|
|
var depEndTime = Math.round((Date.now() - depTime) * 100) / 100;
|
|
var resultEndTime = Math.round((Date.now() - resultTime) * 100) / 100;
|
|
var resultFpsPercentage = resultEndTime / 16;
|
|
var pad = function pad2(str, num) {
|
|
str = String(str);
|
|
while (str.length < num) {
|
|
str = " " + str;
|
|
}
|
|
return str;
|
|
};
|
|
console.info("%c\u23F1 " + pad(resultEndTime, 5) + " /" + pad(depEndTime, 5) + " ms", "\n font-size: .6rem;\n font-weight: bold;\n color: hsl(" + Math.max(0, Math.min(120 - 120 * resultFpsPercentage, 120)) + "deg 100% 31%);", opts == null ? void 0 : opts.key);
|
|
}
|
|
opts == null ? void 0 : opts.onChange == null ? void 0 : opts.onChange(result);
|
|
return result;
|
|
};
|
|
}
|
|
function notUndefined(value, msg) {
|
|
if (value === void 0) {
|
|
throw new Error("Unexpected undefined" + (msg ? ": " + msg : ""));
|
|
} else {
|
|
return value;
|
|
}
|
|
}
|
|
var approxEqual = function approxEqual2(a, b) {
|
|
return Math.abs(a - b) < 1;
|
|
};
|
|
|
|
// ../../node_modules/@tanstack/virtual-core/build/lib/index.mjs
|
|
var defaultKeyExtractor = function defaultKeyExtractor2(index) {
|
|
return index;
|
|
};
|
|
var defaultRangeExtractor = function defaultRangeExtractor2(range) {
|
|
var start = Math.max(range.startIndex - range.overscan, 0);
|
|
var end = Math.min(range.endIndex + range.overscan, range.count - 1);
|
|
var arr = [];
|
|
for (var _i = start; _i <= end; _i++) {
|
|
arr.push(_i);
|
|
}
|
|
return arr;
|
|
};
|
|
var observeElementRect = function observeElementRect2(instance, cb) {
|
|
var element = instance.scrollElement;
|
|
if (!element) {
|
|
return;
|
|
}
|
|
var handler = function handler2(rect) {
|
|
var width = rect.width, height = rect.height;
|
|
cb({
|
|
width: Math.round(width),
|
|
height: Math.round(height)
|
|
});
|
|
};
|
|
handler(element.getBoundingClientRect());
|
|
var observer = new ResizeObserver(function(entries) {
|
|
var entry = entries[0];
|
|
if (entry != null && entry.borderBoxSize) {
|
|
var box = entry.borderBoxSize[0];
|
|
if (box) {
|
|
handler({
|
|
width: box.inlineSize,
|
|
height: box.blockSize
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
handler(element.getBoundingClientRect());
|
|
});
|
|
observer.observe(element, {
|
|
box: "border-box"
|
|
});
|
|
return function() {
|
|
observer.unobserve(element);
|
|
};
|
|
};
|
|
var observeElementOffset = function observeElementOffset2(instance, cb) {
|
|
var element = instance.scrollElement;
|
|
if (!element) {
|
|
return;
|
|
}
|
|
var handler = function handler2() {
|
|
cb(element[instance.options.horizontal ? "scrollLeft" : "scrollTop"]);
|
|
};
|
|
handler();
|
|
element.addEventListener("scroll", handler, {
|
|
passive: true
|
|
});
|
|
return function() {
|
|
element.removeEventListener("scroll", handler);
|
|
};
|
|
};
|
|
var measureElement = function measureElement2(element, entry, instance) {
|
|
if (entry != null && entry.borderBoxSize) {
|
|
var box = entry.borderBoxSize[0];
|
|
if (box) {
|
|
var size = Math.round(box[instance.options.horizontal ? "inlineSize" : "blockSize"]);
|
|
return size;
|
|
}
|
|
}
|
|
return Math.round(element.getBoundingClientRect()[instance.options.horizontal ? "width" : "height"]);
|
|
};
|
|
var elementScroll = function elementScroll2(offset, _ref2, instance) {
|
|
var _instance$scrollEleme3, _instance$scrollEleme4;
|
|
var _ref2$adjustments = _ref2.adjustments, adjustments = _ref2$adjustments === void 0 ? 0 : _ref2$adjustments, behavior = _ref2.behavior;
|
|
var toOffset = offset + adjustments;
|
|
(_instance$scrollEleme3 = instance.scrollElement) == null ? void 0 : _instance$scrollEleme3.scrollTo == null ? void 0 : _instance$scrollEleme3.scrollTo((_instance$scrollEleme4 = {}, _instance$scrollEleme4[instance.options.horizontal ? "left" : "top"] = toOffset, _instance$scrollEleme4.behavior = behavior, _instance$scrollEleme4));
|
|
};
|
|
var Virtualizer = function Virtualizer2(_opts) {
|
|
var _this = this;
|
|
this.unsubs = [];
|
|
this.scrollElement = null;
|
|
this.isScrolling = false;
|
|
this.isScrollingTimeoutId = null;
|
|
this.scrollToIndexTimeoutId = null;
|
|
this.measurementsCache = [];
|
|
this.itemSizeCache = /* @__PURE__ */ new Map();
|
|
this.pendingMeasuredCacheIndexes = [];
|
|
this.scrollDirection = null;
|
|
this.scrollAdjustments = 0;
|
|
this.measureElementCache = /* @__PURE__ */ new Map();
|
|
this.observer = function() {
|
|
var _ro = null;
|
|
var get = function get2() {
|
|
if (_ro) {
|
|
return _ro;
|
|
} else if (typeof ResizeObserver !== "undefined") {
|
|
return _ro = new ResizeObserver(function(entries) {
|
|
entries.forEach(function(entry) {
|
|
_this._measureElement(entry.target, entry);
|
|
});
|
|
});
|
|
} else {
|
|
return null;
|
|
}
|
|
};
|
|
return {
|
|
disconnect: function disconnect() {
|
|
var _get;
|
|
return (_get = get()) == null ? void 0 : _get.disconnect();
|
|
},
|
|
observe: function observe(target) {
|
|
var _get2;
|
|
return (_get2 = get()) == null ? void 0 : _get2.observe(target, {
|
|
box: "border-box"
|
|
});
|
|
},
|
|
unobserve: function unobserve(target) {
|
|
var _get3;
|
|
return (_get3 = get()) == null ? void 0 : _get3.unobserve(target);
|
|
}
|
|
};
|
|
}();
|
|
this.range = {
|
|
startIndex: 0,
|
|
endIndex: 0
|
|
};
|
|
this.setOptions = function(opts) {
|
|
Object.entries(opts).forEach(function(_ref3) {
|
|
var key = _ref3[0], value = _ref3[1];
|
|
if (typeof value === "undefined")
|
|
delete opts[key];
|
|
});
|
|
_this.options = _extends2({
|
|
debug: false,
|
|
initialOffset: 0,
|
|
overscan: 1,
|
|
paddingStart: 0,
|
|
paddingEnd: 0,
|
|
scrollPaddingStart: 0,
|
|
scrollPaddingEnd: 0,
|
|
horizontal: false,
|
|
getItemKey: defaultKeyExtractor,
|
|
rangeExtractor: defaultRangeExtractor,
|
|
onChange: function onChange() {
|
|
},
|
|
measureElement,
|
|
initialRect: {
|
|
width: 0,
|
|
height: 0
|
|
},
|
|
scrollMargin: 0,
|
|
scrollingDelay: 150,
|
|
indexAttribute: "data-index",
|
|
initialMeasurementsCache: [],
|
|
lanes: 1
|
|
}, opts);
|
|
};
|
|
this.notify = function() {
|
|
_this.options.onChange == null ? void 0 : _this.options.onChange(_this);
|
|
};
|
|
this.cleanup = function() {
|
|
_this.unsubs.filter(Boolean).forEach(function(d) {
|
|
return d();
|
|
});
|
|
_this.unsubs = [];
|
|
_this.scrollElement = null;
|
|
};
|
|
this._didMount = function() {
|
|
_this.measureElementCache.forEach(_this.observer.observe);
|
|
return function() {
|
|
_this.observer.disconnect();
|
|
_this.cleanup();
|
|
};
|
|
};
|
|
this._willUpdate = function() {
|
|
var scrollElement = _this.options.getScrollElement();
|
|
if (_this.scrollElement !== scrollElement) {
|
|
_this.cleanup();
|
|
_this.scrollElement = scrollElement;
|
|
_this._scrollToOffset(_this.scrollOffset, {
|
|
adjustments: void 0,
|
|
behavior: void 0
|
|
});
|
|
_this.unsubs.push(_this.options.observeElementRect(_this, function(rect) {
|
|
var prev = _this.scrollRect;
|
|
_this.scrollRect = rect;
|
|
if (_this.options.horizontal ? rect.width !== prev.width : rect.height !== prev.height) {
|
|
_this.maybeNotify();
|
|
}
|
|
}));
|
|
_this.unsubs.push(_this.options.observeElementOffset(_this, function(offset) {
|
|
_this.scrollAdjustments = 0;
|
|
if (_this.scrollOffset === offset) {
|
|
return;
|
|
}
|
|
if (_this.isScrollingTimeoutId !== null) {
|
|
clearTimeout(_this.isScrollingTimeoutId);
|
|
_this.isScrollingTimeoutId = null;
|
|
}
|
|
_this.isScrolling = true;
|
|
_this.scrollDirection = _this.scrollOffset < offset ? "forward" : "backward";
|
|
_this.scrollOffset = offset;
|
|
_this.maybeNotify();
|
|
_this.isScrollingTimeoutId = setTimeout(function() {
|
|
_this.isScrollingTimeoutId = null;
|
|
_this.isScrolling = false;
|
|
_this.scrollDirection = null;
|
|
_this.maybeNotify();
|
|
}, _this.options.scrollingDelay);
|
|
}));
|
|
}
|
|
};
|
|
this.getSize = function() {
|
|
return _this.scrollRect[_this.options.horizontal ? "width" : "height"];
|
|
};
|
|
this.memoOptions = memo(function() {
|
|
return [_this.options.count, _this.options.paddingStart, _this.options.scrollMargin, _this.options.getItemKey];
|
|
}, function(count, paddingStart, scrollMargin, getItemKey) {
|
|
_this.pendingMeasuredCacheIndexes = [];
|
|
return {
|
|
count,
|
|
paddingStart,
|
|
scrollMargin,
|
|
getItemKey
|
|
};
|
|
}, {
|
|
key: false
|
|
});
|
|
this.getFurthestMeasurement = function(measurements, index) {
|
|
var furthestMeasurementsFound = /* @__PURE__ */ new Map();
|
|
var furthestMeasurements = /* @__PURE__ */ new Map();
|
|
for (var m = index - 1; m >= 0; m--) {
|
|
var measurement = measurements[m];
|
|
if (furthestMeasurementsFound.has(measurement.lane)) {
|
|
continue;
|
|
}
|
|
var previousFurthestMeasurement = furthestMeasurements.get(measurement.lane);
|
|
if (previousFurthestMeasurement == null || measurement.end > previousFurthestMeasurement.end) {
|
|
furthestMeasurements.set(measurement.lane, measurement);
|
|
} else if (measurement.end < previousFurthestMeasurement.end) {
|
|
furthestMeasurementsFound.set(measurement.lane, true);
|
|
}
|
|
if (furthestMeasurementsFound.size === _this.options.lanes) {
|
|
break;
|
|
}
|
|
}
|
|
return furthestMeasurements.size === _this.options.lanes ? Array.from(furthestMeasurements.values()).sort(function(a, b) {
|
|
return a.end - b.end;
|
|
})[0] : void 0;
|
|
};
|
|
this.getMeasurements = memo(function() {
|
|
return [_this.memoOptions(), _this.itemSizeCache];
|
|
}, function(_ref4, itemSizeCache) {
|
|
var count = _ref4.count, paddingStart = _ref4.paddingStart, scrollMargin = _ref4.scrollMargin, getItemKey = _ref4.getItemKey;
|
|
var min = _this.pendingMeasuredCacheIndexes.length > 0 ? Math.min.apply(Math, _this.pendingMeasuredCacheIndexes) : 0;
|
|
_this.pendingMeasuredCacheIndexes = [];
|
|
var measurements = _this.measurementsCache.slice(0, min);
|
|
for (var _i2 = min; _i2 < count; _i2++) {
|
|
var key = getItemKey(_i2);
|
|
var furthestMeasurement = _this.options.lanes === 1 ? measurements[_i2 - 1] : _this.getFurthestMeasurement(measurements, _i2);
|
|
var start = furthestMeasurement ? furthestMeasurement.end : paddingStart + scrollMargin;
|
|
var measuredSize = itemSizeCache.get(key);
|
|
var size = typeof measuredSize === "number" ? measuredSize : _this.options.estimateSize(_i2);
|
|
var end = start + size;
|
|
var lane = furthestMeasurement ? furthestMeasurement.lane : _i2 % _this.options.lanes;
|
|
measurements[_i2] = {
|
|
index: _i2,
|
|
start,
|
|
size,
|
|
end,
|
|
key,
|
|
lane
|
|
};
|
|
}
|
|
_this.measurementsCache = measurements;
|
|
return measurements;
|
|
}, {
|
|
key: "getMeasurements",
|
|
debug: function debug() {
|
|
return _this.options.debug;
|
|
}
|
|
});
|
|
this.calculateRange = memo(function() {
|
|
return [_this.getMeasurements(), _this.getSize(), _this.scrollOffset];
|
|
}, function(measurements, outerSize, scrollOffset) {
|
|
return _this.range = calculateRange({
|
|
measurements,
|
|
outerSize,
|
|
scrollOffset
|
|
});
|
|
}, {
|
|
key: "calculateRange",
|
|
debug: function debug() {
|
|
return _this.options.debug;
|
|
}
|
|
});
|
|
this.maybeNotify = memo(function() {
|
|
var range = _this.calculateRange();
|
|
return [range.startIndex, range.endIndex, _this.isScrolling];
|
|
}, function() {
|
|
_this.notify();
|
|
}, {
|
|
key: "maybeNotify",
|
|
debug: function debug() {
|
|
return _this.options.debug;
|
|
},
|
|
initialDeps: [this.range.startIndex, this.range.endIndex, this.isScrolling]
|
|
});
|
|
this.getIndexes = memo(function() {
|
|
return [_this.options.rangeExtractor, _this.calculateRange(), _this.options.overscan, _this.options.count];
|
|
}, function(rangeExtractor, range, overscan, count) {
|
|
return rangeExtractor(_extends2({}, range, {
|
|
overscan,
|
|
count
|
|
}));
|
|
}, {
|
|
key: "getIndexes",
|
|
debug: function debug() {
|
|
return _this.options.debug;
|
|
}
|
|
});
|
|
this.indexFromElement = function(node) {
|
|
var attributeName = _this.options.indexAttribute;
|
|
var indexStr = node.getAttribute(attributeName);
|
|
if (!indexStr) {
|
|
console.warn("Missing attribute name '" + attributeName + "={index}' on measured element.");
|
|
return -1;
|
|
}
|
|
return parseInt(indexStr, 10);
|
|
};
|
|
this._measureElement = function(node, entry) {
|
|
var item = _this.measurementsCache[_this.indexFromElement(node)];
|
|
if (!item) {
|
|
_this.measureElementCache.forEach(function(cached, key) {
|
|
if (cached === node) {
|
|
_this.observer.unobserve(node);
|
|
_this.measureElementCache["delete"](key);
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
var prevNode = _this.measureElementCache.get(item.key);
|
|
if (!node.isConnected) {
|
|
if (prevNode) {
|
|
_this.observer.unobserve(prevNode);
|
|
_this.measureElementCache["delete"](item.key);
|
|
}
|
|
return;
|
|
}
|
|
if (prevNode !== node) {
|
|
if (prevNode) {
|
|
_this.observer.unobserve(prevNode);
|
|
}
|
|
_this.observer.observe(node);
|
|
_this.measureElementCache.set(item.key, node);
|
|
}
|
|
var measuredItemSize = _this.options.measureElement(node, entry, _this);
|
|
_this.resizeItem(item, measuredItemSize);
|
|
};
|
|
this.resizeItem = function(item, size) {
|
|
var _this$itemSizeCache$g;
|
|
var itemSize = (_this$itemSizeCache$g = _this.itemSizeCache.get(item.key)) != null ? _this$itemSizeCache$g : item.size;
|
|
var delta = size - itemSize;
|
|
if (delta !== 0) {
|
|
if (item.start < _this.scrollOffset) {
|
|
if (_this.options.debug) {
|
|
console.info("correction", delta);
|
|
}
|
|
_this._scrollToOffset(_this.scrollOffset, {
|
|
adjustments: _this.scrollAdjustments += delta,
|
|
behavior: void 0
|
|
});
|
|
}
|
|
_this.pendingMeasuredCacheIndexes.push(item.index);
|
|
_this.itemSizeCache = new Map(_this.itemSizeCache.set(item.key, size));
|
|
_this.notify();
|
|
}
|
|
};
|
|
this.measureElement = function(node) {
|
|
if (!node) {
|
|
return;
|
|
}
|
|
_this._measureElement(node, void 0);
|
|
};
|
|
this.getVirtualItems = memo(function() {
|
|
return [_this.getIndexes(), _this.getMeasurements()];
|
|
}, function(indexes, measurements) {
|
|
var virtualItems = [];
|
|
for (var k = 0, len = indexes.length; k < len; k++) {
|
|
var _i3 = indexes[k];
|
|
var measurement = measurements[_i3];
|
|
virtualItems.push(measurement);
|
|
}
|
|
return virtualItems;
|
|
}, {
|
|
key: "getIndexes",
|
|
debug: function debug() {
|
|
return _this.options.debug;
|
|
}
|
|
});
|
|
this.getVirtualItemForOffset = function(offset) {
|
|
var measurements = _this.getMeasurements();
|
|
return notUndefined(measurements[findNearestBinarySearch(0, measurements.length - 1, function(index) {
|
|
return notUndefined(measurements[index]).start;
|
|
}, offset)]);
|
|
};
|
|
this.getOffsetForAlignment = function(toOffset, align) {
|
|
var size = _this.getSize();
|
|
if (align === "auto") {
|
|
if (toOffset <= _this.scrollOffset) {
|
|
align = "start";
|
|
} else if (toOffset >= _this.scrollOffset + size) {
|
|
align = "end";
|
|
} else {
|
|
align = "start";
|
|
}
|
|
}
|
|
if (align === "start") {
|
|
toOffset = toOffset;
|
|
} else if (align === "end") {
|
|
toOffset = toOffset - size;
|
|
} else if (align === "center") {
|
|
toOffset = toOffset - size / 2;
|
|
}
|
|
var scrollSizeProp = _this.options.horizontal ? "scrollWidth" : "scrollHeight";
|
|
var scrollSize = _this.scrollElement ? "document" in _this.scrollElement ? _this.scrollElement.document.documentElement[scrollSizeProp] : _this.scrollElement[scrollSizeProp] : 0;
|
|
var maxOffset = scrollSize - _this.getSize();
|
|
return Math.max(Math.min(maxOffset, toOffset), 0);
|
|
};
|
|
this.getOffsetForIndex = function(index, align) {
|
|
if (align === void 0) {
|
|
align = "auto";
|
|
}
|
|
index = Math.max(0, Math.min(index, _this.options.count - 1));
|
|
var measurement = notUndefined(_this.getMeasurements()[index]);
|
|
if (align === "auto") {
|
|
if (measurement.end >= _this.scrollOffset + _this.getSize() - _this.options.scrollPaddingEnd) {
|
|
align = "end";
|
|
} else if (measurement.start <= _this.scrollOffset + _this.options.scrollPaddingStart) {
|
|
align = "start";
|
|
} else {
|
|
return [_this.scrollOffset, align];
|
|
}
|
|
}
|
|
var toOffset = align === "end" ? measurement.end + _this.options.scrollPaddingEnd : measurement.start - _this.options.scrollPaddingStart;
|
|
return [_this.getOffsetForAlignment(toOffset, align), align];
|
|
};
|
|
this.isDynamicMode = function() {
|
|
return _this.measureElementCache.size > 0;
|
|
};
|
|
this.cancelScrollToIndex = function() {
|
|
if (_this.scrollToIndexTimeoutId !== null) {
|
|
clearTimeout(_this.scrollToIndexTimeoutId);
|
|
_this.scrollToIndexTimeoutId = null;
|
|
}
|
|
};
|
|
this.scrollToOffset = function(toOffset, _temp) {
|
|
var _ref5 = _temp === void 0 ? {} : _temp, _ref5$align = _ref5.align, align = _ref5$align === void 0 ? "start" : _ref5$align, behavior = _ref5.behavior;
|
|
_this.cancelScrollToIndex();
|
|
if (behavior === "smooth" && _this.isDynamicMode()) {
|
|
console.warn("The `smooth` scroll behavior is not fully supported with dynamic size.");
|
|
}
|
|
_this._scrollToOffset(_this.getOffsetForAlignment(toOffset, align), {
|
|
adjustments: void 0,
|
|
behavior
|
|
});
|
|
};
|
|
this.scrollToIndex = function(index, _temp2) {
|
|
var _ref6 = _temp2 === void 0 ? {} : _temp2, _ref6$align = _ref6.align, initialAlign = _ref6$align === void 0 ? "auto" : _ref6$align, behavior = _ref6.behavior;
|
|
index = Math.max(0, Math.min(index, _this.options.count - 1));
|
|
_this.cancelScrollToIndex();
|
|
if (behavior === "smooth" && _this.isDynamicMode()) {
|
|
console.warn("The `smooth` scroll behavior is not fully supported with dynamic size.");
|
|
}
|
|
var _this$getOffsetForInd = _this.getOffsetForIndex(index, initialAlign), toOffset = _this$getOffsetForInd[0], align = _this$getOffsetForInd[1];
|
|
_this._scrollToOffset(toOffset, {
|
|
adjustments: void 0,
|
|
behavior
|
|
});
|
|
if (behavior !== "smooth" && _this.isDynamicMode()) {
|
|
_this.scrollToIndexTimeoutId = setTimeout(function() {
|
|
_this.scrollToIndexTimeoutId = null;
|
|
var elementInDOM = _this.measureElementCache.has(_this.options.getItemKey(index));
|
|
if (elementInDOM) {
|
|
var _this$getOffsetForInd2 = _this.getOffsetForIndex(index, align), _toOffset = _this$getOffsetForInd2[0];
|
|
if (!approxEqual(_toOffset, _this.scrollOffset)) {
|
|
_this.scrollToIndex(index, {
|
|
align,
|
|
behavior
|
|
});
|
|
}
|
|
} else {
|
|
_this.scrollToIndex(index, {
|
|
align,
|
|
behavior
|
|
});
|
|
}
|
|
});
|
|
}
|
|
};
|
|
this.scrollBy = function(delta, _temp3) {
|
|
var _ref7 = _temp3 === void 0 ? {} : _temp3, behavior = _ref7.behavior;
|
|
_this.cancelScrollToIndex();
|
|
if (behavior === "smooth" && _this.isDynamicMode()) {
|
|
console.warn("The `smooth` scroll behavior is not fully supported with dynamic size.");
|
|
}
|
|
_this._scrollToOffset(_this.scrollOffset + delta, {
|
|
adjustments: void 0,
|
|
behavior
|
|
});
|
|
};
|
|
this.getTotalSize = function() {
|
|
var _this$getMeasurements;
|
|
return (((_this$getMeasurements = _this.getMeasurements()[_this.options.count - 1]) == null ? void 0 : _this$getMeasurements.end) || _this.options.paddingStart) - _this.options.scrollMargin + _this.options.paddingEnd;
|
|
};
|
|
this._scrollToOffset = function(offset, _ref8) {
|
|
var adjustments = _ref8.adjustments, behavior = _ref8.behavior;
|
|
_this.options.scrollToFn(offset, {
|
|
behavior,
|
|
adjustments
|
|
}, _this);
|
|
};
|
|
this.measure = function() {
|
|
_this.itemSizeCache = /* @__PURE__ */ new Map();
|
|
_this.notify();
|
|
};
|
|
this.setOptions(_opts);
|
|
this.scrollRect = this.options.initialRect;
|
|
this.scrollOffset = this.options.initialOffset;
|
|
this.measurementsCache = this.options.initialMeasurementsCache;
|
|
this.measurementsCache.forEach(function(item) {
|
|
_this.itemSizeCache.set(item.key, item.size);
|
|
});
|
|
this.maybeNotify();
|
|
};
|
|
var findNearestBinarySearch = function findNearestBinarySearch2(low, high, getCurrentValue, value) {
|
|
while (low <= high) {
|
|
var middle = (low + high) / 2 | 0;
|
|
var currentValue = getCurrentValue(middle);
|
|
if (currentValue < value) {
|
|
low = middle + 1;
|
|
} else if (currentValue > value) {
|
|
high = middle - 1;
|
|
} else {
|
|
return middle;
|
|
}
|
|
}
|
|
if (low > 0) {
|
|
return low - 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
};
|
|
function calculateRange(_ref9) {
|
|
var measurements = _ref9.measurements, outerSize = _ref9.outerSize, scrollOffset = _ref9.scrollOffset;
|
|
var count = measurements.length - 1;
|
|
var getOffset = function getOffset2(index) {
|
|
return measurements[index].start;
|
|
};
|
|
var startIndex = findNearestBinarySearch(0, count, getOffset, scrollOffset);
|
|
var endIndex = startIndex;
|
|
while (endIndex < count && measurements[endIndex].end < scrollOffset + outerSize) {
|
|
endIndex++;
|
|
}
|
|
return {
|
|
startIndex,
|
|
endIndex
|
|
};
|
|
}
|
|
|
|
// ../../node_modules/@tanstack/vue-virtual/build/lib/index.mjs
|
|
var import_vue = require("vue");
|
|
function useVirtualizerBase(options) {
|
|
var virtualizer = new Virtualizer((0, import_vue.unref)(options));
|
|
var state = (0, import_vue.shallowRef)(virtualizer);
|
|
var cleanup = virtualizer._didMount();
|
|
(0, import_vue.watch)(function() {
|
|
return (0, import_vue.unref)(options).getScrollElement();
|
|
}, function(el) {
|
|
if (el) {
|
|
virtualizer._willUpdate();
|
|
}
|
|
}, {
|
|
immediate: true
|
|
});
|
|
(0, import_vue.watch)(function() {
|
|
return (0, import_vue.unref)(options);
|
|
}, function(options2) {
|
|
virtualizer.setOptions(_extends({}, options2, {
|
|
onChange: function onChange(instance) {
|
|
(0, import_vue.triggerRef)(state);
|
|
options2.onChange == null ? void 0 : options2.onChange(instance);
|
|
}
|
|
}));
|
|
virtualizer._willUpdate();
|
|
(0, import_vue.triggerRef)(state);
|
|
}, {
|
|
immediate: true
|
|
});
|
|
(0, import_vue.onScopeDispose)(cleanup);
|
|
return state;
|
|
}
|
|
function useVirtualizer(options) {
|
|
return useVirtualizerBase((0, import_vue.computed)(function() {
|
|
return _extends({
|
|
observeElementRect,
|
|
observeElementOffset,
|
|
scrollToFn: elementScroll
|
|
}, (0, import_vue.unref)(options));
|
|
}));
|
|
}
|
|
|
|
// src/components/combobox/combobox.ts
|
|
var import_vue14 = require("vue");
|
|
|
|
// src/hooks/use-controllable.ts
|
|
var import_vue2 = require("vue");
|
|
function useControllable(controlledValue, onChange, defaultValue) {
|
|
let internalValue = (0, import_vue2.ref)(defaultValue == null ? void 0 : defaultValue.value);
|
|
let isControlled = (0, import_vue2.computed)(() => controlledValue.value !== void 0);
|
|
return [
|
|
(0, import_vue2.computed)(() => isControlled.value ? controlledValue.value : internalValue.value),
|
|
function(value) {
|
|
if (isControlled.value) {
|
|
return onChange == null ? void 0 : onChange(value);
|
|
} else {
|
|
internalValue.value = value;
|
|
return onChange == null ? void 0 : onChange(value);
|
|
}
|
|
}
|
|
];
|
|
}
|
|
|
|
// src/hooks/use-disposables.ts
|
|
var import_vue3 = require("vue");
|
|
|
|
// src/utils/micro-task.ts
|
|
function microTask(cb) {
|
|
if (typeof queueMicrotask === "function") {
|
|
queueMicrotask(cb);
|
|
} else {
|
|
Promise.resolve().then(cb).catch(
|
|
(e) => setTimeout(() => {
|
|
throw e;
|
|
})
|
|
);
|
|
}
|
|
}
|
|
|
|
// src/utils/disposables.ts
|
|
function disposables() {
|
|
let _disposables = [];
|
|
let api = {
|
|
addEventListener(element, name, listener, options) {
|
|
element.addEventListener(name, listener, options);
|
|
return api.add(() => element.removeEventListener(name, listener, options));
|
|
},
|
|
requestAnimationFrame(...args) {
|
|
let raf = requestAnimationFrame(...args);
|
|
api.add(() => cancelAnimationFrame(raf));
|
|
},
|
|
nextFrame(...args) {
|
|
api.requestAnimationFrame(() => {
|
|
api.requestAnimationFrame(...args);
|
|
});
|
|
},
|
|
setTimeout(...args) {
|
|
let timer = setTimeout(...args);
|
|
api.add(() => clearTimeout(timer));
|
|
},
|
|
microTask(...args) {
|
|
let task = { current: true };
|
|
microTask(() => {
|
|
if (task.current) {
|
|
args[0]();
|
|
}
|
|
});
|
|
return api.add(() => {
|
|
task.current = false;
|
|
});
|
|
},
|
|
style(node, property, value) {
|
|
let previous = node.style.getPropertyValue(property);
|
|
Object.assign(node.style, { [property]: value });
|
|
return this.add(() => {
|
|
Object.assign(node.style, { [property]: previous });
|
|
});
|
|
},
|
|
group(cb) {
|
|
let d = disposables();
|
|
cb(d);
|
|
return this.add(() => d.dispose());
|
|
},
|
|
add(cb) {
|
|
_disposables.push(cb);
|
|
return () => {
|
|
let idx = _disposables.indexOf(cb);
|
|
if (idx >= 0) {
|
|
for (let dispose of _disposables.splice(idx, 1)) {
|
|
dispose();
|
|
}
|
|
}
|
|
};
|
|
},
|
|
dispose() {
|
|
for (let dispose of _disposables.splice(0)) {
|
|
dispose();
|
|
}
|
|
}
|
|
};
|
|
return api;
|
|
}
|
|
|
|
// src/hooks/use-disposables.ts
|
|
function useDisposables() {
|
|
let d = disposables();
|
|
(0, import_vue3.onUnmounted)(() => d.dispose());
|
|
return d;
|
|
}
|
|
|
|
// src/hooks/use-frame-debounce.ts
|
|
function useFrameDebounce() {
|
|
let d = useDisposables();
|
|
return (cb) => {
|
|
d.dispose();
|
|
d.nextFrame(cb);
|
|
};
|
|
}
|
|
|
|
// src/hooks/use-id.ts
|
|
var Vue = __toESM(require("vue"), 1);
|
|
var GENERATE_ID = Symbol("headlessui.useid");
|
|
var globalId = 0;
|
|
var _a;
|
|
var useId2 = (
|
|
// Prefer Vue's `useId` if it's available.
|
|
// @ts-expect-error - `useId` doesn't exist in Vue < 3.5.
|
|
(_a = Vue.useId) != null ? _a : function useId3() {
|
|
let generateId = Vue.inject(GENERATE_ID, () => {
|
|
return `${++globalId}`;
|
|
});
|
|
return generateId();
|
|
}
|
|
);
|
|
function provideUseId(fn) {
|
|
Vue.provide(GENERATE_ID, fn);
|
|
}
|
|
|
|
// src/hooks/use-outside-click.ts
|
|
var import_vue7 = require("vue");
|
|
|
|
// src/utils/dom.ts
|
|
function dom(ref24) {
|
|
var _a2;
|
|
if (ref24 == null)
|
|
return null;
|
|
if (ref24.value == null)
|
|
return null;
|
|
let el = (_a2 = ref24.value.$el) != null ? _a2 : ref24.value;
|
|
if (el instanceof Node) {
|
|
return el;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// src/utils/focus-management.ts
|
|
var import_vue4 = require("vue");
|
|
|
|
// src/utils/match.ts
|
|
function match(value, lookup, ...args) {
|
|
if (value in lookup) {
|
|
let returnValue = lookup[value];
|
|
return typeof returnValue === "function" ? returnValue(...args) : returnValue;
|
|
}
|
|
let error = new Error(
|
|
`Tried to handle "${value}" but there is no handler defined. Only defined handlers are: ${Object.keys(
|
|
lookup
|
|
).map((key) => `"${key}"`).join(", ")}.`
|
|
);
|
|
if (Error.captureStackTrace)
|
|
Error.captureStackTrace(error, match);
|
|
throw error;
|
|
}
|
|
|
|
// src/utils/env.ts
|
|
var Env = class {
|
|
constructor() {
|
|
__publicField(this, "current", this.detect());
|
|
__publicField(this, "currentId", 0);
|
|
}
|
|
set(env2) {
|
|
if (this.current === env2)
|
|
return;
|
|
this.currentId = 0;
|
|
this.current = env2;
|
|
}
|
|
reset() {
|
|
this.set(this.detect());
|
|
}
|
|
nextId() {
|
|
return ++this.currentId;
|
|
}
|
|
get isServer() {
|
|
return this.current === "server";
|
|
}
|
|
get isClient() {
|
|
return this.current === "client";
|
|
}
|
|
detect() {
|
|
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
return "server";
|
|
}
|
|
return "client";
|
|
}
|
|
};
|
|
var env = new Env();
|
|
|
|
// src/utils/owner.ts
|
|
function getOwnerDocument(element) {
|
|
if (env.isServer)
|
|
return null;
|
|
if (element instanceof Node)
|
|
return element.ownerDocument;
|
|
if (element == null ? void 0 : element.hasOwnProperty("value")) {
|
|
let domElement = dom(element);
|
|
if (domElement)
|
|
return domElement.ownerDocument;
|
|
}
|
|
return document;
|
|
}
|
|
|
|
// src/utils/focus-management.ts
|
|
var focusableSelector = [
|
|
"[contentEditable=true]",
|
|
"[tabindex]",
|
|
"a[href]",
|
|
"area[href]",
|
|
"button:not([disabled])",
|
|
"iframe",
|
|
"input:not([disabled])",
|
|
"select:not([disabled])",
|
|
"textarea:not([disabled])"
|
|
].map(
|
|
false ? (
|
|
// 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.
|
|
(selector) => `${selector}:not([tabindex='-1']):not([style*='display: none'])`
|
|
) : (selector) => `${selector}:not([tabindex='-1'])`
|
|
).join(",");
|
|
function getFocusableElements(container = document.body) {
|
|
if (container == null)
|
|
return [];
|
|
return Array.from(container.querySelectorAll(focusableSelector)).sort(
|
|
// We want to move `:tabindex="0"` to the end of the list, this is what the browser does as well.
|
|
(a, z) => Math.sign((a.tabIndex || Number.MAX_SAFE_INTEGER) - (z.tabIndex || Number.MAX_SAFE_INTEGER))
|
|
);
|
|
}
|
|
function isFocusableElement(element, mode = 0 /* Strict */) {
|
|
var _a2;
|
|
if (element === ((_a2 = getOwnerDocument(element)) == null ? void 0 : _a2.body))
|
|
return false;
|
|
return match(mode, {
|
|
[0 /* Strict */]() {
|
|
return element.matches(focusableSelector);
|
|
},
|
|
[1 /* Loose */]() {
|
|
let next = element;
|
|
while (next !== null) {
|
|
if (next.matches(focusableSelector))
|
|
return true;
|
|
next = next.parentElement;
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
}
|
|
function restoreFocusIfNecessary(element) {
|
|
let ownerDocument = getOwnerDocument(element);
|
|
(0, import_vue4.nextTick)(() => {
|
|
if (ownerDocument && !isFocusableElement(ownerDocument.activeElement, 0 /* Strict */)) {
|
|
focusElement(element);
|
|
}
|
|
});
|
|
}
|
|
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
document.addEventListener(
|
|
"keydown",
|
|
(event) => {
|
|
if (event.metaKey || event.altKey || event.ctrlKey) {
|
|
return;
|
|
}
|
|
document.documentElement.dataset.headlessuiFocusVisible = "";
|
|
},
|
|
true
|
|
);
|
|
document.addEventListener(
|
|
"click",
|
|
(event) => {
|
|
if (event.detail === 1 /* Mouse */) {
|
|
delete document.documentElement.dataset.headlessuiFocusVisible;
|
|
} else if (event.detail === 0 /* Keyboard */) {
|
|
document.documentElement.dataset.headlessuiFocusVisible = "";
|
|
}
|
|
},
|
|
true
|
|
);
|
|
}
|
|
function focusElement(element) {
|
|
element == null ? void 0 : element.focus({ preventScroll: true });
|
|
}
|
|
var selectableSelector = ["textarea", "input"].join(",");
|
|
function isSelectableElement(element) {
|
|
var _a2, _b;
|
|
return (_b = (_a2 = element == null ? void 0 : element.matches) == null ? void 0 : _a2.call(element, selectableSelector)) != null ? _b : false;
|
|
}
|
|
function sortByDomNode(nodes, resolveKey = (i) => i) {
|
|
return nodes.slice().sort((aItem, zItem) => {
|
|
let a = resolveKey(aItem);
|
|
let z = resolveKey(zItem);
|
|
if (a === null || z === null)
|
|
return 0;
|
|
let position = a.compareDocumentPosition(z);
|
|
if (position & Node.DOCUMENT_POSITION_FOLLOWING)
|
|
return -1;
|
|
if (position & Node.DOCUMENT_POSITION_PRECEDING)
|
|
return 1;
|
|
return 0;
|
|
});
|
|
}
|
|
function focusFrom(current, focus) {
|
|
return focusIn(getFocusableElements(), focus, { relativeTo: current });
|
|
}
|
|
function focusIn(container, focus, {
|
|
sorted = true,
|
|
relativeTo = null,
|
|
skipElements = []
|
|
} = {}) {
|
|
var _a2;
|
|
let ownerDocument = (_a2 = Array.isArray(container) ? container.length > 0 ? container[0].ownerDocument : document : container == null ? void 0 : container.ownerDocument) != null ? _a2 : document;
|
|
let elements = Array.isArray(container) ? sorted ? sortByDomNode(container) : container : getFocusableElements(container);
|
|
if (skipElements.length > 0 && elements.length > 1) {
|
|
elements = elements.filter((x) => !skipElements.includes(x));
|
|
}
|
|
relativeTo = relativeTo != null ? relativeTo : ownerDocument.activeElement;
|
|
let direction = (() => {
|
|
if (focus & (1 /* First */ | 4 /* Next */))
|
|
return 1 /* Next */;
|
|
if (focus & (2 /* Previous */ | 8 /* Last */))
|
|
return -1 /* Previous */;
|
|
throw new Error("Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last");
|
|
})();
|
|
let startIndex = (() => {
|
|
if (focus & 1 /* First */)
|
|
return 0;
|
|
if (focus & 2 /* Previous */)
|
|
return Math.max(0, elements.indexOf(relativeTo)) - 1;
|
|
if (focus & 4 /* Next */)
|
|
return Math.max(0, elements.indexOf(relativeTo)) + 1;
|
|
if (focus & 8 /* Last */)
|
|
return elements.length - 1;
|
|
throw new Error("Missing Focus.First, Focus.Previous, Focus.Next or Focus.Last");
|
|
})();
|
|
let focusOptions = focus & 32 /* NoScroll */ ? { preventScroll: true } : {};
|
|
let offset = 0;
|
|
let total = elements.length;
|
|
let next = void 0;
|
|
do {
|
|
if (offset >= total || offset + total <= 0)
|
|
return 0 /* Error */;
|
|
let nextIdx = startIndex + offset;
|
|
if (focus & 16 /* WrapAround */) {
|
|
nextIdx = (nextIdx + total) % total;
|
|
} else {
|
|
if (nextIdx < 0)
|
|
return 3 /* Underflow */;
|
|
if (nextIdx >= total)
|
|
return 1 /* Overflow */;
|
|
}
|
|
next = elements[nextIdx];
|
|
next == null ? void 0 : next.focus(focusOptions);
|
|
offset += direction;
|
|
} while (next !== ownerDocument.activeElement);
|
|
if (focus & (4 /* Next */ | 2 /* Previous */) && isSelectableElement(next)) {
|
|
next.select();
|
|
}
|
|
return 2 /* Success */;
|
|
}
|
|
|
|
// src/utils/platform.ts
|
|
function isIOS() {
|
|
return (
|
|
// Check if it is an iPhone
|
|
/iPhone/gi.test(window.navigator.platform) || // Check if it is an iPad. iPad reports itself as "MacIntel", but we can check if it is a touch
|
|
// screen. Let's hope that Apple doesn't release a touch screen Mac (or maybe this would then
|
|
// work as expected 🤔).
|
|
/Mac/gi.test(window.navigator.platform) && window.navigator.maxTouchPoints > 0
|
|
);
|
|
}
|
|
function isAndroid() {
|
|
return /Android/gi.test(window.navigator.userAgent);
|
|
}
|
|
function isMobile() {
|
|
return isIOS() || isAndroid();
|
|
}
|
|
|
|
// src/hooks/use-document-event.ts
|
|
var import_vue5 = require("vue");
|
|
function useDocumentEvent(type, listener, options) {
|
|
if (env.isServer)
|
|
return;
|
|
(0, import_vue5.watchEffect)((onInvalidate) => {
|
|
document.addEventListener(type, listener, options);
|
|
onInvalidate(() => document.removeEventListener(type, listener, options));
|
|
});
|
|
}
|
|
|
|
// src/hooks/use-window-event.ts
|
|
var import_vue6 = require("vue");
|
|
function useWindowEvent(type, listener, options) {
|
|
if (env.isServer)
|
|
return;
|
|
(0, import_vue6.watchEffect)((onInvalidate) => {
|
|
window.addEventListener(type, listener, options);
|
|
onInvalidate(() => window.removeEventListener(type, listener, options));
|
|
});
|
|
}
|
|
|
|
// src/hooks/use-outside-click.ts
|
|
function useOutsideClick(containers, cb, enabled = (0, import_vue7.computed)(() => true)) {
|
|
function handleOutsideClick(event, resolveTarget) {
|
|
if (!enabled.value)
|
|
return;
|
|
if (event.defaultPrevented)
|
|
return;
|
|
let target = resolveTarget(event);
|
|
if (target === null) {
|
|
return;
|
|
}
|
|
if (!target.getRootNode().contains(target))
|
|
return;
|
|
let _containers = function resolve(containers2) {
|
|
if (typeof containers2 === "function") {
|
|
return resolve(containers2());
|
|
}
|
|
if (Array.isArray(containers2)) {
|
|
return containers2;
|
|
}
|
|
if (containers2 instanceof Set) {
|
|
return containers2;
|
|
}
|
|
return [containers2];
|
|
}(containers);
|
|
for (let container of _containers) {
|
|
if (container === null)
|
|
continue;
|
|
let domNode = container instanceof HTMLElement ? container : dom(container);
|
|
if (domNode == null ? void 0 : domNode.contains(target)) {
|
|
return;
|
|
}
|
|
if (event.composed && event.composedPath().includes(domNode)) {
|
|
return;
|
|
}
|
|
}
|
|
if (
|
|
// This check alllows us to know whether or not we clicked on a "focusable" element like a
|
|
// button or an input. This is a backwards compatibility check so that you can open a <Menu
|
|
// /> and click on another <Menu /> which should close Menu A and open Menu B. We might
|
|
// revisit that so that you will require 2 clicks instead.
|
|
!isFocusableElement(target, 1 /* Loose */) && // This could be improved, but the `Combobox.Button` adds tabIndex={-1} to make it
|
|
// unfocusable via the keyboard so that tabbing to the next item from the input doesn't
|
|
// first go to the button.
|
|
target.tabIndex !== -1
|
|
) {
|
|
event.preventDefault();
|
|
}
|
|
return cb(event, target);
|
|
}
|
|
let initialClickTarget = (0, import_vue7.ref)(null);
|
|
useDocumentEvent(
|
|
"pointerdown",
|
|
(event) => {
|
|
var _a2, _b;
|
|
if (enabled.value) {
|
|
initialClickTarget.value = ((_b = (_a2 = event.composedPath) == null ? void 0 : _a2.call(event)) == null ? void 0 : _b[0]) || event.target;
|
|
}
|
|
},
|
|
true
|
|
);
|
|
useDocumentEvent(
|
|
"mousedown",
|
|
(event) => {
|
|
var _a2, _b;
|
|
if (enabled.value) {
|
|
initialClickTarget.value = ((_b = (_a2 = event.composedPath) == null ? void 0 : _a2.call(event)) == null ? void 0 : _b[0]) || event.target;
|
|
}
|
|
},
|
|
true
|
|
);
|
|
useDocumentEvent(
|
|
"click",
|
|
(event) => {
|
|
if (isMobile()) {
|
|
return;
|
|
}
|
|
if (!initialClickTarget.value) {
|
|
return;
|
|
}
|
|
handleOutsideClick(event, () => {
|
|
return initialClickTarget.value;
|
|
});
|
|
initialClickTarget.value = null;
|
|
},
|
|
// We will use the `capture` phase so that layers in between with `event.stopPropagation()`
|
|
// don't "cancel" this outside click check. E.g.: A `Menu` inside a `DialogPanel` if the `Menu`
|
|
// is open, and you click outside of it in the `DialogPanel` the `Menu` should close. However,
|
|
// the `DialogPanel` has a `onClick(e) { e.stopPropagation() }` which would cancel this.
|
|
true
|
|
);
|
|
useDocumentEvent(
|
|
"touchend",
|
|
(event) => {
|
|
return handleOutsideClick(event, () => {
|
|
if (event.target instanceof HTMLElement) {
|
|
return event.target;
|
|
}
|
|
return null;
|
|
});
|
|
},
|
|
// We will use the `capture` phase so that layers in between with `event.stopPropagation()`
|
|
// don't "cancel" this outside click check. E.g.: A `Menu` inside a `DialogPanel` if the `Menu`
|
|
// is open, and you click outside of it in the `DialogPanel` the `Menu` should close. However,
|
|
// the `DialogPanel` has a `onClick(e) { e.stopPropagation() }` which would cancel this.
|
|
true
|
|
);
|
|
useWindowEvent(
|
|
"blur",
|
|
(event) => {
|
|
return handleOutsideClick(event, () => {
|
|
return window.document.activeElement instanceof HTMLIFrameElement ? window.document.activeElement : null;
|
|
});
|
|
},
|
|
true
|
|
);
|
|
}
|
|
|
|
// src/hooks/use-resolve-button-type.ts
|
|
var import_vue8 = require("vue");
|
|
function resolveType(type, as) {
|
|
if (type)
|
|
return type;
|
|
let tag = as != null ? as : "button";
|
|
if (typeof tag === "string" && tag.toLowerCase() === "button")
|
|
return "button";
|
|
return void 0;
|
|
}
|
|
function useResolveButtonType(data, refElement) {
|
|
let type = (0, import_vue8.ref)(resolveType(data.value.type, data.value.as));
|
|
(0, import_vue8.onMounted)(() => {
|
|
type.value = resolveType(data.value.type, data.value.as);
|
|
});
|
|
(0, import_vue8.watchEffect)(() => {
|
|
var _a2;
|
|
if (type.value)
|
|
return;
|
|
if (!dom(refElement))
|
|
return;
|
|
if (dom(refElement) instanceof HTMLButtonElement && !((_a2 = dom(refElement)) == null ? void 0 : _a2.hasAttribute("type"))) {
|
|
type.value = "button";
|
|
}
|
|
});
|
|
return type;
|
|
}
|
|
|
|
// src/hooks/use-tracked-pointer.ts
|
|
var import_vue9 = require("vue");
|
|
function eventToPosition(evt) {
|
|
return [evt.screenX, evt.screenY];
|
|
}
|
|
function useTrackedPointer() {
|
|
let lastPos = (0, import_vue9.ref)([-1, -1]);
|
|
return {
|
|
wasMoved(evt) {
|
|
if (typeof process !== "undefined" && false) {
|
|
return true;
|
|
}
|
|
let newPos = eventToPosition(evt);
|
|
if (lastPos.value[0] === newPos[0] && lastPos.value[1] === newPos[1]) {
|
|
return false;
|
|
}
|
|
lastPos.value = newPos;
|
|
return true;
|
|
},
|
|
update(evt) {
|
|
lastPos.value = eventToPosition(evt);
|
|
}
|
|
};
|
|
}
|
|
|
|
// src/hooks/use-tree-walker.ts
|
|
var import_vue10 = require("vue");
|
|
function useTreeWalker({
|
|
container,
|
|
accept,
|
|
walk,
|
|
enabled
|
|
}) {
|
|
(0, import_vue10.watchEffect)(() => {
|
|
let root = container.value;
|
|
if (!root)
|
|
return;
|
|
if (enabled !== void 0 && !enabled.value)
|
|
return;
|
|
let ownerDocument = getOwnerDocument(container);
|
|
if (!ownerDocument)
|
|
return;
|
|
let acceptNode = Object.assign((node) => accept(node), { acceptNode: accept });
|
|
let walker = ownerDocument.createTreeWalker(
|
|
root,
|
|
NodeFilter.SHOW_ELEMENT,
|
|
acceptNode,
|
|
// @ts-expect-error This `false` is a simple small fix for older browsers
|
|
false
|
|
);
|
|
while (walker.nextNode())
|
|
walk(walker.currentNode);
|
|
});
|
|
}
|
|
|
|
// src/internal/hidden.ts
|
|
var import_vue12 = require("vue");
|
|
|
|
// src/utils/render.ts
|
|
var import_vue11 = require("vue");
|
|
function render({
|
|
visible = true,
|
|
features = 0 /* None */,
|
|
ourProps,
|
|
theirProps,
|
|
...main
|
|
}) {
|
|
var _a2;
|
|
let props = mergeProps(theirProps, ourProps);
|
|
let mainWithProps = Object.assign(main, { props });
|
|
if (visible)
|
|
return _render(mainWithProps);
|
|
if (features & 2 /* Static */) {
|
|
if (props.static)
|
|
return _render(mainWithProps);
|
|
}
|
|
if (features & 1 /* RenderStrategy */) {
|
|
let strategy = ((_a2 = props.unmount) != null ? _a2 : true) ? 0 /* Unmount */ : 1 /* Hidden */;
|
|
return match(strategy, {
|
|
[0 /* Unmount */]() {
|
|
return null;
|
|
},
|
|
[1 /* Hidden */]() {
|
|
return _render({
|
|
...main,
|
|
props: { ...props, hidden: true, style: { display: "none" } }
|
|
});
|
|
}
|
|
});
|
|
}
|
|
return _render(mainWithProps);
|
|
}
|
|
function _render({
|
|
props,
|
|
attrs,
|
|
slots,
|
|
slot,
|
|
name
|
|
}) {
|
|
var _a2, _b;
|
|
let { as, ...incomingProps } = omit(props, ["unmount", "static"]);
|
|
let children = (_a2 = slots.default) == null ? void 0 : _a2.call(slots, slot);
|
|
let dataAttributes = {};
|
|
if (slot) {
|
|
let exposeState = false;
|
|
let states = [];
|
|
for (let [k, v] of Object.entries(slot)) {
|
|
if (typeof v === "boolean") {
|
|
exposeState = true;
|
|
}
|
|
if (v === true) {
|
|
states.push(k);
|
|
}
|
|
}
|
|
if (exposeState)
|
|
dataAttributes[`data-headlessui-state`] = states.join(" ");
|
|
}
|
|
if (as === "template") {
|
|
children = flattenFragments(children != null ? children : []);
|
|
if (Object.keys(incomingProps).length > 0 || Object.keys(attrs).length > 0) {
|
|
let [firstChild, ...other] = children != null ? children : [];
|
|
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(incomingProps).concat(Object.keys(attrs)).map((name2) => name2.trim()).filter((current, idx, all) => all.indexOf(current) === idx).sort((a, z) => a.localeCompare(z)).map((line) => ` - ${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((line) => ` - ${line}`).join("\n")
|
|
].join("\n")
|
|
);
|
|
}
|
|
let mergedProps = mergeProps((_b = firstChild.props) != null ? _b : {}, incomingProps, dataAttributes);
|
|
let cloned = (0, import_vue11.cloneVNode)(firstChild, mergedProps, true);
|
|
for (let prop in mergedProps) {
|
|
if (prop.startsWith("on")) {
|
|
cloned.props || (cloned.props = {});
|
|
cloned.props[prop] = mergedProps[prop];
|
|
}
|
|
}
|
|
return cloned;
|
|
}
|
|
if (Array.isArray(children) && children.length === 1) {
|
|
return children[0];
|
|
}
|
|
return children;
|
|
}
|
|
return (0, import_vue11.h)(as, Object.assign({}, incomingProps, dataAttributes), {
|
|
default: () => children
|
|
});
|
|
}
|
|
function flattenFragments(children) {
|
|
return children.flatMap((child) => {
|
|
if (child.type === import_vue11.Fragment) {
|
|
return flattenFragments(child.children);
|
|
}
|
|
return [child];
|
|
});
|
|
}
|
|
function mergeProps(...listOfProps) {
|
|
var _a2;
|
|
if (listOfProps.length === 0)
|
|
return {};
|
|
if (listOfProps.length === 1)
|
|
return listOfProps[0];
|
|
let target = {};
|
|
let eventHandlers = {};
|
|
for (let props of listOfProps) {
|
|
for (let prop in props) {
|
|
if (prop.startsWith("on") && typeof props[prop] === "function") {
|
|
(_a2 = eventHandlers[prop]) != null ? _a2 : eventHandlers[prop] = [];
|
|
eventHandlers[prop].push(props[prop]);
|
|
} else {
|
|
target[prop] = props[prop];
|
|
}
|
|
}
|
|
}
|
|
if (target.disabled || target["aria-disabled"]) {
|
|
return Object.assign(
|
|
target,
|
|
// Set all event listeners that we collected to `undefined`. This is
|
|
// important because of the `cloneElement` from above, which merges the
|
|
// existing and new props, they don't just override therefore we have to
|
|
// explicitly nullify them.
|
|
Object.fromEntries(Object.keys(eventHandlers).map((eventName) => [eventName, void 0]))
|
|
);
|
|
}
|
|
for (let eventName in eventHandlers) {
|
|
Object.assign(target, {
|
|
[eventName](event, ...args) {
|
|
let handlers = eventHandlers[eventName];
|
|
for (let handler of handlers) {
|
|
if (event instanceof Event && event.defaultPrevented) {
|
|
return;
|
|
}
|
|
handler(event, ...args);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
return target;
|
|
}
|
|
function compact(object) {
|
|
let clone = Object.assign({}, object);
|
|
for (let key in clone) {
|
|
if (clone[key] === void 0)
|
|
delete clone[key];
|
|
}
|
|
return clone;
|
|
}
|
|
function omit(object, keysToOmit = []) {
|
|
let clone = Object.assign({}, object);
|
|
for (let key of keysToOmit) {
|
|
if (key in clone)
|
|
delete clone[key];
|
|
}
|
|
return clone;
|
|
}
|
|
function isValidElement(input) {
|
|
if (input == null)
|
|
return false;
|
|
if (typeof input.type === "string")
|
|
return true;
|
|
if (typeof input.type === "object")
|
|
return true;
|
|
if (typeof input.type === "function")
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
// src/internal/hidden.ts
|
|
var Hidden = (0, import_vue12.defineComponent)({
|
|
name: "Hidden",
|
|
props: {
|
|
as: { type: [Object, String], default: "div" },
|
|
features: { type: Number, default: 1 /* None */ }
|
|
},
|
|
setup(props, { slots, attrs }) {
|
|
return () => {
|
|
var _a2;
|
|
let { features, ...theirProps } = props;
|
|
let ourProps = {
|
|
"aria-hidden": (features & 2 /* Focusable */) === 2 /* Focusable */ ? true : (
|
|
// @ts-ignore
|
|
(_a2 = theirProps["aria-hidden"]) != null ? _a2 : void 0
|
|
),
|
|
hidden: (features & 4 /* Hidden */) === 4 /* Hidden */ ? true : void 0,
|
|
style: {
|
|
position: "fixed",
|
|
top: 1,
|
|
left: 1,
|
|
width: 1,
|
|
height: 0,
|
|
padding: 0,
|
|
margin: -1,
|
|
overflow: "hidden",
|
|
clip: "rect(0, 0, 0, 0)",
|
|
whiteSpace: "nowrap",
|
|
borderWidth: "0",
|
|
...(features & 4 /* Hidden */) === 4 /* Hidden */ && !((features & 2 /* Focusable */) === 2 /* Focusable */) && { display: "none" }
|
|
}
|
|
};
|
|
return render({
|
|
ourProps,
|
|
theirProps,
|
|
slot: {},
|
|
attrs,
|
|
slots,
|
|
name: "Hidden"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
|
|
// src/internal/open-closed.ts
|
|
var import_vue13 = require("vue");
|
|
var Context = Symbol("Context");
|
|
function hasOpenClosed() {
|
|
return useOpenClosed() !== null;
|
|
}
|
|
function useOpenClosed() {
|
|
return (0, import_vue13.inject)(Context, null);
|
|
}
|
|
function useOpenClosedProvider(value) {
|
|
(0, import_vue13.provide)(Context, value);
|
|
}
|
|
|
|
// src/utils/document-ready.ts
|
|
function onDocumentReady(cb) {
|
|
function check() {
|
|
if (document.readyState === "loading")
|
|
return;
|
|
cb();
|
|
document.removeEventListener("DOMContentLoaded", check);
|
|
}
|
|
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
document.addEventListener("DOMContentLoaded", check);
|
|
check();
|
|
}
|
|
}
|
|
|
|
// src/utils/active-element-history.ts
|
|
var history = [];
|
|
onDocumentReady(() => {
|
|
function handle(e) {
|
|
if (!(e.target instanceof HTMLElement))
|
|
return;
|
|
if (e.target === document.body)
|
|
return;
|
|
if (history[0] === e.target)
|
|
return;
|
|
history.unshift(e.target);
|
|
history = history.filter((x) => x != null && x.isConnected);
|
|
history.splice(10);
|
|
}
|
|
window.addEventListener("click", handle, { capture: true });
|
|
window.addEventListener("mousedown", handle, { capture: true });
|
|
window.addEventListener("focus", handle, { capture: true });
|
|
document.body.addEventListener("click", handle, { capture: true });
|
|
document.body.addEventListener("mousedown", handle, { capture: true });
|
|
document.body.addEventListener("focus", handle, { capture: true });
|
|
});
|
|
|
|
// src/utils/calculate-active-index.ts
|
|
function assertNever(x) {
|
|
throw new Error("Unexpected object: " + x);
|
|
}
|
|
function calculateActiveIndex(action, resolvers) {
|
|
let items = resolvers.resolveItems();
|
|
if (items.length <= 0)
|
|
return null;
|
|
let currentActiveIndex = resolvers.resolveActiveIndex();
|
|
let activeIndex = currentActiveIndex != null ? currentActiveIndex : -1;
|
|
switch (action.focus) {
|
|
case 0 /* First */: {
|
|
for (let i = 0; i < items.length; ++i) {
|
|
if (!resolvers.resolveDisabled(items[i], i, items)) {
|
|
return i;
|
|
}
|
|
}
|
|
return currentActiveIndex;
|
|
}
|
|
case 1 /* Previous */: {
|
|
if (activeIndex === -1)
|
|
activeIndex = items.length;
|
|
for (let i = activeIndex - 1; i >= 0; --i) {
|
|
if (!resolvers.resolveDisabled(items[i], i, items)) {
|
|
return i;
|
|
}
|
|
}
|
|
return currentActiveIndex;
|
|
}
|
|
case 2 /* Next */: {
|
|
for (let i = activeIndex + 1; i < items.length; ++i) {
|
|
if (!resolvers.resolveDisabled(items[i], i, items)) {
|
|
return i;
|
|
}
|
|
}
|
|
return currentActiveIndex;
|
|
}
|
|
case 3 /* Last */: {
|
|
for (let i = items.length - 1; i >= 0; --i) {
|
|
if (!resolvers.resolveDisabled(items[i], i, items)) {
|
|
return i;
|
|
}
|
|
}
|
|
return currentActiveIndex;
|
|
}
|
|
case 4 /* Specific */: {
|
|
for (let i = 0; i < items.length; ++i) {
|
|
if (resolvers.resolveId(items[i], i, items) === action.id) {
|
|
return i;
|
|
}
|
|
}
|
|
return currentActiveIndex;
|
|
}
|
|
case 5 /* Nothing */:
|
|
return null;
|
|
default:
|
|
assertNever(action);
|
|
}
|
|
}
|
|
|
|
// src/utils/form.ts
|
|
function objectToFormEntries(source = {}, parentKey = null, entries = []) {
|
|
for (let [key, value] of Object.entries(source)) {
|
|
append(entries, composeKey(parentKey, key), value);
|
|
}
|
|
return entries;
|
|
}
|
|
function composeKey(parent, key) {
|
|
return parent ? parent + "[" + key + "]" : key;
|
|
}
|
|
function append(entries, key, value) {
|
|
if (Array.isArray(value)) {
|
|
for (let [subkey, subvalue] of value.entries()) {
|
|
append(entries, composeKey(key, subkey.toString()), subvalue);
|
|
}
|
|
} else if (value instanceof Date) {
|
|
entries.push([key, value.toISOString()]);
|
|
} else if (typeof value === "boolean") {
|
|
entries.push([key, value ? "1" : "0"]);
|
|
} else if (typeof value === "string") {
|
|
entries.push([key, value]);
|
|
} else if (typeof value === "number") {
|
|
entries.push([key, `${value}`]);
|
|
} else if (value === null || value === void 0) {
|
|
entries.push([key, ""]);
|
|
} else {
|
|
objectToFormEntries(value, key, entries);
|
|
}
|
|
}
|
|
function attemptSubmit(elementInForm) {
|
|
var _a2, _b;
|
|
let form = (_a2 = elementInForm == null ? void 0 : elementInForm.form) != null ? _a2 : elementInForm.closest("form");
|
|
if (!form)
|
|
return;
|
|
for (let element of form.elements) {
|
|
if (element === elementInForm)
|
|
continue;
|
|
if (element.tagName === "INPUT" && element.type === "submit" || element.tagName === "BUTTON" && element.type === "submit" || element.nodeName === "INPUT" && element.type === "image") {
|
|
element.click();
|
|
return;
|
|
}
|
|
}
|
|
(_b = form.requestSubmit) == null ? void 0 : _b.call(form);
|
|
}
|
|
|
|
// src/components/combobox/combobox.ts
|
|
function defaultComparator(a, z) {
|
|
return a === z;
|
|
}
|
|
var ComboboxContext = Symbol("ComboboxContext");
|
|
function useComboboxContext(component) {
|
|
let context = (0, import_vue14.inject)(ComboboxContext, null);
|
|
if (context === null) {
|
|
let err = new Error(`<${component} /> is missing a parent <Combobox /> component.`);
|
|
if (Error.captureStackTrace)
|
|
Error.captureStackTrace(err, useComboboxContext);
|
|
throw err;
|
|
}
|
|
return context;
|
|
}
|
|
var VirtualContext = Symbol("VirtualContext");
|
|
var VirtualProvider = (0, import_vue14.defineComponent)({
|
|
name: "VirtualProvider",
|
|
setup(_, { slots }) {
|
|
let api = useComboboxContext("VirtualProvider");
|
|
let padding = (0, import_vue14.computed)(() => {
|
|
let el = dom(api.optionsRef);
|
|
if (!el)
|
|
return { start: 0, end: 0 };
|
|
let styles = window.getComputedStyle(el);
|
|
return {
|
|
start: parseFloat(styles.paddingBlockStart || styles.paddingTop),
|
|
end: parseFloat(styles.paddingBlockEnd || styles.paddingBottom)
|
|
};
|
|
});
|
|
let virtualizer = useVirtualizer(
|
|
(0, import_vue14.computed)(() => {
|
|
return {
|
|
scrollPaddingStart: padding.value.start,
|
|
scrollPaddingEnd: padding.value.end,
|
|
count: api.virtual.value.options.length,
|
|
estimateSize() {
|
|
return 40;
|
|
},
|
|
getScrollElement() {
|
|
return dom(api.optionsRef);
|
|
},
|
|
overscan: 12
|
|
};
|
|
})
|
|
);
|
|
let options = (0, import_vue14.computed)(() => {
|
|
var _a2;
|
|
return (_a2 = api.virtual.value) == null ? void 0 : _a2.options;
|
|
});
|
|
let baseKey = (0, import_vue14.ref)(0);
|
|
(0, import_vue14.watch)([options], () => {
|
|
baseKey.value += 1;
|
|
});
|
|
(0, import_vue14.provide)(VirtualContext, api.virtual.value ? virtualizer : null);
|
|
return () => {
|
|
return [
|
|
(0, import_vue14.h)(
|
|
"div",
|
|
{
|
|
style: {
|
|
position: "relative",
|
|
width: "100%",
|
|
height: `${virtualizer.value.getTotalSize()}px`
|
|
},
|
|
ref: (el) => {
|
|
if (!el) {
|
|
return;
|
|
}
|
|
{
|
|
if (typeof process !== "undefined" && process.env.JEST_WORKER_ID !== void 0) {
|
|
return;
|
|
}
|
|
if (api.activationTrigger.value === 0 /* Pointer */) {
|
|
return;
|
|
}
|
|
if (api.activeOptionIndex.value !== null && api.virtual.value.options.length > api.activeOptionIndex.value) {
|
|
virtualizer.value.scrollToIndex(api.activeOptionIndex.value);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
virtualizer.value.getVirtualItems().map((item) => {
|
|
return (0, import_vue14.cloneVNode)(
|
|
slots.default({
|
|
option: api.virtual.value.options[item.index],
|
|
open: api.comboboxState.value === 0 /* Open */
|
|
})[0],
|
|
{
|
|
key: `${baseKey.value}-${item.index}`,
|
|
"data-index": item.index,
|
|
"aria-setsize": api.virtual.value.options.length,
|
|
"aria-posinset": item.index + 1,
|
|
style: {
|
|
position: "absolute",
|
|
top: 0,
|
|
left: 0,
|
|
transform: `translateY(${item.start}px)`,
|
|
overflowAnchor: "none"
|
|
}
|
|
}
|
|
);
|
|
})
|
|
)
|
|
];
|
|
};
|
|
}
|
|
});
|
|
var Combobox = (0, import_vue14.defineComponent)({
|
|
name: "Combobox",
|
|
emits: { "update:modelValue": (_value) => true },
|
|
props: {
|
|
as: { type: [Object, String], default: "template" },
|
|
disabled: { type: [Boolean], default: false },
|
|
by: { type: [String, Function], nullable: true, default: null },
|
|
modelValue: {
|
|
type: [Object, String, Number, Boolean],
|
|
default: void 0
|
|
},
|
|
defaultValue: {
|
|
type: [Object, String, Number, Boolean],
|
|
default: void 0
|
|
},
|
|
form: { type: String, optional: true },
|
|
name: { type: String, optional: true },
|
|
nullable: { type: Boolean, default: false },
|
|
multiple: { type: [Boolean], default: false },
|
|
immediate: { type: [Boolean], default: false },
|
|
virtual: {
|
|
type: Object,
|
|
default: null
|
|
}
|
|
},
|
|
inheritAttrs: false,
|
|
setup(props, { slots, attrs, emit }) {
|
|
let comboboxState = (0, import_vue14.ref)(1 /* Closed */);
|
|
let labelRef = (0, import_vue14.ref)(null);
|
|
let inputRef = (0, import_vue14.ref)(null);
|
|
let buttonRef = (0, import_vue14.ref)(null);
|
|
let optionsRef = (0, import_vue14.ref)(
|
|
null
|
|
);
|
|
let optionsPropsRef = (0, import_vue14.ref)({
|
|
static: false,
|
|
hold: false
|
|
});
|
|
let options = (0, import_vue14.ref)([]);
|
|
let activeOptionIndex = (0, import_vue14.ref)(null);
|
|
let activationTrigger = (0, import_vue14.ref)(
|
|
2 /* Other */
|
|
);
|
|
let defaultToFirstOption = (0, import_vue14.ref)(false);
|
|
function adjustOrderedState(adjustment = (i) => i) {
|
|
let currentActiveOption = activeOptionIndex.value !== null ? options.value[activeOptionIndex.value] : null;
|
|
let list = adjustment(options.value.slice());
|
|
let sortedOptions = list.length > 0 && list[0].dataRef.order.value !== null ? (
|
|
// Prefer sorting based on the `order`
|
|
list.sort((a, z) => a.dataRef.order.value - z.dataRef.order.value)
|
|
) : (
|
|
// Fallback to much slower DOM order
|
|
sortByDomNode(list, (option) => dom(option.dataRef.domRef))
|
|
);
|
|
let adjustedActiveOptionIndex = currentActiveOption ? sortedOptions.indexOf(currentActiveOption) : null;
|
|
if (adjustedActiveOptionIndex === -1) {
|
|
adjustedActiveOptionIndex = null;
|
|
}
|
|
return {
|
|
options: sortedOptions,
|
|
activeOptionIndex: adjustedActiveOptionIndex
|
|
};
|
|
}
|
|
let mode = (0, import_vue14.computed)(() => props.multiple ? 1 /* Multi */ : 0 /* Single */);
|
|
let nullable = (0, import_vue14.computed)(() => props.nullable);
|
|
let [directValue, theirOnChange] = useControllable(
|
|
(0, import_vue14.computed)(() => props.modelValue),
|
|
(value2) => emit("update:modelValue", value2),
|
|
(0, import_vue14.computed)(() => props.defaultValue)
|
|
);
|
|
let value = (0, import_vue14.computed)(
|
|
() => directValue.value === void 0 ? match(mode.value, {
|
|
[1 /* Multi */]: [],
|
|
[0 /* Single */]: void 0
|
|
}) : directValue.value
|
|
);
|
|
let goToOptionRaf = null;
|
|
let orderOptionsRaf = null;
|
|
function onChange(value2) {
|
|
return match(mode.value, {
|
|
[0 /* Single */]() {
|
|
return theirOnChange == null ? void 0 : theirOnChange(value2);
|
|
},
|
|
[1 /* Multi */]: () => {
|
|
let copy = (0, import_vue14.toRaw)(api.value.value).slice();
|
|
let raw = (0, import_vue14.toRaw)(value2);
|
|
let idx = copy.findIndex((value3) => api.compare(raw, (0, import_vue14.toRaw)(value3)));
|
|
if (idx === -1) {
|
|
copy.push(raw);
|
|
} else {
|
|
copy.splice(idx, 1);
|
|
}
|
|
return theirOnChange == null ? void 0 : theirOnChange(copy);
|
|
}
|
|
});
|
|
}
|
|
let virtualOptions = (0, import_vue14.computed)(() => {
|
|
return void 0;
|
|
});
|
|
(0, import_vue14.watch)([virtualOptions], ([newOptions], [oldOptions]) => {
|
|
if (!api.virtual.value)
|
|
return;
|
|
if (!newOptions)
|
|
return;
|
|
if (!oldOptions)
|
|
return;
|
|
if (activeOptionIndex.value !== null) {
|
|
let idx = newOptions.indexOf(oldOptions[activeOptionIndex.value]);
|
|
if (idx !== -1) {
|
|
activeOptionIndex.value = idx;
|
|
} else {
|
|
activeOptionIndex.value = null;
|
|
}
|
|
}
|
|
});
|
|
let api = {
|
|
comboboxState,
|
|
value,
|
|
mode,
|
|
compare(a, z) {
|
|
if (typeof props.by === "string") {
|
|
let property = props.by;
|
|
return (a == null ? void 0 : a[property]) === (z == null ? void 0 : z[property]);
|
|
}
|
|
if (props.by === null) {
|
|
return defaultComparator(a, z);
|
|
}
|
|
return props.by(a, z);
|
|
},
|
|
calculateIndex(value2) {
|
|
if (api.virtual.value) {
|
|
if (props.by === null) {
|
|
return api.virtual.value.options.indexOf(value2);
|
|
} else {
|
|
return api.virtual.value.options.findIndex((other) => api.compare(other, value2));
|
|
}
|
|
} else {
|
|
return options.value.findIndex((other) => api.compare(other.dataRef.value, value2));
|
|
}
|
|
},
|
|
defaultValue: (0, import_vue14.computed)(() => props.defaultValue),
|
|
nullable,
|
|
immediate: (0, import_vue14.computed)(() => {
|
|
return false;
|
|
}),
|
|
virtual: (0, import_vue14.computed)(() => {
|
|
return null;
|
|
}),
|
|
inputRef,
|
|
labelRef,
|
|
buttonRef,
|
|
optionsRef,
|
|
disabled: (0, import_vue14.computed)(() => props.disabled),
|
|
// @ts-expect-error dateRef types are incorrect due to unwrapped or wrapped refs
|
|
options,
|
|
change(value2) {
|
|
theirOnChange(value2);
|
|
},
|
|
activeOptionIndex: (0, import_vue14.computed)(() => {
|
|
if (defaultToFirstOption.value && activeOptionIndex.value === null && (api.virtual.value ? api.virtual.value.options.length > 0 : options.value.length > 0)) {
|
|
if (api.virtual.value) {
|
|
let localActiveOptionIndex2 = api.virtual.value.options.findIndex(
|
|
(option) => {
|
|
var _a2;
|
|
return !((_a2 = api.virtual.value) == null ? void 0 : _a2.disabled(option));
|
|
}
|
|
);
|
|
if (localActiveOptionIndex2 !== -1) {
|
|
return localActiveOptionIndex2;
|
|
}
|
|
}
|
|
let localActiveOptionIndex = options.value.findIndex((option) => !option.dataRef.disabled);
|
|
if (localActiveOptionIndex !== -1) {
|
|
return localActiveOptionIndex;
|
|
}
|
|
}
|
|
return activeOptionIndex.value;
|
|
}),
|
|
activationTrigger,
|
|
optionsPropsRef,
|
|
closeCombobox() {
|
|
defaultToFirstOption.value = false;
|
|
if (props.disabled)
|
|
return;
|
|
if (comboboxState.value === 1 /* Closed */)
|
|
return;
|
|
comboboxState.value = 1 /* Closed */;
|
|
activeOptionIndex.value = null;
|
|
},
|
|
openCombobox() {
|
|
defaultToFirstOption.value = true;
|
|
if (props.disabled)
|
|
return;
|
|
if (comboboxState.value === 0 /* Open */)
|
|
return;
|
|
if (api.value.value) {
|
|
let idx = api.calculateIndex(api.value.value);
|
|
if (idx !== -1) {
|
|
activeOptionIndex.value = idx;
|
|
}
|
|
}
|
|
comboboxState.value = 0 /* Open */;
|
|
},
|
|
setActivationTrigger(trigger) {
|
|
activationTrigger.value = trigger;
|
|
},
|
|
goToOption(focus, idx, trigger) {
|
|
defaultToFirstOption.value = false;
|
|
if (goToOptionRaf !== null) {
|
|
cancelAnimationFrame(goToOptionRaf);
|
|
}
|
|
goToOptionRaf = requestAnimationFrame(() => {
|
|
if (props.disabled)
|
|
return;
|
|
if (optionsRef.value && !optionsPropsRef.value.static && comboboxState.value === 1 /* Closed */) {
|
|
return;
|
|
}
|
|
if (api.virtual.value) {
|
|
activeOptionIndex.value = focus === 4 /* Specific */ ? idx : calculateActiveIndex(
|
|
{ focus },
|
|
{
|
|
resolveItems: () => api.virtual.value.options,
|
|
resolveActiveIndex: () => {
|
|
var _a2, _b;
|
|
return (_b = (_a2 = api.activeOptionIndex.value) != null ? _a2 : api.virtual.value.options.findIndex(
|
|
(option) => {
|
|
var _a3;
|
|
return !((_a3 = api.virtual.value) == null ? void 0 : _a3.disabled(option));
|
|
}
|
|
)) != null ? _b : null;
|
|
},
|
|
resolveDisabled: (item) => api.virtual.value.disabled(item),
|
|
resolveId() {
|
|
throw new Error("Function not implemented.");
|
|
}
|
|
}
|
|
);
|
|
activationTrigger.value = trigger != null ? trigger : 2 /* Other */;
|
|
return;
|
|
}
|
|
let adjustedState = adjustOrderedState();
|
|
if (adjustedState.activeOptionIndex === null) {
|
|
let localActiveOptionIndex = adjustedState.options.findIndex(
|
|
(option) => !option.dataRef.disabled
|
|
);
|
|
if (localActiveOptionIndex !== -1) {
|
|
adjustedState.activeOptionIndex = localActiveOptionIndex;
|
|
}
|
|
}
|
|
let nextActiveOptionIndex = focus === 4 /* Specific */ ? idx : calculateActiveIndex(
|
|
{ focus },
|
|
{
|
|
resolveItems: () => adjustedState.options,
|
|
resolveActiveIndex: () => adjustedState.activeOptionIndex,
|
|
resolveId: (option) => option.id,
|
|
resolveDisabled: (option) => option.dataRef.disabled
|
|
}
|
|
);
|
|
activeOptionIndex.value = nextActiveOptionIndex;
|
|
activationTrigger.value = trigger != null ? trigger : 2 /* Other */;
|
|
options.value = adjustedState.options;
|
|
});
|
|
},
|
|
selectOption(id) {
|
|
let option = options.value.find((item) => item.id === id);
|
|
if (!option)
|
|
return;
|
|
let { dataRef } = option;
|
|
onChange(dataRef.value);
|
|
},
|
|
selectActiveOption() {
|
|
if (api.activeOptionIndex.value === null)
|
|
return;
|
|
if (api.virtual.value) {
|
|
onChange(api.virtual.value.options[api.activeOptionIndex.value]);
|
|
} else {
|
|
let { dataRef } = options.value[api.activeOptionIndex.value];
|
|
onChange(dataRef.value);
|
|
}
|
|
api.goToOption(4 /* Specific */, api.activeOptionIndex.value);
|
|
},
|
|
registerOption(id, dataRef) {
|
|
let option = (0, import_vue14.reactive)({ id, dataRef });
|
|
if (api.virtual.value) {
|
|
options.value.push(option);
|
|
return;
|
|
}
|
|
if (orderOptionsRaf)
|
|
cancelAnimationFrame(orderOptionsRaf);
|
|
let adjustedState = adjustOrderedState((options2) => {
|
|
options2.push(option);
|
|
return options2;
|
|
});
|
|
if (activeOptionIndex.value === null) {
|
|
if (api.isSelected(dataRef.value.value)) {
|
|
adjustedState.activeOptionIndex = adjustedState.options.indexOf(option);
|
|
}
|
|
}
|
|
options.value = adjustedState.options;
|
|
activeOptionIndex.value = adjustedState.activeOptionIndex;
|
|
activationTrigger.value = 2 /* Other */;
|
|
if (adjustedState.options.some((option2) => !dom(option2.dataRef.domRef))) {
|
|
orderOptionsRaf = requestAnimationFrame(() => {
|
|
let adjustedState2 = adjustOrderedState();
|
|
options.value = adjustedState2.options;
|
|
activeOptionIndex.value = adjustedState2.activeOptionIndex;
|
|
});
|
|
}
|
|
},
|
|
unregisterOption(id, active) {
|
|
if (goToOptionRaf !== null) {
|
|
cancelAnimationFrame(goToOptionRaf);
|
|
}
|
|
if (active) {
|
|
defaultToFirstOption.value = true;
|
|
}
|
|
if (api.virtual.value) {
|
|
options.value = options.value.filter((option) => option.id !== id);
|
|
return;
|
|
}
|
|
let adjustedState = adjustOrderedState((options2) => {
|
|
let idx = options2.findIndex((option) => option.id === id);
|
|
if (idx !== -1)
|
|
options2.splice(idx, 1);
|
|
return options2;
|
|
});
|
|
options.value = adjustedState.options;
|
|
activeOptionIndex.value = adjustedState.activeOptionIndex;
|
|
activationTrigger.value = 2 /* Other */;
|
|
},
|
|
isSelected(other) {
|
|
return match(mode.value, {
|
|
[0 /* Single */]: () => api.compare((0, import_vue14.toRaw)(api.value.value), (0, import_vue14.toRaw)(other)),
|
|
[1 /* Multi */]: () => (0, import_vue14.toRaw)(api.value.value).some(
|
|
(option) => api.compare((0, import_vue14.toRaw)(option), (0, import_vue14.toRaw)(other))
|
|
)
|
|
});
|
|
},
|
|
isActive(other) {
|
|
return activeOptionIndex.value === api.calculateIndex(other);
|
|
}
|
|
};
|
|
useOutsideClick(
|
|
[inputRef, buttonRef, optionsRef],
|
|
() => api.closeCombobox(),
|
|
(0, import_vue14.computed)(() => comboboxState.value === 0 /* Open */)
|
|
);
|
|
(0, import_vue14.provide)(ComboboxContext, api);
|
|
useOpenClosedProvider(
|
|
(0, import_vue14.computed)(
|
|
() => match(comboboxState.value, {
|
|
[0 /* Open */]: 1 /* Open */,
|
|
[1 /* Closed */]: 2 /* Closed */
|
|
})
|
|
)
|
|
);
|
|
let form = (0, import_vue14.computed)(() => {
|
|
var _a2;
|
|
return (_a2 = dom(inputRef)) == null ? void 0 : _a2.closest("form");
|
|
});
|
|
(0, import_vue14.onMounted)(() => {
|
|
(0, import_vue14.watch)(
|
|
[form],
|
|
() => {
|
|
if (!form.value)
|
|
return;
|
|
if (props.defaultValue === void 0)
|
|
return;
|
|
function handle() {
|
|
api.change(props.defaultValue);
|
|
}
|
|
form.value.addEventListener("reset", handle);
|
|
return () => {
|
|
var _a2;
|
|
(_a2 = form.value) == null ? void 0 : _a2.removeEventListener("reset", handle);
|
|
};
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
});
|
|
return () => {
|
|
var _a2, _b, _c;
|
|
let { name, disabled, form: form2, ...theirProps } = props;
|
|
let slot = {
|
|
open: comboboxState.value === 0 /* Open */,
|
|
disabled,
|
|
activeIndex: api.activeOptionIndex.value,
|
|
activeOption: api.activeOptionIndex.value === null ? null : api.virtual.value ? api.virtual.value.options[(_a2 = api.activeOptionIndex.value) != null ? _a2 : 0] : (_c = (_b = api.options.value[api.activeOptionIndex.value]) == null ? void 0 : _b.dataRef.value) != null ? _c : null,
|
|
value: value.value
|
|
};
|
|
return (0, import_vue14.h)(import_vue14.Fragment, [
|
|
...name != null && value.value != null ? objectToFormEntries({ [name]: value.value }).map(([name2, value2]) => {
|
|
return (0, import_vue14.h)(
|
|
Hidden,
|
|
compact({
|
|
features: 4 /* Hidden */,
|
|
key: name2,
|
|
as: "input",
|
|
type: "hidden",
|
|
hidden: true,
|
|
readOnly: true,
|
|
form: form2,
|
|
disabled,
|
|
name: name2,
|
|
value: value2
|
|
})
|
|
);
|
|
}) : [],
|
|
render({
|
|
theirProps: {
|
|
...attrs,
|
|
...omit(theirProps, [
|
|
"by",
|
|
"defaultValue",
|
|
"immediate",
|
|
"modelValue",
|
|
"multiple",
|
|
"nullable",
|
|
"onUpdate:modelValue",
|
|
"virtual"
|
|
])
|
|
},
|
|
ourProps: {},
|
|
slot,
|
|
slots,
|
|
attrs,
|
|
name: "Combobox"
|
|
})
|
|
]);
|
|
};
|
|
}
|
|
});
|
|
var ComboboxLabel = (0, import_vue14.defineComponent)({
|
|
name: "ComboboxLabel",
|
|
props: {
|
|
as: { type: [Object, String], default: "label" },
|
|
id: { type: String, default: null }
|
|
},
|
|
setup(props, { attrs, slots }) {
|
|
var _a2;
|
|
let id = (_a2 = props.id) != null ? _a2 : `headlessui-combobox-label-${useId2()}`;
|
|
let api = useComboboxContext("ComboboxLabel");
|
|
function handleClick() {
|
|
var _a3;
|
|
(_a3 = dom(api.inputRef)) == null ? void 0 : _a3.focus({ preventScroll: true });
|
|
}
|
|
return () => {
|
|
let slot = {
|
|
open: api.comboboxState.value === 0 /* Open */,
|
|
disabled: api.disabled.value
|
|
};
|
|
let { ...theirProps } = props;
|
|
let ourProps = { id, ref: api.labelRef, onClick: handleClick };
|
|
return render({
|
|
ourProps,
|
|
theirProps,
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
name: "ComboboxLabel"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
var ComboboxButton = (0, import_vue14.defineComponent)({
|
|
name: "ComboboxButton",
|
|
props: {
|
|
as: { type: [Object, String], default: "button" },
|
|
id: { type: String, default: null }
|
|
},
|
|
setup(props, { attrs, slots, expose }) {
|
|
var _a2;
|
|
let id = (_a2 = props.id) != null ? _a2 : `headlessui-combobox-button-${useId2()}`;
|
|
let api = useComboboxContext("ComboboxButton");
|
|
expose({ el: api.buttonRef, $el: api.buttonRef });
|
|
function handleClick(event) {
|
|
if (api.disabled.value)
|
|
return;
|
|
if (api.comboboxState.value === 0 /* Open */) {
|
|
api.closeCombobox();
|
|
} else {
|
|
event.preventDefault();
|
|
api.openCombobox();
|
|
}
|
|
(0, import_vue14.nextTick)(() => {
|
|
var _a3;
|
|
return (_a3 = dom(api.inputRef)) == null ? void 0 : _a3.focus({ preventScroll: true });
|
|
});
|
|
}
|
|
function handleKeydown(event) {
|
|
switch (event.key) {
|
|
case "ArrowDown" /* ArrowDown */:
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
if (api.comboboxState.value === 1 /* Closed */) {
|
|
api.openCombobox();
|
|
}
|
|
(0, import_vue14.nextTick)(() => {
|
|
var _a3;
|
|
return (_a3 = api.inputRef.value) == null ? void 0 : _a3.focus({ preventScroll: true });
|
|
});
|
|
return;
|
|
case "ArrowUp" /* ArrowUp */:
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
if (api.comboboxState.value === 1 /* Closed */) {
|
|
api.openCombobox();
|
|
(0, import_vue14.nextTick)(() => {
|
|
if (!api.value.value) {
|
|
api.goToOption(3 /* Last */);
|
|
}
|
|
});
|
|
}
|
|
(0, import_vue14.nextTick)(() => {
|
|
var _a3;
|
|
return (_a3 = api.inputRef.value) == null ? void 0 : _a3.focus({ preventScroll: true });
|
|
});
|
|
return;
|
|
case "Escape" /* Escape */:
|
|
if (api.comboboxState.value !== 0 /* Open */)
|
|
return;
|
|
event.preventDefault();
|
|
if (api.optionsRef.value && !api.optionsPropsRef.value.static) {
|
|
event.stopPropagation();
|
|
}
|
|
api.closeCombobox();
|
|
(0, import_vue14.nextTick)(() => {
|
|
var _a3;
|
|
return (_a3 = api.inputRef.value) == null ? void 0 : _a3.focus({ preventScroll: true });
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
let type = useResolveButtonType(
|
|
(0, import_vue14.computed)(() => ({ as: props.as, type: attrs.type })),
|
|
api.buttonRef
|
|
);
|
|
return () => {
|
|
var _a3, _b;
|
|
let slot = {
|
|
open: api.comboboxState.value === 0 /* Open */,
|
|
disabled: api.disabled.value,
|
|
value: api.value.value
|
|
};
|
|
let { ...theirProps } = props;
|
|
let ourProps = {
|
|
ref: api.buttonRef,
|
|
id,
|
|
type: type.value,
|
|
tabindex: "-1",
|
|
"aria-haspopup": "listbox",
|
|
"aria-controls": (_a3 = dom(api.optionsRef)) == null ? void 0 : _a3.id,
|
|
"aria-expanded": api.comboboxState.value === 0 /* Open */,
|
|
"aria-labelledby": api.labelRef.value ? [(_b = dom(api.labelRef)) == null ? void 0 : _b.id, id].join(" ") : void 0,
|
|
disabled: api.disabled.value === true ? true : void 0,
|
|
onKeydown: handleKeydown,
|
|
onClick: handleClick
|
|
};
|
|
return render({
|
|
ourProps,
|
|
theirProps,
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
name: "ComboboxButton"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
var ComboboxInput = (0, import_vue14.defineComponent)({
|
|
name: "ComboboxInput",
|
|
props: {
|
|
as: { type: [Object, String], default: "input" },
|
|
static: { type: Boolean, default: false },
|
|
unmount: { type: Boolean, default: true },
|
|
displayValue: { type: Function },
|
|
defaultValue: { type: String, default: void 0 },
|
|
id: { type: String, default: null }
|
|
},
|
|
emits: {
|
|
change: (_value) => true
|
|
},
|
|
setup(props, { emit, attrs, slots, expose }) {
|
|
var _a2;
|
|
let id = (_a2 = props.id) != null ? _a2 : `headlessui-combobox-input-${useId2()}`;
|
|
let api = useComboboxContext("ComboboxInput");
|
|
let ownerDocument = (0, import_vue14.computed)(() => getOwnerDocument(dom(api.inputRef)));
|
|
let isTyping = { value: false };
|
|
expose({ el: api.inputRef, $el: api.inputRef });
|
|
function clear() {
|
|
api.change(null);
|
|
let options = dom(api.optionsRef);
|
|
if (options) {
|
|
options.scrollTop = 0;
|
|
}
|
|
api.goToOption(5 /* Nothing */);
|
|
}
|
|
let currentDisplayValue = (0, import_vue14.computed)(() => {
|
|
var _a3;
|
|
let value = api.value.value;
|
|
if (!dom(api.inputRef))
|
|
return "";
|
|
if (typeof props.displayValue !== "undefined" && value !== void 0) {
|
|
return (_a3 = props.displayValue(value)) != null ? _a3 : "";
|
|
} else if (typeof value === "string") {
|
|
return value;
|
|
} else {
|
|
return "";
|
|
}
|
|
});
|
|
(0, import_vue14.onMounted)(() => {
|
|
(0, import_vue14.watch)(
|
|
[currentDisplayValue, api.comboboxState, ownerDocument],
|
|
([currentDisplayValue2, state], [oldCurrentDisplayValue, oldState]) => {
|
|
if (isTyping.value)
|
|
return;
|
|
let input = dom(api.inputRef);
|
|
if (!input)
|
|
return;
|
|
if (oldState === 0 /* Open */ && state === 1 /* Closed */) {
|
|
input.value = currentDisplayValue2;
|
|
} else if (currentDisplayValue2 !== oldCurrentDisplayValue) {
|
|
input.value = currentDisplayValue2;
|
|
}
|
|
requestAnimationFrame(() => {
|
|
var _a3;
|
|
if (isTyping.value)
|
|
return;
|
|
if (!input)
|
|
return;
|
|
if (((_a3 = ownerDocument.value) == null ? void 0 : _a3.activeElement) !== input)
|
|
return;
|
|
let { selectionStart, selectionEnd } = input;
|
|
if (Math.abs((selectionEnd != null ? selectionEnd : 0) - (selectionStart != null ? selectionStart : 0)) !== 0)
|
|
return;
|
|
if (selectionStart !== 0)
|
|
return;
|
|
input.setSelectionRange(input.value.length, input.value.length);
|
|
});
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
(0, import_vue14.watch)([api.comboboxState], ([newState], [oldState]) => {
|
|
if (newState === 0 /* Open */ && oldState === 1 /* Closed */) {
|
|
if (isTyping.value)
|
|
return;
|
|
let input = dom(api.inputRef);
|
|
if (!input)
|
|
return;
|
|
let currentValue = input.value;
|
|
let { selectionStart, selectionEnd, selectionDirection } = input;
|
|
input.value = "";
|
|
input.value = currentValue;
|
|
if (selectionDirection !== null) {
|
|
input.setSelectionRange(selectionStart, selectionEnd, selectionDirection);
|
|
} else {
|
|
input.setSelectionRange(selectionStart, selectionEnd);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
let isComposing = (0, import_vue14.ref)(false);
|
|
function handleCompositionstart() {
|
|
isComposing.value = true;
|
|
}
|
|
function handleCompositionend() {
|
|
disposables().nextFrame(() => {
|
|
isComposing.value = false;
|
|
});
|
|
}
|
|
let debounce = useFrameDebounce();
|
|
function handleKeyDown(event) {
|
|
isTyping.value = true;
|
|
debounce(() => {
|
|
isTyping.value = false;
|
|
});
|
|
switch (event.key) {
|
|
case "Enter" /* Enter */:
|
|
isTyping.value = false;
|
|
if (api.comboboxState.value !== 0 /* Open */)
|
|
return;
|
|
if (isComposing.value)
|
|
return;
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
if (api.activeOptionIndex.value === null) {
|
|
api.closeCombobox();
|
|
return;
|
|
}
|
|
api.selectActiveOption();
|
|
if (api.mode.value === 0 /* Single */) {
|
|
api.closeCombobox();
|
|
}
|
|
break;
|
|
case "ArrowDown" /* ArrowDown */:
|
|
isTyping.value = false;
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return match(api.comboboxState.value, {
|
|
[0 /* Open */]: () => api.goToOption(2 /* Next */),
|
|
[1 /* Closed */]: () => api.openCombobox()
|
|
});
|
|
case "ArrowUp" /* ArrowUp */:
|
|
isTyping.value = false;
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return match(api.comboboxState.value, {
|
|
[0 /* Open */]: () => api.goToOption(1 /* Previous */),
|
|
[1 /* Closed */]: () => {
|
|
api.openCombobox();
|
|
(0, import_vue14.nextTick)(() => {
|
|
if (!api.value.value) {
|
|
api.goToOption(3 /* Last */);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
case "Home" /* Home */:
|
|
if (event.shiftKey) {
|
|
break;
|
|
}
|
|
isTyping.value = false;
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return api.goToOption(0 /* First */);
|
|
case "PageUp" /* PageUp */:
|
|
isTyping.value = false;
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return api.goToOption(0 /* First */);
|
|
case "End" /* End */:
|
|
if (event.shiftKey) {
|
|
break;
|
|
}
|
|
isTyping.value = false;
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return api.goToOption(3 /* Last */);
|
|
case "PageDown" /* PageDown */:
|
|
isTyping.value = false;
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return api.goToOption(3 /* Last */);
|
|
case "Escape" /* Escape */:
|
|
isTyping.value = false;
|
|
if (api.comboboxState.value !== 0 /* Open */)
|
|
return;
|
|
event.preventDefault();
|
|
if (api.optionsRef.value && !api.optionsPropsRef.value.static) {
|
|
event.stopPropagation();
|
|
}
|
|
if (api.nullable.value && api.mode.value === 0 /* Single */) {
|
|
if (api.value.value === null) {
|
|
clear();
|
|
}
|
|
}
|
|
api.closeCombobox();
|
|
break;
|
|
case "Tab" /* Tab */:
|
|
isTyping.value = false;
|
|
if (api.comboboxState.value !== 0 /* Open */)
|
|
return;
|
|
if (api.mode.value === 0 /* Single */ && api.activationTrigger.value !== 1 /* Focus */) {
|
|
api.selectActiveOption();
|
|
}
|
|
api.closeCombobox();
|
|
break;
|
|
}
|
|
}
|
|
function handleInput(event) {
|
|
emit("change", event);
|
|
if (api.nullable.value && api.mode.value === 0 /* Single */) {
|
|
if (event.target.value === "") {
|
|
clear();
|
|
}
|
|
}
|
|
api.openCombobox();
|
|
}
|
|
function handleBlur(event) {
|
|
var _a3, _b, _c;
|
|
let relatedTarget = (_a3 = event.relatedTarget) != null ? _a3 : history.find((x) => x !== event.currentTarget);
|
|
isTyping.value = false;
|
|
if ((_b = dom(api.optionsRef)) == null ? void 0 : _b.contains(relatedTarget)) {
|
|
return;
|
|
}
|
|
if ((_c = dom(api.buttonRef)) == null ? void 0 : _c.contains(relatedTarget)) {
|
|
return;
|
|
}
|
|
if (api.comboboxState.value !== 0 /* Open */)
|
|
return;
|
|
event.preventDefault();
|
|
if (api.mode.value === 0 /* Single */) {
|
|
if (api.nullable.value && api.value.value === null) {
|
|
clear();
|
|
} else if (api.activationTrigger.value !== 1 /* Focus */) {
|
|
api.selectActiveOption();
|
|
}
|
|
}
|
|
return api.closeCombobox();
|
|
}
|
|
function handleFocus(event) {
|
|
var _a3, _b, _c;
|
|
let relatedTarget = (_a3 = event.relatedTarget) != null ? _a3 : history.find((x) => x !== event.currentTarget);
|
|
if ((_b = dom(api.buttonRef)) == null ? void 0 : _b.contains(relatedTarget))
|
|
return;
|
|
if ((_c = dom(api.optionsRef)) == null ? void 0 : _c.contains(relatedTarget))
|
|
return;
|
|
if (api.disabled.value)
|
|
return;
|
|
if (!api.immediate.value)
|
|
return;
|
|
if (api.comboboxState.value === 0 /* Open */)
|
|
return;
|
|
api.openCombobox();
|
|
disposables().nextFrame(() => {
|
|
api.setActivationTrigger(1 /* Focus */);
|
|
});
|
|
}
|
|
let defaultValue = (0, import_vue14.computed)(() => {
|
|
var _a3, _b, _c, _d;
|
|
return (_d = (_c = (_b = props.defaultValue) != null ? _b : api.defaultValue.value !== void 0 ? (_a3 = props.displayValue) == null ? void 0 : _a3.call(props, api.defaultValue.value) : null) != null ? _c : api.defaultValue.value) != null ? _d : "";
|
|
});
|
|
return () => {
|
|
var _a3, _b, _c, _d, _e, _f, _g;
|
|
let slot = { open: api.comboboxState.value === 0 /* Open */ };
|
|
let { displayValue, onChange: _onChange, ...theirProps } = props;
|
|
let ourProps = {
|
|
"aria-controls": (_a3 = api.optionsRef.value) == null ? void 0 : _a3.id,
|
|
"aria-expanded": api.comboboxState.value === 0 /* Open */,
|
|
"aria-activedescendant": api.activeOptionIndex.value === null ? void 0 : api.virtual.value ? (_b = api.options.value.find((option) => {
|
|
return !api.virtual.value.disabled(option.dataRef.value) && api.compare(
|
|
option.dataRef.value,
|
|
api.virtual.value.options[api.activeOptionIndex.value]
|
|
);
|
|
})) == null ? void 0 : _b.id : (_c = api.options.value[api.activeOptionIndex.value]) == null ? void 0 : _c.id,
|
|
"aria-labelledby": (_f = (_d = dom(api.labelRef)) == null ? void 0 : _d.id) != null ? _f : (_e = dom(api.buttonRef)) == null ? void 0 : _e.id,
|
|
"aria-autocomplete": "list",
|
|
id,
|
|
onCompositionstart: handleCompositionstart,
|
|
onCompositionend: handleCompositionend,
|
|
onKeydown: handleKeyDown,
|
|
onInput: handleInput,
|
|
onFocus: handleFocus,
|
|
onBlur: handleBlur,
|
|
role: "combobox",
|
|
type: (_g = attrs.type) != null ? _g : "text",
|
|
tabIndex: 0,
|
|
ref: api.inputRef,
|
|
defaultValue: defaultValue.value,
|
|
disabled: api.disabled.value === true ? true : void 0
|
|
};
|
|
return render({
|
|
ourProps,
|
|
theirProps,
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
features: 1 /* RenderStrategy */ | 2 /* Static */,
|
|
name: "ComboboxInput"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
var ComboboxOptions = (0, import_vue14.defineComponent)({
|
|
name: "ComboboxOptions",
|
|
props: {
|
|
as: { type: [Object, String], default: "ul" },
|
|
static: { type: Boolean, default: false },
|
|
unmount: { type: Boolean, default: true },
|
|
hold: { type: [Boolean], default: false }
|
|
},
|
|
setup(props, { attrs, slots, expose }) {
|
|
let api = useComboboxContext("ComboboxOptions");
|
|
let id = `headlessui-combobox-options-${useId2()}`;
|
|
expose({ el: api.optionsRef, $el: api.optionsRef });
|
|
(0, import_vue14.watchEffect)(() => {
|
|
api.optionsPropsRef.value.static = props.static;
|
|
});
|
|
(0, import_vue14.watchEffect)(() => {
|
|
api.optionsPropsRef.value.hold = props.hold;
|
|
});
|
|
let usesOpenClosedState = useOpenClosed();
|
|
let visible = (0, import_vue14.computed)(() => {
|
|
if (usesOpenClosedState !== null) {
|
|
return (usesOpenClosedState.value & 1 /* Open */) === 1 /* Open */;
|
|
}
|
|
return api.comboboxState.value === 0 /* Open */;
|
|
});
|
|
useTreeWalker({
|
|
container: (0, import_vue14.computed)(() => dom(api.optionsRef)),
|
|
enabled: (0, import_vue14.computed)(() => api.comboboxState.value === 0 /* Open */),
|
|
accept(node) {
|
|
if (node.getAttribute("role") === "option")
|
|
return NodeFilter.FILTER_REJECT;
|
|
if (node.hasAttribute("role"))
|
|
return NodeFilter.FILTER_SKIP;
|
|
return NodeFilter.FILTER_ACCEPT;
|
|
},
|
|
walk(node) {
|
|
node.setAttribute("role", "none");
|
|
}
|
|
});
|
|
function handleMouseDown(event) {
|
|
event.preventDefault();
|
|
}
|
|
return () => {
|
|
var _a2, _b, _c;
|
|
let slot = { open: api.comboboxState.value === 0 /* Open */ };
|
|
let ourProps = {
|
|
"aria-labelledby": (_c = (_a2 = dom(api.labelRef)) == null ? void 0 : _a2.id) != null ? _c : (_b = dom(api.buttonRef)) == null ? void 0 : _b.id,
|
|
id,
|
|
ref: api.optionsRef,
|
|
role: "listbox",
|
|
"aria-multiselectable": api.mode.value === 1 /* Multi */ ? true : void 0,
|
|
onMousedown: handleMouseDown
|
|
};
|
|
let theirProps = omit(props, ["hold"]);
|
|
return render({
|
|
ourProps,
|
|
theirProps,
|
|
slot,
|
|
attrs,
|
|
slots: api.virtual.value && api.comboboxState.value === 0 /* Open */ ? {
|
|
...slots,
|
|
default: () => [(0, import_vue14.h)(VirtualProvider, {}, slots.default)]
|
|
} : slots,
|
|
features: 1 /* RenderStrategy */ | 2 /* Static */,
|
|
visible: visible.value,
|
|
name: "ComboboxOptions"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
var ComboboxOption = (0, import_vue14.defineComponent)({
|
|
name: "ComboboxOption",
|
|
props: {
|
|
as: { type: [Object, String], default: "li" },
|
|
value: {
|
|
type: [Object, String, Number, Boolean]
|
|
},
|
|
disabled: { type: Boolean, default: false },
|
|
order: { type: [Number], default: null }
|
|
},
|
|
setup(props, { slots, attrs, expose }) {
|
|
let api = useComboboxContext("ComboboxOption");
|
|
let id = `headlessui-combobox-option-${useId2()}`;
|
|
let internalOptionRef = (0, import_vue14.ref)(null);
|
|
let disabled = (0, import_vue14.computed)(() => {
|
|
return props.disabled;
|
|
});
|
|
expose({ el: internalOptionRef, $el: internalOptionRef });
|
|
let active = (0, import_vue14.computed)(() => {
|
|
var _a2;
|
|
return api.virtual.value ? api.activeOptionIndex.value === api.calculateIndex(props.value) : api.activeOptionIndex.value === null ? false : ((_a2 = api.options.value[api.activeOptionIndex.value]) == null ? void 0 : _a2.id) === id;
|
|
});
|
|
let selected = (0, import_vue14.computed)(() => api.isSelected(props.value));
|
|
let virtualizer = (0, import_vue14.inject)(VirtualContext, null);
|
|
let dataRef = (0, import_vue14.computed)(() => ({
|
|
disabled: props.disabled,
|
|
value: props.value,
|
|
domRef: internalOptionRef,
|
|
order: (0, import_vue14.computed)(() => props.order)
|
|
}));
|
|
(0, import_vue14.onMounted)(() => api.registerOption(id, dataRef));
|
|
(0, import_vue14.onUnmounted)(() => api.unregisterOption(id, active.value));
|
|
(0, import_vue14.watchEffect)(() => {
|
|
let el = dom(internalOptionRef);
|
|
if (!el)
|
|
return;
|
|
virtualizer == null ? void 0 : virtualizer.value.measureElement(el);
|
|
});
|
|
(0, import_vue14.watchEffect)(() => {
|
|
if (api.comboboxState.value !== 0 /* Open */)
|
|
return;
|
|
if (!active.value)
|
|
return;
|
|
if (api.virtual.value)
|
|
return;
|
|
if (api.activationTrigger.value === 0 /* Pointer */)
|
|
return;
|
|
(0, import_vue14.nextTick)(() => {
|
|
var _a2, _b;
|
|
return (_b = (_a2 = dom(internalOptionRef)) == null ? void 0 : _a2.scrollIntoView) == null ? void 0 : _b.call(_a2, { block: "nearest" });
|
|
});
|
|
});
|
|
function handleMouseDown(event) {
|
|
event.preventDefault();
|
|
if (event.button !== 0 /* Left */) {
|
|
return;
|
|
}
|
|
if (disabled.value)
|
|
return;
|
|
api.selectOption(id);
|
|
if (!isMobile()) {
|
|
requestAnimationFrame(() => {
|
|
var _a2;
|
|
return (_a2 = dom(api.inputRef)) == null ? void 0 : _a2.focus({ preventScroll: true });
|
|
});
|
|
}
|
|
if (api.mode.value === 0 /* Single */) {
|
|
api.closeCombobox();
|
|
}
|
|
}
|
|
function handleFocus() {
|
|
var _a2;
|
|
if (props.disabled || ((_a2 = api.virtual.value) == null ? void 0 : _a2.disabled(props.value))) {
|
|
return api.goToOption(5 /* Nothing */);
|
|
}
|
|
let idx = api.calculateIndex(props.value);
|
|
api.goToOption(4 /* Specific */, idx);
|
|
}
|
|
let pointer = useTrackedPointer();
|
|
function handleEnter(evt) {
|
|
pointer.update(evt);
|
|
}
|
|
function handleMove(evt) {
|
|
var _a2;
|
|
if (!pointer.wasMoved(evt))
|
|
return;
|
|
if (props.disabled || ((_a2 = api.virtual.value) == null ? void 0 : _a2.disabled(props.value)))
|
|
return;
|
|
if (active.value)
|
|
return;
|
|
let idx = api.calculateIndex(props.value);
|
|
api.goToOption(4 /* Specific */, idx, 0 /* Pointer */);
|
|
}
|
|
function handleLeave(evt) {
|
|
var _a2;
|
|
if (!pointer.wasMoved(evt))
|
|
return;
|
|
if (props.disabled || ((_a2 = api.virtual.value) == null ? void 0 : _a2.disabled(props.value)))
|
|
return;
|
|
if (!active.value)
|
|
return;
|
|
if (api.optionsPropsRef.value.hold)
|
|
return;
|
|
api.goToOption(5 /* Nothing */);
|
|
}
|
|
return () => {
|
|
let { disabled: disabled2 } = props;
|
|
let slot = { active: active.value, selected: selected.value, disabled: disabled2 };
|
|
let ourProps = {
|
|
id,
|
|
ref: internalOptionRef,
|
|
role: "option",
|
|
tabIndex: disabled2 === true ? void 0 : -1,
|
|
"aria-disabled": disabled2 === true ? true : void 0,
|
|
// According to the WAI-ARIA best practices, we should use aria-checked for
|
|
// multi-select,but Voice-Over disagrees. So we use aria-selected instead for
|
|
// both single and multi-select.
|
|
"aria-selected": selected.value,
|
|
disabled: void 0,
|
|
// Never forward the `disabled` prop
|
|
onMousedown: handleMouseDown,
|
|
onFocus: handleFocus,
|
|
onPointerenter: handleEnter,
|
|
onMouseenter: handleEnter,
|
|
onPointermove: handleMove,
|
|
onMousemove: handleMove,
|
|
onPointerleave: handleLeave,
|
|
onMouseleave: handleLeave
|
|
};
|
|
let theirProps = omit(props, ["order", "value"]);
|
|
return render({
|
|
ourProps,
|
|
theirProps,
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
name: "ComboboxOption"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
|
|
// src/components/dialog/dialog.ts
|
|
var import_vue26 = require("vue");
|
|
|
|
// src/components/focus-trap/focus-trap.ts
|
|
var import_vue17 = require("vue");
|
|
|
|
// src/hooks/use-event-listener.ts
|
|
var import_vue15 = require("vue");
|
|
function useEventListener(element, type, listener, options) {
|
|
if (env.isServer)
|
|
return;
|
|
(0, import_vue15.watchEffect)((onInvalidate) => {
|
|
element = element != null ? element : window;
|
|
element.addEventListener(type, listener, options);
|
|
onInvalidate(() => element.removeEventListener(type, listener, options));
|
|
});
|
|
}
|
|
|
|
// src/hooks/use-tab-direction.ts
|
|
var import_vue16 = require("vue");
|
|
function useTabDirection() {
|
|
let direction = (0, import_vue16.ref)(0 /* Forwards */);
|
|
useWindowEvent("keydown", (event) => {
|
|
if (event.key === "Tab") {
|
|
direction.value = event.shiftKey ? 1 /* Backwards */ : 0 /* Forwards */;
|
|
}
|
|
});
|
|
return direction;
|
|
}
|
|
|
|
// src/components/focus-trap/focus-trap.ts
|
|
function resolveContainers(containers) {
|
|
if (!containers)
|
|
return /* @__PURE__ */ new Set();
|
|
if (typeof containers === "function")
|
|
return new Set(containers());
|
|
let all = /* @__PURE__ */ new Set();
|
|
for (let container of containers.value) {
|
|
let el = dom(container);
|
|
if (el instanceof HTMLElement) {
|
|
all.add(el);
|
|
}
|
|
}
|
|
return all;
|
|
}
|
|
var Features3 = /* @__PURE__ */ ((Features4) => {
|
|
Features4[Features4["None"] = 1] = "None";
|
|
Features4[Features4["InitialFocus"] = 2] = "InitialFocus";
|
|
Features4[Features4["TabLock"] = 4] = "TabLock";
|
|
Features4[Features4["FocusLock"] = 8] = "FocusLock";
|
|
Features4[Features4["RestoreFocus"] = 16] = "RestoreFocus";
|
|
Features4[Features4["All"] = 30] = "All";
|
|
return Features4;
|
|
})(Features3 || {});
|
|
var FocusTrap = Object.assign(
|
|
(0, import_vue17.defineComponent)({
|
|
name: "FocusTrap",
|
|
props: {
|
|
as: { type: [Object, String], default: "div" },
|
|
initialFocus: { type: Object, default: null },
|
|
features: { type: Number, default: 30 /* All */ },
|
|
containers: {
|
|
type: [Object, Function],
|
|
default: (0, import_vue17.ref)(/* @__PURE__ */ new Set())
|
|
}
|
|
},
|
|
inheritAttrs: false,
|
|
setup(props, { attrs, slots, expose }) {
|
|
let container = (0, import_vue17.ref)(null);
|
|
expose({ el: container, $el: container });
|
|
let ownerDocument = (0, import_vue17.computed)(() => getOwnerDocument(container));
|
|
let mounted = (0, import_vue17.ref)(false);
|
|
(0, import_vue17.onMounted)(() => mounted.value = true);
|
|
(0, import_vue17.onUnmounted)(() => mounted.value = false);
|
|
useRestoreFocus(
|
|
{ ownerDocument },
|
|
(0, import_vue17.computed)(() => mounted.value && Boolean(props.features & 16 /* RestoreFocus */))
|
|
);
|
|
let previousActiveElement = useInitialFocus(
|
|
{ ownerDocument, container, initialFocus: (0, import_vue17.computed)(() => props.initialFocus) },
|
|
(0, import_vue17.computed)(() => mounted.value && Boolean(props.features & 2 /* InitialFocus */))
|
|
);
|
|
useFocusLock(
|
|
{
|
|
ownerDocument,
|
|
container,
|
|
containers: props.containers,
|
|
previousActiveElement
|
|
},
|
|
(0, import_vue17.computed)(() => mounted.value && Boolean(props.features & 8 /* FocusLock */))
|
|
);
|
|
let direction = useTabDirection();
|
|
function handleFocus(e) {
|
|
let el = dom(container);
|
|
if (!el)
|
|
return;
|
|
let wrapper = false ? microTask : (cb) => cb();
|
|
wrapper(() => {
|
|
match(direction.value, {
|
|
[0 /* Forwards */]: () => {
|
|
focusIn(el, 1 /* First */, { skipElements: [e.relatedTarget] });
|
|
},
|
|
[1 /* Backwards */]: () => {
|
|
focusIn(el, 8 /* Last */, { skipElements: [e.relatedTarget] });
|
|
}
|
|
});
|
|
});
|
|
}
|
|
let recentlyUsedTabKey = (0, import_vue17.ref)(false);
|
|
function handleKeyDown(e) {
|
|
if (e.key === "Tab") {
|
|
recentlyUsedTabKey.value = true;
|
|
requestAnimationFrame(() => {
|
|
recentlyUsedTabKey.value = false;
|
|
});
|
|
}
|
|
}
|
|
function handleBlur(e) {
|
|
if (!mounted.value)
|
|
return;
|
|
let allContainers = resolveContainers(props.containers);
|
|
if (dom(container) instanceof HTMLElement)
|
|
allContainers.add(dom(container));
|
|
let relatedTarget = e.relatedTarget;
|
|
if (!(relatedTarget instanceof HTMLElement))
|
|
return;
|
|
if (relatedTarget.dataset.headlessuiFocusGuard === "true") {
|
|
return;
|
|
}
|
|
if (!contains(allContainers, relatedTarget)) {
|
|
if (recentlyUsedTabKey.value) {
|
|
focusIn(
|
|
dom(container),
|
|
match(direction.value, {
|
|
[0 /* Forwards */]: () => 4 /* Next */,
|
|
[1 /* Backwards */]: () => 2 /* Previous */
|
|
}) | 16 /* WrapAround */,
|
|
{ relativeTo: e.target }
|
|
);
|
|
} else if (e.target instanceof HTMLElement) {
|
|
focusElement(e.target);
|
|
}
|
|
}
|
|
}
|
|
return () => {
|
|
let slot = {};
|
|
let ourProps = { ref: container, onKeydown: handleKeyDown, onFocusout: handleBlur };
|
|
let { features, initialFocus, containers: _containers, ...theirProps } = props;
|
|
return (0, import_vue17.h)(import_vue17.Fragment, [
|
|
Boolean(features & 4 /* TabLock */) && (0, import_vue17.h)(Hidden, {
|
|
as: "button",
|
|
type: "button",
|
|
"data-headlessui-focus-guard": true,
|
|
onFocus: handleFocus,
|
|
features: 2 /* Focusable */
|
|
}),
|
|
render({
|
|
ourProps,
|
|
theirProps: { ...attrs, ...theirProps },
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
name: "FocusTrap"
|
|
}),
|
|
Boolean(features & 4 /* TabLock */) && (0, import_vue17.h)(Hidden, {
|
|
as: "button",
|
|
type: "button",
|
|
"data-headlessui-focus-guard": true,
|
|
onFocus: handleFocus,
|
|
features: 2 /* Focusable */
|
|
})
|
|
]);
|
|
};
|
|
}
|
|
}),
|
|
{ features: Features3 }
|
|
);
|
|
function useRestoreElement(enabled) {
|
|
let localHistory = (0, import_vue17.ref)(history.slice());
|
|
(0, import_vue17.watch)(
|
|
[enabled],
|
|
([newEnabled], [oldEnabled]) => {
|
|
if (oldEnabled === true && newEnabled === false) {
|
|
microTask(() => {
|
|
localHistory.value.splice(0);
|
|
});
|
|
} else if (oldEnabled === false && newEnabled === true) {
|
|
localHistory.value = history.slice();
|
|
}
|
|
},
|
|
{ flush: "post" }
|
|
);
|
|
return () => {
|
|
var _a2;
|
|
return (_a2 = localHistory.value.find((x) => x != null && x.isConnected)) != null ? _a2 : null;
|
|
};
|
|
}
|
|
function useRestoreFocus({ ownerDocument }, enabled) {
|
|
let getRestoreElement = useRestoreElement(enabled);
|
|
(0, import_vue17.onMounted)(() => {
|
|
(0, import_vue17.watchEffect)(
|
|
() => {
|
|
var _a2, _b;
|
|
if (enabled.value)
|
|
return;
|
|
if (((_a2 = ownerDocument.value) == null ? void 0 : _a2.activeElement) === ((_b = ownerDocument.value) == null ? void 0 : _b.body)) {
|
|
focusElement(getRestoreElement());
|
|
}
|
|
},
|
|
{ flush: "post" }
|
|
);
|
|
});
|
|
(0, import_vue17.onUnmounted)(() => {
|
|
if (!enabled.value)
|
|
return;
|
|
focusElement(getRestoreElement());
|
|
});
|
|
}
|
|
function useInitialFocus({
|
|
ownerDocument,
|
|
container,
|
|
initialFocus
|
|
}, enabled) {
|
|
let previousActiveElement = (0, import_vue17.ref)(null);
|
|
let mounted = (0, import_vue17.ref)(false);
|
|
(0, import_vue17.onMounted)(() => mounted.value = true);
|
|
(0, import_vue17.onUnmounted)(() => mounted.value = false);
|
|
(0, import_vue17.onMounted)(() => {
|
|
(0, import_vue17.watch)(
|
|
// Handle initial focus
|
|
[container, initialFocus, enabled],
|
|
(newValues, prevValues) => {
|
|
if (newValues.every((value, idx) => (prevValues == null ? void 0 : prevValues[idx]) === value))
|
|
return;
|
|
if (!enabled.value)
|
|
return;
|
|
let containerElement = dom(container);
|
|
if (!containerElement)
|
|
return;
|
|
microTask(() => {
|
|
var _a2, _b;
|
|
if (!mounted.value) {
|
|
return;
|
|
}
|
|
let initialFocusElement = dom(initialFocus);
|
|
let activeElement = (_a2 = ownerDocument.value) == null ? void 0 : _a2.activeElement;
|
|
if (initialFocusElement) {
|
|
if (initialFocusElement === activeElement) {
|
|
previousActiveElement.value = activeElement;
|
|
return;
|
|
}
|
|
} else if (containerElement.contains(activeElement)) {
|
|
previousActiveElement.value = activeElement;
|
|
return;
|
|
}
|
|
if (initialFocusElement) {
|
|
focusElement(initialFocusElement);
|
|
} else {
|
|
if (focusIn(containerElement, 1 /* First */ | 32 /* NoScroll */) === 0 /* Error */) {
|
|
console.warn("There are no focusable elements inside the <FocusTrap />");
|
|
}
|
|
}
|
|
previousActiveElement.value = (_b = ownerDocument.value) == null ? void 0 : _b.activeElement;
|
|
});
|
|
},
|
|
{ immediate: true, flush: "post" }
|
|
);
|
|
});
|
|
return previousActiveElement;
|
|
}
|
|
function useFocusLock({
|
|
ownerDocument,
|
|
container,
|
|
containers,
|
|
previousActiveElement
|
|
}, enabled) {
|
|
var _a2;
|
|
useEventListener(
|
|
(_a2 = ownerDocument.value) == null ? void 0 : _a2.defaultView,
|
|
"focus",
|
|
(event) => {
|
|
if (!enabled.value)
|
|
return;
|
|
let allContainers = resolveContainers(containers);
|
|
if (dom(container) instanceof HTMLElement)
|
|
allContainers.add(dom(container));
|
|
let previous = previousActiveElement.value;
|
|
if (!previous)
|
|
return;
|
|
let toElement = event.target;
|
|
if (toElement && toElement instanceof HTMLElement) {
|
|
if (!contains(allContainers, toElement)) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
focusElement(previous);
|
|
} else {
|
|
previousActiveElement.value = toElement;
|
|
focusElement(toElement);
|
|
}
|
|
} else {
|
|
focusElement(previousActiveElement.value);
|
|
}
|
|
},
|
|
true
|
|
);
|
|
}
|
|
function contains(containers, element) {
|
|
for (let container of containers) {
|
|
if (container.contains(element))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// src/hooks/document-overflow/use-document-overflow.ts
|
|
var import_vue19 = require("vue");
|
|
|
|
// src/hooks/use-store.ts
|
|
var import_vue18 = require("vue");
|
|
function useStore(store) {
|
|
let state = (0, import_vue18.shallowRef)(store.getSnapshot());
|
|
(0, import_vue18.onUnmounted)(
|
|
store.subscribe(() => {
|
|
state.value = store.getSnapshot();
|
|
})
|
|
);
|
|
return state;
|
|
}
|
|
|
|
// src/utils/store.ts
|
|
function createStore(initial, actions) {
|
|
let state = initial();
|
|
let listeners = /* @__PURE__ */ new Set();
|
|
return {
|
|
getSnapshot() {
|
|
return state;
|
|
},
|
|
subscribe(onChange) {
|
|
listeners.add(onChange);
|
|
return () => listeners.delete(onChange);
|
|
},
|
|
dispatch(key, ...args) {
|
|
let newState = actions[key].call(state, ...args);
|
|
if (newState) {
|
|
state = newState;
|
|
listeners.forEach((listener) => listener());
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
// src/hooks/document-overflow/adjust-scrollbar-padding.ts
|
|
function adjustScrollbarPadding() {
|
|
let scrollbarWidthBefore;
|
|
return {
|
|
before({ doc }) {
|
|
var _a2;
|
|
let documentElement = doc.documentElement;
|
|
let ownerWindow = (_a2 = doc.defaultView) != null ? _a2 : window;
|
|
scrollbarWidthBefore = ownerWindow.innerWidth - documentElement.clientWidth;
|
|
},
|
|
after({ doc, d }) {
|
|
let documentElement = doc.documentElement;
|
|
let scrollbarWidthAfter = documentElement.clientWidth - documentElement.offsetWidth;
|
|
let scrollbarWidth = scrollbarWidthBefore - scrollbarWidthAfter;
|
|
d.style(documentElement, "paddingRight", `${scrollbarWidth}px`);
|
|
}
|
|
};
|
|
}
|
|
|
|
// src/hooks/document-overflow/handle-ios-locking.ts
|
|
function handleIOSLocking() {
|
|
if (!isIOS()) {
|
|
return {};
|
|
}
|
|
return {
|
|
before({ doc, d, meta }) {
|
|
function inAllowedContainer(el) {
|
|
return meta.containers.flatMap((resolve) => resolve()).some((container) => container.contains(el));
|
|
}
|
|
d.microTask(() => {
|
|
var _a2;
|
|
if (window.getComputedStyle(doc.documentElement).scrollBehavior !== "auto") {
|
|
let _d = disposables();
|
|
_d.style(doc.documentElement, "scrollBehavior", "auto");
|
|
d.add(() => d.microTask(() => _d.dispose()));
|
|
}
|
|
let scrollPosition = (_a2 = window.scrollY) != null ? _a2 : window.pageYOffset;
|
|
let scrollToElement = null;
|
|
d.addEventListener(
|
|
doc,
|
|
"click",
|
|
(e) => {
|
|
if (!(e.target instanceof HTMLElement)) {
|
|
return;
|
|
}
|
|
try {
|
|
let anchor = e.target.closest("a");
|
|
if (!anchor)
|
|
return;
|
|
let { hash } = new URL(anchor.href);
|
|
let el = doc.querySelector(hash);
|
|
if (el && !inAllowedContainer(el)) {
|
|
scrollToElement = el;
|
|
}
|
|
} catch (err) {
|
|
}
|
|
},
|
|
true
|
|
);
|
|
d.addEventListener(doc, "touchstart", (e) => {
|
|
if (e.target instanceof HTMLElement) {
|
|
if (inAllowedContainer(e.target)) {
|
|
let rootContainer = e.target;
|
|
while (rootContainer.parentElement && inAllowedContainer(rootContainer.parentElement)) {
|
|
rootContainer = rootContainer.parentElement;
|
|
}
|
|
d.style(rootContainer, "overscrollBehavior", "contain");
|
|
} else {
|
|
d.style(e.target, "touchAction", "none");
|
|
}
|
|
}
|
|
});
|
|
d.addEventListener(
|
|
doc,
|
|
"touchmove",
|
|
(e) => {
|
|
if (e.target instanceof HTMLElement) {
|
|
if (e.target.tagName === "INPUT") {
|
|
return;
|
|
}
|
|
if (inAllowedContainer(e.target)) {
|
|
let scrollableParent = e.target;
|
|
while (scrollableParent.parentElement && // Assumption: We are always used in a Headless UI Portal. Once we reach the
|
|
// portal itself, we can stop crawling up the tree.
|
|
scrollableParent.dataset.headlessuiPortal !== "") {
|
|
if (scrollableParent.scrollHeight > scrollableParent.clientHeight || scrollableParent.scrollWidth > scrollableParent.clientWidth) {
|
|
break;
|
|
}
|
|
scrollableParent = scrollableParent.parentElement;
|
|
}
|
|
if (scrollableParent.dataset.headlessuiPortal === "") {
|
|
e.preventDefault();
|
|
}
|
|
} else {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
},
|
|
{ passive: false }
|
|
);
|
|
d.add(() => {
|
|
var _a3;
|
|
let newScrollPosition = (_a3 = window.scrollY) != null ? _a3 : window.pageYOffset;
|
|
if (scrollPosition !== newScrollPosition) {
|
|
window.scrollTo(0, scrollPosition);
|
|
}
|
|
if (scrollToElement && scrollToElement.isConnected) {
|
|
scrollToElement.scrollIntoView({ block: "nearest" });
|
|
scrollToElement = null;
|
|
}
|
|
});
|
|
});
|
|
}
|
|
};
|
|
}
|
|
|
|
// src/hooks/document-overflow/prevent-scroll.ts
|
|
function preventScroll() {
|
|
return {
|
|
before({ doc, d }) {
|
|
d.style(doc.documentElement, "overflow", "hidden");
|
|
}
|
|
};
|
|
}
|
|
|
|
// src/hooks/document-overflow/overflow-store.ts
|
|
function buildMeta(fns) {
|
|
let tmp = {};
|
|
for (let fn of fns) {
|
|
Object.assign(tmp, fn(tmp));
|
|
}
|
|
return tmp;
|
|
}
|
|
var overflows = createStore(() => /* @__PURE__ */ new Map(), {
|
|
PUSH(doc, meta) {
|
|
var _a2;
|
|
let entry = (_a2 = this.get(doc)) != null ? _a2 : {
|
|
doc,
|
|
count: 0,
|
|
d: disposables(),
|
|
meta: /* @__PURE__ */ new Set()
|
|
};
|
|
entry.count++;
|
|
entry.meta.add(meta);
|
|
this.set(doc, entry);
|
|
return this;
|
|
},
|
|
POP(doc, meta) {
|
|
let entry = this.get(doc);
|
|
if (entry) {
|
|
entry.count--;
|
|
entry.meta.delete(meta);
|
|
}
|
|
return this;
|
|
},
|
|
SCROLL_PREVENT({ doc, d, meta }) {
|
|
let ctx = {
|
|
doc,
|
|
d,
|
|
meta: buildMeta(meta)
|
|
};
|
|
let steps = [
|
|
handleIOSLocking(),
|
|
adjustScrollbarPadding(),
|
|
preventScroll()
|
|
];
|
|
steps.forEach(({ before }) => before == null ? void 0 : before(ctx));
|
|
steps.forEach(({ after }) => after == null ? void 0 : after(ctx));
|
|
},
|
|
SCROLL_ALLOW({ d }) {
|
|
d.dispose();
|
|
},
|
|
TEARDOWN({ doc }) {
|
|
this.delete(doc);
|
|
}
|
|
});
|
|
overflows.subscribe(() => {
|
|
let docs = overflows.getSnapshot();
|
|
let styles = /* @__PURE__ */ new Map();
|
|
for (let [doc] of docs) {
|
|
styles.set(doc, doc.documentElement.style.overflow);
|
|
}
|
|
for (let entry of docs.values()) {
|
|
let isHidden = styles.get(entry.doc) === "hidden";
|
|
let isLocked = entry.count !== 0;
|
|
let willChange = isLocked && !isHidden || !isLocked && isHidden;
|
|
if (willChange) {
|
|
overflows.dispatch(entry.count > 0 ? "SCROLL_PREVENT" : "SCROLL_ALLOW", entry);
|
|
}
|
|
if (entry.count === 0) {
|
|
overflows.dispatch("TEARDOWN", entry);
|
|
}
|
|
}
|
|
});
|
|
|
|
// src/hooks/document-overflow/use-document-overflow.ts
|
|
function useDocumentOverflowLockedEffect(doc, shouldBeLocked, meta) {
|
|
let store = useStore(overflows);
|
|
let locked = (0, import_vue19.computed)(() => {
|
|
let entry = doc.value ? store.value.get(doc.value) : void 0;
|
|
return entry ? entry.count > 0 : false;
|
|
});
|
|
(0, import_vue19.watch)(
|
|
[doc, shouldBeLocked],
|
|
([doc2, shouldBeLocked2], [oldDoc], onInvalidate) => {
|
|
if (!doc2 || !shouldBeLocked2) {
|
|
return;
|
|
}
|
|
overflows.dispatch("PUSH", doc2, meta);
|
|
let didRunCleanup = false;
|
|
onInvalidate(() => {
|
|
if (didRunCleanup)
|
|
return;
|
|
overflows.dispatch("POP", oldDoc != null ? oldDoc : doc2, meta);
|
|
didRunCleanup = true;
|
|
});
|
|
},
|
|
{
|
|
immediate: true
|
|
}
|
|
);
|
|
return locked;
|
|
}
|
|
|
|
// src/hooks/use-inert.ts
|
|
var import_vue20 = require("vue");
|
|
var originals = /* @__PURE__ */ new Map();
|
|
var counts = /* @__PURE__ */ new Map();
|
|
function useInert(node, enabled = (0, import_vue20.ref)(true)) {
|
|
(0, import_vue20.watchEffect)((onInvalidate) => {
|
|
var _a2;
|
|
if (!enabled.value)
|
|
return;
|
|
let element = dom(node);
|
|
if (!element)
|
|
return;
|
|
onInvalidate(function cleanup() {
|
|
var _a3;
|
|
if (!element)
|
|
return;
|
|
let count2 = (_a3 = counts.get(element)) != null ? _a3 : 1;
|
|
if (count2 === 1)
|
|
counts.delete(element);
|
|
else
|
|
counts.set(element, count2 - 1);
|
|
if (count2 !== 1)
|
|
return;
|
|
let original = originals.get(element);
|
|
if (!original)
|
|
return;
|
|
if (original["aria-hidden"] === null)
|
|
element.removeAttribute("aria-hidden");
|
|
else
|
|
element.setAttribute("aria-hidden", original["aria-hidden"]);
|
|
element.inert = original.inert;
|
|
originals.delete(element);
|
|
});
|
|
let count = (_a2 = counts.get(element)) != null ? _a2 : 0;
|
|
counts.set(element, count + 1);
|
|
if (count !== 0)
|
|
return;
|
|
originals.set(element, {
|
|
"aria-hidden": element.getAttribute("aria-hidden"),
|
|
inert: element.inert
|
|
});
|
|
element.setAttribute("aria-hidden", "true");
|
|
element.inert = true;
|
|
});
|
|
}
|
|
|
|
// src/hooks/use-root-containers.ts
|
|
var import_vue21 = require("vue");
|
|
function useRootContainers({
|
|
defaultContainers = [],
|
|
portals,
|
|
mainTreeNodeRef: _mainTreeNodeRef
|
|
} = {}) {
|
|
let mainTreeNodeRef = (0, import_vue21.ref)(null);
|
|
let ownerDocument = getOwnerDocument(mainTreeNodeRef);
|
|
function resolveContainers2() {
|
|
var _a2, _b, _c;
|
|
let containers = [];
|
|
for (let container of defaultContainers) {
|
|
if (container === null)
|
|
continue;
|
|
if (container instanceof HTMLElement) {
|
|
containers.push(container);
|
|
} else if ("value" in container && container.value instanceof HTMLElement) {
|
|
containers.push(container.value);
|
|
}
|
|
}
|
|
if (portals == null ? void 0 : portals.value) {
|
|
for (let portal of portals.value) {
|
|
containers.push(portal);
|
|
}
|
|
}
|
|
for (let container of (_a2 = ownerDocument == null ? void 0 : ownerDocument.querySelectorAll("html > *, body > *")) != null ? _a2 : []) {
|
|
if (container === document.body)
|
|
continue;
|
|
if (container === document.head)
|
|
continue;
|
|
if (!(container instanceof HTMLElement))
|
|
continue;
|
|
if (container.id === "headlessui-portal-root")
|
|
continue;
|
|
if (container.contains(dom(mainTreeNodeRef)))
|
|
continue;
|
|
if (container.contains((_c = (_b = dom(mainTreeNodeRef)) == null ? void 0 : _b.getRootNode()) == null ? void 0 : _c.host))
|
|
continue;
|
|
if (containers.some((defaultContainer) => container.contains(defaultContainer)))
|
|
continue;
|
|
containers.push(container);
|
|
}
|
|
return containers;
|
|
}
|
|
return {
|
|
resolveContainers: resolveContainers2,
|
|
contains(element) {
|
|
return resolveContainers2().some((container) => container.contains(element));
|
|
},
|
|
mainTreeNodeRef,
|
|
MainTreeNode() {
|
|
if (_mainTreeNodeRef != null)
|
|
return null;
|
|
return (0, import_vue21.h)(Hidden, { features: 4 /* Hidden */, ref: mainTreeNodeRef });
|
|
}
|
|
};
|
|
}
|
|
function useMainTreeNode() {
|
|
let mainTreeNodeRef = (0, import_vue21.ref)(null);
|
|
return {
|
|
mainTreeNodeRef,
|
|
MainTreeNode() {
|
|
return (0, import_vue21.h)(Hidden, { features: 4 /* Hidden */, ref: mainTreeNodeRef });
|
|
}
|
|
};
|
|
}
|
|
|
|
// src/internal/portal-force-root.ts
|
|
var import_vue22 = require("vue");
|
|
var ForcePortalRootContext = Symbol("ForcePortalRootContext");
|
|
function usePortalRoot() {
|
|
return (0, import_vue22.inject)(ForcePortalRootContext, false);
|
|
}
|
|
var ForcePortalRoot = (0, import_vue22.defineComponent)({
|
|
name: "ForcePortalRoot",
|
|
props: {
|
|
as: { type: [Object, String], default: "template" },
|
|
force: { type: Boolean, default: false }
|
|
},
|
|
setup(props, { slots, attrs }) {
|
|
(0, import_vue22.provide)(ForcePortalRootContext, props.force);
|
|
return () => {
|
|
let { force, ...theirProps } = props;
|
|
return render({
|
|
theirProps,
|
|
ourProps: {},
|
|
slot: {},
|
|
slots,
|
|
attrs,
|
|
name: "ForcePortalRoot"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
|
|
// src/internal/stack-context.ts
|
|
var import_vue23 = require("vue");
|
|
var StackContext = Symbol("StackContext");
|
|
function useStackContext() {
|
|
return (0, import_vue23.inject)(StackContext, () => {
|
|
});
|
|
}
|
|
function useStackProvider({
|
|
type,
|
|
enabled,
|
|
element,
|
|
onUpdate
|
|
}) {
|
|
let parentUpdate = useStackContext();
|
|
function notify(...args) {
|
|
onUpdate == null ? void 0 : onUpdate(...args);
|
|
parentUpdate(...args);
|
|
}
|
|
(0, import_vue23.onMounted)(() => {
|
|
(0, import_vue23.watch)(
|
|
enabled,
|
|
(isEnabled, oldIsEnabled) => {
|
|
if (isEnabled) {
|
|
notify(0 /* Add */, type, element);
|
|
} else if (oldIsEnabled === true) {
|
|
notify(1 /* Remove */, type, element);
|
|
}
|
|
},
|
|
{ immediate: true, flush: "sync" }
|
|
);
|
|
});
|
|
(0, import_vue23.onUnmounted)(() => {
|
|
if (enabled.value) {
|
|
notify(1 /* Remove */, type, element);
|
|
}
|
|
});
|
|
(0, import_vue23.provide)(StackContext, notify);
|
|
}
|
|
|
|
// src/components/description/description.ts
|
|
var import_vue24 = require("vue");
|
|
var DescriptionContext = Symbol("DescriptionContext");
|
|
function useDescriptionContext() {
|
|
let context = (0, import_vue24.inject)(DescriptionContext, null);
|
|
if (context === null) {
|
|
throw new Error("Missing parent");
|
|
}
|
|
return context;
|
|
}
|
|
function useDescriptions({
|
|
slot = (0, import_vue24.ref)({}),
|
|
name = "Description",
|
|
props = {}
|
|
} = {}) {
|
|
let descriptionIds = (0, import_vue24.ref)([]);
|
|
function register(value) {
|
|
descriptionIds.value.push(value);
|
|
return () => {
|
|
let idx = descriptionIds.value.indexOf(value);
|
|
if (idx === -1)
|
|
return;
|
|
descriptionIds.value.splice(idx, 1);
|
|
};
|
|
}
|
|
(0, import_vue24.provide)(DescriptionContext, { register, slot, name, props });
|
|
return (0, import_vue24.computed)(
|
|
() => descriptionIds.value.length > 0 ? descriptionIds.value.join(" ") : void 0
|
|
);
|
|
}
|
|
var Description = (0, import_vue24.defineComponent)({
|
|
name: "Description",
|
|
props: {
|
|
as: { type: [Object, String], default: "p" },
|
|
id: { type: String, default: null }
|
|
},
|
|
setup(myProps, { attrs, slots }) {
|
|
var _a2;
|
|
let id = (_a2 = myProps.id) != null ? _a2 : `headlessui-description-${useId2()}`;
|
|
let context = useDescriptionContext();
|
|
(0, import_vue24.onMounted)(() => (0, import_vue24.onUnmounted)(context.register(id)));
|
|
return () => {
|
|
let { name = "Description", slot = (0, import_vue24.ref)({}), props = {} } = context;
|
|
let { ...theirProps } = myProps;
|
|
let ourProps = {
|
|
...Object.entries(props).reduce(
|
|
(acc, [key, value]) => Object.assign(acc, { [key]: (0, import_vue24.unref)(value) }),
|
|
{}
|
|
),
|
|
id
|
|
};
|
|
return render({
|
|
ourProps,
|
|
theirProps,
|
|
slot: slot.value,
|
|
attrs,
|
|
slots,
|
|
name
|
|
});
|
|
};
|
|
}
|
|
});
|
|
|
|
// src/components/portal/portal.ts
|
|
var import_vue25 = require("vue");
|
|
function getPortalRoot(contextElement) {
|
|
let ownerDocument = getOwnerDocument(contextElement);
|
|
if (!ownerDocument) {
|
|
if (contextElement === null) {
|
|
return null;
|
|
}
|
|
throw new Error(
|
|
`[Headless UI]: Cannot find ownerDocument for contextElement: ${contextElement}`
|
|
);
|
|
}
|
|
let existingRoot = ownerDocument.getElementById("headlessui-portal-root");
|
|
if (existingRoot)
|
|
return existingRoot;
|
|
let root = ownerDocument.createElement("div");
|
|
root.setAttribute("id", "headlessui-portal-root");
|
|
return ownerDocument.body.appendChild(root);
|
|
}
|
|
var counter = /* @__PURE__ */ new WeakMap();
|
|
function getCount(el) {
|
|
var _a2;
|
|
return (_a2 = counter.get(el)) != null ? _a2 : 0;
|
|
}
|
|
function setCount(el, cb) {
|
|
let newCount = cb(getCount(el));
|
|
if (newCount <= 0) {
|
|
counter.delete(el);
|
|
} else {
|
|
counter.set(el, newCount);
|
|
}
|
|
return newCount;
|
|
}
|
|
var Portal = (0, import_vue25.defineComponent)({
|
|
name: "Portal",
|
|
props: {
|
|
as: { type: [Object, String], default: "div" }
|
|
},
|
|
setup(props, { slots, attrs }) {
|
|
let element = (0, import_vue25.ref)(null);
|
|
let ownerDocument = (0, import_vue25.computed)(() => getOwnerDocument(element));
|
|
let forcePortalRoot = usePortalRoot();
|
|
let groupContext = (0, import_vue25.inject)(PortalGroupContext, null);
|
|
let myTarget = (0, import_vue25.ref)(
|
|
forcePortalRoot === true ? getPortalRoot(element.value) : groupContext == null ? getPortalRoot(element.value) : groupContext.resolveTarget()
|
|
);
|
|
if (myTarget.value) {
|
|
setCount(myTarget.value, (val) => val + 1);
|
|
}
|
|
let ready = (0, import_vue25.ref)(false);
|
|
(0, import_vue25.onMounted)(() => {
|
|
ready.value = true;
|
|
});
|
|
(0, import_vue25.watchEffect)(() => {
|
|
if (forcePortalRoot)
|
|
return;
|
|
if (groupContext == null)
|
|
return;
|
|
myTarget.value = groupContext.resolveTarget();
|
|
});
|
|
let parent = (0, import_vue25.inject)(PortalParentContext, null);
|
|
let didRegister = false;
|
|
let instance = (0, import_vue25.getCurrentInstance)();
|
|
(0, import_vue25.watch)(element, () => {
|
|
if (didRegister)
|
|
return;
|
|
if (!parent)
|
|
return;
|
|
let domElement = dom(element);
|
|
if (!domElement)
|
|
return;
|
|
(0, import_vue25.onUnmounted)(parent.register(domElement), instance);
|
|
didRegister = true;
|
|
});
|
|
(0, import_vue25.onUnmounted)(() => {
|
|
var _a2, _b;
|
|
let root = (_a2 = ownerDocument.value) == null ? void 0 : _a2.getElementById("headlessui-portal-root");
|
|
if (!root)
|
|
return;
|
|
if (myTarget.value !== root)
|
|
return;
|
|
let remaining = setCount(myTarget.value, (val) => val - 1);
|
|
if (remaining)
|
|
return;
|
|
if (myTarget.value.children.length > 0)
|
|
return;
|
|
(_b = myTarget.value.parentElement) == null ? void 0 : _b.removeChild(myTarget.value);
|
|
});
|
|
return () => {
|
|
if (!ready.value)
|
|
return null;
|
|
if (myTarget.value === null)
|
|
return null;
|
|
let ourProps = {
|
|
ref: element,
|
|
"data-headlessui-portal": ""
|
|
};
|
|
return (0, import_vue25.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.
|
|
import_vue25.Teleport,
|
|
{ to: myTarget.value },
|
|
render({
|
|
ourProps,
|
|
theirProps: props,
|
|
slot: {},
|
|
attrs,
|
|
slots,
|
|
name: "Portal"
|
|
})
|
|
);
|
|
};
|
|
}
|
|
});
|
|
var PortalParentContext = Symbol("PortalParentContext");
|
|
function useNestedPortals() {
|
|
let parent = (0, import_vue25.inject)(PortalParentContext, null);
|
|
let portals = (0, import_vue25.ref)([]);
|
|
function register(portal) {
|
|
portals.value.push(portal);
|
|
if (parent)
|
|
parent.register(portal);
|
|
return () => unregister(portal);
|
|
}
|
|
function unregister(portal) {
|
|
let idx = portals.value.indexOf(portal);
|
|
if (idx !== -1)
|
|
portals.value.splice(idx, 1);
|
|
if (parent)
|
|
parent.unregister(portal);
|
|
}
|
|
let api = {
|
|
register,
|
|
unregister,
|
|
portals
|
|
};
|
|
return [
|
|
portals,
|
|
(0, import_vue25.defineComponent)({
|
|
name: "PortalWrapper",
|
|
setup(_, { slots }) {
|
|
(0, import_vue25.provide)(PortalParentContext, api);
|
|
return () => {
|
|
var _a2;
|
|
return (_a2 = slots.default) == null ? void 0 : _a2.call(slots);
|
|
};
|
|
}
|
|
})
|
|
];
|
|
}
|
|
var PortalGroupContext = Symbol("PortalGroupContext");
|
|
var PortalGroup = (0, import_vue25.defineComponent)({
|
|
name: "PortalGroup",
|
|
props: {
|
|
as: { type: [Object, String], default: "template" },
|
|
target: { type: Object, default: null }
|
|
},
|
|
setup(props, { attrs, slots }) {
|
|
let api = (0, import_vue25.reactive)({
|
|
resolveTarget() {
|
|
return props.target;
|
|
}
|
|
});
|
|
(0, import_vue25.provide)(PortalGroupContext, api);
|
|
return () => {
|
|
let { target: _, ...theirProps } = props;
|
|
return render({
|
|
theirProps,
|
|
ourProps: {},
|
|
slot: {},
|
|
attrs,
|
|
slots,
|
|
name: "PortalGroup"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
|
|
// src/components/dialog/dialog.ts
|
|
var DialogContext = Symbol("DialogContext");
|
|
function useDialogContext(component) {
|
|
let context = (0, import_vue26.inject)(DialogContext, null);
|
|
if (context === null) {
|
|
let 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 = (0, import_vue26.defineComponent)({
|
|
name: "Dialog",
|
|
inheritAttrs: false,
|
|
// Manually handling this
|
|
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 },
|
|
id: { type: String, default: null },
|
|
role: { type: String, default: "dialog" }
|
|
},
|
|
emits: { close: (_close) => true },
|
|
setup(props, { emit, attrs, slots, expose }) {
|
|
var _a2, _b;
|
|
let id = (_a2 = props.id) != null ? _a2 : `headlessui-dialog-${useId2()}`;
|
|
let ready = (0, import_vue26.ref)(false);
|
|
(0, import_vue26.onMounted)(() => {
|
|
ready.value = true;
|
|
});
|
|
let didWarnOnRole = false;
|
|
let role = (0, import_vue26.computed)(() => {
|
|
if (props.role === "dialog" || props.role === "alertdialog") {
|
|
return props.role;
|
|
}
|
|
if (!didWarnOnRole) {
|
|
didWarnOnRole = true;
|
|
console.warn(
|
|
`Invalid role [${role}] passed to <Dialog />. Only \`dialog\` and and \`alertdialog\` are supported. Using \`dialog\` instead.`
|
|
);
|
|
}
|
|
return "dialog";
|
|
});
|
|
let nestedDialogCount = (0, import_vue26.ref)(0);
|
|
let usesOpenClosedState = useOpenClosed();
|
|
let open = (0, import_vue26.computed)(() => {
|
|
if (props.open === Missing && usesOpenClosedState !== null) {
|
|
return (usesOpenClosedState.value & 1 /* Open */) === 1 /* Open */;
|
|
}
|
|
return props.open;
|
|
});
|
|
let internalDialogRef = (0, import_vue26.ref)(null);
|
|
let ownerDocument = (0, import_vue26.computed)(() => getOwnerDocument(internalDialogRef));
|
|
expose({ el: internalDialogRef, $el: internalDialogRef });
|
|
let 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 ? void 0 : props.open}`
|
|
);
|
|
}
|
|
let dialogState = (0, import_vue26.computed)(
|
|
() => !ready.value ? 1 /* Closed */ : open.value ? 0 /* Open */ : 1 /* Closed */
|
|
);
|
|
let enabled = (0, import_vue26.computed)(() => dialogState.value === 0 /* Open */);
|
|
let hasNestedDialogs = (0, import_vue26.computed)(() => nestedDialogCount.value > 1);
|
|
let hasParentDialog = (0, import_vue26.inject)(DialogContext, null) !== null;
|
|
let [portals, PortalWrapper] = useNestedPortals();
|
|
let {
|
|
resolveContainers: resolveRootContainers,
|
|
mainTreeNodeRef,
|
|
MainTreeNode
|
|
} = useRootContainers({
|
|
portals,
|
|
defaultContainers: [(0, import_vue26.computed)(() => {
|
|
var _a3;
|
|
return (_a3 = api.panelRef.value) != null ? _a3 : internalDialogRef.value;
|
|
})]
|
|
});
|
|
let position = (0, import_vue26.computed)(() => !hasNestedDialogs.value ? "leaf" : "parent");
|
|
let isClosing = (0, import_vue26.computed)(
|
|
() => usesOpenClosedState !== null ? (usesOpenClosedState.value & 4 /* Closing */) === 4 /* Closing */ : false
|
|
);
|
|
let inertOthersEnabled = (0, import_vue26.computed)(() => {
|
|
if (hasParentDialog)
|
|
return false;
|
|
if (isClosing.value)
|
|
return false;
|
|
return enabled.value;
|
|
});
|
|
let resolveRootOfMainTreeNode = (0, import_vue26.computed)(() => {
|
|
var _a3, _b2, _c;
|
|
return (_c = Array.from((_b2 = (_a3 = ownerDocument.value) == null ? void 0 : _a3.querySelectorAll("body > *")) != null ? _b2 : []).find((root) => {
|
|
if (root.id === "headlessui-portal-root")
|
|
return false;
|
|
return root.contains(dom(mainTreeNodeRef)) && root instanceof HTMLElement;
|
|
})) != null ? _c : null;
|
|
});
|
|
useInert(resolveRootOfMainTreeNode, inertOthersEnabled);
|
|
let inertParentDialogs = (0, import_vue26.computed)(() => {
|
|
if (hasNestedDialogs.value)
|
|
return true;
|
|
return enabled.value;
|
|
});
|
|
let resolveRootOfParentDialog = (0, import_vue26.computed)(() => {
|
|
var _a3, _b2, _c;
|
|
return (_c = Array.from(
|
|
(_b2 = (_a3 = ownerDocument.value) == null ? void 0 : _a3.querySelectorAll("[data-headlessui-portal]")) != null ? _b2 : []
|
|
).find((root) => root.contains(dom(mainTreeNodeRef)) && root instanceof HTMLElement)) != null ? _c : null;
|
|
});
|
|
useInert(resolveRootOfParentDialog, inertParentDialogs);
|
|
useStackProvider({
|
|
type: "Dialog",
|
|
enabled: (0, import_vue26.computed)(() => dialogState.value === 0 /* Open */),
|
|
element: internalDialogRef,
|
|
onUpdate: (message, type) => {
|
|
if (type !== "Dialog")
|
|
return;
|
|
return match(message, {
|
|
[0 /* Add */]: () => nestedDialogCount.value += 1,
|
|
[1 /* Remove */]: () => nestedDialogCount.value -= 1
|
|
});
|
|
}
|
|
});
|
|
let describedby = useDescriptions({
|
|
name: "DialogDescription",
|
|
slot: (0, import_vue26.computed)(() => ({ open: open.value }))
|
|
});
|
|
let titleId = (0, import_vue26.ref)(null);
|
|
let api = {
|
|
titleId,
|
|
panelRef: (0, import_vue26.ref)(null),
|
|
dialogState,
|
|
setTitleId(id2) {
|
|
if (titleId.value === id2)
|
|
return;
|
|
titleId.value = id2;
|
|
},
|
|
close() {
|
|
emit("close", false);
|
|
}
|
|
};
|
|
(0, import_vue26.provide)(DialogContext, api);
|
|
let outsideClickEnabled = (0, import_vue26.computed)(() => {
|
|
if (!enabled.value)
|
|
return false;
|
|
if (hasNestedDialogs.value)
|
|
return false;
|
|
return true;
|
|
});
|
|
useOutsideClick(
|
|
resolveRootContainers,
|
|
(event, target) => {
|
|
event.preventDefault();
|
|
api.close();
|
|
(0, import_vue26.nextTick)(() => target == null ? void 0 : target.focus());
|
|
},
|
|
outsideClickEnabled
|
|
);
|
|
let escapeToCloseEnabled = (0, import_vue26.computed)(() => {
|
|
if (hasNestedDialogs.value)
|
|
return false;
|
|
if (dialogState.value !== 0 /* Open */)
|
|
return false;
|
|
return true;
|
|
});
|
|
useEventListener((_b = ownerDocument.value) == null ? void 0 : _b.defaultView, "keydown", (event) => {
|
|
if (!escapeToCloseEnabled.value)
|
|
return;
|
|
if (event.defaultPrevented)
|
|
return;
|
|
if (event.key !== "Escape" /* Escape */)
|
|
return;
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
api.close();
|
|
});
|
|
let scrollLockEnabled = (0, import_vue26.computed)(() => {
|
|
if (isClosing.value)
|
|
return false;
|
|
if (dialogState.value !== 0 /* Open */)
|
|
return false;
|
|
if (hasParentDialog)
|
|
return false;
|
|
return true;
|
|
});
|
|
useDocumentOverflowLockedEffect(ownerDocument, scrollLockEnabled, (meta) => {
|
|
var _a3;
|
|
return {
|
|
containers: [...(_a3 = meta.containers) != null ? _a3 : [], resolveRootContainers]
|
|
};
|
|
});
|
|
(0, import_vue26.watchEffect)((onInvalidate) => {
|
|
if (dialogState.value !== 0 /* Open */)
|
|
return;
|
|
let container = dom(internalDialogRef);
|
|
if (!container)
|
|
return;
|
|
let observer = new ResizeObserver((entries) => {
|
|
for (let entry of entries) {
|
|
let rect = entry.target.getBoundingClientRect();
|
|
if (rect.x === 0 && rect.y === 0 && rect.width === 0 && rect.height === 0) {
|
|
api.close();
|
|
}
|
|
}
|
|
});
|
|
observer.observe(container);
|
|
onInvalidate(() => observer.disconnect());
|
|
});
|
|
return () => {
|
|
let { open: _, initialFocus, ...theirProps } = props;
|
|
let ourProps = {
|
|
// Manually passthrough the attributes, because Vue can't automatically pass
|
|
// it to the underlying div because of all the wrapper components below.
|
|
...attrs,
|
|
ref: internalDialogRef,
|
|
id,
|
|
role: role.value,
|
|
"aria-modal": dialogState.value === 0 /* Open */ ? true : void 0,
|
|
"aria-labelledby": titleId.value,
|
|
"aria-describedby": describedby.value
|
|
};
|
|
let slot = { open: dialogState.value === 0 /* Open */ };
|
|
return (0, import_vue26.h)(ForcePortalRoot, { force: true }, () => [
|
|
(0, import_vue26.h)(
|
|
Portal,
|
|
() => (0, import_vue26.h)(
|
|
PortalGroup,
|
|
{ target: internalDialogRef.value },
|
|
() => (0, import_vue26.h)(
|
|
ForcePortalRoot,
|
|
{ force: false },
|
|
() => (0, import_vue26.h)(
|
|
FocusTrap,
|
|
{
|
|
initialFocus,
|
|
containers: resolveRootContainers,
|
|
features: enabled.value ? match(position.value, {
|
|
parent: FocusTrap.features.RestoreFocus,
|
|
leaf: FocusTrap.features.All & ~FocusTrap.features.FocusLock
|
|
}) : FocusTrap.features.None
|
|
},
|
|
() => (0, import_vue26.h)(
|
|
PortalWrapper,
|
|
{},
|
|
() => render({
|
|
ourProps,
|
|
theirProps: { ...theirProps, ...attrs },
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
visible: dialogState.value === 0 /* Open */,
|
|
features: 1 /* RenderStrategy */ | 2 /* Static */,
|
|
name: "Dialog"
|
|
})
|
|
)
|
|
)
|
|
)
|
|
)
|
|
),
|
|
(0, import_vue26.h)(MainTreeNode)
|
|
]);
|
|
};
|
|
}
|
|
});
|
|
var DialogOverlay = (0, import_vue26.defineComponent)({
|
|
name: "DialogOverlay",
|
|
props: {
|
|
as: { type: [Object, String], default: "div" },
|
|
id: { type: String, default: null }
|
|
},
|
|
setup(props, { attrs, slots }) {
|
|
var _a2;
|
|
let id = (_a2 = props.id) != null ? _a2 : `headlessui-dialog-overlay-${useId2()}`;
|
|
let api = useDialogContext("DialogOverlay");
|
|
function handleClick(event) {
|
|
if (event.target !== event.currentTarget)
|
|
return;
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
api.close();
|
|
}
|
|
return () => {
|
|
let { ...theirProps } = props;
|
|
let ourProps = {
|
|
id,
|
|
"aria-hidden": true,
|
|
onClick: handleClick
|
|
};
|
|
return render({
|
|
ourProps,
|
|
theirProps,
|
|
slot: { open: api.dialogState.value === 0 /* Open */ },
|
|
attrs,
|
|
slots,
|
|
name: "DialogOverlay"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
var DialogBackdrop = (0, import_vue26.defineComponent)({
|
|
name: "DialogBackdrop",
|
|
props: {
|
|
as: { type: [Object, String], default: "div" },
|
|
id: { type: String, default: null }
|
|
},
|
|
inheritAttrs: false,
|
|
setup(props, { attrs, slots, expose }) {
|
|
var _a2;
|
|
let id = (_a2 = props.id) != null ? _a2 : `headlessui-dialog-backdrop-${useId2()}`;
|
|
let api = useDialogContext("DialogBackdrop");
|
|
let internalBackdropRef = (0, import_vue26.ref)(null);
|
|
expose({ el: internalBackdropRef, $el: internalBackdropRef });
|
|
(0, import_vue26.onMounted)(() => {
|
|
if (api.panelRef.value === null) {
|
|
throw new Error(
|
|
`A <DialogBackdrop /> component is being used, but a <DialogPanel /> component is missing.`
|
|
);
|
|
}
|
|
});
|
|
return () => {
|
|
let { ...theirProps } = props;
|
|
let ourProps = {
|
|
id,
|
|
ref: internalBackdropRef,
|
|
"aria-hidden": true
|
|
};
|
|
return (0, import_vue26.h)(
|
|
ForcePortalRoot,
|
|
{ force: true },
|
|
() => (0, import_vue26.h)(
|
|
Portal,
|
|
() => render({
|
|
ourProps,
|
|
theirProps: { ...attrs, ...theirProps },
|
|
slot: { open: api.dialogState.value === 0 /* Open */ },
|
|
attrs,
|
|
slots,
|
|
name: "DialogBackdrop"
|
|
})
|
|
)
|
|
);
|
|
};
|
|
}
|
|
});
|
|
var DialogPanel = (0, import_vue26.defineComponent)({
|
|
name: "DialogPanel",
|
|
props: {
|
|
as: { type: [Object, String], default: "div" },
|
|
id: { type: String, default: null }
|
|
},
|
|
setup(props, { attrs, slots, expose }) {
|
|
var _a2;
|
|
let id = (_a2 = props.id) != null ? _a2 : `headlessui-dialog-panel-${useId2()}`;
|
|
let api = useDialogContext("DialogPanel");
|
|
expose({ el: api.panelRef, $el: api.panelRef });
|
|
function handleClick(event) {
|
|
event.stopPropagation();
|
|
}
|
|
return () => {
|
|
let { ...theirProps } = props;
|
|
let ourProps = {
|
|
id,
|
|
ref: api.panelRef,
|
|
onClick: handleClick
|
|
};
|
|
return render({
|
|
ourProps,
|
|
theirProps,
|
|
slot: { open: api.dialogState.value === 0 /* Open */ },
|
|
attrs,
|
|
slots,
|
|
name: "DialogPanel"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
var DialogTitle = (0, import_vue26.defineComponent)({
|
|
name: "DialogTitle",
|
|
props: {
|
|
as: { type: [Object, String], default: "h2" },
|
|
id: { type: String, default: null }
|
|
},
|
|
setup(props, { attrs, slots }) {
|
|
var _a2;
|
|
let id = (_a2 = props.id) != null ? _a2 : `headlessui-dialog-title-${useId2()}`;
|
|
let api = useDialogContext("DialogTitle");
|
|
(0, import_vue26.onMounted)(() => {
|
|
api.setTitleId(id);
|
|
(0, import_vue26.onUnmounted)(() => api.setTitleId(null));
|
|
});
|
|
return () => {
|
|
let { ...theirProps } = props;
|
|
let ourProps = { id };
|
|
return render({
|
|
ourProps,
|
|
theirProps,
|
|
slot: { open: api.dialogState.value === 0 /* Open */ },
|
|
attrs,
|
|
slots,
|
|
name: "DialogTitle"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
var DialogDescription = Description;
|
|
|
|
// src/components/disclosure/disclosure.ts
|
|
var import_vue27 = require("vue");
|
|
var DisclosureContext = Symbol("DisclosureContext");
|
|
function useDisclosureContext(component) {
|
|
let context = (0, import_vue27.inject)(DisclosureContext, null);
|
|
if (context === null) {
|
|
let err = new Error(`<${component} /> is missing a parent <Disclosure /> component.`);
|
|
if (Error.captureStackTrace)
|
|
Error.captureStackTrace(err, useDisclosureContext);
|
|
throw err;
|
|
}
|
|
return context;
|
|
}
|
|
var DisclosurePanelContext = Symbol("DisclosurePanelContext");
|
|
function useDisclosurePanelContext() {
|
|
return (0, import_vue27.inject)(DisclosurePanelContext, null);
|
|
}
|
|
var Disclosure = (0, import_vue27.defineComponent)({
|
|
name: "Disclosure",
|
|
props: {
|
|
as: { type: [Object, String], default: "template" },
|
|
defaultOpen: { type: [Boolean], default: false }
|
|
},
|
|
setup(props, { slots, attrs }) {
|
|
let disclosureState = (0, import_vue27.ref)(
|
|
props.defaultOpen ? 0 /* Open */ : 1 /* Closed */
|
|
);
|
|
let panelRef = (0, import_vue27.ref)(null);
|
|
let buttonRef = (0, import_vue27.ref)(null);
|
|
let api = {
|
|
buttonId: (0, import_vue27.ref)(`headlessui-disclosure-button-${useId2()}`),
|
|
panelId: (0, import_vue27.ref)(`headlessui-disclosure-panel-${useId2()}`),
|
|
disclosureState,
|
|
panel: panelRef,
|
|
button: buttonRef,
|
|
toggleDisclosure() {
|
|
disclosureState.value = match(disclosureState.value, {
|
|
[0 /* Open */]: 1 /* Closed */,
|
|
[1 /* Closed */]: 0 /* Open */
|
|
});
|
|
},
|
|
closeDisclosure() {
|
|
if (disclosureState.value === 1 /* Closed */)
|
|
return;
|
|
disclosureState.value = 1 /* Closed */;
|
|
},
|
|
close(focusableElement) {
|
|
api.closeDisclosure();
|
|
let restoreElement = (() => {
|
|
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();
|
|
}
|
|
};
|
|
(0, import_vue27.provide)(DisclosureContext, api);
|
|
useOpenClosedProvider(
|
|
(0, import_vue27.computed)(() => {
|
|
return match(disclosureState.value, {
|
|
[0 /* Open */]: 1 /* Open */,
|
|
[1 /* Closed */]: 2 /* Closed */
|
|
});
|
|
})
|
|
);
|
|
return () => {
|
|
let { defaultOpen: _, ...theirProps } = props;
|
|
let slot = { open: disclosureState.value === 0 /* Open */, close: api.close };
|
|
return render({
|
|
theirProps,
|
|
ourProps: {},
|
|
slot,
|
|
slots,
|
|
attrs,
|
|
name: "Disclosure"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
var DisclosureButton = (0, import_vue27.defineComponent)({
|
|
name: "DisclosureButton",
|
|
props: {
|
|
as: { type: [Object, String], default: "button" },
|
|
disabled: { type: [Boolean], default: false },
|
|
id: { type: String, default: null }
|
|
},
|
|
setup(props, { attrs, slots, expose }) {
|
|
let api = useDisclosureContext("DisclosureButton");
|
|
let panelContext = useDisclosurePanelContext();
|
|
let isWithinPanel = (0, import_vue27.computed)(
|
|
() => panelContext === null ? false : panelContext.value === api.panelId.value
|
|
);
|
|
(0, import_vue27.onMounted)(() => {
|
|
if (isWithinPanel.value)
|
|
return;
|
|
if (props.id !== null) {
|
|
api.buttonId.value = props.id;
|
|
}
|
|
});
|
|
(0, import_vue27.onUnmounted)(() => {
|
|
if (isWithinPanel.value)
|
|
return;
|
|
api.buttonId.value = null;
|
|
});
|
|
let internalButtonRef = (0, import_vue27.ref)(null);
|
|
expose({ el: internalButtonRef, $el: internalButtonRef });
|
|
if (!isWithinPanel.value) {
|
|
(0, import_vue27.watchEffect)(() => {
|
|
api.button.value = internalButtonRef.value;
|
|
});
|
|
}
|
|
let type = useResolveButtonType(
|
|
(0, import_vue27.computed)(() => ({ as: props.as, type: attrs.type })),
|
|
internalButtonRef
|
|
);
|
|
function handleClick() {
|
|
var _a2;
|
|
if (props.disabled)
|
|
return;
|
|
if (isWithinPanel.value) {
|
|
api.toggleDisclosure();
|
|
(_a2 = dom(api.button)) == null ? void 0 : _a2.focus();
|
|
} else {
|
|
api.toggleDisclosure();
|
|
}
|
|
}
|
|
function handleKeyDown(event) {
|
|
var _a2;
|
|
if (props.disabled)
|
|
return;
|
|
if (isWithinPanel.value) {
|
|
switch (event.key) {
|
|
case " " /* Space */:
|
|
case "Enter" /* Enter */:
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
api.toggleDisclosure();
|
|
(_a2 = dom(api.button)) == null ? void 0 : _a2.focus();
|
|
break;
|
|
}
|
|
} else {
|
|
switch (event.key) {
|
|
case " " /* Space */:
|
|
case "Enter" /* Enter */:
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
api.toggleDisclosure();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
function handleKeyUp(event) {
|
|
switch (event.key) {
|
|
case " " /* Space */:
|
|
event.preventDefault();
|
|
break;
|
|
}
|
|
}
|
|
return () => {
|
|
var _a2;
|
|
let slot = { open: api.disclosureState.value === 0 /* Open */ };
|
|
let { id, ...theirProps } = props;
|
|
let ourProps = isWithinPanel.value ? {
|
|
ref: internalButtonRef,
|
|
type: type.value,
|
|
onClick: handleClick,
|
|
onKeydown: handleKeyDown
|
|
} : {
|
|
id: (_a2 = api.buttonId.value) != null ? _a2 : id,
|
|
ref: internalButtonRef,
|
|
type: type.value,
|
|
"aria-expanded": api.disclosureState.value === 0 /* Open */,
|
|
"aria-controls": api.disclosureState.value === 0 /* Open */ || dom(api.panel) ? api.panelId.value : void 0,
|
|
disabled: props.disabled ? true : void 0,
|
|
onClick: handleClick,
|
|
onKeydown: handleKeyDown,
|
|
onKeyup: handleKeyUp
|
|
};
|
|
return render({
|
|
ourProps,
|
|
theirProps,
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
name: "DisclosureButton"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
var DisclosurePanel = (0, import_vue27.defineComponent)({
|
|
name: "DisclosurePanel",
|
|
props: {
|
|
as: { type: [Object, String], default: "div" },
|
|
static: { type: Boolean, default: false },
|
|
unmount: { type: Boolean, default: true },
|
|
id: { type: String, default: null }
|
|
},
|
|
setup(props, { attrs, slots, expose }) {
|
|
let api = useDisclosureContext("DisclosurePanel");
|
|
(0, import_vue27.onMounted)(() => {
|
|
if (props.id !== null) {
|
|
api.panelId.value = props.id;
|
|
}
|
|
});
|
|
(0, import_vue27.onUnmounted)(() => {
|
|
api.panelId.value = null;
|
|
});
|
|
expose({ el: api.panel, $el: api.panel });
|
|
(0, import_vue27.provide)(DisclosurePanelContext, api.panelId);
|
|
let usesOpenClosedState = useOpenClosed();
|
|
let visible = (0, import_vue27.computed)(() => {
|
|
if (usesOpenClosedState !== null) {
|
|
return (usesOpenClosedState.value & 1 /* Open */) === 1 /* Open */;
|
|
}
|
|
return api.disclosureState.value === 0 /* Open */;
|
|
});
|
|
return () => {
|
|
var _a2;
|
|
let slot = { open: api.disclosureState.value === 0 /* Open */, close: api.close };
|
|
let { id, ...theirProps } = props;
|
|
let ourProps = { id: (_a2 = api.panelId.value) != null ? _a2 : id, ref: api.panel };
|
|
return render({
|
|
ourProps,
|
|
theirProps,
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
features: 1 /* RenderStrategy */ | 2 /* Static */,
|
|
visible: visible.value,
|
|
name: "DisclosurePanel"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
|
|
// src/components/listbox/listbox.ts
|
|
var import_vue29 = require("vue");
|
|
|
|
// src/hooks/use-text-value.ts
|
|
var import_vue28 = require("vue");
|
|
|
|
// src/utils/get-text-value.ts
|
|
var emojiRegex = /([\u2700-\u27BF]|[\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2011-\u26FF]|\uD83E[\uDD10-\uDDFF])/g;
|
|
function getTextContents(element) {
|
|
var _a2, _b;
|
|
let currentInnerText = (_a2 = element.innerText) != null ? _a2 : "";
|
|
let copy = element.cloneNode(true);
|
|
if (!(copy instanceof HTMLElement)) {
|
|
return currentInnerText;
|
|
}
|
|
let dropped = false;
|
|
for (let child of copy.querySelectorAll('[hidden],[aria-hidden],[role="img"]')) {
|
|
child.remove();
|
|
dropped = true;
|
|
}
|
|
let value = dropped ? (_b = copy.innerText) != null ? _b : "" : currentInnerText;
|
|
if (emojiRegex.test(value)) {
|
|
value = value.replace(emojiRegex, "");
|
|
}
|
|
return value;
|
|
}
|
|
function getTextValue(element) {
|
|
let label = element.getAttribute("aria-label");
|
|
if (typeof label === "string")
|
|
return label.trim();
|
|
let labelledby = element.getAttribute("aria-labelledby");
|
|
if (labelledby) {
|
|
let labels = labelledby.split(" ").map((labelledby2) => {
|
|
let labelEl = document.getElementById(labelledby2);
|
|
if (labelEl) {
|
|
let label2 = labelEl.getAttribute("aria-label");
|
|
if (typeof label2 === "string")
|
|
return label2.trim();
|
|
return getTextContents(labelEl).trim();
|
|
}
|
|
return null;
|
|
}).filter(Boolean);
|
|
if (labels.length > 0)
|
|
return labels.join(", ");
|
|
}
|
|
return getTextContents(element).trim();
|
|
}
|
|
|
|
// src/hooks/use-text-value.ts
|
|
function useTextValue(element) {
|
|
let cacheKey = (0, import_vue28.ref)("");
|
|
let cacheValue = (0, import_vue28.ref)("");
|
|
return () => {
|
|
let el = dom(element);
|
|
if (!el)
|
|
return "";
|
|
let currentKey = el.innerText;
|
|
if (cacheKey.value === currentKey) {
|
|
return cacheValue.value;
|
|
}
|
|
let value = getTextValue(el).trim().toLowerCase();
|
|
cacheKey.value = currentKey;
|
|
cacheValue.value = value;
|
|
return value;
|
|
};
|
|
}
|
|
|
|
// src/components/listbox/listbox.ts
|
|
function defaultComparator2(a, z) {
|
|
return a === z;
|
|
}
|
|
function nextFrame(cb) {
|
|
requestAnimationFrame(() => requestAnimationFrame(cb));
|
|
}
|
|
var ListboxContext = Symbol("ListboxContext");
|
|
function useListboxContext(component) {
|
|
let context = (0, import_vue29.inject)(ListboxContext, null);
|
|
if (context === null) {
|
|
let err = new Error(`<${component} /> is missing a parent <Listbox /> component.`);
|
|
if (Error.captureStackTrace)
|
|
Error.captureStackTrace(err, useListboxContext);
|
|
throw err;
|
|
}
|
|
return context;
|
|
}
|
|
var Listbox = (0, import_vue29.defineComponent)({
|
|
name: "Listbox",
|
|
emits: { "update:modelValue": (_value) => true },
|
|
props: {
|
|
as: { type: [Object, String], default: "template" },
|
|
disabled: { type: [Boolean], default: false },
|
|
by: { type: [String, Function], default: () => defaultComparator2 },
|
|
horizontal: { type: [Boolean], default: false },
|
|
modelValue: {
|
|
type: [Object, String, Number, Boolean],
|
|
default: void 0
|
|
},
|
|
defaultValue: {
|
|
type: [Object, String, Number, Boolean],
|
|
default: void 0
|
|
},
|
|
form: { type: String, optional: true },
|
|
name: { type: String, optional: true },
|
|
multiple: { type: [Boolean], default: false }
|
|
},
|
|
inheritAttrs: false,
|
|
setup(props, { slots, attrs, emit }) {
|
|
let listboxState = (0, import_vue29.ref)(1 /* Closed */);
|
|
let labelRef = (0, import_vue29.ref)(null);
|
|
let buttonRef = (0, import_vue29.ref)(null);
|
|
let optionsRef = (0, import_vue29.ref)(null);
|
|
let options = (0, import_vue29.ref)([]);
|
|
let searchQuery = (0, import_vue29.ref)("");
|
|
let activeOptionIndex = (0, import_vue29.ref)(null);
|
|
let activationTrigger = (0, import_vue29.ref)(
|
|
1 /* Other */
|
|
);
|
|
function adjustOrderedState(adjustment = (i) => i) {
|
|
let currentActiveOption = activeOptionIndex.value !== null ? options.value[activeOptionIndex.value] : null;
|
|
let sortedOptions = sortByDomNode(
|
|
adjustment(options.value.slice()),
|
|
(option) => dom(option.dataRef.domRef)
|
|
);
|
|
let adjustedActiveOptionIndex = currentActiveOption ? sortedOptions.indexOf(currentActiveOption) : null;
|
|
if (adjustedActiveOptionIndex === -1) {
|
|
adjustedActiveOptionIndex = null;
|
|
}
|
|
return {
|
|
options: sortedOptions,
|
|
activeOptionIndex: adjustedActiveOptionIndex
|
|
};
|
|
}
|
|
let mode = (0, import_vue29.computed)(() => props.multiple ? 1 /* Multi */ : 0 /* Single */);
|
|
let [directValue, theirOnChange] = useControllable(
|
|
(0, import_vue29.computed)(() => props.modelValue),
|
|
(value2) => emit("update:modelValue", value2),
|
|
(0, import_vue29.computed)(() => props.defaultValue)
|
|
);
|
|
let value = (0, import_vue29.computed)(
|
|
() => directValue.value === void 0 ? match(mode.value, {
|
|
[1 /* Multi */]: [],
|
|
[0 /* Single */]: void 0
|
|
}) : directValue.value
|
|
);
|
|
let api = {
|
|
listboxState,
|
|
value,
|
|
mode,
|
|
compare(a, z) {
|
|
if (typeof props.by === "string") {
|
|
let property = props.by;
|
|
return (a == null ? void 0 : a[property]) === (z == null ? void 0 : z[property]);
|
|
}
|
|
return props.by(a, z);
|
|
},
|
|
orientation: (0, import_vue29.computed)(() => props.horizontal ? "horizontal" : "vertical"),
|
|
labelRef,
|
|
buttonRef,
|
|
optionsRef,
|
|
disabled: (0, import_vue29.computed)(() => props.disabled),
|
|
options,
|
|
searchQuery,
|
|
activeOptionIndex,
|
|
activationTrigger,
|
|
closeListbox() {
|
|
if (props.disabled)
|
|
return;
|
|
if (listboxState.value === 1 /* Closed */)
|
|
return;
|
|
listboxState.value = 1 /* Closed */;
|
|
activeOptionIndex.value = null;
|
|
},
|
|
openListbox() {
|
|
if (props.disabled)
|
|
return;
|
|
if (listboxState.value === 0 /* Open */)
|
|
return;
|
|
listboxState.value = 0 /* Open */;
|
|
},
|
|
goToOption(focus, id, trigger) {
|
|
if (props.disabled)
|
|
return;
|
|
if (listboxState.value === 1 /* Closed */)
|
|
return;
|
|
let adjustedState = adjustOrderedState();
|
|
let nextActiveOptionIndex = calculateActiveIndex(
|
|
focus === 4 /* Specific */ ? { focus: 4 /* Specific */, id } : { focus },
|
|
{
|
|
resolveItems: () => adjustedState.options,
|
|
resolveActiveIndex: () => adjustedState.activeOptionIndex,
|
|
resolveId: (option) => option.id,
|
|
resolveDisabled: (option) => option.dataRef.disabled
|
|
}
|
|
);
|
|
searchQuery.value = "";
|
|
activeOptionIndex.value = nextActiveOptionIndex;
|
|
activationTrigger.value = trigger != null ? trigger : 1 /* Other */;
|
|
options.value = adjustedState.options;
|
|
},
|
|
search(value2) {
|
|
if (props.disabled)
|
|
return;
|
|
if (listboxState.value === 1 /* Closed */)
|
|
return;
|
|
let wasAlreadySearching = searchQuery.value !== "";
|
|
let offset = wasAlreadySearching ? 0 : 1;
|
|
searchQuery.value += value2.toLowerCase();
|
|
let reOrderedOptions = activeOptionIndex.value !== null ? options.value.slice(activeOptionIndex.value + offset).concat(options.value.slice(0, activeOptionIndex.value + offset)) : options.value;
|
|
let matchingOption = reOrderedOptions.find(
|
|
(option) => option.dataRef.textValue.startsWith(searchQuery.value) && !option.dataRef.disabled
|
|
);
|
|
let matchIdx = matchingOption ? options.value.indexOf(matchingOption) : -1;
|
|
if (matchIdx === -1 || matchIdx === activeOptionIndex.value)
|
|
return;
|
|
activeOptionIndex.value = matchIdx;
|
|
activationTrigger.value = 1 /* Other */;
|
|
},
|
|
clearSearch() {
|
|
if (props.disabled)
|
|
return;
|
|
if (listboxState.value === 1 /* Closed */)
|
|
return;
|
|
if (searchQuery.value === "")
|
|
return;
|
|
searchQuery.value = "";
|
|
},
|
|
registerOption(id, dataRef) {
|
|
let adjustedState = adjustOrderedState((options2) => {
|
|
return [...options2, { id, dataRef }];
|
|
});
|
|
options.value = adjustedState.options;
|
|
activeOptionIndex.value = adjustedState.activeOptionIndex;
|
|
},
|
|
unregisterOption(id) {
|
|
let adjustedState = adjustOrderedState((options2) => {
|
|
let idx = options2.findIndex((a) => a.id === id);
|
|
if (idx !== -1)
|
|
options2.splice(idx, 1);
|
|
return options2;
|
|
});
|
|
options.value = adjustedState.options;
|
|
activeOptionIndex.value = adjustedState.activeOptionIndex;
|
|
activationTrigger.value = 1 /* Other */;
|
|
},
|
|
theirOnChange(value2) {
|
|
if (props.disabled)
|
|
return;
|
|
theirOnChange(value2);
|
|
},
|
|
select(value2) {
|
|
if (props.disabled)
|
|
return;
|
|
theirOnChange(
|
|
match(mode.value, {
|
|
[0 /* Single */]: () => value2,
|
|
[1 /* Multi */]: () => {
|
|
let copy = (0, import_vue29.toRaw)(api.value.value).slice();
|
|
let raw = (0, import_vue29.toRaw)(value2);
|
|
let idx = copy.findIndex((value3) => api.compare(raw, (0, import_vue29.toRaw)(value3)));
|
|
if (idx === -1) {
|
|
copy.push(raw);
|
|
} else {
|
|
copy.splice(idx, 1);
|
|
}
|
|
return copy;
|
|
}
|
|
})
|
|
);
|
|
}
|
|
};
|
|
useOutsideClick(
|
|
[buttonRef, optionsRef],
|
|
(event, target) => {
|
|
var _a2;
|
|
api.closeListbox();
|
|
if (!isFocusableElement(target, 1 /* Loose */)) {
|
|
event.preventDefault();
|
|
(_a2 = dom(buttonRef)) == null ? void 0 : _a2.focus();
|
|
}
|
|
},
|
|
(0, import_vue29.computed)(() => listboxState.value === 0 /* Open */)
|
|
);
|
|
(0, import_vue29.provide)(ListboxContext, api);
|
|
useOpenClosedProvider(
|
|
(0, import_vue29.computed)(
|
|
() => match(listboxState.value, {
|
|
[0 /* Open */]: 1 /* Open */,
|
|
[1 /* Closed */]: 2 /* Closed */
|
|
})
|
|
)
|
|
);
|
|
let form = (0, import_vue29.computed)(() => {
|
|
var _a2;
|
|
return (_a2 = dom(buttonRef)) == null ? void 0 : _a2.closest("form");
|
|
});
|
|
(0, import_vue29.onMounted)(() => {
|
|
(0, import_vue29.watch)(
|
|
[form],
|
|
() => {
|
|
if (!form.value)
|
|
return;
|
|
if (props.defaultValue === void 0)
|
|
return;
|
|
function handle() {
|
|
api.theirOnChange(props.defaultValue);
|
|
}
|
|
form.value.addEventListener("reset", handle);
|
|
return () => {
|
|
var _a2;
|
|
(_a2 = form.value) == null ? void 0 : _a2.removeEventListener("reset", handle);
|
|
};
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
});
|
|
return () => {
|
|
let { name, modelValue, disabled, form: form2, ...theirProps } = props;
|
|
let slot = { open: listboxState.value === 0 /* Open */, disabled, value: value.value };
|
|
return (0, import_vue29.h)(import_vue29.Fragment, [
|
|
...name != null && value.value != null ? objectToFormEntries({ [name]: value.value }).map(
|
|
([name2, value2]) => (0, import_vue29.h)(
|
|
Hidden,
|
|
compact({
|
|
features: 4 /* Hidden */,
|
|
key: name2,
|
|
as: "input",
|
|
type: "hidden",
|
|
hidden: true,
|
|
readOnly: true,
|
|
form: form2,
|
|
disabled,
|
|
name: name2,
|
|
value: value2
|
|
})
|
|
)
|
|
) : [],
|
|
render({
|
|
ourProps: {},
|
|
theirProps: {
|
|
...attrs,
|
|
...omit(theirProps, [
|
|
"defaultValue",
|
|
"onUpdate:modelValue",
|
|
"horizontal",
|
|
"multiple",
|
|
"by"
|
|
])
|
|
},
|
|
slot,
|
|
slots,
|
|
attrs,
|
|
name: "Listbox"
|
|
})
|
|
]);
|
|
};
|
|
}
|
|
});
|
|
var ListboxLabel = (0, import_vue29.defineComponent)({
|
|
name: "ListboxLabel",
|
|
props: {
|
|
as: { type: [Object, String], default: "label" },
|
|
id: { type: String, default: null }
|
|
},
|
|
setup(props, { attrs, slots }) {
|
|
var _a2;
|
|
let id = (_a2 = props.id) != null ? _a2 : `headlessui-listbox-label-${useId2()}`;
|
|
let api = useListboxContext("ListboxLabel");
|
|
function handleClick() {
|
|
var _a3;
|
|
(_a3 = dom(api.buttonRef)) == null ? void 0 : _a3.focus({ preventScroll: true });
|
|
}
|
|
return () => {
|
|
let slot = {
|
|
open: api.listboxState.value === 0 /* Open */,
|
|
disabled: api.disabled.value
|
|
};
|
|
let { ...theirProps } = props;
|
|
let ourProps = { id, ref: api.labelRef, onClick: handleClick };
|
|
return render({
|
|
ourProps,
|
|
theirProps,
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
name: "ListboxLabel"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
var ListboxButton = (0, import_vue29.defineComponent)({
|
|
name: "ListboxButton",
|
|
props: {
|
|
as: { type: [Object, String], default: "button" },
|
|
id: { type: String, default: null }
|
|
},
|
|
setup(props, { attrs, slots, expose }) {
|
|
var _a2;
|
|
let id = (_a2 = props.id) != null ? _a2 : `headlessui-listbox-button-${useId2()}`;
|
|
let api = useListboxContext("ListboxButton");
|
|
expose({ el: api.buttonRef, $el: api.buttonRef });
|
|
function handleKeyDown(event) {
|
|
switch (event.key) {
|
|
case " " /* Space */:
|
|
case "Enter" /* Enter */:
|
|
case "ArrowDown" /* ArrowDown */:
|
|
event.preventDefault();
|
|
api.openListbox();
|
|
(0, import_vue29.nextTick)(() => {
|
|
var _a3;
|
|
(_a3 = dom(api.optionsRef)) == null ? void 0 : _a3.focus({ preventScroll: true });
|
|
if (!api.value.value)
|
|
api.goToOption(0 /* First */);
|
|
});
|
|
break;
|
|
case "ArrowUp" /* ArrowUp */:
|
|
event.preventDefault();
|
|
api.openListbox();
|
|
(0, import_vue29.nextTick)(() => {
|
|
var _a3;
|
|
(_a3 = dom(api.optionsRef)) == null ? void 0 : _a3.focus({ preventScroll: true });
|
|
if (!api.value.value)
|
|
api.goToOption(3 /* Last */);
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
function handleKeyUp(event) {
|
|
switch (event.key) {
|
|
case " " /* Space */:
|
|
event.preventDefault();
|
|
break;
|
|
}
|
|
}
|
|
function handleClick(event) {
|
|
if (api.disabled.value)
|
|
return;
|
|
if (api.listboxState.value === 0 /* Open */) {
|
|
api.closeListbox();
|
|
(0, import_vue29.nextTick)(() => {
|
|
var _a3;
|
|
return (_a3 = dom(api.buttonRef)) == null ? void 0 : _a3.focus({ preventScroll: true });
|
|
});
|
|
} else {
|
|
event.preventDefault();
|
|
api.openListbox();
|
|
nextFrame(() => {
|
|
var _a3;
|
|
return (_a3 = dom(api.optionsRef)) == null ? void 0 : _a3.focus({ preventScroll: true });
|
|
});
|
|
}
|
|
}
|
|
let type = useResolveButtonType(
|
|
(0, import_vue29.computed)(() => ({ as: props.as, type: attrs.type })),
|
|
api.buttonRef
|
|
);
|
|
return () => {
|
|
var _a3, _b;
|
|
let slot = {
|
|
open: api.listboxState.value === 0 /* Open */,
|
|
disabled: api.disabled.value,
|
|
value: api.value.value
|
|
};
|
|
let { ...theirProps } = props;
|
|
let ourProps = {
|
|
ref: api.buttonRef,
|
|
id,
|
|
type: type.value,
|
|
"aria-haspopup": "listbox",
|
|
"aria-controls": (_a3 = dom(api.optionsRef)) == null ? void 0 : _a3.id,
|
|
"aria-expanded": api.listboxState.value === 0 /* Open */,
|
|
"aria-labelledby": api.labelRef.value ? [(_b = dom(api.labelRef)) == null ? void 0 : _b.id, id].join(" ") : void 0,
|
|
disabled: api.disabled.value === true ? true : void 0,
|
|
onKeydown: handleKeyDown,
|
|
onKeyup: handleKeyUp,
|
|
onClick: handleClick
|
|
};
|
|
return render({
|
|
ourProps,
|
|
theirProps,
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
name: "ListboxButton"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
var ListboxOptions = (0, import_vue29.defineComponent)({
|
|
name: "ListboxOptions",
|
|
props: {
|
|
as: { type: [Object, String], default: "ul" },
|
|
static: { type: Boolean, default: false },
|
|
unmount: { type: Boolean, default: true },
|
|
id: { type: String, default: null }
|
|
},
|
|
setup(props, { attrs, slots, expose }) {
|
|
var _a2;
|
|
let id = (_a2 = props.id) != null ? _a2 : `headlessui-listbox-options-${useId2()}`;
|
|
let api = useListboxContext("ListboxOptions");
|
|
let searchDebounce = (0, import_vue29.ref)(null);
|
|
expose({ el: api.optionsRef, $el: api.optionsRef });
|
|
function handleKeyDown(event) {
|
|
if (searchDebounce.value)
|
|
clearTimeout(searchDebounce.value);
|
|
switch (event.key) {
|
|
case " " /* Space */:
|
|
if (api.searchQuery.value !== "") {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return api.search(event.key);
|
|
}
|
|
case "Enter" /* Enter */:
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
if (api.activeOptionIndex.value !== null) {
|
|
let activeOption = api.options.value[api.activeOptionIndex.value];
|
|
api.select(activeOption.dataRef.value);
|
|
}
|
|
if (api.mode.value === 0 /* Single */) {
|
|
api.closeListbox();
|
|
(0, import_vue29.nextTick)(() => {
|
|
var _a3;
|
|
return (_a3 = dom(api.buttonRef)) == null ? void 0 : _a3.focus({ preventScroll: true });
|
|
});
|
|
}
|
|
break;
|
|
case match(api.orientation.value, {
|
|
vertical: "ArrowDown" /* ArrowDown */,
|
|
horizontal: "ArrowRight" /* ArrowRight */
|
|
}):
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return api.goToOption(2 /* Next */);
|
|
case match(api.orientation.value, { vertical: "ArrowUp" /* ArrowUp */, horizontal: "ArrowLeft" /* ArrowLeft */ }):
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return api.goToOption(1 /* Previous */);
|
|
case "Home" /* Home */:
|
|
case "PageUp" /* PageUp */:
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return api.goToOption(0 /* First */);
|
|
case "End" /* End */:
|
|
case "PageDown" /* PageDown */:
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return api.goToOption(3 /* Last */);
|
|
case "Escape" /* Escape */:
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
api.closeListbox();
|
|
(0, import_vue29.nextTick)(() => {
|
|
var _a3;
|
|
return (_a3 = dom(api.buttonRef)) == null ? void 0 : _a3.focus({ preventScroll: true });
|
|
});
|
|
break;
|
|
case "Tab" /* Tab */:
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
break;
|
|
default:
|
|
if (event.key.length === 1) {
|
|
api.search(event.key);
|
|
searchDebounce.value = setTimeout(() => api.clearSearch(), 350);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
let usesOpenClosedState = useOpenClosed();
|
|
let visible = (0, import_vue29.computed)(() => {
|
|
if (usesOpenClosedState !== null) {
|
|
return (usesOpenClosedState.value & 1 /* Open */) === 1 /* Open */;
|
|
}
|
|
return api.listboxState.value === 0 /* Open */;
|
|
});
|
|
return () => {
|
|
var _a3, _b;
|
|
let slot = { open: api.listboxState.value === 0 /* Open */ };
|
|
let { ...theirProps } = props;
|
|
let ourProps = {
|
|
"aria-activedescendant": api.activeOptionIndex.value === null ? void 0 : (_a3 = api.options.value[api.activeOptionIndex.value]) == null ? void 0 : _a3.id,
|
|
"aria-multiselectable": api.mode.value === 1 /* Multi */ ? true : void 0,
|
|
"aria-labelledby": (_b = dom(api.buttonRef)) == null ? void 0 : _b.id,
|
|
"aria-orientation": api.orientation.value,
|
|
id,
|
|
onKeydown: handleKeyDown,
|
|
role: "listbox",
|
|
tabIndex: 0,
|
|
ref: api.optionsRef
|
|
};
|
|
return render({
|
|
ourProps,
|
|
theirProps,
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
features: 1 /* RenderStrategy */ | 2 /* Static */,
|
|
visible: visible.value,
|
|
name: "ListboxOptions"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
var ListboxOption = (0, import_vue29.defineComponent)({
|
|
name: "ListboxOption",
|
|
props: {
|
|
as: { type: [Object, String], default: "li" },
|
|
value: {
|
|
type: [Object, String, Number, Boolean]
|
|
},
|
|
disabled: { type: Boolean, default: false },
|
|
id: { type: String, default: null }
|
|
},
|
|
setup(props, { slots, attrs, expose }) {
|
|
var _a2;
|
|
let id = (_a2 = props.id) != null ? _a2 : `headlessui-listbox-option-${useId2()}`;
|
|
let api = useListboxContext("ListboxOption");
|
|
let internalOptionRef = (0, import_vue29.ref)(null);
|
|
expose({ el: internalOptionRef, $el: internalOptionRef });
|
|
let active = (0, import_vue29.computed)(() => {
|
|
return api.activeOptionIndex.value !== null ? api.options.value[api.activeOptionIndex.value].id === id : false;
|
|
});
|
|
let selected = (0, import_vue29.computed)(
|
|
() => match(api.mode.value, {
|
|
[0 /* Single */]: () => api.compare((0, import_vue29.toRaw)(api.value.value), (0, import_vue29.toRaw)(props.value)),
|
|
[1 /* Multi */]: () => (0, import_vue29.toRaw)(api.value.value).some(
|
|
(value) => api.compare((0, import_vue29.toRaw)(value), (0, import_vue29.toRaw)(props.value))
|
|
)
|
|
})
|
|
);
|
|
let isFirstSelected = (0, import_vue29.computed)(() => {
|
|
return match(api.mode.value, {
|
|
[1 /* Multi */]: () => {
|
|
var _a3;
|
|
let currentValues = (0, import_vue29.toRaw)(api.value.value);
|
|
return ((_a3 = api.options.value.find(
|
|
(option) => currentValues.some((value) => api.compare((0, import_vue29.toRaw)(value), (0, import_vue29.toRaw)(option.dataRef.value)))
|
|
)) == null ? void 0 : _a3.id) === id;
|
|
},
|
|
[0 /* Single */]: () => selected.value
|
|
});
|
|
});
|
|
let getTextValue2 = useTextValue(internalOptionRef);
|
|
let dataRef = (0, import_vue29.computed)(() => ({
|
|
disabled: props.disabled,
|
|
value: props.value,
|
|
get textValue() {
|
|
return getTextValue2();
|
|
},
|
|
domRef: internalOptionRef
|
|
}));
|
|
(0, import_vue29.onMounted)(() => api.registerOption(id, dataRef));
|
|
(0, import_vue29.onUnmounted)(() => api.unregisterOption(id));
|
|
(0, import_vue29.onMounted)(() => {
|
|
(0, import_vue29.watch)(
|
|
[api.listboxState, selected],
|
|
() => {
|
|
if (api.listboxState.value !== 0 /* Open */)
|
|
return;
|
|
if (!selected.value)
|
|
return;
|
|
match(api.mode.value, {
|
|
[1 /* Multi */]: () => {
|
|
if (isFirstSelected.value)
|
|
api.goToOption(4 /* Specific */, id);
|
|
},
|
|
[0 /* Single */]: () => {
|
|
api.goToOption(4 /* Specific */, id);
|
|
}
|
|
});
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
});
|
|
(0, import_vue29.watchEffect)(() => {
|
|
if (api.listboxState.value !== 0 /* Open */)
|
|
return;
|
|
if (!active.value)
|
|
return;
|
|
if (api.activationTrigger.value === 0 /* Pointer */)
|
|
return;
|
|
(0, import_vue29.nextTick)(() => {
|
|
var _a3, _b;
|
|
return (_b = (_a3 = dom(internalOptionRef)) == null ? void 0 : _a3.scrollIntoView) == null ? void 0 : _b.call(_a3, { block: "nearest" });
|
|
});
|
|
});
|
|
function handleClick(event) {
|
|
if (props.disabled)
|
|
return event.preventDefault();
|
|
api.select(props.value);
|
|
if (api.mode.value === 0 /* Single */) {
|
|
api.closeListbox();
|
|
(0, import_vue29.nextTick)(() => {
|
|
var _a3;
|
|
return (_a3 = dom(api.buttonRef)) == null ? void 0 : _a3.focus({ preventScroll: true });
|
|
});
|
|
}
|
|
}
|
|
function handleFocus() {
|
|
if (props.disabled)
|
|
return api.goToOption(5 /* Nothing */);
|
|
api.goToOption(4 /* Specific */, id);
|
|
}
|
|
let pointer = useTrackedPointer();
|
|
function handleEnter(evt) {
|
|
pointer.update(evt);
|
|
}
|
|
function handleMove(evt) {
|
|
if (!pointer.wasMoved(evt))
|
|
return;
|
|
if (props.disabled)
|
|
return;
|
|
if (active.value)
|
|
return;
|
|
api.goToOption(4 /* Specific */, id, 0 /* Pointer */);
|
|
}
|
|
function handleLeave(evt) {
|
|
if (!pointer.wasMoved(evt))
|
|
return;
|
|
if (props.disabled)
|
|
return;
|
|
if (!active.value)
|
|
return;
|
|
api.goToOption(5 /* Nothing */);
|
|
}
|
|
return () => {
|
|
let { disabled } = props;
|
|
let slot = { active: active.value, selected: selected.value, disabled };
|
|
let { value: _value, disabled: _disabled, ...theirProps } = props;
|
|
let ourProps = {
|
|
id,
|
|
ref: internalOptionRef,
|
|
role: "option",
|
|
tabIndex: disabled === true ? void 0 : -1,
|
|
"aria-disabled": disabled === true ? true : void 0,
|
|
// According to the WAI-ARIA best practices, we should use aria-checked for
|
|
// multi-select,but Voice-Over disagrees. So we use aria-checked instead for
|
|
// both single and multi-select.
|
|
"aria-selected": selected.value,
|
|
disabled: void 0,
|
|
// Never forward the `disabled` prop
|
|
onClick: handleClick,
|
|
onFocus: handleFocus,
|
|
onPointerenter: handleEnter,
|
|
onMouseenter: handleEnter,
|
|
onPointermove: handleMove,
|
|
onMousemove: handleMove,
|
|
onPointerleave: handleLeave,
|
|
onMouseleave: handleLeave
|
|
};
|
|
return render({
|
|
ourProps,
|
|
theirProps,
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
name: "ListboxOption"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
|
|
// src/components/menu/menu.ts
|
|
var import_vue30 = require("vue");
|
|
function nextFrame2(cb) {
|
|
requestAnimationFrame(() => requestAnimationFrame(cb));
|
|
}
|
|
var MenuContext = Symbol("MenuContext");
|
|
function useMenuContext(component) {
|
|
let context = (0, import_vue30.inject)(MenuContext, null);
|
|
if (context === null) {
|
|
let err = new Error(`<${component} /> is missing a parent <Menu /> component.`);
|
|
if (Error.captureStackTrace)
|
|
Error.captureStackTrace(err, useMenuContext);
|
|
throw err;
|
|
}
|
|
return context;
|
|
}
|
|
var Menu = (0, import_vue30.defineComponent)({
|
|
name: "Menu",
|
|
props: { as: { type: [Object, String], default: "template" } },
|
|
setup(props, { slots, attrs }) {
|
|
let menuState = (0, import_vue30.ref)(1 /* Closed */);
|
|
let buttonRef = (0, import_vue30.ref)(null);
|
|
let itemsRef = (0, import_vue30.ref)(null);
|
|
let items = (0, import_vue30.ref)([]);
|
|
let searchQuery = (0, import_vue30.ref)("");
|
|
let activeItemIndex = (0, import_vue30.ref)(null);
|
|
let activationTrigger = (0, import_vue30.ref)(
|
|
1 /* Other */
|
|
);
|
|
function adjustOrderedState(adjustment = (i) => i) {
|
|
let currentActiveItem = activeItemIndex.value !== null ? items.value[activeItemIndex.value] : null;
|
|
let sortedItems = sortByDomNode(
|
|
adjustment(items.value.slice()),
|
|
(item) => dom(item.dataRef.domRef)
|
|
);
|
|
let adjustedActiveItemIndex = currentActiveItem ? sortedItems.indexOf(currentActiveItem) : null;
|
|
if (adjustedActiveItemIndex === -1) {
|
|
adjustedActiveItemIndex = null;
|
|
}
|
|
return {
|
|
items: sortedItems,
|
|
activeItemIndex: adjustedActiveItemIndex
|
|
};
|
|
}
|
|
let api = {
|
|
menuState,
|
|
buttonRef,
|
|
itemsRef,
|
|
items,
|
|
searchQuery,
|
|
activeItemIndex,
|
|
activationTrigger,
|
|
closeMenu: () => {
|
|
menuState.value = 1 /* Closed */;
|
|
activeItemIndex.value = null;
|
|
},
|
|
openMenu: () => menuState.value = 0 /* Open */,
|
|
goToItem(focus, id, trigger) {
|
|
let adjustedState = adjustOrderedState();
|
|
let nextActiveItemIndex = calculateActiveIndex(
|
|
focus === 4 /* Specific */ ? { focus: 4 /* Specific */, id } : { focus },
|
|
{
|
|
resolveItems: () => adjustedState.items,
|
|
resolveActiveIndex: () => adjustedState.activeItemIndex,
|
|
resolveId: (item) => item.id,
|
|
resolveDisabled: (item) => item.dataRef.disabled
|
|
}
|
|
);
|
|
searchQuery.value = "";
|
|
activeItemIndex.value = nextActiveItemIndex;
|
|
activationTrigger.value = trigger != null ? trigger : 1 /* Other */;
|
|
items.value = adjustedState.items;
|
|
},
|
|
search(value) {
|
|
let wasAlreadySearching = searchQuery.value !== "";
|
|
let offset = wasAlreadySearching ? 0 : 1;
|
|
searchQuery.value += value.toLowerCase();
|
|
let reOrderedItems = activeItemIndex.value !== null ? items.value.slice(activeItemIndex.value + offset).concat(items.value.slice(0, activeItemIndex.value + offset)) : items.value;
|
|
let matchingItem = reOrderedItems.find(
|
|
(item) => item.dataRef.textValue.startsWith(searchQuery.value) && !item.dataRef.disabled
|
|
);
|
|
let matchIdx = matchingItem ? items.value.indexOf(matchingItem) : -1;
|
|
if (matchIdx === -1 || matchIdx === activeItemIndex.value)
|
|
return;
|
|
activeItemIndex.value = matchIdx;
|
|
activationTrigger.value = 1 /* Other */;
|
|
},
|
|
clearSearch() {
|
|
searchQuery.value = "";
|
|
},
|
|
registerItem(id, dataRef) {
|
|
let adjustedState = adjustOrderedState((items2) => {
|
|
return [...items2, { id, dataRef }];
|
|
});
|
|
items.value = adjustedState.items;
|
|
activeItemIndex.value = adjustedState.activeItemIndex;
|
|
activationTrigger.value = 1 /* Other */;
|
|
},
|
|
unregisterItem(id) {
|
|
let adjustedState = adjustOrderedState((items2) => {
|
|
let idx = items2.findIndex((a) => a.id === id);
|
|
if (idx !== -1)
|
|
items2.splice(idx, 1);
|
|
return items2;
|
|
});
|
|
items.value = adjustedState.items;
|
|
activeItemIndex.value = adjustedState.activeItemIndex;
|
|
activationTrigger.value = 1 /* Other */;
|
|
}
|
|
};
|
|
useOutsideClick(
|
|
[buttonRef, itemsRef],
|
|
(event, target) => {
|
|
var _a2;
|
|
api.closeMenu();
|
|
if (!isFocusableElement(target, 1 /* Loose */)) {
|
|
event.preventDefault();
|
|
(_a2 = dom(buttonRef)) == null ? void 0 : _a2.focus();
|
|
}
|
|
},
|
|
(0, import_vue30.computed)(() => menuState.value === 0 /* Open */)
|
|
);
|
|
(0, import_vue30.provide)(MenuContext, api);
|
|
useOpenClosedProvider(
|
|
(0, import_vue30.computed)(
|
|
() => match(menuState.value, {
|
|
[0 /* Open */]: 1 /* Open */,
|
|
[1 /* Closed */]: 2 /* Closed */
|
|
})
|
|
)
|
|
);
|
|
return () => {
|
|
let slot = { open: menuState.value === 0 /* Open */, close: api.closeMenu };
|
|
return render({ ourProps: {}, theirProps: props, slot, slots, attrs, name: "Menu" });
|
|
};
|
|
}
|
|
});
|
|
var MenuButton = (0, import_vue30.defineComponent)({
|
|
name: "MenuButton",
|
|
props: {
|
|
disabled: { type: Boolean, default: false },
|
|
as: { type: [Object, String], default: "button" },
|
|
id: { type: String, default: null }
|
|
},
|
|
setup(props, { attrs, slots, expose }) {
|
|
var _a2;
|
|
let id = (_a2 = props.id) != null ? _a2 : `headlessui-menu-button-${useId2()}`;
|
|
let api = useMenuContext("MenuButton");
|
|
expose({ el: api.buttonRef, $el: api.buttonRef });
|
|
function handleKeyDown(event) {
|
|
switch (event.key) {
|
|
case " " /* Space */:
|
|
case "Enter" /* Enter */:
|
|
case "ArrowDown" /* ArrowDown */:
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
api.openMenu();
|
|
(0, import_vue30.nextTick)(() => {
|
|
var _a3;
|
|
(_a3 = dom(api.itemsRef)) == null ? void 0 : _a3.focus({ preventScroll: true });
|
|
api.goToItem(0 /* First */);
|
|
});
|
|
break;
|
|
case "ArrowUp" /* ArrowUp */:
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
api.openMenu();
|
|
(0, import_vue30.nextTick)(() => {
|
|
var _a3;
|
|
(_a3 = dom(api.itemsRef)) == null ? void 0 : _a3.focus({ preventScroll: true });
|
|
api.goToItem(3 /* Last */);
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
function handleKeyUp(event) {
|
|
switch (event.key) {
|
|
case " " /* Space */:
|
|
event.preventDefault();
|
|
break;
|
|
}
|
|
}
|
|
function handleClick(event) {
|
|
if (props.disabled)
|
|
return;
|
|
if (api.menuState.value === 0 /* Open */) {
|
|
api.closeMenu();
|
|
(0, import_vue30.nextTick)(() => {
|
|
var _a3;
|
|
return (_a3 = dom(api.buttonRef)) == null ? void 0 : _a3.focus({ preventScroll: true });
|
|
});
|
|
} else {
|
|
event.preventDefault();
|
|
api.openMenu();
|
|
nextFrame2(() => {
|
|
var _a3;
|
|
return (_a3 = dom(api.itemsRef)) == null ? void 0 : _a3.focus({ preventScroll: true });
|
|
});
|
|
}
|
|
}
|
|
let type = useResolveButtonType(
|
|
(0, import_vue30.computed)(() => ({ as: props.as, type: attrs.type })),
|
|
api.buttonRef
|
|
);
|
|
return () => {
|
|
var _a3;
|
|
let slot = { open: api.menuState.value === 0 /* Open */ };
|
|
let { ...theirProps } = props;
|
|
let ourProps = {
|
|
ref: api.buttonRef,
|
|
id,
|
|
type: type.value,
|
|
"aria-haspopup": "menu",
|
|
"aria-controls": (_a3 = dom(api.itemsRef)) == null ? void 0 : _a3.id,
|
|
"aria-expanded": api.menuState.value === 0 /* Open */,
|
|
onKeydown: handleKeyDown,
|
|
onKeyup: handleKeyUp,
|
|
onClick: handleClick
|
|
};
|
|
return render({
|
|
ourProps,
|
|
theirProps,
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
name: "MenuButton"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
var MenuItems = (0, import_vue30.defineComponent)({
|
|
name: "MenuItems",
|
|
props: {
|
|
as: { type: [Object, String], default: "div" },
|
|
static: { type: Boolean, default: false },
|
|
unmount: { type: Boolean, default: true },
|
|
id: { type: String, default: null }
|
|
},
|
|
setup(props, { attrs, slots, expose }) {
|
|
var _a2;
|
|
let id = (_a2 = props.id) != null ? _a2 : `headlessui-menu-items-${useId2()}`;
|
|
let api = useMenuContext("MenuItems");
|
|
let searchDebounce = (0, import_vue30.ref)(null);
|
|
expose({ el: api.itemsRef, $el: api.itemsRef });
|
|
useTreeWalker({
|
|
container: (0, import_vue30.computed)(() => dom(api.itemsRef)),
|
|
enabled: (0, import_vue30.computed)(() => api.menuState.value === 0 /* Open */),
|
|
accept(node) {
|
|
if (node.getAttribute("role") === "menuitem")
|
|
return NodeFilter.FILTER_REJECT;
|
|
if (node.hasAttribute("role"))
|
|
return NodeFilter.FILTER_SKIP;
|
|
return NodeFilter.FILTER_ACCEPT;
|
|
},
|
|
walk(node) {
|
|
node.setAttribute("role", "none");
|
|
}
|
|
});
|
|
function handleKeyDown(event) {
|
|
var _a3;
|
|
if (searchDebounce.value)
|
|
clearTimeout(searchDebounce.value);
|
|
switch (event.key) {
|
|
case " " /* Space */:
|
|
if (api.searchQuery.value !== "") {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return api.search(event.key);
|
|
}
|
|
case "Enter" /* Enter */:
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
if (api.activeItemIndex.value !== null) {
|
|
let activeItem = api.items.value[api.activeItemIndex.value];
|
|
let _activeItem = activeItem;
|
|
(_a3 = dom(_activeItem.dataRef.domRef)) == null ? void 0 : _a3.click();
|
|
}
|
|
api.closeMenu();
|
|
restoreFocusIfNecessary(dom(api.buttonRef));
|
|
break;
|
|
case "ArrowDown" /* ArrowDown */:
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return api.goToItem(2 /* Next */);
|
|
case "ArrowUp" /* ArrowUp */:
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return api.goToItem(1 /* Previous */);
|
|
case "Home" /* Home */:
|
|
case "PageUp" /* PageUp */:
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return api.goToItem(0 /* First */);
|
|
case "End" /* End */:
|
|
case "PageDown" /* PageDown */:
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return api.goToItem(3 /* Last */);
|
|
case "Escape" /* Escape */:
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
api.closeMenu();
|
|
(0, import_vue30.nextTick)(() => {
|
|
var _a4;
|
|
return (_a4 = dom(api.buttonRef)) == null ? void 0 : _a4.focus({ preventScroll: true });
|
|
});
|
|
break;
|
|
case "Tab" /* Tab */:
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
api.closeMenu();
|
|
(0, import_vue30.nextTick)(
|
|
() => focusFrom(
|
|
dom(api.buttonRef),
|
|
event.shiftKey ? 2 /* Previous */ : 4 /* Next */
|
|
)
|
|
);
|
|
break;
|
|
default:
|
|
if (event.key.length === 1) {
|
|
api.search(event.key);
|
|
searchDebounce.value = setTimeout(() => api.clearSearch(), 350);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
function handleKeyUp(event) {
|
|
switch (event.key) {
|
|
case " " /* Space */:
|
|
event.preventDefault();
|
|
break;
|
|
}
|
|
}
|
|
let usesOpenClosedState = useOpenClosed();
|
|
let visible = (0, import_vue30.computed)(() => {
|
|
if (usesOpenClosedState !== null) {
|
|
return (usesOpenClosedState.value & 1 /* Open */) === 1 /* Open */;
|
|
}
|
|
return api.menuState.value === 0 /* Open */;
|
|
});
|
|
return () => {
|
|
var _a3, _b;
|
|
let slot = { open: api.menuState.value === 0 /* Open */ };
|
|
let { ...theirProps } = props;
|
|
let ourProps = {
|
|
"aria-activedescendant": api.activeItemIndex.value === null ? void 0 : (_a3 = api.items.value[api.activeItemIndex.value]) == null ? void 0 : _a3.id,
|
|
"aria-labelledby": (_b = dom(api.buttonRef)) == null ? void 0 : _b.id,
|
|
id,
|
|
onKeydown: handleKeyDown,
|
|
onKeyup: handleKeyUp,
|
|
role: "menu",
|
|
tabIndex: 0,
|
|
ref: api.itemsRef
|
|
};
|
|
return render({
|
|
ourProps,
|
|
theirProps,
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
features: 1 /* RenderStrategy */ | 2 /* Static */,
|
|
visible: visible.value,
|
|
name: "MenuItems"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
var MenuItem = (0, import_vue30.defineComponent)({
|
|
name: "MenuItem",
|
|
inheritAttrs: false,
|
|
props: {
|
|
as: { type: [Object, String], default: "template" },
|
|
disabled: { type: Boolean, default: false },
|
|
id: { type: String, default: null }
|
|
},
|
|
setup(props, { slots, attrs, expose }) {
|
|
var _a2;
|
|
let id = (_a2 = props.id) != null ? _a2 : `headlessui-menu-item-${useId2()}`;
|
|
let api = useMenuContext("MenuItem");
|
|
let internalItemRef = (0, import_vue30.ref)(null);
|
|
expose({ el: internalItemRef, $el: internalItemRef });
|
|
let active = (0, import_vue30.computed)(() => {
|
|
return api.activeItemIndex.value !== null ? api.items.value[api.activeItemIndex.value].id === id : false;
|
|
});
|
|
let getTextValue2 = useTextValue(internalItemRef);
|
|
let dataRef = (0, import_vue30.computed)(() => ({
|
|
disabled: props.disabled,
|
|
get textValue() {
|
|
return getTextValue2();
|
|
},
|
|
domRef: internalItemRef
|
|
}));
|
|
(0, import_vue30.onMounted)(() => api.registerItem(id, dataRef));
|
|
(0, import_vue30.onUnmounted)(() => api.unregisterItem(id));
|
|
(0, import_vue30.watchEffect)(() => {
|
|
if (api.menuState.value !== 0 /* Open */)
|
|
return;
|
|
if (!active.value)
|
|
return;
|
|
if (api.activationTrigger.value === 0 /* Pointer */)
|
|
return;
|
|
(0, import_vue30.nextTick)(() => {
|
|
var _a3, _b;
|
|
return (_b = (_a3 = dom(internalItemRef)) == null ? void 0 : _a3.scrollIntoView) == null ? void 0 : _b.call(_a3, { block: "nearest" });
|
|
});
|
|
});
|
|
function handleClick(event) {
|
|
if (props.disabled)
|
|
return event.preventDefault();
|
|
api.closeMenu();
|
|
restoreFocusIfNecessary(dom(api.buttonRef));
|
|
}
|
|
function handleFocus() {
|
|
if (props.disabled)
|
|
return api.goToItem(5 /* Nothing */);
|
|
api.goToItem(4 /* Specific */, id);
|
|
}
|
|
let pointer = useTrackedPointer();
|
|
function handleEnter(evt) {
|
|
pointer.update(evt);
|
|
}
|
|
function handleMove(evt) {
|
|
if (!pointer.wasMoved(evt))
|
|
return;
|
|
if (props.disabled)
|
|
return;
|
|
if (active.value)
|
|
return;
|
|
api.goToItem(4 /* Specific */, id, 0 /* Pointer */);
|
|
}
|
|
function handleLeave(evt) {
|
|
if (!pointer.wasMoved(evt))
|
|
return;
|
|
if (props.disabled)
|
|
return;
|
|
if (!active.value)
|
|
return;
|
|
api.goToItem(5 /* Nothing */);
|
|
}
|
|
return () => {
|
|
let { disabled, ...theirProps } = props;
|
|
let slot = { active: active.value, disabled, close: api.closeMenu };
|
|
let ourProps = {
|
|
id,
|
|
ref: internalItemRef,
|
|
role: "menuitem",
|
|
tabIndex: disabled === true ? void 0 : -1,
|
|
"aria-disabled": disabled === true ? true : void 0,
|
|
onClick: handleClick,
|
|
onFocus: handleFocus,
|
|
onPointerenter: handleEnter,
|
|
onMouseenter: handleEnter,
|
|
onPointermove: handleMove,
|
|
onMousemove: handleMove,
|
|
onPointerleave: handleLeave,
|
|
onMouseleave: handleLeave
|
|
};
|
|
return render({
|
|
ourProps,
|
|
theirProps: { ...attrs, ...theirProps },
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
name: "MenuItem"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
|
|
// src/components/popover/popover.ts
|
|
var import_vue31 = require("vue");
|
|
var PopoverContext = Symbol("PopoverContext");
|
|
function usePopoverContext(component) {
|
|
let context = (0, import_vue31.inject)(PopoverContext, null);
|
|
if (context === null) {
|
|
let err = new Error(`<${component} /> is missing a parent <${Popover.name} /> component.`);
|
|
if (Error.captureStackTrace)
|
|
Error.captureStackTrace(err, usePopoverContext);
|
|
throw err;
|
|
}
|
|
return context;
|
|
}
|
|
var PopoverGroupContext = Symbol("PopoverGroupContext");
|
|
function usePopoverGroupContext() {
|
|
return (0, import_vue31.inject)(PopoverGroupContext, null);
|
|
}
|
|
var PopoverPanelContext = Symbol("PopoverPanelContext");
|
|
function usePopoverPanelContext() {
|
|
return (0, import_vue31.inject)(PopoverPanelContext, null);
|
|
}
|
|
var Popover = (0, import_vue31.defineComponent)({
|
|
name: "Popover",
|
|
inheritAttrs: false,
|
|
props: {
|
|
as: { type: [Object, String], default: "div" }
|
|
},
|
|
setup(props, { slots, attrs, expose }) {
|
|
var _a2;
|
|
let internalPopoverRef = (0, import_vue31.ref)(null);
|
|
expose({ el: internalPopoverRef, $el: internalPopoverRef });
|
|
let popoverState = (0, import_vue31.ref)(1 /* Closed */);
|
|
let button = (0, import_vue31.ref)(null);
|
|
let beforePanelSentinel = (0, import_vue31.ref)(null);
|
|
let afterPanelSentinel = (0, import_vue31.ref)(null);
|
|
let panel = (0, import_vue31.ref)(null);
|
|
let ownerDocument = (0, import_vue31.computed)(() => getOwnerDocument(internalPopoverRef));
|
|
let isPortalled = (0, import_vue31.computed)(() => {
|
|
var _a3, _b;
|
|
if (!dom(button))
|
|
return false;
|
|
if (!dom(panel))
|
|
return false;
|
|
for (let root2 of document.querySelectorAll("body > *")) {
|
|
if (Number(root2 == null ? void 0 : root2.contains(dom(button))) ^ Number(root2 == null ? void 0 : root2.contains(dom(panel)))) {
|
|
return true;
|
|
}
|
|
}
|
|
let elements = getFocusableElements();
|
|
let buttonIdx = elements.indexOf(dom(button));
|
|
let beforeIdx = (buttonIdx + elements.length - 1) % elements.length;
|
|
let afterIdx = (buttonIdx + 1) % elements.length;
|
|
let beforeElement = elements[beforeIdx];
|
|
let afterElement = elements[afterIdx];
|
|
if (!((_a3 = dom(panel)) == null ? void 0 : _a3.contains(beforeElement)) && !((_b = dom(panel)) == null ? void 0 : _b.contains(afterElement))) {
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
let api = {
|
|
popoverState,
|
|
buttonId: (0, import_vue31.ref)(null),
|
|
panelId: (0, import_vue31.ref)(null),
|
|
panel,
|
|
button,
|
|
isPortalled,
|
|
beforePanelSentinel,
|
|
afterPanelSentinel,
|
|
togglePopover() {
|
|
popoverState.value = match(popoverState.value, {
|
|
[0 /* Open */]: 1 /* Closed */,
|
|
[1 /* Closed */]: 0 /* Open */
|
|
});
|
|
},
|
|
closePopover() {
|
|
if (popoverState.value === 1 /* Closed */)
|
|
return;
|
|
popoverState.value = 1 /* Closed */;
|
|
},
|
|
close(focusableElement) {
|
|
api.closePopover();
|
|
let restoreElement = (() => {
|
|
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();
|
|
}
|
|
};
|
|
(0, import_vue31.provide)(PopoverContext, api);
|
|
useOpenClosedProvider(
|
|
(0, import_vue31.computed)(
|
|
() => match(popoverState.value, {
|
|
[0 /* Open */]: 1 /* Open */,
|
|
[1 /* Closed */]: 2 /* Closed */
|
|
})
|
|
)
|
|
);
|
|
let registerBag = {
|
|
buttonId: api.buttonId,
|
|
panelId: api.panelId,
|
|
close() {
|
|
api.closePopover();
|
|
}
|
|
};
|
|
let groupContext = usePopoverGroupContext();
|
|
let registerPopover = groupContext == null ? void 0 : groupContext.registerPopover;
|
|
let [portals, PortalWrapper] = useNestedPortals();
|
|
let root = useRootContainers({
|
|
mainTreeNodeRef: groupContext == null ? void 0 : groupContext.mainTreeNodeRef,
|
|
portals,
|
|
defaultContainers: [button, panel]
|
|
});
|
|
function isFocusWithinPopoverGroup() {
|
|
var _a3, _b, _c, _d;
|
|
return (_d = groupContext == null ? void 0 : groupContext.isFocusWithinPopoverGroup()) != null ? _d : ((_a3 = ownerDocument.value) == null ? void 0 : _a3.activeElement) && (((_b = dom(button)) == null ? void 0 : _b.contains(ownerDocument.value.activeElement)) || ((_c = dom(panel)) == null ? void 0 : _c.contains(ownerDocument.value.activeElement)));
|
|
}
|
|
(0, import_vue31.watchEffect)(() => registerPopover == null ? void 0 : registerPopover(registerBag));
|
|
useEventListener(
|
|
(_a2 = ownerDocument.value) == null ? void 0 : _a2.defaultView,
|
|
"focus",
|
|
(event) => {
|
|
var _a3, _b;
|
|
if (event.target === window)
|
|
return;
|
|
if (!(event.target instanceof HTMLElement))
|
|
return;
|
|
if (popoverState.value !== 0 /* Open */)
|
|
return;
|
|
if (isFocusWithinPopoverGroup())
|
|
return;
|
|
if (!button)
|
|
return;
|
|
if (!panel)
|
|
return;
|
|
if (root.contains(event.target))
|
|
return;
|
|
if ((_a3 = dom(api.beforePanelSentinel)) == null ? void 0 : _a3.contains(event.target))
|
|
return;
|
|
if ((_b = dom(api.afterPanelSentinel)) == null ? void 0 : _b.contains(event.target))
|
|
return;
|
|
api.closePopover();
|
|
},
|
|
true
|
|
);
|
|
useOutsideClick(
|
|
root.resolveContainers,
|
|
(event, target) => {
|
|
var _a3;
|
|
api.closePopover();
|
|
if (!isFocusableElement(target, 1 /* Loose */)) {
|
|
event.preventDefault();
|
|
(_a3 = dom(button)) == null ? void 0 : _a3.focus();
|
|
}
|
|
},
|
|
(0, import_vue31.computed)(() => popoverState.value === 0 /* Open */)
|
|
);
|
|
return () => {
|
|
let slot = { open: popoverState.value === 0 /* Open */, close: api.close };
|
|
return (0, import_vue31.h)(import_vue31.Fragment, [
|
|
(0, import_vue31.h)(
|
|
PortalWrapper,
|
|
{},
|
|
() => render({
|
|
theirProps: { ...props, ...attrs },
|
|
ourProps: { ref: internalPopoverRef },
|
|
slot,
|
|
slots,
|
|
attrs,
|
|
name: "Popover"
|
|
})
|
|
),
|
|
(0, import_vue31.h)(root.MainTreeNode)
|
|
]);
|
|
};
|
|
}
|
|
});
|
|
var PopoverButton = (0, import_vue31.defineComponent)({
|
|
name: "PopoverButton",
|
|
props: {
|
|
as: { type: [Object, String], default: "button" },
|
|
disabled: { type: [Boolean], default: false },
|
|
id: { type: String, default: null }
|
|
},
|
|
inheritAttrs: false,
|
|
setup(props, { attrs, slots, expose }) {
|
|
var _a2;
|
|
let id = (_a2 = props.id) != null ? _a2 : `headlessui-popover-button-${useId2()}`;
|
|
let api = usePopoverContext("PopoverButton");
|
|
let ownerDocument = (0, import_vue31.computed)(() => getOwnerDocument(api.button));
|
|
expose({ el: api.button, $el: api.button });
|
|
(0, import_vue31.onMounted)(() => {
|
|
api.buttonId.value = id;
|
|
});
|
|
(0, import_vue31.onUnmounted)(() => {
|
|
api.buttonId.value = null;
|
|
});
|
|
let groupContext = usePopoverGroupContext();
|
|
let closeOthers = groupContext == null ? void 0 : groupContext.closeOthers;
|
|
let panelContext = usePopoverPanelContext();
|
|
let isWithinPanel = (0, import_vue31.computed)(
|
|
() => panelContext === null ? false : panelContext.value === api.panelId.value
|
|
);
|
|
let elementRef = (0, import_vue31.ref)(null);
|
|
let sentinelId = `headlessui-focus-sentinel-${useId2()}`;
|
|
if (!isWithinPanel.value) {
|
|
(0, import_vue31.watchEffect)(() => {
|
|
api.button.value = dom(elementRef);
|
|
});
|
|
}
|
|
let type = useResolveButtonType(
|
|
(0, import_vue31.computed)(() => ({ as: props.as, type: attrs.type })),
|
|
elementRef
|
|
);
|
|
function handleKeyDown(event) {
|
|
var _a3, _b, _c, _d, _e;
|
|
if (isWithinPanel.value) {
|
|
if (api.popoverState.value === 1 /* Closed */)
|
|
return;
|
|
switch (event.key) {
|
|
case " " /* Space */:
|
|
case "Enter" /* Enter */:
|
|
event.preventDefault();
|
|
(_b = (_a3 = event.target).click) == null ? void 0 : _b.call(_a3);
|
|
api.closePopover();
|
|
(_c = dom(api.button)) == null ? void 0 : _c.focus();
|
|
break;
|
|
}
|
|
} else {
|
|
switch (event.key) {
|
|
case " " /* Space */:
|
|
case "Enter" /* Enter */:
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
if (api.popoverState.value === 1 /* Closed */)
|
|
closeOthers == null ? void 0 : closeOthers(api.buttonId.value);
|
|
api.togglePopover();
|
|
break;
|
|
case "Escape" /* Escape */:
|
|
if (api.popoverState.value !== 0 /* Open */)
|
|
return closeOthers == null ? void 0 : closeOthers(api.buttonId.value);
|
|
if (!dom(api.button))
|
|
return;
|
|
if (((_d = ownerDocument.value) == null ? void 0 : _d.activeElement) && !((_e = dom(api.button)) == null ? void 0 : _e.contains(ownerDocument.value.activeElement)))
|
|
return;
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
api.closePopover();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
function handleKeyUp(event) {
|
|
if (isWithinPanel.value)
|
|
return;
|
|
if (event.key === " " /* Space */) {
|
|
event.preventDefault();
|
|
}
|
|
}
|
|
function handleClick(event) {
|
|
var _a3, _b;
|
|
if (props.disabled)
|
|
return;
|
|
if (isWithinPanel.value) {
|
|
api.closePopover();
|
|
(_a3 = dom(api.button)) == null ? void 0 : _a3.focus();
|
|
} else {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
if (api.popoverState.value === 1 /* Closed */)
|
|
closeOthers == null ? void 0 : closeOthers(api.buttonId.value);
|
|
api.togglePopover();
|
|
(_b = dom(api.button)) == null ? void 0 : _b.focus();
|
|
}
|
|
}
|
|
function handleMouseDown(event) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
}
|
|
let direction = useTabDirection();
|
|
function handleFocus() {
|
|
let el = dom(api.panel);
|
|
if (!el)
|
|
return;
|
|
function run() {
|
|
let result = match(direction.value, {
|
|
[0 /* Forwards */]: () => focusIn(el, 1 /* First */),
|
|
[1 /* Backwards */]: () => focusIn(el, 8 /* Last */)
|
|
});
|
|
if (result === 0 /* Error */) {
|
|
focusIn(
|
|
getFocusableElements().filter((el2) => el2.dataset.headlessuiFocusGuard !== "true"),
|
|
match(direction.value, {
|
|
[0 /* Forwards */]: 4 /* Next */,
|
|
[1 /* Backwards */]: 2 /* Previous */
|
|
}),
|
|
{ relativeTo: dom(api.button) }
|
|
);
|
|
}
|
|
}
|
|
if (false) {
|
|
microTask(run);
|
|
} else {
|
|
run();
|
|
}
|
|
}
|
|
return () => {
|
|
let visible = api.popoverState.value === 0 /* Open */;
|
|
let slot = { open: visible };
|
|
let { ...theirProps } = props;
|
|
let ourProps = isWithinPanel.value ? {
|
|
ref: elementRef,
|
|
type: type.value,
|
|
onKeydown: handleKeyDown,
|
|
onClick: handleClick
|
|
} : {
|
|
ref: elementRef,
|
|
id,
|
|
type: type.value,
|
|
"aria-expanded": api.popoverState.value === 0 /* Open */,
|
|
"aria-controls": dom(api.panel) ? api.panelId.value : void 0,
|
|
disabled: props.disabled ? true : void 0,
|
|
onKeydown: handleKeyDown,
|
|
onKeyup: handleKeyUp,
|
|
onClick: handleClick,
|
|
onMousedown: handleMouseDown
|
|
};
|
|
return (0, import_vue31.h)(import_vue31.Fragment, [
|
|
render({
|
|
ourProps,
|
|
theirProps: { ...attrs, ...theirProps },
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
name: "PopoverButton"
|
|
}),
|
|
visible && !isWithinPanel.value && api.isPortalled.value && (0, import_vue31.h)(Hidden, {
|
|
id: sentinelId,
|
|
features: 2 /* Focusable */,
|
|
"data-headlessui-focus-guard": true,
|
|
as: "button",
|
|
type: "button",
|
|
onFocus: handleFocus
|
|
})
|
|
]);
|
|
};
|
|
}
|
|
});
|
|
var PopoverOverlay = (0, import_vue31.defineComponent)({
|
|
name: "PopoverOverlay",
|
|
props: {
|
|
as: { type: [Object, String], default: "div" },
|
|
static: { type: Boolean, default: false },
|
|
unmount: { type: Boolean, default: true }
|
|
},
|
|
setup(props, { attrs, slots }) {
|
|
let api = usePopoverContext("PopoverOverlay");
|
|
let id = `headlessui-popover-overlay-${useId2()}`;
|
|
let usesOpenClosedState = useOpenClosed();
|
|
let visible = (0, import_vue31.computed)(() => {
|
|
if (usesOpenClosedState !== null) {
|
|
return (usesOpenClosedState.value & 1 /* Open */) === 1 /* Open */;
|
|
}
|
|
return api.popoverState.value === 0 /* Open */;
|
|
});
|
|
function handleClick() {
|
|
api.closePopover();
|
|
}
|
|
return () => {
|
|
let slot = { open: api.popoverState.value === 0 /* Open */ };
|
|
let ourProps = {
|
|
id,
|
|
"aria-hidden": true,
|
|
onClick: handleClick
|
|
};
|
|
return render({
|
|
ourProps,
|
|
theirProps: props,
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
features: 1 /* RenderStrategy */ | 2 /* Static */,
|
|
visible: visible.value,
|
|
name: "PopoverOverlay"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
var PopoverPanel = (0, import_vue31.defineComponent)({
|
|
name: "PopoverPanel",
|
|
props: {
|
|
as: { type: [Object, String], default: "div" },
|
|
static: { type: Boolean, default: false },
|
|
unmount: { type: Boolean, default: true },
|
|
focus: { type: Boolean, default: false },
|
|
id: { type: String, default: null }
|
|
},
|
|
inheritAttrs: false,
|
|
setup(props, { attrs, slots, expose }) {
|
|
var _a2;
|
|
let id = (_a2 = props.id) != null ? _a2 : `headlessui-popover-panel-${useId2()}`;
|
|
let { focus } = props;
|
|
let api = usePopoverContext("PopoverPanel");
|
|
let ownerDocument = (0, import_vue31.computed)(() => getOwnerDocument(api.panel));
|
|
let beforePanelSentinelId = `headlessui-focus-sentinel-before-${useId2()}`;
|
|
let afterPanelSentinelId = `headlessui-focus-sentinel-after-${useId2()}`;
|
|
expose({ el: api.panel, $el: api.panel });
|
|
(0, import_vue31.onMounted)(() => {
|
|
api.panelId.value = id;
|
|
});
|
|
(0, import_vue31.onUnmounted)(() => {
|
|
api.panelId.value = null;
|
|
});
|
|
(0, import_vue31.provide)(PopoverPanelContext, api.panelId);
|
|
(0, import_vue31.watchEffect)(() => {
|
|
var _a3, _b;
|
|
if (!focus)
|
|
return;
|
|
if (api.popoverState.value !== 0 /* Open */)
|
|
return;
|
|
if (!api.panel)
|
|
return;
|
|
let activeElement = (_a3 = ownerDocument.value) == null ? void 0 : _a3.activeElement;
|
|
if ((_b = dom(api.panel)) == null ? void 0 : _b.contains(activeElement))
|
|
return;
|
|
focusIn(dom(api.panel), 1 /* First */);
|
|
});
|
|
let usesOpenClosedState = useOpenClosed();
|
|
let visible = (0, import_vue31.computed)(() => {
|
|
if (usesOpenClosedState !== null) {
|
|
return (usesOpenClosedState.value & 1 /* Open */) === 1 /* Open */;
|
|
}
|
|
return api.popoverState.value === 0 /* Open */;
|
|
});
|
|
function handleKeyDown(event) {
|
|
var _a3, _b;
|
|
switch (event.key) {
|
|
case "Escape" /* Escape */:
|
|
if (api.popoverState.value !== 0 /* Open */)
|
|
return;
|
|
if (!dom(api.panel))
|
|
return;
|
|
if (ownerDocument.value && !((_a3 = dom(api.panel)) == null ? void 0 : _a3.contains(ownerDocument.value.activeElement))) {
|
|
return;
|
|
}
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
api.closePopover();
|
|
(_b = dom(api.button)) == null ? void 0 : _b.focus();
|
|
break;
|
|
}
|
|
}
|
|
function handleBlur(event) {
|
|
var _a3, _b, _c, _d, _e;
|
|
let el = event.relatedTarget;
|
|
if (!el)
|
|
return;
|
|
if (!dom(api.panel))
|
|
return;
|
|
if ((_a3 = dom(api.panel)) == null ? void 0 : _a3.contains(el))
|
|
return;
|
|
api.closePopover();
|
|
if (((_c = (_b = dom(api.beforePanelSentinel)) == null ? void 0 : _b.contains) == null ? void 0 : _c.call(_b, el)) || ((_e = (_d = dom(api.afterPanelSentinel)) == null ? void 0 : _d.contains) == null ? void 0 : _e.call(_d, el))) {
|
|
el.focus({ preventScroll: true });
|
|
}
|
|
}
|
|
let direction = useTabDirection();
|
|
function handleBeforeFocus() {
|
|
let el = dom(api.panel);
|
|
if (!el)
|
|
return;
|
|
function run() {
|
|
match(direction.value, {
|
|
[0 /* Forwards */]: () => {
|
|
var _a3;
|
|
let result = focusIn(el, 1 /* First */);
|
|
if (result === 0 /* Error */) {
|
|
(_a3 = dom(api.afterPanelSentinel)) == null ? void 0 : _a3.focus();
|
|
}
|
|
},
|
|
[1 /* Backwards */]: () => {
|
|
var _a3;
|
|
(_a3 = dom(api.button)) == null ? void 0 : _a3.focus({ preventScroll: true });
|
|
}
|
|
});
|
|
}
|
|
if (false) {
|
|
microTask(run);
|
|
} else {
|
|
run();
|
|
}
|
|
}
|
|
function handleAfterFocus() {
|
|
let el = dom(api.panel);
|
|
if (!el)
|
|
return;
|
|
function run() {
|
|
match(direction.value, {
|
|
[0 /* Forwards */]: () => {
|
|
let button = dom(api.button);
|
|
let panel = dom(api.panel);
|
|
if (!button)
|
|
return;
|
|
let elements = getFocusableElements();
|
|
let idx = elements.indexOf(button);
|
|
let before = elements.slice(0, idx + 1);
|
|
let after = elements.slice(idx + 1);
|
|
let combined = [...after, ...before];
|
|
for (let element of combined.slice()) {
|
|
if (element.dataset.headlessuiFocusGuard === "true" || (panel == null ? void 0 : panel.contains(element))) {
|
|
let idx2 = combined.indexOf(element);
|
|
if (idx2 !== -1)
|
|
combined.splice(idx2, 1);
|
|
}
|
|
}
|
|
focusIn(combined, 1 /* First */, { sorted: false });
|
|
},
|
|
[1 /* Backwards */]: () => {
|
|
var _a3;
|
|
let result = focusIn(el, 2 /* Previous */);
|
|
if (result === 0 /* Error */) {
|
|
(_a3 = dom(api.button)) == null ? void 0 : _a3.focus();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
if (false) {
|
|
microTask(run);
|
|
} else {
|
|
run();
|
|
}
|
|
}
|
|
return () => {
|
|
let slot = {
|
|
open: api.popoverState.value === 0 /* Open */,
|
|
close: api.close
|
|
};
|
|
let { focus: _focus, ...theirProps } = props;
|
|
let ourProps = {
|
|
ref: api.panel,
|
|
id,
|
|
onKeydown: handleKeyDown,
|
|
onFocusout: focus && api.popoverState.value === 0 /* Open */ ? handleBlur : void 0,
|
|
tabIndex: -1
|
|
};
|
|
return render({
|
|
ourProps,
|
|
theirProps: { ...attrs, ...theirProps },
|
|
attrs,
|
|
slot,
|
|
slots: {
|
|
...slots,
|
|
default: (...args) => {
|
|
var _a3;
|
|
return [
|
|
(0, import_vue31.h)(import_vue31.Fragment, [
|
|
visible.value && api.isPortalled.value && (0, import_vue31.h)(Hidden, {
|
|
id: beforePanelSentinelId,
|
|
ref: api.beforePanelSentinel,
|
|
features: 2 /* Focusable */,
|
|
"data-headlessui-focus-guard": true,
|
|
as: "button",
|
|
type: "button",
|
|
onFocus: handleBeforeFocus
|
|
}),
|
|
(_a3 = slots.default) == null ? void 0 : _a3.call(slots, ...args),
|
|
visible.value && api.isPortalled.value && (0, import_vue31.h)(Hidden, {
|
|
id: afterPanelSentinelId,
|
|
ref: api.afterPanelSentinel,
|
|
features: 2 /* Focusable */,
|
|
"data-headlessui-focus-guard": true,
|
|
as: "button",
|
|
type: "button",
|
|
onFocus: handleAfterFocus
|
|
})
|
|
])
|
|
];
|
|
}
|
|
},
|
|
features: 1 /* RenderStrategy */ | 2 /* Static */,
|
|
visible: visible.value,
|
|
name: "PopoverPanel"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
var PopoverGroup = (0, import_vue31.defineComponent)({
|
|
name: "PopoverGroup",
|
|
inheritAttrs: false,
|
|
props: {
|
|
as: { type: [Object, String], default: "div" }
|
|
},
|
|
setup(props, { attrs, slots, expose }) {
|
|
let groupRef = (0, import_vue31.ref)(null);
|
|
let popovers = (0, import_vue31.shallowRef)([]);
|
|
let ownerDocument = (0, import_vue31.computed)(() => getOwnerDocument(groupRef));
|
|
let root = useMainTreeNode();
|
|
expose({ el: groupRef, $el: groupRef });
|
|
function unregisterPopover(registerBag) {
|
|
let idx = popovers.value.indexOf(registerBag);
|
|
if (idx !== -1)
|
|
popovers.value.splice(idx, 1);
|
|
}
|
|
function registerPopover(registerBag) {
|
|
popovers.value.push(registerBag);
|
|
return () => {
|
|
unregisterPopover(registerBag);
|
|
};
|
|
}
|
|
function isFocusWithinPopoverGroup() {
|
|
var _a2;
|
|
let owner = ownerDocument.value;
|
|
if (!owner)
|
|
return false;
|
|
let element = owner.activeElement;
|
|
if ((_a2 = dom(groupRef)) == null ? void 0 : _a2.contains(element))
|
|
return true;
|
|
return popovers.value.some((bag) => {
|
|
var _a3, _b;
|
|
return ((_a3 = owner.getElementById(bag.buttonId.value)) == null ? void 0 : _a3.contains(element)) || ((_b = owner.getElementById(bag.panelId.value)) == null ? void 0 : _b.contains(element));
|
|
});
|
|
}
|
|
function closeOthers(buttonId) {
|
|
for (let popover of popovers.value) {
|
|
if (popover.buttonId.value !== buttonId)
|
|
popover.close();
|
|
}
|
|
}
|
|
(0, import_vue31.provide)(PopoverGroupContext, {
|
|
registerPopover,
|
|
unregisterPopover,
|
|
isFocusWithinPopoverGroup,
|
|
closeOthers,
|
|
mainTreeNodeRef: root.mainTreeNodeRef
|
|
});
|
|
return () => {
|
|
let ourProps = { ref: groupRef };
|
|
return (0, import_vue31.h)(import_vue31.Fragment, [
|
|
render({
|
|
ourProps,
|
|
theirProps: { ...props, ...attrs },
|
|
slot: {},
|
|
attrs,
|
|
slots,
|
|
name: "PopoverGroup"
|
|
}),
|
|
(0, import_vue31.h)(root.MainTreeNode)
|
|
]);
|
|
};
|
|
}
|
|
});
|
|
|
|
// src/components/radio-group/radio-group.ts
|
|
var import_vue33 = require("vue");
|
|
|
|
// src/components/label/label.ts
|
|
var import_vue32 = require("vue");
|
|
var LabelContext = Symbol("LabelContext");
|
|
function useLabelContext() {
|
|
let context = (0, import_vue32.inject)(LabelContext, null);
|
|
if (context === null) {
|
|
let err = new Error("You used a <Label /> component, but it is not inside a parent.");
|
|
if (Error.captureStackTrace)
|
|
Error.captureStackTrace(err, useLabelContext);
|
|
throw err;
|
|
}
|
|
return context;
|
|
}
|
|
function useLabels({
|
|
slot = {},
|
|
name = "Label",
|
|
props = {}
|
|
} = {}) {
|
|
let labelIds = (0, import_vue32.ref)([]);
|
|
function register(value) {
|
|
labelIds.value.push(value);
|
|
return () => {
|
|
let idx = labelIds.value.indexOf(value);
|
|
if (idx === -1)
|
|
return;
|
|
labelIds.value.splice(idx, 1);
|
|
};
|
|
}
|
|
(0, import_vue32.provide)(LabelContext, { register, slot, name, props });
|
|
return (0, import_vue32.computed)(() => labelIds.value.length > 0 ? labelIds.value.join(" ") : void 0);
|
|
}
|
|
var Label = (0, import_vue32.defineComponent)({
|
|
name: "Label",
|
|
props: {
|
|
as: { type: [Object, String], default: "label" },
|
|
passive: { type: [Boolean], default: false },
|
|
id: { type: String, default: null }
|
|
},
|
|
setup(myProps, { slots, attrs }) {
|
|
var _a2;
|
|
let id = (_a2 = myProps.id) != null ? _a2 : `headlessui-label-${useId2()}`;
|
|
let context = useLabelContext();
|
|
(0, import_vue32.onMounted)(() => (0, import_vue32.onUnmounted)(context.register(id)));
|
|
return () => {
|
|
let { name = "Label", slot = {}, props = {} } = context;
|
|
let { passive, ...theirProps } = myProps;
|
|
let ourProps = {
|
|
...Object.entries(props).reduce(
|
|
(acc, [key, value]) => Object.assign(acc, { [key]: (0, import_vue32.unref)(value) }),
|
|
{}
|
|
),
|
|
id
|
|
};
|
|
if (passive) {
|
|
delete ourProps["onClick"];
|
|
delete ourProps["htmlFor"];
|
|
delete theirProps["onClick"];
|
|
}
|
|
return render({
|
|
ourProps,
|
|
theirProps,
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
name
|
|
});
|
|
};
|
|
}
|
|
});
|
|
|
|
// src/components/radio-group/radio-group.ts
|
|
function defaultComparator3(a, z) {
|
|
return a === z;
|
|
}
|
|
var RadioGroupContext = Symbol("RadioGroupContext");
|
|
function useRadioGroupContext(component) {
|
|
let context = (0, import_vue33.inject)(RadioGroupContext, null);
|
|
if (context === null) {
|
|
let err = new Error(`<${component} /> is missing a parent <RadioGroup /> component.`);
|
|
if (Error.captureStackTrace)
|
|
Error.captureStackTrace(err, useRadioGroupContext);
|
|
throw err;
|
|
}
|
|
return context;
|
|
}
|
|
var RadioGroup = (0, import_vue33.defineComponent)({
|
|
name: "RadioGroup",
|
|
emits: { "update:modelValue": (_value) => true },
|
|
props: {
|
|
as: { type: [Object, String], default: "div" },
|
|
disabled: { type: [Boolean], default: false },
|
|
by: { type: [String, Function], default: () => defaultComparator3 },
|
|
modelValue: { type: [Object, String, Number, Boolean], default: void 0 },
|
|
defaultValue: { type: [Object, String, Number, Boolean], default: void 0 },
|
|
form: { type: String, optional: true },
|
|
name: { type: String, optional: true },
|
|
id: { type: String, default: null }
|
|
},
|
|
inheritAttrs: false,
|
|
setup(props, { emit, attrs, slots, expose }) {
|
|
var _a2;
|
|
let id = (_a2 = props.id) != null ? _a2 : `headlessui-radiogroup-${useId2()}`;
|
|
let radioGroupRef = (0, import_vue33.ref)(null);
|
|
let options = (0, import_vue33.ref)([]);
|
|
let labelledby = useLabels({ name: "RadioGroupLabel" });
|
|
let describedby = useDescriptions({ name: "RadioGroupDescription" });
|
|
expose({ el: radioGroupRef, $el: radioGroupRef });
|
|
let [value, theirOnChange] = useControllable(
|
|
(0, import_vue33.computed)(() => props.modelValue),
|
|
(value2) => emit("update:modelValue", value2),
|
|
(0, import_vue33.computed)(() => props.defaultValue)
|
|
);
|
|
let api = {
|
|
options,
|
|
value,
|
|
disabled: (0, import_vue33.computed)(() => props.disabled),
|
|
firstOption: (0, import_vue33.computed)(
|
|
() => options.value.find((option) => {
|
|
if (option.propsRef.disabled)
|
|
return false;
|
|
return true;
|
|
})
|
|
),
|
|
containsCheckedOption: (0, import_vue33.computed)(
|
|
() => options.value.some(
|
|
(option) => api.compare((0, import_vue33.toRaw)(option.propsRef.value), (0, import_vue33.toRaw)(props.modelValue))
|
|
)
|
|
),
|
|
compare(a, z) {
|
|
if (typeof props.by === "string") {
|
|
let property = props.by;
|
|
return (a == null ? void 0 : a[property]) === (z == null ? void 0 : z[property]);
|
|
}
|
|
return props.by(a, z);
|
|
},
|
|
change(nextValue) {
|
|
var _a3;
|
|
if (props.disabled)
|
|
return false;
|
|
if (api.compare((0, import_vue33.toRaw)(value.value), (0, import_vue33.toRaw)(nextValue)))
|
|
return false;
|
|
let nextOption = (_a3 = options.value.find(
|
|
(option) => api.compare((0, import_vue33.toRaw)(option.propsRef.value), (0, import_vue33.toRaw)(nextValue))
|
|
)) == null ? void 0 : _a3.propsRef;
|
|
if (nextOption == null ? void 0 : nextOption.disabled)
|
|
return false;
|
|
theirOnChange(nextValue);
|
|
return true;
|
|
},
|
|
registerOption(action) {
|
|
options.value.push(action);
|
|
options.value = sortByDomNode(options.value, (option) => option.element);
|
|
},
|
|
unregisterOption(id2) {
|
|
let idx = options.value.findIndex((radio) => radio.id === id2);
|
|
if (idx === -1)
|
|
return;
|
|
options.value.splice(idx, 1);
|
|
}
|
|
};
|
|
(0, import_vue33.provide)(RadioGroupContext, api);
|
|
useTreeWalker({
|
|
container: (0, import_vue33.computed)(() => dom(radioGroupRef)),
|
|
accept(node) {
|
|
if (node.getAttribute("role") === "radio")
|
|
return NodeFilter.FILTER_REJECT;
|
|
if (node.hasAttribute("role"))
|
|
return NodeFilter.FILTER_SKIP;
|
|
return NodeFilter.FILTER_ACCEPT;
|
|
},
|
|
walk(node) {
|
|
node.setAttribute("role", "none");
|
|
}
|
|
});
|
|
function handleKeyDown(event) {
|
|
if (!radioGroupRef.value)
|
|
return;
|
|
if (!radioGroupRef.value.contains(event.target))
|
|
return;
|
|
let all = options.value.filter((option) => option.propsRef.disabled === false).map((radio) => radio.element);
|
|
switch (event.key) {
|
|
case "Enter" /* Enter */:
|
|
attemptSubmit(event.currentTarget);
|
|
break;
|
|
case "ArrowLeft" /* ArrowLeft */:
|
|
case "ArrowUp" /* ArrowUp */:
|
|
{
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
let result = focusIn(all, 2 /* Previous */ | 16 /* WrapAround */);
|
|
if (result === 2 /* Success */) {
|
|
let activeOption = options.value.find(
|
|
(option) => {
|
|
var _a3;
|
|
return option.element === ((_a3 = getOwnerDocument(radioGroupRef)) == null ? void 0 : _a3.activeElement);
|
|
}
|
|
);
|
|
if (activeOption)
|
|
api.change(activeOption.propsRef.value);
|
|
}
|
|
}
|
|
break;
|
|
case "ArrowRight" /* ArrowRight */:
|
|
case "ArrowDown" /* ArrowDown */:
|
|
{
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
let result = focusIn(all, 4 /* Next */ | 16 /* WrapAround */);
|
|
if (result === 2 /* Success */) {
|
|
let activeOption = options.value.find(
|
|
(option) => {
|
|
var _a3;
|
|
return option.element === ((_a3 = getOwnerDocument(option.element)) == null ? void 0 : _a3.activeElement);
|
|
}
|
|
);
|
|
if (activeOption)
|
|
api.change(activeOption.propsRef.value);
|
|
}
|
|
}
|
|
break;
|
|
case " " /* Space */:
|
|
{
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
let activeOption = options.value.find(
|
|
(option) => {
|
|
var _a3;
|
|
return option.element === ((_a3 = getOwnerDocument(option.element)) == null ? void 0 : _a3.activeElement);
|
|
}
|
|
);
|
|
if (activeOption)
|
|
api.change(activeOption.propsRef.value);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
let form = (0, import_vue33.computed)(() => {
|
|
var _a3;
|
|
return (_a3 = dom(radioGroupRef)) == null ? void 0 : _a3.closest("form");
|
|
});
|
|
(0, import_vue33.onMounted)(() => {
|
|
(0, import_vue33.watch)(
|
|
[form],
|
|
() => {
|
|
if (!form.value)
|
|
return;
|
|
if (props.defaultValue === void 0)
|
|
return;
|
|
function handle() {
|
|
api.change(props.defaultValue);
|
|
}
|
|
form.value.addEventListener("reset", handle);
|
|
return () => {
|
|
var _a3;
|
|
(_a3 = form.value) == null ? void 0 : _a3.removeEventListener("reset", handle);
|
|
};
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
});
|
|
return () => {
|
|
let { disabled, name, form: form2, ...theirProps } = props;
|
|
let ourProps = {
|
|
ref: radioGroupRef,
|
|
id,
|
|
role: "radiogroup",
|
|
"aria-labelledby": labelledby.value,
|
|
"aria-describedby": describedby.value,
|
|
onKeydown: handleKeyDown
|
|
};
|
|
return (0, import_vue33.h)(import_vue33.Fragment, [
|
|
...name != null && value.value != null ? objectToFormEntries({ [name]: value.value }).map(
|
|
([name2, value2]) => (0, import_vue33.h)(
|
|
Hidden,
|
|
compact({
|
|
features: 4 /* Hidden */,
|
|
key: name2,
|
|
as: "input",
|
|
type: "hidden",
|
|
hidden: true,
|
|
readOnly: true,
|
|
form: form2,
|
|
disabled,
|
|
name: name2,
|
|
value: value2
|
|
})
|
|
)
|
|
) : [],
|
|
render({
|
|
ourProps,
|
|
theirProps: { ...attrs, ...omit(theirProps, ["modelValue", "defaultValue", "by"]) },
|
|
slot: {},
|
|
attrs,
|
|
slots,
|
|
name: "RadioGroup"
|
|
})
|
|
]);
|
|
};
|
|
}
|
|
});
|
|
var RadioGroupOption = (0, import_vue33.defineComponent)({
|
|
name: "RadioGroupOption",
|
|
props: {
|
|
as: { type: [Object, String], default: "div" },
|
|
value: { type: [Object, String, Number, Boolean] },
|
|
disabled: { type: Boolean, default: false },
|
|
id: { type: String, default: null }
|
|
},
|
|
setup(props, { attrs, slots, expose }) {
|
|
var _a2;
|
|
let id = (_a2 = props.id) != null ? _a2 : `headlessui-radiogroup-option-${useId2()}`;
|
|
let api = useRadioGroupContext("RadioGroupOption");
|
|
let labelledby = useLabels({ name: "RadioGroupLabel" });
|
|
let describedby = useDescriptions({ name: "RadioGroupDescription" });
|
|
let optionRef = (0, import_vue33.ref)(null);
|
|
let propsRef = (0, import_vue33.computed)(() => ({ value: props.value, disabled: props.disabled }));
|
|
let state = (0, import_vue33.ref)(1 /* Empty */);
|
|
expose({ el: optionRef, $el: optionRef });
|
|
let element = (0, import_vue33.computed)(() => dom(optionRef));
|
|
(0, import_vue33.onMounted)(() => api.registerOption({ id, element, propsRef }));
|
|
(0, import_vue33.onUnmounted)(() => api.unregisterOption(id));
|
|
let isFirstOption = (0, import_vue33.computed)(() => {
|
|
var _a3;
|
|
return ((_a3 = api.firstOption.value) == null ? void 0 : _a3.id) === id;
|
|
});
|
|
let disabled = (0, import_vue33.computed)(() => api.disabled.value || props.disabled);
|
|
let checked = (0, import_vue33.computed)(() => api.compare((0, import_vue33.toRaw)(api.value.value), (0, import_vue33.toRaw)(props.value)));
|
|
let tabIndex = (0, import_vue33.computed)(() => {
|
|
if (disabled.value)
|
|
return -1;
|
|
if (checked.value)
|
|
return 0;
|
|
if (!api.containsCheckedOption.value && isFirstOption.value)
|
|
return 0;
|
|
return -1;
|
|
});
|
|
function handleClick() {
|
|
var _a3;
|
|
if (!api.change(props.value))
|
|
return;
|
|
state.value |= 2 /* Active */;
|
|
(_a3 = dom(optionRef)) == null ? void 0 : _a3.focus();
|
|
}
|
|
function handleFocus() {
|
|
state.value |= 2 /* Active */;
|
|
}
|
|
function handleBlur() {
|
|
state.value &= ~2 /* Active */;
|
|
}
|
|
return () => {
|
|
let { value: _value, disabled: _disabled, ...theirProps } = props;
|
|
let slot = {
|
|
checked: checked.value,
|
|
disabled: disabled.value,
|
|
active: Boolean(state.value & 2 /* Active */)
|
|
};
|
|
let ourProps = {
|
|
id,
|
|
ref: optionRef,
|
|
role: "radio",
|
|
"aria-checked": checked.value ? "true" : "false",
|
|
"aria-labelledby": labelledby.value,
|
|
"aria-describedby": describedby.value,
|
|
"aria-disabled": disabled.value ? true : void 0,
|
|
tabIndex: tabIndex.value,
|
|
onClick: disabled.value ? void 0 : handleClick,
|
|
onFocus: disabled.value ? void 0 : handleFocus,
|
|
onBlur: disabled.value ? void 0 : handleBlur
|
|
};
|
|
return render({
|
|
ourProps,
|
|
theirProps,
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
name: "RadioGroupOption"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
var RadioGroupLabel = Label;
|
|
var RadioGroupDescription = Description;
|
|
|
|
// src/components/switch/switch.ts
|
|
var import_vue34 = require("vue");
|
|
var GroupContext = Symbol("GroupContext");
|
|
var SwitchGroup = (0, import_vue34.defineComponent)({
|
|
name: "SwitchGroup",
|
|
props: {
|
|
as: { type: [Object, String], default: "template" }
|
|
},
|
|
setup(props, { slots, attrs }) {
|
|
let switchRef = (0, import_vue34.ref)(null);
|
|
let labelledby = useLabels({
|
|
name: "SwitchLabel",
|
|
props: {
|
|
htmlFor: (0, import_vue34.computed)(() => {
|
|
var _a2;
|
|
return (_a2 = switchRef.value) == null ? void 0 : _a2.id;
|
|
}),
|
|
onClick(event) {
|
|
if (!switchRef.value)
|
|
return;
|
|
if (event.currentTarget.tagName === "LABEL") {
|
|
event.preventDefault();
|
|
}
|
|
switchRef.value.click();
|
|
switchRef.value.focus({ preventScroll: true });
|
|
}
|
|
}
|
|
});
|
|
let describedby = useDescriptions({ name: "SwitchDescription" });
|
|
let api = { switchRef, labelledby, describedby };
|
|
(0, import_vue34.provide)(GroupContext, api);
|
|
return () => render({ theirProps: props, ourProps: {}, slot: {}, slots, attrs, name: "SwitchGroup" });
|
|
}
|
|
});
|
|
var Switch = (0, import_vue34.defineComponent)({
|
|
name: "Switch",
|
|
emits: { "update:modelValue": (_value) => true },
|
|
props: {
|
|
as: { type: [Object, String], default: "button" },
|
|
modelValue: { type: Boolean, default: void 0 },
|
|
defaultChecked: { type: Boolean, optional: true },
|
|
form: { type: String, optional: true },
|
|
name: { type: String, optional: true },
|
|
value: { type: String, optional: true },
|
|
id: { type: String, default: null },
|
|
disabled: { type: Boolean, default: false },
|
|
tabIndex: { type: Number, default: 0 }
|
|
},
|
|
inheritAttrs: false,
|
|
setup(props, { emit, attrs, slots, expose }) {
|
|
var _a2;
|
|
let id = (_a2 = props.id) != null ? _a2 : `headlessui-switch-${useId2()}`;
|
|
let api = (0, import_vue34.inject)(GroupContext, null);
|
|
let [checked, theirOnChange] = useControllable(
|
|
(0, import_vue34.computed)(() => props.modelValue),
|
|
(value) => emit("update:modelValue", value),
|
|
(0, import_vue34.computed)(() => props.defaultChecked)
|
|
);
|
|
function toggle() {
|
|
theirOnChange(!checked.value);
|
|
}
|
|
let internalSwitchRef = (0, import_vue34.ref)(null);
|
|
let switchRef = api === null ? internalSwitchRef : api.switchRef;
|
|
let type = useResolveButtonType(
|
|
(0, import_vue34.computed)(() => ({ as: props.as, type: attrs.type })),
|
|
switchRef
|
|
);
|
|
expose({ el: switchRef, $el: switchRef });
|
|
function handleClick(event) {
|
|
event.preventDefault();
|
|
toggle();
|
|
}
|
|
function handleKeyUp(event) {
|
|
if (event.key === " " /* Space */) {
|
|
event.preventDefault();
|
|
toggle();
|
|
} else if (event.key === "Enter" /* Enter */) {
|
|
attemptSubmit(event.currentTarget);
|
|
}
|
|
}
|
|
function handleKeyPress(event) {
|
|
event.preventDefault();
|
|
}
|
|
let form = (0, import_vue34.computed)(() => {
|
|
var _a3, _b;
|
|
return (_b = (_a3 = dom(switchRef)) == null ? void 0 : _a3.closest) == null ? void 0 : _b.call(_a3, "form");
|
|
});
|
|
(0, import_vue34.onMounted)(() => {
|
|
(0, import_vue34.watch)(
|
|
[form],
|
|
() => {
|
|
if (!form.value)
|
|
return;
|
|
if (props.defaultChecked === void 0)
|
|
return;
|
|
function handle() {
|
|
theirOnChange(props.defaultChecked);
|
|
}
|
|
form.value.addEventListener("reset", handle);
|
|
return () => {
|
|
var _a3;
|
|
(_a3 = form.value) == null ? void 0 : _a3.removeEventListener("reset", handle);
|
|
};
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
});
|
|
return () => {
|
|
let { name, value, form: form2, tabIndex, ...theirProps } = props;
|
|
let slot = { checked: checked.value };
|
|
let ourProps = {
|
|
id,
|
|
ref: switchRef,
|
|
role: "switch",
|
|
type: type.value,
|
|
tabIndex: tabIndex === -1 ? 0 : tabIndex,
|
|
"aria-checked": checked.value,
|
|
"aria-labelledby": api == null ? void 0 : api.labelledby.value,
|
|
"aria-describedby": api == null ? void 0 : api.describedby.value,
|
|
onClick: handleClick,
|
|
onKeyup: handleKeyUp,
|
|
onKeypress: handleKeyPress
|
|
};
|
|
return (0, import_vue34.h)(import_vue34.Fragment, [
|
|
name != null && checked.value != null ? (0, import_vue34.h)(
|
|
Hidden,
|
|
compact({
|
|
features: 4 /* Hidden */,
|
|
as: "input",
|
|
type: "checkbox",
|
|
hidden: true,
|
|
readOnly: true,
|
|
checked: checked.value,
|
|
form: form2,
|
|
disabled: theirProps.disabled,
|
|
name,
|
|
value
|
|
})
|
|
) : null,
|
|
render({
|
|
ourProps,
|
|
theirProps: { ...attrs, ...omit(theirProps, ["modelValue", "defaultChecked"]) },
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
name: "Switch"
|
|
})
|
|
]);
|
|
};
|
|
}
|
|
});
|
|
var SwitchLabel = Label;
|
|
var SwitchDescription = Description;
|
|
|
|
// src/components/tabs/tabs.ts
|
|
var import_vue36 = require("vue");
|
|
|
|
// src/internal/focus-sentinel.ts
|
|
var import_vue35 = require("vue");
|
|
var FocusSentinel = (0, import_vue35.defineComponent)({
|
|
props: {
|
|
onFocus: {
|
|
type: Function,
|
|
required: true
|
|
}
|
|
},
|
|
setup(props) {
|
|
let enabled = (0, import_vue35.ref)(true);
|
|
return () => {
|
|
if (!enabled.value)
|
|
return null;
|
|
return (0, import_vue35.h)(Hidden, {
|
|
as: "button",
|
|
type: "button",
|
|
features: 2 /* Focusable */,
|
|
onFocus(event) {
|
|
event.preventDefault();
|
|
let frame;
|
|
let tries = 50;
|
|
function forwardFocus() {
|
|
var _a2;
|
|
if (tries-- <= 0) {
|
|
if (frame)
|
|
cancelAnimationFrame(frame);
|
|
return;
|
|
}
|
|
if ((_a2 = props.onFocus) == null ? void 0 : _a2.call(props)) {
|
|
enabled.value = false;
|
|
cancelAnimationFrame(frame);
|
|
return;
|
|
}
|
|
frame = requestAnimationFrame(forwardFocus);
|
|
}
|
|
frame = requestAnimationFrame(forwardFocus);
|
|
}
|
|
});
|
|
};
|
|
}
|
|
});
|
|
|
|
// src/components/tabs/tabs.ts
|
|
var TabsContext = Symbol("TabsContext");
|
|
function useTabsContext(component) {
|
|
let context = (0, import_vue36.inject)(TabsContext, null);
|
|
if (context === null) {
|
|
let err = new Error(`<${component} /> is missing a parent <TabGroup /> component.`);
|
|
if (Error.captureStackTrace)
|
|
Error.captureStackTrace(err, useTabsContext);
|
|
throw err;
|
|
}
|
|
return context;
|
|
}
|
|
var TabsSSRContext = Symbol("TabsSSRContext");
|
|
var TabGroup = (0, import_vue36.defineComponent)({
|
|
name: "TabGroup",
|
|
emits: {
|
|
change: (_index) => true
|
|
},
|
|
props: {
|
|
as: { type: [Object, String], default: "template" },
|
|
selectedIndex: { type: [Number], default: null },
|
|
defaultIndex: { type: [Number], default: 0 },
|
|
vertical: { type: [Boolean], default: false },
|
|
manual: { type: [Boolean], default: false }
|
|
},
|
|
inheritAttrs: false,
|
|
setup(props, { slots, attrs, emit }) {
|
|
var _a2;
|
|
let selectedIndex = (0, import_vue36.ref)(
|
|
(_a2 = props.selectedIndex) != null ? _a2 : props.defaultIndex
|
|
);
|
|
let tabs = (0, import_vue36.ref)([]);
|
|
let panels = (0, import_vue36.ref)([]);
|
|
let isControlled = (0, import_vue36.computed)(() => props.selectedIndex !== null);
|
|
let realSelectedIndex = (0, import_vue36.computed)(
|
|
() => isControlled.value ? props.selectedIndex : selectedIndex.value
|
|
);
|
|
function setSelectedIndex(indexToSet) {
|
|
var _a3;
|
|
let tabs2 = sortByDomNode(api.tabs.value, dom);
|
|
let panels2 = sortByDomNode(api.panels.value, dom);
|
|
let focusableTabs = tabs2.filter((tab) => {
|
|
var _a4;
|
|
return !((_a4 = dom(tab)) == null ? void 0 : _a4.hasAttribute("disabled"));
|
|
});
|
|
if (
|
|
// Underflow
|
|
indexToSet < 0 || // Overflow
|
|
indexToSet > tabs2.length - 1
|
|
) {
|
|
let direction = match(
|
|
selectedIndex.value === null ? 0 /* Equal */ : Math.sign(indexToSet - selectedIndex.value),
|
|
{
|
|
[-1 /* Less */]: () => 1 /* Backwards */,
|
|
[0 /* Equal */]: () => {
|
|
return match(Math.sign(indexToSet), {
|
|
[-1 /* Less */]: () => 0 /* Forwards */,
|
|
[0 /* Equal */]: () => 0 /* Forwards */,
|
|
[1 /* Greater */]: () => 1 /* Backwards */
|
|
});
|
|
},
|
|
[1 /* Greater */]: () => 0 /* Forwards */
|
|
}
|
|
);
|
|
let nextSelectedIndex = match(direction, {
|
|
[0 /* Forwards */]: () => tabs2.indexOf(focusableTabs[0]),
|
|
[1 /* Backwards */]: () => tabs2.indexOf(focusableTabs[focusableTabs.length - 1])
|
|
});
|
|
if (nextSelectedIndex !== -1) {
|
|
selectedIndex.value = nextSelectedIndex;
|
|
}
|
|
api.tabs.value = tabs2;
|
|
api.panels.value = panels2;
|
|
} else {
|
|
let before = tabs2.slice(0, indexToSet);
|
|
let after = tabs2.slice(indexToSet);
|
|
let next = [...after, ...before].find((tab) => focusableTabs.includes(tab));
|
|
if (!next)
|
|
return;
|
|
let localSelectedIndex = (_a3 = tabs2.indexOf(next)) != null ? _a3 : api.selectedIndex.value;
|
|
if (localSelectedIndex === -1)
|
|
localSelectedIndex = api.selectedIndex.value;
|
|
selectedIndex.value = localSelectedIndex;
|
|
api.tabs.value = tabs2;
|
|
api.panels.value = panels2;
|
|
}
|
|
}
|
|
let api = {
|
|
selectedIndex: (0, import_vue36.computed)(() => {
|
|
var _a3, _b;
|
|
return (_b = (_a3 = selectedIndex.value) != null ? _a3 : props.defaultIndex) != null ? _b : null;
|
|
}),
|
|
orientation: (0, import_vue36.computed)(() => props.vertical ? "vertical" : "horizontal"),
|
|
activation: (0, import_vue36.computed)(() => props.manual ? "manual" : "auto"),
|
|
tabs,
|
|
panels,
|
|
setSelectedIndex(index) {
|
|
if (realSelectedIndex.value !== index) {
|
|
emit("change", index);
|
|
}
|
|
if (!isControlled.value) {
|
|
setSelectedIndex(index);
|
|
}
|
|
},
|
|
registerTab(tab) {
|
|
var _a3;
|
|
if (tabs.value.includes(tab))
|
|
return;
|
|
let activeTab = tabs.value[selectedIndex.value];
|
|
tabs.value.push(tab);
|
|
tabs.value = sortByDomNode(tabs.value, dom);
|
|
if (!isControlled.value) {
|
|
let localSelectedIndex = (_a3 = tabs.value.indexOf(activeTab)) != null ? _a3 : selectedIndex.value;
|
|
if (localSelectedIndex !== -1) {
|
|
selectedIndex.value = localSelectedIndex;
|
|
}
|
|
}
|
|
},
|
|
unregisterTab(tab) {
|
|
let idx = tabs.value.indexOf(tab);
|
|
if (idx !== -1)
|
|
tabs.value.splice(idx, 1);
|
|
},
|
|
registerPanel(panel) {
|
|
if (panels.value.includes(panel))
|
|
return;
|
|
panels.value.push(panel);
|
|
panels.value = sortByDomNode(panels.value, dom);
|
|
},
|
|
unregisterPanel(panel) {
|
|
let idx = panels.value.indexOf(panel);
|
|
if (idx !== -1)
|
|
panels.value.splice(idx, 1);
|
|
}
|
|
};
|
|
(0, import_vue36.provide)(TabsContext, api);
|
|
let SSRCounter = (0, import_vue36.ref)({ tabs: [], panels: [] });
|
|
let mounted = (0, import_vue36.ref)(false);
|
|
(0, import_vue36.onMounted)(() => {
|
|
mounted.value = true;
|
|
});
|
|
(0, import_vue36.provide)(
|
|
TabsSSRContext,
|
|
(0, import_vue36.computed)(() => mounted.value ? null : SSRCounter.value)
|
|
);
|
|
let incomingSelectedIndex = (0, import_vue36.computed)(() => props.selectedIndex);
|
|
(0, import_vue36.onMounted)(() => {
|
|
(0, import_vue36.watch)(
|
|
[
|
|
incomingSelectedIndex
|
|
/* Deliberately skipping defaultIndex */
|
|
],
|
|
() => {
|
|
var _a3;
|
|
return setSelectedIndex((_a3 = props.selectedIndex) != null ? _a3 : props.defaultIndex);
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
});
|
|
(0, import_vue36.watchEffect)(() => {
|
|
if (!isControlled.value)
|
|
return;
|
|
if (realSelectedIndex.value == null)
|
|
return;
|
|
if (api.tabs.value.length <= 0)
|
|
return;
|
|
let sorted = sortByDomNode(api.tabs.value, dom);
|
|
let didOrderChange = sorted.some((tab, i) => dom(api.tabs.value[i]) !== dom(tab));
|
|
if (didOrderChange) {
|
|
api.setSelectedIndex(
|
|
sorted.findIndex((x) => dom(x) === dom(api.tabs.value[realSelectedIndex.value]))
|
|
);
|
|
}
|
|
});
|
|
return () => {
|
|
let slot = { selectedIndex: selectedIndex.value };
|
|
return (0, import_vue36.h)(import_vue36.Fragment, [
|
|
tabs.value.length <= 0 && (0, import_vue36.h)(FocusSentinel, {
|
|
onFocus: () => {
|
|
for (let tab of tabs.value) {
|
|
let el = dom(tab);
|
|
if ((el == null ? void 0 : el.tabIndex) === 0) {
|
|
el.focus();
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}),
|
|
render({
|
|
theirProps: {
|
|
...attrs,
|
|
...omit(props, ["selectedIndex", "defaultIndex", "manual", "vertical", "onChange"])
|
|
},
|
|
ourProps: {},
|
|
slot,
|
|
slots,
|
|
attrs,
|
|
name: "TabGroup"
|
|
})
|
|
]);
|
|
};
|
|
}
|
|
});
|
|
var TabList = (0, import_vue36.defineComponent)({
|
|
name: "TabList",
|
|
props: {
|
|
as: { type: [Object, String], default: "div" }
|
|
},
|
|
setup(props, { attrs, slots }) {
|
|
let api = useTabsContext("TabList");
|
|
return () => {
|
|
let slot = { selectedIndex: api.selectedIndex.value };
|
|
let ourProps = {
|
|
role: "tablist",
|
|
"aria-orientation": api.orientation.value
|
|
};
|
|
let theirProps = props;
|
|
return render({
|
|
ourProps,
|
|
theirProps,
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
name: "TabList"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
var Tab = (0, import_vue36.defineComponent)({
|
|
name: "Tab",
|
|
props: {
|
|
as: { type: [Object, String], default: "button" },
|
|
disabled: { type: [Boolean], default: false },
|
|
id: { type: String, default: null }
|
|
},
|
|
setup(props, { attrs, slots, expose }) {
|
|
var _a2;
|
|
let id = (_a2 = props.id) != null ? _a2 : `headlessui-tabs-tab-${useId2()}`;
|
|
let api = useTabsContext("Tab");
|
|
let internalTabRef = (0, import_vue36.ref)(null);
|
|
expose({ el: internalTabRef, $el: internalTabRef });
|
|
(0, import_vue36.onMounted)(() => api.registerTab(internalTabRef));
|
|
(0, import_vue36.onUnmounted)(() => api.unregisterTab(internalTabRef));
|
|
let SSRContext = (0, import_vue36.inject)(TabsSSRContext);
|
|
let mySSRIndex = (0, import_vue36.computed)(() => {
|
|
if (SSRContext.value) {
|
|
let mySSRIndex2 = SSRContext.value.tabs.indexOf(id);
|
|
if (mySSRIndex2 === -1)
|
|
return SSRContext.value.tabs.push(id) - 1;
|
|
return mySSRIndex2;
|
|
}
|
|
return -1;
|
|
});
|
|
let myIndex = (0, import_vue36.computed)(() => {
|
|
let myIndex2 = api.tabs.value.indexOf(internalTabRef);
|
|
if (myIndex2 === -1)
|
|
return mySSRIndex.value;
|
|
return myIndex2;
|
|
});
|
|
let selected = (0, import_vue36.computed)(() => myIndex.value === api.selectedIndex.value);
|
|
function activateUsing(cb) {
|
|
var _a3;
|
|
let result = cb();
|
|
if (result === 2 /* Success */ && api.activation.value === "auto") {
|
|
let newTab = (_a3 = getOwnerDocument(internalTabRef)) == null ? void 0 : _a3.activeElement;
|
|
let idx = api.tabs.value.findIndex((tab) => dom(tab) === newTab);
|
|
if (idx !== -1)
|
|
api.setSelectedIndex(idx);
|
|
}
|
|
return result;
|
|
}
|
|
function handleKeyDown(event) {
|
|
let list = api.tabs.value.map((tab) => dom(tab)).filter(Boolean);
|
|
if (event.key === " " /* Space */ || event.key === "Enter" /* Enter */) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
api.setSelectedIndex(myIndex.value);
|
|
return;
|
|
}
|
|
switch (event.key) {
|
|
case "Home" /* Home */:
|
|
case "PageUp" /* PageUp */:
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return activateUsing(() => focusIn(list, 1 /* First */));
|
|
case "End" /* End */:
|
|
case "PageDown" /* PageDown */:
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
return activateUsing(() => focusIn(list, 8 /* Last */));
|
|
}
|
|
let result = activateUsing(
|
|
() => match(api.orientation.value, {
|
|
vertical() {
|
|
if (event.key === "ArrowUp" /* ArrowUp */)
|
|
return focusIn(list, 2 /* Previous */ | 16 /* WrapAround */);
|
|
if (event.key === "ArrowDown" /* ArrowDown */)
|
|
return focusIn(list, 4 /* Next */ | 16 /* WrapAround */);
|
|
return 0 /* Error */;
|
|
},
|
|
horizontal() {
|
|
if (event.key === "ArrowLeft" /* ArrowLeft */)
|
|
return focusIn(list, 2 /* Previous */ | 16 /* WrapAround */);
|
|
if (event.key === "ArrowRight" /* ArrowRight */)
|
|
return focusIn(list, 4 /* Next */ | 16 /* WrapAround */);
|
|
return 0 /* Error */;
|
|
}
|
|
})
|
|
);
|
|
if (result === 2 /* Success */) {
|
|
return event.preventDefault();
|
|
}
|
|
}
|
|
let ready = (0, import_vue36.ref)(false);
|
|
function handleSelection() {
|
|
var _a3;
|
|
if (ready.value)
|
|
return;
|
|
ready.value = true;
|
|
if (props.disabled)
|
|
return;
|
|
(_a3 = dom(internalTabRef)) == null ? void 0 : _a3.focus({ preventScroll: true });
|
|
api.setSelectedIndex(myIndex.value);
|
|
microTask(() => {
|
|
ready.value = false;
|
|
});
|
|
}
|
|
function handleMouseDown(event) {
|
|
event.preventDefault();
|
|
}
|
|
let type = useResolveButtonType(
|
|
(0, import_vue36.computed)(() => ({ as: props.as, type: attrs.type })),
|
|
internalTabRef
|
|
);
|
|
return () => {
|
|
var _a3, _b;
|
|
let slot = { selected: selected.value, disabled: (_a3 = props.disabled) != null ? _a3 : false };
|
|
let { ...theirProps } = props;
|
|
let ourProps = {
|
|
ref: internalTabRef,
|
|
onKeydown: handleKeyDown,
|
|
onMousedown: handleMouseDown,
|
|
onClick: handleSelection,
|
|
id,
|
|
role: "tab",
|
|
type: type.value,
|
|
"aria-controls": (_b = dom(api.panels.value[myIndex.value])) == null ? void 0 : _b.id,
|
|
"aria-selected": selected.value,
|
|
tabIndex: selected.value ? 0 : -1,
|
|
disabled: props.disabled ? true : void 0
|
|
};
|
|
return render({
|
|
ourProps,
|
|
theirProps,
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
name: "Tab"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
var TabPanels = (0, import_vue36.defineComponent)({
|
|
name: "TabPanels",
|
|
props: {
|
|
as: { type: [Object, String], default: "div" }
|
|
},
|
|
setup(props, { slots, attrs }) {
|
|
let api = useTabsContext("TabPanels");
|
|
return () => {
|
|
let slot = { selectedIndex: api.selectedIndex.value };
|
|
return render({
|
|
theirProps: props,
|
|
ourProps: {},
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
name: "TabPanels"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
var TabPanel = (0, import_vue36.defineComponent)({
|
|
name: "TabPanel",
|
|
props: {
|
|
as: { type: [Object, String], default: "div" },
|
|
static: { type: Boolean, default: false },
|
|
unmount: { type: Boolean, default: true },
|
|
id: { type: String, default: null },
|
|
tabIndex: { type: Number, default: 0 }
|
|
},
|
|
setup(props, { attrs, slots, expose }) {
|
|
var _a2;
|
|
let id = (_a2 = props.id) != null ? _a2 : `headlessui-tabs-panel-${useId2()}`;
|
|
let api = useTabsContext("TabPanel");
|
|
let internalPanelRef = (0, import_vue36.ref)(null);
|
|
expose({ el: internalPanelRef, $el: internalPanelRef });
|
|
(0, import_vue36.onMounted)(() => api.registerPanel(internalPanelRef));
|
|
(0, import_vue36.onUnmounted)(() => api.unregisterPanel(internalPanelRef));
|
|
let SSRContext = (0, import_vue36.inject)(TabsSSRContext);
|
|
let mySSRIndex = (0, import_vue36.computed)(() => {
|
|
if (SSRContext.value) {
|
|
let mySSRIndex2 = SSRContext.value.panels.indexOf(id);
|
|
if (mySSRIndex2 === -1)
|
|
return SSRContext.value.panels.push(id) - 1;
|
|
return mySSRIndex2;
|
|
}
|
|
return -1;
|
|
});
|
|
let myIndex = (0, import_vue36.computed)(() => {
|
|
let myIndex2 = api.panels.value.indexOf(internalPanelRef);
|
|
if (myIndex2 === -1)
|
|
return mySSRIndex.value;
|
|
return myIndex2;
|
|
});
|
|
let selected = (0, import_vue36.computed)(() => myIndex.value === api.selectedIndex.value);
|
|
return () => {
|
|
var _a3;
|
|
let slot = { selected: selected.value };
|
|
let { tabIndex, ...theirProps } = props;
|
|
let ourProps = {
|
|
ref: internalPanelRef,
|
|
id,
|
|
role: "tabpanel",
|
|
"aria-labelledby": (_a3 = dom(api.tabs.value[myIndex.value])) == null ? void 0 : _a3.id,
|
|
tabIndex: selected.value ? tabIndex : -1
|
|
};
|
|
if (!selected.value && props.unmount && !props.static) {
|
|
return (0, import_vue36.h)(Hidden, { as: "span", "aria-hidden": true, ...ourProps });
|
|
}
|
|
return render({
|
|
ourProps,
|
|
theirProps,
|
|
slot,
|
|
attrs,
|
|
slots,
|
|
features: 2 /* Static */ | 1 /* RenderStrategy */,
|
|
visible: selected.value,
|
|
name: "TabPanel"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
|
|
// src/components/transitions/transition.ts
|
|
var import_vue37 = require("vue");
|
|
|
|
// src/utils/once.ts
|
|
function once(cb) {
|
|
let state = { called: false };
|
|
return (...args) => {
|
|
if (state.called)
|
|
return;
|
|
state.called = true;
|
|
return cb(...args);
|
|
};
|
|
}
|
|
|
|
// src/components/transitions/utils/transition.ts
|
|
function addClasses(node, ...classes) {
|
|
node && classes.length > 0 && node.classList.add(...classes);
|
|
}
|
|
function removeClasses(node, ...classes) {
|
|
node && classes.length > 0 && node.classList.remove(...classes);
|
|
}
|
|
function waitForTransition(node, done) {
|
|
let d = disposables();
|
|
if (!node)
|
|
return d.dispose;
|
|
let { transitionDuration, transitionDelay } = getComputedStyle(node);
|
|
let [durationMs, delaysMs] = [transitionDuration, transitionDelay].map((value) => {
|
|
let [resolvedValue = 0] = value.split(",").filter(Boolean).map((v) => v.includes("ms") ? parseFloat(v) : parseFloat(v) * 1e3).sort((a, z) => z - a);
|
|
return resolvedValue;
|
|
});
|
|
if (durationMs !== 0) {
|
|
d.setTimeout(() => done("finished" /* Finished */), durationMs + delaysMs);
|
|
} else {
|
|
done("finished" /* Finished */);
|
|
}
|
|
d.add(() => done("cancelled" /* Cancelled */));
|
|
return d.dispose;
|
|
}
|
|
function transition(node, base, from, to, entered, done) {
|
|
let d = disposables();
|
|
let _done = done !== void 0 ? once(done) : () => {
|
|
};
|
|
removeClasses(node, ...entered);
|
|
addClasses(node, ...base, ...from);
|
|
d.nextFrame(() => {
|
|
removeClasses(node, ...from);
|
|
addClasses(node, ...to);
|
|
d.add(
|
|
waitForTransition(node, (reason) => {
|
|
removeClasses(node, ...to, ...base);
|
|
addClasses(node, ...entered);
|
|
return _done(reason);
|
|
})
|
|
);
|
|
});
|
|
d.add(() => removeClasses(node, ...base, ...from, ...to, ...entered));
|
|
d.add(() => _done("cancelled" /* Cancelled */));
|
|
return d.dispose;
|
|
}
|
|
|
|
// src/components/transitions/transition.ts
|
|
function splitClasses(classes = "") {
|
|
return classes.split(/\s+/).filter((className) => className.length > 1);
|
|
}
|
|
var TransitionContext = Symbol("TransitionContext");
|
|
function hasTransitionContext() {
|
|
return (0, import_vue37.inject)(TransitionContext, null) !== null;
|
|
}
|
|
function useTransitionContext() {
|
|
let context = (0, import_vue37.inject)(TransitionContext, null);
|
|
if (context === null) {
|
|
throw new Error("A <TransitionChild /> is used but it is missing a parent <TransitionRoot />.");
|
|
}
|
|
return context;
|
|
}
|
|
function useParentNesting() {
|
|
let context = (0, import_vue37.inject)(NestingContext, null);
|
|
if (context === null) {
|
|
throw new Error("A <TransitionChild /> is used but it is missing a parent <TransitionRoot />.");
|
|
}
|
|
return context;
|
|
}
|
|
var NestingContext = Symbol("NestingContext");
|
|
function hasChildren(bag) {
|
|
if ("children" in bag)
|
|
return hasChildren(bag.children);
|
|
return bag.value.filter(({ state }) => state === "visible" /* Visible */).length > 0;
|
|
}
|
|
function useNesting(done) {
|
|
let transitionableChildren = (0, import_vue37.ref)([]);
|
|
let mounted = (0, import_vue37.ref)(false);
|
|
(0, import_vue37.onMounted)(() => mounted.value = true);
|
|
(0, import_vue37.onUnmounted)(() => mounted.value = false);
|
|
function unregister(childId, strategy = 1 /* Hidden */) {
|
|
let idx = transitionableChildren.value.findIndex(({ id }) => id === childId);
|
|
if (idx === -1)
|
|
return;
|
|
match(strategy, {
|
|
[0 /* Unmount */]() {
|
|
transitionableChildren.value.splice(idx, 1);
|
|
},
|
|
[1 /* Hidden */]() {
|
|
transitionableChildren.value[idx].state = "hidden" /* Hidden */;
|
|
}
|
|
});
|
|
if (!hasChildren(transitionableChildren) && mounted.value) {
|
|
done == null ? void 0 : done();
|
|
}
|
|
}
|
|
function register(childId) {
|
|
let child = transitionableChildren.value.find(({ id }) => id === childId);
|
|
if (!child) {
|
|
transitionableChildren.value.push({ id: childId, state: "visible" /* Visible */ });
|
|
} else if (child.state !== "visible" /* Visible */) {
|
|
child.state = "visible" /* Visible */;
|
|
}
|
|
return () => unregister(childId, 0 /* Unmount */);
|
|
}
|
|
return {
|
|
children: transitionableChildren,
|
|
register,
|
|
unregister
|
|
};
|
|
}
|
|
var TransitionChildRenderFeatures = 1 /* RenderStrategy */;
|
|
var TransitionChild = (0, import_vue37.defineComponent)({
|
|
props: {
|
|
as: { type: [Object, String], default: "div" },
|
|
show: { type: [Boolean], default: null },
|
|
unmount: { type: [Boolean], default: true },
|
|
appear: { type: [Boolean], default: false },
|
|
enter: { type: [String], default: "" },
|
|
enterFrom: { type: [String], default: "" },
|
|
enterTo: { type: [String], default: "" },
|
|
entered: { type: [String], default: "" },
|
|
leave: { type: [String], default: "" },
|
|
leaveFrom: { type: [String], default: "" },
|
|
leaveTo: { type: [String], default: "" }
|
|
},
|
|
emits: {
|
|
beforeEnter: () => true,
|
|
afterEnter: () => true,
|
|
beforeLeave: () => true,
|
|
afterLeave: () => true
|
|
},
|
|
setup(props, { emit, attrs, slots, expose }) {
|
|
let transitionStateFlags = (0, import_vue37.ref)(0);
|
|
function beforeEnter() {
|
|
transitionStateFlags.value |= 8 /* Opening */;
|
|
emit("beforeEnter");
|
|
}
|
|
function afterEnter() {
|
|
transitionStateFlags.value &= ~8 /* Opening */;
|
|
emit("afterEnter");
|
|
}
|
|
function beforeLeave() {
|
|
transitionStateFlags.value |= 4 /* Closing */;
|
|
emit("beforeLeave");
|
|
}
|
|
function afterLeave() {
|
|
transitionStateFlags.value &= ~4 /* Closing */;
|
|
emit("afterLeave");
|
|
}
|
|
if (!hasTransitionContext() && hasOpenClosed()) {
|
|
return () => (0, import_vue37.h)(
|
|
TransitionRoot,
|
|
{
|
|
...props,
|
|
onBeforeEnter: beforeEnter,
|
|
onAfterEnter: afterEnter,
|
|
onBeforeLeave: beforeLeave,
|
|
onAfterLeave: afterLeave
|
|
},
|
|
slots
|
|
);
|
|
}
|
|
let container = (0, import_vue37.ref)(null);
|
|
let strategy = (0, import_vue37.computed)(() => props.unmount ? 0 /* Unmount */ : 1 /* Hidden */);
|
|
expose({ el: container, $el: container });
|
|
let { show, appear } = useTransitionContext();
|
|
let { register, unregister } = useParentNesting();
|
|
let state = (0, import_vue37.ref)(show.value ? "visible" /* Visible */ : "hidden" /* Hidden */);
|
|
let initial = { value: true };
|
|
let id = useId2();
|
|
let isTransitioning = { value: false };
|
|
let nesting = useNesting(() => {
|
|
if (!isTransitioning.value && state.value !== "hidden" /* Hidden */) {
|
|
state.value = "hidden" /* Hidden */;
|
|
unregister(id);
|
|
afterLeave();
|
|
}
|
|
});
|
|
(0, import_vue37.onMounted)(() => {
|
|
let unregister2 = register(id);
|
|
(0, import_vue37.onUnmounted)(unregister2);
|
|
});
|
|
(0, import_vue37.watchEffect)(() => {
|
|
if (strategy.value !== 1 /* Hidden */)
|
|
return;
|
|
if (!id)
|
|
return;
|
|
if (show.value && state.value !== "visible" /* Visible */) {
|
|
state.value = "visible" /* Visible */;
|
|
return;
|
|
}
|
|
match(state.value, {
|
|
["hidden" /* Hidden */]: () => unregister(id),
|
|
["visible" /* Visible */]: () => register(id)
|
|
});
|
|
});
|
|
let enterClasses = splitClasses(props.enter);
|
|
let enterFromClasses = splitClasses(props.enterFrom);
|
|
let enterToClasses = splitClasses(props.enterTo);
|
|
let enteredClasses = splitClasses(props.entered);
|
|
let leaveClasses = splitClasses(props.leave);
|
|
let leaveFromClasses = splitClasses(props.leaveFrom);
|
|
let leaveToClasses = splitClasses(props.leaveTo);
|
|
(0, import_vue37.onMounted)(() => {
|
|
(0, import_vue37.watchEffect)(() => {
|
|
if (state.value === "visible" /* Visible */) {
|
|
let domElement = dom(container);
|
|
let isEmptyDOMNode = domElement instanceof Comment && domElement.data === "";
|
|
if (isEmptyDOMNode) {
|
|
throw new Error("Did you forget to passthrough the `ref` to the actual DOM node?");
|
|
}
|
|
}
|
|
});
|
|
});
|
|
function executeTransition(onInvalidate) {
|
|
let skip = initial.value && !appear.value;
|
|
let node = dom(container);
|
|
if (!node || !(node instanceof HTMLElement))
|
|
return;
|
|
if (skip)
|
|
return;
|
|
isTransitioning.value = true;
|
|
if (show.value)
|
|
beforeEnter();
|
|
if (!show.value)
|
|
beforeLeave();
|
|
onInvalidate(
|
|
show.value ? transition(
|
|
node,
|
|
enterClasses,
|
|
enterFromClasses,
|
|
enterToClasses,
|
|
enteredClasses,
|
|
(reason) => {
|
|
isTransitioning.value = false;
|
|
if (reason === "finished" /* Finished */)
|
|
afterEnter();
|
|
}
|
|
) : transition(
|
|
node,
|
|
leaveClasses,
|
|
leaveFromClasses,
|
|
leaveToClasses,
|
|
enteredClasses,
|
|
(reason) => {
|
|
isTransitioning.value = false;
|
|
if (reason !== "finished" /* Finished */)
|
|
return;
|
|
if (!hasChildren(nesting)) {
|
|
state.value = "hidden" /* Hidden */;
|
|
unregister(id);
|
|
afterLeave();
|
|
}
|
|
}
|
|
)
|
|
);
|
|
}
|
|
(0, import_vue37.onMounted)(() => {
|
|
(0, import_vue37.watch)(
|
|
[show],
|
|
(_oldValues, _newValues, onInvalidate) => {
|
|
executeTransition(onInvalidate);
|
|
initial.value = false;
|
|
},
|
|
{ immediate: true }
|
|
);
|
|
});
|
|
(0, import_vue37.provide)(NestingContext, nesting);
|
|
useOpenClosedProvider(
|
|
(0, import_vue37.computed)(
|
|
() => match(state.value, {
|
|
["visible" /* Visible */]: 1 /* Open */,
|
|
["hidden" /* Hidden */]: 2 /* Closed */
|
|
}) | transitionStateFlags.value
|
|
)
|
|
);
|
|
return () => {
|
|
let {
|
|
appear: _appear,
|
|
show: _show,
|
|
// Class names
|
|
enter,
|
|
enterFrom,
|
|
enterTo,
|
|
entered,
|
|
leave,
|
|
leaveFrom,
|
|
leaveTo,
|
|
...rest
|
|
} = props;
|
|
let ourProps = { ref: container };
|
|
let theirProps = {
|
|
...rest,
|
|
...appear.value && show.value && env.isServer ? {
|
|
// Already apply the `enter` and `enterFrom` on the server if required
|
|
class: (0, import_vue37.normalizeClass)([
|
|
attrs.class,
|
|
// @ts-expect-error not explicitly defined
|
|
rest.class,
|
|
...enterClasses,
|
|
...enterFromClasses
|
|
])
|
|
} : {}
|
|
};
|
|
return render({
|
|
theirProps,
|
|
ourProps,
|
|
slot: {},
|
|
slots,
|
|
attrs,
|
|
features: TransitionChildRenderFeatures,
|
|
visible: state.value === "visible" /* Visible */,
|
|
name: "TransitionChild"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
var _TransitionChild = TransitionChild;
|
|
var TransitionRoot = (0, import_vue37.defineComponent)({
|
|
inheritAttrs: false,
|
|
props: {
|
|
as: { type: [Object, String], default: "div" },
|
|
show: { type: [Boolean], default: null },
|
|
unmount: { type: [Boolean], default: true },
|
|
appear: { type: [Boolean], default: false },
|
|
enter: { type: [String], default: "" },
|
|
enterFrom: { type: [String], default: "" },
|
|
enterTo: { type: [String], default: "" },
|
|
entered: { type: [String], default: "" },
|
|
leave: { type: [String], default: "" },
|
|
leaveFrom: { type: [String], default: "" },
|
|
leaveTo: { type: [String], default: "" }
|
|
},
|
|
emits: {
|
|
beforeEnter: () => true,
|
|
afterEnter: () => true,
|
|
beforeLeave: () => true,
|
|
afterLeave: () => true
|
|
},
|
|
setup(props, { emit, attrs, slots }) {
|
|
let usesOpenClosedState = useOpenClosed();
|
|
let show = (0, import_vue37.computed)(() => {
|
|
if (props.show === null && usesOpenClosedState !== null) {
|
|
return (usesOpenClosedState.value & 1 /* Open */) === 1 /* Open */;
|
|
}
|
|
return props.show;
|
|
});
|
|
(0, import_vue37.watchEffect)(() => {
|
|
if (![true, false].includes(show.value)) {
|
|
throw new Error('A <Transition /> is used but it is missing a `:show="true | false"` prop.');
|
|
}
|
|
});
|
|
let state = (0, import_vue37.ref)(show.value ? "visible" /* Visible */ : "hidden" /* Hidden */);
|
|
let nestingBag = useNesting(() => {
|
|
state.value = "hidden" /* Hidden */;
|
|
});
|
|
let initial = (0, import_vue37.ref)(true);
|
|
let transitionBag = {
|
|
show,
|
|
appear: (0, import_vue37.computed)(() => props.appear || !initial.value)
|
|
};
|
|
(0, import_vue37.onMounted)(() => {
|
|
(0, import_vue37.watchEffect)(() => {
|
|
initial.value = false;
|
|
if (show.value) {
|
|
state.value = "visible" /* Visible */;
|
|
} else if (!hasChildren(nestingBag)) {
|
|
state.value = "hidden" /* Hidden */;
|
|
}
|
|
});
|
|
});
|
|
(0, import_vue37.provide)(NestingContext, nestingBag);
|
|
(0, import_vue37.provide)(TransitionContext, transitionBag);
|
|
return () => {
|
|
let theirProps = omit(props, [
|
|
"show",
|
|
"appear",
|
|
"unmount",
|
|
"onBeforeEnter",
|
|
"onBeforeLeave",
|
|
"onAfterEnter",
|
|
"onAfterLeave"
|
|
]);
|
|
let sharedProps = { unmount: props.unmount };
|
|
return render({
|
|
ourProps: {
|
|
...sharedProps,
|
|
as: "template"
|
|
},
|
|
theirProps: {},
|
|
slot: {},
|
|
slots: {
|
|
...slots,
|
|
default: () => [
|
|
(0, import_vue37.h)(
|
|
_TransitionChild,
|
|
{
|
|
onBeforeEnter: () => emit("beforeEnter"),
|
|
onAfterEnter: () => emit("afterEnter"),
|
|
onBeforeLeave: () => emit("beforeLeave"),
|
|
onAfterLeave: () => emit("afterLeave"),
|
|
...attrs,
|
|
...sharedProps,
|
|
...theirProps
|
|
},
|
|
slots.default
|
|
)
|
|
]
|
|
},
|
|
attrs: {},
|
|
features: TransitionChildRenderFeatures,
|
|
visible: state.value === "visible" /* Visible */,
|
|
name: "Transition"
|
|
});
|
|
};
|
|
}
|
|
});
|
|
/*! Bundled license information:
|
|
|
|
@tanstack/vue-virtual/build/lib/_virtual/_rollupPluginBabelHelpers.mjs:
|
|
(**
|
|
* vue-virtual
|
|
*
|
|
* Copyright (c) TanStack
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE.md file in the root directory of this source tree.
|
|
*
|
|
* @license MIT
|
|
*)
|
|
|
|
@tanstack/virtual-core/build/lib/_virtual/_rollupPluginBabelHelpers.mjs:
|
|
(**
|
|
* virtual-core
|
|
*
|
|
* Copyright (c) TanStack
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE.md file in the root directory of this source tree.
|
|
*
|
|
* @license MIT
|
|
*)
|
|
|
|
@tanstack/virtual-core/build/lib/utils.mjs:
|
|
(**
|
|
* virtual-core
|
|
*
|
|
* Copyright (c) TanStack
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE.md file in the root directory of this source tree.
|
|
*
|
|
* @license MIT
|
|
*)
|
|
|
|
@tanstack/virtual-core/build/lib/index.mjs:
|
|
(**
|
|
* virtual-core
|
|
*
|
|
* Copyright (c) TanStack
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE.md file in the root directory of this source tree.
|
|
*
|
|
* @license MIT
|
|
*)
|
|
|
|
@tanstack/vue-virtual/build/lib/index.mjs:
|
|
(**
|
|
* vue-virtual
|
|
*
|
|
* Copyright (c) TanStack
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE.md file in the root directory of this source tree.
|
|
*
|
|
* @license MIT
|
|
*)
|
|
*/
|