4957 lines
148 KiB
JavaScript
4957 lines
148 KiB
JavaScript
import { af as ATTACHMENT_KEY, ag as lifecycle_function_unavailable, ah as hasContext, i as getContext, ae as setContext, ai as derived, aj as run, ak as getAllContexts, al as props_id, d as attributes, g as bind_props, am as spread_props, e as escape_html, b as attr } from "../../chunks/index2.js";
|
|
import "@sveltejs/kit/internal";
|
|
import "../../chunks/exports.js";
|
|
import "../../chunks/utils2.js";
|
|
import "@sveltejs/kit/internal/server";
|
|
import { o as on } from "../../chunks/root.js";
|
|
import "../../chunks/state.svelte.js";
|
|
import { c as cn } from "../../chunks/utils.js";
|
|
import { clsx } from "clsx";
|
|
import parse from "style-to-object";
|
|
import { isTabbable, tabbable, isFocusable, focusable } from "tabbable";
|
|
import { computePosition, offset, shift, limitShift, flip, size, arrow, hide } from "@floating-ui/dom";
|
|
function createAttachmentKey() {
|
|
return Symbol(ATTACHMENT_KEY);
|
|
}
|
|
function mount() {
|
|
lifecycle_function_unavailable("mount");
|
|
}
|
|
function unmount() {
|
|
lifecycle_function_unavailable("unmount");
|
|
}
|
|
async function tick() {
|
|
}
|
|
function isFunction$1(value) {
|
|
return typeof value === "function";
|
|
}
|
|
function isObject(value) {
|
|
return value !== null && typeof value === "object";
|
|
}
|
|
const CLASS_VALUE_PRIMITIVE_TYPES = ["string", "number", "bigint", "boolean"];
|
|
function isClassValue(value) {
|
|
if (value === null || value === void 0)
|
|
return true;
|
|
if (CLASS_VALUE_PRIMITIVE_TYPES.includes(typeof value))
|
|
return true;
|
|
if (Array.isArray(value))
|
|
return value.every((item) => isClassValue(item));
|
|
if (typeof value === "object") {
|
|
if (Object.getPrototypeOf(value) !== Object.prototype)
|
|
return false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
const BoxSymbol = Symbol("box");
|
|
const isWritableSymbol = Symbol("is-writable");
|
|
function boxWith(getter, setter) {
|
|
const derived2 = getter();
|
|
if (setter) {
|
|
return {
|
|
[BoxSymbol]: true,
|
|
[isWritableSymbol]: true,
|
|
get current() {
|
|
return derived2;
|
|
},
|
|
set current(v) {
|
|
setter(v);
|
|
}
|
|
};
|
|
}
|
|
return {
|
|
[BoxSymbol]: true,
|
|
get current() {
|
|
return getter();
|
|
}
|
|
};
|
|
}
|
|
function isBox(value) {
|
|
return isObject(value) && BoxSymbol in value;
|
|
}
|
|
function isWritableBox(value) {
|
|
return isBox(value) && isWritableSymbol in value;
|
|
}
|
|
function boxFrom(value) {
|
|
if (isBox(value)) return value;
|
|
if (isFunction$1(value)) return boxWith(value);
|
|
return simpleBox(value);
|
|
}
|
|
function boxFlatten(boxes) {
|
|
return Object.entries(boxes).reduce(
|
|
(acc, [key, b]) => {
|
|
if (!isBox(b)) {
|
|
return Object.assign(acc, { [key]: b });
|
|
}
|
|
if (isWritableBox(b)) {
|
|
Object.defineProperty(acc, key, {
|
|
get() {
|
|
return b.current;
|
|
},
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
set(v) {
|
|
b.current = v;
|
|
}
|
|
});
|
|
} else {
|
|
Object.defineProperty(acc, key, {
|
|
get() {
|
|
return b.current;
|
|
}
|
|
});
|
|
}
|
|
return acc;
|
|
},
|
|
{}
|
|
);
|
|
}
|
|
function toReadonlyBox(b) {
|
|
if (!isWritableBox(b)) return b;
|
|
return {
|
|
[BoxSymbol]: true,
|
|
get current() {
|
|
return b.current;
|
|
}
|
|
};
|
|
}
|
|
function simpleBox(initialValue) {
|
|
let current = initialValue;
|
|
return {
|
|
[BoxSymbol]: true,
|
|
[isWritableSymbol]: true,
|
|
get current() {
|
|
return current;
|
|
},
|
|
set current(v) {
|
|
current = v;
|
|
}
|
|
};
|
|
}
|
|
function box(initialValue) {
|
|
let current = initialValue;
|
|
return {
|
|
[BoxSymbol]: true,
|
|
[isWritableSymbol]: true,
|
|
get current() {
|
|
return current;
|
|
},
|
|
set current(v) {
|
|
current = v;
|
|
}
|
|
};
|
|
}
|
|
box.from = boxFrom;
|
|
box.with = boxWith;
|
|
box.flatten = boxFlatten;
|
|
box.readonly = toReadonlyBox;
|
|
box.isBox = isBox;
|
|
box.isWritableBox = isWritableBox;
|
|
function composeHandlers(...handlers) {
|
|
return function(e) {
|
|
for (const handler of handlers) {
|
|
if (!handler)
|
|
continue;
|
|
if (e.defaultPrevented)
|
|
return;
|
|
if (typeof handler === "function") {
|
|
handler.call(this, e);
|
|
} else {
|
|
handler.current?.call(this, e);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
const NUMBER_CHAR_RE = /\d/;
|
|
const STR_SPLITTERS = ["-", "_", "/", "."];
|
|
function isUppercase(char = "") {
|
|
if (NUMBER_CHAR_RE.test(char))
|
|
return void 0;
|
|
return char !== char.toLowerCase();
|
|
}
|
|
function splitByCase(str) {
|
|
const parts = [];
|
|
let buff = "";
|
|
let previousUpper;
|
|
let previousSplitter;
|
|
for (const char of str) {
|
|
const isSplitter = STR_SPLITTERS.includes(char);
|
|
if (isSplitter === true) {
|
|
parts.push(buff);
|
|
buff = "";
|
|
previousUpper = void 0;
|
|
continue;
|
|
}
|
|
const isUpper = isUppercase(char);
|
|
if (previousSplitter === false) {
|
|
if (previousUpper === false && isUpper === true) {
|
|
parts.push(buff);
|
|
buff = char;
|
|
previousUpper = isUpper;
|
|
continue;
|
|
}
|
|
if (previousUpper === true && isUpper === false && buff.length > 1) {
|
|
const lastChar = buff.at(-1);
|
|
parts.push(buff.slice(0, Math.max(0, buff.length - 1)));
|
|
buff = lastChar + char;
|
|
previousUpper = isUpper;
|
|
continue;
|
|
}
|
|
}
|
|
buff += char;
|
|
previousUpper = isUpper;
|
|
previousSplitter = isSplitter;
|
|
}
|
|
parts.push(buff);
|
|
return parts;
|
|
}
|
|
function pascalCase(str) {
|
|
if (!str)
|
|
return "";
|
|
return splitByCase(str).map((p) => upperFirst(p)).join("");
|
|
}
|
|
function camelCase(str) {
|
|
return lowerFirst(pascalCase(str || ""));
|
|
}
|
|
function upperFirst(str) {
|
|
return str ? str[0].toUpperCase() + str.slice(1) : "";
|
|
}
|
|
function lowerFirst(str) {
|
|
return str ? str[0].toLowerCase() + str.slice(1) : "";
|
|
}
|
|
function cssToStyleObj(css) {
|
|
if (!css)
|
|
return {};
|
|
const styleObj = {};
|
|
function iterator(name, value) {
|
|
if (name.startsWith("-moz-") || name.startsWith("-webkit-") || name.startsWith("-ms-") || name.startsWith("-o-")) {
|
|
styleObj[pascalCase(name)] = value;
|
|
return;
|
|
}
|
|
if (name.startsWith("--")) {
|
|
styleObj[name] = value;
|
|
return;
|
|
}
|
|
styleObj[camelCase(name)] = value;
|
|
}
|
|
parse(css, iterator);
|
|
return styleObj;
|
|
}
|
|
function executeCallbacks(...callbacks) {
|
|
return (...args) => {
|
|
for (const callback of callbacks) {
|
|
if (typeof callback === "function") {
|
|
callback(...args);
|
|
}
|
|
}
|
|
};
|
|
}
|
|
function createParser(matcher, replacer) {
|
|
const regex = RegExp(matcher, "g");
|
|
return (str) => {
|
|
if (typeof str !== "string") {
|
|
throw new TypeError(`expected an argument of type string, but got ${typeof str}`);
|
|
}
|
|
if (!str.match(regex))
|
|
return str;
|
|
return str.replace(regex, replacer);
|
|
};
|
|
}
|
|
const camelToKebab = createParser(/[A-Z]/, (match) => `-${match.toLowerCase()}`);
|
|
function styleToCSS(styleObj) {
|
|
if (!styleObj || typeof styleObj !== "object" || Array.isArray(styleObj)) {
|
|
throw new TypeError(`expected an argument of type object, but got ${typeof styleObj}`);
|
|
}
|
|
return Object.keys(styleObj).map((property) => `${camelToKebab(property)}: ${styleObj[property]};`).join("\n");
|
|
}
|
|
function styleToString(style = {}) {
|
|
return styleToCSS(style).replace("\n", " ");
|
|
}
|
|
const EVENT_LIST = [
|
|
"onabort",
|
|
"onanimationcancel",
|
|
"onanimationend",
|
|
"onanimationiteration",
|
|
"onanimationstart",
|
|
"onauxclick",
|
|
"onbeforeinput",
|
|
"onbeforetoggle",
|
|
"onblur",
|
|
"oncancel",
|
|
"oncanplay",
|
|
"oncanplaythrough",
|
|
"onchange",
|
|
"onclick",
|
|
"onclose",
|
|
"oncompositionend",
|
|
"oncompositionstart",
|
|
"oncompositionupdate",
|
|
"oncontextlost",
|
|
"oncontextmenu",
|
|
"oncontextrestored",
|
|
"oncopy",
|
|
"oncuechange",
|
|
"oncut",
|
|
"ondblclick",
|
|
"ondrag",
|
|
"ondragend",
|
|
"ondragenter",
|
|
"ondragleave",
|
|
"ondragover",
|
|
"ondragstart",
|
|
"ondrop",
|
|
"ondurationchange",
|
|
"onemptied",
|
|
"onended",
|
|
"onerror",
|
|
"onfocus",
|
|
"onfocusin",
|
|
"onfocusout",
|
|
"onformdata",
|
|
"ongotpointercapture",
|
|
"oninput",
|
|
"oninvalid",
|
|
"onkeydown",
|
|
"onkeypress",
|
|
"onkeyup",
|
|
"onload",
|
|
"onloadeddata",
|
|
"onloadedmetadata",
|
|
"onloadstart",
|
|
"onlostpointercapture",
|
|
"onmousedown",
|
|
"onmouseenter",
|
|
"onmouseleave",
|
|
"onmousemove",
|
|
"onmouseout",
|
|
"onmouseover",
|
|
"onmouseup",
|
|
"onpaste",
|
|
"onpause",
|
|
"onplay",
|
|
"onplaying",
|
|
"onpointercancel",
|
|
"onpointerdown",
|
|
"onpointerenter",
|
|
"onpointerleave",
|
|
"onpointermove",
|
|
"onpointerout",
|
|
"onpointerover",
|
|
"onpointerup",
|
|
"onprogress",
|
|
"onratechange",
|
|
"onreset",
|
|
"onresize",
|
|
"onscroll",
|
|
"onscrollend",
|
|
"onsecuritypolicyviolation",
|
|
"onseeked",
|
|
"onseeking",
|
|
"onselect",
|
|
"onselectionchange",
|
|
"onselectstart",
|
|
"onslotchange",
|
|
"onstalled",
|
|
"onsubmit",
|
|
"onsuspend",
|
|
"ontimeupdate",
|
|
"ontoggle",
|
|
"ontouchcancel",
|
|
"ontouchend",
|
|
"ontouchmove",
|
|
"ontouchstart",
|
|
"ontransitioncancel",
|
|
"ontransitionend",
|
|
"ontransitionrun",
|
|
"ontransitionstart",
|
|
"onvolumechange",
|
|
"onwaiting",
|
|
"onwebkitanimationend",
|
|
"onwebkitanimationiteration",
|
|
"onwebkitanimationstart",
|
|
"onwebkittransitionend",
|
|
"onwheel"
|
|
];
|
|
const EVENT_LIST_SET = new Set(EVENT_LIST);
|
|
function isEventHandler(key) {
|
|
return EVENT_LIST_SET.has(key);
|
|
}
|
|
function mergeProps(...args) {
|
|
const result = { ...args[0] };
|
|
for (let i = 1; i < args.length; i++) {
|
|
const props = args[i];
|
|
if (!props)
|
|
continue;
|
|
for (const key of Object.keys(props)) {
|
|
const a = result[key];
|
|
const b = props[key];
|
|
const aIsFunction = typeof a === "function";
|
|
const bIsFunction = typeof b === "function";
|
|
if (aIsFunction && typeof bIsFunction && isEventHandler(key)) {
|
|
const aHandler = a;
|
|
const bHandler = b;
|
|
result[key] = composeHandlers(aHandler, bHandler);
|
|
} else if (aIsFunction && bIsFunction) {
|
|
result[key] = executeCallbacks(a, b);
|
|
} else if (key === "class") {
|
|
const aIsClassValue = isClassValue(a);
|
|
const bIsClassValue = isClassValue(b);
|
|
if (aIsClassValue && bIsClassValue) {
|
|
result[key] = clsx(a, b);
|
|
} else if (aIsClassValue) {
|
|
result[key] = clsx(a);
|
|
} else if (bIsClassValue) {
|
|
result[key] = clsx(b);
|
|
}
|
|
} else if (key === "style") {
|
|
const aIsObject = typeof a === "object";
|
|
const bIsObject = typeof b === "object";
|
|
const aIsString = typeof a === "string";
|
|
const bIsString = typeof b === "string";
|
|
if (aIsObject && bIsObject) {
|
|
result[key] = { ...a, ...b };
|
|
} else if (aIsObject && bIsString) {
|
|
const parsedStyle = cssToStyleObj(b);
|
|
result[key] = { ...a, ...parsedStyle };
|
|
} else if (aIsString && bIsObject) {
|
|
const parsedStyle = cssToStyleObj(a);
|
|
result[key] = { ...parsedStyle, ...b };
|
|
} else if (aIsString && bIsString) {
|
|
const parsedStyleA = cssToStyleObj(a);
|
|
const parsedStyleB = cssToStyleObj(b);
|
|
result[key] = { ...parsedStyleA, ...parsedStyleB };
|
|
} else if (aIsObject) {
|
|
result[key] = a;
|
|
} else if (bIsObject) {
|
|
result[key] = b;
|
|
} else if (aIsString) {
|
|
result[key] = a;
|
|
} else if (bIsString) {
|
|
result[key] = b;
|
|
}
|
|
} else {
|
|
result[key] = b !== void 0 ? b : a;
|
|
}
|
|
}
|
|
for (const key of Object.getOwnPropertySymbols(props)) {
|
|
const a = result[key];
|
|
const b = props[key];
|
|
result[key] = b !== void 0 ? b : a;
|
|
}
|
|
}
|
|
if (typeof result.style === "object") {
|
|
result.style = styleToString(result.style).replaceAll("\n", " ");
|
|
}
|
|
if (result.hidden === false) {
|
|
result.hidden = void 0;
|
|
delete result.hidden;
|
|
}
|
|
if (result.disabled === false) {
|
|
result.disabled = void 0;
|
|
delete result.disabled;
|
|
}
|
|
return result;
|
|
}
|
|
const srOnlyStyles = {
|
|
position: "absolute",
|
|
width: "1px",
|
|
height: "1px",
|
|
padding: "0",
|
|
margin: "-1px",
|
|
overflow: "hidden",
|
|
clip: "rect(0, 0, 0, 0)",
|
|
whiteSpace: "nowrap",
|
|
borderWidth: "0",
|
|
transform: "translateX(-100%)"
|
|
};
|
|
const srOnlyStylesString = styleToString(srOnlyStyles);
|
|
const defaultWindow = void 0;
|
|
function getActiveElement$1(document2) {
|
|
let activeElement = document2.activeElement;
|
|
while (activeElement?.shadowRoot) {
|
|
const node = activeElement.shadowRoot.activeElement;
|
|
if (node === activeElement)
|
|
break;
|
|
else
|
|
activeElement = node;
|
|
}
|
|
return activeElement;
|
|
}
|
|
const SvelteMap = globalThis.Map;
|
|
function createSubscriber(_) {
|
|
return () => {
|
|
};
|
|
}
|
|
class ActiveElement {
|
|
#document;
|
|
#subscribe;
|
|
constructor(options = {}) {
|
|
const { window: window2 = defaultWindow, document: document2 = window2?.document } = options;
|
|
if (window2 === void 0) return;
|
|
this.#document = document2;
|
|
this.#subscribe = createSubscriber();
|
|
}
|
|
get current() {
|
|
this.#subscribe?.();
|
|
if (!this.#document) return null;
|
|
return getActiveElement$1(this.#document);
|
|
}
|
|
}
|
|
new ActiveElement();
|
|
function isFunction(value) {
|
|
return typeof value === "function";
|
|
}
|
|
class Context {
|
|
#name;
|
|
#key;
|
|
/**
|
|
* @param name The name of the context.
|
|
* This is used for generating the context key and error messages.
|
|
*/
|
|
constructor(name) {
|
|
this.#name = name;
|
|
this.#key = Symbol(name);
|
|
}
|
|
/**
|
|
* The key used to get and set the context.
|
|
*
|
|
* It is not recommended to use this value directly.
|
|
* Instead, use the methods provided by this class.
|
|
*/
|
|
get key() {
|
|
return this.#key;
|
|
}
|
|
/**
|
|
* Checks whether this has been set in the context of a parent component.
|
|
*
|
|
* Must be called during component initialisation.
|
|
*/
|
|
exists() {
|
|
return hasContext(this.#key);
|
|
}
|
|
/**
|
|
* Retrieves the context that belongs to the closest parent component.
|
|
*
|
|
* Must be called during component initialisation.
|
|
*
|
|
* @throws An error if the context does not exist.
|
|
*/
|
|
get() {
|
|
const context = getContext(this.#key);
|
|
if (context === void 0) {
|
|
throw new Error(`Context "${this.#name}" not found`);
|
|
}
|
|
return context;
|
|
}
|
|
/**
|
|
* Retrieves the context that belongs to the closest parent component,
|
|
* or the given fallback value if the context does not exist.
|
|
*
|
|
* Must be called during component initialisation.
|
|
*/
|
|
getOr(fallback) {
|
|
const context = getContext(this.#key);
|
|
if (context === void 0) {
|
|
return fallback;
|
|
}
|
|
return context;
|
|
}
|
|
/**
|
|
* Associates the given value with the current component and returns it.
|
|
*
|
|
* Must be called during component initialisation.
|
|
*/
|
|
set(context) {
|
|
return setContext(this.#key, context);
|
|
}
|
|
}
|
|
function runWatcher(sources, flush, effect, options = {}) {
|
|
const { lazy = false } = options;
|
|
}
|
|
function watch(sources, effect, options) {
|
|
runWatcher(sources, "post", effect, options);
|
|
}
|
|
function watchPre(sources, effect, options) {
|
|
runWatcher(sources, "pre", effect, options);
|
|
}
|
|
watch.pre = watchPre;
|
|
function get$1(value) {
|
|
if (isFunction(value)) {
|
|
return value();
|
|
}
|
|
return value;
|
|
}
|
|
class ElementSize {
|
|
// no need to use `$state` here since we are using createSubscriber
|
|
#size = { width: 0, height: 0 };
|
|
#observed = false;
|
|
#options;
|
|
#node;
|
|
#window;
|
|
// we use a derived here to extract the width so that if the width doesn't change we don't get a state update
|
|
// which we would get if we would just use a getter since the version of the subscriber will be changing
|
|
#width = derived(() => {
|
|
this.#subscribe()?.();
|
|
return this.getSize().width;
|
|
});
|
|
// we use a derived here to extract the height so that if the height doesn't change we don't get a state update
|
|
// which we would get if we would just use a getter since the version of the subscriber will be changing
|
|
#height = derived(() => {
|
|
this.#subscribe()?.();
|
|
return this.getSize().height;
|
|
});
|
|
// we need to use a derived here because the class will be created before the node is bound to the ref
|
|
#subscribe = derived(() => {
|
|
const node$ = get$1(this.#node);
|
|
if (!node$) return;
|
|
return createSubscriber();
|
|
});
|
|
constructor(node, options = { box: "border-box" }) {
|
|
this.#window = options.window ?? defaultWindow;
|
|
this.#options = options;
|
|
this.#node = node;
|
|
this.#size = { width: 0, height: 0 };
|
|
}
|
|
calculateSize() {
|
|
const element = get$1(this.#node);
|
|
if (!element || !this.#window) {
|
|
return;
|
|
}
|
|
const offsetWidth = element.offsetWidth;
|
|
const offsetHeight = element.offsetHeight;
|
|
if (this.#options.box === "border-box") {
|
|
return { width: offsetWidth, height: offsetHeight };
|
|
}
|
|
const style = this.#window.getComputedStyle(element);
|
|
const paddingWidth = parseFloat(style.paddingLeft) + parseFloat(style.paddingRight);
|
|
const paddingHeight = parseFloat(style.paddingTop) + parseFloat(style.paddingBottom);
|
|
const borderWidth = parseFloat(style.borderLeftWidth) + parseFloat(style.borderRightWidth);
|
|
const borderHeight = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth);
|
|
const contentWidth = offsetWidth - paddingWidth - borderWidth;
|
|
const contentHeight = offsetHeight - paddingHeight - borderHeight;
|
|
return { width: contentWidth, height: contentHeight };
|
|
}
|
|
getSize() {
|
|
return this.#observed ? this.#size : this.calculateSize() ?? this.#size;
|
|
}
|
|
get current() {
|
|
this.#subscribe()?.();
|
|
return this.getSize();
|
|
}
|
|
get width() {
|
|
return this.#width();
|
|
}
|
|
get height() {
|
|
return this.#height();
|
|
}
|
|
}
|
|
function afterSleep(ms, cb) {
|
|
return setTimeout(cb, ms);
|
|
}
|
|
function afterTick(fn) {
|
|
tick().then(fn);
|
|
}
|
|
const ELEMENT_NODE = 1;
|
|
const DOCUMENT_NODE = 9;
|
|
const DOCUMENT_FRAGMENT_NODE = 11;
|
|
function isHTMLElement$1(node) {
|
|
return isObject(node) && node.nodeType === ELEMENT_NODE && typeof node.nodeName === "string";
|
|
}
|
|
function isDocument(node) {
|
|
return isObject(node) && node.nodeType === DOCUMENT_NODE;
|
|
}
|
|
function isWindow(node) {
|
|
return isObject(node) && node.constructor?.name === "VisualViewport";
|
|
}
|
|
function isNode(node) {
|
|
return isObject(node) && node.nodeType !== void 0;
|
|
}
|
|
function isShadowRoot(node) {
|
|
return isNode(node) && node.nodeType === DOCUMENT_FRAGMENT_NODE && "host" in node;
|
|
}
|
|
function contains(parent, child) {
|
|
if (!parent || !child)
|
|
return false;
|
|
if (!isHTMLElement$1(parent) || !isHTMLElement$1(child))
|
|
return false;
|
|
const rootNode = child.getRootNode?.();
|
|
if (parent === child)
|
|
return true;
|
|
if (parent.contains(child))
|
|
return true;
|
|
if (rootNode && isShadowRoot(rootNode)) {
|
|
let next = child;
|
|
while (next) {
|
|
if (parent === next)
|
|
return true;
|
|
next = next.parentNode || next.host;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
function getDocument(node) {
|
|
if (isDocument(node))
|
|
return node;
|
|
if (isWindow(node))
|
|
return node.document;
|
|
return node?.ownerDocument ?? document;
|
|
}
|
|
function getWindow(node) {
|
|
if (isShadowRoot(node))
|
|
return getWindow(node.host);
|
|
if (isDocument(node))
|
|
return node.defaultView ?? window;
|
|
if (isHTMLElement$1(node))
|
|
return node.ownerDocument?.defaultView ?? window;
|
|
return window;
|
|
}
|
|
function getActiveElement(rootNode) {
|
|
let activeElement = rootNode.activeElement;
|
|
while (activeElement?.shadowRoot) {
|
|
const el = activeElement.shadowRoot.activeElement;
|
|
if (el === activeElement)
|
|
break;
|
|
else
|
|
activeElement = el;
|
|
}
|
|
return activeElement;
|
|
}
|
|
class DOMContext {
|
|
element;
|
|
#root = derived(() => {
|
|
if (!this.element.current) return document;
|
|
const rootNode = this.element.current.getRootNode() ?? document;
|
|
return rootNode;
|
|
});
|
|
get root() {
|
|
return this.#root();
|
|
}
|
|
set root($$value) {
|
|
return this.#root($$value);
|
|
}
|
|
constructor(element) {
|
|
if (typeof element === "function") {
|
|
this.element = boxWith(element);
|
|
} else {
|
|
this.element = element;
|
|
}
|
|
}
|
|
getDocument = () => {
|
|
return getDocument(this.root);
|
|
};
|
|
getWindow = () => {
|
|
return this.getDocument().defaultView ?? window;
|
|
};
|
|
getActiveElement = () => {
|
|
return getActiveElement(this.root);
|
|
};
|
|
isActiveElement = (node) => {
|
|
return node === this.getActiveElement();
|
|
};
|
|
getElementById(id) {
|
|
return this.root.getElementById(id);
|
|
}
|
|
querySelector = (selector) => {
|
|
if (!this.root) return null;
|
|
return this.root.querySelector(selector);
|
|
};
|
|
querySelectorAll = (selector) => {
|
|
if (!this.root) return [];
|
|
return this.root.querySelectorAll(selector);
|
|
};
|
|
setTimeout = (callback, delay) => {
|
|
return this.getWindow().setTimeout(callback, delay);
|
|
};
|
|
clearTimeout = (timeoutId) => {
|
|
return this.getWindow().clearTimeout(timeoutId);
|
|
};
|
|
}
|
|
function attachRef(ref, onChange) {
|
|
return {
|
|
[createAttachmentKey()]: (node) => {
|
|
if (isBox(ref)) {
|
|
ref.current = node;
|
|
run(() => onChange?.(node));
|
|
return () => {
|
|
if ("isConnected" in node && node.isConnected)
|
|
return;
|
|
ref.current = null;
|
|
onChange?.(null);
|
|
};
|
|
}
|
|
ref(node);
|
|
run(() => onChange?.(node));
|
|
return () => {
|
|
if ("isConnected" in node && node.isConnected)
|
|
return;
|
|
ref(null);
|
|
onChange?.(null);
|
|
};
|
|
}
|
|
};
|
|
}
|
|
function boolToStr(condition) {
|
|
return condition ? "true" : "false";
|
|
}
|
|
function boolToEmptyStrOrUndef(condition) {
|
|
return condition ? "" : void 0;
|
|
}
|
|
function boolToTrueOrUndef(condition) {
|
|
return condition ? true : void 0;
|
|
}
|
|
function getDataOpenClosed(condition) {
|
|
return condition ? "open" : "closed";
|
|
}
|
|
function getDataChecked(condition) {
|
|
return condition ? "checked" : "unchecked";
|
|
}
|
|
function getAriaChecked(checked, indeterminate) {
|
|
return checked ? "true" : "false";
|
|
}
|
|
class BitsAttrs {
|
|
#variant;
|
|
#prefix;
|
|
attrs;
|
|
constructor(config) {
|
|
this.#variant = config.getVariant ? config.getVariant() : null;
|
|
this.#prefix = this.#variant ? `data-${this.#variant}-` : `data-${config.component}-`;
|
|
this.getAttr = this.getAttr.bind(this);
|
|
this.selector = this.selector.bind(this);
|
|
this.attrs = Object.fromEntries(config.parts.map((part) => [part, this.getAttr(part)]));
|
|
}
|
|
getAttr(part, variantOverride) {
|
|
if (variantOverride)
|
|
return `data-${variantOverride}-${part}`;
|
|
return `${this.#prefix}${part}`;
|
|
}
|
|
selector(part, variantOverride) {
|
|
return `[${this.getAttr(part, variantOverride)}]`;
|
|
}
|
|
}
|
|
function createBitsAttrs(config) {
|
|
const bitsAttrs = new BitsAttrs(config);
|
|
return {
|
|
...bitsAttrs.attrs,
|
|
selector: bitsAttrs.selector,
|
|
getAttr: bitsAttrs.getAttr
|
|
};
|
|
}
|
|
const ARROW_DOWN = "ArrowDown";
|
|
const ARROW_LEFT = "ArrowLeft";
|
|
const ARROW_RIGHT = "ArrowRight";
|
|
const ARROW_UP = "ArrowUp";
|
|
const END = "End";
|
|
const ENTER = "Enter";
|
|
const ESCAPE = "Escape";
|
|
const HOME = "Home";
|
|
const PAGE_DOWN = "PageDown";
|
|
const PAGE_UP = "PageUp";
|
|
const SPACE = " ";
|
|
const TAB = "Tab";
|
|
function getElemDirection(elem) {
|
|
const style = window.getComputedStyle(elem);
|
|
const direction = style.getPropertyValue("direction");
|
|
return direction;
|
|
}
|
|
function getNextKey(dir = "ltr", orientation = "horizontal") {
|
|
return {
|
|
horizontal: dir === "rtl" ? ARROW_LEFT : ARROW_RIGHT,
|
|
vertical: ARROW_DOWN
|
|
}[orientation];
|
|
}
|
|
function getPrevKey(dir = "ltr", orientation = "horizontal") {
|
|
return {
|
|
horizontal: dir === "rtl" ? ARROW_RIGHT : ARROW_LEFT,
|
|
vertical: ARROW_UP
|
|
}[orientation];
|
|
}
|
|
function getDirectionalKeys(dir = "ltr", orientation = "horizontal") {
|
|
if (!["ltr", "rtl"].includes(dir))
|
|
dir = "ltr";
|
|
if (!["horizontal", "vertical"].includes(orientation))
|
|
orientation = "horizontal";
|
|
return {
|
|
nextKey: getNextKey(dir, orientation),
|
|
prevKey: getPrevKey(dir, orientation)
|
|
};
|
|
}
|
|
const isBrowser = typeof document !== "undefined";
|
|
const isIOS = getIsIOS();
|
|
function getIsIOS() {
|
|
return isBrowser && window?.navigator?.userAgent && (/iP(ad|hone|od)/.test(window.navigator.userAgent) || // The new iPad Pro Gen3 does not identify itself as iPad, but as Macintosh.
|
|
window?.navigator?.maxTouchPoints > 2 && /iPad|Macintosh/.test(window?.navigator.userAgent));
|
|
}
|
|
function isHTMLElement(element) {
|
|
return element instanceof HTMLElement;
|
|
}
|
|
function isElement(element) {
|
|
return element instanceof Element;
|
|
}
|
|
function isElementOrSVGElement(element) {
|
|
return element instanceof Element || element instanceof SVGElement;
|
|
}
|
|
function isNotNull(value) {
|
|
return value !== null;
|
|
}
|
|
function isSelectableInput(element) {
|
|
return element instanceof HTMLInputElement && "select" in element;
|
|
}
|
|
class RovingFocusGroup {
|
|
#opts;
|
|
#currentTabStopId = box(null);
|
|
constructor(opts) {
|
|
this.#opts = opts;
|
|
}
|
|
getCandidateNodes() {
|
|
return [];
|
|
}
|
|
focusFirstCandidate() {
|
|
const items = this.getCandidateNodes();
|
|
if (!items.length)
|
|
return;
|
|
items[0]?.focus();
|
|
}
|
|
handleKeydown(node, e, both = false) {
|
|
const rootNode = this.#opts.rootNode.current;
|
|
if (!rootNode || !node)
|
|
return;
|
|
const items = this.getCandidateNodes();
|
|
if (!items.length)
|
|
return;
|
|
const currentIndex = items.indexOf(node);
|
|
const dir = getElemDirection(rootNode);
|
|
const { nextKey, prevKey } = getDirectionalKeys(dir, this.#opts.orientation.current);
|
|
const loop = this.#opts.loop.current;
|
|
const keyToIndex = {
|
|
[nextKey]: currentIndex + 1,
|
|
[prevKey]: currentIndex - 1,
|
|
[HOME]: 0,
|
|
[END]: items.length - 1
|
|
};
|
|
if (both) {
|
|
const altNextKey = nextKey === ARROW_DOWN ? ARROW_RIGHT : ARROW_DOWN;
|
|
const altPrevKey = prevKey === ARROW_UP ? ARROW_LEFT : ARROW_UP;
|
|
keyToIndex[altNextKey] = currentIndex + 1;
|
|
keyToIndex[altPrevKey] = currentIndex - 1;
|
|
}
|
|
let itemIndex = keyToIndex[e.key];
|
|
if (itemIndex === void 0)
|
|
return;
|
|
e.preventDefault();
|
|
if (itemIndex < 0 && loop) {
|
|
itemIndex = items.length - 1;
|
|
} else if (itemIndex === items.length && loop) {
|
|
itemIndex = 0;
|
|
}
|
|
const itemToFocus = items[itemIndex];
|
|
if (!itemToFocus)
|
|
return;
|
|
itemToFocus.focus();
|
|
this.#currentTabStopId.current = itemToFocus.id;
|
|
this.#opts.onCandidateFocus?.(itemToFocus);
|
|
return itemToFocus;
|
|
}
|
|
getTabIndex(node) {
|
|
const items = this.getCandidateNodes();
|
|
const anyActive = this.#currentTabStopId.current !== null;
|
|
if (node && !anyActive && items[0] === node) {
|
|
this.#currentTabStopId.current = node.id;
|
|
return 0;
|
|
} else if (node?.id === this.#currentTabStopId.current) {
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
setCurrentTabStopId(id) {
|
|
this.#currentTabStopId.current = id;
|
|
}
|
|
focusCurrentTabStop() {
|
|
const currentTabStopId = this.#currentTabStopId.current;
|
|
if (!currentTabStopId)
|
|
return;
|
|
const currentTabStop = this.#opts.rootNode.current?.querySelector(`#${currentTabStopId}`);
|
|
if (!currentTabStop || !isHTMLElement(currentTabStop))
|
|
return;
|
|
currentTabStop.focus();
|
|
}
|
|
}
|
|
class AnimationsComplete {
|
|
#opts;
|
|
#currentFrame = null;
|
|
constructor(opts) {
|
|
this.#opts = opts;
|
|
}
|
|
#cleanup() {
|
|
if (!this.#currentFrame)
|
|
return;
|
|
window.cancelAnimationFrame(this.#currentFrame);
|
|
this.#currentFrame = null;
|
|
}
|
|
run(fn) {
|
|
this.#cleanup();
|
|
const node = this.#opts.ref.current;
|
|
if (!node)
|
|
return;
|
|
if (typeof node.getAnimations !== "function") {
|
|
this.#executeCallback(fn);
|
|
return;
|
|
}
|
|
this.#currentFrame = window.requestAnimationFrame(() => {
|
|
const animations = node.getAnimations();
|
|
if (animations.length === 0) {
|
|
this.#executeCallback(fn);
|
|
return;
|
|
}
|
|
Promise.allSettled(animations.map((animation) => animation.finished)).then(() => {
|
|
this.#executeCallback(fn);
|
|
});
|
|
});
|
|
}
|
|
#executeCallback(fn) {
|
|
const execute = () => {
|
|
fn();
|
|
};
|
|
if (this.#opts.afterTick) {
|
|
afterTick(execute);
|
|
} else {
|
|
execute();
|
|
}
|
|
}
|
|
}
|
|
class PresenceManager {
|
|
#opts;
|
|
#enabled;
|
|
#afterAnimations;
|
|
#shouldRender = false;
|
|
constructor(opts) {
|
|
this.#opts = opts;
|
|
this.#shouldRender = opts.open.current;
|
|
this.#enabled = opts.enabled ?? true;
|
|
this.#afterAnimations = new AnimationsComplete({ ref: this.#opts.ref, afterTick: this.#opts.open });
|
|
watch(() => this.#opts.open.current, (isOpen) => {
|
|
if (isOpen) this.#shouldRender = true;
|
|
if (!this.#enabled) return;
|
|
this.#afterAnimations.run(() => {
|
|
if (isOpen === this.#opts.open.current) {
|
|
if (!this.#opts.open.current) {
|
|
this.#shouldRender = false;
|
|
}
|
|
this.#opts.onComplete?.();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
get shouldRender() {
|
|
return this.#shouldRender;
|
|
}
|
|
}
|
|
function noop() {
|
|
}
|
|
function createId(prefixOrUid, uid) {
|
|
return `bits-${prefixOrUid}`;
|
|
}
|
|
const BitsConfigContext = new Context("BitsConfig");
|
|
function getBitsConfig() {
|
|
const fallback = new BitsConfigState(null, {});
|
|
return BitsConfigContext.getOr(fallback).opts;
|
|
}
|
|
class BitsConfigState {
|
|
opts;
|
|
constructor(parent, opts) {
|
|
const resolveConfigOption = createConfigResolver(parent, opts);
|
|
this.opts = {
|
|
defaultPortalTo: resolveConfigOption((config) => config.defaultPortalTo),
|
|
defaultLocale: resolveConfigOption((config) => config.defaultLocale)
|
|
};
|
|
}
|
|
}
|
|
function createConfigResolver(parent, currentOpts) {
|
|
return (getter) => {
|
|
const configOption = boxWith(() => {
|
|
const value = getter(currentOpts)?.current;
|
|
if (value !== void 0)
|
|
return value;
|
|
if (parent === null)
|
|
return void 0;
|
|
return getter(parent.opts)?.current;
|
|
});
|
|
return configOption;
|
|
};
|
|
}
|
|
function createPropResolver(configOption, fallback) {
|
|
return (getProp) => {
|
|
const config = getBitsConfig();
|
|
return boxWith(() => {
|
|
const propValue = getProp();
|
|
if (propValue !== void 0)
|
|
return propValue;
|
|
const option = configOption(config).current;
|
|
if (option !== void 0)
|
|
return option;
|
|
return fallback;
|
|
});
|
|
};
|
|
}
|
|
const resolvePortalToProp = createPropResolver((config) => config.defaultPortalTo, "body");
|
|
function Portal($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let { to: toProp, children, disabled } = $$props;
|
|
const to = resolvePortalToProp(() => toProp);
|
|
getAllContexts();
|
|
let target = getTarget();
|
|
function getTarget() {
|
|
if (!isBrowser || disabled) return null;
|
|
let localTarget = null;
|
|
if (typeof to.current === "string") {
|
|
const target2 = document.querySelector(to.current);
|
|
localTarget = target2;
|
|
} else {
|
|
localTarget = to.current;
|
|
}
|
|
return localTarget;
|
|
}
|
|
let instance;
|
|
function unmountInstance() {
|
|
if (instance) {
|
|
unmount();
|
|
instance = null;
|
|
}
|
|
}
|
|
watch([() => target, () => disabled], ([target2, disabled2]) => {
|
|
if (!target2 || disabled2) {
|
|
unmountInstance();
|
|
return;
|
|
}
|
|
instance = mount();
|
|
return () => {
|
|
unmountInstance();
|
|
};
|
|
});
|
|
if (disabled) {
|
|
$$renderer2.push("<!--[-->");
|
|
children?.($$renderer2);
|
|
$$renderer2.push(`<!---->`);
|
|
} else {
|
|
$$renderer2.push("<!--[!-->");
|
|
}
|
|
$$renderer2.push(`<!--]-->`);
|
|
});
|
|
}
|
|
class CustomEventDispatcher {
|
|
eventName;
|
|
options;
|
|
constructor(eventName, options = { bubbles: true, cancelable: true }) {
|
|
this.eventName = eventName;
|
|
this.options = options;
|
|
}
|
|
createEvent(detail) {
|
|
return new CustomEvent(this.eventName, {
|
|
...this.options,
|
|
detail
|
|
});
|
|
}
|
|
dispatch(element, detail) {
|
|
const event = this.createEvent(detail);
|
|
element.dispatchEvent(event);
|
|
return event;
|
|
}
|
|
listen(element, callback, options) {
|
|
const handler = (event) => {
|
|
callback(event);
|
|
};
|
|
return on(element, this.eventName, handler, options);
|
|
}
|
|
}
|
|
function debounce(fn, wait = 500) {
|
|
let timeout = null;
|
|
const debounced = (...args) => {
|
|
if (timeout !== null) {
|
|
clearTimeout(timeout);
|
|
}
|
|
timeout = setTimeout(() => {
|
|
fn(...args);
|
|
}, wait);
|
|
};
|
|
debounced.destroy = () => {
|
|
if (timeout !== null) {
|
|
clearTimeout(timeout);
|
|
timeout = null;
|
|
}
|
|
};
|
|
return debounced;
|
|
}
|
|
function isOrContainsTarget(node, target) {
|
|
return node === target || node.contains(target);
|
|
}
|
|
function getOwnerDocument(el) {
|
|
return el?.ownerDocument ?? document;
|
|
}
|
|
function isClickTrulyOutside(event, contentNode) {
|
|
const { clientX, clientY } = event;
|
|
const rect = contentNode.getBoundingClientRect();
|
|
return clientX < rect.left || clientX > rect.right || clientY < rect.top || clientY > rect.bottom;
|
|
}
|
|
const SELECTION_KEYS = [ENTER, SPACE];
|
|
const FIRST_KEYS = [ARROW_DOWN, PAGE_UP, HOME];
|
|
const LAST_KEYS = [ARROW_UP, PAGE_DOWN, END];
|
|
const FIRST_LAST_KEYS = [...FIRST_KEYS, ...LAST_KEYS];
|
|
function isMouseEvent(event) {
|
|
return event.pointerType === "mouse";
|
|
}
|
|
function focus(element, { select = false } = {}) {
|
|
if (!element || !element.focus)
|
|
return;
|
|
const doc = getDocument(element);
|
|
if (doc.activeElement === element)
|
|
return;
|
|
const previouslyFocusedElement = doc.activeElement;
|
|
element.focus({ preventScroll: true });
|
|
if (element !== previouslyFocusedElement && isSelectableInput(element) && select) {
|
|
element.select();
|
|
}
|
|
}
|
|
function focusFirst(candidates, { select = false } = {}, getActiveElement2) {
|
|
const previouslyFocusedElement = getActiveElement2();
|
|
for (const candidate of candidates) {
|
|
focus(candidate, { select });
|
|
if (getActiveElement2() !== previouslyFocusedElement)
|
|
return true;
|
|
}
|
|
}
|
|
let isUsingKeyboard = false;
|
|
class IsUsingKeyboard {
|
|
static _refs = 0;
|
|
// Reference counting to avoid multiple listeners.
|
|
static _cleanup;
|
|
constructor() {
|
|
}
|
|
get current() {
|
|
return isUsingKeyboard;
|
|
}
|
|
set current(value) {
|
|
isUsingKeyboard = value;
|
|
}
|
|
}
|
|
function getTabbableOptions() {
|
|
return {
|
|
getShadowRoot: true,
|
|
displayCheck: (
|
|
// JSDOM does not support the `tabbable` library. To solve this we can
|
|
// check if `ResizeObserver` is a real function (not polyfilled), which
|
|
// determines if the current environment is JSDOM-like.
|
|
typeof ResizeObserver === "function" && ResizeObserver.toString().includes("[native code]") ? "full" : "none"
|
|
)
|
|
};
|
|
}
|
|
function getTabbableFrom(currentNode, direction) {
|
|
if (!isTabbable(currentNode, getTabbableOptions())) {
|
|
return getTabbableFromFocusable(currentNode, direction);
|
|
}
|
|
const doc = getDocument(currentNode);
|
|
const allTabbable = tabbable(doc.body, getTabbableOptions());
|
|
if (direction === "prev")
|
|
allTabbable.reverse();
|
|
const activeIndex = allTabbable.indexOf(currentNode);
|
|
if (activeIndex === -1)
|
|
return doc.body;
|
|
const nextTabbableElements = allTabbable.slice(activeIndex + 1);
|
|
return nextTabbableElements[0];
|
|
}
|
|
function getTabbableFromFocusable(currentNode, direction) {
|
|
const doc = getDocument(currentNode);
|
|
if (!isFocusable(currentNode, getTabbableOptions()))
|
|
return doc.body;
|
|
const allFocusable = focusable(doc.body, getTabbableOptions());
|
|
if (direction === "prev")
|
|
allFocusable.reverse();
|
|
const activeIndex = allFocusable.indexOf(currentNode);
|
|
if (activeIndex === -1)
|
|
return doc.body;
|
|
const nextFocusableElements = allFocusable.slice(activeIndex + 1);
|
|
return nextFocusableElements.find((node) => isTabbable(node, getTabbableOptions())) ?? doc.body;
|
|
}
|
|
function getNextMatch(values, search, currentMatch) {
|
|
const lowerSearch = search.toLowerCase();
|
|
if (lowerSearch.endsWith(" ")) {
|
|
const searchWithoutSpace = lowerSearch.slice(0, -1);
|
|
const matchesWithoutSpace = values.filter((value) => value.toLowerCase().startsWith(searchWithoutSpace));
|
|
if (matchesWithoutSpace.length <= 1) {
|
|
return getNextMatch(values, searchWithoutSpace, currentMatch);
|
|
}
|
|
const currentMatchLowercase = currentMatch?.toLowerCase();
|
|
if (currentMatchLowercase && currentMatchLowercase.startsWith(searchWithoutSpace) && currentMatchLowercase.charAt(searchWithoutSpace.length) === " " && search.trim() === searchWithoutSpace) {
|
|
return currentMatch;
|
|
}
|
|
const spacedMatches = values.filter((value) => value.toLowerCase().startsWith(lowerSearch));
|
|
if (spacedMatches.length > 0) {
|
|
const currentMatchIndex2 = currentMatch ? values.indexOf(currentMatch) : -1;
|
|
let wrappedMatches = wrapArray(spacedMatches, Math.max(currentMatchIndex2, 0));
|
|
const nextMatch2 = wrappedMatches.find((match) => match !== currentMatch);
|
|
return nextMatch2 || currentMatch;
|
|
}
|
|
}
|
|
const isRepeated = search.length > 1 && Array.from(search).every((char) => char === search[0]);
|
|
const normalizedSearch = isRepeated ? search[0] : search;
|
|
const normalizedLowerSearch = normalizedSearch.toLowerCase();
|
|
const currentMatchIndex = currentMatch ? values.indexOf(currentMatch) : -1;
|
|
let wrappedValues = wrapArray(values, Math.max(currentMatchIndex, 0));
|
|
const excludeCurrentMatch = normalizedSearch.length === 1;
|
|
if (excludeCurrentMatch)
|
|
wrappedValues = wrappedValues.filter((v) => v !== currentMatch);
|
|
const nextMatch = wrappedValues.find((value) => value?.toLowerCase().startsWith(normalizedLowerSearch));
|
|
return nextMatch !== currentMatch ? nextMatch : void 0;
|
|
}
|
|
function wrapArray(array, startIndex) {
|
|
return array.map((_, index) => array[(startIndex + index) % array.length]);
|
|
}
|
|
const defaultOptions = { afterMs: 1e4, onChange: noop };
|
|
function boxAutoReset(defaultValue, options) {
|
|
const { afterMs, onChange, getWindow: getWindow2 } = { ...defaultOptions, ...options };
|
|
let timeout = null;
|
|
let value = defaultValue;
|
|
function resetAfter() {
|
|
return getWindow2().setTimeout(
|
|
() => {
|
|
value = defaultValue;
|
|
onChange?.(defaultValue);
|
|
},
|
|
afterMs
|
|
);
|
|
}
|
|
return boxWith(() => value, (v) => {
|
|
value = v;
|
|
onChange?.(v);
|
|
if (timeout) getWindow2().clearTimeout(timeout);
|
|
timeout = resetAfter();
|
|
});
|
|
}
|
|
class DOMTypeahead {
|
|
#opts;
|
|
#search;
|
|
#onMatch = derived(() => {
|
|
if (this.#opts.onMatch) return this.#opts.onMatch;
|
|
return (node) => node.focus();
|
|
});
|
|
#getCurrentItem = derived(() => {
|
|
if (this.#opts.getCurrentItem) return this.#opts.getCurrentItem;
|
|
return this.#opts.getActiveElement;
|
|
});
|
|
constructor(opts) {
|
|
this.#opts = opts;
|
|
this.#search = boxAutoReset("", { afterMs: 1e3, getWindow: opts.getWindow });
|
|
this.handleTypeaheadSearch = this.handleTypeaheadSearch.bind(this);
|
|
this.resetTypeahead = this.resetTypeahead.bind(this);
|
|
}
|
|
handleTypeaheadSearch(key, candidates) {
|
|
if (!candidates.length) return;
|
|
this.#search.current = this.#search.current + key;
|
|
const currentItem = this.#getCurrentItem()();
|
|
const currentMatch = candidates.find((item) => item === currentItem)?.textContent?.trim() ?? "";
|
|
const values = candidates.map((item) => item.textContent?.trim() ?? "");
|
|
const nextMatch = getNextMatch(values, this.#search.current, currentMatch);
|
|
const newItem = candidates.find((item) => item.textContent?.trim() === nextMatch);
|
|
if (newItem) this.#onMatch()(newItem);
|
|
return newItem;
|
|
}
|
|
resetTypeahead() {
|
|
this.#search.current = "";
|
|
}
|
|
get search() {
|
|
return this.#search.current;
|
|
}
|
|
}
|
|
class GraceArea {
|
|
#opts;
|
|
#enabled;
|
|
#isPointerInTransit;
|
|
#pointerGraceArea = null;
|
|
constructor(opts) {
|
|
this.#opts = opts;
|
|
this.#enabled = derived(() => this.#opts.enabled());
|
|
this.#isPointerInTransit = boxAutoReset(false, {
|
|
afterMs: opts.transitTimeout ?? 300,
|
|
onChange: (value) => {
|
|
if (!this.#enabled()) return;
|
|
this.#opts.setIsPointerInTransit?.(value);
|
|
},
|
|
getWindow: () => getWindow(this.#opts.triggerNode())
|
|
});
|
|
watch([opts.triggerNode, opts.contentNode, opts.enabled], ([triggerNode, contentNode, enabled]) => {
|
|
if (!triggerNode || !contentNode || !enabled) return;
|
|
const handleTriggerLeave = (e) => {
|
|
this.#createGraceArea(e, contentNode);
|
|
};
|
|
const handleContentLeave = (e) => {
|
|
this.#createGraceArea(e, triggerNode);
|
|
};
|
|
return executeCallbacks(on(triggerNode, "pointerleave", handleTriggerLeave), on(contentNode, "pointerleave", handleContentLeave));
|
|
});
|
|
watch(() => this.#pointerGraceArea, () => {
|
|
const handleTrackPointerGrace = (e) => {
|
|
if (!this.#pointerGraceArea) return;
|
|
const target = e.target;
|
|
if (!isElement(target)) return;
|
|
const pointerPosition = { x: e.clientX, y: e.clientY };
|
|
const hasEnteredTarget = opts.triggerNode()?.contains(target) || opts.contentNode()?.contains(target);
|
|
const isPointerOutsideGraceArea = !isPointInPolygon(pointerPosition, this.#pointerGraceArea);
|
|
if (hasEnteredTarget) {
|
|
this.#removeGraceArea();
|
|
} else if (isPointerOutsideGraceArea) {
|
|
this.#removeGraceArea();
|
|
opts.onPointerExit();
|
|
}
|
|
};
|
|
const doc = getDocument(opts.triggerNode() ?? opts.contentNode());
|
|
if (!doc) return;
|
|
return on(doc, "pointermove", handleTrackPointerGrace);
|
|
});
|
|
}
|
|
#removeGraceArea() {
|
|
this.#pointerGraceArea = null;
|
|
this.#isPointerInTransit.current = false;
|
|
}
|
|
#createGraceArea(e, hoverTarget) {
|
|
const currentTarget = e.currentTarget;
|
|
if (!isHTMLElement(currentTarget)) return;
|
|
const exitPoint = { x: e.clientX, y: e.clientY };
|
|
const exitSide = getExitSideFromRect(exitPoint, currentTarget.getBoundingClientRect());
|
|
const paddedExitPoints = getPaddedExitPoints(exitPoint, exitSide);
|
|
const hoverTargetPoints = getPointsFromRect(hoverTarget.getBoundingClientRect());
|
|
const graceArea = getHull([...paddedExitPoints, ...hoverTargetPoints]);
|
|
this.#pointerGraceArea = graceArea;
|
|
this.#isPointerInTransit.current = true;
|
|
}
|
|
}
|
|
function getExitSideFromRect(point, rect) {
|
|
const top = Math.abs(rect.top - point.y);
|
|
const bottom = Math.abs(rect.bottom - point.y);
|
|
const right = Math.abs(rect.right - point.x);
|
|
const left = Math.abs(rect.left - point.x);
|
|
switch (Math.min(top, bottom, right, left)) {
|
|
case left:
|
|
return "left";
|
|
case right:
|
|
return "right";
|
|
case top:
|
|
return "top";
|
|
case bottom:
|
|
return "bottom";
|
|
default:
|
|
throw new Error("unreachable");
|
|
}
|
|
}
|
|
function getPaddedExitPoints(exitPoint, exitSide, padding = 5) {
|
|
const tipPadding = padding * 1.5;
|
|
switch (exitSide) {
|
|
case "top":
|
|
return [
|
|
{ x: exitPoint.x - padding, y: exitPoint.y + padding },
|
|
{ x: exitPoint.x, y: exitPoint.y - tipPadding },
|
|
{ x: exitPoint.x + padding, y: exitPoint.y + padding }
|
|
];
|
|
case "bottom":
|
|
return [
|
|
{ x: exitPoint.x - padding, y: exitPoint.y - padding },
|
|
{ x: exitPoint.x, y: exitPoint.y + tipPadding },
|
|
{ x: exitPoint.x + padding, y: exitPoint.y - padding }
|
|
];
|
|
case "left":
|
|
return [
|
|
{ x: exitPoint.x + padding, y: exitPoint.y - padding },
|
|
{ x: exitPoint.x - tipPadding, y: exitPoint.y },
|
|
{ x: exitPoint.x + padding, y: exitPoint.y + padding }
|
|
];
|
|
case "right":
|
|
return [
|
|
{ x: exitPoint.x - padding, y: exitPoint.y - padding },
|
|
{ x: exitPoint.x + tipPadding, y: exitPoint.y },
|
|
{ x: exitPoint.x - padding, y: exitPoint.y + padding }
|
|
];
|
|
}
|
|
}
|
|
function getPointsFromRect(rect) {
|
|
const { top, right, bottom, left } = rect;
|
|
return [
|
|
{ x: left, y: top },
|
|
{ x: right, y: top },
|
|
{ x: right, y: bottom },
|
|
{ x: left, y: bottom }
|
|
];
|
|
}
|
|
function isPointInPolygon(point, polygon) {
|
|
const { x, y } = point;
|
|
let inside = false;
|
|
for (let i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
|
|
const xi = polygon[i].x;
|
|
const yi = polygon[i].y;
|
|
const xj = polygon[j].x;
|
|
const yj = polygon[j].y;
|
|
const intersect = yi > y !== yj > y && x < (xj - xi) * (y - yi) / (yj - yi) + xi;
|
|
if (intersect) inside = !inside;
|
|
}
|
|
return inside;
|
|
}
|
|
function getHull(points) {
|
|
const newPoints = points.slice();
|
|
newPoints.sort((a, b) => {
|
|
if (a.x < b.x) return -1;
|
|
else if (a.x > b.x) return 1;
|
|
else if (a.y < b.y) return -1;
|
|
else if (a.y > b.y) return 1;
|
|
else return 0;
|
|
});
|
|
return getHullPresorted(newPoints);
|
|
}
|
|
function getHullPresorted(points) {
|
|
if (points.length <= 1) return points.slice();
|
|
const upperHull = [];
|
|
for (let i = 0; i < points.length; i++) {
|
|
const p = points[i];
|
|
while (upperHull.length >= 2) {
|
|
const q = upperHull[upperHull.length - 1];
|
|
const r = upperHull[upperHull.length - 2];
|
|
if ((q.x - r.x) * (p.y - r.y) >= (q.y - r.y) * (p.x - r.x)) upperHull.pop();
|
|
else break;
|
|
}
|
|
upperHull.push(p);
|
|
}
|
|
upperHull.pop();
|
|
const lowerHull = [];
|
|
for (let i = points.length - 1; i >= 0; i--) {
|
|
const p = points[i];
|
|
while (lowerHull.length >= 2) {
|
|
const q = lowerHull[lowerHull.length - 1];
|
|
const r = lowerHull[lowerHull.length - 2];
|
|
if ((q.x - r.x) * (p.y - r.y) >= (q.y - r.y) * (p.x - r.x)) lowerHull.pop();
|
|
else break;
|
|
}
|
|
lowerHull.push(p);
|
|
}
|
|
lowerHull.pop();
|
|
if (upperHull.length === 1 && lowerHull.length === 1 && upperHull[0].x === lowerHull[0].x && upperHull[0].y === lowerHull[0].y) return upperHull;
|
|
else return upperHull.concat(lowerHull);
|
|
}
|
|
const CONTEXT_MENU_TRIGGER_ATTR = "data-context-menu-trigger";
|
|
const CONTEXT_MENU_CONTENT_ATTR = "data-context-menu-content";
|
|
const MenuRootContext = new Context("Menu.Root");
|
|
const MenuMenuContext = new Context("Menu.Root | Menu.Sub");
|
|
const MenuContentContext = new Context("Menu.Content");
|
|
const MenuOpenEvent = new CustomEventDispatcher("bitsmenuopen", { bubbles: false, cancelable: true });
|
|
const menuAttrs = createBitsAttrs({
|
|
component: "menu",
|
|
parts: [
|
|
"trigger",
|
|
"content",
|
|
"sub-trigger",
|
|
"item",
|
|
"group",
|
|
"group-heading",
|
|
"checkbox-group",
|
|
"checkbox-item",
|
|
"radio-group",
|
|
"radio-item",
|
|
"separator",
|
|
"sub-content",
|
|
"arrow"
|
|
]
|
|
});
|
|
class MenuRootState {
|
|
static create(opts) {
|
|
const root = new MenuRootState(opts);
|
|
return MenuRootContext.set(root);
|
|
}
|
|
opts;
|
|
isUsingKeyboard = new IsUsingKeyboard();
|
|
ignoreCloseAutoFocus = false;
|
|
isPointerInTransit = false;
|
|
constructor(opts) {
|
|
this.opts = opts;
|
|
}
|
|
getBitsAttr = (part) => {
|
|
return menuAttrs.getAttr(part, this.opts.variant.current);
|
|
};
|
|
}
|
|
class MenuMenuState {
|
|
static create(opts, root) {
|
|
return MenuMenuContext.set(new MenuMenuState(opts, root, null));
|
|
}
|
|
opts;
|
|
root;
|
|
parentMenu;
|
|
contentId = boxWith(() => "");
|
|
contentNode = null;
|
|
contentPresence;
|
|
triggerNode = null;
|
|
constructor(opts, root, parentMenu) {
|
|
this.opts = opts;
|
|
this.root = root;
|
|
this.parentMenu = parentMenu;
|
|
this.contentPresence = new PresenceManager({
|
|
ref: boxWith(() => this.contentNode),
|
|
open: this.opts.open,
|
|
onComplete: () => {
|
|
this.opts.onOpenChangeComplete.current(this.opts.open.current);
|
|
}
|
|
});
|
|
if (parentMenu) {
|
|
watch(() => parentMenu.opts.open.current, () => {
|
|
if (parentMenu.opts.open.current) return;
|
|
this.opts.open.current = false;
|
|
});
|
|
}
|
|
}
|
|
toggleOpen() {
|
|
this.opts.open.current = !this.opts.open.current;
|
|
}
|
|
onOpen() {
|
|
this.opts.open.current = true;
|
|
}
|
|
onClose() {
|
|
this.opts.open.current = false;
|
|
}
|
|
}
|
|
class MenuContentState {
|
|
static create(opts) {
|
|
return MenuContentContext.set(new MenuContentState(opts, MenuMenuContext.get()));
|
|
}
|
|
opts;
|
|
parentMenu;
|
|
rovingFocusGroup;
|
|
domContext;
|
|
attachment;
|
|
search = "";
|
|
#timer = 0;
|
|
#handleTypeaheadSearch;
|
|
mounted = false;
|
|
#isSub;
|
|
constructor(opts, parentMenu) {
|
|
this.opts = opts;
|
|
this.parentMenu = parentMenu;
|
|
this.domContext = new DOMContext(opts.ref);
|
|
this.attachment = attachRef(this.opts.ref, (v) => {
|
|
if (this.parentMenu.contentNode !== v) {
|
|
this.parentMenu.contentNode = v;
|
|
}
|
|
});
|
|
parentMenu.contentId = opts.id;
|
|
this.#isSub = opts.isSub ?? false;
|
|
this.onkeydown = this.onkeydown.bind(this);
|
|
this.onblur = this.onblur.bind(this);
|
|
this.onfocus = this.onfocus.bind(this);
|
|
this.handleInteractOutside = this.handleInteractOutside.bind(this);
|
|
new GraceArea({
|
|
contentNode: () => this.parentMenu.contentNode,
|
|
triggerNode: () => this.parentMenu.triggerNode,
|
|
enabled: () => this.parentMenu.opts.open.current && Boolean(this.parentMenu.triggerNode?.hasAttribute(this.parentMenu.root.getBitsAttr("sub-trigger"))),
|
|
onPointerExit: () => {
|
|
this.parentMenu.opts.open.current = false;
|
|
},
|
|
setIsPointerInTransit: (value) => {
|
|
this.parentMenu.root.isPointerInTransit = value;
|
|
}
|
|
});
|
|
this.#handleTypeaheadSearch = new DOMTypeahead({
|
|
getActiveElement: () => this.domContext.getActiveElement(),
|
|
getWindow: () => this.domContext.getWindow()
|
|
}).handleTypeaheadSearch;
|
|
this.rovingFocusGroup = new RovingFocusGroup({
|
|
rootNode: boxWith(() => this.parentMenu.contentNode),
|
|
candidateAttr: this.parentMenu.root.getBitsAttr("item"),
|
|
loop: this.opts.loop,
|
|
orientation: boxWith(() => "vertical")
|
|
});
|
|
watch(() => this.parentMenu.contentNode, (contentNode) => {
|
|
if (!contentNode) return;
|
|
const handler = () => {
|
|
afterTick(() => {
|
|
if (!this.parentMenu.root.isUsingKeyboard.current) return;
|
|
this.rovingFocusGroup.focusFirstCandidate();
|
|
});
|
|
};
|
|
return MenuOpenEvent.listen(contentNode, handler);
|
|
});
|
|
}
|
|
#getCandidateNodes() {
|
|
const node = this.parentMenu.contentNode;
|
|
if (!node) return [];
|
|
const candidates = Array.from(node.querySelectorAll(`[${this.parentMenu.root.getBitsAttr("item")}]:not([data-disabled])`));
|
|
return candidates;
|
|
}
|
|
#isPointerMovingToSubmenu() {
|
|
return this.parentMenu.root.isPointerInTransit;
|
|
}
|
|
onCloseAutoFocus = (e) => {
|
|
this.opts.onCloseAutoFocus.current?.(e);
|
|
if (e.defaultPrevented || this.#isSub) return;
|
|
if (this.parentMenu.triggerNode && isTabbable(this.parentMenu.triggerNode)) {
|
|
e.preventDefault();
|
|
this.parentMenu.triggerNode.focus();
|
|
}
|
|
};
|
|
handleTabKeyDown(e) {
|
|
let rootMenu = this.parentMenu;
|
|
while (rootMenu.parentMenu !== null) {
|
|
rootMenu = rootMenu.parentMenu;
|
|
}
|
|
if (!rootMenu.triggerNode) return;
|
|
e.preventDefault();
|
|
const nodeToFocus = getTabbableFrom(rootMenu.triggerNode, e.shiftKey ? "prev" : "next");
|
|
if (nodeToFocus) {
|
|
this.parentMenu.root.ignoreCloseAutoFocus = true;
|
|
rootMenu.onClose();
|
|
afterTick(() => {
|
|
nodeToFocus.focus();
|
|
afterTick(() => {
|
|
this.parentMenu.root.ignoreCloseAutoFocus = false;
|
|
});
|
|
});
|
|
} else {
|
|
this.domContext.getDocument().body.focus();
|
|
}
|
|
}
|
|
onkeydown(e) {
|
|
if (e.defaultPrevented) return;
|
|
if (e.key === TAB) {
|
|
this.handleTabKeyDown(e);
|
|
return;
|
|
}
|
|
const target = e.target;
|
|
const currentTarget = e.currentTarget;
|
|
if (!isHTMLElement(target) || !isHTMLElement(currentTarget)) return;
|
|
const isKeydownInside = target.closest(`[${this.parentMenu.root.getBitsAttr("content")}]`)?.id === this.parentMenu.contentId.current;
|
|
const isModifierKey = e.ctrlKey || e.altKey || e.metaKey;
|
|
const isCharacterKey = e.key.length === 1;
|
|
const kbdFocusedEl = this.rovingFocusGroup.handleKeydown(target, e);
|
|
if (kbdFocusedEl) return;
|
|
if (e.code === "Space") return;
|
|
const candidateNodes = this.#getCandidateNodes();
|
|
if (isKeydownInside) {
|
|
if (!isModifierKey && isCharacterKey) {
|
|
this.#handleTypeaheadSearch(e.key, candidateNodes);
|
|
}
|
|
}
|
|
if (e.target?.id !== this.parentMenu.contentId.current) return;
|
|
if (!FIRST_LAST_KEYS.includes(e.key)) return;
|
|
e.preventDefault();
|
|
if (LAST_KEYS.includes(e.key)) {
|
|
candidateNodes.reverse();
|
|
}
|
|
focusFirst(candidateNodes, { select: false }, () => this.domContext.getActiveElement());
|
|
}
|
|
onblur(e) {
|
|
if (!isElement(e.currentTarget)) return;
|
|
if (!isElement(e.target)) return;
|
|
if (!e.currentTarget.contains?.(e.target)) {
|
|
this.domContext.getWindow().clearTimeout(this.#timer);
|
|
this.search = "";
|
|
}
|
|
}
|
|
onfocus(_) {
|
|
if (!this.parentMenu.root.isUsingKeyboard.current) return;
|
|
afterTick(() => this.rovingFocusGroup.focusFirstCandidate());
|
|
}
|
|
onItemEnter() {
|
|
return this.#isPointerMovingToSubmenu();
|
|
}
|
|
onItemLeave(e) {
|
|
if (e.currentTarget.hasAttribute(this.parentMenu.root.getBitsAttr("sub-trigger"))) return;
|
|
if (this.#isPointerMovingToSubmenu() || this.parentMenu.root.isUsingKeyboard.current) return;
|
|
const contentNode = this.parentMenu.contentNode;
|
|
contentNode?.focus();
|
|
this.rovingFocusGroup.setCurrentTabStopId("");
|
|
}
|
|
onTriggerLeave() {
|
|
if (this.#isPointerMovingToSubmenu()) return true;
|
|
return false;
|
|
}
|
|
handleInteractOutside(e) {
|
|
if (!isElementOrSVGElement(e.target)) return;
|
|
const triggerId = this.parentMenu.triggerNode?.id;
|
|
if (e.target.id === triggerId) {
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
if (e.target.closest(`#${triggerId}`)) {
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
get shouldRender() {
|
|
return this.parentMenu.contentPresence.shouldRender;
|
|
}
|
|
#snippetProps = derived(() => ({ open: this.parentMenu.opts.open.current }));
|
|
get snippetProps() {
|
|
return this.#snippetProps();
|
|
}
|
|
set snippetProps($$value) {
|
|
return this.#snippetProps($$value);
|
|
}
|
|
#props = derived(() => ({
|
|
id: this.opts.id.current,
|
|
role: "menu",
|
|
"aria-orientation": "vertical",
|
|
[this.parentMenu.root.getBitsAttr("content")]: "",
|
|
"data-state": getDataOpenClosed(this.parentMenu.opts.open.current),
|
|
onkeydown: this.onkeydown,
|
|
onblur: this.onblur,
|
|
onfocus: this.onfocus,
|
|
dir: this.parentMenu.root.opts.dir.current,
|
|
style: { pointerEvents: "auto", contain: "layout style" },
|
|
...this.attachment
|
|
}));
|
|
get props() {
|
|
return this.#props();
|
|
}
|
|
set props($$value) {
|
|
return this.#props($$value);
|
|
}
|
|
popperProps = { onCloseAutoFocus: (e) => this.onCloseAutoFocus(e) };
|
|
}
|
|
class MenuItemSharedState {
|
|
opts;
|
|
content;
|
|
attachment;
|
|
#isFocused = false;
|
|
constructor(opts, content) {
|
|
this.opts = opts;
|
|
this.content = content;
|
|
this.attachment = attachRef(this.opts.ref);
|
|
this.onpointermove = this.onpointermove.bind(this);
|
|
this.onpointerleave = this.onpointerleave.bind(this);
|
|
this.onfocus = this.onfocus.bind(this);
|
|
this.onblur = this.onblur.bind(this);
|
|
}
|
|
onpointermove(e) {
|
|
if (e.defaultPrevented) return;
|
|
if (!isMouseEvent(e)) return;
|
|
if (this.opts.disabled.current) {
|
|
this.content.onItemLeave(e);
|
|
} else {
|
|
const defaultPrevented = this.content.onItemEnter();
|
|
if (defaultPrevented) return;
|
|
const item = e.currentTarget;
|
|
if (!isHTMLElement(item)) return;
|
|
item.focus();
|
|
}
|
|
}
|
|
onpointerleave(e) {
|
|
if (e.defaultPrevented) return;
|
|
if (!isMouseEvent(e)) return;
|
|
this.content.onItemLeave(e);
|
|
}
|
|
onfocus(e) {
|
|
afterTick(() => {
|
|
if (e.defaultPrevented || this.opts.disabled.current) return;
|
|
this.#isFocused = true;
|
|
});
|
|
}
|
|
onblur(e) {
|
|
afterTick(() => {
|
|
if (e.defaultPrevented) return;
|
|
this.#isFocused = false;
|
|
});
|
|
}
|
|
#props = derived(() => ({
|
|
id: this.opts.id.current,
|
|
tabindex: -1,
|
|
role: "menuitem",
|
|
"aria-disabled": boolToStr(this.opts.disabled.current),
|
|
"data-disabled": boolToEmptyStrOrUndef(this.opts.disabled.current),
|
|
"data-highlighted": this.#isFocused ? "" : void 0,
|
|
[this.content.parentMenu.root.getBitsAttr("item")]: "",
|
|
//
|
|
onpointermove: this.onpointermove,
|
|
onpointerleave: this.onpointerleave,
|
|
onfocus: this.onfocus,
|
|
onblur: this.onblur,
|
|
...this.attachment
|
|
}));
|
|
get props() {
|
|
return this.#props();
|
|
}
|
|
set props($$value) {
|
|
return this.#props($$value);
|
|
}
|
|
}
|
|
class MenuItemState {
|
|
static create(opts) {
|
|
const item = new MenuItemSharedState(opts, MenuContentContext.get());
|
|
return new MenuItemState(opts, item);
|
|
}
|
|
opts;
|
|
item;
|
|
root;
|
|
#isPointerDown = false;
|
|
constructor(opts, item) {
|
|
this.opts = opts;
|
|
this.item = item;
|
|
this.root = item.content.parentMenu.root;
|
|
this.onkeydown = this.onkeydown.bind(this);
|
|
this.onclick = this.onclick.bind(this);
|
|
this.onpointerdown = this.onpointerdown.bind(this);
|
|
this.onpointerup = this.onpointerup.bind(this);
|
|
}
|
|
#handleSelect() {
|
|
if (this.item.opts.disabled.current) return;
|
|
const selectEvent = new CustomEvent("menuitemselect", { bubbles: true, cancelable: true });
|
|
this.opts.onSelect.current(selectEvent);
|
|
if (selectEvent.defaultPrevented) {
|
|
this.item.content.parentMenu.root.isUsingKeyboard.current = false;
|
|
return;
|
|
}
|
|
if (this.opts.closeOnSelect.current) {
|
|
this.item.content.parentMenu.root.opts.onClose();
|
|
}
|
|
}
|
|
onkeydown(e) {
|
|
const isTypingAhead = this.item.content.search !== "";
|
|
if (this.item.opts.disabled.current || isTypingAhead && e.key === SPACE) return;
|
|
if (SELECTION_KEYS.includes(e.key)) {
|
|
if (!isHTMLElement(e.currentTarget)) return;
|
|
e.currentTarget.click();
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
onclick(_) {
|
|
if (this.item.opts.disabled.current) return;
|
|
this.#handleSelect();
|
|
}
|
|
onpointerup(e) {
|
|
if (e.defaultPrevented) return;
|
|
if (!this.#isPointerDown) {
|
|
if (!isHTMLElement(e.currentTarget)) return;
|
|
e.currentTarget?.click();
|
|
}
|
|
}
|
|
onpointerdown(_) {
|
|
this.#isPointerDown = true;
|
|
}
|
|
#props = derived(() => mergeProps(this.item.props, {
|
|
onclick: this.onclick,
|
|
onpointerdown: this.onpointerdown,
|
|
onpointerup: this.onpointerup,
|
|
onkeydown: this.onkeydown
|
|
}));
|
|
get props() {
|
|
return this.#props();
|
|
}
|
|
set props($$value) {
|
|
return this.#props($$value);
|
|
}
|
|
}
|
|
class MenuSeparatorState {
|
|
static create(opts) {
|
|
return new MenuSeparatorState(opts, MenuRootContext.get());
|
|
}
|
|
opts;
|
|
root;
|
|
attachment;
|
|
constructor(opts, root) {
|
|
this.opts = opts;
|
|
this.root = root;
|
|
this.attachment = attachRef(this.opts.ref);
|
|
}
|
|
#props = derived(() => ({
|
|
id: this.opts.id.current,
|
|
role: "group",
|
|
[this.root.getBitsAttr("separator")]: "",
|
|
...this.attachment
|
|
}));
|
|
get props() {
|
|
return this.#props();
|
|
}
|
|
set props($$value) {
|
|
return this.#props($$value);
|
|
}
|
|
}
|
|
class DropdownMenuTriggerState {
|
|
static create(opts) {
|
|
return new DropdownMenuTriggerState(opts, MenuMenuContext.get());
|
|
}
|
|
opts;
|
|
parentMenu;
|
|
attachment;
|
|
constructor(opts, parentMenu) {
|
|
this.opts = opts;
|
|
this.parentMenu = parentMenu;
|
|
this.attachment = attachRef(this.opts.ref, (v) => this.parentMenu.triggerNode = v);
|
|
}
|
|
onclick = (e) => {
|
|
if (this.opts.disabled.current || e.detail !== 0) return;
|
|
this.parentMenu.toggleOpen();
|
|
e.preventDefault();
|
|
};
|
|
onpointerdown = (e) => {
|
|
if (this.opts.disabled.current) return;
|
|
if (e.pointerType === "touch") return e.preventDefault();
|
|
if (e.button === 0 && e.ctrlKey === false) {
|
|
this.parentMenu.toggleOpen();
|
|
if (!this.parentMenu.opts.open.current) e.preventDefault();
|
|
}
|
|
};
|
|
onpointerup = (e) => {
|
|
if (this.opts.disabled.current) return;
|
|
if (e.pointerType === "touch") {
|
|
e.preventDefault();
|
|
this.parentMenu.toggleOpen();
|
|
}
|
|
};
|
|
onkeydown = (e) => {
|
|
if (this.opts.disabled.current) return;
|
|
if (e.key === SPACE || e.key === ENTER) {
|
|
this.parentMenu.toggleOpen();
|
|
e.preventDefault();
|
|
return;
|
|
}
|
|
if (e.key === ARROW_DOWN) {
|
|
this.parentMenu.onOpen();
|
|
e.preventDefault();
|
|
}
|
|
};
|
|
#ariaControls = derived(() => {
|
|
if (this.parentMenu.opts.open.current && this.parentMenu.contentId.current) return this.parentMenu.contentId.current;
|
|
return void 0;
|
|
});
|
|
#props = derived(() => ({
|
|
id: this.opts.id.current,
|
|
disabled: this.opts.disabled.current,
|
|
"aria-haspopup": "menu",
|
|
"aria-expanded": boolToStr(this.parentMenu.opts.open.current),
|
|
"aria-controls": this.#ariaControls(),
|
|
"data-disabled": boolToEmptyStrOrUndef(this.opts.disabled.current),
|
|
"data-state": getDataOpenClosed(this.parentMenu.opts.open.current),
|
|
[this.parentMenu.root.getBitsAttr("trigger")]: "",
|
|
//
|
|
onclick: this.onclick,
|
|
onpointerdown: this.onpointerdown,
|
|
onpointerup: this.onpointerup,
|
|
onkeydown: this.onkeydown,
|
|
...this.attachment
|
|
}));
|
|
get props() {
|
|
return this.#props();
|
|
}
|
|
set props($$value) {
|
|
return this.#props($$value);
|
|
}
|
|
}
|
|
globalThis.bitsDismissableLayers ??= /* @__PURE__ */ new Map();
|
|
class DismissibleLayerState {
|
|
static create(opts) {
|
|
return new DismissibleLayerState(opts);
|
|
}
|
|
opts;
|
|
#interactOutsideProp;
|
|
#behaviorType;
|
|
#interceptedEvents = { pointerdown: false };
|
|
#isResponsibleLayer = false;
|
|
#isFocusInsideDOMTree = false;
|
|
#documentObj = void 0;
|
|
#onFocusOutside;
|
|
#unsubClickListener = noop;
|
|
constructor(opts) {
|
|
this.opts = opts;
|
|
this.#behaviorType = opts.interactOutsideBehavior;
|
|
this.#interactOutsideProp = opts.onInteractOutside;
|
|
this.#onFocusOutside = opts.onFocusOutside;
|
|
let unsubEvents = noop;
|
|
const cleanup = () => {
|
|
this.#resetState();
|
|
globalThis.bitsDismissableLayers.delete(this);
|
|
this.#handleInteractOutside.destroy();
|
|
unsubEvents();
|
|
};
|
|
watch([() => this.opts.enabled.current, () => this.opts.ref.current], () => {
|
|
if (!this.opts.enabled.current || !this.opts.ref.current) return;
|
|
afterSleep(1, () => {
|
|
if (!this.opts.ref.current) return;
|
|
globalThis.bitsDismissableLayers.set(this, this.#behaviorType);
|
|
unsubEvents();
|
|
unsubEvents = this.#addEventListeners();
|
|
});
|
|
return cleanup;
|
|
});
|
|
}
|
|
#handleFocus = (event) => {
|
|
if (event.defaultPrevented) return;
|
|
if (!this.opts.ref.current) return;
|
|
afterTick(() => {
|
|
if (!this.opts.ref.current || this.#isTargetWithinLayer(event.target)) return;
|
|
if (event.target && !this.#isFocusInsideDOMTree) {
|
|
this.#onFocusOutside.current?.(event);
|
|
}
|
|
});
|
|
};
|
|
#addEventListeners() {
|
|
return executeCallbacks(
|
|
/**
|
|
* CAPTURE INTERACTION START
|
|
* mark interaction-start event as intercepted.
|
|
* mark responsible layer during interaction start
|
|
* to avoid checking if is responsible layer during interaction end
|
|
* when a new floating element may have been opened.
|
|
*/
|
|
on(this.#documentObj, "pointerdown", executeCallbacks(this.#markInterceptedEvent, this.#markResponsibleLayer), { capture: true }),
|
|
/**
|
|
* BUBBLE INTERACTION START
|
|
* Mark interaction-start event as non-intercepted. Debounce `onInteractOutsideStart`
|
|
* to avoid prematurely checking if other events were intercepted.
|
|
*/
|
|
on(this.#documentObj, "pointerdown", executeCallbacks(this.#markNonInterceptedEvent, this.#handleInteractOutside)),
|
|
/**
|
|
* HANDLE FOCUS OUTSIDE
|
|
*/
|
|
on(this.#documentObj, "focusin", this.#handleFocus)
|
|
);
|
|
}
|
|
#handleDismiss = (e) => {
|
|
let event = e;
|
|
if (event.defaultPrevented) {
|
|
event = createWrappedEvent(e);
|
|
}
|
|
this.#interactOutsideProp.current(e);
|
|
};
|
|
#handleInteractOutside = debounce(
|
|
(e) => {
|
|
if (!this.opts.ref.current) {
|
|
this.#unsubClickListener();
|
|
return;
|
|
}
|
|
const isEventValid = this.opts.isValidEvent.current(e, this.opts.ref.current) || isValidEvent(e, this.opts.ref.current);
|
|
if (!this.#isResponsibleLayer || this.#isAnyEventIntercepted() || !isEventValid) {
|
|
this.#unsubClickListener();
|
|
return;
|
|
}
|
|
let event = e;
|
|
if (event.defaultPrevented) {
|
|
event = createWrappedEvent(event);
|
|
}
|
|
if (this.#behaviorType.current !== "close" && this.#behaviorType.current !== "defer-otherwise-close") {
|
|
this.#unsubClickListener();
|
|
return;
|
|
}
|
|
if (e.pointerType === "touch") {
|
|
this.#unsubClickListener();
|
|
this.#unsubClickListener = on(this.#documentObj, "click", this.#handleDismiss, { once: true });
|
|
} else {
|
|
this.#interactOutsideProp.current(event);
|
|
}
|
|
},
|
|
10
|
|
);
|
|
#markInterceptedEvent = (e) => {
|
|
this.#interceptedEvents[e.type] = true;
|
|
};
|
|
#markNonInterceptedEvent = (e) => {
|
|
this.#interceptedEvents[e.type] = false;
|
|
};
|
|
#markResponsibleLayer = () => {
|
|
if (!this.opts.ref.current) return;
|
|
this.#isResponsibleLayer = isResponsibleLayer(this.opts.ref.current);
|
|
};
|
|
#isTargetWithinLayer = (target) => {
|
|
if (!this.opts.ref.current) return false;
|
|
return isOrContainsTarget(this.opts.ref.current, target);
|
|
};
|
|
#resetState = debounce(
|
|
() => {
|
|
for (const eventType in this.#interceptedEvents) {
|
|
this.#interceptedEvents[eventType] = false;
|
|
}
|
|
this.#isResponsibleLayer = false;
|
|
},
|
|
20
|
|
);
|
|
#isAnyEventIntercepted() {
|
|
const i = Object.values(this.#interceptedEvents).some(Boolean);
|
|
return i;
|
|
}
|
|
#onfocuscapture = () => {
|
|
this.#isFocusInsideDOMTree = true;
|
|
};
|
|
#onblurcapture = () => {
|
|
this.#isFocusInsideDOMTree = false;
|
|
};
|
|
props = {
|
|
onfocuscapture: this.#onfocuscapture,
|
|
onblurcapture: this.#onblurcapture
|
|
};
|
|
}
|
|
function getTopMostDismissableLayer(layersArr = [...globalThis.bitsDismissableLayers]) {
|
|
return layersArr.findLast(([_, { current: behaviorType }]) => behaviorType === "close" || behaviorType === "ignore");
|
|
}
|
|
function isResponsibleLayer(node) {
|
|
const layersArr = [...globalThis.bitsDismissableLayers];
|
|
const topMostLayer = getTopMostDismissableLayer(layersArr);
|
|
if (topMostLayer) return topMostLayer[0].opts.ref.current === node;
|
|
const [firstLayerNode] = layersArr[0];
|
|
return firstLayerNode.opts.ref.current === node;
|
|
}
|
|
function isValidEvent(e, node) {
|
|
const target = e.target;
|
|
if (!isElementOrSVGElement(target)) return false;
|
|
const targetIsContextMenuTrigger = Boolean(target.closest(`[${CONTEXT_MENU_TRIGGER_ATTR}]`));
|
|
if ("button" in e && e.button > 0 && !targetIsContextMenuTrigger) return false;
|
|
if ("button" in e && e.button === 0 && targetIsContextMenuTrigger) return true;
|
|
const nodeIsContextMenu = Boolean(node.closest(`[${CONTEXT_MENU_CONTENT_ATTR}]`));
|
|
if (targetIsContextMenuTrigger && nodeIsContextMenu) return false;
|
|
const ownerDocument = getOwnerDocument(target);
|
|
const isValid = ownerDocument.documentElement.contains(target) && !isOrContainsTarget(node, target) && isClickTrulyOutside(e, node);
|
|
return isValid;
|
|
}
|
|
function createWrappedEvent(e) {
|
|
const capturedCurrentTarget = e.currentTarget;
|
|
const capturedTarget = e.target;
|
|
let newEvent;
|
|
if (e instanceof PointerEvent) {
|
|
newEvent = new PointerEvent(e.type, e);
|
|
} else {
|
|
newEvent = new PointerEvent("pointerdown", e);
|
|
}
|
|
let isPrevented = false;
|
|
const wrappedEvent = new Proxy(newEvent, {
|
|
get: (target, prop) => {
|
|
if (prop === "currentTarget") {
|
|
return capturedCurrentTarget;
|
|
}
|
|
if (prop === "target") {
|
|
return capturedTarget;
|
|
}
|
|
if (prop === "preventDefault") {
|
|
return () => {
|
|
isPrevented = true;
|
|
if (typeof target.preventDefault === "function") {
|
|
target.preventDefault();
|
|
}
|
|
};
|
|
}
|
|
if (prop === "defaultPrevented") {
|
|
return isPrevented;
|
|
}
|
|
if (prop in target) {
|
|
return target[prop];
|
|
}
|
|
return e[prop];
|
|
}
|
|
});
|
|
return wrappedEvent;
|
|
}
|
|
function Dismissible_layer($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let {
|
|
interactOutsideBehavior = "close",
|
|
onInteractOutside = noop,
|
|
onFocusOutside = noop,
|
|
id,
|
|
children,
|
|
enabled,
|
|
isValidEvent: isValidEvent2 = () => false,
|
|
ref
|
|
} = $$props;
|
|
const dismissibleLayerState = DismissibleLayerState.create({
|
|
id: boxWith(() => id),
|
|
interactOutsideBehavior: boxWith(() => interactOutsideBehavior),
|
|
onInteractOutside: boxWith(() => onInteractOutside),
|
|
enabled: boxWith(() => enabled),
|
|
onFocusOutside: boxWith(() => onFocusOutside),
|
|
isValidEvent: boxWith(() => isValidEvent2),
|
|
ref
|
|
});
|
|
children?.($$renderer2, { props: dismissibleLayerState.props });
|
|
$$renderer2.push(`<!---->`);
|
|
});
|
|
}
|
|
globalThis.bitsEscapeLayers ??= /* @__PURE__ */ new Map();
|
|
class EscapeLayerState {
|
|
static create(opts) {
|
|
return new EscapeLayerState(opts);
|
|
}
|
|
opts;
|
|
domContext;
|
|
constructor(opts) {
|
|
this.opts = opts;
|
|
this.domContext = new DOMContext(this.opts.ref);
|
|
let unsubEvents = noop;
|
|
watch(() => opts.enabled.current, (enabled) => {
|
|
if (enabled) {
|
|
globalThis.bitsEscapeLayers.set(this, opts.escapeKeydownBehavior);
|
|
unsubEvents = this.#addEventListener();
|
|
}
|
|
return () => {
|
|
unsubEvents();
|
|
globalThis.bitsEscapeLayers.delete(this);
|
|
};
|
|
});
|
|
}
|
|
#addEventListener = () => {
|
|
return on(this.domContext.getDocument(), "keydown", this.#onkeydown, { passive: false });
|
|
};
|
|
#onkeydown = (e) => {
|
|
if (e.key !== ESCAPE || !isResponsibleEscapeLayer(this)) return;
|
|
const clonedEvent = new KeyboardEvent(e.type, e);
|
|
e.preventDefault();
|
|
const behaviorType = this.opts.escapeKeydownBehavior.current;
|
|
if (behaviorType !== "close" && behaviorType !== "defer-otherwise-close") return;
|
|
this.opts.onEscapeKeydown.current(clonedEvent);
|
|
};
|
|
}
|
|
function isResponsibleEscapeLayer(instance) {
|
|
const layersArr = [...globalThis.bitsEscapeLayers];
|
|
const topMostLayer = layersArr.findLast(([_, { current: behaviorType }]) => behaviorType === "close" || behaviorType === "ignore");
|
|
if (topMostLayer) return topMostLayer[0] === instance;
|
|
const [firstLayerNode] = layersArr[0];
|
|
return firstLayerNode === instance;
|
|
}
|
|
function Escape_layer($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let {
|
|
escapeKeydownBehavior = "close",
|
|
onEscapeKeydown = noop,
|
|
children,
|
|
enabled,
|
|
ref
|
|
} = $$props;
|
|
EscapeLayerState.create({
|
|
escapeKeydownBehavior: boxWith(() => escapeKeydownBehavior),
|
|
onEscapeKeydown: boxWith(() => onEscapeKeydown),
|
|
enabled: boxWith(() => enabled),
|
|
ref
|
|
});
|
|
children?.($$renderer2);
|
|
$$renderer2.push(`<!---->`);
|
|
});
|
|
}
|
|
class FocusScopeManager {
|
|
static instance;
|
|
#scopeStack = simpleBox([]);
|
|
#focusHistory = /* @__PURE__ */ new WeakMap();
|
|
#preFocusHistory = /* @__PURE__ */ new WeakMap();
|
|
static getInstance() {
|
|
if (!this.instance) {
|
|
this.instance = new FocusScopeManager();
|
|
}
|
|
return this.instance;
|
|
}
|
|
register(scope) {
|
|
const current = this.getActive();
|
|
if (current && current !== scope) {
|
|
current.pause();
|
|
}
|
|
const activeElement = document.activeElement;
|
|
if (activeElement && activeElement !== document.body) {
|
|
this.#preFocusHistory.set(scope, activeElement);
|
|
}
|
|
this.#scopeStack.current = this.#scopeStack.current.filter((s) => s !== scope);
|
|
this.#scopeStack.current.unshift(scope);
|
|
}
|
|
unregister(scope) {
|
|
this.#scopeStack.current = this.#scopeStack.current.filter((s) => s !== scope);
|
|
const next = this.getActive();
|
|
if (next) {
|
|
next.resume();
|
|
}
|
|
}
|
|
getActive() {
|
|
return this.#scopeStack.current[0];
|
|
}
|
|
setFocusMemory(scope, element) {
|
|
this.#focusHistory.set(scope, element);
|
|
}
|
|
getFocusMemory(scope) {
|
|
return this.#focusHistory.get(scope);
|
|
}
|
|
isActiveScope(scope) {
|
|
return this.getActive() === scope;
|
|
}
|
|
setPreFocusMemory(scope, element) {
|
|
this.#preFocusHistory.set(scope, element);
|
|
}
|
|
getPreFocusMemory(scope) {
|
|
return this.#preFocusHistory.get(scope);
|
|
}
|
|
clearPreFocusMemory(scope) {
|
|
this.#preFocusHistory.delete(scope);
|
|
}
|
|
}
|
|
class FocusScope {
|
|
#paused = false;
|
|
#container = null;
|
|
#manager = FocusScopeManager.getInstance();
|
|
#cleanupFns = [];
|
|
#opts;
|
|
constructor(opts) {
|
|
this.#opts = opts;
|
|
}
|
|
get paused() {
|
|
return this.#paused;
|
|
}
|
|
pause() {
|
|
this.#paused = true;
|
|
}
|
|
resume() {
|
|
this.#paused = false;
|
|
}
|
|
#cleanup() {
|
|
for (const fn of this.#cleanupFns) {
|
|
fn();
|
|
}
|
|
this.#cleanupFns = [];
|
|
}
|
|
mount(container) {
|
|
if (this.#container) {
|
|
this.unmount();
|
|
}
|
|
this.#container = container;
|
|
this.#manager.register(this);
|
|
this.#setupEventListeners();
|
|
this.#handleOpenAutoFocus();
|
|
}
|
|
unmount() {
|
|
if (!this.#container) return;
|
|
this.#cleanup();
|
|
this.#handleCloseAutoFocus();
|
|
this.#manager.unregister(this);
|
|
this.#manager.clearPreFocusMemory(this);
|
|
this.#container = null;
|
|
}
|
|
#handleOpenAutoFocus() {
|
|
if (!this.#container) return;
|
|
const event = new CustomEvent("focusScope.onOpenAutoFocus", { bubbles: false, cancelable: true });
|
|
this.#opts.onOpenAutoFocus.current(event);
|
|
if (!event.defaultPrevented) {
|
|
requestAnimationFrame(() => {
|
|
if (!this.#container) return;
|
|
const firstTabbable = this.#getFirstTabbable();
|
|
if (firstTabbable) {
|
|
firstTabbable.focus();
|
|
this.#manager.setFocusMemory(this, firstTabbable);
|
|
} else {
|
|
this.#container.focus();
|
|
}
|
|
});
|
|
}
|
|
}
|
|
#handleCloseAutoFocus() {
|
|
const event = new CustomEvent("focusScope.onCloseAutoFocus", { bubbles: false, cancelable: true });
|
|
this.#opts.onCloseAutoFocus.current?.(event);
|
|
if (!event.defaultPrevented) {
|
|
const preFocusedElement = this.#manager.getPreFocusMemory(this);
|
|
if (preFocusedElement && document.contains(preFocusedElement)) {
|
|
try {
|
|
preFocusedElement.focus();
|
|
} catch {
|
|
document.body.focus();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#setupEventListeners() {
|
|
if (!this.#container || !this.#opts.trap.current) return;
|
|
const container = this.#container;
|
|
const doc = container.ownerDocument;
|
|
const handleFocus = (e) => {
|
|
if (this.#paused || !this.#manager.isActiveScope(this)) return;
|
|
const target = e.target;
|
|
if (!target) return;
|
|
const isInside = container.contains(target);
|
|
if (isInside) {
|
|
this.#manager.setFocusMemory(this, target);
|
|
} else {
|
|
const lastFocused = this.#manager.getFocusMemory(this);
|
|
if (lastFocused && container.contains(lastFocused) && isFocusable(lastFocused)) {
|
|
e.preventDefault();
|
|
lastFocused.focus();
|
|
} else {
|
|
const firstTabbable = this.#getFirstTabbable();
|
|
const firstFocusable = this.#getAllFocusables()[0];
|
|
(firstTabbable || firstFocusable || container).focus();
|
|
}
|
|
}
|
|
};
|
|
const handleKeydown = (e) => {
|
|
if (!this.#opts.loop || this.#paused || e.key !== "Tab") return;
|
|
if (!this.#manager.isActiveScope(this)) return;
|
|
const tabbables = this.#getTabbables();
|
|
if (tabbables.length === 0) return;
|
|
const first = tabbables[0];
|
|
const last = tabbables[tabbables.length - 1];
|
|
if (!e.shiftKey && doc.activeElement === last) {
|
|
e.preventDefault();
|
|
first.focus();
|
|
} else if (e.shiftKey && doc.activeElement === first) {
|
|
e.preventDefault();
|
|
last.focus();
|
|
}
|
|
};
|
|
this.#cleanupFns.push(on(doc, "focusin", handleFocus, { capture: true }), on(container, "keydown", handleKeydown));
|
|
const observer = new MutationObserver(() => {
|
|
const lastFocused = this.#manager.getFocusMemory(this);
|
|
if (lastFocused && !container.contains(lastFocused)) {
|
|
const firstTabbable = this.#getFirstTabbable();
|
|
const firstFocusable = this.#getAllFocusables()[0];
|
|
const elementToFocus = firstTabbable || firstFocusable;
|
|
if (elementToFocus) {
|
|
elementToFocus.focus();
|
|
this.#manager.setFocusMemory(this, elementToFocus);
|
|
} else {
|
|
container.focus();
|
|
}
|
|
}
|
|
});
|
|
observer.observe(container, { childList: true, subtree: true });
|
|
this.#cleanupFns.push(() => observer.disconnect());
|
|
}
|
|
#getTabbables() {
|
|
if (!this.#container) return [];
|
|
return tabbable(this.#container, { includeContainer: false, getShadowRoot: true });
|
|
}
|
|
#getFirstTabbable() {
|
|
const tabbables = this.#getTabbables();
|
|
return tabbables[0] || null;
|
|
}
|
|
#getAllFocusables() {
|
|
if (!this.#container) return [];
|
|
return focusable(this.#container, { includeContainer: false, getShadowRoot: true });
|
|
}
|
|
static use(opts) {
|
|
let scope = null;
|
|
watch([() => opts.ref.current, () => opts.enabled.current], ([ref, enabled]) => {
|
|
if (ref && enabled) {
|
|
if (!scope) {
|
|
scope = new FocusScope(opts);
|
|
}
|
|
scope.mount(ref);
|
|
} else if (scope) {
|
|
scope.unmount();
|
|
scope = null;
|
|
}
|
|
});
|
|
return {
|
|
get props() {
|
|
return { tabindex: -1 };
|
|
}
|
|
};
|
|
}
|
|
}
|
|
function Focus_scope($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let {
|
|
enabled = false,
|
|
trapFocus = false,
|
|
loop = false,
|
|
onCloseAutoFocus = noop,
|
|
onOpenAutoFocus = noop,
|
|
focusScope,
|
|
ref
|
|
} = $$props;
|
|
const focusScopeState = FocusScope.use({
|
|
enabled: boxWith(() => enabled),
|
|
trap: boxWith(() => trapFocus),
|
|
loop,
|
|
onCloseAutoFocus: boxWith(() => onCloseAutoFocus),
|
|
onOpenAutoFocus: boxWith(() => onOpenAutoFocus),
|
|
ref
|
|
});
|
|
focusScope?.($$renderer2, { props: focusScopeState.props });
|
|
$$renderer2.push(`<!---->`);
|
|
});
|
|
}
|
|
globalThis.bitsTextSelectionLayers ??= /* @__PURE__ */ new Map();
|
|
class TextSelectionLayerState {
|
|
static create(opts) {
|
|
return new TextSelectionLayerState(opts);
|
|
}
|
|
opts;
|
|
domContext;
|
|
#unsubSelectionLock = noop;
|
|
constructor(opts) {
|
|
this.opts = opts;
|
|
this.domContext = new DOMContext(opts.ref);
|
|
let unsubEvents = noop;
|
|
watch(() => this.opts.enabled.current, (isEnabled) => {
|
|
if (isEnabled) {
|
|
globalThis.bitsTextSelectionLayers.set(this, this.opts.enabled);
|
|
unsubEvents();
|
|
unsubEvents = this.#addEventListeners();
|
|
}
|
|
return () => {
|
|
unsubEvents();
|
|
this.#resetSelectionLock();
|
|
globalThis.bitsTextSelectionLayers.delete(this);
|
|
};
|
|
});
|
|
}
|
|
#addEventListeners() {
|
|
return executeCallbacks(on(this.domContext.getDocument(), "pointerdown", this.#pointerdown), on(this.domContext.getDocument(), "pointerup", composeHandlers(this.#resetSelectionLock, this.opts.onPointerUp.current)));
|
|
}
|
|
#pointerdown = (e) => {
|
|
const node = this.opts.ref.current;
|
|
const target = e.target;
|
|
if (!isHTMLElement(node) || !isHTMLElement(target) || !this.opts.enabled.current) return;
|
|
if (!isHighestLayer(this) || !contains(node, target)) return;
|
|
this.opts.onPointerDown.current(e);
|
|
if (e.defaultPrevented) return;
|
|
this.#unsubSelectionLock = preventTextSelectionOverflow(node, this.domContext.getDocument().body);
|
|
};
|
|
#resetSelectionLock = () => {
|
|
this.#unsubSelectionLock();
|
|
this.#unsubSelectionLock = noop;
|
|
};
|
|
}
|
|
const getUserSelect = (node) => node.style.userSelect || node.style.webkitUserSelect;
|
|
function preventTextSelectionOverflow(node, body) {
|
|
const originalBodyUserSelect = getUserSelect(body);
|
|
const originalNodeUserSelect = getUserSelect(node);
|
|
setUserSelect(body, "none");
|
|
setUserSelect(node, "text");
|
|
return () => {
|
|
setUserSelect(body, originalBodyUserSelect);
|
|
setUserSelect(node, originalNodeUserSelect);
|
|
};
|
|
}
|
|
function setUserSelect(node, value) {
|
|
node.style.userSelect = value;
|
|
node.style.webkitUserSelect = value;
|
|
}
|
|
function isHighestLayer(instance) {
|
|
const layersArr = [...globalThis.bitsTextSelectionLayers];
|
|
if (!layersArr.length) return false;
|
|
const highestLayer = layersArr.at(-1);
|
|
if (!highestLayer) return false;
|
|
return highestLayer[0] === instance;
|
|
}
|
|
function Text_selection_layer($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let {
|
|
preventOverflowTextSelection = true,
|
|
onPointerDown = noop,
|
|
onPointerUp = noop,
|
|
id,
|
|
children,
|
|
enabled,
|
|
ref
|
|
} = $$props;
|
|
TextSelectionLayerState.create({
|
|
id: boxWith(() => id),
|
|
onPointerDown: boxWith(() => onPointerDown),
|
|
onPointerUp: boxWith(() => onPointerUp),
|
|
enabled: boxWith(() => enabled && preventOverflowTextSelection),
|
|
ref
|
|
});
|
|
children?.($$renderer2);
|
|
$$renderer2.push(`<!---->`);
|
|
});
|
|
}
|
|
globalThis.bitsIdCounter ??= { current: 0 };
|
|
function useId(prefix = "bits") {
|
|
globalThis.bitsIdCounter.current++;
|
|
return `${prefix}-${globalThis.bitsIdCounter.current}`;
|
|
}
|
|
class SharedState {
|
|
#factory;
|
|
#subscribers = 0;
|
|
#state;
|
|
#scope;
|
|
constructor(factory) {
|
|
this.#factory = factory;
|
|
}
|
|
#dispose() {
|
|
this.#subscribers -= 1;
|
|
if (this.#scope && this.#subscribers <= 0) {
|
|
this.#scope();
|
|
this.#state = void 0;
|
|
this.#scope = void 0;
|
|
}
|
|
}
|
|
get(...args) {
|
|
this.#subscribers += 1;
|
|
if (this.#state === void 0) {
|
|
this.#scope = () => {
|
|
};
|
|
}
|
|
return this.#state;
|
|
}
|
|
}
|
|
const lockMap = new SvelteMap();
|
|
let initialBodyStyle = null;
|
|
let cleanupTimeoutId = null;
|
|
let isInCleanupTransition = false;
|
|
const anyLocked = boxWith(() => {
|
|
for (const value of lockMap.values()) {
|
|
if (value) return true;
|
|
}
|
|
return false;
|
|
});
|
|
let cleanupScheduledAt = null;
|
|
const bodyLockStackCount = new SharedState(() => {
|
|
function resetBodyStyle() {
|
|
return;
|
|
}
|
|
function cancelPendingCleanup() {
|
|
if (cleanupTimeoutId === null) return;
|
|
window.clearTimeout(cleanupTimeoutId);
|
|
cleanupTimeoutId = null;
|
|
}
|
|
function scheduleCleanupIfNoNewLocks(delay, callback) {
|
|
cancelPendingCleanup();
|
|
isInCleanupTransition = true;
|
|
cleanupScheduledAt = Date.now();
|
|
const currentCleanupId = cleanupScheduledAt;
|
|
const cleanupFn = () => {
|
|
cleanupTimeoutId = null;
|
|
if (cleanupScheduledAt !== currentCleanupId) return;
|
|
if (!isAnyLocked(lockMap)) {
|
|
isInCleanupTransition = false;
|
|
callback();
|
|
} else {
|
|
isInCleanupTransition = false;
|
|
}
|
|
};
|
|
const actualDelay = delay === null ? 24 : delay;
|
|
cleanupTimeoutId = window.setTimeout(cleanupFn, actualDelay);
|
|
}
|
|
function ensureInitialStyleCaptured() {
|
|
if (initialBodyStyle === null && lockMap.size === 0 && !isInCleanupTransition) {
|
|
initialBodyStyle = document.body.getAttribute("style");
|
|
}
|
|
}
|
|
watch(() => anyLocked.current, () => {
|
|
if (!anyLocked.current) return;
|
|
ensureInitialStyleCaptured();
|
|
isInCleanupTransition = false;
|
|
const htmlStyle = getComputedStyle(document.documentElement);
|
|
const bodyStyle = getComputedStyle(document.body);
|
|
const hasStableGutter = htmlStyle.scrollbarGutter?.includes("stable") || bodyStyle.scrollbarGutter?.includes("stable");
|
|
const verticalScrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
|
|
const paddingRight = Number.parseInt(bodyStyle.paddingRight ?? "0", 10);
|
|
const config = {
|
|
padding: paddingRight + verticalScrollbarWidth,
|
|
margin: Number.parseInt(bodyStyle.marginRight ?? "0", 10)
|
|
};
|
|
if (verticalScrollbarWidth > 0 && !hasStableGutter) {
|
|
document.body.style.paddingRight = `${config.padding}px`;
|
|
document.body.style.marginRight = `${config.margin}px`;
|
|
document.body.style.setProperty("--scrollbar-width", `${verticalScrollbarWidth}px`);
|
|
}
|
|
document.body.style.overflow = "hidden";
|
|
if (isIOS) {
|
|
on(
|
|
document,
|
|
"touchmove",
|
|
(e) => {
|
|
if (e.target !== document.documentElement) return;
|
|
if (e.touches.length > 1) return;
|
|
e.preventDefault();
|
|
},
|
|
{ passive: false }
|
|
);
|
|
}
|
|
afterTick(() => {
|
|
document.body.style.pointerEvents = "none";
|
|
document.body.style.overflow = "hidden";
|
|
});
|
|
});
|
|
return {
|
|
get lockMap() {
|
|
return lockMap;
|
|
},
|
|
resetBodyStyle,
|
|
scheduleCleanupIfNoNewLocks,
|
|
cancelPendingCleanup,
|
|
ensureInitialStyleCaptured
|
|
};
|
|
});
|
|
class BodyScrollLock {
|
|
#id = useId();
|
|
#initialState;
|
|
#restoreScrollDelay = () => null;
|
|
#countState;
|
|
locked;
|
|
constructor(initialState, restoreScrollDelay = () => null) {
|
|
this.#initialState = initialState;
|
|
this.#restoreScrollDelay = restoreScrollDelay;
|
|
this.#countState = bodyLockStackCount.get();
|
|
if (!this.#countState) return;
|
|
this.#countState.cancelPendingCleanup();
|
|
this.#countState.ensureInitialStyleCaptured();
|
|
this.#countState.lockMap.set(this.#id, this.#initialState ?? false);
|
|
this.locked = boxWith(() => this.#countState.lockMap.get(this.#id) ?? false, (v) => this.#countState.lockMap.set(this.#id, v));
|
|
}
|
|
}
|
|
function isAnyLocked(map) {
|
|
for (const [_, value] of map) {
|
|
if (value) return true;
|
|
}
|
|
return false;
|
|
}
|
|
function Scroll_lock($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let { preventScroll = true, restoreScrollDelay = null } = $$props;
|
|
if (preventScroll) {
|
|
new BodyScrollLock(preventScroll, () => restoreScrollDelay);
|
|
}
|
|
});
|
|
}
|
|
const avatarAttrs = createBitsAttrs({ component: "avatar", parts: ["root", "image", "fallback"] });
|
|
const AvatarRootContext = new Context("Avatar.Root");
|
|
class AvatarRootState {
|
|
static create(opts) {
|
|
return AvatarRootContext.set(new AvatarRootState(opts));
|
|
}
|
|
opts;
|
|
domContext;
|
|
attachment;
|
|
constructor(opts) {
|
|
this.opts = opts;
|
|
this.domContext = new DOMContext(this.opts.ref);
|
|
this.loadImage = this.loadImage.bind(this);
|
|
this.attachment = attachRef(this.opts.ref);
|
|
}
|
|
loadImage(src, crossorigin, referrerPolicy) {
|
|
if (this.opts.loadingStatus.current === "loaded") return;
|
|
let imageTimerId;
|
|
const image = new Image();
|
|
image.src = src;
|
|
if (crossorigin !== void 0) image.crossOrigin = crossorigin;
|
|
if (referrerPolicy) image.referrerPolicy = referrerPolicy;
|
|
this.opts.loadingStatus.current = "loading";
|
|
image.onload = () => {
|
|
imageTimerId = this.domContext.setTimeout(
|
|
() => {
|
|
this.opts.loadingStatus.current = "loaded";
|
|
},
|
|
this.opts.delayMs.current
|
|
);
|
|
};
|
|
image.onerror = () => {
|
|
this.opts.loadingStatus.current = "error";
|
|
};
|
|
return () => {
|
|
if (!imageTimerId) return;
|
|
this.domContext.clearTimeout(imageTimerId);
|
|
};
|
|
}
|
|
#props = derived(() => ({
|
|
id: this.opts.id.current,
|
|
[avatarAttrs.root]: "",
|
|
"data-status": this.opts.loadingStatus.current,
|
|
...this.attachment
|
|
}));
|
|
get props() {
|
|
return this.#props();
|
|
}
|
|
set props($$value) {
|
|
return this.#props($$value);
|
|
}
|
|
}
|
|
class AvatarImageState {
|
|
static create(opts) {
|
|
return new AvatarImageState(opts, AvatarRootContext.get());
|
|
}
|
|
opts;
|
|
root;
|
|
attachment;
|
|
constructor(opts, root) {
|
|
this.opts = opts;
|
|
this.root = root;
|
|
this.attachment = attachRef(this.opts.ref);
|
|
watch.pre(
|
|
[
|
|
() => this.opts.src.current,
|
|
() => this.opts.crossOrigin.current
|
|
],
|
|
([src, crossOrigin]) => {
|
|
if (!src) {
|
|
this.root.opts.loadingStatus.current = "error";
|
|
return;
|
|
}
|
|
this.root.loadImage(src, crossOrigin, this.opts.referrerPolicy.current);
|
|
}
|
|
);
|
|
}
|
|
#props = derived(() => ({
|
|
id: this.opts.id.current,
|
|
style: {
|
|
display: this.root.opts.loadingStatus.current === "loaded" ? "block" : "none"
|
|
},
|
|
"data-status": this.root.opts.loadingStatus.current,
|
|
[avatarAttrs.image]: "",
|
|
src: this.opts.src.current,
|
|
crossorigin: this.opts.crossOrigin.current,
|
|
referrerpolicy: this.opts.referrerPolicy.current,
|
|
...this.attachment
|
|
}));
|
|
get props() {
|
|
return this.#props();
|
|
}
|
|
set props($$value) {
|
|
return this.#props($$value);
|
|
}
|
|
}
|
|
class AvatarFallbackState {
|
|
static create(opts) {
|
|
return new AvatarFallbackState(opts, AvatarRootContext.get());
|
|
}
|
|
opts;
|
|
root;
|
|
attachment;
|
|
constructor(opts, root) {
|
|
this.opts = opts;
|
|
this.root = root;
|
|
this.attachment = attachRef(this.opts.ref);
|
|
}
|
|
#style = derived(() => this.root.opts.loadingStatus.current === "loaded" ? { display: "none" } : void 0);
|
|
get style() {
|
|
return this.#style();
|
|
}
|
|
set style($$value) {
|
|
return this.#style($$value);
|
|
}
|
|
#props = derived(() => ({
|
|
style: this.style,
|
|
"data-status": this.root.opts.loadingStatus.current,
|
|
[avatarAttrs.fallback]: "",
|
|
...this.attachment
|
|
}));
|
|
get props() {
|
|
return this.#props();
|
|
}
|
|
set props($$value) {
|
|
return this.#props($$value);
|
|
}
|
|
}
|
|
function Avatar$1($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
const uid = props_id($$renderer2);
|
|
let {
|
|
delayMs = 0,
|
|
loadingStatus = "loading",
|
|
onLoadingStatusChange,
|
|
child,
|
|
children,
|
|
id = createId(uid),
|
|
ref = null,
|
|
$$slots,
|
|
$$events,
|
|
...restProps
|
|
} = $$props;
|
|
const rootState = AvatarRootState.create({
|
|
delayMs: boxWith(() => delayMs),
|
|
loadingStatus: boxWith(() => loadingStatus, (v) => {
|
|
if (loadingStatus !== v) {
|
|
loadingStatus = v;
|
|
onLoadingStatusChange?.(v);
|
|
}
|
|
}),
|
|
id: boxWith(() => id),
|
|
ref: boxWith(() => ref, (v) => ref = v)
|
|
});
|
|
const mergedProps = mergeProps(restProps, rootState.props);
|
|
if (child) {
|
|
$$renderer2.push("<!--[-->");
|
|
child($$renderer2, { props: mergedProps });
|
|
$$renderer2.push(`<!---->`);
|
|
} else {
|
|
$$renderer2.push("<!--[!-->");
|
|
$$renderer2.push(`<div${attributes({ ...mergedProps })}>`);
|
|
children?.($$renderer2);
|
|
$$renderer2.push(`<!----></div>`);
|
|
}
|
|
$$renderer2.push(`<!--]-->`);
|
|
bind_props($$props, { loadingStatus, ref });
|
|
});
|
|
}
|
|
function Avatar_image$1($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
const uid = props_id($$renderer2);
|
|
let {
|
|
src,
|
|
child,
|
|
id = createId(uid),
|
|
ref = null,
|
|
crossorigin = void 0,
|
|
referrerpolicy = void 0,
|
|
$$slots,
|
|
$$events,
|
|
...restProps
|
|
} = $$props;
|
|
const imageState = AvatarImageState.create({
|
|
src: boxWith(() => src),
|
|
id: boxWith(() => id),
|
|
ref: boxWith(() => ref, (v) => ref = v),
|
|
crossOrigin: boxWith(() => crossorigin),
|
|
referrerPolicy: boxWith(() => referrerpolicy)
|
|
});
|
|
const mergedProps = mergeProps(restProps, imageState.props);
|
|
if (child) {
|
|
$$renderer2.push("<!--[-->");
|
|
child($$renderer2, { props: mergedProps });
|
|
$$renderer2.push(`<!---->`);
|
|
} else {
|
|
$$renderer2.push("<!--[!-->");
|
|
$$renderer2.push(`<img${attributes({ ...mergedProps, src })} onload="this.__e=event" onerror="this.__e=event"/>`);
|
|
}
|
|
$$renderer2.push(`<!--]-->`);
|
|
bind_props($$props, { ref });
|
|
});
|
|
}
|
|
function Avatar_fallback$1($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
const uid = props_id($$renderer2);
|
|
let {
|
|
children,
|
|
child,
|
|
id = createId(uid),
|
|
ref = null,
|
|
$$slots,
|
|
$$events,
|
|
...restProps
|
|
} = $$props;
|
|
const fallbackState = AvatarFallbackState.create({
|
|
id: boxWith(() => id),
|
|
ref: boxWith(() => ref, (v) => ref = v)
|
|
});
|
|
const mergedProps = mergeProps(restProps, fallbackState.props);
|
|
if (child) {
|
|
$$renderer2.push("<!--[-->");
|
|
child($$renderer2, { props: mergedProps });
|
|
$$renderer2.push(`<!---->`);
|
|
} else {
|
|
$$renderer2.push("<!--[!-->");
|
|
$$renderer2.push(`<span${attributes({ ...mergedProps })}>`);
|
|
children?.($$renderer2);
|
|
$$renderer2.push(`<!----></span>`);
|
|
}
|
|
$$renderer2.push(`<!--]-->`);
|
|
bind_props($$props, { ref });
|
|
});
|
|
}
|
|
function Hidden_input($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let { value = void 0, $$slots, $$events, ...restProps } = $$props;
|
|
const mergedProps = mergeProps(restProps, {
|
|
"aria-hidden": "true",
|
|
tabindex: -1,
|
|
style: srOnlyStylesString
|
|
});
|
|
if (mergedProps.type === "checkbox") {
|
|
$$renderer2.push("<!--[-->");
|
|
$$renderer2.push(`<input${attributes({ ...mergedProps, value }, void 0, void 0, void 0, 4)}/>`);
|
|
} else {
|
|
$$renderer2.push("<!--[!-->");
|
|
$$renderer2.push(`<input${attributes({ value, ...mergedProps }, void 0, void 0, void 0, 4)}/>`);
|
|
}
|
|
$$renderer2.push(`<!--]-->`);
|
|
bind_props($$props, { value });
|
|
});
|
|
}
|
|
function get(valueOrGetValue) {
|
|
return typeof valueOrGetValue === "function" ? valueOrGetValue() : valueOrGetValue;
|
|
}
|
|
function getDPR(element) {
|
|
if (typeof window === "undefined") return 1;
|
|
const win = element.ownerDocument.defaultView || window;
|
|
return win.devicePixelRatio || 1;
|
|
}
|
|
function roundByDPR(element, value) {
|
|
const dpr = getDPR(element);
|
|
return Math.round(value * dpr) / dpr;
|
|
}
|
|
function getFloatingContentCSSVars(name) {
|
|
return {
|
|
[`--bits-${name}-content-transform-origin`]: `var(--bits-floating-transform-origin)`,
|
|
[`--bits-${name}-content-available-width`]: `var(--bits-floating-available-width)`,
|
|
[`--bits-${name}-content-available-height`]: `var(--bits-floating-available-height)`,
|
|
[`--bits-${name}-anchor-width`]: `var(--bits-floating-anchor-width)`,
|
|
[`--bits-${name}-anchor-height`]: `var(--bits-floating-anchor-height)`
|
|
};
|
|
}
|
|
function useFloating(options) {
|
|
const openOption = get(options.open) ?? true;
|
|
const middlewareOption = get(options.middleware);
|
|
const transformOption = get(options.transform) ?? true;
|
|
const placementOption = get(options.placement) ?? "bottom";
|
|
const strategyOption = get(options.strategy) ?? "absolute";
|
|
const sideOffsetOption = get(options.sideOffset) ?? 0;
|
|
const alignOffsetOption = get(options.alignOffset) ?? 0;
|
|
const reference = options.reference;
|
|
let x = 0;
|
|
let y = 0;
|
|
const floating = simpleBox(null);
|
|
let strategy = strategyOption;
|
|
let placement = placementOption;
|
|
let middlewareData = {};
|
|
let isPositioned = false;
|
|
const floatingStyles = (() => {
|
|
const xVal = floating.current ? roundByDPR(floating.current, x) : x;
|
|
const yVal = floating.current ? roundByDPR(floating.current, y) : y;
|
|
if (transformOption) {
|
|
return {
|
|
position: strategy,
|
|
left: "0",
|
|
top: "0",
|
|
transform: `translate(${xVal}px, ${yVal}px)`,
|
|
...floating.current && getDPR(floating.current) >= 1.5 && { willChange: "transform" }
|
|
};
|
|
}
|
|
return { position: strategy, left: `${xVal}px`, top: `${yVal}px` };
|
|
})();
|
|
function update() {
|
|
if (reference.current === null || floating.current === null) return;
|
|
computePosition(reference.current, floating.current, {
|
|
middleware: middlewareOption,
|
|
placement: placementOption,
|
|
strategy: strategyOption
|
|
}).then((position) => {
|
|
if (!openOption && x !== 0 && y !== 0) {
|
|
const maxExpectedOffset = Math.max(Math.abs(sideOffsetOption), Math.abs(alignOffsetOption), 15);
|
|
if (position.x <= maxExpectedOffset && position.y <= maxExpectedOffset) return;
|
|
}
|
|
x = position.x;
|
|
y = position.y;
|
|
strategy = position.strategy;
|
|
placement = position.placement;
|
|
middlewareData = position.middlewareData;
|
|
isPositioned = true;
|
|
});
|
|
}
|
|
return {
|
|
floating,
|
|
reference,
|
|
get strategy() {
|
|
return strategy;
|
|
},
|
|
get placement() {
|
|
return placement;
|
|
},
|
|
get middlewareData() {
|
|
return middlewareData;
|
|
},
|
|
get isPositioned() {
|
|
return isPositioned;
|
|
},
|
|
get floatingStyles() {
|
|
return floatingStyles;
|
|
},
|
|
get update() {
|
|
return update;
|
|
}
|
|
};
|
|
}
|
|
const OPPOSITE_SIDE = { top: "bottom", right: "left", bottom: "top", left: "right" };
|
|
const FloatingRootContext = new Context("Floating.Root");
|
|
const FloatingContentContext = new Context("Floating.Content");
|
|
const FloatingTooltipRootContext = new Context("Floating.Root");
|
|
class FloatingRootState {
|
|
static create(tooltip = false) {
|
|
return tooltip ? FloatingTooltipRootContext.set(new FloatingRootState()) : FloatingRootContext.set(new FloatingRootState());
|
|
}
|
|
anchorNode = simpleBox(null);
|
|
customAnchorNode = simpleBox(null);
|
|
triggerNode = simpleBox(null);
|
|
constructor() {
|
|
}
|
|
}
|
|
class FloatingContentState {
|
|
static create(opts, tooltip = false) {
|
|
return tooltip ? FloatingContentContext.set(new FloatingContentState(opts, FloatingTooltipRootContext.get())) : FloatingContentContext.set(new FloatingContentState(opts, FloatingRootContext.get()));
|
|
}
|
|
opts;
|
|
root;
|
|
// nodes
|
|
contentRef = simpleBox(null);
|
|
wrapperRef = simpleBox(null);
|
|
arrowRef = simpleBox(null);
|
|
contentAttachment = attachRef(this.contentRef);
|
|
wrapperAttachment = attachRef(this.wrapperRef);
|
|
arrowAttachment = attachRef(this.arrowRef);
|
|
// ids
|
|
arrowId = simpleBox(useId());
|
|
#transformedStyle = derived(() => {
|
|
if (typeof this.opts.style === "string") return cssToStyleObj(this.opts.style);
|
|
if (!this.opts.style) return {};
|
|
});
|
|
#updatePositionStrategy = void 0;
|
|
#arrowSize = new ElementSize(() => this.arrowRef.current ?? void 0);
|
|
#arrowWidth = derived(() => this.#arrowSize?.width ?? 0);
|
|
#arrowHeight = derived(() => this.#arrowSize?.height ?? 0);
|
|
#desiredPlacement = derived(() => this.opts.side?.current + (this.opts.align.current !== "center" ? `-${this.opts.align.current}` : ""));
|
|
#boundary = derived(() => Array.isArray(this.opts.collisionBoundary.current) ? this.opts.collisionBoundary.current : [this.opts.collisionBoundary.current]);
|
|
#hasExplicitBoundaries = derived(() => this.#boundary().length > 0);
|
|
get hasExplicitBoundaries() {
|
|
return this.#hasExplicitBoundaries();
|
|
}
|
|
set hasExplicitBoundaries($$value) {
|
|
return this.#hasExplicitBoundaries($$value);
|
|
}
|
|
#detectOverflowOptions = derived(() => ({
|
|
padding: this.opts.collisionPadding.current,
|
|
boundary: this.#boundary().filter(isNotNull),
|
|
altBoundary: this.hasExplicitBoundaries
|
|
}));
|
|
get detectOverflowOptions() {
|
|
return this.#detectOverflowOptions();
|
|
}
|
|
set detectOverflowOptions($$value) {
|
|
return this.#detectOverflowOptions($$value);
|
|
}
|
|
#availableWidth = void 0;
|
|
#availableHeight = void 0;
|
|
#anchorWidth = void 0;
|
|
#anchorHeight = void 0;
|
|
#middleware = derived(() => [
|
|
offset({
|
|
mainAxis: this.opts.sideOffset.current + this.#arrowHeight(),
|
|
alignmentAxis: this.opts.alignOffset.current
|
|
}),
|
|
this.opts.avoidCollisions.current && shift({
|
|
mainAxis: true,
|
|
crossAxis: false,
|
|
limiter: this.opts.sticky.current === "partial" ? limitShift() : void 0,
|
|
...this.detectOverflowOptions
|
|
}),
|
|
this.opts.avoidCollisions.current && flip({ ...this.detectOverflowOptions }),
|
|
size({
|
|
...this.detectOverflowOptions,
|
|
apply: ({ rects, availableWidth, availableHeight }) => {
|
|
const { width: anchorWidth, height: anchorHeight } = rects.reference;
|
|
this.#availableWidth = availableWidth;
|
|
this.#availableHeight = availableHeight;
|
|
this.#anchorWidth = anchorWidth;
|
|
this.#anchorHeight = anchorHeight;
|
|
}
|
|
}),
|
|
this.arrowRef.current && arrow({
|
|
element: this.arrowRef.current,
|
|
padding: this.opts.arrowPadding.current
|
|
}),
|
|
transformOrigin({
|
|
arrowWidth: this.#arrowWidth(),
|
|
arrowHeight: this.#arrowHeight()
|
|
}),
|
|
this.opts.hideWhenDetached.current && hide({ strategy: "referenceHidden", ...this.detectOverflowOptions })
|
|
].filter(Boolean));
|
|
get middleware() {
|
|
return this.#middleware();
|
|
}
|
|
set middleware($$value) {
|
|
return this.#middleware($$value);
|
|
}
|
|
floating;
|
|
#placedSide = derived(() => getSideFromPlacement(this.floating.placement));
|
|
get placedSide() {
|
|
return this.#placedSide();
|
|
}
|
|
set placedSide($$value) {
|
|
return this.#placedSide($$value);
|
|
}
|
|
#placedAlign = derived(() => getAlignFromPlacement(this.floating.placement));
|
|
get placedAlign() {
|
|
return this.#placedAlign();
|
|
}
|
|
set placedAlign($$value) {
|
|
return this.#placedAlign($$value);
|
|
}
|
|
#arrowX = derived(() => this.floating.middlewareData.arrow?.x ?? 0);
|
|
get arrowX() {
|
|
return this.#arrowX();
|
|
}
|
|
set arrowX($$value) {
|
|
return this.#arrowX($$value);
|
|
}
|
|
#arrowY = derived(() => this.floating.middlewareData.arrow?.y ?? 0);
|
|
get arrowY() {
|
|
return this.#arrowY();
|
|
}
|
|
set arrowY($$value) {
|
|
return this.#arrowY($$value);
|
|
}
|
|
#cannotCenterArrow = derived(() => this.floating.middlewareData.arrow?.centerOffset !== 0);
|
|
get cannotCenterArrow() {
|
|
return this.#cannotCenterArrow();
|
|
}
|
|
set cannotCenterArrow($$value) {
|
|
return this.#cannotCenterArrow($$value);
|
|
}
|
|
contentZIndex;
|
|
#arrowBaseSide = derived(() => OPPOSITE_SIDE[this.placedSide]);
|
|
get arrowBaseSide() {
|
|
return this.#arrowBaseSide();
|
|
}
|
|
set arrowBaseSide($$value) {
|
|
return this.#arrowBaseSide($$value);
|
|
}
|
|
#wrapperProps = derived(() => ({
|
|
id: this.opts.wrapperId.current,
|
|
"data-bits-floating-content-wrapper": "",
|
|
style: {
|
|
...this.floating.floatingStyles,
|
|
transform: this.floating.isPositioned ? this.floating.floatingStyles.transform : "translate(0, -200%)",
|
|
minWidth: "max-content",
|
|
zIndex: this.contentZIndex,
|
|
"--bits-floating-transform-origin": `${this.floating.middlewareData.transformOrigin?.x} ${this.floating.middlewareData.transformOrigin?.y}`,
|
|
"--bits-floating-available-width": `${this.#availableWidth}px`,
|
|
"--bits-floating-available-height": `${this.#availableHeight}px`,
|
|
"--bits-floating-anchor-width": `${this.#anchorWidth}px`,
|
|
"--bits-floating-anchor-height": `${this.#anchorHeight}px`,
|
|
...this.floating.middlewareData.hide?.referenceHidden && { visibility: "hidden", "pointer-events": "none" },
|
|
...this.#transformedStyle()
|
|
},
|
|
dir: this.opts.dir.current,
|
|
...this.wrapperAttachment
|
|
}));
|
|
get wrapperProps() {
|
|
return this.#wrapperProps();
|
|
}
|
|
set wrapperProps($$value) {
|
|
return this.#wrapperProps($$value);
|
|
}
|
|
#props = derived(() => ({
|
|
"data-side": this.placedSide,
|
|
"data-align": this.placedAlign,
|
|
style: styleToString({ ...this.#transformedStyle() }),
|
|
...this.contentAttachment
|
|
}));
|
|
get props() {
|
|
return this.#props();
|
|
}
|
|
set props($$value) {
|
|
return this.#props($$value);
|
|
}
|
|
#arrowStyle = derived(() => ({
|
|
position: "absolute",
|
|
left: this.arrowX ? `${this.arrowX}px` : void 0,
|
|
top: this.arrowY ? `${this.arrowY}px` : void 0,
|
|
[this.arrowBaseSide]: 0,
|
|
"transform-origin": { top: "", right: "0 0", bottom: "center 0", left: "100% 0" }[this.placedSide],
|
|
transform: {
|
|
top: "translateY(100%)",
|
|
right: "translateY(50%) rotate(90deg) translateX(-50%)",
|
|
bottom: "rotate(180deg)",
|
|
left: "translateY(50%) rotate(-90deg) translateX(50%)"
|
|
}[this.placedSide],
|
|
visibility: this.cannotCenterArrow ? "hidden" : void 0
|
|
}));
|
|
get arrowStyle() {
|
|
return this.#arrowStyle();
|
|
}
|
|
set arrowStyle($$value) {
|
|
return this.#arrowStyle($$value);
|
|
}
|
|
constructor(opts, root) {
|
|
this.opts = opts;
|
|
this.root = root;
|
|
if (opts.customAnchor) {
|
|
this.root.customAnchorNode.current = opts.customAnchor.current;
|
|
}
|
|
watch(() => opts.customAnchor.current, (customAnchor) => {
|
|
this.root.customAnchorNode.current = customAnchor;
|
|
});
|
|
this.floating = useFloating({
|
|
strategy: () => this.opts.strategy.current,
|
|
placement: () => this.#desiredPlacement(),
|
|
middleware: () => this.middleware,
|
|
reference: this.root.anchorNode,
|
|
open: () => this.opts.enabled.current,
|
|
sideOffset: () => this.opts.sideOffset.current,
|
|
alignOffset: () => this.opts.alignOffset.current
|
|
});
|
|
watch(() => this.contentRef.current, (contentNode) => {
|
|
if (!contentNode) return;
|
|
const win = getWindow(contentNode);
|
|
this.contentZIndex = win.getComputedStyle(contentNode).zIndex;
|
|
});
|
|
}
|
|
}
|
|
class FloatingAnchorState {
|
|
static create(opts, tooltip = false) {
|
|
return tooltip ? new FloatingAnchorState(opts, FloatingTooltipRootContext.get()) : new FloatingAnchorState(opts, FloatingRootContext.get());
|
|
}
|
|
opts;
|
|
root;
|
|
constructor(opts, root) {
|
|
this.opts = opts;
|
|
this.root = root;
|
|
if (opts.virtualEl && opts.virtualEl.current) {
|
|
root.triggerNode = boxFrom(opts.virtualEl.current);
|
|
} else {
|
|
root.triggerNode = opts.ref;
|
|
}
|
|
}
|
|
}
|
|
function transformOrigin(options) {
|
|
return {
|
|
name: "transformOrigin",
|
|
options,
|
|
fn(data) {
|
|
const { placement, rects, middlewareData } = data;
|
|
const cannotCenterArrow = middlewareData.arrow?.centerOffset !== 0;
|
|
const isArrowHidden = cannotCenterArrow;
|
|
const arrowWidth = isArrowHidden ? 0 : options.arrowWidth;
|
|
const arrowHeight = isArrowHidden ? 0 : options.arrowHeight;
|
|
const [placedSide, placedAlign] = getSideAndAlignFromPlacement(placement);
|
|
const noArrowAlign = { start: "0%", center: "50%", end: "100%" }[placedAlign];
|
|
const arrowXCenter = (middlewareData.arrow?.x ?? 0) + arrowWidth / 2;
|
|
const arrowYCenter = (middlewareData.arrow?.y ?? 0) + arrowHeight / 2;
|
|
let x = "";
|
|
let y = "";
|
|
if (placedSide === "bottom") {
|
|
x = isArrowHidden ? noArrowAlign : `${arrowXCenter}px`;
|
|
y = `${-arrowHeight}px`;
|
|
} else if (placedSide === "top") {
|
|
x = isArrowHidden ? noArrowAlign : `${arrowXCenter}px`;
|
|
y = `${rects.floating.height + arrowHeight}px`;
|
|
} else if (placedSide === "right") {
|
|
x = `${-arrowHeight}px`;
|
|
y = isArrowHidden ? noArrowAlign : `${arrowYCenter}px`;
|
|
} else if (placedSide === "left") {
|
|
x = `${rects.floating.width + arrowHeight}px`;
|
|
y = isArrowHidden ? noArrowAlign : `${arrowYCenter}px`;
|
|
}
|
|
return { data: { x, y } };
|
|
}
|
|
};
|
|
}
|
|
function getSideAndAlignFromPlacement(placement) {
|
|
const [side, align = "center"] = placement.split("-");
|
|
return [side, align];
|
|
}
|
|
function getSideFromPlacement(placement) {
|
|
return getSideAndAlignFromPlacement(placement)[0];
|
|
}
|
|
function getAlignFromPlacement(placement) {
|
|
return getSideAndAlignFromPlacement(placement)[1];
|
|
}
|
|
function Floating_layer($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let { children, tooltip = false } = $$props;
|
|
FloatingRootState.create(tooltip);
|
|
children?.($$renderer2);
|
|
$$renderer2.push(`<!---->`);
|
|
});
|
|
}
|
|
function Floating_layer_anchor($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let { id, children, virtualEl, ref, tooltip = false } = $$props;
|
|
FloatingAnchorState.create(
|
|
{
|
|
id: boxWith(() => id),
|
|
virtualEl: boxWith(() => virtualEl),
|
|
ref
|
|
},
|
|
tooltip
|
|
);
|
|
children?.($$renderer2);
|
|
$$renderer2.push(`<!---->`);
|
|
});
|
|
}
|
|
function Floating_layer_content($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let {
|
|
content,
|
|
side = "bottom",
|
|
sideOffset = 0,
|
|
align = "center",
|
|
alignOffset = 0,
|
|
id,
|
|
arrowPadding = 0,
|
|
avoidCollisions = true,
|
|
collisionBoundary = [],
|
|
collisionPadding = 0,
|
|
hideWhenDetached = false,
|
|
onPlaced = () => {
|
|
},
|
|
sticky = "partial",
|
|
updatePositionStrategy = "optimized",
|
|
strategy = "fixed",
|
|
dir = "ltr",
|
|
style = {},
|
|
wrapperId = useId(),
|
|
customAnchor = null,
|
|
enabled,
|
|
tooltip = false
|
|
} = $$props;
|
|
const contentState = FloatingContentState.create(
|
|
{
|
|
side: boxWith(() => side),
|
|
sideOffset: boxWith(() => sideOffset),
|
|
align: boxWith(() => align),
|
|
alignOffset: boxWith(() => alignOffset),
|
|
id: boxWith(() => id),
|
|
arrowPadding: boxWith(() => arrowPadding),
|
|
avoidCollisions: boxWith(() => avoidCollisions),
|
|
collisionBoundary: boxWith(() => collisionBoundary),
|
|
collisionPadding: boxWith(() => collisionPadding),
|
|
hideWhenDetached: boxWith(() => hideWhenDetached),
|
|
onPlaced: boxWith(() => onPlaced),
|
|
sticky: boxWith(() => sticky),
|
|
updatePositionStrategy: boxWith(() => updatePositionStrategy),
|
|
strategy: boxWith(() => strategy),
|
|
dir: boxWith(() => dir),
|
|
style: boxWith(() => style),
|
|
enabled: boxWith(() => enabled),
|
|
wrapperId: boxWith(() => wrapperId),
|
|
customAnchor: boxWith(() => customAnchor)
|
|
},
|
|
tooltip
|
|
);
|
|
const mergedProps = mergeProps(contentState.wrapperProps, { style: { pointerEvents: "auto" } });
|
|
content?.($$renderer2, { props: contentState.props, wrapperProps: mergedProps });
|
|
$$renderer2.push(`<!---->`);
|
|
});
|
|
}
|
|
function Floating_layer_content_static($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let { content } = $$props;
|
|
content?.($$renderer2, { props: {}, wrapperProps: {} });
|
|
$$renderer2.push(`<!---->`);
|
|
});
|
|
}
|
|
function Popper_content($$renderer, $$props) {
|
|
let {
|
|
content,
|
|
isStatic = false,
|
|
onPlaced,
|
|
$$slots,
|
|
$$events,
|
|
...restProps
|
|
} = $$props;
|
|
if (isStatic) {
|
|
$$renderer.push("<!--[-->");
|
|
Floating_layer_content_static($$renderer, { content });
|
|
} else {
|
|
$$renderer.push("<!--[!-->");
|
|
Floating_layer_content($$renderer, spread_props([{ content, onPlaced }, restProps]));
|
|
}
|
|
$$renderer.push(`<!--]-->`);
|
|
}
|
|
function Popper_layer_inner($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let {
|
|
popper,
|
|
onEscapeKeydown,
|
|
escapeKeydownBehavior,
|
|
preventOverflowTextSelection,
|
|
id,
|
|
onPointerDown,
|
|
onPointerUp,
|
|
side,
|
|
sideOffset,
|
|
align,
|
|
alignOffset,
|
|
arrowPadding,
|
|
avoidCollisions,
|
|
collisionBoundary,
|
|
collisionPadding,
|
|
sticky,
|
|
hideWhenDetached,
|
|
updatePositionStrategy,
|
|
strategy,
|
|
dir,
|
|
preventScroll,
|
|
wrapperId,
|
|
style,
|
|
onPlaced,
|
|
onInteractOutside,
|
|
onCloseAutoFocus,
|
|
onOpenAutoFocus,
|
|
onFocusOutside,
|
|
interactOutsideBehavior = "close",
|
|
loop,
|
|
trapFocus = true,
|
|
isValidEvent: isValidEvent2 = () => false,
|
|
customAnchor = null,
|
|
isStatic = false,
|
|
enabled,
|
|
ref,
|
|
tooltip = false,
|
|
contentPointerEvents = "auto",
|
|
$$slots,
|
|
$$events,
|
|
...restProps
|
|
} = $$props;
|
|
{
|
|
let content = function($$renderer3, { props: floatingProps, wrapperProps }) {
|
|
if (restProps.forceMount && enabled) {
|
|
$$renderer3.push("<!--[-->");
|
|
Scroll_lock($$renderer3, { preventScroll });
|
|
} else if (!restProps.forceMount) {
|
|
$$renderer3.push("<!--[1-->");
|
|
Scroll_lock($$renderer3, { preventScroll });
|
|
} else {
|
|
$$renderer3.push("<!--[!-->");
|
|
}
|
|
$$renderer3.push(`<!--]--> `);
|
|
{
|
|
let focusScope = function($$renderer4, { props: focusScopeProps }) {
|
|
Escape_layer($$renderer4, {
|
|
onEscapeKeydown,
|
|
escapeKeydownBehavior,
|
|
enabled,
|
|
ref,
|
|
children: ($$renderer5) => {
|
|
{
|
|
let children = function($$renderer6, { props: dismissibleProps }) {
|
|
Text_selection_layer($$renderer6, {
|
|
id,
|
|
preventOverflowTextSelection,
|
|
onPointerDown,
|
|
onPointerUp,
|
|
enabled,
|
|
ref,
|
|
children: ($$renderer7) => {
|
|
popper?.($$renderer7, {
|
|
props: mergeProps(restProps, floatingProps, dismissibleProps, focusScopeProps, { style: { pointerEvents: contentPointerEvents } }),
|
|
wrapperProps
|
|
});
|
|
$$renderer7.push(`<!---->`);
|
|
}
|
|
});
|
|
};
|
|
Dismissible_layer($$renderer5, {
|
|
id,
|
|
onInteractOutside,
|
|
onFocusOutside,
|
|
interactOutsideBehavior,
|
|
isValidEvent: isValidEvent2,
|
|
enabled,
|
|
ref,
|
|
children
|
|
});
|
|
}
|
|
}
|
|
});
|
|
};
|
|
Focus_scope($$renderer3, {
|
|
onOpenAutoFocus,
|
|
onCloseAutoFocus,
|
|
loop,
|
|
enabled,
|
|
trapFocus,
|
|
forceMount: restProps.forceMount,
|
|
ref,
|
|
focusScope
|
|
});
|
|
}
|
|
$$renderer3.push(`<!---->`);
|
|
};
|
|
Popper_content($$renderer2, {
|
|
isStatic,
|
|
id,
|
|
side,
|
|
sideOffset,
|
|
align,
|
|
alignOffset,
|
|
arrowPadding,
|
|
avoidCollisions,
|
|
collisionBoundary,
|
|
collisionPadding,
|
|
sticky,
|
|
hideWhenDetached,
|
|
updatePositionStrategy,
|
|
strategy,
|
|
dir,
|
|
wrapperId,
|
|
style,
|
|
onPlaced,
|
|
customAnchor,
|
|
enabled,
|
|
tooltip,
|
|
content,
|
|
$$slots: { content: true }
|
|
});
|
|
}
|
|
});
|
|
}
|
|
function Popper_layer($$renderer, $$props) {
|
|
let {
|
|
popper,
|
|
open,
|
|
onEscapeKeydown,
|
|
escapeKeydownBehavior,
|
|
preventOverflowTextSelection,
|
|
id,
|
|
onPointerDown,
|
|
onPointerUp,
|
|
side,
|
|
sideOffset,
|
|
align,
|
|
alignOffset,
|
|
arrowPadding,
|
|
avoidCollisions,
|
|
collisionBoundary,
|
|
collisionPadding,
|
|
sticky,
|
|
hideWhenDetached,
|
|
updatePositionStrategy,
|
|
strategy,
|
|
dir,
|
|
preventScroll,
|
|
wrapperId,
|
|
style,
|
|
onPlaced,
|
|
onInteractOutside,
|
|
onCloseAutoFocus,
|
|
onOpenAutoFocus,
|
|
onFocusOutside,
|
|
interactOutsideBehavior = "close",
|
|
loop,
|
|
trapFocus = true,
|
|
isValidEvent: isValidEvent2 = () => false,
|
|
customAnchor = null,
|
|
isStatic = false,
|
|
ref,
|
|
shouldRender,
|
|
$$slots,
|
|
$$events,
|
|
...restProps
|
|
} = $$props;
|
|
if (shouldRender) {
|
|
$$renderer.push("<!--[-->");
|
|
Popper_layer_inner($$renderer, spread_props([
|
|
{
|
|
popper,
|
|
onEscapeKeydown,
|
|
escapeKeydownBehavior,
|
|
preventOverflowTextSelection,
|
|
id,
|
|
onPointerDown,
|
|
onPointerUp,
|
|
side,
|
|
sideOffset,
|
|
align,
|
|
alignOffset,
|
|
arrowPadding,
|
|
avoidCollisions,
|
|
collisionBoundary,
|
|
collisionPadding,
|
|
sticky,
|
|
hideWhenDetached,
|
|
updatePositionStrategy,
|
|
strategy,
|
|
dir,
|
|
preventScroll,
|
|
wrapperId,
|
|
style,
|
|
onPlaced,
|
|
customAnchor,
|
|
isStatic,
|
|
enabled: open,
|
|
onInteractOutside,
|
|
onCloseAutoFocus,
|
|
onOpenAutoFocus,
|
|
interactOutsideBehavior,
|
|
loop,
|
|
trapFocus,
|
|
isValidEvent: isValidEvent2,
|
|
onFocusOutside,
|
|
forceMount: false,
|
|
ref
|
|
},
|
|
restProps
|
|
]));
|
|
} else {
|
|
$$renderer.push("<!--[!-->");
|
|
}
|
|
$$renderer.push(`<!--]-->`);
|
|
}
|
|
function Popper_layer_force_mount($$renderer, $$props) {
|
|
let {
|
|
popper,
|
|
onEscapeKeydown,
|
|
escapeKeydownBehavior,
|
|
preventOverflowTextSelection,
|
|
id,
|
|
onPointerDown,
|
|
onPointerUp,
|
|
side,
|
|
sideOffset,
|
|
align,
|
|
alignOffset,
|
|
arrowPadding,
|
|
avoidCollisions,
|
|
collisionBoundary,
|
|
collisionPadding,
|
|
sticky,
|
|
hideWhenDetached,
|
|
updatePositionStrategy,
|
|
strategy,
|
|
dir,
|
|
preventScroll,
|
|
wrapperId,
|
|
style,
|
|
onPlaced,
|
|
onInteractOutside,
|
|
onCloseAutoFocus,
|
|
onOpenAutoFocus,
|
|
onFocusOutside,
|
|
interactOutsideBehavior = "close",
|
|
loop,
|
|
trapFocus = true,
|
|
isValidEvent: isValidEvent2 = () => false,
|
|
customAnchor = null,
|
|
isStatic = false,
|
|
enabled,
|
|
$$slots,
|
|
$$events,
|
|
...restProps
|
|
} = $$props;
|
|
Popper_layer_inner($$renderer, spread_props([
|
|
{
|
|
popper,
|
|
onEscapeKeydown,
|
|
escapeKeydownBehavior,
|
|
preventOverflowTextSelection,
|
|
id,
|
|
onPointerDown,
|
|
onPointerUp,
|
|
side,
|
|
sideOffset,
|
|
align,
|
|
alignOffset,
|
|
arrowPadding,
|
|
avoidCollisions,
|
|
collisionBoundary,
|
|
collisionPadding,
|
|
sticky,
|
|
hideWhenDetached,
|
|
updatePositionStrategy,
|
|
strategy,
|
|
dir,
|
|
preventScroll,
|
|
wrapperId,
|
|
style,
|
|
onPlaced,
|
|
customAnchor,
|
|
isStatic,
|
|
enabled,
|
|
onInteractOutside,
|
|
onCloseAutoFocus,
|
|
onOpenAutoFocus,
|
|
interactOutsideBehavior,
|
|
loop,
|
|
trapFocus,
|
|
isValidEvent: isValidEvent2,
|
|
onFocusOutside
|
|
},
|
|
restProps,
|
|
{ forceMount: true }
|
|
]));
|
|
}
|
|
function Menu_item($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
const uid = props_id($$renderer2);
|
|
let {
|
|
child,
|
|
children,
|
|
ref = null,
|
|
id = createId(uid),
|
|
disabled = false,
|
|
onSelect = noop,
|
|
closeOnSelect = true,
|
|
$$slots,
|
|
$$events,
|
|
...restProps
|
|
} = $$props;
|
|
const itemState = MenuItemState.create({
|
|
id: boxWith(() => id),
|
|
disabled: boxWith(() => disabled),
|
|
onSelect: boxWith(() => onSelect),
|
|
ref: boxWith(() => ref, (v) => ref = v),
|
|
closeOnSelect: boxWith(() => closeOnSelect)
|
|
});
|
|
const mergedProps = mergeProps(restProps, itemState.props);
|
|
if (child) {
|
|
$$renderer2.push("<!--[-->");
|
|
child($$renderer2, { props: mergedProps });
|
|
$$renderer2.push(`<!---->`);
|
|
} else {
|
|
$$renderer2.push("<!--[!-->");
|
|
$$renderer2.push(`<div${attributes({ ...mergedProps })}>`);
|
|
children?.($$renderer2);
|
|
$$renderer2.push(`<!----></div>`);
|
|
}
|
|
$$renderer2.push(`<!--]-->`);
|
|
bind_props($$props, { ref });
|
|
});
|
|
}
|
|
function Menu_separator($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
const uid = props_id($$renderer2);
|
|
let {
|
|
ref = null,
|
|
id = createId(uid),
|
|
child,
|
|
children,
|
|
$$slots,
|
|
$$events,
|
|
...restProps
|
|
} = $$props;
|
|
const separatorState = MenuSeparatorState.create({
|
|
id: boxWith(() => id),
|
|
ref: boxWith(() => ref, (v) => ref = v)
|
|
});
|
|
const mergedProps = mergeProps(restProps, separatorState.props);
|
|
if (child) {
|
|
$$renderer2.push("<!--[-->");
|
|
child($$renderer2, { props: mergedProps });
|
|
$$renderer2.push(`<!---->`);
|
|
} else {
|
|
$$renderer2.push("<!--[!-->");
|
|
$$renderer2.push(`<div${attributes({ ...mergedProps })}>`);
|
|
children?.($$renderer2);
|
|
$$renderer2.push(`<!----></div>`);
|
|
}
|
|
$$renderer2.push(`<!--]-->`);
|
|
bind_props($$props, { ref });
|
|
});
|
|
}
|
|
function Menu($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let {
|
|
open = false,
|
|
dir = "ltr",
|
|
onOpenChange = noop,
|
|
onOpenChangeComplete = noop,
|
|
_internal_variant: variant = "dropdown-menu",
|
|
children
|
|
} = $$props;
|
|
const root = MenuRootState.create({
|
|
variant: boxWith(() => variant),
|
|
dir: boxWith(() => dir),
|
|
onClose: () => {
|
|
open = false;
|
|
onOpenChange(false);
|
|
}
|
|
});
|
|
MenuMenuState.create(
|
|
{
|
|
open: boxWith(() => open, (v) => {
|
|
open = v;
|
|
onOpenChange(v);
|
|
}),
|
|
onOpenChangeComplete: boxWith(() => onOpenChangeComplete)
|
|
},
|
|
root
|
|
);
|
|
Floating_layer($$renderer2, {
|
|
children: ($$renderer3) => {
|
|
children?.($$renderer3);
|
|
$$renderer3.push(`<!---->`);
|
|
}
|
|
});
|
|
bind_props($$props, { open });
|
|
});
|
|
}
|
|
function Dropdown_menu_content$1($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
const uid = props_id($$renderer2);
|
|
let {
|
|
id = createId(uid),
|
|
child,
|
|
children,
|
|
ref = null,
|
|
loop = true,
|
|
onInteractOutside = noop,
|
|
onEscapeKeydown = noop,
|
|
onCloseAutoFocus = noop,
|
|
forceMount = false,
|
|
trapFocus = false,
|
|
style,
|
|
$$slots,
|
|
$$events,
|
|
...restProps
|
|
} = $$props;
|
|
const contentState = MenuContentState.create({
|
|
id: boxWith(() => id),
|
|
loop: boxWith(() => loop),
|
|
ref: boxWith(() => ref, (v) => ref = v),
|
|
onCloseAutoFocus: boxWith(() => onCloseAutoFocus)
|
|
});
|
|
const mergedProps = mergeProps(restProps, contentState.props);
|
|
function handleInteractOutside(e) {
|
|
contentState.handleInteractOutside(e);
|
|
if (e.defaultPrevented) return;
|
|
onInteractOutside(e);
|
|
if (e.defaultPrevented) return;
|
|
if (e.target && e.target instanceof Element) {
|
|
const subContentSelector = `[${contentState.parentMenu.root.getBitsAttr("sub-content")}]`;
|
|
if (e.target.closest(subContentSelector)) return;
|
|
}
|
|
contentState.parentMenu.onClose();
|
|
}
|
|
function handleEscapeKeydown(e) {
|
|
onEscapeKeydown(e);
|
|
if (e.defaultPrevented) return;
|
|
contentState.parentMenu.onClose();
|
|
}
|
|
if (forceMount) {
|
|
$$renderer2.push("<!--[-->");
|
|
{
|
|
let popper = function($$renderer3, { props, wrapperProps }) {
|
|
const finalProps = mergeProps(props, { style: getFloatingContentCSSVars("dropdown-menu") }, { style });
|
|
if (child) {
|
|
$$renderer3.push("<!--[-->");
|
|
child($$renderer3, {
|
|
props: finalProps,
|
|
wrapperProps,
|
|
...contentState.snippetProps
|
|
});
|
|
$$renderer3.push(`<!---->`);
|
|
} else {
|
|
$$renderer3.push("<!--[!-->");
|
|
$$renderer3.push(`<div${attributes({ ...wrapperProps })}><div${attributes({ ...finalProps })}>`);
|
|
children?.($$renderer3);
|
|
$$renderer3.push(`<!----></div></div>`);
|
|
}
|
|
$$renderer3.push(`<!--]-->`);
|
|
};
|
|
Popper_layer_force_mount($$renderer2, spread_props([
|
|
mergedProps,
|
|
contentState.popperProps,
|
|
{
|
|
ref: contentState.opts.ref,
|
|
enabled: contentState.parentMenu.opts.open.current,
|
|
onInteractOutside: handleInteractOutside,
|
|
onEscapeKeydown: handleEscapeKeydown,
|
|
trapFocus,
|
|
loop,
|
|
forceMount: true,
|
|
id,
|
|
shouldRender: contentState.shouldRender,
|
|
popper,
|
|
$$slots: { popper: true }
|
|
}
|
|
]));
|
|
}
|
|
} else if (!forceMount) {
|
|
$$renderer2.push("<!--[1-->");
|
|
{
|
|
let popper = function($$renderer3, { props, wrapperProps }) {
|
|
const finalProps = mergeProps(props, { style: getFloatingContentCSSVars("dropdown-menu") }, { style });
|
|
if (child) {
|
|
$$renderer3.push("<!--[-->");
|
|
child($$renderer3, {
|
|
props: finalProps,
|
|
wrapperProps,
|
|
...contentState.snippetProps
|
|
});
|
|
$$renderer3.push(`<!---->`);
|
|
} else {
|
|
$$renderer3.push("<!--[!-->");
|
|
$$renderer3.push(`<div${attributes({ ...wrapperProps })}><div${attributes({ ...finalProps })}>`);
|
|
children?.($$renderer3);
|
|
$$renderer3.push(`<!----></div></div>`);
|
|
}
|
|
$$renderer3.push(`<!--]-->`);
|
|
};
|
|
Popper_layer($$renderer2, spread_props([
|
|
mergedProps,
|
|
contentState.popperProps,
|
|
{
|
|
ref: contentState.opts.ref,
|
|
open: contentState.parentMenu.opts.open.current,
|
|
onInteractOutside: handleInteractOutside,
|
|
onEscapeKeydown: handleEscapeKeydown,
|
|
trapFocus,
|
|
loop,
|
|
forceMount: false,
|
|
id,
|
|
shouldRender: contentState.shouldRender,
|
|
popper,
|
|
$$slots: { popper: true }
|
|
}
|
|
]));
|
|
}
|
|
} else {
|
|
$$renderer2.push("<!--[!-->");
|
|
}
|
|
$$renderer2.push(`<!--]-->`);
|
|
bind_props($$props, { ref });
|
|
});
|
|
}
|
|
function Menu_trigger($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
const uid = props_id($$renderer2);
|
|
let {
|
|
id = createId(uid),
|
|
ref = null,
|
|
child,
|
|
children,
|
|
disabled = false,
|
|
type = "button",
|
|
$$slots,
|
|
$$events,
|
|
...restProps
|
|
} = $$props;
|
|
const triggerState = DropdownMenuTriggerState.create({
|
|
id: boxWith(() => id),
|
|
disabled: boxWith(() => disabled ?? false),
|
|
ref: boxWith(() => ref, (v) => ref = v)
|
|
});
|
|
const mergedProps = mergeProps(restProps, triggerState.props, { type });
|
|
Floating_layer_anchor($$renderer2, {
|
|
id,
|
|
ref: triggerState.opts.ref,
|
|
children: ($$renderer3) => {
|
|
if (child) {
|
|
$$renderer3.push("<!--[-->");
|
|
child($$renderer3, { props: mergedProps });
|
|
$$renderer3.push(`<!---->`);
|
|
} else {
|
|
$$renderer3.push("<!--[!-->");
|
|
$$renderer3.push(`<button${attributes({ ...mergedProps })}>`);
|
|
children?.($$renderer3);
|
|
$$renderer3.push(`<!----></button>`);
|
|
}
|
|
$$renderer3.push(`<!--]-->`);
|
|
}
|
|
});
|
|
bind_props($$props, { ref });
|
|
});
|
|
}
|
|
const labelAttrs = createBitsAttrs({ component: "label", parts: ["root"] });
|
|
class LabelRootState {
|
|
static create(opts) {
|
|
return new LabelRootState(opts);
|
|
}
|
|
opts;
|
|
attachment;
|
|
constructor(opts) {
|
|
this.opts = opts;
|
|
this.attachment = attachRef(this.opts.ref);
|
|
this.onmousedown = this.onmousedown.bind(this);
|
|
}
|
|
onmousedown(e) {
|
|
if (e.detail > 1) e.preventDefault();
|
|
}
|
|
#props = derived(() => ({
|
|
id: this.opts.id.current,
|
|
[labelAttrs.root]: "",
|
|
onmousedown: this.onmousedown,
|
|
...this.attachment
|
|
}));
|
|
get props() {
|
|
return this.#props();
|
|
}
|
|
set props($$value) {
|
|
return this.#props($$value);
|
|
}
|
|
}
|
|
function Label$1($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
const uid = props_id($$renderer2);
|
|
let {
|
|
children,
|
|
child,
|
|
id = createId(uid),
|
|
ref = null,
|
|
for: forProp,
|
|
$$slots,
|
|
$$events,
|
|
...restProps
|
|
} = $$props;
|
|
const rootState = LabelRootState.create({
|
|
id: boxWith(() => id),
|
|
ref: boxWith(() => ref, (v) => ref = v)
|
|
});
|
|
const mergedProps = mergeProps(restProps, rootState.props, { for: forProp });
|
|
if (child) {
|
|
$$renderer2.push("<!--[-->");
|
|
child($$renderer2, { props: mergedProps });
|
|
$$renderer2.push(`<!---->`);
|
|
} else {
|
|
$$renderer2.push("<!--[!-->");
|
|
$$renderer2.push(`<label${attributes({ ...mergedProps, for: forProp })}>`);
|
|
children?.($$renderer2);
|
|
$$renderer2.push(`<!----></label>`);
|
|
}
|
|
$$renderer2.push(`<!--]-->`);
|
|
bind_props($$props, { ref });
|
|
});
|
|
}
|
|
const switchAttrs = createBitsAttrs({ component: "switch", parts: ["root", "thumb"] });
|
|
const SwitchRootContext = new Context("Switch.Root");
|
|
class SwitchRootState {
|
|
static create(opts) {
|
|
return SwitchRootContext.set(new SwitchRootState(opts));
|
|
}
|
|
opts;
|
|
attachment;
|
|
constructor(opts) {
|
|
this.opts = opts;
|
|
this.attachment = attachRef(opts.ref);
|
|
this.onkeydown = this.onkeydown.bind(this);
|
|
this.onclick = this.onclick.bind(this);
|
|
}
|
|
#toggle() {
|
|
this.opts.checked.current = !this.opts.checked.current;
|
|
}
|
|
onkeydown(e) {
|
|
if (!(e.key === ENTER || e.key === SPACE) || this.opts.disabled.current) return;
|
|
e.preventDefault();
|
|
this.#toggle();
|
|
}
|
|
onclick(_) {
|
|
if (this.opts.disabled.current) return;
|
|
this.#toggle();
|
|
}
|
|
#sharedProps = derived(() => ({
|
|
"data-disabled": boolToEmptyStrOrUndef(this.opts.disabled.current),
|
|
"data-state": getDataChecked(this.opts.checked.current),
|
|
"data-required": boolToEmptyStrOrUndef(this.opts.required.current)
|
|
}));
|
|
get sharedProps() {
|
|
return this.#sharedProps();
|
|
}
|
|
set sharedProps($$value) {
|
|
return this.#sharedProps($$value);
|
|
}
|
|
#snippetProps = derived(() => ({ checked: this.opts.checked.current }));
|
|
get snippetProps() {
|
|
return this.#snippetProps();
|
|
}
|
|
set snippetProps($$value) {
|
|
return this.#snippetProps($$value);
|
|
}
|
|
#props = derived(() => ({
|
|
...this.sharedProps,
|
|
id: this.opts.id.current,
|
|
role: "switch",
|
|
disabled: boolToTrueOrUndef(this.opts.disabled.current),
|
|
"aria-checked": getAriaChecked(this.opts.checked.current),
|
|
"aria-required": boolToStr(this.opts.required.current),
|
|
[switchAttrs.root]: "",
|
|
onclick: this.onclick,
|
|
onkeydown: this.onkeydown,
|
|
...this.attachment
|
|
}));
|
|
get props() {
|
|
return this.#props();
|
|
}
|
|
set props($$value) {
|
|
return this.#props($$value);
|
|
}
|
|
}
|
|
class SwitchInputState {
|
|
static create() {
|
|
return new SwitchInputState(SwitchRootContext.get());
|
|
}
|
|
root;
|
|
#shouldRender = derived(() => this.root.opts.name.current !== void 0);
|
|
get shouldRender() {
|
|
return this.#shouldRender();
|
|
}
|
|
set shouldRender($$value) {
|
|
return this.#shouldRender($$value);
|
|
}
|
|
constructor(root) {
|
|
this.root = root;
|
|
}
|
|
#props = derived(() => ({
|
|
type: "checkbox",
|
|
name: this.root.opts.name.current,
|
|
value: this.root.opts.value.current,
|
|
checked: this.root.opts.checked.current,
|
|
disabled: this.root.opts.disabled.current,
|
|
required: this.root.opts.required.current
|
|
}));
|
|
get props() {
|
|
return this.#props();
|
|
}
|
|
set props($$value) {
|
|
return this.#props($$value);
|
|
}
|
|
}
|
|
class SwitchThumbState {
|
|
static create(opts) {
|
|
return new SwitchThumbState(opts, SwitchRootContext.get());
|
|
}
|
|
opts;
|
|
root;
|
|
attachment;
|
|
constructor(opts, root) {
|
|
this.opts = opts;
|
|
this.root = root;
|
|
this.attachment = attachRef(opts.ref);
|
|
}
|
|
#snippetProps = derived(() => ({ checked: this.root.opts.checked.current }));
|
|
get snippetProps() {
|
|
return this.#snippetProps();
|
|
}
|
|
set snippetProps($$value) {
|
|
return this.#snippetProps($$value);
|
|
}
|
|
#props = derived(() => ({
|
|
...this.root.sharedProps,
|
|
id: this.opts.id.current,
|
|
[switchAttrs.thumb]: "",
|
|
...this.attachment
|
|
}));
|
|
get props() {
|
|
return this.#props();
|
|
}
|
|
set props($$value) {
|
|
return this.#props($$value);
|
|
}
|
|
}
|
|
function Switch_input($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
const inputState = SwitchInputState.create();
|
|
if (inputState.shouldRender) {
|
|
$$renderer2.push("<!--[-->");
|
|
Hidden_input($$renderer2, spread_props([inputState.props]));
|
|
} else {
|
|
$$renderer2.push("<!--[!-->");
|
|
}
|
|
$$renderer2.push(`<!--]-->`);
|
|
});
|
|
}
|
|
function Switch$1($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
const uid = props_id($$renderer2);
|
|
let {
|
|
child,
|
|
children,
|
|
ref = null,
|
|
id = createId(uid),
|
|
disabled = false,
|
|
required = false,
|
|
checked = false,
|
|
value = "on",
|
|
name = void 0,
|
|
type = "button",
|
|
onCheckedChange = noop,
|
|
$$slots,
|
|
$$events,
|
|
...restProps
|
|
} = $$props;
|
|
const rootState = SwitchRootState.create({
|
|
checked: boxWith(() => checked, (v) => {
|
|
checked = v;
|
|
onCheckedChange?.(v);
|
|
}),
|
|
disabled: boxWith(() => disabled ?? false),
|
|
required: boxWith(() => required),
|
|
value: boxWith(() => value),
|
|
name: boxWith(() => name),
|
|
id: boxWith(() => id),
|
|
ref: boxWith(() => ref, (v) => ref = v)
|
|
});
|
|
const mergedProps = mergeProps(restProps, rootState.props, { type });
|
|
if (child) {
|
|
$$renderer2.push("<!--[-->");
|
|
child($$renderer2, { props: mergedProps, ...rootState.snippetProps });
|
|
$$renderer2.push(`<!---->`);
|
|
} else {
|
|
$$renderer2.push("<!--[!-->");
|
|
$$renderer2.push(`<button${attributes({ ...mergedProps })}>`);
|
|
children?.($$renderer2, rootState.snippetProps);
|
|
$$renderer2.push(`<!----></button>`);
|
|
}
|
|
$$renderer2.push(`<!--]--> `);
|
|
Switch_input($$renderer2);
|
|
$$renderer2.push(`<!---->`);
|
|
bind_props($$props, { ref, checked });
|
|
});
|
|
}
|
|
function Switch_thumb($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
const uid = props_id($$renderer2);
|
|
let {
|
|
child,
|
|
children,
|
|
ref = null,
|
|
id = createId(uid),
|
|
$$slots,
|
|
$$events,
|
|
...restProps
|
|
} = $$props;
|
|
const thumbState = SwitchThumbState.create({
|
|
id: boxWith(() => id),
|
|
ref: boxWith(() => ref, (v) => ref = v)
|
|
});
|
|
const mergedProps = mergeProps(restProps, thumbState.props);
|
|
if (child) {
|
|
$$renderer2.push("<!--[-->");
|
|
child($$renderer2, { props: mergedProps, ...thumbState.snippetProps });
|
|
$$renderer2.push(`<!---->`);
|
|
} else {
|
|
$$renderer2.push("<!--[!-->");
|
|
$$renderer2.push(`<span${attributes({ ...mergedProps })}>`);
|
|
children?.($$renderer2, thumbState.snippetProps);
|
|
$$renderer2.push(`<!----></span>`);
|
|
}
|
|
$$renderer2.push(`<!--]-->`);
|
|
bind_props($$props, { ref });
|
|
});
|
|
}
|
|
function Avatar($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let {
|
|
ref = null,
|
|
loadingStatus = "loading",
|
|
class: className,
|
|
$$slots,
|
|
$$events,
|
|
...restProps
|
|
} = $$props;
|
|
let $$settled = true;
|
|
let $$inner_renderer;
|
|
function $$render_inner($$renderer3) {
|
|
$$renderer3.push("<!---->");
|
|
Avatar$1?.($$renderer3, spread_props([
|
|
{
|
|
"data-slot": "avatar",
|
|
class: cn("relative flex size-8 shrink-0 overflow-hidden rounded-full", className)
|
|
},
|
|
restProps,
|
|
{
|
|
get ref() {
|
|
return ref;
|
|
},
|
|
set ref($$value) {
|
|
ref = $$value;
|
|
$$settled = false;
|
|
},
|
|
get loadingStatus() {
|
|
return loadingStatus;
|
|
},
|
|
set loadingStatus($$value) {
|
|
loadingStatus = $$value;
|
|
$$settled = false;
|
|
}
|
|
}
|
|
]));
|
|
$$renderer3.push(`<!---->`);
|
|
}
|
|
do {
|
|
$$settled = true;
|
|
$$inner_renderer = $$renderer2.copy();
|
|
$$render_inner($$inner_renderer);
|
|
} while (!$$settled);
|
|
$$renderer2.subsume($$inner_renderer);
|
|
bind_props($$props, { ref, loadingStatus });
|
|
});
|
|
}
|
|
function Avatar_image($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let {
|
|
ref = null,
|
|
class: className,
|
|
$$slots,
|
|
$$events,
|
|
...restProps
|
|
} = $$props;
|
|
let $$settled = true;
|
|
let $$inner_renderer;
|
|
function $$render_inner($$renderer3) {
|
|
$$renderer3.push("<!---->");
|
|
Avatar_image$1?.($$renderer3, spread_props([
|
|
{
|
|
"data-slot": "avatar-image",
|
|
class: cn("aspect-square size-full", className)
|
|
},
|
|
restProps,
|
|
{
|
|
get ref() {
|
|
return ref;
|
|
},
|
|
set ref($$value) {
|
|
ref = $$value;
|
|
$$settled = false;
|
|
}
|
|
}
|
|
]));
|
|
$$renderer3.push(`<!---->`);
|
|
}
|
|
do {
|
|
$$settled = true;
|
|
$$inner_renderer = $$renderer2.copy();
|
|
$$render_inner($$inner_renderer);
|
|
} while (!$$settled);
|
|
$$renderer2.subsume($$inner_renderer);
|
|
bind_props($$props, { ref });
|
|
});
|
|
}
|
|
function Avatar_fallback($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let {
|
|
ref = null,
|
|
class: className,
|
|
$$slots,
|
|
$$events,
|
|
...restProps
|
|
} = $$props;
|
|
let $$settled = true;
|
|
let $$inner_renderer;
|
|
function $$render_inner($$renderer3) {
|
|
$$renderer3.push("<!---->");
|
|
Avatar_fallback$1?.($$renderer3, spread_props([
|
|
{
|
|
"data-slot": "avatar-fallback",
|
|
class: cn("bg-muted flex size-full items-center justify-center rounded-full", className)
|
|
},
|
|
restProps,
|
|
{
|
|
get ref() {
|
|
return ref;
|
|
},
|
|
set ref($$value) {
|
|
ref = $$value;
|
|
$$settled = false;
|
|
}
|
|
}
|
|
]));
|
|
$$renderer3.push(`<!---->`);
|
|
}
|
|
do {
|
|
$$settled = true;
|
|
$$inner_renderer = $$renderer2.copy();
|
|
$$render_inner($$inner_renderer);
|
|
} while (!$$settled);
|
|
$$renderer2.subsume($$inner_renderer);
|
|
bind_props($$props, { ref });
|
|
});
|
|
}
|
|
function Switch($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let {
|
|
ref = null,
|
|
class: className,
|
|
checked = false,
|
|
$$slots,
|
|
$$events,
|
|
...restProps
|
|
} = $$props;
|
|
let $$settled = true;
|
|
let $$inner_renderer;
|
|
function $$render_inner($$renderer3) {
|
|
$$renderer3.push("<!---->");
|
|
Switch$1?.($$renderer3, spread_props([
|
|
{
|
|
"data-slot": "switch",
|
|
class: cn("data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 peer inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50", className)
|
|
},
|
|
restProps,
|
|
{
|
|
get ref() {
|
|
return ref;
|
|
},
|
|
set ref($$value) {
|
|
ref = $$value;
|
|
$$settled = false;
|
|
},
|
|
get checked() {
|
|
return checked;
|
|
},
|
|
set checked($$value) {
|
|
checked = $$value;
|
|
$$settled = false;
|
|
},
|
|
children: ($$renderer4) => {
|
|
$$renderer4.push("<!---->");
|
|
Switch_thumb?.($$renderer4, {
|
|
"data-slot": "switch-thumb",
|
|
class: cn("bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0")
|
|
});
|
|
$$renderer4.push(`<!---->`);
|
|
},
|
|
$$slots: { default: true }
|
|
}
|
|
]));
|
|
$$renderer3.push(`<!---->`);
|
|
}
|
|
do {
|
|
$$settled = true;
|
|
$$inner_renderer = $$renderer2.copy();
|
|
$$render_inner($$inner_renderer);
|
|
} while (!$$settled);
|
|
$$renderer2.subsume($$inner_renderer);
|
|
bind_props($$props, { ref, checked });
|
|
});
|
|
}
|
|
function Dropdown_menu($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let { open = false, $$slots, $$events, ...restProps } = $$props;
|
|
let $$settled = true;
|
|
let $$inner_renderer;
|
|
function $$render_inner($$renderer3) {
|
|
$$renderer3.push("<!---->");
|
|
Menu?.($$renderer3, spread_props([
|
|
restProps,
|
|
{
|
|
get open() {
|
|
return open;
|
|
},
|
|
set open($$value) {
|
|
open = $$value;
|
|
$$settled = false;
|
|
}
|
|
}
|
|
]));
|
|
$$renderer3.push(`<!---->`);
|
|
}
|
|
do {
|
|
$$settled = true;
|
|
$$inner_renderer = $$renderer2.copy();
|
|
$$render_inner($$inner_renderer);
|
|
} while (!$$settled);
|
|
$$renderer2.subsume($$inner_renderer);
|
|
bind_props($$props, { open });
|
|
});
|
|
}
|
|
function Dropdown_menu_portal($$renderer, $$props) {
|
|
let { $$slots, $$events, ...restProps } = $$props;
|
|
$$renderer.push("<!---->");
|
|
Portal?.($$renderer, spread_props([restProps]));
|
|
$$renderer.push(`<!---->`);
|
|
}
|
|
function Dropdown_menu_content($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let {
|
|
ref = null,
|
|
sideOffset = 4,
|
|
portalProps,
|
|
class: className,
|
|
$$slots,
|
|
$$events,
|
|
...restProps
|
|
} = $$props;
|
|
let $$settled = true;
|
|
let $$inner_renderer;
|
|
function $$render_inner($$renderer3) {
|
|
Dropdown_menu_portal($$renderer3, spread_props([
|
|
portalProps,
|
|
{
|
|
children: ($$renderer4) => {
|
|
$$renderer4.push("<!---->");
|
|
Dropdown_menu_content$1?.($$renderer4, spread_props([
|
|
{
|
|
"data-slot": "dropdown-menu-content",
|
|
sideOffset,
|
|
class: cn("bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-end-2 data-[side=right]:slide-in-from-start-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--bits-dropdown-menu-content-available-height) min-w-[8rem] origin-(--bits-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md outline-none", className)
|
|
},
|
|
restProps,
|
|
{
|
|
get ref() {
|
|
return ref;
|
|
},
|
|
set ref($$value) {
|
|
ref = $$value;
|
|
$$settled = false;
|
|
}
|
|
}
|
|
]));
|
|
$$renderer4.push(`<!---->`);
|
|
},
|
|
$$slots: { default: true }
|
|
}
|
|
]));
|
|
}
|
|
do {
|
|
$$settled = true;
|
|
$$inner_renderer = $$renderer2.copy();
|
|
$$render_inner($$inner_renderer);
|
|
} while (!$$settled);
|
|
$$renderer2.subsume($$inner_renderer);
|
|
bind_props($$props, { ref });
|
|
});
|
|
}
|
|
function Dropdown_menu_item($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let {
|
|
ref = null,
|
|
class: className,
|
|
inset,
|
|
variant = "default",
|
|
$$slots,
|
|
$$events,
|
|
...restProps
|
|
} = $$props;
|
|
let $$settled = true;
|
|
let $$inner_renderer;
|
|
function $$render_inner($$renderer3) {
|
|
$$renderer3.push("<!---->");
|
|
Menu_item?.($$renderer3, spread_props([
|
|
{
|
|
"data-slot": "dropdown-menu-item",
|
|
"data-inset": inset,
|
|
"data-variant": variant,
|
|
class: cn("data-highlighted:bg-accent data-highlighted:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:data-highlighted:bg-destructive/10 dark:data-[variant=destructive]:data-highlighted:bg-destructive/20 data-[variant=destructive]:data-highlighted:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:ps-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className)
|
|
},
|
|
restProps,
|
|
{
|
|
get ref() {
|
|
return ref;
|
|
},
|
|
set ref($$value) {
|
|
ref = $$value;
|
|
$$settled = false;
|
|
}
|
|
}
|
|
]));
|
|
$$renderer3.push(`<!---->`);
|
|
}
|
|
do {
|
|
$$settled = true;
|
|
$$inner_renderer = $$renderer2.copy();
|
|
$$render_inner($$inner_renderer);
|
|
} while (!$$settled);
|
|
$$renderer2.subsume($$inner_renderer);
|
|
bind_props($$props, { ref });
|
|
});
|
|
}
|
|
function Dropdown_menu_separator($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let {
|
|
ref = null,
|
|
class: className,
|
|
$$slots,
|
|
$$events,
|
|
...restProps
|
|
} = $$props;
|
|
let $$settled = true;
|
|
let $$inner_renderer;
|
|
function $$render_inner($$renderer3) {
|
|
$$renderer3.push("<!---->");
|
|
Menu_separator?.($$renderer3, spread_props([
|
|
{
|
|
"data-slot": "dropdown-menu-separator",
|
|
class: cn("bg-border -mx-1 my-1 h-px", className)
|
|
},
|
|
restProps,
|
|
{
|
|
get ref() {
|
|
return ref;
|
|
},
|
|
set ref($$value) {
|
|
ref = $$value;
|
|
$$settled = false;
|
|
}
|
|
}
|
|
]));
|
|
$$renderer3.push(`<!---->`);
|
|
}
|
|
do {
|
|
$$settled = true;
|
|
$$inner_renderer = $$renderer2.copy();
|
|
$$render_inner($$inner_renderer);
|
|
} while (!$$settled);
|
|
$$renderer2.subsume($$inner_renderer);
|
|
bind_props($$props, { ref });
|
|
});
|
|
}
|
|
function Dropdown_menu_trigger($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let { ref = null, $$slots, $$events, ...restProps } = $$props;
|
|
let $$settled = true;
|
|
let $$inner_renderer;
|
|
function $$render_inner($$renderer3) {
|
|
$$renderer3.push("<!---->");
|
|
Menu_trigger?.($$renderer3, spread_props([
|
|
{ "data-slot": "dropdown-menu-trigger" },
|
|
restProps,
|
|
{
|
|
get ref() {
|
|
return ref;
|
|
},
|
|
set ref($$value) {
|
|
ref = $$value;
|
|
$$settled = false;
|
|
}
|
|
}
|
|
]));
|
|
$$renderer3.push(`<!---->`);
|
|
}
|
|
do {
|
|
$$settled = true;
|
|
$$inner_renderer = $$renderer2.copy();
|
|
$$render_inner($$inner_renderer);
|
|
} while (!$$settled);
|
|
$$renderer2.subsume($$inner_renderer);
|
|
bind_props($$props, { ref });
|
|
});
|
|
}
|
|
function Label($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let {
|
|
ref = null,
|
|
class: className,
|
|
$$slots,
|
|
$$events,
|
|
...restProps
|
|
} = $$props;
|
|
let $$settled = true;
|
|
let $$inner_renderer;
|
|
function $$render_inner($$renderer3) {
|
|
$$renderer3.push("<!---->");
|
|
Label$1?.($$renderer3, spread_props([
|
|
{
|
|
"data-slot": "label",
|
|
class: cn("flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50", className)
|
|
},
|
|
restProps,
|
|
{
|
|
get ref() {
|
|
return ref;
|
|
},
|
|
set ref($$value) {
|
|
ref = $$value;
|
|
$$settled = false;
|
|
}
|
|
}
|
|
]));
|
|
$$renderer3.push(`<!---->`);
|
|
}
|
|
do {
|
|
$$settled = true;
|
|
$$inner_renderer = $$renderer2.copy();
|
|
$$render_inner($$inner_renderer);
|
|
} while (!$$settled);
|
|
$$renderer2.subsume($$inner_renderer);
|
|
bind_props($$props, { ref });
|
|
});
|
|
}
|
|
function _layout($$renderer, $$props) {
|
|
$$renderer.component(($$renderer2) => {
|
|
let { data, children } = $$props;
|
|
let darkmode = false;
|
|
function submitDarkmodeForm(formEl) {
|
|
const input = formEl.querySelector('input[name="darkmode"]');
|
|
if (input && input instanceof HTMLInputElement) {
|
|
input.value = darkmode ? "true" : "false";
|
|
}
|
|
formEl.requestSubmit();
|
|
}
|
|
let $$settled = true;
|
|
let $$inner_renderer;
|
|
function $$render_inner($$renderer3) {
|
|
$$renderer3.push(`<div class="app-shell">`);
|
|
if (data.user) {
|
|
$$renderer3.push("<!--[-->");
|
|
$$renderer3.push(`<header class="fixed right-0 top-0 z-50 flex items-center gap-2 p-2 pr-[max(0.5rem,env(safe-area-inset-right))] pt-[max(0.5rem,env(safe-area-inset-top))]" aria-label="User menu">`);
|
|
$$renderer3.push("<!---->");
|
|
Dropdown_menu?.($$renderer3, {
|
|
children: ($$renderer4) => {
|
|
$$renderer4.push("<!---->");
|
|
Dropdown_menu_trigger?.($$renderer4, {
|
|
class: "border-0 bg-transparent hover:bg-muted inline-flex size-10 shrink-0 items-center justify-center rounded-full focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
children: ($$renderer5) => {
|
|
$$renderer5.push("<!---->");
|
|
Avatar?.($$renderer5, {
|
|
class: "size-10 rounded-full",
|
|
children: ($$renderer6) => {
|
|
$$renderer6.push("<!---->");
|
|
Avatar_image?.($$renderer6, { src: data.user.avatar, alt: "" });
|
|
$$renderer6.push(`<!----> `);
|
|
$$renderer6.push("<!---->");
|
|
Avatar_fallback?.($$renderer6, {
|
|
class: "bg-muted text-muted-foreground flex size-10 items-center justify-center rounded-full text-sm font-medium",
|
|
children: ($$renderer7) => {
|
|
$$renderer7.push(`<!---->${escape_html(data.user.name ? String(data.user.name).slice(0, 2).toUpperCase() : data.user.email ? String(data.user.email).slice(0, 2).toUpperCase() : "?")}`);
|
|
},
|
|
$$slots: { default: true }
|
|
});
|
|
$$renderer6.push(`<!---->`);
|
|
},
|
|
$$slots: { default: true }
|
|
});
|
|
$$renderer5.push(`<!---->`);
|
|
},
|
|
$$slots: { default: true }
|
|
});
|
|
$$renderer4.push(`<!----> `);
|
|
$$renderer4.push("<!---->");
|
|
Dropdown_menu_portal?.($$renderer4, {
|
|
children: ($$renderer5) => {
|
|
$$renderer5.push("<!---->");
|
|
Dropdown_menu_content?.($$renderer5, {
|
|
align: "end",
|
|
class: "w-56",
|
|
children: ($$renderer6) => {
|
|
$$renderer6.push(`<form id="darkmode-form" method="POST" action="/?/setDarkmode" class="contents"><input type="hidden" name="darkmode"${attr("value", darkmode ? "true" : "false")}/> `);
|
|
$$renderer6.push("<!---->");
|
|
Dropdown_menu_item?.($$renderer6, {
|
|
class: "cursor-default focus:bg-transparent",
|
|
onpointerdown: (e) => e.preventDefault(),
|
|
children: ($$renderer7) => {
|
|
Label($$renderer7, {
|
|
for: "dark-mode-switch",
|
|
class: "flex flex-1 cursor-pointer items-center justify-between font-normal",
|
|
children: ($$renderer8) => {
|
|
$$renderer8.push(`<span>Dark mode</span> `);
|
|
Switch($$renderer8, {
|
|
id: "dark-mode-switch",
|
|
onkeydown: (e) => {
|
|
if (e.key === "Enter" || e.key === " ") {
|
|
e.preventDefault();
|
|
darkmode = !darkmode;
|
|
const f = document.getElementById("darkmode-form");
|
|
if (f) submitDarkmodeForm(f);
|
|
}
|
|
},
|
|
onclick: () => {
|
|
setTimeout(
|
|
() => {
|
|
const f = document.getElementById("darkmode-form");
|
|
if (f) submitDarkmodeForm(f);
|
|
},
|
|
0
|
|
);
|
|
},
|
|
get checked() {
|
|
return darkmode;
|
|
},
|
|
set checked($$value) {
|
|
darkmode = $$value;
|
|
$$settled = false;
|
|
}
|
|
});
|
|
$$renderer8.push(`<!---->`);
|
|
},
|
|
$$slots: { default: true }
|
|
});
|
|
},
|
|
$$slots: { default: true }
|
|
});
|
|
$$renderer6.push(`<!----></form> `);
|
|
$$renderer6.push("<!---->");
|
|
Dropdown_menu_separator?.($$renderer6, {});
|
|
$$renderer6.push(`<!----> <a href="/logout">`);
|
|
$$renderer6.push("<!---->");
|
|
Dropdown_menu_item?.($$renderer6, {
|
|
children: ($$renderer7) => {
|
|
$$renderer7.push(`<!---->Log out`);
|
|
},
|
|
$$slots: { default: true }
|
|
});
|
|
$$renderer6.push(`<!----></a>`);
|
|
},
|
|
$$slots: { default: true }
|
|
});
|
|
$$renderer5.push(`<!---->`);
|
|
},
|
|
$$slots: { default: true }
|
|
});
|
|
$$renderer4.push(`<!---->`);
|
|
},
|
|
$$slots: { default: true }
|
|
});
|
|
$$renderer3.push(`<!----></header>`);
|
|
} else {
|
|
$$renderer3.push("<!--[!-->");
|
|
}
|
|
$$renderer3.push(`<!--]--> `);
|
|
children?.($$renderer3);
|
|
$$renderer3.push(`<!----></div>`);
|
|
}
|
|
do {
|
|
$$settled = true;
|
|
$$inner_renderer = $$renderer2.copy();
|
|
$$render_inner($$inner_renderer);
|
|
} while (!$$settled);
|
|
$$renderer2.subsume($$inner_renderer);
|
|
});
|
|
}
|
|
export {
|
|
_layout as default
|
|
};
|