@baleada/vue-features
Version:
User interface features composed from Baleada Composition functions and the Vue 3 Composition API
1,534 lines (1,500 loc) • 221 kB
JavaScript
import { pipe, reverse, join, some, unique, findIndex, at, find, includes, toLength, map, min, max, filter, every } from 'lazy-collections';
import { isRef, computed, unref, nextTick, useId, onMounted, watch, shallowRef, onBeforeUpdate, watchEffect, ref, provide, inject, onScopeDispose, getCurrentInstance } from 'vue';
import { createEntries, createClip, Listenable, createKeycomboMatch, createDeepEqual, createList, createComputedStyle, createConcat, createFocusable, Navigateable, createMap, Pickable, createFilter, createPointerhover, createOmit, createKeys, createKeypress, createPointerpress, createReduce, createSlice, createResults, createAssociativeArrayHas, createAssociativeArrayValue, createAssociativeArraySet, createDeepMerge, createClamp } from '@baleada/logic';
import { useNavigateable, useStoreable, useDelayable, useCompleteable, usePickable, useListenable } from '@baleada/vue-composition';
class Plane extends Array {
constructor(...initial) {
super(...initial);
}
get({ row, column }) {
return this[row]?.[column];
}
set({ row, column }, value) {
(this[row] ?? (this[row] = []))[column] = value;
}
*points() {
for (let row = 0; row < this.length; row++) {
for (let column = 0; column < this[0].length; column++) {
yield {
row,
column,
point: this.get({ row, column })
};
}
}
}
}
function narrowElement(extendable) {
if (isRef(extendable))
return extendable;
if (typeof extendable === "function")
return computed(extendable);
return extendable.root.element;
}
function narrowListenOptions(options) {
if (!options) {
return {};
}
if ("observer" in options && "root" in options.observer) {
return {
observer: {
...options.observer,
root: unref(options.observer.root)
}
};
}
return options;
}
function narrowReactivePlane(rendered) {
if (isRef(rendered)) {
if (rendered.value instanceof Plane) {
return computed(() => rendered.value);
}
if (Array.isArray(rendered.value)) {
return computed(() => new Plane(rendered.value));
}
return computed(() => new Plane(
rendered.value ? [rendered.value] : []
));
}
if (rendered instanceof Plane) {
return computed(() => rendered);
}
if (Array.isArray(rendered)) {
return computed(() => new Plane(rendered));
}
return computed(() => new Plane([rendered]));
}
function narrowTransitionOption(elementOrListOrPlane, optionOrOptionCreator) {
return typeof optionOrOptionCreator === "function" ? optionOrOptionCreator(elementOrListOrPlane) : optionOrOptionCreator;
}
function narrowWatchSources(rawWatchSources) {
return rawWatchSources === void 0 ? [] : Array.isArray(rawWatchSources) ? rawWatchSources : [rawWatchSources];
}
function toRenderedKind(rendered) {
if (isRef(rendered)) {
if (rendered.value instanceof Plane)
return "plane";
if (Array.isArray(rendered.value))
return "list";
return "element";
}
if (rendered instanceof Plane)
return "plane";
if (Array.isArray(rendered))
return "list";
return "element";
}
function toEntries(object) {
return createEntries()(object);
}
function toInputEffectNames({
previousString,
newString,
lastRecordedString,
previousSelection,
newSelection
}) {
const change = {
operation: newString.length - previousString.length > 0 && "add" || newString.length - previousString.length < 0 && "remove" || "replace",
quantity: Math.abs(newString.length - previousString.length) > 1 ? "multiple" : "single",
previousStatus: lastRecordedString === previousString ? "recorded" : "unrecorded",
newLastCharacter: /\s/.test(newString[newSelection.start - 1]) ? "whitespace" : "not whitespace",
previousLastCharacter: /\s/.test(previousString[previousSelection.start - 1]) ? "whitespace" : "not whitespace"
};
if (change.operation === "replace" && change.previousStatus === "recorded") {
return ["recordNew"];
}
if (change.operation === "replace" && change.previousStatus === "unrecorded") {
return ["recordPrevious", "recordNew"];
}
if (change.operation === "add" && change.quantity === "single" && change.newLastCharacter === "not whitespace" && change.previousStatus === "recorded") {
return ["sync"];
}
if (change.operation === "add" && change.quantity === "single" && change.newLastCharacter === "not whitespace" && change.previousStatus === "unrecorded") {
if (lastRecordedString.length > previousString.length) {
return ["recordPrevious", "recordNew"];
}
return ["sync"];
}
if (change.operation === "add" && change.quantity === "single" && change.newLastCharacter === "whitespace" && change.previousStatus === "recorded") {
return ["recordNew"];
}
if (change.operation === "add" && change.quantity === "single" && change.newLastCharacter === "whitespace" && change.previousStatus === "unrecorded") {
return ["recordPrevious", "recordNew"];
}
if (change.operation === "add" && change.quantity === "multiple" && change.previousStatus === "recorded") {
return ["recordNew"];
}
if (change.operation === "add" && change.quantity === "multiple" && change.previousStatus === "unrecorded") {
return ["recordPrevious", "recordNew"];
}
if (change.operation === "remove" && change.quantity === "single" && change.previousLastCharacter === "not whitespace" && change.previousStatus === "recorded") {
return ["sync"];
}
if (change.operation === "remove" && change.quantity === "single" && change.previousLastCharacter === "not whitespace" && change.previousStatus === "unrecorded") {
if (lastRecordedString.length > previousString.length) {
return ["sync"];
}
return ["recordPrevious", "sync"];
}
if (change.operation === "remove" && change.quantity === "single" && change.previousLastCharacter === "whitespace" && change.previousStatus === "recorded") {
return ["recordNew"];
}
if (change.operation === "remove" && change.quantity === "single" && change.previousLastCharacter === "whitespace" && change.previousStatus === "unrecorded") {
return ["recordPrevious", "recordNew"];
}
if (change.operation === "remove" && change.quantity === "multiple" && change.previousStatus === "recorded") {
return ["recordNew"];
}
if (change.operation === "remove" && change.quantity === "multiple" && change.previousStatus === "unrecorded") {
return ["recordPrevious", "recordNew"];
}
}
function toSymmetricalCompletion({ punctuation, segment }) {
return segment.startsWith(punctuation) && segment.endsWith(punctuation) ? segment.slice(punctuation.length, segment.length - punctuation.length) : `${punctuation}${segment}${punctuation}`;
}
function toMappedCompletion({ punctuation, segment }) {
const lines = segment.split("\n"), isCompleted = lines.every((line, index) => {
if (typeof punctuation === "function") {
return line.startsWith(punctuation(index));
}
return line.startsWith(punctuation);
});
if (isCompleted) {
return lines.map((line, index) => {
if (typeof punctuation === "function") {
return line.slice(punctuation(index).length);
}
return line.slice(punctuation.length);
}).join("\n");
}
return lines.map((line, index) => {
if (typeof punctuation === "function") {
return `${punctuation(index)}${line}`;
}
return `${punctuation}${line}`;
}).join("\n");
}
function toMirroredCompletion({ punctuation, segment }) {
const mirrored = pipe(
reverse(),
join("")
)(punctuation.split(""));
return segment.startsWith(punctuation) && segment.endsWith(mirrored) ? segment.slice(punctuation.length, segment.length - mirrored.length) : `${punctuation}${segment}${mirrored}`;
}
function toHeadingCompletion({ level, segment }) {
const hashes = (() => {
let hashes2 = "";
for (let i = 0; i < level; i++) {
hashes2 += "#";
}
return hashes2;
})();
return segment.startsWith(`${hashes} `) ? segment.slice(`${hashes} `.length) : `${hashes} ${toWithoutHeading(segment)}`;
}
const toWithoutHeading = createClip(/^#+\s+/);
function toHorizontalRuleCompletion({ character, segment }) {
return `${segment}${segment.length > 0 ? "\n" : ""}${character}${character}${character}
`;
}
function show(elementOrListOrPlane, condition, options = {}) {
const originalStyles = /* @__PURE__ */ new WeakMap(), cancels = /* @__PURE__ */ new WeakMap(), statuses = /* @__PURE__ */ new WeakMap(), { transition = {} } = options, affordanceElementKind = toRenderedKind(elementOrListOrPlane), { appear = {}, enter = {}, leave = {} } = transition, transitionTypes = toTransitionTypes({ appear, enter, leave });
onRenderedBind(
elementOrListOrPlane,
(element, value, row, column) => {
const didCancel = cancels.get(element)?.();
if (!originalStyles.get(element)) {
originalStyles.set(element, {
display: null
// transitionProperty: style.transitionProperty,
// transitionDuration: style.transitionDuration,
// transitionTimingFunction: style.transitionTimingFunction,
// transitionDelay: style.transitionDelay,
});
}
const originalStyle = originalStyles.get(element);
if (didCancel) {
cancels.set(element, void 0);
if (value) {
if (element.style.display === originalStyle.display) {
return;
}
element.style.display = originalStyle.display;
return;
}
element.style.display = "none";
return;
}
if (!value) {
if (element.style.display === "none") {
return;
}
if (statuses.get(element) !== "appeared") {
element.style.display = "none";
return;
}
if (transitionTypes.leave === "none") {
element.style.display = "none";
return;
}
if (transitionTypes.leave === "css") {
const cancel = transitionCss(element, {
...leave,
start: (addFrom) => {
leave.start?.();
addFrom();
},
end: (removeTo) => {
element.style.display = "none";
removeTo();
leave.end?.();
},
cancel: () => {
leave.cancel?.();
}
});
cancels.set(element, cancel);
return;
}
if (transitionTypes.leave === "js") {
const cancel = transitionJs(
affordanceElementKind,
{
column,
row,
before: leave.before,
start: () => {
},
active: leave.active,
end: (status) => {
if (status === "canceled") {
return;
}
element.style.display = "none";
},
after: leave.after,
cancel: leave.cancel
}
);
cancels.set(element, cancel);
return;
}
}
if (value) {
let enterEffect = function() {
if (element.style.display === originalStyle.display) {
return;
}
if (transitionTypes.enter === "none") {
element.style.display = originalStyle.display;
return;
}
if (transitionTypes.enter === "css") {
const cancel = transitionCss(element, {
...enter,
start: (addFrom) => {
enter.start?.();
addFrom();
element.style.display = originalStyle.display;
},
end: (removeTo) => {
removeTo();
enter.end?.();
},
cancel: () => {
enter.cancel?.();
}
});
cancels.set(element, cancel);
return;
}
if (transitionTypes.enter === "js") {
const cancel = transitionJs(
affordanceElementKind,
{
row,
column,
before: enter.before,
start: () => element.style.display = originalStyle.display,
active: enter.active,
end: () => {
},
after: enter.after,
cancel: enter.cancel
}
);
cancels.set(element, cancel);
return;
}
};
if (statuses.get(element) !== "appeared") {
statuses.set(element, "appeared");
if (element.style.display === originalStyle.display) {
return;
}
if (transitionTypes.appear === "none") {
element.style.display = originalStyle.display;
return;
}
if (transitionTypes.appear === "css") {
const cancel = transitionCss(element, {
...appear,
start: (addFrom) => {
appear.start?.();
addFrom();
element.style.display = originalStyle.display;
},
end: (removeTo) => {
removeTo();
appear.end?.();
},
cancel: () => {
appear.cancel?.();
}
});
cancels.set(element, cancel);
return;
}
if (transitionTypes.appear === "js") {
const hooks = transition?.appear === true && transition?.enter || !transition?.appear && {} || transition?.appear;
const cancel = transitionJs(
affordanceElementKind,
{
row,
column,
before: hooks?.before,
start: () => element.style.display = originalStyle.display,
active: hooks?.active,
end: () => {
},
after: hooks?.after,
cancel: hooks?.cancel
}
);
cancels.set(element, cancel);
return;
}
if (transitionTypes.appear === "enter") {
enterEffect();
return;
}
}
enterEffect();
}
},
() => {
},
narrowBindValue(condition),
narrowWatchSourceOrSources(condition)
);
}
function defineTransition(transition) {
return transition;
}
function transitionJs(affordanceElementKind, config) {
let status = "ready";
const { row, column } = config, { before, start, active, end, after, cancel } = (() => {
if (affordanceElementKind === "plane") {
return {
before: () => config.before?.({ row, column }),
start: () => config.start?.(),
active: () => config.active?.({ row, column }, done),
end: () => config.end?.(status),
after: () => config.after?.({ row, column }),
cancel: () => config.cancel?.({ row, column })
};
}
if (affordanceElementKind === "list") {
return {
before: () => config.before?.(row),
start: () => config.start?.(),
active: () => config.active?.(row, done),
end: () => config.end?.(status),
after: () => config.after?.(row),
cancel: () => config.cancel?.(row)
};
}
return {
before: () => config.before?.(),
start: () => config.start?.(),
active: () => config.active?.(done),
end: () => config.end?.(status),
after: () => config.after?.(),
cancel: () => config.cancel?.()
};
})(), done = () => {
end();
if (status === "canceled")
return;
after();
status = "transitioned";
};
before();
start();
status = "transitioning";
if (config.active) {
active();
} else {
done();
}
return () => {
if (status === "transitioned")
return false;
status = "canceled";
requestAnimationFrame(() => {
cancel();
done();
});
return true;
};
}
function transitionCss(element, config) {
let status = "ready";
const from = config.from.split(" ") || [], active = config.active.split(" ") || [], to = config.to.split(" ") || [], transitionend = new Listenable("transitionend"), transitioncancel = new Listenable("transitioncancel"), end = () => {
status = "transitioned";
transitionend.stop();
transitioncancel.stop();
requestAnimationFrame(() => {
const removeTo = () => element.classList.remove(...active, ...to);
config.end(removeTo);
});
}, cancel = () => {
transitioncancel.stop();
transitionend.stop();
requestAnimationFrame(() => {
element.style.transitionProperty = "";
config.cancel();
});
};
transitionend.listen((event) => {
if (event.target !== element)
return;
end();
});
transitioncancel.listen((event) => {
if (event.target !== element)
return;
cancel();
});
const addFrom = () => element.classList.add(...from);
config.start(addFrom);
status = "transitioning";
requestAnimationFrame(() => {
element.classList.add(...active);
requestAnimationFrame(() => {
element.classList.remove(...from);
element.classList.add(...to);
if (predicateInstantTransition(element))
end();
});
});
return () => {
if (status === "transitioned")
return false;
status = "canceled";
element.style.transitionProperty = "none";
element.classList.remove(...from, ...active, ...to);
return true;
};
}
function predicateInstantTransition(element) {
return toComputedStyle(element).transitionDuration === "0s";
}
function toTransitionTypes({ appear = {}, enter = {}, leave = {} }) {
const enterType = (() => {
if ("from" in enter)
return "css";
if (some((key) => key in enter)(["before", "active", "after", "cancel"]))
return "js";
return "none";
})(), leaveType = (() => {
if ("from" in leave)
return "css";
if (some((key) => key in leave)(["before", "active", "after", "cancel"]))
return "js";
return "none";
})(), appearType = (() => {
if (appear === true)
return "enter";
if ("from" in appear)
return "css";
if (some((key) => key in appear)(["before", "active", "after", "cancel"]))
return "js";
return "none";
})();
return {
appear: appearType,
enter: enterType,
leave: leaveType
};
}
function toTransitionWithEffects(effects, options) {
const transitionOption = options?.transition ?? {}, transitionTypes = toTransitionTypes(transitionOption);
const [enter, leave] = ["enter", "leave"].map((stage) => {
if (transitionTypes[stage] === "none") {
return effects[stage]?.none || {};
}
if (transitionTypes[stage] === "js") {
return {
before: (...args) => {
transitionOption[stage]?.before?.(...args);
effects[stage]?.js?.before?.(...args);
},
active: (...args) => {
const withoutDone = args.slice(0, args.length - 1), performTransitionEffect = () => {
effects[stage]?.js?.active?.(...args);
};
if (transitionOption[stage]?.active)
transitionOption[stage]?.active?.(...withoutDone, performTransitionEffect);
else
performTransitionEffect();
},
after: (...args) => {
transitionOption[stage]?.after?.(...args);
effects[stage]?.js?.after?.(...args);
},
cancel: (...args) => {
transitionOption[stage]?.cancel?.(...args);
effects[stage]?.js?.cancel?.(...args);
}
};
}
return {
...transitionOption[stage],
start: () => {
transitionOption[stage].start?.();
effects[stage]?.css?.start?.();
},
end: () => {
transitionOption[stage].end?.();
effects[stage]?.css?.end?.();
},
cancel: () => {
transitionOption[stage].cancel?.();
effects[stage]?.css?.cancel?.();
}
};
}), appear = (() => {
if (transitionOption.appear === true)
return enter;
if (transitionTypes.appear === "none") {
return effects.appear?.none || {};
}
if (transitionTypes.appear === "js") {
return {
before: (...args) => {
transitionOption.appear?.before?.(...args);
effects.appear?.js?.before?.(...args);
},
active: (...args) => {
const withoutDone = args.slice(0, args.length - 1), performTransitionEffect = () => {
effects.appear?.js?.active?.(...args);
};
if (transitionOption.appear?.active)
transitionOption.appear?.active?.(...withoutDone, performTransitionEffect);
else
performTransitionEffect();
},
after: (...args) => {
transitionOption.appear?.after?.(...args);
effects.appear?.js?.after?.(...args);
},
cancel: (...args) => {
transitionOption.appear?.cancel?.(...args);
effects.appear?.js?.cancel?.(...args);
}
};
}
return {
...transitionOption.appear,
start: () => {
transitionOption.appear.start?.();
effects.appear?.css?.start?.();
},
end: () => {
transitionOption.appear.end?.();
effects.appear?.css?.end?.();
},
cancel: () => {
transitionOption.appear.cancel?.();
effects.appear?.css?.cancel?.();
}
};
})();
return { appear, enter, leave };
}
function toTransitionWithFocus({ focusAfterEnter, focusAfterLeave }, options) {
const appearAndEnterJsEffects = {
after: () => nextTick(focusAfterEnter),
cancel: () => nextTick(focusAfterLeave),
active: (...args) => {
const done = args[args.length - 1];
done();
}
}, appearAndEnterEffects = {
none: appearAndEnterJsEffects,
js: appearAndEnterJsEffects,
css: {
end: () => nextTick(focusAfterEnter),
cancel: () => nextTick(focusAfterLeave)
}
}, leaveJsEffects = {
after: () => nextTick(focusAfterLeave),
cancel: () => nextTick(focusAfterEnter),
active: (...args) => {
const done = args[args.length - 1];
done();
}
};
return toTransitionWithEffects(
{
appear: appearAndEnterEffects,
enter: appearAndEnterEffects,
leave: {
none: leaveJsEffects,
js: leaveJsEffects,
css: {
end: () => nextTick(focusAfterLeave),
cancel: () => nextTick(focusAfterEnter)
}
}
},
options
);
}
const predicateUp = createKeycomboMatch("up");
const predicateRight = createKeycomboMatch("right");
const predicateDown = createKeycomboMatch("down");
const predicateLeft = createKeycomboMatch("left");
const predicateArrow = (event) => predicateUp(event) || predicateRight(event) || predicateDown(event) || predicateLeft(event);
createKeycomboMatch("alt");
const predicateCmd = createKeycomboMatch("cmd");
createKeycomboMatch("ctrl");
createKeycomboMatch("shift");
createKeycomboMatch("backspace");
const predicateEnter = createKeycomboMatch("enter");
const predicateEsc = createKeycomboMatch("esc");
createKeycomboMatch("tab");
const predicateHome = createKeycomboMatch("home");
const predicateEnd = createKeycomboMatch("end");
const predicateSpace = createKeycomboMatch("space");
function predicateNullish(value) {
return value === void 0 || value === null;
}
const defaultOptions$s = {
predicateEqual: (currentPoint, previousPoint) => currentPoint === previousPoint
};
function toPlaneOrder(currentPoints, previousPoints, options = {}) {
const { predicateEqual } = { ...defaultOptions$s, ...options };
for (const { row, column, point: currentPoint } of currentPoints.points()) {
const previousPoint = previousPoints.get({ row, column });
if (predicateNullish(currentPoint) || predicateNullish(previousPoint))
continue;
if (!predicateEqual(currentPoint, previousPoint))
return "changed";
}
return "none";
}
function toPlaneStatus(currentPlane, previousPlane, options = {}) {
if (currentPlane && !previousPlane)
return {
rowWidth: "lengthened",
columnHeight: "lengthened",
order: "changed"
};
const rowWidth = (() => {
if (!currentPlane.length)
return "n/a";
if (currentPlane[0].length > (previousPlane[0]?.length || 0))
return "lengthened";
if (currentPlane[0].length < (previousPlane[0]?.length || 0))
return "shortened";
return "none";
})(), columnHeight = (() => {
if (!currentPlane[0]?.length)
return "n/a";
if (currentPlane.length > previousPlane.length)
return "lengthened";
if (currentPlane.length < previousPlane.length)
return "shortened";
return "none";
})(), order = toPlaneOrder(
currentPlane,
previousPlane,
options
);
return { rowWidth, columnHeight, order };
}
function predicateRenderedWatchSourcesChanged(current, previous) {
if (current.length > 1) {
for (let i = 1; i < current.length; i++) {
if (!createDeepEqual(previous?.[i])(current[i]))
return true;
}
}
const currentPlane = current[0], previousPlane = previous?.[0], { rowWidth, columnHeight, order } = toPlaneStatus(currentPlane, previousPlane);
return ![rowWidth, columnHeight, order].every((status) => status === "none");
}
const defaultAbilityMeta = {
ability: "enabled"
};
function toAbilityBindValues(elementOrListOrPlaneApi) {
if (elementOrListOrPlaneApi.meta.value instanceof Plane) {
return {
disabled: {
get: ({ row, column }) => elementOrListOrPlaneApi.meta.value?.get({ row, column })?.ability === "disabled" ? true : void 0,
watchSource: elementOrListOrPlaneApi.meta
},
ariaDisabled: {
get: ({ row, column }) => elementOrListOrPlaneApi.meta.value?.get({ row, column })?.ability === "disabled" ? true : void 0,
watchSource: elementOrListOrPlaneApi.meta
},
tabindex: {
get: ({ row, column }) => elementOrListOrPlaneApi.meta.value?.get({ row, column })?.ability === "enabled" ? 0 : -1,
watchSource: elementOrListOrPlaneApi.meta
}
};
}
if (Array.isArray(elementOrListOrPlaneApi.meta.value)) {
return {
disabled: {
get: (index) => elementOrListOrPlaneApi.meta.value?.[index]?.ability === "disabled" ? true : void 0,
watchSource: elementOrListOrPlaneApi.meta
},
ariaDisabled: {
get: (index) => elementOrListOrPlaneApi.meta.value?.[index]?.ability === "disabled" ? true : void 0,
watchSource: elementOrListOrPlaneApi.meta
},
tabindex: {
get: (index) => elementOrListOrPlaneApi.meta.value?.[index]?.ability === "enabled" ? 0 : -1,
watchSource: elementOrListOrPlaneApi.meta
}
};
}
return {
disabled: {
get: () => elementOrListOrPlaneApi.meta.value.ability === "disabled" ? true : void 0,
watchSource: elementOrListOrPlaneApi.meta
},
ariaDisabled: {
get: () => elementOrListOrPlaneApi.meta.value.ability === "disabled" ? true : void 0,
watchSource: elementOrListOrPlaneApi.meta
},
tabindex: {
get: () => elementOrListOrPlaneApi.meta.value.ability === "enabled" ? 0 : -1,
watchSource: elementOrListOrPlaneApi.meta
}
};
}
const defaultValidityMeta = {
validity: "valid"
};
function toValidityBindValues(elementApi) {
return {
ariaInvalid: {
get: () => elementApi.meta.value.validity === "invalid" ? true : void 0,
watchSource: elementApi.meta
}
};
}
const defaultLabelMeta = {
label: void 0,
labelledBy: void 0,
description: void 0,
describedBy: void 0,
errorMessage: void 0,
details: void 0
};
function toLabelBindValues(elementOrListOrPlaneApi) {
if (elementOrListOrPlaneApi.meta.value instanceof Plane) {
return {
ariaLabel: {
get: ({ row, column }) => elementOrListOrPlaneApi.meta.value?.get({ row, column })?.label || void 0,
watchSource: elementOrListOrPlaneApi.meta
},
ariaLabelledbys: {
get: ({ row, column }) => toListValue(
elementOrListOrPlaneApi.meta.value?.get({ row, column })?.labelledBy
),
watchSource: elementOrListOrPlaneApi.meta
},
ariaDescription: {
get: ({ row, column }) => elementOrListOrPlaneApi.meta.value?.get({ row, column })?.description || void 0,
watchSource: elementOrListOrPlaneApi.meta
},
ariaDescribedbys: {
get: ({ row, column }) => toListValue(
[
toListValue(
elementOrListOrPlaneApi.meta.value?.get({ row, column })?.describedBy
),
toListValue(
elementOrListOrPlaneApi.meta.value?.get({ row, column })?.errorMessage
)
]
),
watchSource: elementOrListOrPlaneApi.meta
},
ariaDetails: {
get: ({ row, column }) => toListValue(
elementOrListOrPlaneApi.meta.value?.get({ row, column })?.details
),
watchSource: elementOrListOrPlaneApi.meta
}
};
}
if (Array.isArray(elementOrListOrPlaneApi.meta.value)) {
return {
ariaLabel: {
get: (index) => elementOrListOrPlaneApi.meta.value?.[index]?.label || void 0,
watchSource: elementOrListOrPlaneApi.meta
},
ariaLabelledbys: {
get: (index) => toListValue(
elementOrListOrPlaneApi.meta.value?.[index]?.labelledBy
),
watchSource: elementOrListOrPlaneApi.meta
},
ariaDescription: {
get: (index) => elementOrListOrPlaneApi.meta.value?.[index]?.description || void 0,
watchSource: elementOrListOrPlaneApi.meta
},
ariaDescribedbys: {
get: (index) => toListValue(
[
toListValue(
elementOrListOrPlaneApi.meta.value?.[index]?.describedBy
),
toListValue(
elementOrListOrPlaneApi.meta.value?.[index]?.errorMessage
)
]
),
watchSource: elementOrListOrPlaneApi.meta
},
ariaDetails: {
get: (index) => toListValue(
elementOrListOrPlaneApi.meta.value?.[index]?.details
),
watchSource: elementOrListOrPlaneApi.meta
}
};
}
return {
ariaLabel: {
get: () => elementOrListOrPlaneApi.meta.value.label || void 0,
watchSource: elementOrListOrPlaneApi.meta
},
ariaLabelledbys: {
get: () => toListValue(
elementOrListOrPlaneApi.meta.value.labelledBy
),
watchSource: elementOrListOrPlaneApi.meta
},
ariaDescription: {
get: () => elementOrListOrPlaneApi.meta.value.description || void 0,
watchSource: elementOrListOrPlaneApi.meta
},
ariaDescribedbys: {
get: () => toListValue(
[
toListValue(
elementOrListOrPlaneApi.meta.value.describedBy
),
toListValue(
elementOrListOrPlaneApi.meta.value.errorMessage
)
]
),
watchSource: elementOrListOrPlaneApi.meta
},
ariaDetails: {
get: () => toListValue(
elementOrListOrPlaneApi.meta.value.details
),
watchSource: elementOrListOrPlaneApi.meta
}
};
}
function toListValue(idOrList) {
return Array.isArray(idOrList) ? toList(...idOrList) || void 0 : idOrList;
}
const toList = createList();
const toTokenList = join(" ");
const toComputedStyle = createComputedStyle();
function toLastPointeroutTarget(sequence) {
let lastPointerout, index = sequence.length - 1;
while (index >= 0) {
const event = sequence[index];
if (event.type === "pointerout") {
lastPointerout = event;
break;
}
index--;
}
return lastPointerout?.target;
}
function getId(instance) {
return instance ? (instance.appContext.config.idPrefix || "v") + "-" + instance.ids[0] + instance.ids[1]++ : useId();
}
const defaultOptions$r = {
predicateRenderedWatchSourcesChanged: () => true,
watchSources: []
};
function onPlaneRendered(plane, options = {}) {
const {
predicateRenderedWatchSourcesChanged,
planeEffect,
beforeItemEffects,
itemEffect,
afterItemEffects,
watchSources
} = { ...defaultOptions$r, ...options }, withGuards = (current, previous) => {
if (!current[0].length || !predicateRenderedWatchSourcesChanged(current, previous))
return;
planeEffect?.(current, previous);
if (itemEffect) {
beforeItemEffects?.();
for (let row = 0; row < plane.value.length; row++) {
for (let column = 0; column < plane.value[row].length; column++) {
const rendered = plane.value.get({ row, column });
itemEffect(rendered, { row, column });
}
}
afterItemEffects?.();
}
};
onMounted(() => {
if (!plane.value[0].length)
return;
withGuards(
[plane.value, ...watchSources.map((watchSource) => {
if (isRef(watchSource)) {
return watchSource.value;
}
if (typeof watchSource === "function") {
return watchSource();
}
return watchSource;
})],
[void 0, ...watchSources.map(() => void 0)]
);
});
return watch(
[plane, ...watchSources],
withGuards,
{ flush: "post" }
);
}
function onRendered(element, options = {}) {
const plane = narrowReactivePlane(element), { predicateRenderedWatchSourcesChanged, effect } = { ...defaultOptions$r, ...options };
return onPlaneRendered(
plane,
{
...options,
predicateRenderedWatchSourcesChanged: (current, previous) => predicateRenderedWatchSourcesChanged(
[current[0]?.get({ row: 0, column: 0 }), ...current.slice(1)],
[previous[0]?.get({ row: 0, column: 0 }), ...previous.slice(1)]
),
...effect && {
itemEffect: (rendered) => effect(rendered)
}
}
);
}
function onRenderedBind(elementOrListOrPlane, assign, remove, value, watchSources) {
const renderedKind = toRenderedKind(elementOrListOrPlane), elements = narrowReactivePlane(elementOrListOrPlane), narrowedWatchSources = narrowWatchSources(watchSources);
if (isRef(value)) {
return onPlaneRendered(
elements,
{
predicateRenderedWatchSourcesChanged,
itemEffect: (element, { row, column }) => {
if (!element)
return;
if (value.value === void 0) {
remove(element);
return;
}
assign(element, value.value, column, row);
},
watchSources: [value, ...narrowedWatchSources]
}
);
}
if (typeof value === "function") {
const get = value;
return onPlaneRendered(
elements,
{
predicateRenderedWatchSourcesChanged,
itemEffect: (element, { row, column }) => {
if (!element)
return;
const value2 = renderedKind === "plane" ? get({ row, column }) : renderedKind === "list" ? get(column) : get();
if (value2 === void 0) {
remove(element);
return;
}
assign(element, value2, column, row);
},
watchSources: narrowedWatchSources
// `get` is not used a watch source because it often needs arguments
}
);
}
return onPlaneRendered(
elements,
{
predicateRenderedWatchSourcesChanged,
itemEffect: (element, { row, column }) => {
if (!element)
return;
if (value === void 0) {
remove(element);
return;
}
assign(element, value, column, row);
},
watchSources: narrowedWatchSources
}
);
}
function bindAttributeOrProperty(elementOrListOrPlane, key, value, watchSources) {
const narrowedKey = narrowKey(key);
return onRenderedBind(
elementOrListOrPlane,
(element, value2) => {
if (shouldPerformPropertyEffect({ element, key: narrowedKey, value: value2 })) {
propertyEffect({ element, property: narrowedKey, value: value2 });
return;
}
attributeEffect({ element, attribute: narrowedKey, value: value2 });
},
(element) => {
if (shouldPerformPropertyEffect({ element, key: narrowedKey, value: void 0 })) {
return;
}
element.removeAttribute(narrowedKey);
},
value,
watchSources
);
}
function narrowKey(rawKey) {
switch (rawKey) {
case "for":
return "htmlFor";
case "allowfullscreen":
return "allowFullscreen";
case "formnovalidate":
return "formNoValidate";
case "ismap":
return "isMap";
case "nomodule":
return "noModule";
case "novalidate":
return "noValidate";
case "readonly":
return "readOnly";
default:
return /^(aria|data)[A-Z]/.test(rawKey) ? `${rawKey.slice(0, 4)}-${rawKey.slice(4).toLowerCase()}` : rawKey;
}
}
function shouldPerformPropertyEffect({ element, key, value }) {
if (key === "spellcheck" || key === "draggable") {
return false;
}
if (key === "form" && typeof value === "string") {
return false;
}
if (key === "list" && element.tagName === "INPUT") {
return false;
}
return key in element;
}
function propertyEffect({ element, property, value }) {
if (property === "value" && element.tagName !== "PROGRESS") {
const narrowedValue = value == null ? "" : value;
if (element.value === narrowedValue) {
return;
}
element.value = narrowedValue;
return;
}
if (typeof value === "string" && value === "" || value == null) {
const type = typeof element[property];
switch (type) {
case "boolean":
if (typeof value === "string" && value === "") {
element[property] = true;
return;
}
break;
case "string":
if (value == null) {
element[property] = "";
element.removeAttribute(property);
return;
}
break;
case "number":
element[property] = 0;
element.removeAttribute(property);
return;
}
}
element[property] = value;
}
const xlinkNS = "http://www.w3.org/1999/xlink";
function attributeEffect({ element, attribute, value }) {
if (element instanceof SVGElement && attribute.startsWith("xlink:")) {
if (value == null) {
element.removeAttributeNS(xlinkNS, attribute.slice(6, attribute.length));
}
element.setAttributeNS(xlinkNS, attribute, value);
return;
}
if (attribute === "itemscope") {
if (value == null || typeof value === "boolean" && value === false) {
element.removeAttribute(attribute);
return;
}
if (element.getAttribute(attribute) === "") {
return;
}
element.setAttribute(attribute, "");
return;
}
if (element.getAttribute(attribute) === value) {
return;
}
element.setAttribute(attribute, value);
}
function bindList(elementOrListOrPlane, list, value, watchSources) {
const cache = /* @__PURE__ */ new WeakMap();
return onRenderedBind(
elementOrListOrPlane,
(element, value2) => {
if (list === "class" || list === "rel") {
const domTokenList = element[`${list}List`];
if (domTokenList.contains(value2))
return;
const cached = cache.get(element) || "";
domTokenList.remove(...toListStrings(cached));
domTokenList.add(...toListStrings(value2));
cache.set(element, value2);
return;
}
const attribute = toAttribute(list);
pipe(
() => element.getAttribute(attribute),
toListStrings,
createConcat(value2.split(" ")),
unique(),
join(" "),
(ids) => element.setAttribute(attribute, ids)
)();
},
() => {
},
value,
watchSources
);
}
function toListStrings(value) {
return (value || "").split(" ").filter((string) => string);
}
const re = /aria-?(\w+)s$/;
function toAttribute(list) {
return `aria-${list.match(re)[1]}`.toLowerCase();
}
function bindStyle(elementOrListOrPlane, property, value, watchSources) {
return onRenderedBind(
elementOrListOrPlane,
(element, value2) => {
if (element.style[property] === value2) {
return;
}
element.style[property] = value2;
},
(element) => {
element.style[property] = "";
},
value,
watchSources
);
}
function ariaHiddenFocusManage({
root,
list,
selectedItems
}) {
on(
root,
{
// When focus leaves the selected item, make sure it can't move into an unselected item.
focusout: (event) => {
if (list.value[selectedItems.newest].contains(event.relatedTarget) || !some((item) => item.contains(event.target))(list.value))
return;
const relatedTargetIndex = findIndex(
(item) => item.contains(event.relatedTarget)
)(list.value);
if (relatedTargetIndex === -1)
return;
const relativeIndex = relatedTargetIndex - selectedItems.newest;
if (relativeIndex <= 0) {
createFocusable("previous")(list.value[0])?.focus();
return;
}
pipe(
at(-1),
(item) => createFocusable("last")(item) || item,
createFocusable("next"),
(el) => el?.focus()
)(list.value);
},
/**
* When focus moves into an unselected item, move it to the selected item.
*
* WARNING: This function will not work if unselected items _do_ contain focusable
* elements but the selected item does _not_ contain focusable elements. We don't
* currently have this use case, but if we ever do, we can adjust this function.
*/
focusin: (event) => {
if (list.value[selectedItems.newest].contains(event.target) || !some((item) => item.contains(event.target))(list.value))
return;
pipe(
at(selectedItems.newest),
createFocusable("first", { predicatesElement: true }),
(el) => el?.focus()
)(list.value);
}
}
);
}
function popupList({
controllerApis,
popupApi,
popup,
getEscShouldClose,
receivesFocus
}) {
const [primaryControllerApi] = controllerApis;
for (const controllerApi of controllerApis) {
bind(
controllerApi.element,
{
ariaExpanded: computed(() => `${popup.is.opened()}`),
ariaControls: computed(() => popup.is.opened() ? popupApi.id.value : void 0)
}
);
on(
controllerApi.element,
{
focusout: (event) => {
if (popup.is.closed() || some(
(controllerApi2) => controllerApi2.element.value.contains(event.relatedTarget)
)(controllerApis) || popupApi.element.value.contains(event.relatedTarget))
return;
popup.close();
},
keydown: (event) => {
if (popup.is.closed() && (predicateDown(event) || predicateUp(event))) {
popup.open();
if (receivesFocus)
return;
primaryControllerApi.element.value.focus();
return;
}
if (popup.is.opened() && predicateEsc(event)) {
popup.close();
return;
}
}
}
);
}
if (receivesFocus) {
on(
popupApi.element,
{
keydown: (event) => {
if (!predicateEsc(event) || !getEscShouldClose())
return;
event.preventDefault();
popup.close();
if (popup.is.removed()) {
primaryControllerApi.element.value.focus();
return;
}
const stop = watch(
() => popup.is.removed(),
(is) => {
if (!is)
return;
stop();
primaryControllerApi.element.value.focus();
}
);
}
}
);
on(
popupApi.element,
{
focusout: (event) => {
if (some(
(controllerApi) => controllerApi.element.value.contains(event.relatedTarget)
)(controllerApis) || popupApi.element.value.contains(event.relatedTarget))
return;
if (event.relatedTarget === createFocusable("previous")(popupApi.element.value)) {
event.preventDefault();
primaryControllerApi.element.value.focus();
return;
}
popup.close();
}
}
);
}
}
function createToNextEligible({ api }) {
return ({
coordinates: { row, column },
toEligibility,
loops,
direction
}) => {
if (api.plane.value.length === 0 || api.plane.value[0].length === 0)
return "none";
const { row: rowLimit, column: columnLimit } = (() => {
if (!loops)
return {
row: api.plane.value.length - 1,
column: api.plane.value[0].length - 1
};
switch (direction) {
case "vertical":
return {
row: row < 1 ? api.plane.value.length - 1 : row - 1,
column: row < 1 && column < 1 ? api.plane.value[0].length - 1 : Math.max(column, 0)
};
case "horizontal":
return {
row: row < 1 && column < 1 ? api.plane.value.length - 1 : Math.max(row, 0),
column: column < 1 ? api.plane.value[0].length - 1 : column - 1
};
}
})(), r = new Navigateable(api.plane.value).navigate(
direction === "horizontal" ? Math.max(row, 0) : row,
{ allow: "any" }
), c = new Navigateable(api.plane.value[0]).navigate(
direction === "vertical" ? Math.max(column, 0) : column,
{ allow: "any" }
);
let nextEligible = "none", didReachLimit = r.location === r.array.length - 1 && c.location === c.array.length - 1 && !loops;
switch (direction) {
case "vertical":
while (nextEligible === "none" && !didReachLimit) {
let didReachRowLimit = false;
while (nextEligible === "none" && !didReachRowLimit) {
r.next({ loops });
didReachRowLimit = r.location === rowLimit;
const eligibility = toEligibility({ row: r.location, column: c.location });
if (eligibility === "eligible") {
nextEligible = { row: r.location, column: c.location };
break;
}
}
c.next({ loops });
didReachLimit = didReachRowLimit && c.location === columnLimit;
r.navigate(-1, { allow: "any" });
}
break;
case "horizontal":
while (nextEligible === "none" && !didReachLimit) {
let didReachColumnLimit = false;
while (nextEligible === "none" && !didReachColumnLimit) {
c.next({ loops });
didReachColumnLimit = c.location === columnLimit;
const eligibility = toEligibility({ row: r.location, column: c.location });
if (eligibility === "eligible") {
nextEligible = { row: r.location, column: c.location };
break;
}
}
r.next({ loops });
didReachLimit = didReachColumnLimit && r.location === rowLimit;
c.navigate(-1, { allow: "any" });
}
break;
}
return nextEligible;
};
}
function createToPreviousEligible({ api }) {
return ({
coordinates: { row, column },
toEligibility,
loops,
direction
}) => {
if (api.plane.value.length === 0 || api.plane.value[0].length === 0)
return "none";
const [rowLimit, columnLimit] = (() => {
if (!loops)
return [0, 0];
switch (direction) {
case "vertical":
return [
row > api.plane.value.length - 2 ? 0 : row + 1,
row > api.plane.value.length - 2 && column > api.plane.value[0].length - 2 ? 0 : Math.min(column, api.plane.value[0].length - 1)
];
case "horizontal":
return [
row > api.plane.value.length - 2 && column > api.plane.value[0].length - 2 ? 0 : Math.min(row, api.plane.value.length - 1),
column > api.plane.value[0].length - 2 ? 0 : column + 1
];
}
})(), r = new Navigateable(api.plane.value).navigate(
direction === "horizontal" ? Math.min(row, api.plane.value.length - 1) : row,
{ allow: "any" }
), c = new Navigateable(api.plane.value[0]).navigate(
direction === "vertical" ? Math.min(column, api.plane.value[0].length - 1) : column,
{ allow: "any" }
);
let previousEligible = "none", didReachLimit = r.location === 0 && c.location === 0 && !loops;
switch (direction) {
case "vertical":
while (previousEligible === "none" && !didReachLimit) {
let didReachRowLimit = false;
while (previousEligible === "none" && !didReachRowLimit) {
r.previous({ loops });
didReachRowLimit = r.location === rowLimit;
const eligibility = toEligibility({ row: r.location, column: c.location });
if (eligibility === "eligible") {
previousEligible = { row: r.location, column: c.location };
break;
}
}
c.previous({ loops });
didReachLimit = didReachRowLimit && c.location === columnLimit;
r.navigate(api.plane.value.length, { allow: "any" });
}
break;
case "horizontal":
while (previousEligible === "none" && !didReachLimit) {
let didReachColumnLimit = false;
while (previousEligible === "none" && !didReachColumnLimit) {
c.previous({ loops });
didReachColumnLimit = c.location === columnLimit;
const eligibility = toEligibility({ row: r.location, column: c.location });
if (eligibility === "eligible") {
previousEligible = { row: r.location, column: c.location };
break;
}
}
r.previous({ loops });
didReachLimit = didReachColumnLimit &