Bug: T362650 Bug: T370057 Bug: T371118 Bug: T374659 Bug: T374882 Bug: T375699 Bug: T375700 Bug: T376024 Bug: T376622 Change-Id: I18684997862a1a6427627d8061a2583fff00e621
10573 lines
337 KiB
JavaScript
10573 lines
337 KiB
JavaScript
var __defProp = Object.defineProperty;
|
||
var __defProps = Object.defineProperties;
|
||
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
||
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
||
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
||
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
||
var __spreadValues = (a, b) => {
|
||
for (var prop in b || (b = {}))
|
||
if (__hasOwnProp.call(b, prop))
|
||
__defNormalProp(a, prop, b[prop]);
|
||
if (__getOwnPropSymbols)
|
||
for (var prop of __getOwnPropSymbols(b)) {
|
||
if (__propIsEnum.call(b, prop))
|
||
__defNormalProp(a, prop, b[prop]);
|
||
}
|
||
return a;
|
||
};
|
||
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
||
var __objRest = (source, exclude) => {
|
||
var target = {};
|
||
for (var prop in source)
|
||
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
|
||
target[prop] = source[prop];
|
||
if (source != null && __getOwnPropSymbols)
|
||
for (var prop of __getOwnPropSymbols(source)) {
|
||
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
|
||
target[prop] = source[prop];
|
||
}
|
||
return target;
|
||
};
|
||
var __publicField = (obj, key, value) => {
|
||
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
||
return value;
|
||
};
|
||
var __async = (__this, __arguments, generator) => {
|
||
return new Promise((resolve, reject) => {
|
||
var fulfilled = (value) => {
|
||
try {
|
||
step(generator.next(value));
|
||
} catch (e) {
|
||
reject(e);
|
||
}
|
||
};
|
||
var rejected = (value) => {
|
||
try {
|
||
step(generator.throw(value));
|
||
} catch (e) {
|
||
reject(e);
|
||
}
|
||
};
|
||
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
||
step((generator = generator.apply(__this, __arguments)).next());
|
||
});
|
||
};
|
||
import { ref, onMounted, defineComponent, computed, openBlock, createElementBlock, normalizeClass, toDisplayString, createCommentVNode, Comment, warn, watch, withKeys, withModifiers, renderSlot, resolveComponent, createElementVNode, createBlock, resolveDynamicComponent, withCtx, createVNode, toRef, Fragment, renderList, createTextVNode, Transition, normalizeStyle, inject, mergeProps, getCurrentInstance, withDirectives, vModelCheckbox, createSlots, nextTick, vModelDynamic, onUnmounted, vShow, unref, shallowRef, getCurrentScope, onScopeDispose, shallowReadonly, Teleport, toRefs, provide, vModelRadio, vModelText } from "vue";
|
||
const g = '<path d="M11.53 2.3A1.85 1.85 0 0010 1.21 1.85 1.85 0 008.48 2.3L.36 16.36C-.48 17.81.21 19 1.88 19h16.24c1.67 0 2.36-1.19 1.52-2.64zM11 16H9v-2h2zm0-4H9V6h2z"/>', q = '<path d="M12.43 14.34A5 5 0 0110 15a5 5 0 113.95-2L17 16.09V3a2 2 0 00-2-2H5a2 2 0 00-2 2v14a2 2 0 002 2h10a2 2 0 001.45-.63z"/><circle cx="10" cy="10" r="3"/>', s1 = '<path d="M7 14.17 2.83 10l-1.41 1.41L7 17 19 5l-1.41-1.42z"/>', n1 = '<path d="M10 0a10 10 0 1010 10A10 10 0 0010 0m5.66 14.24-1.41 1.41L10 11.41l-4.24 4.25-1.42-1.42L8.59 10 4.34 5.76l1.42-1.42L10 8.59l4.24-4.24 1.41 1.41L11.41 10z"/>', d1 = '<path d="m4.34 2.93 12.73 12.73-1.41 1.41L2.93 4.35z"/><path d="M17.07 4.34 4.34 17.07l-1.41-1.41L15.66 2.93z"/>', g1 = '<path d="M10 15 2 5h16z"/>', k1 = '<path d="M13.728 1H6.272L1 6.272v7.456L6.272 19h7.456L19 13.728V6.272zM11 15H9v-2h2zm0-4H9V5h2z"/>', F1 = '<path d="m17.5 4.75-7.5 7.5-7.5-7.5L1 6.25l9 9 9-9z"/>', t0 = '<path d="M19 3H1v14h18zM3 14l3.5-4.5 2.5 3L12.5 8l4.5 6z"/><path d="M19 5H1V3h18zm0 12H1v-2h18z"/>', p = '<path d="M8 19a1 1 0 001 1h2a1 1 0 001-1v-1H8zm9-12a7 7 0 10-12 4.9S7 14 7 15v1a1 1 0 001 1h4a1 1 0 001-1v-1c0-1 2-3.1 2-3.1A7 7 0 0017 7"/>', e0 = '<path d="M10 0C4.477 0 0 4.477 0 10s4.477 10 10 10 10-4.477 10-10S15.523 0 10 0M9 5h2v2H9zm0 4h2v6H9z"/>', z2 = '<path d="M3 1h2v18H3zm13.5 1.5L15 1l-9 9 9 9 1.5-1.5L9 10z"/>', i2 = '<path d="M15 1h2v18h-2zM3.5 2.5 11 10l-7.5 7.5L5 19l9-9-9-9z"/>', V2 = '<path d="M7 1 5.6 2.5 13 10l-7.4 7.5L7 19l9-9z"/>', b2 = '<path d="m4 10 9 9 1.4-1.5L7 10l7.4-7.5L13 1z"/>', J2 = '<path d="M12.2 13.6a7 7 0 111.4-1.4l5.4 5.4-1.4 1.4zM3 8a5 5 0 1010 0A5 5 0 003 8"/>', h5 = '<path d="M10 0 3 8h14zm0 18-7-8h14z"/>', M5 = '<path d="M10 20a10 10 0 010-20 10 10 0 110 20m-2-5 9-8.5L15.5 5 8 12 4.5 8.5 3 10z"/>', G5 = '<path d="m10 5 8 10H2z"/>', M3 = g, S3 = q, K3 = s1, Y3 = n1, X3 = d1, l4 = g1, i4 = k1, m4 = F1, P4 = t0, Y4 = {
|
||
langCodeMap: {
|
||
ar: p
|
||
},
|
||
default: e0
|
||
}, P6 = {
|
||
ltr: z2,
|
||
shouldFlip: true
|
||
}, N6 = {
|
||
ltr: i2,
|
||
shouldFlip: true
|
||
}, j6 = {
|
||
ltr: V2,
|
||
shouldFlip: true
|
||
}, d7 = {
|
||
ltr: b2,
|
||
shouldFlip: true
|
||
}, A7 = J2, T7 = h5, O7 = M5, x8 = G5;
|
||
function N8(c, s, h) {
|
||
if (typeof c == "string" || "path" in c)
|
||
return c;
|
||
if ("shouldFlip" in c)
|
||
return c.ltr;
|
||
if ("rtl" in c)
|
||
return h === "rtl" ? c.rtl : c.ltr;
|
||
const n = s in c.langCodeMap ? c.langCodeMap[s] : c.default;
|
||
return typeof n == "string" || "path" in n ? n : n.ltr;
|
||
}
|
||
function O8(c, s) {
|
||
if (typeof c == "string")
|
||
return false;
|
||
if ("langCodeMap" in c) {
|
||
const h = s in c.langCodeMap ? c.langCodeMap[s] : c.default;
|
||
if (typeof h == "string")
|
||
return false;
|
||
c = h;
|
||
}
|
||
if ("shouldFlipExceptions" in c && Array.isArray(c.shouldFlipExceptions)) {
|
||
const h = c.shouldFlipExceptions.indexOf(s);
|
||
return h === void 0 || h === -1;
|
||
}
|
||
return "shouldFlip" in c ? c.shouldFlip : false;
|
||
}
|
||
function useComputedDirection(root) {
|
||
const computedDir = ref(null);
|
||
onMounted(() => {
|
||
const dir = window.getComputedStyle(root.value).direction;
|
||
computedDir.value = dir === "ltr" || dir === "rtl" ? dir : null;
|
||
});
|
||
return computedDir;
|
||
}
|
||
function useComputedLanguage(root) {
|
||
const computedLang = ref("");
|
||
onMounted(() => {
|
||
let ancestor = root.value;
|
||
while (ancestor && ancestor.lang === "") {
|
||
ancestor = ancestor.parentElement;
|
||
}
|
||
computedLang.value = ancestor ? ancestor.lang : null;
|
||
});
|
||
return computedLang;
|
||
}
|
||
function makeStringTypeValidator(allowedValues) {
|
||
return (s) => typeof s === "string" && allowedValues.indexOf(s) !== -1;
|
||
}
|
||
const LibraryPrefix = "cdx";
|
||
const ButtonActions = [
|
||
"default",
|
||
"progressive",
|
||
"destructive"
|
||
];
|
||
const ButtonWeights = [
|
||
"normal",
|
||
"primary",
|
||
"quiet"
|
||
];
|
||
const ButtonSizes = [
|
||
"medium",
|
||
"large"
|
||
];
|
||
const IconSizes = [
|
||
"x-small",
|
||
"small",
|
||
"medium"
|
||
];
|
||
const StatusTypes = [
|
||
"notice",
|
||
"warning",
|
||
"error",
|
||
"success"
|
||
];
|
||
const statusTypeValidator = makeStringTypeValidator(StatusTypes);
|
||
const TextInputTypes = [
|
||
"text",
|
||
"search",
|
||
"number",
|
||
"email",
|
||
"month",
|
||
"password",
|
||
"tel",
|
||
"url",
|
||
"week",
|
||
"date",
|
||
"datetime-local",
|
||
"time"
|
||
];
|
||
const ValidationStatusTypes = [
|
||
"default",
|
||
"warning",
|
||
"error",
|
||
"success"
|
||
];
|
||
const TableTextAlignments = [
|
||
"start",
|
||
"center",
|
||
"end",
|
||
// Numbers should be aligned to the right in all reading directionalities.
|
||
"number"
|
||
];
|
||
const DebounceInterval = 120;
|
||
const PendingDelay = 500;
|
||
const MenuFooterValue = "cdx-menu-footer-item";
|
||
const TabsKey = Symbol("CdxTabs");
|
||
const ActiveTabKey = Symbol("CdxActiveTab");
|
||
const AllowArbitraryKey = Symbol("CdxAllowArbitrary");
|
||
const FieldInputIdKey = Symbol("CdxFieldInputId");
|
||
const FieldDescriptionIdKey = Symbol("CdxFieldDescriptionId");
|
||
const FieldStatusKey = Symbol("CdxFieldStatus");
|
||
const DisabledKey = Symbol("CdxDisabled");
|
||
const NoInvertClass = "".concat(LibraryPrefix, "-no-invert");
|
||
const TableRowIdentifier = Symbol("CdxTableRowIdentifier");
|
||
const TablePaginationPositions = [
|
||
"top",
|
||
"bottom",
|
||
"both"
|
||
];
|
||
const iconSizeValidator = makeStringTypeValidator(IconSizes);
|
||
const _sfc_main$y = defineComponent({
|
||
name: "CdxIcon",
|
||
props: {
|
||
/** The SVG path or an object containing that path plus other data. */
|
||
icon: {
|
||
type: [String, Object],
|
||
required: true
|
||
},
|
||
/**
|
||
* Accessible label for the icon. If not included, the icon will be hidden from screen
|
||
* readers via `aria-hidden="true"`. Browsers also display this label as a tooltip when the
|
||
* user hovers over the icon. Note that this label is not rendered as visible text next
|
||
* to the icon.
|
||
*/
|
||
iconLabel: {
|
||
type: String,
|
||
default: ""
|
||
},
|
||
/**
|
||
* Explicitly set the language code to use for the icon. See
|
||
* https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/lang.
|
||
* Defaults to the lang attribute of the nearest ancestor at mount time.
|
||
*/
|
||
lang: {
|
||
type: String,
|
||
default: null
|
||
},
|
||
/**
|
||
* Explicitly set the direction to use for the icon. See
|
||
* https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dir.
|
||
* Defaults to the computed direction at mount time.
|
||
*/
|
||
dir: {
|
||
type: String,
|
||
default: null
|
||
},
|
||
/**
|
||
* Specify icon size by choosing one of several pre-defined size
|
||
* options. See the type documentation for supported size options.
|
||
* The `medium` size is used by default if no size prop is provided.
|
||
*/
|
||
size: {
|
||
type: String,
|
||
default: "medium",
|
||
validator: iconSizeValidator
|
||
}
|
||
},
|
||
setup(props) {
|
||
const rootElement = ref();
|
||
const computedDir = useComputedDirection(rootElement);
|
||
const computedLang = useComputedLanguage(rootElement);
|
||
const overriddenDir = computed(() => {
|
||
var _a;
|
||
return (_a = props.dir) != null ? _a : computedDir.value;
|
||
});
|
||
const overriddenLang = computed(() => {
|
||
var _a;
|
||
return (_a = props.lang) != null ? _a : computedLang.value;
|
||
});
|
||
const rootClasses = computed(() => {
|
||
return {
|
||
"cdx-icon--flipped": overriddenDir.value === "rtl" && overriddenLang.value !== null && O8(props.icon, overriddenLang.value),
|
||
["cdx-icon--".concat(props.size)]: true
|
||
};
|
||
});
|
||
const resolvedIcon = computed(
|
||
() => {
|
||
var _a, _b;
|
||
return N8(props.icon, (_a = overriddenLang.value) != null ? _a : "", (_b = overriddenDir.value) != null ? _b : "ltr");
|
||
}
|
||
);
|
||
const iconSvg = computed(() => typeof resolvedIcon.value === "string" ? resolvedIcon.value : "");
|
||
const iconPath = computed(() => typeof resolvedIcon.value !== "string" ? resolvedIcon.value.path : "");
|
||
return {
|
||
rootElement,
|
||
rootClasses,
|
||
iconSvg,
|
||
iconPath
|
||
};
|
||
}
|
||
});
|
||
const _export_sfc = (sfc, props) => {
|
||
const target = sfc.__vccOpts || sfc;
|
||
for (const [key, val] of props) {
|
||
target[key] = val;
|
||
}
|
||
return target;
|
||
};
|
||
const _hoisted_1$v = ["aria-hidden"];
|
||
const _hoisted_2$m = { key: 0 };
|
||
const _hoisted_3$e = ["innerHTML"];
|
||
const _hoisted_4$9 = ["d"];
|
||
function _sfc_render$y(_ctx, _cache, $props, $setup, $data, $options) {
|
||
return openBlock(), createElementBlock(
|
||
"span",
|
||
{
|
||
ref: "rootElement",
|
||
class: normalizeClass(["cdx-icon", _ctx.rootClasses])
|
||
},
|
||
[
|
||
(openBlock(), createElementBlock("svg", {
|
||
xmlns: "http://www.w3.org/2000/svg",
|
||
"xmlns:xlink": "http://www.w3.org/1999/xlink",
|
||
width: "20",
|
||
height: "20",
|
||
viewBox: "0 0 20 20",
|
||
"aria-hidden": _ctx.iconLabel ? void 0 : true
|
||
}, [
|
||
_ctx.iconLabel ? (openBlock(), createElementBlock(
|
||
"title",
|
||
_hoisted_2$m,
|
||
toDisplayString(_ctx.iconLabel),
|
||
1
|
||
/* TEXT */
|
||
)) : createCommentVNode("v-if", true),
|
||
_ctx.iconSvg ? (openBlock(), createElementBlock("g", {
|
||
key: 1,
|
||
innerHTML: _ctx.iconSvg
|
||
}, null, 8, _hoisted_3$e)) : (openBlock(), createElementBlock("path", {
|
||
key: 2,
|
||
d: _ctx.iconPath
|
||
}, null, 8, _hoisted_4$9))
|
||
], 8, _hoisted_1$v))
|
||
],
|
||
2
|
||
/* CLASS */
|
||
);
|
||
}
|
||
const CdxIcon = /* @__PURE__ */ _export_sfc(_sfc_main$y, [["render", _sfc_render$y]]);
|
||
function flattenSlotContents(slotContents) {
|
||
const flattened = [];
|
||
for (const node of slotContents) {
|
||
if (
|
||
// HTML tag
|
||
typeof node.type === "string" || // Component
|
||
typeof node.type === "object"
|
||
) {
|
||
flattened.push(node);
|
||
} else if (node.type !== Comment) {
|
||
if (typeof node.children === "string" && node.children.trim() !== "") {
|
||
flattened.push(node.children);
|
||
} else if (Array.isArray(node.children)) {
|
||
flattened.push(...flattenSlotContents(node.children));
|
||
}
|
||
}
|
||
}
|
||
return flattened;
|
||
}
|
||
function isComponentVNode(node, componentName) {
|
||
if (typeof node.type === "object" && "name" in node.type) {
|
||
if (componentName !== void 0) {
|
||
return node.type.name === componentName;
|
||
}
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
function isTagVNode(node, tagName) {
|
||
if (typeof node.type === "string") {
|
||
{
|
||
return node.type === tagName.toLowerCase();
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
function useSlotContents(slot) {
|
||
const slotContents = typeof slot === "function" ? slot() : slot;
|
||
return slotContents ? flattenSlotContents(slotContents) : [];
|
||
}
|
||
function useWarnOnce(shouldWarn, message) {
|
||
if (shouldWarn()) {
|
||
warn(message);
|
||
return;
|
||
}
|
||
const stop = watch(shouldWarn, (newValue) => {
|
||
if (newValue) {
|
||
warn(message);
|
||
stop();
|
||
}
|
||
});
|
||
}
|
||
function useIconOnlyButton(slot, attrs, componentName) {
|
||
const isIconOnly = computed(() => {
|
||
const slotContents = useSlotContents(slot);
|
||
if (slotContents.length !== 1) {
|
||
return false;
|
||
}
|
||
const soleNode = slotContents[0];
|
||
if (typeof soleNode === "object" && (isComponentVNode(soleNode, "CdxIcon") || isTagVNode(soleNode, "svg"))) {
|
||
return true;
|
||
}
|
||
return false;
|
||
});
|
||
useWarnOnce(
|
||
() => isIconOnly.value && !attrs["aria-label"] && !attrs["aria-hidden"],
|
||
"".concat(componentName, ": Icon-only buttons require one of the following attributes: aria-label or aria-hidden. See documentation at https://doc.wikimedia.org/codex/latest/components/demos/button.html#icon-only-button")
|
||
);
|
||
return isIconOnly;
|
||
}
|
||
const buttonActionValidator = makeStringTypeValidator(ButtonActions);
|
||
const buttonWeightValidator = makeStringTypeValidator(ButtonWeights);
|
||
const buttonSizeValidator = makeStringTypeValidator(ButtonSizes);
|
||
const _sfc_main$x = defineComponent({
|
||
name: "CdxButton",
|
||
props: {
|
||
/**
|
||
* The kind of action that will be taken on click.
|
||
*
|
||
* @values 'default', 'progressive', 'destructive'
|
||
*/
|
||
action: {
|
||
type: String,
|
||
default: "default",
|
||
validator: buttonActionValidator
|
||
},
|
||
/**
|
||
* Visual prominence of the button.
|
||
*
|
||
* @values 'normal', 'primary', 'quiet'
|
||
*/
|
||
weight: {
|
||
type: String,
|
||
default: "normal",
|
||
validator: buttonWeightValidator
|
||
},
|
||
/**
|
||
* Button size.
|
||
*
|
||
* Most buttons should use the default medium size. In rare cases the large size should
|
||
* be used, for example to make icon-only buttons larger on touchscreens.
|
||
*
|
||
* @values 'medium', 'large'
|
||
*/
|
||
size: {
|
||
type: String,
|
||
default: "medium",
|
||
validator: buttonSizeValidator
|
||
}
|
||
},
|
||
emits: ["click"],
|
||
setup(props, { emit, slots, attrs }) {
|
||
const button = ref();
|
||
const isIconOnly = useIconOnlyButton(slots.default, attrs, "CdxButton");
|
||
const isActive = ref(false);
|
||
const rootClasses = computed(() => ({
|
||
["cdx-button--action-".concat(props.action)]: true,
|
||
["cdx-button--weight-".concat(props.weight)]: true,
|
||
["cdx-button--size-".concat(props.size)]: true,
|
||
"cdx-button--framed": props.weight !== "quiet",
|
||
"cdx-button--icon-only": isIconOnly.value,
|
||
"cdx-button--is-active": isActive.value
|
||
}));
|
||
const onClick = (event) => {
|
||
emit("click", event);
|
||
};
|
||
const setActive = (active) => {
|
||
isActive.value = active;
|
||
};
|
||
function onKeyDown() {
|
||
setActive(true);
|
||
}
|
||
function onKeyUp() {
|
||
var _a;
|
||
setActive(false);
|
||
(_a = button.value) == null ? void 0 : _a.click();
|
||
}
|
||
return {
|
||
button,
|
||
rootClasses,
|
||
onClick,
|
||
onKeyDown,
|
||
onKeyUp
|
||
};
|
||
}
|
||
});
|
||
function _sfc_render$x(_ctx, _cache, $props, $setup, $data, $options) {
|
||
return openBlock(), createElementBlock(
|
||
"button",
|
||
{
|
||
ref: "button",
|
||
class: normalizeClass(["cdx-button", _ctx.rootClasses]),
|
||
onKeydown: _cache[0] || (_cache[0] = withKeys(withModifiers((...args) => _ctx.onKeyDown && _ctx.onKeyDown(...args), ["prevent"]), ["space", "enter"])),
|
||
onKeyup: _cache[1] || (_cache[1] = withKeys((...args) => _ctx.onKeyUp && _ctx.onKeyUp(...args), ["space", "enter"])),
|
||
onClick: _cache[2] || (_cache[2] = (...args) => _ctx.onClick && _ctx.onClick(...args))
|
||
},
|
||
[
|
||
renderSlot(_ctx.$slots, "default")
|
||
],
|
||
34
|
||
/* CLASS, NEED_HYDRATION */
|
||
);
|
||
}
|
||
const CdxButton = /* @__PURE__ */ _export_sfc(_sfc_main$x, [["render", _sfc_render$x]]);
|
||
const _sfc_main$w = defineComponent({
|
||
name: "CdxAccordion",
|
||
components: { CdxButton, CdxIcon },
|
||
props: {
|
||
/**
|
||
* Forces the accordion to show the action icon.
|
||
*
|
||
* @values 'true', 'false'
|
||
*/
|
||
actionAlwaysVisible: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* The icon that will be displayed on the right side of the accordion header when expanded.
|
||
*
|
||
*/
|
||
actionIcon: {
|
||
type: [String, Object],
|
||
default: null
|
||
},
|
||
/**
|
||
* Label for the action button. If an action icon is being used, then a label for that icon
|
||
* should be provided for ARIA support.
|
||
*/
|
||
actionButtonLabel: {
|
||
type: String,
|
||
default: ""
|
||
},
|
||
/**
|
||
* The heading level of the accordion title.
|
||
*
|
||
* @values 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'
|
||
*/
|
||
headingLevel: {
|
||
type: String,
|
||
default: "h3"
|
||
}
|
||
},
|
||
emits: [
|
||
/**
|
||
* When the action button is clicked.
|
||
*
|
||
*/
|
||
"action-button-click"
|
||
],
|
||
setup(props, { attrs, emit }) {
|
||
const isExpanded = ref("open" in attrs);
|
||
const emitActionButtonClick = () => {
|
||
emit("action-button-click");
|
||
};
|
||
const onToggle = (e) => {
|
||
isExpanded.value = e.newState === "open";
|
||
};
|
||
const shouldShowActionButton = computed(() => {
|
||
return props.actionIcon && (isExpanded.value || props.actionAlwaysVisible);
|
||
});
|
||
const rootClasses = computed(() => ({
|
||
"cdx-accordion--has-icon": shouldShowActionButton.value
|
||
}));
|
||
return {
|
||
emitActionButtonClick,
|
||
rootClasses,
|
||
shouldShowActionButton,
|
||
onToggle
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$u = { class: "cdx-accordion__header__title" };
|
||
const _hoisted_2$l = { class: "cdx-accordion__header__description" };
|
||
function _sfc_render$w(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_icon = resolveComponent("cdx-icon");
|
||
const _component_cdx_button = resolveComponent("cdx-button");
|
||
return openBlock(), createElementBlock(
|
||
"details",
|
||
{
|
||
class: normalizeClass(["cdx-accordion", _ctx.rootClasses]),
|
||
onToggle: _cache[1] || (_cache[1] = (...args) => _ctx.onToggle && _ctx.onToggle(...args))
|
||
},
|
||
[
|
||
createElementVNode("summary", null, [
|
||
(openBlock(), createBlock(resolveDynamicComponent(_ctx.headingLevel), { class: "cdx-accordion__header" }, {
|
||
default: withCtx(() => [
|
||
createElementVNode("span", _hoisted_1$u, [
|
||
renderSlot(_ctx.$slots, "title")
|
||
]),
|
||
createElementVNode("span", _hoisted_2$l, [
|
||
renderSlot(_ctx.$slots, "description")
|
||
])
|
||
]),
|
||
_: 3
|
||
/* FORWARDED */
|
||
})),
|
||
_ctx.shouldShowActionButton ? (openBlock(), createBlock(_component_cdx_button, {
|
||
key: 0,
|
||
class: "cdx-accordion__action",
|
||
"aria-label": _ctx.actionButtonLabel,
|
||
type: "button",
|
||
weight: "quiet",
|
||
onClick: withModifiers(_ctx.emitActionButtonClick, ["stop"])
|
||
}, {
|
||
default: withCtx(() => [
|
||
createVNode(_component_cdx_icon, {
|
||
icon: _ctx.actionIcon,
|
||
"icon-label": _ctx.actionButtonLabel,
|
||
size: "medium"
|
||
}, null, 8, ["icon", "icon-label"])
|
||
]),
|
||
_: 1
|
||
/* STABLE */
|
||
}, 8, ["aria-label", "onClick"])) : createCommentVNode("v-if", true)
|
||
]),
|
||
createElementVNode("div", {
|
||
class: "cdx-accordion__content",
|
||
onClick: _cache[0] || (_cache[0] = withModifiers(() => {
|
||
}, ["stop"]))
|
||
}, [
|
||
renderSlot(_ctx.$slots, "default")
|
||
])
|
||
],
|
||
34
|
||
/* CLASS, NEED_HYDRATION */
|
||
);
|
||
}
|
||
const Accordion = /* @__PURE__ */ _export_sfc(_sfc_main$w, [["render", _sfc_render$w]]);
|
||
function getButtonLabel(button) {
|
||
if (button.label === void 0) {
|
||
return button.value;
|
||
}
|
||
if (button.label === null) {
|
||
return "";
|
||
}
|
||
return button.label;
|
||
}
|
||
function useButtonGroupKeyboardNav(buttonsProp) {
|
||
const rootElement = ref();
|
||
const focusedButtonIndex = ref();
|
||
const buttonRefs = ref(/* @__PURE__ */ new Map());
|
||
const currentDirection = useComputedDirection(rootElement);
|
||
function assignTemplateRef(templateRef, index) {
|
||
const button = templateRef;
|
||
if (button) {
|
||
buttonRefs.value.set(index, button);
|
||
}
|
||
}
|
||
function onFocus(index) {
|
||
focusedButtonIndex.value = index;
|
||
}
|
||
function onBlur() {
|
||
focusedButtonIndex.value = void 0;
|
||
}
|
||
function focusNonDisabled(index, increment) {
|
||
var _a;
|
||
const newIndex = index + increment;
|
||
const targetButton = buttonsProp.value[newIndex];
|
||
if (targetButton) {
|
||
if (targetButton.disabled) {
|
||
focusNonDisabled(newIndex, increment);
|
||
} else {
|
||
const buttonElement = (_a = buttonRefs.value.get(newIndex)) == null ? void 0 : _a.$el;
|
||
buttonElement == null ? void 0 : buttonElement.focus();
|
||
}
|
||
}
|
||
}
|
||
function next() {
|
||
var _a;
|
||
focusNonDisabled((_a = focusedButtonIndex.value) != null ? _a : -1, 1);
|
||
}
|
||
function prev() {
|
||
var _a;
|
||
focusNonDisabled((_a = focusedButtonIndex.value) != null ? _a : buttonsProp.value.length, -1);
|
||
}
|
||
function moveRight() {
|
||
if (currentDirection.value === "rtl") {
|
||
prev();
|
||
} else {
|
||
next();
|
||
}
|
||
}
|
||
function moveLeft() {
|
||
if (currentDirection.value === "rtl") {
|
||
next();
|
||
} else {
|
||
prev();
|
||
}
|
||
}
|
||
function onKeydown(e) {
|
||
switch (e.key) {
|
||
case "ArrowRight":
|
||
e.preventDefault();
|
||
moveRight();
|
||
break;
|
||
case "ArrowLeft":
|
||
e.preventDefault();
|
||
moveLeft();
|
||
break;
|
||
case "ArrowDown":
|
||
e.preventDefault();
|
||
next();
|
||
break;
|
||
case "ArrowUp":
|
||
e.preventDefault();
|
||
prev();
|
||
break;
|
||
}
|
||
}
|
||
return {
|
||
rootElement,
|
||
assignTemplateRef,
|
||
onFocus,
|
||
onBlur,
|
||
onKeydown
|
||
};
|
||
}
|
||
const _sfc_main$v = defineComponent({
|
||
name: "CdxButtonGroup",
|
||
components: {
|
||
CdxButton,
|
||
CdxIcon
|
||
},
|
||
props: {
|
||
/**
|
||
* Objects describing the buttons in the group. See the ButtonGroupItem type.
|
||
*/
|
||
buttons: {
|
||
type: Array,
|
||
required: true,
|
||
validator: (value) => Array.isArray(value) && value.length >= 1
|
||
},
|
||
/**
|
||
* Whether the entire group is disabled.
|
||
*
|
||
* If this is set to true, all buttons in the group are disabled. Buttons can also be
|
||
* disabled individually by setting their `disabled` property to true.
|
||
*/
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
},
|
||
emits: [
|
||
/**
|
||
* Emitted when a button is clicked
|
||
*
|
||
* @property {string | number} value The `value` property of the button that was clicked
|
||
*/
|
||
"click"
|
||
],
|
||
setup(props) {
|
||
const {
|
||
rootElement,
|
||
assignTemplateRef,
|
||
onFocus,
|
||
onBlur,
|
||
onKeydown
|
||
} = useButtonGroupKeyboardNav(toRef(props, "buttons"));
|
||
return {
|
||
rootElement,
|
||
assignTemplateRef,
|
||
onFocus,
|
||
onBlur,
|
||
onKeydown,
|
||
getButtonLabel
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$t = {
|
||
ref: "rootElement",
|
||
class: "cdx-button-group"
|
||
};
|
||
function _sfc_render$v(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_icon = resolveComponent("cdx-icon");
|
||
const _component_cdx_button = resolveComponent("cdx-button");
|
||
return openBlock(), createElementBlock(
|
||
"div",
|
||
_hoisted_1$t,
|
||
[
|
||
(openBlock(true), createElementBlock(
|
||
Fragment,
|
||
null,
|
||
renderList(_ctx.buttons, (button, index) => {
|
||
return openBlock(), createBlock(_component_cdx_button, {
|
||
key: button.value,
|
||
ref_for: true,
|
||
ref: (ref2) => _ctx.assignTemplateRef(ref2, index),
|
||
disabled: button.disabled || _ctx.disabled,
|
||
"aria-label": button.ariaLabel,
|
||
onClick: ($event) => _ctx.$emit("click", button.value),
|
||
onFocus: ($event) => _ctx.onFocus(index),
|
||
onBlur: _ctx.onBlur,
|
||
onKeydown: _ctx.onKeydown
|
||
}, {
|
||
default: withCtx(() => [
|
||
renderSlot(_ctx.$slots, "default", { button }, () => [
|
||
button.icon ? (openBlock(), createBlock(_component_cdx_icon, {
|
||
key: 0,
|
||
icon: button.icon
|
||
}, null, 8, ["icon"])) : createCommentVNode("v-if", true),
|
||
createTextVNode(
|
||
" " + toDisplayString(_ctx.getButtonLabel(button)),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
])
|
||
]),
|
||
_: 2
|
||
/* DYNAMIC */
|
||
}, 1032, ["disabled", "aria-label", "onClick", "onFocus", "onBlur", "onKeydown"]);
|
||
}),
|
||
128
|
||
/* KEYED_FRAGMENT */
|
||
))
|
||
],
|
||
512
|
||
/* NEED_PATCH */
|
||
);
|
||
}
|
||
const ButtonGroup = /* @__PURE__ */ _export_sfc(_sfc_main$v, [["render", _sfc_render$v]]);
|
||
const _sfc_main$u = defineComponent({
|
||
name: "CdxThumbnail",
|
||
components: { CdxIcon },
|
||
props: {
|
||
/**
|
||
* Thumbnail data.
|
||
*/
|
||
thumbnail: {
|
||
type: [Object, null],
|
||
default: null
|
||
},
|
||
/**
|
||
* Thumbnail placeholder icon.
|
||
*/
|
||
placeholderIcon: {
|
||
type: [String, Object],
|
||
default: P4
|
||
}
|
||
},
|
||
setup: (props) => {
|
||
const thumbnailLoaded = ref(false);
|
||
const thumbnailStyle = ref({});
|
||
const preloadThumbnail = (url) => {
|
||
const escapedUrl = url.replace(/([\\"\n])/g, "\\$1");
|
||
const image = new Image();
|
||
image.onload = () => {
|
||
thumbnailStyle.value = { backgroundImage: 'url("'.concat(escapedUrl, '")') };
|
||
thumbnailLoaded.value = true;
|
||
};
|
||
image.onerror = () => {
|
||
thumbnailLoaded.value = false;
|
||
};
|
||
image.src = escapedUrl;
|
||
};
|
||
onMounted(() => {
|
||
var _a;
|
||
if ((_a = props.thumbnail) == null ? void 0 : _a.url) {
|
||
preloadThumbnail(props.thumbnail.url);
|
||
}
|
||
});
|
||
return {
|
||
thumbnailStyle,
|
||
thumbnailLoaded,
|
||
NoInvertClass
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$s = { class: "cdx-thumbnail" };
|
||
const _hoisted_2$k = {
|
||
key: 0,
|
||
class: "cdx-thumbnail__placeholder"
|
||
};
|
||
function _sfc_render$u(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_icon = resolveComponent("cdx-icon");
|
||
return openBlock(), createElementBlock("span", _hoisted_1$s, [
|
||
!_ctx.thumbnailLoaded ? (openBlock(), createElementBlock("span", _hoisted_2$k, [
|
||
createVNode(_component_cdx_icon, {
|
||
icon: _ctx.placeholderIcon,
|
||
class: "cdx-thumbnail__placeholder__icon--vue"
|
||
}, null, 8, ["icon"])
|
||
])) : createCommentVNode("v-if", true),
|
||
createVNode(Transition, { name: "cdx-thumbnail__image" }, {
|
||
default: withCtx(() => [
|
||
_ctx.thumbnailLoaded ? (openBlock(), createElementBlock(
|
||
"span",
|
||
{
|
||
key: 0,
|
||
style: normalizeStyle(_ctx.thumbnailStyle),
|
||
class: normalizeClass([_ctx.NoInvertClass, "cdx-thumbnail__image"])
|
||
},
|
||
null,
|
||
6
|
||
/* CLASS, STYLE */
|
||
)) : createCommentVNode("v-if", true)
|
||
]),
|
||
_: 1
|
||
/* STABLE */
|
||
})
|
||
]);
|
||
}
|
||
const CdxThumbnail = /* @__PURE__ */ _export_sfc(_sfc_main$u, [["render", _sfc_render$u]]);
|
||
const _sfc_main$t = defineComponent({
|
||
name: "CdxCard",
|
||
components: { CdxIcon, CdxThumbnail },
|
||
props: {
|
||
/**
|
||
* If provided, the Card will be a link to this URL.
|
||
*/
|
||
url: {
|
||
type: String,
|
||
default: ""
|
||
},
|
||
/**
|
||
* Icon displayed at the start of the Card.
|
||
*/
|
||
icon: {
|
||
type: [String, Object],
|
||
default: ""
|
||
},
|
||
/**
|
||
* Thumbnail image data for the Card.
|
||
*/
|
||
thumbnail: {
|
||
type: [Object, null],
|
||
default: null
|
||
},
|
||
/**
|
||
* Option to force a thumbnail layout.
|
||
*
|
||
* When set to `true`, the Card will display a Thumbnail. If a `thumbnail` prop was also
|
||
* provided, the thumbnail image will display. Otherwise, a placeholder icon will display.
|
||
*
|
||
* This is useful when displaying groups of Cards when some of the cards have thumbnail
|
||
* images but some do not. `forceThumbnail` will provide a consistent layout for that group.
|
||
*
|
||
* Note that this prop is not needed to display a thumbnail image: if the `thumbnail` prop
|
||
* is provided, it will display. This prop is only needed to enable the display of the
|
||
* thumbnail placeholder icon when the `thumbnail` prop is not provided.
|
||
*/
|
||
forceThumbnail: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Optional custom icon for the placeholder shown when `forceThumbnail` is true but no
|
||
* thumbnail is provided.
|
||
*
|
||
* Defaults to the default placeholder icon set in the Thumbnail component.
|
||
*/
|
||
customPlaceholderIcon: {
|
||
type: [String, Object],
|
||
default: void 0
|
||
}
|
||
},
|
||
setup(props) {
|
||
const isLink = computed(() => !!props.url);
|
||
const contentTag = computed(() => isLink.value ? "a" : "span");
|
||
const cardLink = computed(() => isLink.value ? props.url : void 0);
|
||
return {
|
||
isLink,
|
||
contentTag,
|
||
cardLink
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$r = { class: "cdx-card__text" };
|
||
const _hoisted_2$j = { class: "cdx-card__text__title" };
|
||
const _hoisted_3$d = {
|
||
key: 0,
|
||
class: "cdx-card__text__description"
|
||
};
|
||
const _hoisted_4$8 = {
|
||
key: 1,
|
||
class: "cdx-card__text__supporting-text"
|
||
};
|
||
function _sfc_render$t(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_thumbnail = resolveComponent("cdx-thumbnail");
|
||
const _component_cdx_icon = resolveComponent("cdx-icon");
|
||
return openBlock(), createBlock(resolveDynamicComponent(_ctx.contentTag), {
|
||
href: _ctx.cardLink,
|
||
class: normalizeClass(["cdx-card", {
|
||
"cdx-card--is-link": _ctx.isLink,
|
||
// Include dynamic classes in the template so that $slots is reactive.
|
||
"cdx-card--title-only": !_ctx.$slots.description && !_ctx.$slots["supporting-text"]
|
||
}])
|
||
}, {
|
||
default: withCtx(() => [
|
||
_ctx.thumbnail || _ctx.forceThumbnail ? (openBlock(), createBlock(_component_cdx_thumbnail, {
|
||
key: 0,
|
||
thumbnail: _ctx.thumbnail,
|
||
"placeholder-icon": _ctx.customPlaceholderIcon,
|
||
class: "cdx-card__thumbnail"
|
||
}, null, 8, ["thumbnail", "placeholder-icon"])) : _ctx.icon ? (openBlock(), createBlock(_component_cdx_icon, {
|
||
key: 1,
|
||
icon: _ctx.icon,
|
||
class: "cdx-card__icon"
|
||
}, null, 8, ["icon"])) : createCommentVNode("v-if", true),
|
||
createElementVNode("span", _hoisted_1$r, [
|
||
createElementVNode("span", _hoisted_2$j, [
|
||
renderSlot(_ctx.$slots, "title")
|
||
]),
|
||
_ctx.$slots.description ? (openBlock(), createElementBlock("span", _hoisted_3$d, [
|
||
renderSlot(_ctx.$slots, "description")
|
||
])) : createCommentVNode("v-if", true),
|
||
_ctx.$slots["supporting-text"] ? (openBlock(), createElementBlock("span", _hoisted_4$8, [
|
||
renderSlot(_ctx.$slots, "supporting-text")
|
||
])) : createCommentVNode("v-if", true)
|
||
])
|
||
]),
|
||
_: 3
|
||
/* FORWARDED */
|
||
}, 8, ["href", "class"]);
|
||
}
|
||
const Card = /* @__PURE__ */ _export_sfc(_sfc_main$t, [["render", _sfc_render$t]]);
|
||
function useComputedDisabled(disabledProp) {
|
||
const providedDisabled = inject(DisabledKey, ref(false));
|
||
return computed(() => providedDisabled.value || disabledProp.value);
|
||
}
|
||
function useFieldData(disabledProp, statusProp, idAttr) {
|
||
const computedDisabled = useComputedDisabled(disabledProp);
|
||
const providedStatus = inject(FieldStatusKey, ref("default"));
|
||
const computedStatus = computed(() => {
|
||
if ((statusProp == null ? void 0 : statusProp.value) && statusProp.value !== "default") {
|
||
return statusProp.value;
|
||
}
|
||
return providedStatus.value;
|
||
});
|
||
const providedId = inject(FieldInputIdKey, void 0);
|
||
const computedInputId = computed(() => {
|
||
var _a;
|
||
return (_a = providedId == null ? void 0 : providedId.value) != null ? _a : idAttr;
|
||
});
|
||
return {
|
||
computedDisabled,
|
||
computedStatus,
|
||
computedInputId
|
||
};
|
||
}
|
||
function useSplitAttributes(attrs, internalClasses = computed(() => ({}))) {
|
||
const rootClasses = computed(() => {
|
||
const classRecord = __objRest(internalClasses.value, []);
|
||
if (attrs.class) {
|
||
const providedClasses = attrs.class.split(" ");
|
||
providedClasses.forEach((className) => {
|
||
classRecord[className] = true;
|
||
});
|
||
}
|
||
return classRecord;
|
||
});
|
||
const rootStyle = computed(() => {
|
||
if ("style" in attrs) {
|
||
return attrs.style;
|
||
}
|
||
return void 0;
|
||
});
|
||
const otherAttrs = computed(() => {
|
||
const _a = attrs, { class: _ignoredClass, style: _ignoredStyle } = _a, attrsCopy = __objRest(_a, ["class", "style"]);
|
||
return attrsCopy;
|
||
});
|
||
return {
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrs
|
||
};
|
||
}
|
||
function useI18n(messageKey, defaultValue, params = []) {
|
||
const providedI18nFunc = inject("CdxI18nFunction", void 0);
|
||
return computed(() => {
|
||
const unwrappedParams = params.map((p2) => typeof p2 === "function" ? p2() : p2.value);
|
||
const fromProvidedFunc = providedI18nFunc == null ? void 0 : providedI18nFunc(messageKey, ...unwrappedParams);
|
||
if (fromProvidedFunc !== void 0 && fromProvidedFunc !== null) {
|
||
return fromProvidedFunc;
|
||
}
|
||
return typeof defaultValue === "function" ? defaultValue(...unwrappedParams) : defaultValue;
|
||
});
|
||
}
|
||
function useI18nWithOverride(override, messageKey, defaultValue, params = []) {
|
||
const translatedMessage = useI18n(messageKey, defaultValue, params);
|
||
return computed(() => override.value || translatedMessage.value);
|
||
}
|
||
const _sfc_main$s = defineComponent({
|
||
name: "CdxLabel",
|
||
components: { CdxIcon },
|
||
/**
|
||
* We want the label or legend to inherit attributes, not the root element.
|
||
*/
|
||
inheritAttrs: false,
|
||
props: {
|
||
/**
|
||
* Icon before the label text.
|
||
*
|
||
* Do not use this if including a start icon within the input component.
|
||
*/
|
||
icon: {
|
||
type: [String, Object],
|
||
default: null
|
||
},
|
||
/**
|
||
* Whether the field is optional.
|
||
*
|
||
* This will add a flag next to the label indicating that the field is optional.
|
||
*/
|
||
optional: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
// DEPRECATED: set default to '(optional)' (T368444).
|
||
/**
|
||
* Text to indicate that the field is optional.
|
||
*
|
||
* Omit this prop to use the default value, "(optional)".
|
||
*/
|
||
optionalFlag: {
|
||
type: String,
|
||
default: ""
|
||
},
|
||
/**
|
||
* Whether the label should be visually hidden.
|
||
*/
|
||
visuallyHidden: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether this component should output a `<legend>` element.
|
||
*
|
||
* Always set this to true when this component is used inside a `<fieldset>` element. Do not
|
||
* set it to true otherwise.
|
||
*/
|
||
isLegend: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* The ID of the input/control this label is for.
|
||
*
|
||
* Will be added as the `for` attribute of the `<label>`. Not needed for `<legend>`.
|
||
*/
|
||
inputId: {
|
||
type: String,
|
||
default: ""
|
||
},
|
||
/**
|
||
* The ID of the description element.
|
||
*
|
||
* This ID can be used for the `aria-describedby` attribute of the input.
|
||
*/
|
||
descriptionId: {
|
||
type: String,
|
||
default: ""
|
||
},
|
||
/**
|
||
* Whether this label is for a disabled field or input.
|
||
*/
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
},
|
||
setup(props, { attrs }) {
|
||
const { computedDisabled } = useFieldData(toRef(props, "disabled"));
|
||
const internalClasses = computed(() => {
|
||
return {
|
||
"cdx-label--visually-hidden": props.visuallyHidden,
|
||
"cdx-label--disabled": computedDisabled.value
|
||
};
|
||
});
|
||
const {
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrs
|
||
} = useSplitAttributes(attrs, internalClasses);
|
||
const translatedOptionalFlag = useI18nWithOverride(
|
||
toRef(props, "optionalFlag"),
|
||
"cdx-label-optional-flag",
|
||
"(optional)"
|
||
);
|
||
return {
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrs,
|
||
translatedOptionalFlag
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$q = ["for"];
|
||
const _hoisted_2$i = { class: "cdx-label__label__text" };
|
||
const _hoisted_3$c = {
|
||
key: 1,
|
||
class: "cdx-label__label__optional-flag"
|
||
};
|
||
const _hoisted_4$7 = ["id"];
|
||
const _hoisted_5$7 = { class: "cdx-label__label" };
|
||
const _hoisted_6$6 = { class: "cdx-label__label__text" };
|
||
const _hoisted_7$2 = {
|
||
key: 1,
|
||
class: "cdx-label__label__optional-flag"
|
||
};
|
||
const _hoisted_8$1 = {
|
||
key: 0,
|
||
class: "cdx-label__description"
|
||
};
|
||
function _sfc_render$s(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_icon = resolveComponent("cdx-icon");
|
||
return !_ctx.isLegend ? (openBlock(), createElementBlock(
|
||
"div",
|
||
{
|
||
key: 0,
|
||
class: normalizeClass(["cdx-label", _ctx.rootClasses]),
|
||
style: normalizeStyle(_ctx.rootStyle)
|
||
},
|
||
[
|
||
createElementVNode("label", mergeProps({
|
||
class: "cdx-label__label",
|
||
for: _ctx.inputId ? _ctx.inputId : void 0
|
||
}, _ctx.otherAttrs), [
|
||
_ctx.icon ? (openBlock(), createBlock(_component_cdx_icon, {
|
||
key: 0,
|
||
icon: _ctx.icon,
|
||
class: "cdx-label__label__icon"
|
||
}, null, 8, ["icon"])) : createCommentVNode("v-if", true),
|
||
createElementVNode("span", _hoisted_2$i, [
|
||
renderSlot(_ctx.$slots, "default")
|
||
]),
|
||
_ctx.optionalFlag || _ctx.optional ? (openBlock(), createElementBlock(
|
||
"span",
|
||
_hoisted_3$c,
|
||
toDisplayString(" ") + " " + toDisplayString(_ctx.translatedOptionalFlag),
|
||
1
|
||
/* TEXT */
|
||
)) : createCommentVNode("v-if", true)
|
||
], 16, _hoisted_1$q),
|
||
_ctx.$slots.description && _ctx.$slots.description().length > 0 ? (openBlock(), createElementBlock("span", {
|
||
key: 0,
|
||
id: _ctx.descriptionId || void 0,
|
||
class: "cdx-label__description"
|
||
}, [
|
||
renderSlot(_ctx.$slots, "description")
|
||
], 8, _hoisted_4$7)) : createCommentVNode("v-if", true)
|
||
],
|
||
6
|
||
/* CLASS, STYLE */
|
||
)) : (openBlock(), createElementBlock(
|
||
"legend",
|
||
mergeProps({
|
||
key: 1,
|
||
class: ["cdx-label", _ctx.rootClasses],
|
||
style: _ctx.rootStyle
|
||
}, _ctx.otherAttrs),
|
||
[
|
||
createElementVNode("span", _hoisted_5$7, [
|
||
_ctx.icon ? (openBlock(), createBlock(_component_cdx_icon, {
|
||
key: 0,
|
||
icon: _ctx.icon,
|
||
class: "cdx-label__label__icon"
|
||
}, null, 8, ["icon"])) : createCommentVNode("v-if", true),
|
||
createElementVNode("span", _hoisted_6$6, [
|
||
renderSlot(_ctx.$slots, "default")
|
||
]),
|
||
_ctx.optionalFlag || _ctx.optional ? (openBlock(), createElementBlock(
|
||
"span",
|
||
_hoisted_7$2,
|
||
toDisplayString(" ") + " " + toDisplayString(_ctx.translatedOptionalFlag),
|
||
1
|
||
/* TEXT */
|
||
)) : createCommentVNode("v-if", true)
|
||
]),
|
||
_ctx.$slots.description && _ctx.$slots.description().length > 0 ? (openBlock(), createElementBlock("span", _hoisted_8$1, [
|
||
renderSlot(_ctx.$slots, "description")
|
||
])) : createCommentVNode("v-if", true)
|
||
],
|
||
16
|
||
/* FULL_PROPS */
|
||
));
|
||
}
|
||
const CdxLabel = /* @__PURE__ */ _export_sfc(_sfc_main$s, [["render", _sfc_render$s]]);
|
||
function useLabelChecker(slot, attrs, componentName) {
|
||
useWarnOnce(
|
||
() => useSlotContents(slot).length === 0 && !(attrs == null ? void 0 : attrs["aria-label"]) && !(attrs == null ? void 0 : attrs["aria-labelledby"]),
|
||
"".concat(componentName, ": Inputs must have an associated label. Provide one of the following:\n - A label via the appropriate slot\n - An `aria-label` attribute set to the label text\n - An `aria-labelledby` attribute set to the ID of the label element")
|
||
);
|
||
}
|
||
function useModelWrapper(modelValueRef, emit, eventName) {
|
||
return computed({
|
||
get: () => modelValueRef.value,
|
||
set: (value) => (
|
||
// If eventName is undefined, then 'update:modelValue' must be a valid EventName,
|
||
// but TypeScript's type analysis isn't clever enough to realize that
|
||
emit(eventName || "update:modelValue", value)
|
||
)
|
||
});
|
||
}
|
||
let counter = 0;
|
||
function useGeneratedId(identifier) {
|
||
var _a;
|
||
const vm = getCurrentInstance();
|
||
const externalId = (_a = vm == null ? void 0 : vm.props.id) != null ? _a : vm == null ? void 0 : vm.attrs.id;
|
||
if (identifier) {
|
||
return "".concat(LibraryPrefix, "-").concat(identifier, "-").concat(counter++);
|
||
} else if (externalId) {
|
||
return "".concat(LibraryPrefix, "-").concat(externalId, "-").concat(counter++);
|
||
} else {
|
||
return "".concat(LibraryPrefix, "-").concat(counter++);
|
||
}
|
||
}
|
||
const statusValidator$a = makeStringTypeValidator(ValidationStatusTypes);
|
||
const _sfc_main$r = defineComponent({
|
||
name: "CdxCheckbox",
|
||
components: { CdxLabel },
|
||
props: {
|
||
/**
|
||
* Value of the checkbox or checkbox group.
|
||
*
|
||
* Provided by `v-model` binding in the parent component.
|
||
*/
|
||
modelValue: {
|
||
type: [Boolean, Array],
|
||
default: false
|
||
},
|
||
/**
|
||
* HTML "value" attribute to assign to the input.
|
||
*
|
||
* Required for input groups.
|
||
*/
|
||
inputValue: {
|
||
type: [String, Number, Boolean],
|
||
default: false
|
||
},
|
||
/**
|
||
* HTML "name" attribute to assign to the input.
|
||
*/
|
||
name: {
|
||
type: String,
|
||
default: null
|
||
},
|
||
/**
|
||
* Whether the disabled attribute should be added to the input.
|
||
*/
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether the indeterminate visual state should be displayed.
|
||
*
|
||
* This is unrelated to the value provided by `v-model`, and the indeterminate visual state
|
||
* will override the checked or unchecked visual state.
|
||
*/
|
||
indeterminate: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether the component should display inline.
|
||
*
|
||
* By default, `display: block` is set and a margin exists between
|
||
* sibling components, for a stacked layout.
|
||
*/
|
||
inline: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether the label should be visually hidden.
|
||
*
|
||
* When true, the label will remain accessible to assistive technology.
|
||
*/
|
||
hideLabel: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Validation status of the Checkbox.
|
||
*/
|
||
status: {
|
||
type: String,
|
||
default: "default",
|
||
validator: statusValidator$a
|
||
}
|
||
},
|
||
emits: [
|
||
/**
|
||
* Emitted when modelValue changes.
|
||
*
|
||
* @property {boolean | string[] | number[]} modelValue The new model value
|
||
*/
|
||
"update:modelValue"
|
||
],
|
||
setup(props, { emit, slots, attrs }) {
|
||
var _a;
|
||
useLabelChecker((_a = slots.default) == null ? void 0 : _a.call(slots), attrs, "CdxCheckbox");
|
||
const {
|
||
computedDisabled,
|
||
computedStatus
|
||
} = useFieldData(
|
||
toRef(props, "disabled"),
|
||
toRef(props, "status")
|
||
);
|
||
const rootClasses = computed(() => {
|
||
return {
|
||
"cdx-checkbox--inline": props.inline,
|
||
["cdx-checkbox--status-".concat(computedStatus.value)]: true
|
||
};
|
||
});
|
||
const customInputClasses = computed(() => {
|
||
return {
|
||
"cdx-checkbox__custom-input--inline": props.inline
|
||
};
|
||
});
|
||
const input = ref();
|
||
const checkboxId = useGeneratedId("checkbox");
|
||
const descriptionId = useGeneratedId("description");
|
||
const wrappedModel = useModelWrapper(toRef(props, "modelValue"), emit);
|
||
return {
|
||
rootClasses,
|
||
computedDisabled,
|
||
input,
|
||
checkboxId,
|
||
descriptionId,
|
||
wrappedModel,
|
||
customInputClasses
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$p = { class: "cdx-checkbox__wrapper" };
|
||
const _hoisted_2$h = ["id", "aria-describedby", "value", "name", "disabled", ".indeterminate"];
|
||
const _hoisted_3$b = /* @__PURE__ */ createElementVNode(
|
||
"span",
|
||
{ class: "cdx-checkbox__icon" },
|
||
null,
|
||
-1
|
||
/* HOISTED */
|
||
);
|
||
function _sfc_render$r(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_label = resolveComponent("cdx-label");
|
||
return openBlock(), createElementBlock(
|
||
"div",
|
||
{
|
||
class: normalizeClass(["cdx-checkbox", _ctx.rootClasses])
|
||
},
|
||
[
|
||
createElementVNode("div", _hoisted_1$p, [
|
||
withDirectives(createElementVNode("input", {
|
||
id: _ctx.checkboxId,
|
||
ref: "input",
|
||
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => _ctx.wrappedModel = $event),
|
||
class: "cdx-checkbox__input",
|
||
type: "checkbox",
|
||
"aria-describedby": _ctx.$slots.description && _ctx.$slots.description().length > 0 ? _ctx.descriptionId : void 0,
|
||
value: _ctx.inputValue,
|
||
name: _ctx.name,
|
||
disabled: _ctx.computedDisabled,
|
||
".indeterminate": _ctx.indeterminate
|
||
}, null, 40, _hoisted_2$h), [
|
||
[vModelCheckbox, _ctx.wrappedModel]
|
||
]),
|
||
_hoisted_3$b,
|
||
_ctx.$slots.default && _ctx.$slots.default().length ? (openBlock(), createBlock(_component_cdx_label, {
|
||
key: 0,
|
||
class: "cdx-checkbox__label",
|
||
"input-id": _ctx.checkboxId,
|
||
"description-id": _ctx.$slots.description && _ctx.$slots.description().length > 0 ? _ctx.descriptionId : void 0,
|
||
disabled: _ctx.computedDisabled,
|
||
"visually-hidden": _ctx.hideLabel
|
||
}, createSlots({
|
||
default: withCtx(() => [
|
||
renderSlot(_ctx.$slots, "default")
|
||
]),
|
||
_: 2
|
||
/* DYNAMIC */
|
||
}, [
|
||
_ctx.$slots.description && _ctx.$slots.description().length > 0 ? {
|
||
name: "description",
|
||
fn: withCtx(() => [
|
||
renderSlot(_ctx.$slots, "description")
|
||
]),
|
||
key: "0"
|
||
} : void 0
|
||
]), 1032, ["input-id", "description-id", "disabled", "visually-hidden"])) : createCommentVNode("v-if", true)
|
||
]),
|
||
_ctx.$slots["custom-input"] ? (openBlock(), createElementBlock(
|
||
"div",
|
||
{
|
||
key: 0,
|
||
class: normalizeClass(["cdx-checkbox__custom-input", _ctx.customInputClasses])
|
||
},
|
||
[
|
||
renderSlot(_ctx.$slots, "custom-input")
|
||
],
|
||
2
|
||
/* CLASS */
|
||
)) : createCommentVNode("v-if", true)
|
||
],
|
||
2
|
||
/* CLASS */
|
||
);
|
||
}
|
||
const CdxCheckbox = /* @__PURE__ */ _export_sfc(_sfc_main$r, [["render", _sfc_render$r]]);
|
||
const _sfc_main$q = defineComponent({
|
||
name: "CdxInputChip",
|
||
components: {
|
||
CdxButton,
|
||
CdxIcon
|
||
},
|
||
props: {
|
||
/**
|
||
* Custom icon.
|
||
*/
|
||
icon: {
|
||
type: [String, Object],
|
||
default: null
|
||
},
|
||
/**
|
||
* Whether the input chip can be removed.
|
||
*/
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
},
|
||
expose: [
|
||
"focus"
|
||
],
|
||
emits: [
|
||
/**
|
||
* Emitted when a chip is removed by the user.
|
||
*
|
||
* @property {'button'|'Backspace'|'Delete'} method How the chip was removed
|
||
*/
|
||
"remove-chip",
|
||
/**
|
||
* Emitted when a chip is clicked by the user.
|
||
*/
|
||
"click-chip",
|
||
/**
|
||
* Emitted when the user presses the left arrow key.
|
||
*/
|
||
"arrow-left",
|
||
/**
|
||
* Emitted when the user presses the right arrow key.
|
||
*/
|
||
"arrow-right"
|
||
],
|
||
setup(props, { emit }) {
|
||
const rootElement = ref();
|
||
const rootClasses = computed(() => {
|
||
return {
|
||
"cdx-input-chip--disabled": props.disabled
|
||
};
|
||
});
|
||
const ariaDescription = useI18n(
|
||
"cdx-input-chip-aria-description",
|
||
"Press Enter to edit or Delete to remove"
|
||
);
|
||
function onKeydown(e) {
|
||
var _a;
|
||
switch (e.key) {
|
||
case "Enter":
|
||
emit("click-chip");
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
break;
|
||
case "Escape":
|
||
(_a = rootElement.value) == null ? void 0 : _a.blur();
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
break;
|
||
case "Backspace":
|
||
case "Delete":
|
||
emit("remove-chip", e.key);
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
break;
|
||
case "ArrowLeft":
|
||
emit("arrow-left");
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
break;
|
||
case "ArrowRight":
|
||
emit("arrow-right");
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
break;
|
||
}
|
||
}
|
||
return {
|
||
rootElement,
|
||
rootClasses,
|
||
ariaDescription,
|
||
onKeydown,
|
||
cdxIconClose: X3
|
||
};
|
||
},
|
||
methods: {
|
||
/**
|
||
* Focus the chip.
|
||
*
|
||
* @public
|
||
*/
|
||
focus() {
|
||
this.$refs.rootElement.focus();
|
||
}
|
||
}
|
||
});
|
||
const _hoisted_1$o = ["aria-description"];
|
||
const _hoisted_2$g = { class: "cdx-input-chip__text" };
|
||
function _sfc_render$q(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_icon = resolveComponent("cdx-icon");
|
||
const _component_cdx_button = resolveComponent("cdx-button");
|
||
return openBlock(), createElementBlock("div", {
|
||
ref: "rootElement",
|
||
class: normalizeClass(["cdx-input-chip", _ctx.rootClasses]),
|
||
tabindex: "0",
|
||
role: "option",
|
||
"aria-description": _ctx.ariaDescription,
|
||
onKeydown: _cache[1] || (_cache[1] = (...args) => _ctx.onKeydown && _ctx.onKeydown(...args)),
|
||
onClick: _cache[2] || (_cache[2] = ($event) => _ctx.$emit("click-chip"))
|
||
}, [
|
||
_ctx.icon ? (openBlock(), createBlock(_component_cdx_icon, {
|
||
key: 0,
|
||
icon: _ctx.icon,
|
||
size: "small"
|
||
}, null, 8, ["icon"])) : createCommentVNode("v-if", true),
|
||
createElementVNode("span", _hoisted_2$g, [
|
||
renderSlot(_ctx.$slots, "default")
|
||
]),
|
||
createVNode(_component_cdx_button, {
|
||
class: "cdx-input-chip__button",
|
||
weight: "quiet",
|
||
tabindex: "-1",
|
||
"aria-hidden": "true",
|
||
disabled: _ctx.disabled,
|
||
onClick: _cache[0] || (_cache[0] = withModifiers(($event) => _ctx.$emit("remove-chip", "button"), ["stop"]))
|
||
}, {
|
||
default: withCtx(() => [
|
||
createVNode(_component_cdx_icon, {
|
||
icon: _ctx.cdxIconClose,
|
||
size: "x-small"
|
||
}, null, 8, ["icon"])
|
||
]),
|
||
_: 1
|
||
/* STABLE */
|
||
}, 8, ["disabled"])
|
||
], 42, _hoisted_1$o);
|
||
}
|
||
const CdxInputChip = /* @__PURE__ */ _export_sfc(_sfc_main$q, [["render", _sfc_render$q]]);
|
||
function useOptionalModelWrapper(internalValueRef, modelValueRef, emit, eventName) {
|
||
return computed({
|
||
get: () => {
|
||
var _a;
|
||
return (_a = modelValueRef.value) != null ? _a : internalValueRef.value;
|
||
},
|
||
set: (value) => {
|
||
if (modelValueRef.value !== null) {
|
||
emit(eventName || "update:modelValue", value);
|
||
} else {
|
||
internalValueRef.value = value;
|
||
}
|
||
}
|
||
});
|
||
}
|
||
const statusValidator$9 = makeStringTypeValidator(ValidationStatusTypes);
|
||
const _sfc_main$p = defineComponent({
|
||
name: "CdxChipInput",
|
||
components: {
|
||
CdxInputChip
|
||
},
|
||
/**
|
||
* We want the input to inherit attributes, not the root element.
|
||
*/
|
||
inheritAttrs: false,
|
||
props: {
|
||
/**
|
||
* Current chips present in the input.
|
||
*
|
||
* Provided by `v-model` binding in the parent component.
|
||
*/
|
||
inputChips: {
|
||
type: Array,
|
||
required: true
|
||
},
|
||
/**
|
||
* Current value of the text input. This prop is optional and should only be used if you
|
||
* need to keep track of the text input value for some reason (e.g. for validation).
|
||
*
|
||
* Optionally provided by `v-model:input-value` binding in the parent component.
|
||
*/
|
||
inputValue: {
|
||
type: [String, Number],
|
||
default: null
|
||
},
|
||
/**
|
||
* Whether the text input should appear below the set of input chips.
|
||
*
|
||
* By default, the input chips are inline with the input.
|
||
*/
|
||
separateInput: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* `status` attribute of the input.
|
||
*/
|
||
status: {
|
||
type: String,
|
||
default: "default",
|
||
validator: statusValidator$9
|
||
},
|
||
/**
|
||
* Validation function for chip text. If it returns false, the chip will not be added and
|
||
* the error status will be set.
|
||
*/
|
||
chipValidator: {
|
||
type: Function,
|
||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||
default: (value) => true
|
||
},
|
||
/**
|
||
* Whether the input is disabled.
|
||
*/
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
},
|
||
emits: [
|
||
/**
|
||
* When the input chips change.
|
||
*
|
||
* @property {ChipInputItem[]} inputChips The new set of inputChips
|
||
*/
|
||
"update:input-chips",
|
||
/**
|
||
* When the input value changes. Only emitted if the inputValue prop is provided.
|
||
*
|
||
* @property {string | number} inputValue The new input value
|
||
*/
|
||
"update:input-value"
|
||
],
|
||
setup(props, { emit, attrs }) {
|
||
const rootElement = ref();
|
||
const chipsContainer = ref();
|
||
const separateInputWrapper = ref();
|
||
const statusMessageContent = ref("");
|
||
const computedDirection = useComputedDirection(rootElement);
|
||
const input = ref();
|
||
const allowArbitrary = inject(AllowArbitraryKey, ref(true));
|
||
const internalInputValue = ref("");
|
||
const computedInputValue = useOptionalModelWrapper(
|
||
internalInputValue,
|
||
toRef(props, "inputValue"),
|
||
emit,
|
||
"update:input-value"
|
||
);
|
||
const validatedStatus = ref("default");
|
||
const internalStatus = computed(() => {
|
||
if (validatedStatus.value === "error" || props.status === "error") {
|
||
return "error";
|
||
}
|
||
return "default";
|
||
});
|
||
const { computedDisabled, computedStatus } = useFieldData(toRef(props, "disabled"), internalStatus);
|
||
const isFocused = ref(false);
|
||
const internalClasses = computed(() => {
|
||
return {
|
||
"cdx-chip-input--has-separate-input": props.separateInput,
|
||
["cdx-chip-input--status-".concat(computedStatus.value)]: true,
|
||
// We need focused and disabled classes on the root element, which contains the
|
||
// chips and the input, since it is styled to look like the input.
|
||
"cdx-chip-input--focused": isFocused.value,
|
||
"cdx-chip-input--disabled": computedDisabled.value
|
||
};
|
||
});
|
||
const {
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrs
|
||
} = useSplitAttributes(attrs, internalClasses);
|
||
const chipRefs = [];
|
||
const currentChipToRemove = ref(null);
|
||
const computedChipToRemove = computed(() => currentChipToRemove.value ? currentChipToRemove.value.value : "");
|
||
const chipAddedMessage = useI18n(
|
||
"cdx-chip-input-chip-added",
|
||
(x) => "Chip ".concat(x, " was added."),
|
||
[computedInputValue]
|
||
);
|
||
const chipRemovedMessage = useI18n(
|
||
"cdx-chip-input-chip-removed",
|
||
(x) => "Chip ".concat(x, " was removed."),
|
||
[computedChipToRemove]
|
||
);
|
||
function assignChipTemplateRef(chip, index) {
|
||
if (chip !== null) {
|
||
chipRefs[index] = chip;
|
||
}
|
||
}
|
||
const focusInput = () => {
|
||
input.value.focus();
|
||
};
|
||
function addChip() {
|
||
if (
|
||
// If the input value is the same as a chip's value, or...
|
||
!!props.inputChips.find((chip) => chip.value === computedInputValue.value) || // ...validation fails, set status to error.
|
||
!props.chipValidator(computedInputValue.value)
|
||
) {
|
||
validatedStatus.value = "error";
|
||
} else if (computedInputValue.value.toString().length > 0) {
|
||
statusMessageContent.value = chipAddedMessage.value;
|
||
emit("update:input-chips", props.inputChips.concat({ value: computedInputValue.value }));
|
||
computedInputValue.value = "";
|
||
}
|
||
}
|
||
function removeChip(chipToRemove) {
|
||
emit("update:input-chips", props.inputChips.filter(
|
||
(chip) => chip.value !== chipToRemove.value
|
||
));
|
||
}
|
||
function moveChipFocus(direction, startIndex) {
|
||
const resolvedDirection = (
|
||
// -1 for prev (left in LTR, right in RTL), +1 for next (right in LTR, left in RTL)
|
||
computedDirection.value === "ltr" && direction === "left" || computedDirection.value === "rtl" && direction === "right" ? -1 : 1
|
||
);
|
||
const newIndex = startIndex + resolvedDirection;
|
||
if (newIndex < 0) {
|
||
return;
|
||
}
|
||
if (newIndex >= props.inputChips.length) {
|
||
focusInput();
|
||
return;
|
||
}
|
||
chipRefs[newIndex].focus();
|
||
}
|
||
function handleChipClick(clickedChip) {
|
||
return __async(this, null, function* () {
|
||
var _a;
|
||
addChip();
|
||
yield nextTick();
|
||
removeChip(clickedChip);
|
||
computedInputValue.value = (_a = clickedChip.label) != null ? _a : clickedChip.value;
|
||
focusInput();
|
||
});
|
||
}
|
||
function handleChipRemove(chipToRemove, index, method) {
|
||
currentChipToRemove.value = chipToRemove;
|
||
statusMessageContent.value = chipRemovedMessage.value;
|
||
if (method === "button") {
|
||
focusInput();
|
||
} else if (method === "Backspace") {
|
||
const newIndex = index === 0 ? 1 : index - 1;
|
||
if (newIndex < props.inputChips.length) {
|
||
chipRefs[newIndex].focus();
|
||
} else {
|
||
focusInput();
|
||
}
|
||
} else if (method === "Delete") {
|
||
const newIndex = index + 1;
|
||
if (newIndex < props.inputChips.length) {
|
||
chipRefs[newIndex].focus();
|
||
} else {
|
||
focusInput();
|
||
}
|
||
}
|
||
removeChip(chipToRemove);
|
||
}
|
||
function onInputKeydown(e) {
|
||
var _a, _b;
|
||
const prevArrow = computedDirection.value === "rtl" ? "ArrowRight" : "ArrowLeft";
|
||
switch (e.key) {
|
||
case "Enter":
|
||
if (computedInputValue.value.toString().length > 0 && allowArbitrary.value) {
|
||
addChip();
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
return;
|
||
}
|
||
break;
|
||
case "Escape":
|
||
(_a = input.value) == null ? void 0 : _a.blur();
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
return;
|
||
case "Backspace":
|
||
case prevArrow:
|
||
if (((_b = input.value) == null ? void 0 : _b.selectionStart) === 0 && input.value.selectionEnd === 0 && props.inputChips.length > 0) {
|
||
chipRefs[props.inputChips.length - 1].focus();
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
return;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
function onInputFocus() {
|
||
isFocused.value = true;
|
||
}
|
||
function onInputBlur() {
|
||
isFocused.value = false;
|
||
}
|
||
function onFocusOut(e) {
|
||
var _a;
|
||
if (!((_a = rootElement.value) == null ? void 0 : _a.contains(e.relatedTarget)) && allowArbitrary.value) {
|
||
addChip();
|
||
}
|
||
}
|
||
watch(toRef(props, "inputChips"), (newVal) => {
|
||
const matchingChip = newVal.find((chip) => chip.value === computedInputValue.value);
|
||
validatedStatus.value = matchingChip ? "error" : "default";
|
||
});
|
||
watch(computedInputValue, () => {
|
||
if (validatedStatus.value === "error") {
|
||
validatedStatus.value = "default";
|
||
}
|
||
});
|
||
return {
|
||
rootElement,
|
||
chipsContainer,
|
||
separateInputWrapper,
|
||
input,
|
||
computedInputValue,
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrs,
|
||
assignChipTemplateRef,
|
||
handleChipClick,
|
||
handleChipRemove,
|
||
moveChipFocus,
|
||
onInputKeydown,
|
||
focusInput,
|
||
onInputFocus,
|
||
onInputBlur,
|
||
onFocusOut,
|
||
computedDisabled,
|
||
statusMessageContent
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$n = {
|
||
ref: "chipsContainer",
|
||
class: "cdx-chip-input__chips",
|
||
role: "listbox",
|
||
"aria-orientation": "horizontal"
|
||
};
|
||
const _hoisted_2$f = ["disabled"];
|
||
const _hoisted_3$a = {
|
||
key: 0,
|
||
ref: "separateInputWrapper",
|
||
class: "cdx-chip-input__separate-input"
|
||
};
|
||
const _hoisted_4$6 = ["disabled"];
|
||
const _hoisted_5$6 = {
|
||
class: "cdx-chip-input__aria-status",
|
||
role: "status",
|
||
"aria-live": "polite"
|
||
};
|
||
function _sfc_render$p(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_input_chip = resolveComponent("cdx-input-chip");
|
||
return openBlock(), createElementBlock(
|
||
"div",
|
||
{
|
||
ref: "rootElement",
|
||
class: normalizeClass(["cdx-chip-input", _ctx.rootClasses]),
|
||
style: normalizeStyle(_ctx.rootStyle),
|
||
onClick: _cache[8] || (_cache[8] = (...args) => _ctx.focusInput && _ctx.focusInput(...args)),
|
||
onFocusout: _cache[9] || (_cache[9] = (...args) => _ctx.onFocusOut && _ctx.onFocusOut(...args))
|
||
},
|
||
[
|
||
createElementVNode(
|
||
"div",
|
||
_hoisted_1$n,
|
||
[
|
||
(openBlock(true), createElementBlock(
|
||
Fragment,
|
||
null,
|
||
renderList(_ctx.inputChips, (chip, index) => {
|
||
return openBlock(), createBlock(_component_cdx_input_chip, {
|
||
key: chip.value,
|
||
ref_for: true,
|
||
ref: (ref2) => _ctx.assignChipTemplateRef(ref2, index),
|
||
class: "cdx-chip-input__item",
|
||
icon: chip.icon,
|
||
disabled: _ctx.computedDisabled,
|
||
onClickChip: ($event) => _ctx.handleChipClick(chip),
|
||
onRemoveChip: (method) => _ctx.handleChipRemove(chip, index, method),
|
||
onArrowLeft: ($event) => _ctx.moveChipFocus("left", index),
|
||
onArrowRight: ($event) => _ctx.moveChipFocus("right", index)
|
||
}, {
|
||
default: withCtx(() => {
|
||
var _a;
|
||
return [
|
||
createTextVNode(
|
||
toDisplayString((_a = chip.label) != null ? _a : chip.value),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
];
|
||
}),
|
||
_: 2
|
||
/* DYNAMIC */
|
||
}, 1032, ["icon", "disabled", "onClickChip", "onRemoveChip", "onArrowLeft", "onArrowRight"]);
|
||
}),
|
||
128
|
||
/* KEYED_FRAGMENT */
|
||
)),
|
||
!_ctx.separateInput ? withDirectives((openBlock(), createElementBlock("input", mergeProps({
|
||
key: 0,
|
||
ref: "input",
|
||
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => _ctx.computedInputValue = $event),
|
||
class: "cdx-chip-input__input",
|
||
disabled: _ctx.computedDisabled
|
||
}, _ctx.otherAttrs, {
|
||
onBlur: _cache[1] || (_cache[1] = (...args) => _ctx.onInputBlur && _ctx.onInputBlur(...args)),
|
||
onFocus: _cache[2] || (_cache[2] = (...args) => _ctx.onInputFocus && _ctx.onInputFocus(...args)),
|
||
onKeydown: _cache[3] || (_cache[3] = (...args) => _ctx.onInputKeydown && _ctx.onInputKeydown(...args))
|
||
}), null, 16, _hoisted_2$f)), [
|
||
[vModelDynamic, _ctx.computedInputValue]
|
||
]) : createCommentVNode("v-if", true)
|
||
],
|
||
512
|
||
/* NEED_PATCH */
|
||
),
|
||
_ctx.separateInput ? (openBlock(), createElementBlock(
|
||
"div",
|
||
_hoisted_3$a,
|
||
[
|
||
withDirectives(createElementVNode("input", mergeProps({
|
||
ref: "input",
|
||
"onUpdate:modelValue": _cache[4] || (_cache[4] = ($event) => _ctx.computedInputValue = $event),
|
||
class: "cdx-chip-input__input",
|
||
disabled: _ctx.computedDisabled
|
||
}, _ctx.otherAttrs, {
|
||
onBlur: _cache[5] || (_cache[5] = (...args) => _ctx.onInputBlur && _ctx.onInputBlur(...args)),
|
||
onFocus: _cache[6] || (_cache[6] = (...args) => _ctx.onInputFocus && _ctx.onInputFocus(...args)),
|
||
onKeydown: _cache[7] || (_cache[7] = (...args) => _ctx.onInputKeydown && _ctx.onInputKeydown(...args))
|
||
}), null, 16, _hoisted_4$6), [
|
||
[vModelDynamic, _ctx.computedInputValue]
|
||
])
|
||
],
|
||
512
|
||
/* NEED_PATCH */
|
||
)) : createCommentVNode("v-if", true),
|
||
createElementVNode(
|
||
"div",
|
||
_hoisted_5$6,
|
||
toDisplayString(_ctx.statusMessageContent),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
],
|
||
38
|
||
/* CLASS, STYLE, NEED_HYDRATION */
|
||
);
|
||
}
|
||
const CdxChipInput = /* @__PURE__ */ _export_sfc(_sfc_main$p, [["render", _sfc_render$p]]);
|
||
function regExpEscape(value) {
|
||
return value.replace(/([\\{}()|.?*+\-^$[\]])/g, "\\$1");
|
||
}
|
||
const COMBINING_MARK = "[̀-ͯ҃-҉֑-ׇֽֿׁׂׅׄؐ-ًؚ-ٰٟۖ-ۜ۟-۪ۤۧۨ-ܑۭܰ-݊ަ-ް߫-߽߳ࠖ-࠙ࠛ-ࠣࠥ-ࠧࠩ-࡙࠭-࡛࣓-ࣣ࣡-ःऺ-़ा-ॏ॑-ॗॢॣঁ-ঃ়া-ৄেৈো-্ৗৢৣ৾ਁ-ਃ਼ਾ-ੂੇੈੋ-੍ੑੰੱੵઁ-ઃ઼ા-ૅે-ૉો-્ૢૣૺ-૿ଁ-ଃ଼ା-ୄେୈୋ-୍ୖୗୢୣஂா-ூெ-ைொ-்ௗఀ-ఄా-ౄె-ైొ-్ౕౖౢౣಁ-ಃ಼ಾ-ೄೆ-ೈೊ-್ೕೖೢೣഀ-ഃ഻഼ാ-ൄെ-ൈൊ-്ൗൢൣංඃ්ා-ුූෘ-ෟෲෳัิ-ฺ็-๎ັິ-ູົຼ່-ໍ༹༘༙༵༷༾༿ཱ-྄྆྇ྍ-ྗྙ-ྼ࿆ါ-ှၖ-ၙၞ-ၠၢ-ၤၧ-ၭၱ-ၴႂ-ႍႏႚ-ႝ፝-፟ᜒ-᜔ᜲ-᜴ᝒᝓᝲᝳ឴-៓៝᠋-᠍ᢅᢆᢩᤠ-ᤫᤰ-᤻ᨗ-ᨛᩕ-ᩞ᩠-᩿᩼᪰-᪾ᬀ-ᬄ᬴-᭄᭫-᭳ᮀ-ᮂᮡ-ᮭ᯦-᯳ᰤ-᰷᳐-᳔᳒-᳨᳭ᳲ-᳴᳷-᳹᷀-᷹᷻-᷿⃐-⃰⳯-⵿⳱ⷠ-〪ⷿ-゙゚〯꙯-꙲ꙴ-꙽ꚞꚟ꛰꛱ꠂ꠆ꠋꠣ-ꠧꢀꢁꢴ-ꣅ꣠-꣱ꣿꤦ-꤭ꥇ-꥓ꦀ-ꦃ꦳-꧀ꧥꨩ-ꨶꩃꩌꩍꩻ-ꩽꪰꪲ-ꪴꪷꪸꪾ꪿꫁ꫫ-ꫯꫵ꫶ꯣ-ꯪ꯬꯭ﬞ︀-️︠-︯]";
|
||
function splitStringAtMatch(query, title) {
|
||
if (!query) {
|
||
return [title, "", ""];
|
||
}
|
||
const sanitizedQuery = regExpEscape(query);
|
||
const match = new RegExp(
|
||
// Per https://www.regular-expressions.info/unicode.html, "any code point that is not a
|
||
// combining mark can be followed by any number of combining marks." See also the
|
||
// discussion in https://phabricator.wikimedia.org/T35242.
|
||
sanitizedQuery + COMBINING_MARK + "*",
|
||
"i"
|
||
).exec(title);
|
||
if (!match || match.index === void 0) {
|
||
return [title, "", ""];
|
||
}
|
||
const matchStartIndex = match.index;
|
||
const matchEndIndex = matchStartIndex + match[0].length;
|
||
const highlightedTitle = title.slice(matchStartIndex, matchEndIndex);
|
||
const beforeHighlight = title.slice(0, matchStartIndex);
|
||
const afterHighlight = title.slice(matchEndIndex, title.length);
|
||
return [beforeHighlight, highlightedTitle, afterHighlight];
|
||
}
|
||
const stringHelpers = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
||
__proto__: null,
|
||
regExpEscape,
|
||
splitStringAtMatch
|
||
}, Symbol.toStringTag, { value: "Module" }));
|
||
const _sfc_main$o = defineComponent({
|
||
name: "CdxSearchResultTitle",
|
||
props: {
|
||
/**
|
||
* Title text.
|
||
*/
|
||
title: {
|
||
type: String,
|
||
required: true
|
||
},
|
||
/**
|
||
* The current search query.
|
||
*/
|
||
searchQuery: {
|
||
type: String,
|
||
default: ""
|
||
}
|
||
},
|
||
setup: (props) => {
|
||
const titleChunks = computed(() => splitStringAtMatch(props.searchQuery, String(props.title)));
|
||
return {
|
||
titleChunks
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$m = { class: "cdx-search-result-title" };
|
||
const _hoisted_2$e = { class: "cdx-search-result-title__match" };
|
||
function _sfc_render$o(_ctx, _cache, $props, $setup, $data, $options) {
|
||
return openBlock(), createElementBlock("span", _hoisted_1$m, [
|
||
createElementVNode("bdi", null, [
|
||
createTextVNode(
|
||
toDisplayString(_ctx.titleChunks[0]),
|
||
1
|
||
/* TEXT */
|
||
),
|
||
createElementVNode(
|
||
"span",
|
||
_hoisted_2$e,
|
||
toDisplayString(_ctx.titleChunks[1]),
|
||
1
|
||
/* TEXT */
|
||
),
|
||
createTextVNode(
|
||
toDisplayString(_ctx.titleChunks[2]),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
])
|
||
]);
|
||
}
|
||
const CdxSearchResultTitle = /* @__PURE__ */ _export_sfc(_sfc_main$o, [["render", _sfc_render$o]]);
|
||
const _sfc_main$n = defineComponent({
|
||
name: "CdxMenuItem",
|
||
components: { CdxIcon, CdxThumbnail, CdxSearchResultTitle },
|
||
props: {
|
||
/**
|
||
* ID for HTML `id` attribute.
|
||
*/
|
||
id: {
|
||
type: String,
|
||
required: true
|
||
},
|
||
/**
|
||
* The value provided to the parent menu component when this menu item is selected.
|
||
*/
|
||
value: {
|
||
type: [String, Number],
|
||
required: true
|
||
},
|
||
/**
|
||
* Whether the menu item is disabled.
|
||
*/
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether this menu item is selected.
|
||
*/
|
||
selected: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether this menu item is being pressed.
|
||
*/
|
||
active: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether this menu item is visually highlighted due to hover or keyboard navigation.
|
||
*/
|
||
highlighted: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Label for the menu item. If this isn't provided, the value will be displayed instead.
|
||
*/
|
||
label: {
|
||
type: String,
|
||
default: ""
|
||
},
|
||
/**
|
||
* Text that matches current search query. Only used for search results and will be
|
||
* displayed after the label.
|
||
*/
|
||
match: {
|
||
type: String,
|
||
default: ""
|
||
},
|
||
/**
|
||
* Text that supports the label. Supporting text will appear next to the label in a more
|
||
* subtle color.
|
||
*/
|
||
supportingText: {
|
||
type: String,
|
||
default: ""
|
||
},
|
||
/**
|
||
* URL for the menu item. If provided, the content of the menu item will be wrapped in an
|
||
* anchor tag.
|
||
*/
|
||
url: {
|
||
type: String,
|
||
default: ""
|
||
},
|
||
/**
|
||
* Icon for the menu item.
|
||
*/
|
||
icon: {
|
||
type: [String, Object],
|
||
default: ""
|
||
},
|
||
/**
|
||
* Whether a thumbnail (or a placeholder icon) should be displayed.
|
||
*/
|
||
showThumbnail: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Thumbnail for the menu item.
|
||
*/
|
||
thumbnail: {
|
||
type: [Object, null],
|
||
default: null
|
||
},
|
||
/**
|
||
* Description of the menu item.
|
||
*/
|
||
description: {
|
||
type: [String, null],
|
||
default: ""
|
||
},
|
||
/**
|
||
* The search query to be highlighted within the menu item's title.
|
||
*/
|
||
searchQuery: {
|
||
type: String,
|
||
default: ""
|
||
},
|
||
/**
|
||
* Whether to bold menu item labels.
|
||
*/
|
||
boldLabel: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether to hide description text overflow via an ellipsis.
|
||
*/
|
||
hideDescriptionOverflow: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Optional language codes for label, match, supporting text, and description.
|
||
*
|
||
* If included, that language code will be added as a `lang` attribute to the element
|
||
* wrapping that text node.
|
||
* @default {}
|
||
*/
|
||
language: {
|
||
type: Object,
|
||
default: () => {
|
||
return {};
|
||
}
|
||
},
|
||
/**
|
||
* MenuItems inside a MenuButton can also support an "action" prop
|
||
*/
|
||
action: {
|
||
type: String,
|
||
default: "default"
|
||
},
|
||
/**
|
||
* Whether this menu is in multiselect mode.
|
||
*/
|
||
multiselect: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
},
|
||
emits: [
|
||
/**
|
||
* Emitted when the menu item becomes selected, active or highlighted in response to
|
||
* user interaction. Handled in the Menu component.
|
||
*
|
||
* @property {MenuState} menuState State to change
|
||
* @property {boolean} setState Whether to set that state to this menu item
|
||
*/
|
||
"change"
|
||
],
|
||
setup: (props, { emit }) => {
|
||
const onMouseMove = () => {
|
||
if (!props.highlighted) {
|
||
emit("change", "highlighted", true);
|
||
}
|
||
};
|
||
const onMouseLeave = () => {
|
||
emit("change", "highlighted", false);
|
||
};
|
||
const onMouseDown = (e) => {
|
||
if (e.button === 0) {
|
||
emit("change", "active", true);
|
||
}
|
||
};
|
||
const onClick = () => {
|
||
emit("change", "selected", true);
|
||
};
|
||
const highlightQuery = computed(() => props.searchQuery.length > 0);
|
||
const rootClasses = computed(() => {
|
||
return {
|
||
"cdx-menu-item--selected": props.selected,
|
||
// Only show the active visual state when the menu item is both active and
|
||
// highlighted. This means, on mousedown -> mouseleave, the menu item is still
|
||
// technically tracked by the menu as active, but will not appear active to the
|
||
// user. This also means in the case of mousedown -> mouseleave -> mouseenter, the
|
||
// menu item will appear active again, and on click (releasing the mouse button),
|
||
// the item will be selected.
|
||
"cdx-menu-item--active": props.active && props.highlighted,
|
||
"cdx-menu-item--highlighted": props.highlighted,
|
||
"cdx-menu-item--destructive": props.action && props.action === "destructive",
|
||
"cdx-menu-item--enabled": !props.disabled,
|
||
"cdx-menu-item--disabled": props.disabled,
|
||
"cdx-menu-item--highlight-query": highlightQuery.value,
|
||
"cdx-menu-item--bold-label": props.boldLabel,
|
||
"cdx-menu-item--has-description": !!props.description,
|
||
"cdx-menu-item--hide-description-overflow": props.hideDescriptionOverflow
|
||
};
|
||
});
|
||
const contentTag = computed(() => props.url ? "a" : "span");
|
||
const title = computed(() => props.label || String(props.value));
|
||
return {
|
||
onMouseMove,
|
||
onMouseLeave,
|
||
onMouseDown,
|
||
onClick,
|
||
highlightQuery,
|
||
rootClasses,
|
||
contentTag,
|
||
title,
|
||
cdxIconCheck: K3
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$l = ["id", "aria-disabled", "aria-selected", "aria-checked"];
|
||
const _hoisted_2$d = { class: "cdx-menu-item__text" };
|
||
const _hoisted_3$9 = ["lang"];
|
||
const _hoisted_4$5 = ["lang"];
|
||
const _hoisted_5$5 = ["lang"];
|
||
const _hoisted_6$5 = ["lang"];
|
||
function _sfc_render$n(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_thumbnail = resolveComponent("cdx-thumbnail");
|
||
const _component_cdx_icon = resolveComponent("cdx-icon");
|
||
const _component_cdx_search_result_title = resolveComponent("cdx-search-result-title");
|
||
return openBlock(), createElementBlock("li", {
|
||
id: _ctx.id,
|
||
role: "option",
|
||
class: normalizeClass(["cdx-menu-item", _ctx.rootClasses]),
|
||
"aria-disabled": _ctx.disabled,
|
||
"aria-selected": _ctx.selected && !_ctx.multiselect ? true : void 0,
|
||
"aria-checked": _ctx.selected && _ctx.multiselect ? true : void 0,
|
||
onMousemove: _cache[0] || (_cache[0] = (...args) => _ctx.onMouseMove && _ctx.onMouseMove(...args)),
|
||
onMouseleave: _cache[1] || (_cache[1] = (...args) => _ctx.onMouseLeave && _ctx.onMouseLeave(...args)),
|
||
onMousedown: _cache[2] || (_cache[2] = withModifiers((...args) => _ctx.onMouseDown && _ctx.onMouseDown(...args), ["prevent"])),
|
||
onClick: _cache[3] || (_cache[3] = (...args) => _ctx.onClick && _ctx.onClick(...args))
|
||
}, [
|
||
renderSlot(_ctx.$slots, "default", {}, () => [
|
||
(openBlock(), createBlock(resolveDynamicComponent(_ctx.contentTag), {
|
||
href: _ctx.url ? _ctx.url : void 0,
|
||
class: "cdx-menu-item__content"
|
||
}, {
|
||
default: withCtx(() => {
|
||
var _a, _b, _c, _d, _e, _f;
|
||
return [
|
||
_ctx.showThumbnail ? (openBlock(), createBlock(_component_cdx_thumbnail, {
|
||
key: 0,
|
||
thumbnail: _ctx.thumbnail,
|
||
class: "cdx-menu-item__thumbnail"
|
||
}, null, 8, ["thumbnail"])) : _ctx.icon ? (openBlock(), createBlock(_component_cdx_icon, {
|
||
key: 1,
|
||
icon: _ctx.icon,
|
||
class: "cdx-menu-item__icon"
|
||
}, null, 8, ["icon"])) : createCommentVNode("v-if", true),
|
||
createElementVNode("span", _hoisted_2$d, [
|
||
_ctx.highlightQuery ? (openBlock(), createBlock(_component_cdx_search_result_title, {
|
||
key: 0,
|
||
title: _ctx.title,
|
||
"search-query": _ctx.searchQuery,
|
||
lang: (_a = _ctx.language) == null ? void 0 : _a.label
|
||
}, null, 8, ["title", "search-query", "lang"])) : (openBlock(), createElementBlock("span", {
|
||
key: 1,
|
||
class: "cdx-menu-item__text__label",
|
||
lang: (_b = _ctx.language) == null ? void 0 : _b.label
|
||
}, [
|
||
createElementVNode(
|
||
"bdi",
|
||
null,
|
||
toDisplayString(_ctx.title),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
], 8, _hoisted_3$9)),
|
||
_ctx.match ? (openBlock(), createElementBlock(
|
||
Fragment,
|
||
{ key: 2 },
|
||
[
|
||
createTextVNode(toDisplayString(" ") + " "),
|
||
_ctx.highlightQuery ? (openBlock(), createBlock(_component_cdx_search_result_title, {
|
||
key: 0,
|
||
title: _ctx.match,
|
||
"search-query": _ctx.searchQuery,
|
||
lang: (_c = _ctx.language) == null ? void 0 : _c.match
|
||
}, null, 8, ["title", "search-query", "lang"])) : (openBlock(), createElementBlock("span", {
|
||
key: 1,
|
||
class: "cdx-menu-item__text__match",
|
||
lang: (_d = _ctx.language) == null ? void 0 : _d.match
|
||
}, [
|
||
createElementVNode(
|
||
"bdi",
|
||
null,
|
||
toDisplayString(_ctx.match),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
], 8, _hoisted_4$5))
|
||
],
|
||
64
|
||
/* STABLE_FRAGMENT */
|
||
)) : createCommentVNode("v-if", true),
|
||
_ctx.supportingText ? (openBlock(), createElementBlock(
|
||
Fragment,
|
||
{ key: 3 },
|
||
[
|
||
createTextVNode(toDisplayString(" ") + " "),
|
||
createElementVNode("span", {
|
||
class: "cdx-menu-item__text__supporting-text",
|
||
lang: (_e = _ctx.language) == null ? void 0 : _e.supportingText
|
||
}, [
|
||
createElementVNode(
|
||
"bdi",
|
||
null,
|
||
toDisplayString(_ctx.supportingText),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
], 8, _hoisted_5$5)
|
||
],
|
||
64
|
||
/* STABLE_FRAGMENT */
|
||
)) : createCommentVNode("v-if", true),
|
||
_ctx.description ? (openBlock(), createElementBlock("span", {
|
||
key: 4,
|
||
class: "cdx-menu-item__text__description",
|
||
lang: (_f = _ctx.language) == null ? void 0 : _f.description
|
||
}, [
|
||
createElementVNode(
|
||
"bdi",
|
||
null,
|
||
toDisplayString(_ctx.description),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
], 8, _hoisted_6$5)) : createCommentVNode("v-if", true)
|
||
]),
|
||
_ctx.multiselect && _ctx.selected ? (openBlock(), createBlock(_component_cdx_icon, {
|
||
key: 2,
|
||
icon: _ctx.cdxIconCheck,
|
||
size: "small",
|
||
class: "cdx-menu-item__selected-icon"
|
||
}, null, 8, ["icon"])) : createCommentVNode("v-if", true)
|
||
];
|
||
}),
|
||
_: 1
|
||
/* STABLE */
|
||
}, 8, ["href"]))
|
||
])
|
||
], 42, _hoisted_1$l);
|
||
}
|
||
const CdxMenuItem = /* @__PURE__ */ _export_sfc(_sfc_main$n, [["render", _sfc_render$n]]);
|
||
const _sfc_main$m = defineComponent({
|
||
name: "CdxProgressBar",
|
||
props: {
|
||
/**
|
||
* Whether this is the smaller, inline variant.
|
||
*/
|
||
inline: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether the progress bar is disabled.
|
||
*/
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
},
|
||
setup(props, { attrs }) {
|
||
useWarnOnce(
|
||
() => !props.inline && !attrs["aria-label"] && !attrs["aria-hidden"],
|
||
"CdxProgressBar: Progress bars require one of the following attribute, aria-label or aria-hidden. See documentation on https://doc.wikimedia.org/codex/latest/components/demos/progressbar.html"
|
||
);
|
||
const rootClasses = computed(() => {
|
||
return {
|
||
"cdx-progress-bar--block": !props.inline,
|
||
"cdx-progress-bar--inline": props.inline,
|
||
"cdx-progress-bar--enabled": !props.disabled,
|
||
"cdx-progress-bar--disabled": props.disabled
|
||
};
|
||
});
|
||
const computedAriaHidden = computed(() => {
|
||
return props.inline ? "true" : void 0;
|
||
});
|
||
return {
|
||
rootClasses,
|
||
computedAriaHidden
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$k = ["aria-hidden", "aria-disabled"];
|
||
const _hoisted_2$c = /* @__PURE__ */ createElementVNode(
|
||
"div",
|
||
{ class: "cdx-progress-bar__bar" },
|
||
null,
|
||
-1
|
||
/* HOISTED */
|
||
);
|
||
const _hoisted_3$8 = [
|
||
_hoisted_2$c
|
||
];
|
||
function _sfc_render$m(_ctx, _cache, $props, $setup, $data, $options) {
|
||
return openBlock(), createElementBlock("div", {
|
||
class: normalizeClass(["cdx-progress-bar", _ctx.rootClasses]),
|
||
role: "progressbar",
|
||
"aria-hidden": _ctx.computedAriaHidden,
|
||
"aria-disabled": _ctx.disabled
|
||
}, _hoisted_3$8, 10, _hoisted_1$k);
|
||
}
|
||
const CdxProgressBar = /* @__PURE__ */ _export_sfc(_sfc_main$m, [["render", _sfc_render$m]]);
|
||
function useIntersectionObserver(templateRef, observerOptions) {
|
||
const intersectionRef = ref(false);
|
||
let mounted = false;
|
||
if (typeof window !== "object") {
|
||
return intersectionRef;
|
||
}
|
||
if (!("IntersectionObserver" in window && "IntersectionObserverEntry" in window && "intersectionRatio" in window.IntersectionObserverEntry.prototype)) {
|
||
return intersectionRef;
|
||
}
|
||
const observer = new window.IntersectionObserver(
|
||
(entries) => {
|
||
const entry = entries[0];
|
||
if (entry) {
|
||
intersectionRef.value = entry.isIntersecting;
|
||
}
|
||
},
|
||
observerOptions
|
||
);
|
||
onMounted(() => {
|
||
mounted = true;
|
||
if (templateRef.value) {
|
||
observer.observe(templateRef.value);
|
||
}
|
||
});
|
||
onUnmounted(() => {
|
||
mounted = false;
|
||
observer.disconnect();
|
||
});
|
||
watch(templateRef, (newElement) => {
|
||
if (!mounted) {
|
||
return;
|
||
}
|
||
observer.disconnect();
|
||
intersectionRef.value = false;
|
||
if (newElement) {
|
||
observer.observe(newElement);
|
||
}
|
||
});
|
||
return intersectionRef;
|
||
}
|
||
function selectedIsArray(selected) {
|
||
return selected !== null && Array.isArray(selected);
|
||
}
|
||
const _sfc_main$l = defineComponent({
|
||
name: "CdxMenu",
|
||
components: {
|
||
CdxMenuItem,
|
||
CdxProgressBar
|
||
},
|
||
/**
|
||
* Attributes, besides class and style, will be passed to the <ul> element.
|
||
*/
|
||
inheritAttrs: false,
|
||
props: {
|
||
/** Menu items. See the MenuItemData type. */
|
||
menuItems: {
|
||
type: Array,
|
||
required: true
|
||
},
|
||
/**
|
||
* Interactive footer item.
|
||
*
|
||
* This is a special menu item which is pinned to the bottom of the menu. When scrolling is
|
||
* enabled within the menu, the footer item will always be visible at the bottom of the
|
||
* menu. When scrolling is not enabled, the footer item will simply appear as the last menu
|
||
* item.
|
||
*
|
||
* The footer item is selectable, like other menu items.
|
||
*/
|
||
footer: {
|
||
type: Object,
|
||
default: null
|
||
},
|
||
/**
|
||
* Value(s) of the selected menu item(s). A single value for single-select, or an array of
|
||
* values for multi-select.
|
||
*
|
||
* Must be bound with `v-model:selected`.
|
||
*
|
||
* The property should be initialized to `null` (for single-select) or an empty array (for
|
||
* multi-select) rather than using a falsy value.
|
||
*/
|
||
selected: {
|
||
// eslint-disable-next-line max-len
|
||
type: [String, Number, Array, null],
|
||
required: true
|
||
},
|
||
/**
|
||
* Whether the menu is expanded. Must be bound with `v-model:expanded`.
|
||
*/
|
||
expanded: {
|
||
type: Boolean,
|
||
required: true
|
||
},
|
||
/**
|
||
* Whether to display pending state indicators. Meant to indicate that new menu items are
|
||
* being fetched or computed.
|
||
*
|
||
* When true, the menu will expand if not already expanded, and an inline progress bar will
|
||
* display. If there are no menu items yet, a message can be displayed in the `pending`
|
||
* slot, e.g. "Loading results".
|
||
*/
|
||
showPending: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Limit the number of menu items to display before scrolling.
|
||
*
|
||
* Setting this prop to anything falsy will show all menu items.
|
||
*
|
||
* By default, all menu items are shown.
|
||
*/
|
||
visibleItemLimit: {
|
||
type: Number,
|
||
default: null
|
||
},
|
||
/**
|
||
* Whether menu item thumbnails (or a placeholder icon) should be displayed.
|
||
*/
|
||
showThumbnail: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether to bold menu item labels.
|
||
*/
|
||
boldLabel: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether to hide description text overflow via an ellipsis.
|
||
*/
|
||
hideDescriptionOverflow: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* The search query to be highlighted within the menu items' titles.
|
||
*/
|
||
searchQuery: {
|
||
type: String,
|
||
default: ""
|
||
},
|
||
/**
|
||
* Whether to show the `no-results` slot content.
|
||
*
|
||
* The Menu component automatically shows this slot when there is content in the
|
||
* `no-results` slot and there are zero menu items. However, some components may need to
|
||
* customize this behavior, e.g. to show the slot even when there is at least one menu item.
|
||
* This prop can be used to override the default Menu behavior.
|
||
*
|
||
* Possible values:
|
||
* `null` (default): the `no-results` slot will display only if there are zero menu items.
|
||
* `true`: the `no-results` slot will display, regardless of number of menu items.
|
||
* `false`: the `no-results` slot will not display, regardless of number of menu items.
|
||
*/
|
||
showNoResultsSlot: {
|
||
type: Boolean,
|
||
default: null
|
||
}
|
||
},
|
||
emits: [
|
||
// Don't remove the spaces in the "string | number | null" type below; removing these
|
||
// spaces causes the documentation to render the type as "union" instead.
|
||
// Keep property descriptions on a single line or they will get cut off.
|
||
/**
|
||
* When the selected menu item changes.
|
||
*
|
||
* Property will be a single value or `null` in single-select mode, or an array of values or
|
||
* an empty array in multiselect mode.
|
||
*
|
||
* @property {MenuItemValue | MenuItemValue[] | null} selectedValue selected value or values
|
||
*/
|
||
"update:selected",
|
||
/**
|
||
* When the menu opens or closes.
|
||
*
|
||
* @property {boolean} newValue The new expanded state (true for open, false for closed)
|
||
*/
|
||
"update:expanded",
|
||
/**
|
||
* When a menu item is clicked.
|
||
*
|
||
* Typically, components with menus will respond to the selected value change, but
|
||
* occasionally, a component might want to react specifically when a menu item is clicked.
|
||
*
|
||
* @property {MenuItemDataWithId} menuItem The menu item that was clicked
|
||
*/
|
||
"menu-item-click",
|
||
/**
|
||
* When a menu item is highlighted via keyboard navigation.
|
||
*
|
||
* @property {MenuItemDataWithId} highlightedMenuItem The menu item
|
||
* was highlighted
|
||
*/
|
||
"menu-item-keyboard-navigation",
|
||
/**
|
||
* When the user scrolls towards the bottom of the menu.
|
||
*
|
||
* If it is possible to add or load more menu items, then now would be a good moment
|
||
* so that the user can experience infinite scrolling.
|
||
*/
|
||
"load-more"
|
||
],
|
||
expose: [
|
||
"isExpanded",
|
||
"clearActive",
|
||
"getHighlightedMenuItem",
|
||
"getHighlightedViaKeyboard",
|
||
"delegateKeyNavigation"
|
||
],
|
||
setup(props, { emit, slots, attrs }) {
|
||
const computedMenuItems = computed(() => {
|
||
const menuItemsWithFooter = props.footer && props.menuItems ? [...props.menuItems, props.footer] : props.menuItems;
|
||
return menuItemsWithFooter.map((menuItem) => __spreadProps(__spreadValues({}, menuItem), {
|
||
id: useGeneratedId("menu-item")
|
||
}));
|
||
});
|
||
const computedShowNoResultsSlot = computed(() => {
|
||
if (!slots["no-results"]) {
|
||
return false;
|
||
}
|
||
if (props.showNoResultsSlot !== null) {
|
||
return props.showNoResultsSlot;
|
||
}
|
||
return computedMenuItems.value.length === 0;
|
||
});
|
||
const highlightedMenuItem = ref(null);
|
||
const highlightedViaKeyboard = ref(false);
|
||
const activeMenuItem = ref(null);
|
||
const ariaRelevant = "additions removals";
|
||
let keyBuffer = "";
|
||
let keyBufferTimeout = null;
|
||
function clearKeyBuffer() {
|
||
keyBuffer = "";
|
||
if (keyBufferTimeout !== null) {
|
||
clearTimeout(keyBufferTimeout);
|
||
keyBufferTimeout = null;
|
||
}
|
||
}
|
||
function resetKeyBufferTimeout() {
|
||
if (keyBufferTimeout !== null) {
|
||
clearTimeout(keyBufferTimeout);
|
||
}
|
||
keyBufferTimeout = setTimeout(clearKeyBuffer, 1500);
|
||
}
|
||
function findFirstSelectedMenuItem() {
|
||
var _a;
|
||
return (_a = computedMenuItems.value.find(
|
||
(menuItem) => selectedIsArray(props.selected) ? props.selected.indexOf(menuItem.value) !== -1 : menuItem.value === props.selected
|
||
)) != null ? _a : null;
|
||
}
|
||
const isMultiselect = computed(() => selectedIsArray(props.selected));
|
||
function isItemSelected(value) {
|
||
return selectedIsArray(props.selected) ? props.selected.indexOf(value) !== -1 : value === props.selected;
|
||
}
|
||
function updateSelected(value) {
|
||
if (selectedIsArray(props.selected)) {
|
||
const newSelected = props.selected.indexOf(value) === -1 ? props.selected.concat(value) : props.selected.filter((item) => item !== value);
|
||
emit("update:selected", newSelected);
|
||
} else {
|
||
emit("update:selected", value);
|
||
}
|
||
}
|
||
function handleMenuItemChange(menuState, menuItem) {
|
||
if (menuItem && menuItem.disabled) {
|
||
return;
|
||
}
|
||
switch (menuState) {
|
||
case "selected":
|
||
if (menuItem) {
|
||
updateSelected(menuItem.value);
|
||
}
|
||
if (!isMultiselect.value) {
|
||
emit("update:expanded", false);
|
||
}
|
||
activeMenuItem.value = null;
|
||
break;
|
||
case "highlighted":
|
||
highlightedMenuItem.value = menuItem != null ? menuItem : null;
|
||
highlightedViaKeyboard.value = false;
|
||
break;
|
||
case "highlightedViaKeyboard":
|
||
highlightedMenuItem.value = menuItem != null ? menuItem : null;
|
||
highlightedViaKeyboard.value = true;
|
||
break;
|
||
case "active":
|
||
activeMenuItem.value = menuItem != null ? menuItem : null;
|
||
break;
|
||
}
|
||
}
|
||
const highlightedMenuItemIndex = computed(() => {
|
||
if (highlightedMenuItem.value === null) {
|
||
return;
|
||
}
|
||
return computedMenuItems.value.findIndex(
|
||
(menuItem) => (
|
||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||
menuItem.value === highlightedMenuItem.value.value
|
||
)
|
||
);
|
||
});
|
||
function handleHighlightViaKeyboard(newHighlightedMenuItem) {
|
||
if (!newHighlightedMenuItem) {
|
||
return;
|
||
}
|
||
handleMenuItemChange("highlightedViaKeyboard", newHighlightedMenuItem);
|
||
emit("menu-item-keyboard-navigation", newHighlightedMenuItem);
|
||
}
|
||
function highlightPrev(highlightedIndex) {
|
||
var _a;
|
||
const findPrevEnabled = (startIndex) => {
|
||
for (let index = startIndex - 1; index >= 0; index--) {
|
||
if (!computedMenuItems.value[index].disabled) {
|
||
return computedMenuItems.value[index];
|
||
}
|
||
}
|
||
};
|
||
highlightedIndex = highlightedIndex != null ? highlightedIndex : computedMenuItems.value.length;
|
||
const prev = (_a = findPrevEnabled(highlightedIndex)) != null ? _a : findPrevEnabled(computedMenuItems.value.length);
|
||
handleHighlightViaKeyboard(prev);
|
||
}
|
||
function highlightNext(highlightedIndex) {
|
||
var _a;
|
||
const findNextEnabled = (startIndex) => computedMenuItems.value.find((item, index) => !item.disabled && index > startIndex);
|
||
highlightedIndex = highlightedIndex != null ? highlightedIndex : -1;
|
||
const next = (_a = findNextEnabled(highlightedIndex)) != null ? _a : findNextEnabled(-1);
|
||
handleHighlightViaKeyboard(next);
|
||
}
|
||
function handleCharacterNavigation(e) {
|
||
if (e.key === "Clear") {
|
||
clearKeyBuffer();
|
||
return true;
|
||
}
|
||
if (e.key === "Backspace") {
|
||
keyBuffer = keyBuffer.slice(0, -1);
|
||
resetKeyBufferTimeout();
|
||
return true;
|
||
}
|
||
if (e.key.length === 1 && !e.metaKey && !e.ctrlKey && !e.altKey) {
|
||
if (!props.expanded) {
|
||
emit("update:expanded", true);
|
||
}
|
||
if (e.key === " " && keyBuffer.length < 1) {
|
||
return false;
|
||
}
|
||
keyBuffer += e.key.toLowerCase();
|
||
const isRepeatedCharacter = keyBuffer.length > 1 && keyBuffer.split("").every((char) => char === keyBuffer[0]);
|
||
let itemsToMatch = computedMenuItems.value;
|
||
let stringToMatch = keyBuffer;
|
||
if (isRepeatedCharacter && highlightedMenuItemIndex.value !== void 0) {
|
||
itemsToMatch = itemsToMatch.slice(highlightedMenuItemIndex.value + 1).concat(itemsToMatch.slice(0, highlightedMenuItemIndex.value));
|
||
stringToMatch = keyBuffer[0];
|
||
}
|
||
const matchingItem = itemsToMatch.find(
|
||
(item) => {
|
||
var _a;
|
||
return !item.disabled && String((_a = item.label) != null ? _a : item.value).toLowerCase().startsWith(stringToMatch);
|
||
}
|
||
);
|
||
if (matchingItem) {
|
||
handleMenuItemChange("highlightedViaKeyboard", matchingItem);
|
||
maybeScrollIntoView();
|
||
}
|
||
resetKeyBufferTimeout();
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
function handleKeyNavigation(e, { prevent = true, characterNavigation = false } = {}) {
|
||
if (characterNavigation) {
|
||
if (handleCharacterNavigation(e)) {
|
||
e.preventDefault();
|
||
return true;
|
||
}
|
||
clearKeyBuffer();
|
||
}
|
||
function maybePrevent() {
|
||
if (prevent) {
|
||
e.preventDefault();
|
||
e.stopPropagation();
|
||
}
|
||
}
|
||
switch (e.key) {
|
||
case "Enter":
|
||
case " ":
|
||
maybePrevent();
|
||
if (props.expanded) {
|
||
if (highlightedMenuItem.value && highlightedViaKeyboard.value) {
|
||
updateSelected(highlightedMenuItem.value.value);
|
||
}
|
||
if (!isMultiselect.value) {
|
||
emit("update:expanded", false);
|
||
}
|
||
} else {
|
||
emit("update:expanded", true);
|
||
}
|
||
return true;
|
||
case "Tab":
|
||
if (props.expanded) {
|
||
if (highlightedMenuItem.value && highlightedViaKeyboard.value && !isMultiselect.value) {
|
||
updateSelected(highlightedMenuItem.value.value);
|
||
emit("update:expanded", false);
|
||
}
|
||
}
|
||
return true;
|
||
case "ArrowUp":
|
||
maybePrevent();
|
||
if (props.expanded) {
|
||
if (highlightedMenuItem.value === null) {
|
||
handleMenuItemChange("highlightedViaKeyboard", findFirstSelectedMenuItem());
|
||
}
|
||
highlightPrev(highlightedMenuItemIndex.value);
|
||
} else {
|
||
emit("update:expanded", true);
|
||
}
|
||
maybeScrollIntoView();
|
||
return true;
|
||
case "ArrowDown":
|
||
maybePrevent();
|
||
if (props.expanded) {
|
||
if (highlightedMenuItem.value === null) {
|
||
handleMenuItemChange("highlightedViaKeyboard", findFirstSelectedMenuItem());
|
||
}
|
||
highlightNext(highlightedMenuItemIndex.value);
|
||
} else {
|
||
emit("update:expanded", true);
|
||
}
|
||
maybeScrollIntoView();
|
||
return true;
|
||
case "Home":
|
||
maybePrevent();
|
||
if (props.expanded) {
|
||
if (highlightedMenuItem.value === null) {
|
||
handleMenuItemChange("highlightedViaKeyboard", findFirstSelectedMenuItem());
|
||
}
|
||
highlightNext();
|
||
} else {
|
||
emit("update:expanded", true);
|
||
}
|
||
maybeScrollIntoView();
|
||
return true;
|
||
case "End":
|
||
maybePrevent();
|
||
if (props.expanded) {
|
||
if (highlightedMenuItem.value === null) {
|
||
handleMenuItemChange("highlightedViaKeyboard", findFirstSelectedMenuItem());
|
||
}
|
||
highlightPrev();
|
||
} else {
|
||
emit("update:expanded", true);
|
||
}
|
||
maybeScrollIntoView();
|
||
return true;
|
||
case "Escape":
|
||
maybePrevent();
|
||
emit("update:expanded", false);
|
||
return true;
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
function onMouseUp() {
|
||
handleMenuItemChange("active", null);
|
||
}
|
||
const menuItemElements = [];
|
||
const loadMoreTriggerElement = ref(void 0);
|
||
const isTriggerVisible = useIntersectionObserver(
|
||
loadMoreTriggerElement,
|
||
{ threshold: 0.8 }
|
||
);
|
||
watch(isTriggerVisible, (value) => {
|
||
if (value) {
|
||
emit("load-more");
|
||
}
|
||
});
|
||
function assignTemplateRef(templateRef, index) {
|
||
if (templateRef) {
|
||
menuItemElements[index] = templateRef.$el;
|
||
const visibleItemLimit = props.visibleItemLimit;
|
||
if (!visibleItemLimit || props.menuItems.length < visibleItemLimit) {
|
||
return;
|
||
}
|
||
const loadMoreThreshold = Math.min(
|
||
visibleItemLimit,
|
||
Math.max(2, Math.floor(0.2 * props.menuItems.length))
|
||
);
|
||
if (index === props.menuItems.length - loadMoreThreshold) {
|
||
loadMoreTriggerElement.value = templateRef.$el;
|
||
}
|
||
}
|
||
}
|
||
const menuListbox = ref();
|
||
function maybeScrollIntoView() {
|
||
const isListboxScrollable = menuListbox.value && menuListbox.value.scrollHeight > menuListbox.value.clientHeight;
|
||
if (highlightedMenuItemIndex.value === void 0 || !isListboxScrollable) {
|
||
return;
|
||
}
|
||
const scrollIndex = highlightedMenuItemIndex.value >= 0 ? highlightedMenuItemIndex.value : 0;
|
||
menuItemElements[scrollIndex].scrollIntoView({
|
||
behavior: "smooth",
|
||
block: "nearest"
|
||
});
|
||
}
|
||
const maxMenuHeight = ref(null);
|
||
const footerHeight = ref(null);
|
||
function resizeMenu() {
|
||
return __async(this, null, function* () {
|
||
yield nextTick();
|
||
updateFooterHeight();
|
||
updateMaxMenuHeight();
|
||
yield nextTick();
|
||
maybeScrollIntoView();
|
||
});
|
||
}
|
||
function updateFooterHeight() {
|
||
if (props.footer) {
|
||
const footerElement = menuItemElements[menuItemElements.length - 1];
|
||
footerHeight.value = footerElement.scrollHeight;
|
||
} else {
|
||
footerHeight.value = null;
|
||
}
|
||
}
|
||
function updateMaxMenuHeight() {
|
||
if (!props.visibleItemLimit || menuItemElements.length <= props.visibleItemLimit) {
|
||
maxMenuHeight.value = null;
|
||
return;
|
||
}
|
||
const firstMenuItemTop = menuItemElements[0].getBoundingClientRect().top;
|
||
const firstHiddenMenuItemTop = menuItemElements[props.visibleItemLimit].getBoundingClientRect().top;
|
||
maxMenuHeight.value = firstHiddenMenuItemTop - firstMenuItemTop + 2;
|
||
}
|
||
onMounted(() => {
|
||
document.addEventListener("mouseup", onMouseUp);
|
||
});
|
||
onUnmounted(() => {
|
||
document.removeEventListener("mouseup", onMouseUp);
|
||
});
|
||
watch(toRef(props, "expanded"), (newVal) => __async(this, null, function* () {
|
||
if (newVal) {
|
||
const selectedMenuItem = findFirstSelectedMenuItem();
|
||
if (selectedMenuItem && !highlightedMenuItem.value) {
|
||
handleMenuItemChange("highlighted", selectedMenuItem);
|
||
}
|
||
yield resizeMenu();
|
||
} else {
|
||
handleMenuItemChange("highlighted", null);
|
||
}
|
||
}));
|
||
watch(toRef(props, "menuItems"), (newPropMenuItems) => __async(this, null, function* () {
|
||
if (newPropMenuItems.length < menuItemElements.length) {
|
||
menuItemElements.length = newPropMenuItems.length;
|
||
}
|
||
if (props.expanded) {
|
||
yield resizeMenu();
|
||
}
|
||
}), { deep: true });
|
||
const listBoxStyle = computed(() => {
|
||
return {
|
||
"max-height": maxMenuHeight.value ? "".concat(maxMenuHeight.value, "px") : void 0,
|
||
"margin-bottom": footerHeight.value ? "".concat(footerHeight.value, "px") : void 0
|
||
};
|
||
});
|
||
const internalClasses = computed(() => {
|
||
return {
|
||
"cdx-menu--has-footer": !!props.footer
|
||
};
|
||
});
|
||
const {
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrs
|
||
} = useSplitAttributes(attrs, internalClasses);
|
||
return {
|
||
listBoxStyle,
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrs,
|
||
assignTemplateRef,
|
||
computedMenuItems,
|
||
computedShowNoResultsSlot,
|
||
highlightedMenuItem,
|
||
highlightedViaKeyboard,
|
||
activeMenuItem,
|
||
handleMenuItemChange,
|
||
handleKeyNavigation,
|
||
ariaRelevant,
|
||
isMultiselect,
|
||
isItemSelected,
|
||
menuListbox
|
||
};
|
||
},
|
||
// Public methods
|
||
// These must be in the methods block, not in the setup function, otherwise their documentation
|
||
// won't be picked up by vue-docgen
|
||
methods: {
|
||
/**
|
||
* Returns whether the menu is expanded.
|
||
*
|
||
* @return {boolean}
|
||
*/
|
||
isExpanded() {
|
||
return this.expanded;
|
||
},
|
||
/**
|
||
* Get the highlighted menu item, if any.
|
||
*
|
||
* The parent component should set `aria-activedescendant` to the `.id` property of the
|
||
* object returned by this method. If this method returns null, `aria-activedescendant`
|
||
* should not be set.
|
||
*
|
||
* @public
|
||
* @return {MenuItemDataWithId|null} The highlighted menu item,
|
||
* or null if no item is highlighted or if the menu is closed.
|
||
*/
|
||
getHighlightedMenuItem() {
|
||
return this.expanded ? this.highlightedMenuItem : null;
|
||
},
|
||
/**
|
||
* Get whether the last highlighted item was highlighted via the keyboard.
|
||
*
|
||
* @public
|
||
* @return {boolean} Whether the last highlighted menu item was highlighted via keyboard.
|
||
*/
|
||
getHighlightedViaKeyboard() {
|
||
return this.highlightedViaKeyboard;
|
||
},
|
||
/**
|
||
* Ensure no menu item is active. This unsets the active item if there is one.
|
||
*
|
||
* @public
|
||
*/
|
||
clearActive() {
|
||
this.handleMenuItemChange("active", null);
|
||
},
|
||
/**
|
||
* Handles all necessary keyboard navigation.
|
||
*
|
||
* The parent component should listen for keydown events on its focusable element,
|
||
* and pass those events to this method. Events for arrow keys, tab and enter are handled
|
||
* by this method. If a different key was pressed, this method will return false to indicate
|
||
* that it didn't handle the event.
|
||
*
|
||
* @public
|
||
* @param event {KeyboardEvent} Keydown event object
|
||
* @param options
|
||
* @param options.prevent {boolean} If false, do not call e.preventDefault() or
|
||
* e.stopPropagation()
|
||
* @param options.characterNavigation {boolean}
|
||
* @return Whether the event was handled
|
||
*/
|
||
delegateKeyNavigation(event, { prevent = true, characterNavigation = false } = {}) {
|
||
return this.handleKeyNavigation(event, { prevent, characterNavigation });
|
||
}
|
||
}
|
||
});
|
||
const _hoisted_1$j = ["aria-live", "aria-relevant", "aria-multiselectable"];
|
||
const _hoisted_2$b = {
|
||
key: 0,
|
||
class: "cdx-menu__pending cdx-menu-item"
|
||
};
|
||
const _hoisted_3$7 = {
|
||
key: 1,
|
||
class: "cdx-menu__no-results cdx-menu-item",
|
||
role: "option"
|
||
};
|
||
function _sfc_render$l(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_menu_item = resolveComponent("cdx-menu-item");
|
||
const _component_cdx_progress_bar = resolveComponent("cdx-progress-bar");
|
||
return withDirectives((openBlock(), createElementBlock(
|
||
"div",
|
||
{
|
||
class: normalizeClass(["cdx-menu", _ctx.rootClasses]),
|
||
style: normalizeStyle(_ctx.rootStyle)
|
||
},
|
||
[
|
||
createElementVNode("ul", mergeProps({
|
||
ref: "menuListbox",
|
||
class: "cdx-menu__listbox",
|
||
role: "listbox",
|
||
style: _ctx.listBoxStyle,
|
||
"aria-live": _ctx.showPending ? "polite" : void 0,
|
||
"aria-relevant": _ctx.showPending ? _ctx.ariaRelevant : void 0,
|
||
"aria-multiselectable": _ctx.isMultiselect ? true : void 0
|
||
}, _ctx.otherAttrs), [
|
||
_ctx.showPending && _ctx.computedMenuItems.length === 0 && _ctx.$slots.pending ? (openBlock(), createElementBlock("li", _hoisted_2$b, [
|
||
renderSlot(_ctx.$slots, "pending")
|
||
])) : createCommentVNode("v-if", true),
|
||
_ctx.computedShowNoResultsSlot ? (openBlock(), createElementBlock("li", _hoisted_3$7, [
|
||
renderSlot(_ctx.$slots, "no-results")
|
||
])) : createCommentVNode("v-if", true),
|
||
(openBlock(true), createElementBlock(
|
||
Fragment,
|
||
null,
|
||
renderList(_ctx.computedMenuItems, (menuItem, index) => {
|
||
var _a, _b;
|
||
return openBlock(), createBlock(_component_cdx_menu_item, mergeProps({
|
||
key: menuItem.value,
|
||
ref_for: true,
|
||
ref: (ref2) => _ctx.assignTemplateRef(ref2, index)
|
||
}, menuItem, {
|
||
selected: _ctx.isItemSelected(menuItem.value),
|
||
active: menuItem.value === ((_a = _ctx.activeMenuItem) == null ? void 0 : _a.value),
|
||
highlighted: menuItem.value === ((_b = _ctx.highlightedMenuItem) == null ? void 0 : _b.value),
|
||
"show-thumbnail": _ctx.showThumbnail,
|
||
"bold-label": _ctx.boldLabel,
|
||
"hide-description-overflow": _ctx.hideDescriptionOverflow,
|
||
"search-query": _ctx.searchQuery,
|
||
multiselect: _ctx.isMultiselect,
|
||
onChange: (menuState, setState) => _ctx.handleMenuItemChange(menuState, setState ? menuItem : null),
|
||
onClick: ($event) => _ctx.$emit("menu-item-click", menuItem)
|
||
}), {
|
||
default: withCtx(() => {
|
||
var _a2, _b2;
|
||
return [
|
||
renderSlot(_ctx.$slots, "default", {
|
||
menuItem,
|
||
active: menuItem.value === ((_a2 = _ctx.activeMenuItem) == null ? void 0 : _a2.value) && menuItem.value === ((_b2 = _ctx.highlightedMenuItem) == null ? void 0 : _b2.value)
|
||
})
|
||
];
|
||
}),
|
||
_: 2
|
||
/* DYNAMIC */
|
||
}, 1040, ["selected", "active", "highlighted", "show-thumbnail", "bold-label", "hide-description-overflow", "search-query", "multiselect", "onChange", "onClick"]);
|
||
}),
|
||
128
|
||
/* KEYED_FRAGMENT */
|
||
)),
|
||
_ctx.showPending ? (openBlock(), createBlock(_component_cdx_progress_bar, {
|
||
key: 2,
|
||
class: "cdx-menu__progress-bar",
|
||
inline: true
|
||
})) : createCommentVNode("v-if", true)
|
||
], 16, _hoisted_1$j)
|
||
],
|
||
6
|
||
/* CLASS, STYLE */
|
||
)), [
|
||
[vShow, _ctx.expanded]
|
||
]);
|
||
}
|
||
const CdxMenu = /* @__PURE__ */ _export_sfc(_sfc_main$l, [["render", _sfc_render$l]]);
|
||
const textInputTypeValidator = makeStringTypeValidator(TextInputTypes);
|
||
const statusValidator$8 = makeStringTypeValidator(ValidationStatusTypes);
|
||
const _sfc_main$k = defineComponent({
|
||
name: "CdxTextInput",
|
||
components: { CdxIcon },
|
||
/**
|
||
* We want the input to inherit attributes, not the root element.
|
||
*/
|
||
inheritAttrs: false,
|
||
expose: [
|
||
"focus",
|
||
"blur"
|
||
],
|
||
props: {
|
||
/**
|
||
* Current value of the input.
|
||
*
|
||
* Provided by `v-model` binding in the parent component.
|
||
*/
|
||
modelValue: {
|
||
type: [String, Number],
|
||
default: ""
|
||
},
|
||
/**
|
||
* `type` attribute of the input.
|
||
*
|
||
* @values 'text', 'search', 'number', 'email', 'password', 'tel', 'url',
|
||
* 'week', 'month', 'date', 'datetime-local', 'time'
|
||
*/
|
||
inputType: {
|
||
type: String,
|
||
default: "text",
|
||
validator: textInputTypeValidator
|
||
},
|
||
/**
|
||
* `status` attribute of the input.
|
||
*/
|
||
status: {
|
||
type: String,
|
||
default: "default",
|
||
validator: statusValidator$8
|
||
},
|
||
/**
|
||
* Whether the input is disabled.
|
||
*/
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* An icon at the start of the input element. Similar to a `::before` pseudo-element.
|
||
*/
|
||
startIcon: {
|
||
type: [String, Object],
|
||
default: void 0
|
||
},
|
||
/**
|
||
* An icon at the end of the input element. Similar to an `::after` pseudo-element.
|
||
*/
|
||
endIcon: {
|
||
type: [String, Object],
|
||
default: void 0
|
||
},
|
||
/**
|
||
* Add a clear button at the end of the input element.
|
||
*
|
||
* When the clear button is pressed, the input's value is set to an empty string.
|
||
* The clear button is displayed when input text is present.
|
||
*/
|
||
clearable: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
},
|
||
emits: [
|
||
/**
|
||
* When the input value changes
|
||
*
|
||
* @property {string | number} modelValue The new model value
|
||
*/
|
||
"update:modelValue",
|
||
/**
|
||
* When the user presses a key.
|
||
*
|
||
* This event is not emitted when the user presses the Home or End key (T314728),
|
||
* but is emitted for Ctrl/Cmd+Home and Ctrl/Cmd+End.
|
||
*
|
||
* @property {KeyboardEvent}
|
||
*/
|
||
"keydown",
|
||
/**
|
||
* When the input value changes via direct use of the input
|
||
*
|
||
* @property {InputEvent} event
|
||
*/
|
||
"input",
|
||
/**
|
||
* When an input value change is committed by the user (e.g. on blur)
|
||
*
|
||
* @property {Event} event
|
||
*/
|
||
"change",
|
||
/**
|
||
* When the input comes into focus
|
||
*
|
||
* @property {FocusEvent} event
|
||
*/
|
||
"focus",
|
||
/**
|
||
* When the input loses focus
|
||
*
|
||
* @property {FocusEvent} event
|
||
*/
|
||
"blur",
|
||
/**
|
||
* When the input value is cleared through the use of the clear button
|
||
*
|
||
* @property {MouseEvent} event
|
||
*/
|
||
"clear"
|
||
],
|
||
setup(props, { emit, attrs }) {
|
||
const idAttribute = attrs.id;
|
||
const {
|
||
computedDisabled,
|
||
computedStatus,
|
||
computedInputId
|
||
} = useFieldData(
|
||
toRef(props, "disabled"),
|
||
toRef(props, "status"),
|
||
idAttribute
|
||
);
|
||
const descriptionId = inject(FieldDescriptionIdKey, void 0);
|
||
const wrappedModel = useModelWrapper(toRef(props, "modelValue"), emit);
|
||
const isClearable = computed(() => {
|
||
return props.clearable && !!wrappedModel.value && !computedDisabled.value;
|
||
});
|
||
const internalClasses = computed(() => {
|
||
return {
|
||
"cdx-text-input--has-start-icon": !!props.startIcon,
|
||
"cdx-text-input--has-end-icon": !!props.endIcon,
|
||
"cdx-text-input--clearable": isClearable.value,
|
||
["cdx-text-input--status-".concat(computedStatus.value)]: true
|
||
};
|
||
});
|
||
const {
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrs
|
||
} = useSplitAttributes(attrs, internalClasses);
|
||
const otherAttrsMinusId = computed(() => {
|
||
const _a = otherAttrs.value, { id } = _a, everythingElse = __objRest(_a, ["id"]);
|
||
return everythingElse;
|
||
});
|
||
const inputClasses = computed(() => {
|
||
return {
|
||
"cdx-text-input__input--has-value": !!wrappedModel.value
|
||
};
|
||
});
|
||
const onClear = (event) => {
|
||
wrappedModel.value = "";
|
||
emit("clear", event);
|
||
};
|
||
const onKeydown = (event) => {
|
||
if ((event.key === "Home" || event.key === "End") && !event.ctrlKey && !event.metaKey) {
|
||
return;
|
||
}
|
||
emit("keydown", event);
|
||
};
|
||
const onInput = (event) => {
|
||
emit("input", event);
|
||
};
|
||
const onChange = (event) => {
|
||
emit("change", event);
|
||
};
|
||
const onFocus = (event) => {
|
||
emit("focus", event);
|
||
};
|
||
const onBlur = (event) => {
|
||
emit("blur", event);
|
||
};
|
||
return {
|
||
computedInputId,
|
||
descriptionId,
|
||
wrappedModel,
|
||
isClearable,
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrsMinusId,
|
||
inputClasses,
|
||
computedDisabled,
|
||
onClear,
|
||
onInput,
|
||
onChange,
|
||
onKeydown,
|
||
onFocus,
|
||
onBlur,
|
||
cdxIconClear: Y3
|
||
};
|
||
},
|
||
// Public methods
|
||
// These must be in the methods block, not in the setup function, otherwise their documentation
|
||
// won't be picked up by vue-docgen
|
||
methods: {
|
||
/**
|
||
* Focus the component's input element.
|
||
*
|
||
* @public
|
||
*/
|
||
focus() {
|
||
const input = this.$refs.input;
|
||
input.focus();
|
||
},
|
||
/**
|
||
* Blur the component's input element.
|
||
*
|
||
* @public
|
||
*/
|
||
blur() {
|
||
const input = this.$refs.input;
|
||
input.blur();
|
||
}
|
||
}
|
||
});
|
||
const _hoisted_1$i = ["id", "type", "aria-describedby", "disabled"];
|
||
function _sfc_render$k(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_icon = resolveComponent("cdx-icon");
|
||
return openBlock(), createElementBlock(
|
||
"div",
|
||
{
|
||
class: normalizeClass(["cdx-text-input", _ctx.rootClasses]),
|
||
style: normalizeStyle(_ctx.rootStyle)
|
||
},
|
||
[
|
||
withDirectives(createElementVNode("input", mergeProps({
|
||
id: _ctx.computedInputId,
|
||
ref: "input",
|
||
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => _ctx.wrappedModel = $event),
|
||
class: ["cdx-text-input__input", _ctx.inputClasses]
|
||
}, _ctx.otherAttrsMinusId, {
|
||
type: _ctx.inputType,
|
||
"aria-describedby": _ctx.descriptionId,
|
||
disabled: _ctx.computedDisabled,
|
||
size: "1",
|
||
onInput: _cache[1] || (_cache[1] = (...args) => _ctx.onInput && _ctx.onInput(...args)),
|
||
onChange: _cache[2] || (_cache[2] = (...args) => _ctx.onChange && _ctx.onChange(...args)),
|
||
onFocus: _cache[3] || (_cache[3] = (...args) => _ctx.onFocus && _ctx.onFocus(...args)),
|
||
onBlur: _cache[4] || (_cache[4] = (...args) => _ctx.onBlur && _ctx.onBlur(...args)),
|
||
onKeydown: _cache[5] || (_cache[5] = (...args) => _ctx.onKeydown && _ctx.onKeydown(...args))
|
||
}), null, 16, _hoisted_1$i), [
|
||
[vModelDynamic, _ctx.wrappedModel]
|
||
]),
|
||
_ctx.startIcon ? (openBlock(), createBlock(_component_cdx_icon, {
|
||
key: 0,
|
||
icon: _ctx.startIcon,
|
||
class: "cdx-text-input__icon-vue cdx-text-input__start-icon"
|
||
}, null, 8, ["icon"])) : createCommentVNode("v-if", true),
|
||
_ctx.endIcon ? (openBlock(), createBlock(_component_cdx_icon, {
|
||
key: 1,
|
||
icon: _ctx.endIcon,
|
||
class: "cdx-text-input__icon-vue cdx-text-input__end-icon"
|
||
}, null, 8, ["icon"])) : createCommentVNode("v-if", true),
|
||
_ctx.isClearable ? (openBlock(), createBlock(_component_cdx_icon, {
|
||
key: 2,
|
||
icon: _ctx.cdxIconClear,
|
||
class: "cdx-text-input__icon-vue cdx-text-input__clear-icon",
|
||
onMousedown: _cache[6] || (_cache[6] = withModifiers(() => {
|
||
}, ["prevent"])),
|
||
onClick: _ctx.onClear
|
||
}, null, 8, ["icon", "onClick"])) : createCommentVNode("v-if", true)
|
||
],
|
||
6
|
||
/* CLASS, STYLE */
|
||
);
|
||
}
|
||
const CdxTextInput = /* @__PURE__ */ _export_sfc(_sfc_main$k, [["render", _sfc_render$k]]);
|
||
const sides = ["top", "right", "bottom", "left"];
|
||
const min = Math.min;
|
||
const max = Math.max;
|
||
const round = Math.round;
|
||
const floor = Math.floor;
|
||
const createCoords = (v) => ({
|
||
x: v,
|
||
y: v
|
||
});
|
||
const oppositeSideMap = {
|
||
left: "right",
|
||
right: "left",
|
||
bottom: "top",
|
||
top: "bottom"
|
||
};
|
||
const oppositeAlignmentMap = {
|
||
start: "end",
|
||
end: "start"
|
||
};
|
||
function clamp(start, value, end) {
|
||
return max(start, min(value, end));
|
||
}
|
||
function evaluate(value, param) {
|
||
return typeof value === "function" ? value(param) : value;
|
||
}
|
||
function getSide(placement) {
|
||
return placement.split("-")[0];
|
||
}
|
||
function getAlignment(placement) {
|
||
return placement.split("-")[1];
|
||
}
|
||
function getOppositeAxis(axis) {
|
||
return axis === "x" ? "y" : "x";
|
||
}
|
||
function getAxisLength(axis) {
|
||
return axis === "y" ? "height" : "width";
|
||
}
|
||
function getSideAxis(placement) {
|
||
return ["top", "bottom"].includes(getSide(placement)) ? "y" : "x";
|
||
}
|
||
function getAlignmentAxis(placement) {
|
||
return getOppositeAxis(getSideAxis(placement));
|
||
}
|
||
function getAlignmentSides(placement, rects, rtl) {
|
||
if (rtl === void 0) {
|
||
rtl = false;
|
||
}
|
||
const alignment = getAlignment(placement);
|
||
const alignmentAxis = getAlignmentAxis(placement);
|
||
const length = getAxisLength(alignmentAxis);
|
||
let mainAlignmentSide = alignmentAxis === "x" ? alignment === (rtl ? "end" : "start") ? "right" : "left" : alignment === "start" ? "bottom" : "top";
|
||
if (rects.reference[length] > rects.floating[length]) {
|
||
mainAlignmentSide = getOppositePlacement(mainAlignmentSide);
|
||
}
|
||
return [mainAlignmentSide, getOppositePlacement(mainAlignmentSide)];
|
||
}
|
||
function getExpandedPlacements(placement) {
|
||
const oppositePlacement = getOppositePlacement(placement);
|
||
return [getOppositeAlignmentPlacement(placement), oppositePlacement, getOppositeAlignmentPlacement(oppositePlacement)];
|
||
}
|
||
function getOppositeAlignmentPlacement(placement) {
|
||
return placement.replace(/start|end/g, (alignment) => oppositeAlignmentMap[alignment]);
|
||
}
|
||
function getSideList(side, isStart, rtl) {
|
||
const lr = ["left", "right"];
|
||
const rl = ["right", "left"];
|
||
const tb = ["top", "bottom"];
|
||
const bt = ["bottom", "top"];
|
||
switch (side) {
|
||
case "top":
|
||
case "bottom":
|
||
if (rtl)
|
||
return isStart ? rl : lr;
|
||
return isStart ? lr : rl;
|
||
case "left":
|
||
case "right":
|
||
return isStart ? tb : bt;
|
||
default:
|
||
return [];
|
||
}
|
||
}
|
||
function getOppositeAxisPlacements(placement, flipAlignment, direction, rtl) {
|
||
const alignment = getAlignment(placement);
|
||
let list = getSideList(getSide(placement), direction === "start", rtl);
|
||
if (alignment) {
|
||
list = list.map((side) => side + "-" + alignment);
|
||
if (flipAlignment) {
|
||
list = list.concat(list.map(getOppositeAlignmentPlacement));
|
||
}
|
||
}
|
||
return list;
|
||
}
|
||
function getOppositePlacement(placement) {
|
||
return placement.replace(/left|right|bottom|top/g, (side) => oppositeSideMap[side]);
|
||
}
|
||
function expandPaddingObject(padding) {
|
||
return __spreadValues({
|
||
top: 0,
|
||
right: 0,
|
||
bottom: 0,
|
||
left: 0
|
||
}, padding);
|
||
}
|
||
function getPaddingObject(padding) {
|
||
return typeof padding !== "number" ? expandPaddingObject(padding) : {
|
||
top: padding,
|
||
right: padding,
|
||
bottom: padding,
|
||
left: padding
|
||
};
|
||
}
|
||
function rectToClientRect(rect) {
|
||
return __spreadProps(__spreadValues({}, rect), {
|
||
top: rect.y,
|
||
left: rect.x,
|
||
right: rect.x + rect.width,
|
||
bottom: rect.y + rect.height
|
||
});
|
||
}
|
||
function computeCoordsFromPlacement(_ref, placement, rtl) {
|
||
let {
|
||
reference,
|
||
floating
|
||
} = _ref;
|
||
const sideAxis = getSideAxis(placement);
|
||
const alignmentAxis = getAlignmentAxis(placement);
|
||
const alignLength = getAxisLength(alignmentAxis);
|
||
const side = getSide(placement);
|
||
const isVertical = sideAxis === "y";
|
||
const commonX = reference.x + reference.width / 2 - floating.width / 2;
|
||
const commonY = reference.y + reference.height / 2 - floating.height / 2;
|
||
const commonAlign = reference[alignLength] / 2 - floating[alignLength] / 2;
|
||
let coords;
|
||
switch (side) {
|
||
case "top":
|
||
coords = {
|
||
x: commonX,
|
||
y: reference.y - floating.height
|
||
};
|
||
break;
|
||
case "bottom":
|
||
coords = {
|
||
x: commonX,
|
||
y: reference.y + reference.height
|
||
};
|
||
break;
|
||
case "right":
|
||
coords = {
|
||
x: reference.x + reference.width,
|
||
y: commonY
|
||
};
|
||
break;
|
||
case "left":
|
||
coords = {
|
||
x: reference.x - floating.width,
|
||
y: commonY
|
||
};
|
||
break;
|
||
default:
|
||
coords = {
|
||
x: reference.x,
|
||
y: reference.y
|
||
};
|
||
}
|
||
switch (getAlignment(placement)) {
|
||
case "start":
|
||
coords[alignmentAxis] -= commonAlign * (rtl && isVertical ? -1 : 1);
|
||
break;
|
||
case "end":
|
||
coords[alignmentAxis] += commonAlign * (rtl && isVertical ? -1 : 1);
|
||
break;
|
||
}
|
||
return coords;
|
||
}
|
||
const computePosition$1 = (reference, floating, config) => __async(void 0, null, function* () {
|
||
const {
|
||
placement = "bottom",
|
||
strategy = "absolute",
|
||
middleware = [],
|
||
platform: platform2
|
||
} = config;
|
||
const validMiddleware = middleware.filter(Boolean);
|
||
const rtl = yield platform2.isRTL == null ? void 0 : platform2.isRTL(floating);
|
||
let rects = yield platform2.getElementRects({
|
||
reference,
|
||
floating,
|
||
strategy
|
||
});
|
||
let {
|
||
x,
|
||
y
|
||
} = computeCoordsFromPlacement(rects, placement, rtl);
|
||
let statefulPlacement = placement;
|
||
let middlewareData = {};
|
||
let resetCount = 0;
|
||
for (let i = 0; i < validMiddleware.length; i++) {
|
||
const {
|
||
name,
|
||
fn
|
||
} = validMiddleware[i];
|
||
const {
|
||
x: nextX,
|
||
y: nextY,
|
||
data,
|
||
reset
|
||
} = yield fn({
|
||
x,
|
||
y,
|
||
initialPlacement: placement,
|
||
placement: statefulPlacement,
|
||
strategy,
|
||
middlewareData,
|
||
rects,
|
||
platform: platform2,
|
||
elements: {
|
||
reference,
|
||
floating
|
||
}
|
||
});
|
||
x = nextX != null ? nextX : x;
|
||
y = nextY != null ? nextY : y;
|
||
middlewareData = __spreadProps(__spreadValues({}, middlewareData), {
|
||
[name]: __spreadValues(__spreadValues({}, middlewareData[name]), data)
|
||
});
|
||
if (reset && resetCount <= 50) {
|
||
resetCount++;
|
||
if (typeof reset === "object") {
|
||
if (reset.placement) {
|
||
statefulPlacement = reset.placement;
|
||
}
|
||
if (reset.rects) {
|
||
rects = reset.rects === true ? yield platform2.getElementRects({
|
||
reference,
|
||
floating,
|
||
strategy
|
||
}) : reset.rects;
|
||
}
|
||
({
|
||
x,
|
||
y
|
||
} = computeCoordsFromPlacement(rects, statefulPlacement, rtl));
|
||
}
|
||
i = -1;
|
||
}
|
||
}
|
||
return {
|
||
x,
|
||
y,
|
||
placement: statefulPlacement,
|
||
strategy,
|
||
middlewareData
|
||
};
|
||
});
|
||
function detectOverflow(state, options) {
|
||
return __async(this, null, function* () {
|
||
var _await$platform$isEle;
|
||
if (options === void 0) {
|
||
options = {};
|
||
}
|
||
const {
|
||
x,
|
||
y,
|
||
platform: platform2,
|
||
rects,
|
||
elements,
|
||
strategy
|
||
} = state;
|
||
const {
|
||
boundary = "clippingAncestors",
|
||
rootBoundary = "viewport",
|
||
elementContext = "floating",
|
||
altBoundary = false,
|
||
padding = 0
|
||
} = evaluate(options, state);
|
||
const paddingObject = getPaddingObject(padding);
|
||
const altContext = elementContext === "floating" ? "reference" : "floating";
|
||
const element = elements[altBoundary ? altContext : elementContext];
|
||
const clippingClientRect = rectToClientRect(yield platform2.getClippingRect({
|
||
element: ((_await$platform$isEle = yield platform2.isElement == null ? void 0 : platform2.isElement(element)) != null ? _await$platform$isEle : true) ? element : element.contextElement || (yield platform2.getDocumentElement == null ? void 0 : platform2.getDocumentElement(elements.floating)),
|
||
boundary,
|
||
rootBoundary,
|
||
strategy
|
||
}));
|
||
const rect = elementContext === "floating" ? __spreadProps(__spreadValues({}, rects.floating), {
|
||
x,
|
||
y
|
||
}) : rects.reference;
|
||
const offsetParent = yield platform2.getOffsetParent == null ? void 0 : platform2.getOffsetParent(elements.floating);
|
||
const offsetScale = (yield platform2.isElement == null ? void 0 : platform2.isElement(offsetParent)) ? (yield platform2.getScale == null ? void 0 : platform2.getScale(offsetParent)) || {
|
||
x: 1,
|
||
y: 1
|
||
} : {
|
||
x: 1,
|
||
y: 1
|
||
};
|
||
const elementClientRect = rectToClientRect(platform2.convertOffsetParentRelativeRectToViewportRelativeRect ? yield platform2.convertOffsetParentRelativeRectToViewportRelativeRect({
|
||
elements,
|
||
rect,
|
||
offsetParent,
|
||
strategy
|
||
}) : rect);
|
||
return {
|
||
top: (clippingClientRect.top - elementClientRect.top + paddingObject.top) / offsetScale.y,
|
||
bottom: (elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom) / offsetScale.y,
|
||
left: (clippingClientRect.left - elementClientRect.left + paddingObject.left) / offsetScale.x,
|
||
right: (elementClientRect.right - clippingClientRect.right + paddingObject.right) / offsetScale.x
|
||
};
|
||
});
|
||
}
|
||
const flip$1 = function(options) {
|
||
if (options === void 0) {
|
||
options = {};
|
||
}
|
||
return {
|
||
name: "flip",
|
||
options,
|
||
fn(state) {
|
||
return __async(this, null, function* () {
|
||
var _middlewareData$arrow, _middlewareData$flip;
|
||
const {
|
||
placement,
|
||
middlewareData,
|
||
rects,
|
||
initialPlacement,
|
||
platform: platform2,
|
||
elements
|
||
} = state;
|
||
const _a2 = evaluate(options, state), {
|
||
mainAxis: checkMainAxis = true,
|
||
crossAxis: checkCrossAxis = true,
|
||
fallbackPlacements: specifiedFallbackPlacements,
|
||
fallbackStrategy = "bestFit",
|
||
fallbackAxisSideDirection = "none",
|
||
flipAlignment = true
|
||
} = _a2, detectOverflowOptions = __objRest(_a2, [
|
||
"mainAxis",
|
||
"crossAxis",
|
||
"fallbackPlacements",
|
||
"fallbackStrategy",
|
||
"fallbackAxisSideDirection",
|
||
"flipAlignment"
|
||
]);
|
||
if ((_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) {
|
||
return {};
|
||
}
|
||
const side = getSide(placement);
|
||
const isBasePlacement = getSide(initialPlacement) === initialPlacement;
|
||
const rtl = yield platform2.isRTL == null ? void 0 : platform2.isRTL(elements.floating);
|
||
const fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipAlignment ? [getOppositePlacement(initialPlacement)] : getExpandedPlacements(initialPlacement));
|
||
if (!specifiedFallbackPlacements && fallbackAxisSideDirection !== "none") {
|
||
fallbackPlacements.push(...getOppositeAxisPlacements(initialPlacement, flipAlignment, fallbackAxisSideDirection, rtl));
|
||
}
|
||
const placements = [initialPlacement, ...fallbackPlacements];
|
||
const overflow = yield detectOverflow(state, detectOverflowOptions);
|
||
const overflows = [];
|
||
let overflowsData = ((_middlewareData$flip = middlewareData.flip) == null ? void 0 : _middlewareData$flip.overflows) || [];
|
||
if (checkMainAxis) {
|
||
overflows.push(overflow[side]);
|
||
}
|
||
if (checkCrossAxis) {
|
||
const sides2 = getAlignmentSides(placement, rects, rtl);
|
||
overflows.push(overflow[sides2[0]], overflow[sides2[1]]);
|
||
}
|
||
overflowsData = [...overflowsData, {
|
||
placement,
|
||
overflows
|
||
}];
|
||
if (!overflows.every((side2) => side2 <= 0)) {
|
||
var _middlewareData$flip2, _overflowsData$filter;
|
||
const nextIndex = (((_middlewareData$flip2 = middlewareData.flip) == null ? void 0 : _middlewareData$flip2.index) || 0) + 1;
|
||
const nextPlacement = placements[nextIndex];
|
||
if (nextPlacement) {
|
||
return {
|
||
data: {
|
||
index: nextIndex,
|
||
overflows: overflowsData
|
||
},
|
||
reset: {
|
||
placement: nextPlacement
|
||
}
|
||
};
|
||
}
|
||
let resetPlacement = (_overflowsData$filter = overflowsData.filter((d) => d.overflows[0] <= 0).sort((a, b) => a.overflows[1] - b.overflows[1])[0]) == null ? void 0 : _overflowsData$filter.placement;
|
||
if (!resetPlacement) {
|
||
switch (fallbackStrategy) {
|
||
case "bestFit": {
|
||
var _overflowsData$map$so;
|
||
const placement2 = (_overflowsData$map$so = overflowsData.map((d) => [d.placement, d.overflows.filter((overflow2) => overflow2 > 0).reduce((acc, overflow2) => acc + overflow2, 0)]).sort((a, b) => a[1] - b[1])[0]) == null ? void 0 : _overflowsData$map$so[0];
|
||
if (placement2) {
|
||
resetPlacement = placement2;
|
||
}
|
||
break;
|
||
}
|
||
case "initialPlacement":
|
||
resetPlacement = initialPlacement;
|
||
break;
|
||
}
|
||
}
|
||
if (placement !== resetPlacement) {
|
||
return {
|
||
reset: {
|
||
placement: resetPlacement
|
||
}
|
||
};
|
||
}
|
||
}
|
||
return {};
|
||
});
|
||
}
|
||
};
|
||
};
|
||
function getSideOffsets(overflow, rect) {
|
||
return {
|
||
top: overflow.top - rect.height,
|
||
right: overflow.right - rect.width,
|
||
bottom: overflow.bottom - rect.height,
|
||
left: overflow.left - rect.width
|
||
};
|
||
}
|
||
function isAnySideFullyClipped(overflow) {
|
||
return sides.some((side) => overflow[side] >= 0);
|
||
}
|
||
const hide$1 = function(options) {
|
||
if (options === void 0) {
|
||
options = {};
|
||
}
|
||
return {
|
||
name: "hide",
|
||
options,
|
||
fn(state) {
|
||
return __async(this, null, function* () {
|
||
const {
|
||
rects
|
||
} = state;
|
||
const _a2 = evaluate(options, state), {
|
||
strategy = "referenceHidden"
|
||
} = _a2, detectOverflowOptions = __objRest(_a2, [
|
||
"strategy"
|
||
]);
|
||
switch (strategy) {
|
||
case "referenceHidden": {
|
||
const overflow = yield detectOverflow(state, __spreadProps(__spreadValues({}, detectOverflowOptions), {
|
||
elementContext: "reference"
|
||
}));
|
||
const offsets = getSideOffsets(overflow, rects.reference);
|
||
return {
|
||
data: {
|
||
referenceHiddenOffsets: offsets,
|
||
referenceHidden: isAnySideFullyClipped(offsets)
|
||
}
|
||
};
|
||
}
|
||
case "escaped": {
|
||
const overflow = yield detectOverflow(state, __spreadProps(__spreadValues({}, detectOverflowOptions), {
|
||
altBoundary: true
|
||
}));
|
||
const offsets = getSideOffsets(overflow, rects.floating);
|
||
return {
|
||
data: {
|
||
escapedOffsets: offsets,
|
||
escaped: isAnySideFullyClipped(offsets)
|
||
}
|
||
};
|
||
}
|
||
default: {
|
||
return {};
|
||
}
|
||
}
|
||
});
|
||
}
|
||
};
|
||
};
|
||
function convertValueToCoords(state, options) {
|
||
return __async(this, null, function* () {
|
||
const {
|
||
placement,
|
||
platform: platform2,
|
||
elements
|
||
} = state;
|
||
const rtl = yield platform2.isRTL == null ? void 0 : platform2.isRTL(elements.floating);
|
||
const side = getSide(placement);
|
||
const alignment = getAlignment(placement);
|
||
const isVertical = getSideAxis(placement) === "y";
|
||
const mainAxisMulti = ["left", "top"].includes(side) ? -1 : 1;
|
||
const crossAxisMulti = rtl && isVertical ? -1 : 1;
|
||
const rawValue = evaluate(options, state);
|
||
let {
|
||
mainAxis,
|
||
crossAxis,
|
||
alignmentAxis
|
||
} = typeof rawValue === "number" ? {
|
||
mainAxis: rawValue,
|
||
crossAxis: 0,
|
||
alignmentAxis: null
|
||
} : __spreadValues({
|
||
mainAxis: 0,
|
||
crossAxis: 0,
|
||
alignmentAxis: null
|
||
}, rawValue);
|
||
if (alignment && typeof alignmentAxis === "number") {
|
||
crossAxis = alignment === "end" ? alignmentAxis * -1 : alignmentAxis;
|
||
}
|
||
return isVertical ? {
|
||
x: crossAxis * crossAxisMulti,
|
||
y: mainAxis * mainAxisMulti
|
||
} : {
|
||
x: mainAxis * mainAxisMulti,
|
||
y: crossAxis * crossAxisMulti
|
||
};
|
||
});
|
||
}
|
||
const offset = function(options) {
|
||
if (options === void 0) {
|
||
options = 0;
|
||
}
|
||
return {
|
||
name: "offset",
|
||
options,
|
||
fn(state) {
|
||
return __async(this, null, function* () {
|
||
var _middlewareData$offse, _middlewareData$arrow;
|
||
const {
|
||
x,
|
||
y,
|
||
placement,
|
||
middlewareData
|
||
} = state;
|
||
const diffCoords = yield convertValueToCoords(state, options);
|
||
if (placement === ((_middlewareData$offse = middlewareData.offset) == null ? void 0 : _middlewareData$offse.placement) && (_middlewareData$arrow = middlewareData.arrow) != null && _middlewareData$arrow.alignmentOffset) {
|
||
return {};
|
||
}
|
||
return {
|
||
x: x + diffCoords.x,
|
||
y: y + diffCoords.y,
|
||
data: __spreadProps(__spreadValues({}, diffCoords), {
|
||
placement
|
||
})
|
||
};
|
||
});
|
||
}
|
||
};
|
||
};
|
||
const shift$1 = function(options) {
|
||
if (options === void 0) {
|
||
options = {};
|
||
}
|
||
return {
|
||
name: "shift",
|
||
options,
|
||
fn(state) {
|
||
return __async(this, null, function* () {
|
||
const {
|
||
x,
|
||
y,
|
||
placement
|
||
} = state;
|
||
const _a2 = evaluate(options, state), {
|
||
mainAxis: checkMainAxis = true,
|
||
crossAxis: checkCrossAxis = false,
|
||
limiter = {
|
||
fn: (_ref) => {
|
||
let {
|
||
x: x2,
|
||
y: y2
|
||
} = _ref;
|
||
return {
|
||
x: x2,
|
||
y: y2
|
||
};
|
||
}
|
||
}
|
||
} = _a2, detectOverflowOptions = __objRest(_a2, [
|
||
"mainAxis",
|
||
"crossAxis",
|
||
"limiter"
|
||
]);
|
||
const coords = {
|
||
x,
|
||
y
|
||
};
|
||
const overflow = yield detectOverflow(state, detectOverflowOptions);
|
||
const crossAxis = getSideAxis(getSide(placement));
|
||
const mainAxis = getOppositeAxis(crossAxis);
|
||
let mainAxisCoord = coords[mainAxis];
|
||
let crossAxisCoord = coords[crossAxis];
|
||
if (checkMainAxis) {
|
||
const minSide = mainAxis === "y" ? "top" : "left";
|
||
const maxSide = mainAxis === "y" ? "bottom" : "right";
|
||
const min2 = mainAxisCoord + overflow[minSide];
|
||
const max2 = mainAxisCoord - overflow[maxSide];
|
||
mainAxisCoord = clamp(min2, mainAxisCoord, max2);
|
||
}
|
||
if (checkCrossAxis) {
|
||
const minSide = crossAxis === "y" ? "top" : "left";
|
||
const maxSide = crossAxis === "y" ? "bottom" : "right";
|
||
const min2 = crossAxisCoord + overflow[minSide];
|
||
const max2 = crossAxisCoord - overflow[maxSide];
|
||
crossAxisCoord = clamp(min2, crossAxisCoord, max2);
|
||
}
|
||
const limitedCoords = limiter.fn(__spreadProps(__spreadValues({}, state), {
|
||
[mainAxis]: mainAxisCoord,
|
||
[crossAxis]: crossAxisCoord
|
||
}));
|
||
return __spreadProps(__spreadValues({}, limitedCoords), {
|
||
data: {
|
||
x: limitedCoords.x - x,
|
||
y: limitedCoords.y - y
|
||
}
|
||
});
|
||
});
|
||
}
|
||
};
|
||
};
|
||
const size$1 = function(options) {
|
||
if (options === void 0) {
|
||
options = {};
|
||
}
|
||
return {
|
||
name: "size",
|
||
options,
|
||
fn(state) {
|
||
return __async(this, null, function* () {
|
||
const {
|
||
placement,
|
||
rects,
|
||
platform: platform2,
|
||
elements
|
||
} = state;
|
||
const _a2 = evaluate(options, state), {
|
||
apply = () => {
|
||
}
|
||
} = _a2, detectOverflowOptions = __objRest(_a2, [
|
||
"apply"
|
||
]);
|
||
const overflow = yield detectOverflow(state, detectOverflowOptions);
|
||
const side = getSide(placement);
|
||
const alignment = getAlignment(placement);
|
||
const isYAxis = getSideAxis(placement) === "y";
|
||
const {
|
||
width,
|
||
height
|
||
} = rects.floating;
|
||
let heightSide;
|
||
let widthSide;
|
||
if (side === "top" || side === "bottom") {
|
||
heightSide = side;
|
||
widthSide = alignment === ((yield platform2.isRTL == null ? void 0 : platform2.isRTL(elements.floating)) ? "start" : "end") ? "left" : "right";
|
||
} else {
|
||
widthSide = side;
|
||
heightSide = alignment === "end" ? "top" : "bottom";
|
||
}
|
||
const overflowAvailableHeight = height - overflow[heightSide];
|
||
const overflowAvailableWidth = width - overflow[widthSide];
|
||
const noShift = !state.middlewareData.shift;
|
||
let availableHeight = overflowAvailableHeight;
|
||
let availableWidth = overflowAvailableWidth;
|
||
if (isYAxis) {
|
||
const maximumClippingWidth = width - overflow.left - overflow.right;
|
||
availableWidth = alignment || noShift ? min(overflowAvailableWidth, maximumClippingWidth) : maximumClippingWidth;
|
||
} else {
|
||
const maximumClippingHeight = height - overflow.top - overflow.bottom;
|
||
availableHeight = alignment || noShift ? min(overflowAvailableHeight, maximumClippingHeight) : maximumClippingHeight;
|
||
}
|
||
if (noShift && !alignment) {
|
||
const xMin = max(overflow.left, 0);
|
||
const xMax = max(overflow.right, 0);
|
||
const yMin = max(overflow.top, 0);
|
||
const yMax = max(overflow.bottom, 0);
|
||
if (isYAxis) {
|
||
availableWidth = width - 2 * (xMin !== 0 || xMax !== 0 ? xMin + xMax : max(overflow.left, overflow.right));
|
||
} else {
|
||
availableHeight = height - 2 * (yMin !== 0 || yMax !== 0 ? yMin + yMax : max(overflow.top, overflow.bottom));
|
||
}
|
||
}
|
||
yield apply(__spreadProps(__spreadValues({}, state), {
|
||
availableWidth,
|
||
availableHeight
|
||
}));
|
||
const nextDimensions = yield platform2.getDimensions(elements.floating);
|
||
if (width !== nextDimensions.width || height !== nextDimensions.height) {
|
||
return {
|
||
reset: {
|
||
rects: true
|
||
}
|
||
};
|
||
}
|
||
return {};
|
||
});
|
||
}
|
||
};
|
||
};
|
||
function getNodeName(node) {
|
||
if (isNode(node)) {
|
||
return (node.nodeName || "").toLowerCase();
|
||
}
|
||
return "#document";
|
||
}
|
||
function getWindow(node) {
|
||
var _node$ownerDocument;
|
||
return (node == null || (_node$ownerDocument = node.ownerDocument) == null ? void 0 : _node$ownerDocument.defaultView) || window;
|
||
}
|
||
function getDocumentElement(node) {
|
||
var _ref;
|
||
return (_ref = (isNode(node) ? node.ownerDocument : node.document) || window.document) == null ? void 0 : _ref.documentElement;
|
||
}
|
||
function isNode(value) {
|
||
return value instanceof Node || value instanceof getWindow(value).Node;
|
||
}
|
||
function isElement(value) {
|
||
return value instanceof Element || value instanceof getWindow(value).Element;
|
||
}
|
||
function isHTMLElement(value) {
|
||
return value instanceof HTMLElement || value instanceof getWindow(value).HTMLElement;
|
||
}
|
||
function isShadowRoot(value) {
|
||
if (typeof ShadowRoot === "undefined") {
|
||
return false;
|
||
}
|
||
return value instanceof ShadowRoot || value instanceof getWindow(value).ShadowRoot;
|
||
}
|
||
function isOverflowElement(element) {
|
||
const {
|
||
overflow,
|
||
overflowX,
|
||
overflowY,
|
||
display
|
||
} = getComputedStyle(element);
|
||
return /auto|scroll|overlay|hidden|clip/.test(overflow + overflowY + overflowX) && !["inline", "contents"].includes(display);
|
||
}
|
||
function isTableElement(element) {
|
||
return ["table", "td", "th"].includes(getNodeName(element));
|
||
}
|
||
function isContainingBlock(element) {
|
||
const webkit = isWebKit();
|
||
const css = getComputedStyle(element);
|
||
return css.transform !== "none" || css.perspective !== "none" || (css.containerType ? css.containerType !== "normal" : false) || !webkit && (css.backdropFilter ? css.backdropFilter !== "none" : false) || !webkit && (css.filter ? css.filter !== "none" : false) || ["transform", "perspective", "filter"].some((value) => (css.willChange || "").includes(value)) || ["paint", "layout", "strict", "content"].some((value) => (css.contain || "").includes(value));
|
||
}
|
||
function getContainingBlock(element) {
|
||
let currentNode = getParentNode(element);
|
||
while (isHTMLElement(currentNode) && !isLastTraversableNode(currentNode)) {
|
||
if (isContainingBlock(currentNode)) {
|
||
return currentNode;
|
||
} else {
|
||
currentNode = getParentNode(currentNode);
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
function isWebKit() {
|
||
if (typeof CSS === "undefined" || !CSS.supports)
|
||
return false;
|
||
return CSS.supports("-webkit-backdrop-filter", "none");
|
||
}
|
||
function isLastTraversableNode(node) {
|
||
return ["html", "body", "#document"].includes(getNodeName(node));
|
||
}
|
||
function getComputedStyle(element) {
|
||
return getWindow(element).getComputedStyle(element);
|
||
}
|
||
function getNodeScroll(element) {
|
||
if (isElement(element)) {
|
||
return {
|
||
scrollLeft: element.scrollLeft,
|
||
scrollTop: element.scrollTop
|
||
};
|
||
}
|
||
return {
|
||
scrollLeft: element.pageXOffset,
|
||
scrollTop: element.pageYOffset
|
||
};
|
||
}
|
||
function getParentNode(node) {
|
||
if (getNodeName(node) === "html") {
|
||
return node;
|
||
}
|
||
const result = (
|
||
// Step into the shadow DOM of the parent of a slotted node.
|
||
node.assignedSlot || // DOM Element detected.
|
||
node.parentNode || // ShadowRoot detected.
|
||
isShadowRoot(node) && node.host || // Fallback.
|
||
getDocumentElement(node)
|
||
);
|
||
return isShadowRoot(result) ? result.host : result;
|
||
}
|
||
function getNearestOverflowAncestor(node) {
|
||
const parentNode = getParentNode(node);
|
||
if (isLastTraversableNode(parentNode)) {
|
||
return node.ownerDocument ? node.ownerDocument.body : node.body;
|
||
}
|
||
if (isHTMLElement(parentNode) && isOverflowElement(parentNode)) {
|
||
return parentNode;
|
||
}
|
||
return getNearestOverflowAncestor(parentNode);
|
||
}
|
||
function getOverflowAncestors(node, list, traverseIframes) {
|
||
var _node$ownerDocument2;
|
||
if (list === void 0) {
|
||
list = [];
|
||
}
|
||
if (traverseIframes === void 0) {
|
||
traverseIframes = true;
|
||
}
|
||
const scrollableAncestor = getNearestOverflowAncestor(node);
|
||
const isBody = scrollableAncestor === ((_node$ownerDocument2 = node.ownerDocument) == null ? void 0 : _node$ownerDocument2.body);
|
||
const win = getWindow(scrollableAncestor);
|
||
if (isBody) {
|
||
return list.concat(win, win.visualViewport || [], isOverflowElement(scrollableAncestor) ? scrollableAncestor : [], win.frameElement && traverseIframes ? getOverflowAncestors(win.frameElement) : []);
|
||
}
|
||
return list.concat(scrollableAncestor, getOverflowAncestors(scrollableAncestor, [], traverseIframes));
|
||
}
|
||
function getCssDimensions(element) {
|
||
const css = getComputedStyle(element);
|
||
let width = parseFloat(css.width) || 0;
|
||
let height = parseFloat(css.height) || 0;
|
||
const hasOffset = isHTMLElement(element);
|
||
const offsetWidth = hasOffset ? element.offsetWidth : width;
|
||
const offsetHeight = hasOffset ? element.offsetHeight : height;
|
||
const shouldFallback = round(width) !== offsetWidth || round(height) !== offsetHeight;
|
||
if (shouldFallback) {
|
||
width = offsetWidth;
|
||
height = offsetHeight;
|
||
}
|
||
return {
|
||
width,
|
||
height,
|
||
$: shouldFallback
|
||
};
|
||
}
|
||
function unwrapElement$2(element) {
|
||
return !isElement(element) ? element.contextElement : element;
|
||
}
|
||
function getScale(element) {
|
||
const domElement = unwrapElement$2(element);
|
||
if (!isHTMLElement(domElement)) {
|
||
return createCoords(1);
|
||
}
|
||
const rect = domElement.getBoundingClientRect();
|
||
const {
|
||
width,
|
||
height,
|
||
$
|
||
} = getCssDimensions(domElement);
|
||
let x = ($ ? round(rect.width) : rect.width) / width;
|
||
let y = ($ ? round(rect.height) : rect.height) / height;
|
||
if (!x || !Number.isFinite(x)) {
|
||
x = 1;
|
||
}
|
||
if (!y || !Number.isFinite(y)) {
|
||
y = 1;
|
||
}
|
||
return {
|
||
x,
|
||
y
|
||
};
|
||
}
|
||
const noOffsets = /* @__PURE__ */ createCoords(0);
|
||
function getVisualOffsets(element) {
|
||
const win = getWindow(element);
|
||
if (!isWebKit() || !win.visualViewport) {
|
||
return noOffsets;
|
||
}
|
||
return {
|
||
x: win.visualViewport.offsetLeft,
|
||
y: win.visualViewport.offsetTop
|
||
};
|
||
}
|
||
function shouldAddVisualOffsets(element, isFixed, floatingOffsetParent) {
|
||
if (isFixed === void 0) {
|
||
isFixed = false;
|
||
}
|
||
if (!floatingOffsetParent || isFixed && floatingOffsetParent !== getWindow(element)) {
|
||
return false;
|
||
}
|
||
return isFixed;
|
||
}
|
||
function getBoundingClientRect(element, includeScale, isFixedStrategy, offsetParent) {
|
||
if (includeScale === void 0) {
|
||
includeScale = false;
|
||
}
|
||
if (isFixedStrategy === void 0) {
|
||
isFixedStrategy = false;
|
||
}
|
||
const clientRect = element.getBoundingClientRect();
|
||
const domElement = unwrapElement$2(element);
|
||
let scale = createCoords(1);
|
||
if (includeScale) {
|
||
if (offsetParent) {
|
||
if (isElement(offsetParent)) {
|
||
scale = getScale(offsetParent);
|
||
}
|
||
} else {
|
||
scale = getScale(element);
|
||
}
|
||
}
|
||
const visualOffsets = shouldAddVisualOffsets(domElement, isFixedStrategy, offsetParent) ? getVisualOffsets(domElement) : createCoords(0);
|
||
let x = (clientRect.left + visualOffsets.x) / scale.x;
|
||
let y = (clientRect.top + visualOffsets.y) / scale.y;
|
||
let width = clientRect.width / scale.x;
|
||
let height = clientRect.height / scale.y;
|
||
if (domElement) {
|
||
const win = getWindow(domElement);
|
||
const offsetWin = offsetParent && isElement(offsetParent) ? getWindow(offsetParent) : offsetParent;
|
||
let currentWin = win;
|
||
let currentIFrame = currentWin.frameElement;
|
||
while (currentIFrame && offsetParent && offsetWin !== currentWin) {
|
||
const iframeScale = getScale(currentIFrame);
|
||
const iframeRect = currentIFrame.getBoundingClientRect();
|
||
const css = getComputedStyle(currentIFrame);
|
||
const left = iframeRect.left + (currentIFrame.clientLeft + parseFloat(css.paddingLeft)) * iframeScale.x;
|
||
const top = iframeRect.top + (currentIFrame.clientTop + parseFloat(css.paddingTop)) * iframeScale.y;
|
||
x *= iframeScale.x;
|
||
y *= iframeScale.y;
|
||
width *= iframeScale.x;
|
||
height *= iframeScale.y;
|
||
x += left;
|
||
y += top;
|
||
currentWin = getWindow(currentIFrame);
|
||
currentIFrame = currentWin.frameElement;
|
||
}
|
||
}
|
||
return rectToClientRect({
|
||
width,
|
||
height,
|
||
x,
|
||
y
|
||
});
|
||
}
|
||
const topLayerSelectors = [":popover-open", ":modal"];
|
||
function isTopLayer(floating) {
|
||
return topLayerSelectors.some((selector) => {
|
||
try {
|
||
return floating.matches(selector);
|
||
} catch (e) {
|
||
return false;
|
||
}
|
||
});
|
||
}
|
||
function convertOffsetParentRelativeRectToViewportRelativeRect(_ref) {
|
||
let {
|
||
elements,
|
||
rect,
|
||
offsetParent,
|
||
strategy
|
||
} = _ref;
|
||
const isFixed = strategy === "fixed";
|
||
const documentElement = getDocumentElement(offsetParent);
|
||
const topLayer = elements ? isTopLayer(elements.floating) : false;
|
||
if (offsetParent === documentElement || topLayer && isFixed) {
|
||
return rect;
|
||
}
|
||
let scroll = {
|
||
scrollLeft: 0,
|
||
scrollTop: 0
|
||
};
|
||
let scale = createCoords(1);
|
||
const offsets = createCoords(0);
|
||
const isOffsetParentAnElement = isHTMLElement(offsetParent);
|
||
if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {
|
||
if (getNodeName(offsetParent) !== "body" || isOverflowElement(documentElement)) {
|
||
scroll = getNodeScroll(offsetParent);
|
||
}
|
||
if (isHTMLElement(offsetParent)) {
|
||
const offsetRect = getBoundingClientRect(offsetParent);
|
||
scale = getScale(offsetParent);
|
||
offsets.x = offsetRect.x + offsetParent.clientLeft;
|
||
offsets.y = offsetRect.y + offsetParent.clientTop;
|
||
}
|
||
}
|
||
return {
|
||
width: rect.width * scale.x,
|
||
height: rect.height * scale.y,
|
||
x: rect.x * scale.x - scroll.scrollLeft * scale.x + offsets.x,
|
||
y: rect.y * scale.y - scroll.scrollTop * scale.y + offsets.y
|
||
};
|
||
}
|
||
function getClientRects(element) {
|
||
return Array.from(element.getClientRects());
|
||
}
|
||
function getWindowScrollBarX(element) {
|
||
return getBoundingClientRect(getDocumentElement(element)).left + getNodeScroll(element).scrollLeft;
|
||
}
|
||
function getDocumentRect(element) {
|
||
const html = getDocumentElement(element);
|
||
const scroll = getNodeScroll(element);
|
||
const body = element.ownerDocument.body;
|
||
const width = max(html.scrollWidth, html.clientWidth, body.scrollWidth, body.clientWidth);
|
||
const height = max(html.scrollHeight, html.clientHeight, body.scrollHeight, body.clientHeight);
|
||
let x = -scroll.scrollLeft + getWindowScrollBarX(element);
|
||
const y = -scroll.scrollTop;
|
||
if (getComputedStyle(body).direction === "rtl") {
|
||
x += max(html.clientWidth, body.clientWidth) - width;
|
||
}
|
||
return {
|
||
width,
|
||
height,
|
||
x,
|
||
y
|
||
};
|
||
}
|
||
function getViewportRect(element, strategy) {
|
||
const win = getWindow(element);
|
||
const html = getDocumentElement(element);
|
||
const visualViewport = win.visualViewport;
|
||
let width = html.clientWidth;
|
||
let height = html.clientHeight;
|
||
let x = 0;
|
||
let y = 0;
|
||
if (visualViewport) {
|
||
width = visualViewport.width;
|
||
height = visualViewport.height;
|
||
const visualViewportBased = isWebKit();
|
||
if (!visualViewportBased || visualViewportBased && strategy === "fixed") {
|
||
x = visualViewport.offsetLeft;
|
||
y = visualViewport.offsetTop;
|
||
}
|
||
}
|
||
return {
|
||
width,
|
||
height,
|
||
x,
|
||
y
|
||
};
|
||
}
|
||
function getInnerBoundingClientRect(element, strategy) {
|
||
const clientRect = getBoundingClientRect(element, true, strategy === "fixed");
|
||
const top = clientRect.top + element.clientTop;
|
||
const left = clientRect.left + element.clientLeft;
|
||
const scale = isHTMLElement(element) ? getScale(element) : createCoords(1);
|
||
const width = element.clientWidth * scale.x;
|
||
const height = element.clientHeight * scale.y;
|
||
const x = left * scale.x;
|
||
const y = top * scale.y;
|
||
return {
|
||
width,
|
||
height,
|
||
x,
|
||
y
|
||
};
|
||
}
|
||
function getClientRectFromClippingAncestor(element, clippingAncestor, strategy) {
|
||
let rect;
|
||
if (clippingAncestor === "viewport") {
|
||
rect = getViewportRect(element, strategy);
|
||
} else if (clippingAncestor === "document") {
|
||
rect = getDocumentRect(getDocumentElement(element));
|
||
} else if (isElement(clippingAncestor)) {
|
||
rect = getInnerBoundingClientRect(clippingAncestor, strategy);
|
||
} else {
|
||
const visualOffsets = getVisualOffsets(element);
|
||
rect = __spreadProps(__spreadValues({}, clippingAncestor), {
|
||
x: clippingAncestor.x - visualOffsets.x,
|
||
y: clippingAncestor.y - visualOffsets.y
|
||
});
|
||
}
|
||
return rectToClientRect(rect);
|
||
}
|
||
function hasFixedPositionAncestor(element, stopNode) {
|
||
const parentNode = getParentNode(element);
|
||
if (parentNode === stopNode || !isElement(parentNode) || isLastTraversableNode(parentNode)) {
|
||
return false;
|
||
}
|
||
return getComputedStyle(parentNode).position === "fixed" || hasFixedPositionAncestor(parentNode, stopNode);
|
||
}
|
||
function getClippingElementAncestors(element, cache) {
|
||
const cachedResult = cache.get(element);
|
||
if (cachedResult) {
|
||
return cachedResult;
|
||
}
|
||
let result = getOverflowAncestors(element, [], false).filter((el) => isElement(el) && getNodeName(el) !== "body");
|
||
let currentContainingBlockComputedStyle = null;
|
||
const elementIsFixed = getComputedStyle(element).position === "fixed";
|
||
let currentNode = elementIsFixed ? getParentNode(element) : element;
|
||
while (isElement(currentNode) && !isLastTraversableNode(currentNode)) {
|
||
const computedStyle = getComputedStyle(currentNode);
|
||
const currentNodeIsContaining = isContainingBlock(currentNode);
|
||
if (!currentNodeIsContaining && computedStyle.position === "fixed") {
|
||
currentContainingBlockComputedStyle = null;
|
||
}
|
||
const shouldDropCurrentNode = elementIsFixed ? !currentNodeIsContaining && !currentContainingBlockComputedStyle : !currentNodeIsContaining && computedStyle.position === "static" && !!currentContainingBlockComputedStyle && ["absolute", "fixed"].includes(currentContainingBlockComputedStyle.position) || isOverflowElement(currentNode) && !currentNodeIsContaining && hasFixedPositionAncestor(element, currentNode);
|
||
if (shouldDropCurrentNode) {
|
||
result = result.filter((ancestor) => ancestor !== currentNode);
|
||
} else {
|
||
currentContainingBlockComputedStyle = computedStyle;
|
||
}
|
||
currentNode = getParentNode(currentNode);
|
||
}
|
||
cache.set(element, result);
|
||
return result;
|
||
}
|
||
function getClippingRect(_ref) {
|
||
let {
|
||
element,
|
||
boundary,
|
||
rootBoundary,
|
||
strategy
|
||
} = _ref;
|
||
const elementClippingAncestors = boundary === "clippingAncestors" ? getClippingElementAncestors(element, this._c) : [].concat(boundary);
|
||
const clippingAncestors = [...elementClippingAncestors, rootBoundary];
|
||
const firstClippingAncestor = clippingAncestors[0];
|
||
const clippingRect = clippingAncestors.reduce((accRect, clippingAncestor) => {
|
||
const rect = getClientRectFromClippingAncestor(element, clippingAncestor, strategy);
|
||
accRect.top = max(rect.top, accRect.top);
|
||
accRect.right = min(rect.right, accRect.right);
|
||
accRect.bottom = min(rect.bottom, accRect.bottom);
|
||
accRect.left = max(rect.left, accRect.left);
|
||
return accRect;
|
||
}, getClientRectFromClippingAncestor(element, firstClippingAncestor, strategy));
|
||
return {
|
||
width: clippingRect.right - clippingRect.left,
|
||
height: clippingRect.bottom - clippingRect.top,
|
||
x: clippingRect.left,
|
||
y: clippingRect.top
|
||
};
|
||
}
|
||
function getDimensions(element) {
|
||
const {
|
||
width,
|
||
height
|
||
} = getCssDimensions(element);
|
||
return {
|
||
width,
|
||
height
|
||
};
|
||
}
|
||
function getRectRelativeToOffsetParent(element, offsetParent, strategy) {
|
||
const isOffsetParentAnElement = isHTMLElement(offsetParent);
|
||
const documentElement = getDocumentElement(offsetParent);
|
||
const isFixed = strategy === "fixed";
|
||
const rect = getBoundingClientRect(element, true, isFixed, offsetParent);
|
||
let scroll = {
|
||
scrollLeft: 0,
|
||
scrollTop: 0
|
||
};
|
||
const offsets = createCoords(0);
|
||
if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) {
|
||
if (getNodeName(offsetParent) !== "body" || isOverflowElement(documentElement)) {
|
||
scroll = getNodeScroll(offsetParent);
|
||
}
|
||
if (isOffsetParentAnElement) {
|
||
const offsetRect = getBoundingClientRect(offsetParent, true, isFixed, offsetParent);
|
||
offsets.x = offsetRect.x + offsetParent.clientLeft;
|
||
offsets.y = offsetRect.y + offsetParent.clientTop;
|
||
} else if (documentElement) {
|
||
offsets.x = getWindowScrollBarX(documentElement);
|
||
}
|
||
}
|
||
const x = rect.left + scroll.scrollLeft - offsets.x;
|
||
const y = rect.top + scroll.scrollTop - offsets.y;
|
||
return {
|
||
x,
|
||
y,
|
||
width: rect.width,
|
||
height: rect.height
|
||
};
|
||
}
|
||
function getTrueOffsetParent(element, polyfill) {
|
||
if (!isHTMLElement(element) || getComputedStyle(element).position === "fixed") {
|
||
return null;
|
||
}
|
||
if (polyfill) {
|
||
return polyfill(element);
|
||
}
|
||
return element.offsetParent;
|
||
}
|
||
function getOffsetParent(element, polyfill) {
|
||
const window2 = getWindow(element);
|
||
if (!isHTMLElement(element) || isTopLayer(element)) {
|
||
return window2;
|
||
}
|
||
let offsetParent = getTrueOffsetParent(element, polyfill);
|
||
while (offsetParent && isTableElement(offsetParent) && getComputedStyle(offsetParent).position === "static") {
|
||
offsetParent = getTrueOffsetParent(offsetParent, polyfill);
|
||
}
|
||
if (offsetParent && (getNodeName(offsetParent) === "html" || getNodeName(offsetParent) === "body" && getComputedStyle(offsetParent).position === "static" && !isContainingBlock(offsetParent))) {
|
||
return window2;
|
||
}
|
||
return offsetParent || getContainingBlock(element) || window2;
|
||
}
|
||
const getElementRects = function(data) {
|
||
return __async(this, null, function* () {
|
||
const getOffsetParentFn = this.getOffsetParent || getOffsetParent;
|
||
const getDimensionsFn = this.getDimensions;
|
||
return {
|
||
reference: getRectRelativeToOffsetParent(data.reference, yield getOffsetParentFn(data.floating), data.strategy),
|
||
floating: __spreadValues({
|
||
x: 0,
|
||
y: 0
|
||
}, yield getDimensionsFn(data.floating))
|
||
};
|
||
});
|
||
};
|
||
function isRTL(element) {
|
||
return getComputedStyle(element).direction === "rtl";
|
||
}
|
||
const platform = {
|
||
convertOffsetParentRelativeRectToViewportRelativeRect,
|
||
getDocumentElement,
|
||
getClippingRect,
|
||
getOffsetParent,
|
||
getElementRects,
|
||
getClientRects,
|
||
getDimensions,
|
||
getScale,
|
||
isElement,
|
||
isRTL
|
||
};
|
||
function observeMove(element, onMove) {
|
||
let io = null;
|
||
let timeoutId;
|
||
const root = getDocumentElement(element);
|
||
function cleanup() {
|
||
var _io;
|
||
clearTimeout(timeoutId);
|
||
(_io = io) == null || _io.disconnect();
|
||
io = null;
|
||
}
|
||
function refresh(skip, threshold) {
|
||
if (skip === void 0) {
|
||
skip = false;
|
||
}
|
||
if (threshold === void 0) {
|
||
threshold = 1;
|
||
}
|
||
cleanup();
|
||
const {
|
||
left,
|
||
top,
|
||
width,
|
||
height
|
||
} = element.getBoundingClientRect();
|
||
if (!skip) {
|
||
onMove();
|
||
}
|
||
if (!width || !height) {
|
||
return;
|
||
}
|
||
const insetTop = floor(top);
|
||
const insetRight = floor(root.clientWidth - (left + width));
|
||
const insetBottom = floor(root.clientHeight - (top + height));
|
||
const insetLeft = floor(left);
|
||
const rootMargin = -insetTop + "px " + -insetRight + "px " + -insetBottom + "px " + -insetLeft + "px";
|
||
const options = {
|
||
rootMargin,
|
||
threshold: max(0, min(1, threshold)) || 1
|
||
};
|
||
let isFirstUpdate = true;
|
||
function handleObserve(entries) {
|
||
const ratio = entries[0].intersectionRatio;
|
||
if (ratio !== threshold) {
|
||
if (!isFirstUpdate) {
|
||
return refresh();
|
||
}
|
||
if (!ratio) {
|
||
timeoutId = setTimeout(() => {
|
||
refresh(false, 1e-7);
|
||
}, 100);
|
||
} else {
|
||
refresh(false, ratio);
|
||
}
|
||
}
|
||
isFirstUpdate = false;
|
||
}
|
||
try {
|
||
io = new IntersectionObserver(handleObserve, __spreadProps(__spreadValues({}, options), {
|
||
// Handle <iframe>s
|
||
root: root.ownerDocument
|
||
}));
|
||
} catch (e) {
|
||
io = new IntersectionObserver(handleObserve, options);
|
||
}
|
||
io.observe(element);
|
||
}
|
||
refresh(true);
|
||
return cleanup;
|
||
}
|
||
function autoUpdate(reference, floating, update, options) {
|
||
if (options === void 0) {
|
||
options = {};
|
||
}
|
||
const {
|
||
ancestorScroll = true,
|
||
ancestorResize = true,
|
||
elementResize = typeof ResizeObserver === "function",
|
||
layoutShift = typeof IntersectionObserver === "function",
|
||
animationFrame = false
|
||
} = options;
|
||
const referenceEl = unwrapElement$2(reference);
|
||
const ancestors = ancestorScroll || ancestorResize ? [...referenceEl ? getOverflowAncestors(referenceEl) : [], ...getOverflowAncestors(floating)] : [];
|
||
ancestors.forEach((ancestor) => {
|
||
ancestorScroll && ancestor.addEventListener("scroll", update, {
|
||
passive: true
|
||
});
|
||
ancestorResize && ancestor.addEventListener("resize", update);
|
||
});
|
||
const cleanupIo = referenceEl && layoutShift ? observeMove(referenceEl, update) : null;
|
||
let reobserveFrame = -1;
|
||
let resizeObserver = null;
|
||
if (elementResize) {
|
||
resizeObserver = new ResizeObserver((_ref) => {
|
||
let [firstEntry] = _ref;
|
||
if (firstEntry && firstEntry.target === referenceEl && resizeObserver) {
|
||
resizeObserver.unobserve(floating);
|
||
cancelAnimationFrame(reobserveFrame);
|
||
reobserveFrame = requestAnimationFrame(() => {
|
||
var _resizeObserver;
|
||
(_resizeObserver = resizeObserver) == null || _resizeObserver.observe(floating);
|
||
});
|
||
}
|
||
update();
|
||
});
|
||
if (referenceEl && !animationFrame) {
|
||
resizeObserver.observe(referenceEl);
|
||
}
|
||
resizeObserver.observe(floating);
|
||
}
|
||
let frameId;
|
||
let prevRefRect = animationFrame ? getBoundingClientRect(reference) : null;
|
||
if (animationFrame) {
|
||
frameLoop();
|
||
}
|
||
function frameLoop() {
|
||
const nextRefRect = getBoundingClientRect(reference);
|
||
if (prevRefRect && (nextRefRect.x !== prevRefRect.x || nextRefRect.y !== prevRefRect.y || nextRefRect.width !== prevRefRect.width || nextRefRect.height !== prevRefRect.height)) {
|
||
update();
|
||
}
|
||
prevRefRect = nextRefRect;
|
||
frameId = requestAnimationFrame(frameLoop);
|
||
}
|
||
update();
|
||
return () => {
|
||
var _resizeObserver2;
|
||
ancestors.forEach((ancestor) => {
|
||
ancestorScroll && ancestor.removeEventListener("scroll", update);
|
||
ancestorResize && ancestor.removeEventListener("resize", update);
|
||
});
|
||
cleanupIo == null || cleanupIo();
|
||
(_resizeObserver2 = resizeObserver) == null || _resizeObserver2.disconnect();
|
||
resizeObserver = null;
|
||
if (animationFrame) {
|
||
cancelAnimationFrame(frameId);
|
||
}
|
||
};
|
||
}
|
||
const shift = shift$1;
|
||
const flip = flip$1;
|
||
const size = size$1;
|
||
const hide = hide$1;
|
||
const computePosition = (reference, floating, options) => {
|
||
const cache = /* @__PURE__ */ new Map();
|
||
const mergedOptions = __spreadValues({
|
||
platform
|
||
}, options);
|
||
const platformWithCache = __spreadProps(__spreadValues({}, mergedOptions.platform), {
|
||
_c: cache
|
||
});
|
||
return computePosition$1(reference, floating, __spreadProps(__spreadValues({}, mergedOptions), {
|
||
platform: platformWithCache
|
||
}));
|
||
};
|
||
function isComponentPublicInstance(target) {
|
||
return target != null && typeof target === "object" && "$el" in target;
|
||
}
|
||
function unwrapElement$1(target) {
|
||
if (isComponentPublicInstance(target)) {
|
||
const element = target.$el;
|
||
return isNode(element) && getNodeName(element) === "#comment" ? null : element;
|
||
}
|
||
return target;
|
||
}
|
||
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 useFloating(reference, floating, options) {
|
||
if (options === void 0) {
|
||
options = {};
|
||
}
|
||
const whileElementsMountedOption = options.whileElementsMounted;
|
||
const openOption = computed(() => {
|
||
var _unref;
|
||
return (_unref = unref(options.open)) != null ? _unref : true;
|
||
});
|
||
const middlewareOption = computed(() => unref(options.middleware));
|
||
const placementOption = computed(() => {
|
||
var _unref2;
|
||
return (_unref2 = unref(options.placement)) != null ? _unref2 : "bottom";
|
||
});
|
||
const strategyOption = computed(() => {
|
||
var _unref3;
|
||
return (_unref3 = unref(options.strategy)) != null ? _unref3 : "absolute";
|
||
});
|
||
const transformOption = computed(() => {
|
||
var _unref4;
|
||
return (_unref4 = unref(options.transform)) != null ? _unref4 : true;
|
||
});
|
||
const referenceElement = computed(() => unwrapElement$1(reference.value));
|
||
const floatingElement = computed(() => unwrapElement$1(floating.value));
|
||
const x = ref(0);
|
||
const y = ref(0);
|
||
const strategy = ref(strategyOption.value);
|
||
const placement = ref(placementOption.value);
|
||
const middlewareData = shallowRef({});
|
||
const isPositioned = ref(false);
|
||
const floatingStyles = computed(() => {
|
||
const initialStyles = {
|
||
position: strategy.value,
|
||
left: "0",
|
||
top: "0"
|
||
};
|
||
if (!floatingElement.value) {
|
||
return initialStyles;
|
||
}
|
||
const xVal = roundByDPR(floatingElement.value, x.value);
|
||
const yVal = roundByDPR(floatingElement.value, y.value);
|
||
if (transformOption.value) {
|
||
return __spreadValues(__spreadProps(__spreadValues({}, initialStyles), {
|
||
transform: "translate(" + xVal + "px, " + yVal + "px)"
|
||
}), getDPR(floatingElement.value) >= 1.5 && {
|
||
willChange: "transform"
|
||
});
|
||
}
|
||
return {
|
||
position: strategy.value,
|
||
left: xVal + "px",
|
||
top: yVal + "px"
|
||
};
|
||
});
|
||
let whileElementsMountedCleanup;
|
||
function update() {
|
||
if (referenceElement.value == null || floatingElement.value == null) {
|
||
return;
|
||
}
|
||
computePosition(referenceElement.value, floatingElement.value, {
|
||
middleware: middlewareOption.value,
|
||
placement: placementOption.value,
|
||
strategy: strategyOption.value
|
||
}).then((position) => {
|
||
x.value = position.x;
|
||
y.value = position.y;
|
||
strategy.value = position.strategy;
|
||
placement.value = position.placement;
|
||
middlewareData.value = position.middlewareData;
|
||
isPositioned.value = true;
|
||
});
|
||
}
|
||
function cleanup() {
|
||
if (typeof whileElementsMountedCleanup === "function") {
|
||
whileElementsMountedCleanup();
|
||
whileElementsMountedCleanup = void 0;
|
||
}
|
||
}
|
||
function attach() {
|
||
cleanup();
|
||
if (whileElementsMountedOption === void 0) {
|
||
update();
|
||
return;
|
||
}
|
||
if (referenceElement.value != null && floatingElement.value != null) {
|
||
whileElementsMountedCleanup = whileElementsMountedOption(referenceElement.value, floatingElement.value, update);
|
||
return;
|
||
}
|
||
}
|
||
function reset() {
|
||
if (!openOption.value) {
|
||
isPositioned.value = false;
|
||
}
|
||
}
|
||
watch([middlewareOption, placementOption, strategyOption], update, {
|
||
flush: "sync"
|
||
});
|
||
watch([referenceElement, floatingElement], attach, {
|
||
flush: "sync"
|
||
});
|
||
watch(openOption, reset, {
|
||
flush: "sync"
|
||
});
|
||
if (getCurrentScope()) {
|
||
onScopeDispose(cleanup);
|
||
}
|
||
return {
|
||
x: shallowReadonly(x),
|
||
y: shallowReadonly(y),
|
||
strategy: shallowReadonly(strategy),
|
||
placement: shallowReadonly(placement),
|
||
middlewareData: shallowReadonly(middlewareData),
|
||
isPositioned: shallowReadonly(isPositioned),
|
||
floatingStyles,
|
||
update
|
||
};
|
||
}
|
||
function unwrapElement(element) {
|
||
return element && "$el" in element ? element.$el : element;
|
||
}
|
||
const clipPadding = 16;
|
||
const minClipHeight = 128;
|
||
function useFloatingMenu(referenceElement, menu, opt) {
|
||
var _a;
|
||
const menuIsExpanded = () => {
|
||
var _a2;
|
||
return (_a2 = menu.value) == null ? void 0 : _a2.isExpanded();
|
||
};
|
||
const middleware = [
|
||
offset(opt == null ? void 0 : opt.offset),
|
||
size({
|
||
// Don't size the menu to take up exactly all of the available height, because that
|
||
// makes it look like it's cut off. Instead, leave 16px of free space between the bottom
|
||
// of the menu and the bottom edge of the viewport / scrollable container.
|
||
padding: clipPadding,
|
||
apply({ rects, elements, availableHeight, availableWidth }) {
|
||
Object.assign(elements.floating.style, {
|
||
// Optionally use all available width
|
||
// Else, set the width of the menu to match the width of the triggering element.
|
||
// This is needed in Dialogs, when the menu's position is set relative to
|
||
// the dialog, not the triggering element.
|
||
width: "".concat((opt == null ? void 0 : opt.useAvailableWidth) ? availableWidth : rects.reference.width, "px"),
|
||
// Set the max-height to the available height, to prevent the menu from
|
||
// extending past the edge of the viewport or scrollable container. But don't
|
||
// allow the menu to be shrunk to less than 128px; this is necessary to make
|
||
// the flip() call below work.
|
||
maxHeight: "".concat(Math.max(minClipHeight, availableHeight), "px")
|
||
});
|
||
}
|
||
}),
|
||
// If there is not enough space to put the menu below the triggering element, put it above
|
||
// it instead. Because of the maxHeight logic above, this happens when there is less than
|
||
// 128px available below the triggering element.
|
||
flip({
|
||
// Apply the same padding here as in size(), otherwise the gap between the bottom of
|
||
// the menu and the bottom edge of the viewport is allowed to shrink to zero before the
|
||
// menu flips.
|
||
padding: clipPadding
|
||
}),
|
||
// Hide the menu when it has escaped the reference element's clipping context (e.g. the menu
|
||
// is opened down and you scroll up until the reference element just starts to leave the
|
||
// container).
|
||
hide({
|
||
strategy: "escaped"
|
||
}),
|
||
// Hide the menu when the reference element is fully hidden (e.g. the menu is opened down
|
||
// and you scroll down until the whole reference element is gone).
|
||
hide()
|
||
];
|
||
const { floatingStyles, placement, middlewareData, update } = useFloating(
|
||
referenceElement,
|
||
menu,
|
||
{
|
||
middleware,
|
||
placement: (_a = opt == null ? void 0 : opt.placement) != null ? _a : "bottom"
|
||
}
|
||
);
|
||
const menuVisibility = computed(() => {
|
||
var _a2, _b;
|
||
const isHidden = !menuIsExpanded() || !!((_a2 = middlewareData.value.hide) == null ? void 0 : _a2.escaped) || ((_b = middlewareData.value.hide) == null ? void 0 : _b.referenceHidden);
|
||
return isHidden ? "hidden" : "visible";
|
||
});
|
||
watch(
|
||
[floatingStyles, menuVisibility, placement],
|
||
([newStyles, newVisibility, newPlacement]) => {
|
||
var _a2, _b, _c, _d, _e;
|
||
Object.assign((_b = (_a2 = menu.value) == null ? void 0 : _a2.$el.style) != null ? _b : {}, {
|
||
visibility: newVisibility,
|
||
position: newStyles.position,
|
||
top: "".concat(newStyles.top, "px"),
|
||
// `left: 0` is set in the Menu component, which gets transformed to `right: 0` for
|
||
// RTL. For this component, we must unset `right: 0`, because the transform value
|
||
// is relative to the left side of the screen regardless of reading direction.
|
||
right: "unset",
|
||
// Set `left` value to ensure the menu is translated relative to the left side of
|
||
// the screen, which is what FloatingUI expects when it calculates the translate-x
|
||
// value for both LTR and RTL.
|
||
left: "".concat(newStyles.left, "px"),
|
||
// If menuWidth is specified, transform shifts negative, for now ignore that
|
||
transform: (_c = newStyles.transform) != null ? _c : "none",
|
||
// Zero out border-radius on the corners of the menu where it touches the reference
|
||
// element. Which corners these are depends on whether the menu is flipped
|
||
borderTopLeftRadius: newPlacement === "bottom" && newVisibility === "visible" ? "0" : "",
|
||
borderTopRightRadius: newPlacement === "bottom" && newVisibility === "visible" ? "0" : "",
|
||
borderBottomLeftRadius: newPlacement === "top" && newVisibility === "visible" ? "0" : "",
|
||
borderBottomRightRadius: newPlacement === "top" && newVisibility === "visible" ? "0" : ""
|
||
});
|
||
Object.assign((_e = (_d = unwrapElement(referenceElement.value)) == null ? void 0 : _d.style) != null ? _e : {}, {
|
||
// Zero out border-radius on the corners of the reference element where it touches
|
||
// the menu. Which corners these are depends on whether the menu is flipped
|
||
borderTopLeftRadius: newPlacement === "top" && newVisibility === "visible" ? "0" : "",
|
||
borderTopRightRadius: newPlacement === "top" && newVisibility === "visible" ? "0" : "",
|
||
borderBottomLeftRadius: newPlacement === "bottom" && newVisibility === "visible" ? "0" : "",
|
||
borderBottomRightRadius: newPlacement === "bottom" && newVisibility === "visible" ? "0" : ""
|
||
});
|
||
}
|
||
);
|
||
let cleanupAutoUpdate = null;
|
||
watch(menuIsExpanded, (newExpanded) => {
|
||
var _a2;
|
||
if (newExpanded) {
|
||
cleanupAutoUpdate = autoUpdate(
|
||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||
referenceElement.value && "$el" in referenceElement.value ? referenceElement.value.$el : referenceElement,
|
||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
|
||
(_a2 = menu.value) == null ? void 0 : _a2.$el,
|
||
update
|
||
);
|
||
} else {
|
||
if (cleanupAutoUpdate) {
|
||
cleanupAutoUpdate();
|
||
cleanupAutoUpdate = null;
|
||
}
|
||
}
|
||
});
|
||
}
|
||
const statusValidator$7 = makeStringTypeValidator(ValidationStatusTypes);
|
||
const _sfc_main$j = defineComponent({
|
||
name: "CdxCombobox",
|
||
components: {
|
||
CdxButton,
|
||
CdxIcon,
|
||
CdxMenu,
|
||
CdxTextInput
|
||
},
|
||
/**
|
||
* Attributes applied to this component by a parent will be applied
|
||
* to the TextInput child component rather than the root element.
|
||
*/
|
||
inheritAttrs: false,
|
||
props: {
|
||
/**
|
||
* Menu items. See the MenuItemData type.
|
||
*/
|
||
menuItems: {
|
||
type: Array,
|
||
required: true
|
||
},
|
||
/**
|
||
* Value of the current selection.
|
||
*
|
||
* Must be bound with `v-model:selected`.
|
||
*/
|
||
selected: {
|
||
type: [String, Number],
|
||
required: true
|
||
},
|
||
/**
|
||
* Whether the dropdown is disabled.
|
||
*/
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Configuration for various menu features. All properties default to false.
|
||
*
|
||
* See the MenuConfig type.
|
||
* @default {}
|
||
*/
|
||
menuConfig: {
|
||
type: Object,
|
||
default: () => {
|
||
return {};
|
||
}
|
||
},
|
||
/**
|
||
* `status` property of the TextInput component
|
||
*/
|
||
status: {
|
||
type: String,
|
||
default: "default",
|
||
validator: statusValidator$7
|
||
}
|
||
},
|
||
emits: [
|
||
/**
|
||
* When the selected value changes.
|
||
*
|
||
* @property {string | number} selected The new selected value
|
||
*/
|
||
"update:selected",
|
||
/**
|
||
* When the user scrolls towards the bottom of the menu.
|
||
*
|
||
* If it is possible to add or load more menu items, then now would be a good moment
|
||
* so that the user can experience infinite scrolling.
|
||
*/
|
||
"load-more",
|
||
/**
|
||
* When the input value changes via direct use of the input
|
||
*
|
||
* @property {InputEvent} event
|
||
*/
|
||
"input",
|
||
/**
|
||
* When an input value change is committed by the user (e.g. on blur)
|
||
*
|
||
* @property {Event} event
|
||
*/
|
||
"change",
|
||
/**
|
||
* When the input comes into focus
|
||
*
|
||
* @property {FocusEvent} event
|
||
*/
|
||
"focus",
|
||
/**
|
||
* When the input loses focus
|
||
*
|
||
* @property {FocusEvent} event
|
||
*/
|
||
"blur"
|
||
],
|
||
setup(props, { emit, attrs, slots }) {
|
||
const input = ref();
|
||
const inputWrapper = ref();
|
||
const menu = ref();
|
||
const menuId = useGeneratedId("combobox");
|
||
const selectedProp = toRef(props, "selected");
|
||
const modelWrapper = useModelWrapper(selectedProp, emit, "update:selected");
|
||
const expanded = ref(false);
|
||
const expanderClicked = ref(false);
|
||
const highlightedId = computed(() => {
|
||
var _a, _b;
|
||
return (_b = (_a = menu.value) == null ? void 0 : _a.getHighlightedMenuItem()) == null ? void 0 : _b.id;
|
||
});
|
||
const {
|
||
computedDisabled,
|
||
computedStatus
|
||
} = useFieldData(
|
||
toRef(props, "disabled"),
|
||
toRef(props, "status")
|
||
);
|
||
const internalClasses = computed(() => {
|
||
return {
|
||
"cdx-combobox--expanded": expanded.value,
|
||
"cdx-combobox--disabled": computedDisabled.value
|
||
};
|
||
});
|
||
const {
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrs
|
||
} = useSplitAttributes(attrs, internalClasses);
|
||
function onInputFocus(event) {
|
||
if (expanderClicked.value && expanded.value) {
|
||
expanded.value = false;
|
||
} else if (props.menuItems.length > 0 || slots["no-results"]) {
|
||
expanded.value = true;
|
||
}
|
||
emit("focus", event);
|
||
}
|
||
function onInputBlur(event) {
|
||
expanded.value = expanderClicked.value && expanded.value;
|
||
emit("blur", event);
|
||
}
|
||
function onButtonMousedown() {
|
||
if (computedDisabled.value) {
|
||
return;
|
||
}
|
||
expanderClicked.value = true;
|
||
}
|
||
function onButtonClick() {
|
||
var _a;
|
||
if (computedDisabled.value) {
|
||
return;
|
||
}
|
||
(_a = input.value) == null ? void 0 : _a.focus();
|
||
}
|
||
function onKeydown(e) {
|
||
if (!menu.value || computedDisabled.value || props.menuItems.length === 0 || e.key === " ") {
|
||
return;
|
||
}
|
||
menu.value.delegateKeyNavigation(e);
|
||
}
|
||
useFloatingMenu(input, menu);
|
||
watch(expanded, () => {
|
||
expanderClicked.value = false;
|
||
});
|
||
return {
|
||
input,
|
||
inputWrapper,
|
||
menu,
|
||
menuId,
|
||
modelWrapper,
|
||
expanded,
|
||
highlightedId,
|
||
computedDisabled,
|
||
computedStatus,
|
||
onInputFocus,
|
||
onInputBlur,
|
||
onKeydown,
|
||
onButtonClick,
|
||
onButtonMousedown,
|
||
cdxIconExpand: m4,
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrs
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$h = {
|
||
ref: "inputWrapper",
|
||
class: "cdx-combobox__input-wrapper"
|
||
};
|
||
function _sfc_render$j(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_text_input = resolveComponent("cdx-text-input");
|
||
const _component_cdx_icon = resolveComponent("cdx-icon");
|
||
const _component_cdx_button = resolveComponent("cdx-button");
|
||
const _component_cdx_menu = resolveComponent("cdx-menu");
|
||
return openBlock(), createElementBlock(
|
||
"div",
|
||
{
|
||
class: normalizeClass(["cdx-combobox", _ctx.rootClasses]),
|
||
style: normalizeStyle(_ctx.rootStyle)
|
||
},
|
||
[
|
||
createElementVNode(
|
||
"div",
|
||
_hoisted_1$h,
|
||
[
|
||
createVNode(_component_cdx_text_input, mergeProps({
|
||
ref: "input",
|
||
modelValue: _ctx.modelWrapper,
|
||
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => _ctx.modelWrapper = $event)
|
||
}, _ctx.otherAttrs, {
|
||
class: "cdx-combobox__input",
|
||
"aria-activedescendant": _ctx.highlightedId,
|
||
"aria-expanded": _ctx.expanded,
|
||
"aria-controls": _ctx.menuId,
|
||
disabled: _ctx.computedDisabled,
|
||
status: _ctx.computedStatus,
|
||
autocomplete: "off",
|
||
role: "combobox",
|
||
onKeydown: _ctx.onKeydown,
|
||
onInput: _cache[1] || (_cache[1] = ($event) => _ctx.$emit("input", $event)),
|
||
onChange: _cache[2] || (_cache[2] = ($event) => _ctx.$emit("change", $event)),
|
||
onFocus: _ctx.onInputFocus,
|
||
onBlur: _ctx.onInputBlur
|
||
}), null, 16, ["modelValue", "aria-activedescendant", "aria-expanded", "aria-controls", "disabled", "status", "onKeydown", "onFocus", "onBlur"]),
|
||
createVNode(_component_cdx_button, {
|
||
class: "cdx-combobox__expand-button",
|
||
"aria-hidden": "true",
|
||
disabled: _ctx.computedDisabled,
|
||
tabindex: "-1",
|
||
type: "button",
|
||
onMousedown: _ctx.onButtonMousedown,
|
||
onClick: _ctx.onButtonClick
|
||
}, {
|
||
default: withCtx(() => [
|
||
createVNode(_component_cdx_icon, {
|
||
class: "cdx-combobox__expand-icon",
|
||
icon: _ctx.cdxIconExpand
|
||
}, null, 8, ["icon"])
|
||
]),
|
||
_: 1
|
||
/* STABLE */
|
||
}, 8, ["disabled", "onMousedown", "onClick"])
|
||
],
|
||
512
|
||
/* NEED_PATCH */
|
||
),
|
||
createVNode(_component_cdx_menu, mergeProps({
|
||
id: _ctx.menuId,
|
||
ref: "menu",
|
||
selected: _ctx.modelWrapper,
|
||
"onUpdate:selected": _cache[3] || (_cache[3] = ($event) => _ctx.modelWrapper = $event),
|
||
expanded: _ctx.expanded,
|
||
"onUpdate:expanded": _cache[4] || (_cache[4] = ($event) => _ctx.expanded = $event),
|
||
"menu-items": _ctx.menuItems
|
||
}, _ctx.menuConfig, {
|
||
onLoadMore: _cache[5] || (_cache[5] = ($event) => _ctx.$emit("load-more"))
|
||
}), {
|
||
default: withCtx(({ menuItem }) => [
|
||
renderSlot(_ctx.$slots, "menu-item", { menuItem })
|
||
]),
|
||
"no-results": withCtx(() => [
|
||
renderSlot(_ctx.$slots, "no-results")
|
||
]),
|
||
_: 3
|
||
/* FORWARDED */
|
||
}, 16, ["id", "selected", "expanded", "menu-items"])
|
||
],
|
||
6
|
||
/* CLASS, STYLE */
|
||
);
|
||
}
|
||
const Combobox = /* @__PURE__ */ _export_sfc(_sfc_main$j, [["render", _sfc_render$j]]);
|
||
function useResizeObserver(templateRef) {
|
||
const currentDimensions = ref(
|
||
{ width: void 0, height: void 0 }
|
||
);
|
||
if (typeof window !== "object" || !("ResizeObserver" in window) || !("ResizeObserverEntry" in window)) {
|
||
return currentDimensions;
|
||
}
|
||
const observer = new window.ResizeObserver(
|
||
(entries) => {
|
||
const entry = entries[0];
|
||
if (entry) {
|
||
currentDimensions.value = {
|
||
width: entry.borderBoxSize[0].inlineSize,
|
||
height: entry.borderBoxSize[0].blockSize
|
||
};
|
||
}
|
||
}
|
||
);
|
||
let mounted = false;
|
||
onMounted(() => {
|
||
mounted = true;
|
||
if (templateRef.value) {
|
||
observer.observe(templateRef.value);
|
||
}
|
||
});
|
||
onUnmounted(() => {
|
||
mounted = false;
|
||
observer.disconnect();
|
||
});
|
||
watch(templateRef, (newElement) => {
|
||
if (!mounted) {
|
||
return;
|
||
}
|
||
observer.disconnect();
|
||
currentDimensions.value = {
|
||
width: void 0,
|
||
height: void 0
|
||
};
|
||
if (newElement) {
|
||
observer.observe(newElement);
|
||
}
|
||
});
|
||
return currentDimensions;
|
||
}
|
||
const _sfc_main$i = defineComponent({
|
||
name: "CdxDialog",
|
||
components: {
|
||
CdxButton,
|
||
CdxIcon
|
||
},
|
||
inheritAttrs: false,
|
||
props: {
|
||
/**
|
||
* Whether the dialog is visible. Should be provided via a v-model:open
|
||
* binding in the parent scope.
|
||
*/
|
||
open: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Title for the dialog header. Used for ARIA purposes even if no
|
||
* visible header element is displayed.
|
||
*/
|
||
title: {
|
||
type: String,
|
||
required: true
|
||
},
|
||
/**
|
||
* Optional subtitle for the dialog.
|
||
*/
|
||
subtitle: {
|
||
type: String,
|
||
required: false,
|
||
default: null
|
||
},
|
||
/**
|
||
* Whether the dialog header should hide the title & subtitle
|
||
*/
|
||
hideTitle: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Add an icon-only close button to the dialog header.
|
||
*/
|
||
useCloseButton: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
// DEPRECATED: Set default to 'Close' (T368444)
|
||
/**
|
||
* Visually-hidden label text for the icon-only close button in the header.
|
||
*
|
||
* Omit this prop to use the default value, "Close".
|
||
*/
|
||
closeButtonLabel: {
|
||
type: String,
|
||
default: ""
|
||
},
|
||
/**
|
||
* Primary user action. This will display a primary button with the specified action
|
||
* (progressive or destructive).
|
||
*/
|
||
primaryAction: {
|
||
type: Object,
|
||
default: null
|
||
},
|
||
/**
|
||
* Default user action. This will display a normal button.
|
||
*/
|
||
defaultAction: {
|
||
type: Object,
|
||
default: null
|
||
},
|
||
/**
|
||
* Whether action buttons should be vertically stacked and 100% width.
|
||
*/
|
||
stackedActions: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Selector or DOM element identifying the container the dialog should
|
||
* be rendered in. The dialog will be `<teleport>`ed to this element.
|
||
* An ID selector is recommended, e.g. `#foo-bar`, but providing an
|
||
* actual element is also supported.
|
||
*
|
||
* If this prop is not set, and the parent or one of its ancestors
|
||
* provides a teleport target using `provide( 'CdxTeleportTarget',
|
||
* '#foo-bar' )`, the provided target will be used. If there is no
|
||
* provided target, the dialog will be teleported to the end of the
|
||
* `<body>` element.
|
||
*/
|
||
target: {
|
||
type: String,
|
||
default: null
|
||
},
|
||
/**
|
||
* Whether to disable the use of teleport and render the Dialog in its
|
||
* original location in the document. If this is true, the `target` prop
|
||
* is ignored.
|
||
*/
|
||
renderInPlace: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
},
|
||
emits: [
|
||
/**
|
||
* When the open/close state changes, e.g. when the close button is clicked.
|
||
*
|
||
* @property {boolean} newValue The new open/close state (true for open, false for closed)
|
||
*/
|
||
"update:open",
|
||
/**
|
||
* When the primary action button is clicked.
|
||
*/
|
||
"primary",
|
||
/**
|
||
* When the default action button is clicked.
|
||
*/
|
||
"default"
|
||
],
|
||
setup(props, { emit }) {
|
||
const labelId = useGeneratedId("dialog-label");
|
||
const backdrop = ref();
|
||
const dialogElement = ref();
|
||
const dialogBody = ref();
|
||
const focusHolder = ref();
|
||
const focusTrapStart = ref();
|
||
const focusTrapEnd = ref();
|
||
let previouslyFocused = null;
|
||
const useCloseButtonOrLabel = computed(
|
||
() => props.useCloseButton || props.closeButtonLabel.length > 0
|
||
);
|
||
const translatedCloseButtonLabel = useI18nWithOverride(
|
||
toRef(props, "closeButtonLabel"),
|
||
"cdx-dialog-close-button-label",
|
||
"Close"
|
||
);
|
||
const showHeader = computed(() => !props.hideTitle || useCloseButtonOrLabel.value);
|
||
const showFooterActions = computed(() => !!props.primaryAction || !!props.defaultAction);
|
||
const bodyDimensions = useResizeObserver(dialogBody);
|
||
const currentBodyHeight = computed(() => {
|
||
var _a;
|
||
return (_a = bodyDimensions.value.height) != null ? _a : 0;
|
||
});
|
||
const showDividers = ref(false);
|
||
const rootClasses = computed(() => ({
|
||
"cdx-dialog--vertical-actions": props.stackedActions,
|
||
"cdx-dialog--horizontal-actions": !props.stackedActions,
|
||
"cdx-dialog--dividers": showDividers.value
|
||
}));
|
||
const providedTarget = inject("CdxTeleportTarget", void 0);
|
||
const computedTarget = computed(() => {
|
||
var _a, _b;
|
||
return (_b = (_a = props.target) != null ? _a : providedTarget) != null ? _b : "body";
|
||
});
|
||
const scrollWidth = ref(0);
|
||
function close() {
|
||
emit("update:open", false);
|
||
}
|
||
function focusFirst() {
|
||
focusFirstFocusableElement(dialogElement.value);
|
||
}
|
||
function focusLast() {
|
||
focusFirstFocusableElement(dialogElement.value, true);
|
||
}
|
||
function focusFirstFocusableElement(container, backwards = false) {
|
||
let candidates = Array.from(
|
||
container.querySelectorAll('\n input, select, textarea, button, object, a, area,\n [contenteditable], [tabindex]:not([tabindex^="-"])\n ')
|
||
);
|
||
if (backwards) {
|
||
candidates = candidates.reverse();
|
||
}
|
||
for (const candidate of candidates) {
|
||
candidate.focus();
|
||
if (document.activeElement === candidate) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
let ariaHiddenElements = [];
|
||
let inertElements = [];
|
||
function setAriaHiddenAndInert() {
|
||
let element = backdrop.value;
|
||
while (element.parentElement && element.nodeName !== "BODY") {
|
||
for (const sibling of Array.from(element.parentElement.children)) {
|
||
if (sibling === element || sibling.nodeName === "SCRIPT") {
|
||
continue;
|
||
}
|
||
if (!sibling.hasAttribute("aria-hidden")) {
|
||
sibling.setAttribute("aria-hidden", "true");
|
||
ariaHiddenElements.push(sibling);
|
||
}
|
||
if (!sibling.hasAttribute("inert")) {
|
||
sibling.setAttribute("inert", "");
|
||
inertElements.push(sibling);
|
||
}
|
||
}
|
||
element = element.parentElement;
|
||
}
|
||
}
|
||
function unsetAriaHiddenAndInert() {
|
||
for (const element of ariaHiddenElements) {
|
||
element.removeAttribute("aria-hidden");
|
||
}
|
||
for (const element of inertElements) {
|
||
element.removeAttribute("inert");
|
||
}
|
||
ariaHiddenElements = [];
|
||
inertElements = [];
|
||
}
|
||
function onDialogOpen() {
|
||
return __async(this, null, function* () {
|
||
var _a;
|
||
yield nextTick();
|
||
scrollWidth.value = window.innerWidth - document.documentElement.clientWidth;
|
||
document.documentElement.style.setProperty("margin-right", "".concat(scrollWidth.value, "px"));
|
||
document.body.classList.add("cdx-dialog-open");
|
||
setAriaHiddenAndInert();
|
||
previouslyFocused = document.activeElement;
|
||
if (!focusFirstFocusableElement(dialogBody.value)) {
|
||
(_a = focusHolder.value) == null ? void 0 : _a.focus();
|
||
}
|
||
});
|
||
}
|
||
function onDialogClose() {
|
||
document.body.classList.remove("cdx-dialog-open");
|
||
document.documentElement.style.removeProperty("margin-right");
|
||
unsetAriaHiddenAndInert();
|
||
if (previouslyFocused instanceof HTMLElement && document.contains(previouslyFocused)) {
|
||
previouslyFocused.focus();
|
||
previouslyFocused = null;
|
||
}
|
||
}
|
||
onMounted(() => {
|
||
if (props.open) {
|
||
onDialogOpen();
|
||
}
|
||
});
|
||
onUnmounted(() => {
|
||
if (props.open) {
|
||
onDialogClose();
|
||
}
|
||
});
|
||
watch(toRef(props, "open"), (opened) => {
|
||
if (opened) {
|
||
onDialogOpen();
|
||
} else {
|
||
onDialogClose();
|
||
}
|
||
});
|
||
watch(currentBodyHeight, () => {
|
||
if (dialogBody.value) {
|
||
showDividers.value = dialogBody.value.clientHeight < dialogBody.value.scrollHeight;
|
||
}
|
||
});
|
||
return {
|
||
close,
|
||
cdxIconClose: X3,
|
||
labelId,
|
||
rootClasses,
|
||
backdrop,
|
||
dialogElement,
|
||
focusTrapStart,
|
||
focusTrapEnd,
|
||
focusFirst,
|
||
focusLast,
|
||
dialogBody,
|
||
focusHolder,
|
||
showHeader,
|
||
showFooterActions,
|
||
useCloseButtonOrLabel,
|
||
translatedCloseButtonLabel,
|
||
computedTarget
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$g = ["aria-label", "aria-labelledby"];
|
||
const _hoisted_2$a = {
|
||
key: 0,
|
||
class: "cdx-dialog__header__title-group"
|
||
};
|
||
const _hoisted_3$6 = ["id"];
|
||
const _hoisted_4$4 = {
|
||
key: 0,
|
||
class: "cdx-dialog__header__subtitle"
|
||
};
|
||
const _hoisted_5$4 = {
|
||
ref: "focusHolder",
|
||
class: "cdx-dialog-focus-trap",
|
||
tabindex: "-1"
|
||
};
|
||
const _hoisted_6$4 = {
|
||
key: 0,
|
||
class: "cdx-dialog__footer__text"
|
||
};
|
||
const _hoisted_7$1 = {
|
||
key: 1,
|
||
class: "cdx-dialog__footer__actions"
|
||
};
|
||
function _sfc_render$i(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_icon = resolveComponent("cdx-icon");
|
||
const _component_cdx_button = resolveComponent("cdx-button");
|
||
return openBlock(), createBlock(Teleport, {
|
||
to: _ctx.computedTarget,
|
||
disabled: _ctx.renderInPlace
|
||
}, [
|
||
createVNode(Transition, {
|
||
name: "cdx-dialog-fade",
|
||
appear: ""
|
||
}, {
|
||
default: withCtx(() => [
|
||
_ctx.open ? (openBlock(), createElementBlock(
|
||
"div",
|
||
{
|
||
key: 0,
|
||
ref: "backdrop",
|
||
class: "cdx-dialog-backdrop",
|
||
onClick: _cache[5] || (_cache[5] = (...args) => _ctx.close && _ctx.close(...args)),
|
||
onKeyup: _cache[6] || (_cache[6] = withKeys((...args) => _ctx.close && _ctx.close(...args), ["escape"]))
|
||
},
|
||
[
|
||
createElementVNode(
|
||
"div",
|
||
{
|
||
ref: "focusTrapStart",
|
||
tabindex: "0",
|
||
onFocus: _cache[0] || (_cache[0] = (...args) => _ctx.focusLast && _ctx.focusLast(...args))
|
||
},
|
||
null,
|
||
544
|
||
/* NEED_HYDRATION, NEED_PATCH */
|
||
),
|
||
createElementVNode("div", mergeProps({
|
||
ref: "dialogElement",
|
||
class: ["cdx-dialog", _ctx.rootClasses],
|
||
role: "dialog"
|
||
}, _ctx.$attrs, {
|
||
"aria-label": _ctx.$slots.header || _ctx.hideTitle ? _ctx.title : void 0,
|
||
"aria-labelledby": !_ctx.$slots.header && !_ctx.hideTitle ? _ctx.labelId : void 0,
|
||
"aria-modal": "true",
|
||
onClick: _cache[3] || (_cache[3] = withModifiers(() => {
|
||
}, ["stop"]))
|
||
}), [
|
||
_ctx.showHeader || _ctx.$slots.header ? (openBlock(), createElementBlock(
|
||
"header",
|
||
{
|
||
key: 0,
|
||
class: normalizeClass(["cdx-dialog__header", { "cdx-dialog__header--default": !_ctx.$slots.header }])
|
||
},
|
||
[
|
||
renderSlot(_ctx.$slots, "header", {}, () => [
|
||
!_ctx.hideTitle ? (openBlock(), createElementBlock("div", _hoisted_2$a, [
|
||
createElementVNode("h2", {
|
||
id: _ctx.labelId,
|
||
class: "cdx-dialog__header__title"
|
||
}, toDisplayString(_ctx.title), 9, _hoisted_3$6),
|
||
_ctx.subtitle ? (openBlock(), createElementBlock(
|
||
"p",
|
||
_hoisted_4$4,
|
||
toDisplayString(_ctx.subtitle),
|
||
1
|
||
/* TEXT */
|
||
)) : createCommentVNode("v-if", true)
|
||
])) : createCommentVNode("v-if", true),
|
||
_ctx.useCloseButtonOrLabel ? (openBlock(), createBlock(_component_cdx_button, {
|
||
key: 1,
|
||
class: "cdx-dialog__header__close-button",
|
||
weight: "quiet",
|
||
type: "button",
|
||
"aria-label": _ctx.translatedCloseButtonLabel,
|
||
onClick: _ctx.close
|
||
}, {
|
||
default: withCtx(() => [
|
||
createVNode(_component_cdx_icon, { icon: _ctx.cdxIconClose }, null, 8, ["icon"])
|
||
]),
|
||
_: 1
|
||
/* STABLE */
|
||
}, 8, ["aria-label", "onClick"])) : createCommentVNode("v-if", true)
|
||
])
|
||
],
|
||
2
|
||
/* CLASS */
|
||
)) : createCommentVNode("v-if", true),
|
||
createElementVNode(
|
||
"div",
|
||
_hoisted_5$4,
|
||
null,
|
||
512
|
||
/* NEED_PATCH */
|
||
),
|
||
createElementVNode(
|
||
"div",
|
||
{
|
||
ref: "dialogBody",
|
||
class: normalizeClass(["cdx-dialog__body", {
|
||
"cdx-dialog__body--no-header": !(_ctx.showHeader || _ctx.$slots.header),
|
||
"cdx-dialog__body--no-footer": !(_ctx.showFooterActions || _ctx.$slots.footer || _ctx.$slots["footer-text"])
|
||
}])
|
||
},
|
||
[
|
||
renderSlot(_ctx.$slots, "default")
|
||
],
|
||
2
|
||
/* CLASS */
|
||
),
|
||
_ctx.showFooterActions || _ctx.$slots.footer || _ctx.$slots["footer-text"] ? (openBlock(), createElementBlock(
|
||
"footer",
|
||
{
|
||
key: 1,
|
||
class: normalizeClass(["cdx-dialog__footer", { "cdx-dialog__footer--default": !_ctx.$slots.footer }])
|
||
},
|
||
[
|
||
renderSlot(_ctx.$slots, "footer", {}, () => [
|
||
_ctx.$slots["footer-text"] ? (openBlock(), createElementBlock("p", _hoisted_6$4, [
|
||
renderSlot(_ctx.$slots, "footer-text")
|
||
])) : createCommentVNode("v-if", true),
|
||
_ctx.showFooterActions ? (openBlock(), createElementBlock("div", _hoisted_7$1, [
|
||
_ctx.primaryAction ? (openBlock(), createBlock(_component_cdx_button, {
|
||
key: 0,
|
||
class: "cdx-dialog__footer__primary-action",
|
||
weight: "primary",
|
||
action: _ctx.primaryAction.actionType,
|
||
disabled: _ctx.primaryAction.disabled,
|
||
onClick: _cache[1] || (_cache[1] = ($event) => _ctx.$emit("primary"))
|
||
}, {
|
||
default: withCtx(() => [
|
||
createTextVNode(
|
||
toDisplayString(_ctx.primaryAction.label),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
]),
|
||
_: 1
|
||
/* STABLE */
|
||
}, 8, ["action", "disabled"])) : createCommentVNode("v-if", true),
|
||
_ctx.defaultAction ? (openBlock(), createBlock(_component_cdx_button, {
|
||
key: 1,
|
||
class: "cdx-dialog__footer__default-action",
|
||
disabled: _ctx.defaultAction.disabled,
|
||
onClick: _cache[2] || (_cache[2] = ($event) => _ctx.$emit("default"))
|
||
}, {
|
||
default: withCtx(() => [
|
||
createTextVNode(
|
||
toDisplayString(_ctx.defaultAction.label),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
]),
|
||
_: 1
|
||
/* STABLE */
|
||
}, 8, ["disabled"])) : createCommentVNode("v-if", true)
|
||
])) : createCommentVNode("v-if", true)
|
||
])
|
||
],
|
||
2
|
||
/* CLASS */
|
||
)) : createCommentVNode("v-if", true)
|
||
], 16, _hoisted_1$g),
|
||
createElementVNode(
|
||
"div",
|
||
{
|
||
ref: "focusTrapEnd",
|
||
tabindex: "0",
|
||
onFocus: _cache[4] || (_cache[4] = (...args) => _ctx.focusFirst && _ctx.focusFirst(...args))
|
||
},
|
||
null,
|
||
544
|
||
/* NEED_HYDRATION, NEED_PATCH */
|
||
)
|
||
],
|
||
544
|
||
/* NEED_HYDRATION, NEED_PATCH */
|
||
)) : createCommentVNode("v-if", true)
|
||
]),
|
||
_: 3
|
||
/* FORWARDED */
|
||
})
|
||
], 8, ["to", "disabled"]);
|
||
}
|
||
const Dialog = /* @__PURE__ */ _export_sfc(_sfc_main$i, [["render", _sfc_render$i]]);
|
||
const iconMap$2 = {
|
||
notice: Y4,
|
||
error: i4,
|
||
warning: M3,
|
||
success: O7
|
||
};
|
||
const _sfc_main$h = defineComponent({
|
||
name: "CdxMessage",
|
||
components: { CdxButton, CdxIcon },
|
||
props: {
|
||
/**
|
||
* Status type of Message.
|
||
*
|
||
* @values 'notice', 'warning', 'error', 'success'
|
||
*/
|
||
type: {
|
||
type: String,
|
||
default: "notice",
|
||
validator: statusTypeValidator
|
||
},
|
||
/**
|
||
* Whether this message follows the inline design (no padding, background color, or border).
|
||
*/
|
||
inline: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Custom message icon. Only allowed for notice messages.
|
||
*/
|
||
icon: {
|
||
type: [String, Object],
|
||
default: null
|
||
},
|
||
/**
|
||
* Whether the message should fade in. Should be used for messages that are dynamically
|
||
* displayed, not present on page load.
|
||
*/
|
||
fadeIn: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Allow the message to be dismissed by the user. Adds an icon-only dismiss button.
|
||
*/
|
||
allowUserDismiss: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
// DEPRECATED: set default to 'Close' (T368444).
|
||
/**
|
||
* Visually-hidden label text for the dismiss button for user-dismissable messages.
|
||
*
|
||
* Omit this prop to use the default value, "Close".
|
||
*/
|
||
dismissButtonLabel: {
|
||
type: String,
|
||
default: ""
|
||
},
|
||
/**
|
||
* Enable automatic dismissal of message after a period of time.
|
||
*
|
||
* This prop can be set to `true` to use the default display time of 4000 milliseconds. To
|
||
* customize the display time, set this prop to a number of milliseconds.
|
||
*
|
||
* Error messages cannot be automatically dismissed. If the `type` prop is set to `error`,
|
||
* this prop will be ignored.
|
||
*
|
||
* TODO: consider adding a stricter validator to set limits on this. If the time is too
|
||
* short, the message may not be readable. If the time is too long, the message probably
|
||
* shouldn't be auto-dismissed.
|
||
*/
|
||
autoDismiss: {
|
||
type: [Boolean, Number],
|
||
default: false,
|
||
validator: (value) => typeof value === "boolean" || typeof value === "number" && value > 0
|
||
}
|
||
},
|
||
emits: [
|
||
/**
|
||
* Emitted when the message is dismissed by the user via the dismiss button.
|
||
*/
|
||
"user-dismissed",
|
||
/**
|
||
* Emitted when the message is automatically dismissed after the display time.
|
||
*/
|
||
"auto-dismissed"
|
||
],
|
||
setup(props, { emit }) {
|
||
const dismissed = ref(false);
|
||
const userDismissable = computed(
|
||
() => props.inline === false && // DEPRECATED: require use of new prop allowUserDismiss (T368444).
|
||
(props.dismissButtonLabel.length > 0 || props.allowUserDismiss)
|
||
);
|
||
const translatedDismissButtonLabel = useI18nWithOverride(
|
||
toRef(props, "dismissButtonLabel"),
|
||
"cdx-message-dismiss-button-label",
|
||
"Close"
|
||
);
|
||
const displayTime = computed(() => {
|
||
if (props.autoDismiss === false || props.type === "error") {
|
||
return false;
|
||
} else if (props.autoDismiss === true) {
|
||
return 4e3;
|
||
}
|
||
return props.autoDismiss;
|
||
});
|
||
const rootClasses = computed(() => {
|
||
return {
|
||
"cdx-message--inline": props.inline,
|
||
"cdx-message--block": !props.inline,
|
||
"cdx-message--user-dismissable": userDismissable.value,
|
||
["cdx-message--".concat(props.type)]: true
|
||
};
|
||
});
|
||
const computedIcon = computed(
|
||
() => props.icon && props.type === "notice" ? props.icon : iconMap$2[props.type]
|
||
);
|
||
const leaveActiveClass = ref("");
|
||
function onDismiss(eventName) {
|
||
if (dismissed.value) {
|
||
return;
|
||
}
|
||
leaveActiveClass.value = eventName === "user-dismissed" ? "cdx-message-leave-active-user" : "cdx-message-leave-active-system";
|
||
dismissed.value = true;
|
||
emit(eventName);
|
||
}
|
||
onMounted(() => {
|
||
if (props.type === "error" && props.autoDismiss !== false) {
|
||
warn('CdxMessage: Message with type="error" cannot use auto-dismiss');
|
||
} else if (displayTime.value) {
|
||
setTimeout(() => onDismiss("auto-dismissed"), displayTime.value);
|
||
}
|
||
});
|
||
return {
|
||
dismissed,
|
||
userDismissable,
|
||
translatedDismissButtonLabel,
|
||
rootClasses,
|
||
leaveActiveClass,
|
||
computedIcon,
|
||
onDismiss,
|
||
cdxIconClose: X3
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$f = ["aria-live", "role"];
|
||
const _hoisted_2$9 = { class: "cdx-message__content" };
|
||
function _sfc_render$h(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_icon = resolveComponent("cdx-icon");
|
||
const _component_cdx_button = resolveComponent("cdx-button");
|
||
return openBlock(), createBlock(Transition, {
|
||
name: "cdx-message",
|
||
appear: _ctx.fadeIn,
|
||
"leave-active-class": _ctx.leaveActiveClass
|
||
}, {
|
||
default: withCtx(() => [
|
||
!_ctx.dismissed ? (openBlock(), createElementBlock("div", {
|
||
key: 0,
|
||
class: normalizeClass(["cdx-message", _ctx.rootClasses]),
|
||
"aria-live": _ctx.type !== "error" ? "polite" : void 0,
|
||
role: _ctx.type === "error" ? "alert" : void 0
|
||
}, [
|
||
createVNode(_component_cdx_icon, {
|
||
class: "cdx-message__icon--vue",
|
||
icon: _ctx.computedIcon
|
||
}, null, 8, ["icon"]),
|
||
createElementVNode("div", _hoisted_2$9, [
|
||
renderSlot(_ctx.$slots, "default")
|
||
]),
|
||
_ctx.userDismissable ? (openBlock(), createBlock(_component_cdx_button, {
|
||
key: 0,
|
||
class: "cdx-message__dismiss-button",
|
||
weight: "quiet",
|
||
type: "button",
|
||
"aria-label": _ctx.translatedDismissButtonLabel,
|
||
onClick: _cache[0] || (_cache[0] = ($event) => _ctx.onDismiss("user-dismissed"))
|
||
}, {
|
||
default: withCtx(() => [
|
||
createVNode(_component_cdx_icon, { icon: _ctx.cdxIconClose }, null, 8, ["icon"])
|
||
]),
|
||
_: 1
|
||
/* STABLE */
|
||
}, 8, ["aria-label"])) : createCommentVNode("v-if", true)
|
||
], 10, _hoisted_1$f)) : createCommentVNode("v-if", true)
|
||
]),
|
||
_: 3
|
||
/* FORWARDED */
|
||
}, 8, ["appear", "leave-active-class"]);
|
||
}
|
||
const CdxMessage = /* @__PURE__ */ _export_sfc(_sfc_main$h, [["render", _sfc_render$h]]);
|
||
const statusValidator$6 = makeStringTypeValidator(ValidationStatusTypes);
|
||
const _sfc_main$g = defineComponent({
|
||
name: "CdxField",
|
||
components: { CdxLabel, CdxMessage },
|
||
props: {
|
||
/**
|
||
* Icon before the label text.
|
||
*
|
||
* Do not use this if including a start icon within the input component.
|
||
*/
|
||
labelIcon: {
|
||
type: [String, Object],
|
||
default: ""
|
||
},
|
||
/**
|
||
* Whether the field is optional.
|
||
*
|
||
* This will add a flag next to the label indicating that the field is optional.
|
||
*/
|
||
optional: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
// DEPRECATED: set default to '(optional)' (T368444).
|
||
/**
|
||
* Text to indicate that the field is optional.
|
||
*
|
||
* Omit this prop to use the default value, "(optional)".
|
||
*/
|
||
optionalFlag: {
|
||
type: String,
|
||
default: ""
|
||
},
|
||
/**
|
||
* Whether the label should be visually hidden.
|
||
*
|
||
* Note that this will also hide the description.
|
||
*/
|
||
hideLabel: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether this field contains a group of inputs.
|
||
*
|
||
* When true, this outputs a `<fieldset>` element with a semantic `<legend>`. When false,
|
||
* it outputs a `<div>` with a semantic `<label>`.
|
||
*/
|
||
isFieldset: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether the entire field is disabled.
|
||
*/
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* `status` attribute of the input. This also determines which validation message is shown.
|
||
*/
|
||
status: {
|
||
type: String,
|
||
default: "default",
|
||
validator: statusValidator$6
|
||
},
|
||
/**
|
||
* Message text keyed on validation status type.
|
||
* @default {}
|
||
*/
|
||
messages: {
|
||
type: Object,
|
||
default: () => {
|
||
return {};
|
||
}
|
||
}
|
||
},
|
||
setup(props, { slots }) {
|
||
const { disabled, status, isFieldset } = toRefs(props);
|
||
const computedDisabled = useComputedDisabled(disabled);
|
||
const rootClasses = computed(() => {
|
||
return {
|
||
"cdx-field--disabled": computedDisabled.value,
|
||
"cdx-field--is-fieldset": isFieldset.value
|
||
};
|
||
});
|
||
const labelId = useGeneratedId("label");
|
||
const descriptionId = useGeneratedId("description");
|
||
const inputId = useGeneratedId("input");
|
||
const computedInputId = computed(() => !isFieldset.value ? inputId : void 0);
|
||
provide(FieldInputIdKey, computedInputId);
|
||
const computedDescriptionId = computed(
|
||
() => !isFieldset.value && slots.description ? descriptionId : void 0
|
||
);
|
||
provide(FieldDescriptionIdKey, computedDescriptionId);
|
||
provide(DisabledKey, computedDisabled);
|
||
provide(FieldStatusKey, status);
|
||
const validationMessage = computed(
|
||
() => props.status !== "default" && props.status in props.messages ? props.messages[props.status] : ""
|
||
);
|
||
const validationMessageType = computed(() => props.status === "default" ? "notice" : props.status);
|
||
return {
|
||
rootClasses,
|
||
computedDisabled,
|
||
labelId,
|
||
descriptionId,
|
||
inputId,
|
||
validationMessage,
|
||
validationMessageType
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$e = { class: "cdx-field__control" };
|
||
const _hoisted_2$8 = { class: "cdx-field__help-text" };
|
||
const _hoisted_3$5 = {
|
||
key: 0,
|
||
class: "cdx-field__validation-message"
|
||
};
|
||
function _sfc_render$g(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_label = resolveComponent("cdx-label");
|
||
const _component_cdx_message = resolveComponent("cdx-message");
|
||
return openBlock(), createBlock(resolveDynamicComponent(_ctx.isFieldset ? "fieldset" : "div"), {
|
||
class: normalizeClass(["cdx-field", _ctx.rootClasses]),
|
||
"aria-disabled": !_ctx.isFieldset && _ctx.computedDisabled ? true : void 0,
|
||
disabled: _ctx.isFieldset && _ctx.computedDisabled ? true : void 0
|
||
}, {
|
||
default: withCtx(() => [
|
||
createVNode(_component_cdx_label, {
|
||
id: _ctx.labelId,
|
||
icon: _ctx.labelIcon,
|
||
"visually-hidden": _ctx.hideLabel,
|
||
optional: _ctx.optional,
|
||
"optional-flag": _ctx.optionalFlag,
|
||
"input-id": _ctx.inputId,
|
||
"description-id": _ctx.descriptionId,
|
||
disabled: _ctx.computedDisabled,
|
||
"is-legend": _ctx.isFieldset
|
||
}, createSlots({
|
||
default: withCtx(() => [
|
||
renderSlot(_ctx.$slots, "label")
|
||
]),
|
||
_: 2
|
||
/* DYNAMIC */
|
||
}, [
|
||
_ctx.$slots.description && _ctx.$slots.description().length > 0 ? {
|
||
name: "description",
|
||
fn: withCtx(() => [
|
||
renderSlot(_ctx.$slots, "description")
|
||
]),
|
||
key: "0"
|
||
} : void 0
|
||
]), 1032, ["id", "icon", "visually-hidden", "optional", "optional-flag", "input-id", "description-id", "disabled", "is-legend"]),
|
||
createElementVNode("div", _hoisted_1$e, [
|
||
renderSlot(_ctx.$slots, "default")
|
||
]),
|
||
createElementVNode("div", _hoisted_2$8, [
|
||
renderSlot(_ctx.$slots, "help-text")
|
||
]),
|
||
!_ctx.computedDisabled && _ctx.validationMessage ? (openBlock(), createElementBlock("div", _hoisted_3$5, [
|
||
createVNode(_component_cdx_message, {
|
||
type: _ctx.validationMessageType,
|
||
inline: true
|
||
}, {
|
||
default: withCtx(() => [
|
||
createTextVNode(
|
||
toDisplayString(_ctx.validationMessage),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
]),
|
||
_: 1
|
||
/* STABLE */
|
||
}, 8, ["type"])
|
||
])) : createCommentVNode("v-if", true)
|
||
]),
|
||
_: 3
|
||
/* FORWARDED */
|
||
}, 8, ["class", "aria-disabled", "disabled"]);
|
||
}
|
||
const Field = /* @__PURE__ */ _export_sfc(_sfc_main$g, [["render", _sfc_render$g]]);
|
||
const iconMap$1 = {
|
||
error: i4,
|
||
warning: M3,
|
||
success: O7
|
||
};
|
||
const _sfc_main$f = defineComponent({
|
||
name: "CdxInfoChip",
|
||
components: { CdxIcon },
|
||
props: {
|
||
/**
|
||
* Status type.
|
||
*
|
||
* @values 'notice', 'warning', 'error', 'success'
|
||
*/
|
||
status: {
|
||
type: String,
|
||
default: "notice",
|
||
validator: statusTypeValidator
|
||
},
|
||
/**
|
||
* Custom icon to use for "notice" chips. Chips with other status types
|
||
* (warning, etc) do not allow custom icons and will ignore this option.
|
||
*/
|
||
icon: {
|
||
type: [String, Object],
|
||
default: null
|
||
}
|
||
},
|
||
setup(props) {
|
||
const iconClass = computed(() => {
|
||
return {
|
||
["cdx-info-chip__icon--".concat(props.status)]: true
|
||
};
|
||
});
|
||
const computedIcon = computed(
|
||
() => props.status === "notice" ? props.icon : iconMap$1[props.status]
|
||
);
|
||
return {
|
||
iconClass,
|
||
computedIcon
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$d = { class: "cdx-info-chip" };
|
||
const _hoisted_2$7 = { class: "cdx-info-chip--text" };
|
||
function _sfc_render$f(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_icon = resolveComponent("cdx-icon");
|
||
return openBlock(), createElementBlock("div", _hoisted_1$d, [
|
||
_ctx.computedIcon ? (openBlock(), createBlock(_component_cdx_icon, {
|
||
key: 0,
|
||
class: normalizeClass(["cdx-info-chip__icon", _ctx.iconClass]),
|
||
icon: _ctx.computedIcon
|
||
}, null, 8, ["class", "icon"])) : createCommentVNode("v-if", true),
|
||
createElementVNode("span", _hoisted_2$7, [
|
||
renderSlot(_ctx.$slots, "default")
|
||
])
|
||
]);
|
||
}
|
||
const InfoChip = /* @__PURE__ */ _export_sfc(_sfc_main$f, [["render", _sfc_render$f]]);
|
||
const statusValidator$5 = makeStringTypeValidator(ValidationStatusTypes);
|
||
const _sfc_main$e = defineComponent({
|
||
name: "CdxLookup",
|
||
components: {
|
||
CdxMenu,
|
||
CdxTextInput
|
||
},
|
||
/**
|
||
* We want the input to inherit attributes, not the root element.
|
||
*/
|
||
inheritAttrs: false,
|
||
props: {
|
||
/**
|
||
* Value of the current selection.
|
||
*
|
||
* Must be bound with `v-model:selected`.
|
||
*
|
||
* The property should be initialized to `null` rather than using a falsy value.
|
||
*/
|
||
selected: {
|
||
type: [String, Number, null],
|
||
required: true
|
||
},
|
||
/**
|
||
* Menu items.
|
||
*/
|
||
menuItems: {
|
||
type: Array,
|
||
required: true
|
||
},
|
||
/**
|
||
* Current value of the input. This prop is optional and should only be used if you need to
|
||
* keep track of the input value for some reason (e.g. to set an initial value).
|
||
*
|
||
* Optionally provided by `v-model:input-value` binding in the parent component.
|
||
*/
|
||
inputValue: {
|
||
type: [String, Number],
|
||
default: null
|
||
},
|
||
// DEPRECATED: Remove (T373532).
|
||
/**
|
||
* Initial value of the text input. Non-reactive.
|
||
*
|
||
* @deprecated Use `inputValue` instead.
|
||
*/
|
||
initialInputValue: {
|
||
type: [String, Number],
|
||
default: "",
|
||
validator: (value) => {
|
||
if (value) {
|
||
console.warn(
|
||
"CdxLookup: prop initialInputValue is deprecated. Use inputValue instead."
|
||
);
|
||
}
|
||
return true;
|
||
}
|
||
},
|
||
/**
|
||
* Whether the entire component is disabled.
|
||
*/
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Configuration for various menu features. All properties default to false.
|
||
*
|
||
* See the MenuConfig type.
|
||
* @default {}
|
||
*/
|
||
menuConfig: {
|
||
type: Object,
|
||
default: () => {
|
||
return {};
|
||
}
|
||
},
|
||
/**
|
||
* `status` property of the TextInput component
|
||
*/
|
||
status: {
|
||
type: String,
|
||
default: "default",
|
||
validator: statusValidator$5
|
||
}
|
||
},
|
||
emits: [
|
||
/**
|
||
* When the selected value changes.
|
||
*
|
||
* @property {string | number | null} selected The new selected value
|
||
*/
|
||
"update:selected",
|
||
/**
|
||
* When the input value changes. Only emitted if the inputValue prop is provided.
|
||
*
|
||
* @property {string | number} inputValue The new input value
|
||
*/
|
||
"update:input-value",
|
||
/**
|
||
* When the user scrolls towards the bottom of the menu.
|
||
*
|
||
* If it is possible to add or load more menu items, then now would be a good moment
|
||
* so that the user can experience infinite scrolling.
|
||
*/
|
||
"load-more",
|
||
/**
|
||
* When the text input value changes.
|
||
*
|
||
* @property {string | number} value The new value
|
||
*/
|
||
"input",
|
||
/**
|
||
* When an input value change is committed by the user (e.g. on blur)
|
||
*
|
||
* @property {Event} event
|
||
*/
|
||
"change",
|
||
/**
|
||
* When the input comes into focus
|
||
*
|
||
* @property {FocusEvent} event
|
||
*/
|
||
"focus",
|
||
/**
|
||
* When the input loses focus
|
||
*
|
||
* @property {FocusEvent} event
|
||
*/
|
||
"blur"
|
||
],
|
||
setup: (props, { emit, attrs, slots }) => {
|
||
const rootElement = ref();
|
||
const textInput = ref();
|
||
const menu = ref();
|
||
const menuId = useGeneratedId("lookup-menu");
|
||
const pending = ref(false);
|
||
const expanded = ref(false);
|
||
const isActive = ref(false);
|
||
const initialMenuItems = ref(props.menuItems);
|
||
const {
|
||
computedDisabled,
|
||
computedStatus
|
||
} = useFieldData(
|
||
toRef(props, "disabled"),
|
||
toRef(props, "status")
|
||
);
|
||
const selectedProp = toRef(props, "selected");
|
||
const modelWrapper = useModelWrapper(selectedProp, emit, "update:selected");
|
||
const selectedMenuItem = computed(
|
||
() => props.menuItems.find((item) => item.value === props.selected)
|
||
);
|
||
const highlightedId = computed(() => {
|
||
var _a, _b;
|
||
return (_b = (_a = menu.value) == null ? void 0 : _a.getHighlightedMenuItem()) == null ? void 0 : _b.id;
|
||
});
|
||
const internalInputValue = ref(props.initialInputValue);
|
||
const computedInputValue = useOptionalModelWrapper(
|
||
internalInputValue,
|
||
toRef(props, "inputValue"),
|
||
emit,
|
||
"update:input-value"
|
||
);
|
||
const internalClasses = computed(() => {
|
||
return {
|
||
"cdx-lookup--disabled": computedDisabled.value,
|
||
"cdx-lookup--pending": pending.value
|
||
};
|
||
});
|
||
const {
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrs
|
||
} = useSplitAttributes(attrs, internalClasses);
|
||
function onUpdateInput(newVal) {
|
||
if (selectedMenuItem.value) {
|
||
if (selectedMenuItem.value.label !== newVal && selectedMenuItem.value.value !== newVal) {
|
||
modelWrapper.value = null;
|
||
}
|
||
} else if (props.selected !== null && props.selected !== newVal) {
|
||
modelWrapper.value = null;
|
||
}
|
||
if (newVal === "" && initialMenuItems.value.length === 0) {
|
||
expanded.value = false;
|
||
pending.value = false;
|
||
} else {
|
||
pending.value = true;
|
||
}
|
||
emit("input", newVal);
|
||
}
|
||
function onInputFocus(event) {
|
||
isActive.value = true;
|
||
const hasInput = computedInputValue.value !== null && computedInputValue.value !== "";
|
||
const hasMenuItems = !!(props.menuItems.length > 0 || slots["no-results"]);
|
||
if (hasMenuItems && (hasInput || initialMenuItems.value.length > 0)) {
|
||
expanded.value = true;
|
||
}
|
||
emit("focus", event);
|
||
}
|
||
function onInputBlur(event) {
|
||
isActive.value = false;
|
||
expanded.value = false;
|
||
emit("blur", event);
|
||
}
|
||
function onKeydown(e) {
|
||
if (!menu.value || computedDisabled.value || props.menuItems.length === 0 && !slots["no-results"] || e.key === " ") {
|
||
return;
|
||
}
|
||
menu.value.delegateKeyNavigation(e);
|
||
}
|
||
useFloatingMenu(textInput, menu);
|
||
watch(selectedProp, (newVal) => {
|
||
var _a;
|
||
if (newVal !== null) {
|
||
const selectedValue = selectedMenuItem.value ? (_a = selectedMenuItem.value.label) != null ? _a : selectedMenuItem.value.value : "";
|
||
if (computedInputValue.value !== selectedValue) {
|
||
computedInputValue.value = selectedValue;
|
||
emit("input", computedInputValue.value);
|
||
}
|
||
}
|
||
});
|
||
watch(toRef(props, "menuItems"), (newVal) => {
|
||
if (
|
||
// Only show the menu if we were in the pending state (meaning this menuItems change
|
||
// was in response to user input) and the menu is still focused
|
||
isActive.value && pending.value && // Show the menu if there are either menu items or no-results content to show
|
||
(newVal.length > 0 || slots["no-results"])
|
||
) {
|
||
expanded.value = true;
|
||
}
|
||
if (newVal.length === 0 && !slots["no-results"]) {
|
||
expanded.value = false;
|
||
}
|
||
pending.value = false;
|
||
});
|
||
return {
|
||
rootElement,
|
||
textInput,
|
||
menu,
|
||
menuId,
|
||
highlightedId,
|
||
computedInputValue,
|
||
modelWrapper,
|
||
expanded,
|
||
computedDisabled,
|
||
computedStatus,
|
||
onInputBlur,
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrs,
|
||
onUpdateInput,
|
||
onInputFocus,
|
||
onKeydown
|
||
};
|
||
}
|
||
});
|
||
function _sfc_render$e(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_text_input = resolveComponent("cdx-text-input");
|
||
const _component_cdx_menu = resolveComponent("cdx-menu");
|
||
return openBlock(), createElementBlock(
|
||
"div",
|
||
{
|
||
ref: "rootElement",
|
||
class: normalizeClass(["cdx-lookup", _ctx.rootClasses]),
|
||
style: normalizeStyle(_ctx.rootStyle)
|
||
},
|
||
[
|
||
createVNode(_component_cdx_text_input, mergeProps({
|
||
ref: "textInput",
|
||
modelValue: _ctx.computedInputValue,
|
||
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => _ctx.computedInputValue = $event)
|
||
}, _ctx.otherAttrs, {
|
||
class: "cdx-lookup__input",
|
||
role: "combobox",
|
||
autocomplete: "off",
|
||
"aria-autocomplete": "list",
|
||
"aria-controls": _ctx.menuId,
|
||
"aria-expanded": _ctx.expanded,
|
||
"aria-activedescendant": _ctx.highlightedId,
|
||
disabled: _ctx.computedDisabled,
|
||
status: _ctx.computedStatus,
|
||
"onUpdate:modelValue": _ctx.onUpdateInput,
|
||
onChange: _cache[1] || (_cache[1] = ($event) => _ctx.$emit("change", $event)),
|
||
onFocus: _ctx.onInputFocus,
|
||
onBlur: _ctx.onInputBlur,
|
||
onKeydown: _ctx.onKeydown
|
||
}), null, 16, ["modelValue", "aria-controls", "aria-expanded", "aria-activedescendant", "disabled", "status", "onUpdate:modelValue", "onFocus", "onBlur", "onKeydown"]),
|
||
createVNode(_component_cdx_menu, mergeProps({
|
||
id: _ctx.menuId,
|
||
ref: "menu",
|
||
selected: _ctx.modelWrapper,
|
||
"onUpdate:selected": _cache[2] || (_cache[2] = ($event) => _ctx.modelWrapper = $event),
|
||
expanded: _ctx.expanded,
|
||
"onUpdate:expanded": _cache[3] || (_cache[3] = ($event) => _ctx.expanded = $event),
|
||
"menu-items": _ctx.menuItems
|
||
}, _ctx.menuConfig, {
|
||
onLoadMore: _cache[4] || (_cache[4] = ($event) => _ctx.$emit("load-more"))
|
||
}), {
|
||
default: withCtx(({ menuItem }) => [
|
||
renderSlot(_ctx.$slots, "menu-item", { menuItem })
|
||
]),
|
||
"no-results": withCtx(() => [
|
||
renderSlot(_ctx.$slots, "no-results")
|
||
]),
|
||
_: 3
|
||
/* FORWARDED */
|
||
}, 16, ["id", "selected", "expanded", "menu-items"])
|
||
],
|
||
6
|
||
/* CLASS, STYLE */
|
||
);
|
||
}
|
||
const Lookup = /* @__PURE__ */ _export_sfc(_sfc_main$e, [["render", _sfc_render$e]]);
|
||
const _sfc_main$d = defineComponent({
|
||
name: "CdxToggleButton",
|
||
props: {
|
||
/**
|
||
* Whether the button should be set to "on" (true) or "off" (false).
|
||
*
|
||
* Provided by `v-model` binding in the parent component.
|
||
*/
|
||
modelValue: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether the disabled attribute should be added to the button, which prevents
|
||
* it from being clicked.
|
||
*/
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether the toggle button should be "quiet", which renders more minimally.
|
||
*/
|
||
quiet: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
},
|
||
emits: [
|
||
/**
|
||
* Emitted when modelValue changes (i.e. when the state is toggled)
|
||
*
|
||
* @property {boolean} modelValue The new model value
|
||
*/
|
||
"update:modelValue"
|
||
],
|
||
setup(props, { emit, slots, attrs }) {
|
||
const isIconOnly = useIconOnlyButton(slots.default, attrs, "CdxToggleButton");
|
||
const isActive = ref(false);
|
||
const rootClasses = computed(() => ({
|
||
// Quiet means frameless among other things
|
||
"cdx-toggle-button--quiet": props.quiet,
|
||
"cdx-toggle-button--framed": !props.quiet,
|
||
// Provide --toggled-off too so that we can simplify selectors
|
||
"cdx-toggle-button--toggled-on": props.modelValue,
|
||
"cdx-toggle-button--toggled-off": !props.modelValue,
|
||
"cdx-toggle-button--icon-only": isIconOnly.value,
|
||
"cdx-toggle-button--is-active": isActive.value
|
||
}));
|
||
const onClick = () => {
|
||
emit("update:modelValue", !props.modelValue);
|
||
};
|
||
const setActive = (active) => {
|
||
isActive.value = active;
|
||
};
|
||
function onKeyDown() {
|
||
setActive(true);
|
||
}
|
||
function onKeyUp() {
|
||
setActive(false);
|
||
onClick();
|
||
}
|
||
return {
|
||
rootClasses,
|
||
onClick,
|
||
onKeyDown,
|
||
onKeyUp
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$c = ["aria-pressed", "disabled"];
|
||
function _sfc_render$d(_ctx, _cache, $props, $setup, $data, $options) {
|
||
return openBlock(), createElementBlock("button", {
|
||
class: normalizeClass(["cdx-toggle-button", _ctx.rootClasses]),
|
||
"aria-pressed": _ctx.modelValue,
|
||
disabled: _ctx.disabled,
|
||
onClick: _cache[0] || (_cache[0] = (...args) => _ctx.onClick && _ctx.onClick(...args)),
|
||
onKeydown: _cache[1] || (_cache[1] = withKeys(withModifiers((...args) => _ctx.onKeyDown && _ctx.onKeyDown(...args), ["prevent"]), ["space", "enter"])),
|
||
onKeyup: _cache[2] || (_cache[2] = withKeys((...args) => _ctx.onKeyUp && _ctx.onKeyUp(...args), ["space", "enter"]))
|
||
}, [
|
||
renderSlot(_ctx.$slots, "default")
|
||
], 42, _hoisted_1$c);
|
||
}
|
||
const CdxToggleButton = /* @__PURE__ */ _export_sfc(_sfc_main$d, [["render", _sfc_render$d]]);
|
||
const _sfc_main$c = defineComponent({
|
||
name: "CdxMenuButton",
|
||
components: {
|
||
CdxToggleButton,
|
||
CdxMenu
|
||
},
|
||
inheritAttrs: false,
|
||
props: {
|
||
/**
|
||
* Value of the current selection.
|
||
*
|
||
* Must be bound with `v-model:selected`.
|
||
*/
|
||
selected: {
|
||
type: [String, Number, null],
|
||
required: true
|
||
},
|
||
/**
|
||
* Menu items. See the MenuItemData type.
|
||
*/
|
||
menuItems: {
|
||
type: Array,
|
||
required: true
|
||
},
|
||
/**
|
||
* Configuration for various menu features. All properties default to false.
|
||
*
|
||
* See the MenuConfig type.
|
||
* @default {}
|
||
*/
|
||
menuConfig: {
|
||
type: Object,
|
||
default: () => {
|
||
return {};
|
||
}
|
||
},
|
||
/**
|
||
* Whether the dropdown is disabled.
|
||
*/
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
},
|
||
emits: [
|
||
/**
|
||
* When the selected value changes.
|
||
*
|
||
* @property {string | number} selected The new selected value
|
||
*/
|
||
"update:selected"
|
||
],
|
||
setup(props, { emit, attrs }) {
|
||
const menu = ref();
|
||
const toggle = ref();
|
||
const selectedProp = toRef(props, "selected");
|
||
const modelWrapper = useModelWrapper(selectedProp, emit, "update:selected");
|
||
const expanded = ref(false);
|
||
const toggleId = useGeneratedId("menuToggle");
|
||
const menuId = useGeneratedId("menu");
|
||
const { computedDisabled } = useFieldData(toRef(props, "disabled"));
|
||
const { rootClasses, rootStyle, otherAttrs } = useSplitAttributes(attrs);
|
||
function onKeydown(e) {
|
||
if (!menu.value || computedDisabled.value || props.menuItems.length === 0 || e.key === " ") {
|
||
return;
|
||
}
|
||
menu.value.delegateKeyNavigation(e);
|
||
}
|
||
useFloatingMenu(toggle, menu, {
|
||
useAvailableWidth: true,
|
||
placement: "bottom-start",
|
||
offset: 4
|
||
});
|
||
return {
|
||
computedDisabled,
|
||
expanded,
|
||
menu,
|
||
menuId,
|
||
modelWrapper,
|
||
onKeydown,
|
||
toggle,
|
||
toggleId,
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrs
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$b = { class: "cdx-menu-button__menu-wrapper" };
|
||
function _sfc_render$c(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_toggle_button = resolveComponent("cdx-toggle-button");
|
||
const _component_cdx_menu = resolveComponent("cdx-menu");
|
||
return openBlock(), createElementBlock(
|
||
"div",
|
||
{
|
||
class: normalizeClass(["cdx-menu-button", _ctx.rootClasses]),
|
||
style: normalizeStyle(_ctx.rootStyle)
|
||
},
|
||
[
|
||
createVNode(_component_cdx_toggle_button, mergeProps({
|
||
id: _ctx.toggleId,
|
||
ref: "toggle"
|
||
}, _ctx.otherAttrs, {
|
||
modelValue: _ctx.expanded,
|
||
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => _ctx.expanded = $event),
|
||
disabled: _ctx.computedDisabled,
|
||
quiet: "",
|
||
"aria-haspopup": "menu",
|
||
"aria-controls": _ctx.menuId,
|
||
"aria-expanded": _ctx.expanded,
|
||
onBlur: _cache[1] || (_cache[1] = ($event) => _ctx.expanded = false),
|
||
onKeydown: _ctx.onKeydown
|
||
}), {
|
||
default: withCtx(() => [
|
||
renderSlot(_ctx.$slots, "default")
|
||
]),
|
||
_: 3
|
||
/* FORWARDED */
|
||
}, 16, ["id", "modelValue", "disabled", "aria-controls", "aria-expanded", "onKeydown"]),
|
||
createElementVNode("div", _hoisted_1$b, [
|
||
createVNode(_component_cdx_menu, mergeProps({
|
||
id: _ctx.menuId,
|
||
ref: "menu",
|
||
selected: _ctx.modelWrapper,
|
||
"onUpdate:selected": _cache[2] || (_cache[2] = ($event) => _ctx.modelWrapper = $event),
|
||
expanded: _ctx.expanded,
|
||
"onUpdate:expanded": _cache[3] || (_cache[3] = ($event) => _ctx.expanded = $event),
|
||
"menu-items": _ctx.menuItems
|
||
}, _ctx.menuConfig, {
|
||
role: "menu",
|
||
"aria-labelledby": _ctx.toggleId
|
||
}), null, 16, ["id", "selected", "expanded", "menu-items", "aria-labelledby"])
|
||
])
|
||
],
|
||
6
|
||
/* CLASS, STYLE */
|
||
);
|
||
}
|
||
const MenuButton = /* @__PURE__ */ _export_sfc(_sfc_main$c, [["render", _sfc_render$c]]);
|
||
const statusValidator$4 = makeStringTypeValidator(ValidationStatusTypes);
|
||
const _sfc_main$b = defineComponent({
|
||
name: "CdxMultiselectLookup",
|
||
components: {
|
||
CdxChipInput,
|
||
CdxMenu
|
||
},
|
||
props: {
|
||
/**
|
||
* Current chips present in the input.
|
||
*
|
||
* Must be bound with `v-model:input-chips`. Initialize to an empty array if there are no
|
||
* initial selections. If there are, initialize to an array of input chips matching those
|
||
* selections.
|
||
*/
|
||
inputChips: {
|
||
type: Array,
|
||
required: true
|
||
},
|
||
/**
|
||
* Value(s) of the current selection(s).
|
||
*
|
||
* Must be bound with `v-model:selected`. Initialize to an empty array if there are no
|
||
* initial selections.
|
||
*/
|
||
selected: {
|
||
type: [Array],
|
||
required: true
|
||
},
|
||
/**
|
||
* Menu items.
|
||
*
|
||
* Initialize to an empty array if there are no initial menu items.
|
||
*/
|
||
menuItems: {
|
||
type: Array,
|
||
required: true
|
||
},
|
||
/**
|
||
* Current value of the text input. This prop is optional and should only be used if you
|
||
* need to keep track of the text input value for some reason (e.g. for validation).
|
||
*
|
||
* Optionally provided by `v-model:input-value` binding in the parent component.
|
||
*/
|
||
inputValue: {
|
||
type: [String, Number],
|
||
default: null
|
||
},
|
||
/**
|
||
* Whether the text input should appear below the set of input chips.
|
||
*
|
||
* By default, the input chips are inline with the input.
|
||
*/
|
||
separateInput: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether the entire component is disabled.
|
||
*/
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* `status` attribute of the input.
|
||
*/
|
||
status: {
|
||
type: String,
|
||
default: "default",
|
||
validator: statusValidator$4
|
||
},
|
||
/**
|
||
* Configuration for various menu features. All properties default to false.
|
||
*
|
||
* See the MenuConfig type.
|
||
* @default {}
|
||
*/
|
||
menuConfig: {
|
||
type: Object,
|
||
default: () => {
|
||
return {};
|
||
}
|
||
}
|
||
},
|
||
emits: [
|
||
/**
|
||
* When the input chips change.
|
||
*
|
||
* @property {ChipInputItem[]} inputChips The new set of inputChips
|
||
*/
|
||
"update:input-chips",
|
||
/**
|
||
* When the selected value changes.
|
||
*
|
||
* @property {MenuItemValue[]} selected The new set of selected values
|
||
*/
|
||
"update:selected",
|
||
/**
|
||
* When the input value changes. Only emitted if the inputValue prop is provided.
|
||
*
|
||
* This event is emitted both when the user changes the input and when the input is changed
|
||
* or cleared automatically (e.g. on selection).
|
||
*
|
||
* @property {string | number} inputValue The new input value
|
||
*/
|
||
"update:input-value",
|
||
/**
|
||
* When the user scrolls towards the bottom of the menu.
|
||
*
|
||
* If it is possible to add or load more menu items, then now would be a good moment
|
||
* so that the user can experience infinite scrolling.
|
||
*/
|
||
"load-more",
|
||
/**
|
||
* When the user changes the value of the input. Not emitted when the input is changed
|
||
* automatically (e.g. on selection).
|
||
*
|
||
* @property {string | number} value The new value
|
||
*/
|
||
"input",
|
||
/**
|
||
* When an input value change is committed by the user (e.g. on blur)
|
||
*
|
||
* @property {Event} event
|
||
*/
|
||
"change",
|
||
/**
|
||
* When the input comes into focus
|
||
*
|
||
* @property {FocusEvent} event
|
||
*/
|
||
"focus",
|
||
/**
|
||
* When the input loses focus
|
||
*
|
||
* @property {FocusEvent} event
|
||
*/
|
||
"blur"
|
||
],
|
||
setup: (props, { emit, attrs, slots }) => {
|
||
const chipInput = ref();
|
||
const menu = ref();
|
||
const menuId = useGeneratedId("multiselect-lookup-menu");
|
||
const highlightedId = computed(() => {
|
||
var _a, _b;
|
||
return (_b = (_a = menu.value) == null ? void 0 : _a.getHighlightedMenuItem()) == null ? void 0 : _b.id;
|
||
});
|
||
const pending = ref(false);
|
||
const expanded = ref(false);
|
||
const isActive = ref(false);
|
||
provide(AllowArbitraryKey, ref(false));
|
||
const {
|
||
computedDisabled,
|
||
computedStatus
|
||
} = useFieldData(
|
||
toRef(props, "disabled"),
|
||
toRef(props, "status")
|
||
);
|
||
const internalClasses = computed(() => {
|
||
return {
|
||
"cdx-multiselect-lookup--disabled": computedDisabled.value,
|
||
"cdx-multiselect-lookup--pending": pending.value
|
||
};
|
||
});
|
||
const {
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrs
|
||
} = useSplitAttributes(attrs, internalClasses);
|
||
useFloatingMenu(chipInput, menu);
|
||
const selectedWrapper = useModelWrapper(toRef(props, "selected"), emit, "update:selected");
|
||
const inputChipsWrapper = useModelWrapper(toRef(props, "inputChips"), emit, "update:input-chips");
|
||
const internalInputValue = ref("");
|
||
const computedInputValue = useOptionalModelWrapper(
|
||
internalInputValue,
|
||
toRef(props, "inputValue"),
|
||
emit,
|
||
"update:input-value"
|
||
);
|
||
const showNoResults = computed(() => computedInputValue.value.toString().length > 0 && slots["no-results"]);
|
||
function onUpdateInputValue(newVal) {
|
||
return __async(this, null, function* () {
|
||
yield nextTick();
|
||
pending.value = newVal !== null && newVal !== "";
|
||
emit("input", newVal);
|
||
});
|
||
}
|
||
function onInputFocus(event) {
|
||
isActive.value = true;
|
||
if (props.menuItems.length > 0 || showNoResults.value) {
|
||
expanded.value = true;
|
||
}
|
||
emit("focus", event);
|
||
}
|
||
function onInputBlur(event) {
|
||
isActive.value = false;
|
||
expanded.value = false;
|
||
emit("blur", event);
|
||
}
|
||
function onKeydown(e) {
|
||
if (!menu.value || computedDisabled.value || props.menuItems.length === 0 && !showNoResults.value || e.key === " ") {
|
||
return;
|
||
}
|
||
menu.value.delegateKeyNavigation(e);
|
||
}
|
||
watch(toRef(props, "selected"), (newVal) => {
|
||
const newSelections = newVal.filter((selection) => inputChipsWrapper.value.find((chip) => selection === chip.value) === void 0);
|
||
if (newSelections.length > 0) {
|
||
newSelections.forEach((newSelection) => {
|
||
const newMenuItem = props.menuItems.find((menuItem) => menuItem.value === newSelection);
|
||
if (newMenuItem) {
|
||
inputChipsWrapper.value.push(newMenuItem);
|
||
}
|
||
});
|
||
computedInputValue.value = "";
|
||
emit("input", "");
|
||
}
|
||
inputChipsWrapper.value = inputChipsWrapper.value.filter((chip) => {
|
||
return newVal.find((selection) => chip.value === selection) !== void 0;
|
||
});
|
||
});
|
||
watch(toRef(props, "inputChips"), (newVal) => {
|
||
if (newVal.length < selectedWrapper.value.length) {
|
||
selectedWrapper.value = newVal.map((chip) => chip.value);
|
||
}
|
||
});
|
||
watch(toRef(props, "menuItems"), (newVal) => {
|
||
if (newVal.length === 0 && !showNoResults.value) {
|
||
expanded.value = false;
|
||
} else if (isActive.value && pending.value) {
|
||
expanded.value = true;
|
||
}
|
||
pending.value = false;
|
||
});
|
||
return {
|
||
chipInput,
|
||
menu,
|
||
menuId,
|
||
highlightedId,
|
||
expanded,
|
||
computedDisabled,
|
||
computedStatus,
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrs,
|
||
selectedWrapper,
|
||
inputChipsWrapper,
|
||
computedInputValue,
|
||
onUpdateInputValue,
|
||
onInputBlur,
|
||
onInputFocus,
|
||
onKeydown
|
||
};
|
||
}
|
||
});
|
||
function _sfc_render$b(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_chip_input = resolveComponent("cdx-chip-input");
|
||
const _component_cdx_menu = resolveComponent("cdx-menu");
|
||
return openBlock(), createElementBlock(
|
||
"div",
|
||
{
|
||
class: normalizeClass(["cdx-multiselect-lookup", _ctx.rootClasses]),
|
||
style: normalizeStyle(_ctx.rootStyle)
|
||
},
|
||
[
|
||
createVNode(_component_cdx_chip_input, mergeProps({
|
||
ref: "chipInput",
|
||
"input-chips": _ctx.inputChipsWrapper,
|
||
"onUpdate:inputChips": _cache[0] || (_cache[0] = ($event) => _ctx.inputChipsWrapper = $event),
|
||
"input-value": _ctx.computedInputValue,
|
||
"onUpdate:inputValue": _cache[1] || (_cache[1] = ($event) => _ctx.computedInputValue = $event)
|
||
}, _ctx.otherAttrs, {
|
||
class: "cdx-multiselect-lookup__chip-input",
|
||
role: "combobox",
|
||
autocomplete: "off",
|
||
"aria-autocomplete": "list",
|
||
"aria-controls": _ctx.menuId,
|
||
"aria-expanded": _ctx.expanded,
|
||
"aria-activedescendant": _ctx.highlightedId,
|
||
"separate-input": _ctx.separateInput,
|
||
disabled: _ctx.computedDisabled,
|
||
status: _ctx.computedStatus,
|
||
"disallow-arbitrary": true,
|
||
"onUpdate:inputValue": _ctx.onUpdateInputValue,
|
||
onFocus: _ctx.onInputFocus,
|
||
onBlur: _ctx.onInputBlur,
|
||
onKeydown: _ctx.onKeydown
|
||
}), null, 16, ["input-chips", "input-value", "aria-controls", "aria-expanded", "aria-activedescendant", "separate-input", "disabled", "status", "onUpdate:inputValue", "onFocus", "onBlur", "onKeydown"]),
|
||
createVNode(_component_cdx_menu, mergeProps({
|
||
id: _ctx.menuId,
|
||
ref: "menu",
|
||
selected: _ctx.selectedWrapper,
|
||
"onUpdate:selected": _cache[2] || (_cache[2] = ($event) => _ctx.selectedWrapper = $event),
|
||
expanded: _ctx.expanded,
|
||
"onUpdate:expanded": _cache[3] || (_cache[3] = ($event) => _ctx.expanded = $event),
|
||
"menu-items": _ctx.menuItems
|
||
}, _ctx.menuConfig, {
|
||
onLoadMore: _cache[4] || (_cache[4] = ($event) => _ctx.$emit("load-more"))
|
||
}), {
|
||
default: withCtx(({ menuItem }) => [
|
||
renderSlot(_ctx.$slots, "menu-item", { menuItem })
|
||
]),
|
||
"no-results": withCtx(() => [
|
||
renderSlot(_ctx.$slots, "no-results")
|
||
]),
|
||
_: 3
|
||
/* FORWARDED */
|
||
}, 16, ["id", "selected", "expanded", "menu-items"])
|
||
],
|
||
6
|
||
/* CLASS, STYLE */
|
||
);
|
||
}
|
||
const MultiselectLookup = /* @__PURE__ */ _export_sfc(_sfc_main$b, [["render", _sfc_render$b]]);
|
||
const statusValidator$3 = makeStringTypeValidator(ValidationStatusTypes);
|
||
const _sfc_main$a = defineComponent({
|
||
name: "CdxRadio",
|
||
components: { CdxLabel },
|
||
props: {
|
||
/**
|
||
* Value of the currently selected radio.
|
||
*
|
||
* Provided by `v-model` binding in the parent component.
|
||
*/
|
||
modelValue: {
|
||
type: [String, Number, Boolean],
|
||
default: ""
|
||
},
|
||
/**
|
||
* HTML "value" attribute to assign to the input.
|
||
*
|
||
* Required for input groups.
|
||
*/
|
||
inputValue: {
|
||
type: [String, Number, Boolean],
|
||
default: false
|
||
},
|
||
/**
|
||
* HTML "name" attribute to assign to the input.
|
||
*/
|
||
name: {
|
||
type: String,
|
||
required: true
|
||
},
|
||
/**
|
||
* Whether the disabled attribute should be added to the input.
|
||
*/
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether the component should display inline.
|
||
*
|
||
* By default, `display: block` is set and a margin exists between
|
||
* sibling components, for a stacked layout.
|
||
*/
|
||
inline: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Validation status of the Radio.
|
||
*/
|
||
status: {
|
||
type: String,
|
||
default: "default",
|
||
validator: statusValidator$3
|
||
}
|
||
},
|
||
emits: [
|
||
/**
|
||
* Emitted when modelValue changes.
|
||
*
|
||
* @property {string | number | boolean} modelValue The new model value
|
||
*/
|
||
"update:modelValue"
|
||
],
|
||
setup(props, { emit, slots, attrs }) {
|
||
var _a;
|
||
useLabelChecker((_a = slots.default) == null ? void 0 : _a.call(slots), attrs, "CdxRadio");
|
||
const {
|
||
computedDisabled,
|
||
computedStatus
|
||
} = useFieldData(
|
||
toRef(props, "disabled"),
|
||
toRef(props, "status")
|
||
);
|
||
const rootClasses = computed(() => {
|
||
return {
|
||
"cdx-radio--inline": props.inline,
|
||
["cdx-radio--status-".concat(computedStatus.value)]: true
|
||
};
|
||
});
|
||
const customInputClasses = computed(() => {
|
||
return {
|
||
"cdx-radio__custom-input--inline": props.inline
|
||
};
|
||
});
|
||
const input = ref();
|
||
const radioId = useGeneratedId("radio");
|
||
const descriptionId = useGeneratedId("description");
|
||
const focusInput = () => {
|
||
input.value.focus();
|
||
};
|
||
const wrappedModel = useModelWrapper(toRef(props, "modelValue"), emit);
|
||
return {
|
||
rootClasses,
|
||
computedDisabled,
|
||
input,
|
||
radioId,
|
||
descriptionId,
|
||
focusInput,
|
||
wrappedModel,
|
||
customInputClasses
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$a = { class: "cdx-radio__wrapper" };
|
||
const _hoisted_2$6 = ["id", "aria-describedby", "name", "value", "disabled"];
|
||
const _hoisted_3$4 = /* @__PURE__ */ createElementVNode(
|
||
"span",
|
||
{ class: "cdx-radio__icon" },
|
||
null,
|
||
-1
|
||
/* HOISTED */
|
||
);
|
||
function _sfc_render$a(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_label = resolveComponent("cdx-label");
|
||
return openBlock(), createElementBlock(
|
||
"div",
|
||
{
|
||
class: normalizeClass(["cdx-radio", _ctx.rootClasses])
|
||
},
|
||
[
|
||
createElementVNode("div", _hoisted_1$a, [
|
||
withDirectives(createElementVNode("input", {
|
||
id: _ctx.radioId,
|
||
ref: "input",
|
||
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => _ctx.wrappedModel = $event),
|
||
class: "cdx-radio__input",
|
||
type: "radio",
|
||
"aria-describedby": _ctx.$slots.description && _ctx.$slots.description().length > 0 ? _ctx.descriptionId : void 0,
|
||
name: _ctx.name,
|
||
value: _ctx.inputValue,
|
||
disabled: _ctx.computedDisabled
|
||
}, null, 8, _hoisted_2$6), [
|
||
[vModelRadio, _ctx.wrappedModel]
|
||
]),
|
||
_hoisted_3$4,
|
||
_ctx.$slots.default && _ctx.$slots.default().length ? (openBlock(), createBlock(_component_cdx_label, {
|
||
key: 0,
|
||
class: "cdx-radio__label",
|
||
"input-id": _ctx.radioId,
|
||
"description-id": _ctx.$slots.description && _ctx.$slots.description().length > 0 ? _ctx.descriptionId : void 0,
|
||
disabled: _ctx.computedDisabled,
|
||
onClick: _ctx.focusInput
|
||
}, createSlots({
|
||
default: withCtx(() => [
|
||
renderSlot(_ctx.$slots, "default")
|
||
]),
|
||
_: 2
|
||
/* DYNAMIC */
|
||
}, [
|
||
_ctx.$slots.description && _ctx.$slots.description().length > 0 ? {
|
||
name: "description",
|
||
fn: withCtx(() => [
|
||
renderSlot(_ctx.$slots, "description")
|
||
]),
|
||
key: "0"
|
||
} : void 0
|
||
]), 1032, ["input-id", "description-id", "disabled", "onClick"])) : createCommentVNode("v-if", true)
|
||
]),
|
||
_ctx.$slots["custom-input"] ? (openBlock(), createElementBlock(
|
||
"div",
|
||
{
|
||
key: 0,
|
||
class: normalizeClass(["cdx-radio__custom-input", _ctx.customInputClasses])
|
||
},
|
||
[
|
||
renderSlot(_ctx.$slots, "custom-input")
|
||
],
|
||
2
|
||
/* CLASS */
|
||
)) : createCommentVNode("v-if", true)
|
||
],
|
||
2
|
||
/* CLASS */
|
||
);
|
||
}
|
||
const Radio = /* @__PURE__ */ _export_sfc(_sfc_main$a, [["render", _sfc_render$a]]);
|
||
const statusValidator$2 = makeStringTypeValidator(ValidationStatusTypes);
|
||
const _sfc_main$9 = defineComponent({
|
||
name: "CdxSearchInput",
|
||
components: {
|
||
CdxButton,
|
||
CdxTextInput
|
||
},
|
||
/**
|
||
* Attributes, besides class, will be passed to the TextInput's input element.
|
||
*/
|
||
inheritAttrs: false,
|
||
props: {
|
||
/**
|
||
* Value of the search input, provided by `v-model` binding in the parent component.
|
||
*/
|
||
modelValue: {
|
||
type: [String, Number],
|
||
default: ""
|
||
},
|
||
/**
|
||
*
|
||
* Whether to display the search button.
|
||
*/
|
||
useButton: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
// DEPRECATED: set default to 'Search' (T368444).
|
||
/**
|
||
* Search button text.
|
||
*
|
||
* Omit this prop to use the default value, "Search".
|
||
*/
|
||
buttonLabel: {
|
||
type: String,
|
||
default: ""
|
||
},
|
||
/**
|
||
* Whether the search input is disabled.
|
||
*/
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* `status` property of the TextInput component
|
||
*/
|
||
status: {
|
||
type: String,
|
||
default: "default",
|
||
validator: statusValidator$2
|
||
}
|
||
},
|
||
emits: [
|
||
/**
|
||
* When the input value changes
|
||
*
|
||
* @property {string | number} value The new value
|
||
*/
|
||
"update:modelValue",
|
||
/**
|
||
* When the submit button is clicked.
|
||
*
|
||
* @property {string | number} value The current input
|
||
*/
|
||
"submit-click",
|
||
/**
|
||
* When the input value changes via direct use of the input
|
||
*
|
||
* @property {InputEvent} event
|
||
*/
|
||
"input",
|
||
/**
|
||
* When an input value change is committed by the user (e.g. on blur)
|
||
*
|
||
* @property {Event} event
|
||
*/
|
||
"change",
|
||
/**
|
||
* When the input comes into focus
|
||
*
|
||
* @property {FocusEvent} event
|
||
*/
|
||
"focus",
|
||
/**
|
||
* When the input loses focus
|
||
*
|
||
* @property {FocusEvent} event
|
||
*/
|
||
"blur"
|
||
],
|
||
setup(props, { emit, attrs }) {
|
||
const wrappedModel = useModelWrapper(toRef(props, "modelValue"), emit);
|
||
const { computedDisabled } = useFieldData(toRef(props, "disabled"));
|
||
const internalClasses = computed(() => {
|
||
return {
|
||
"cdx-search-input--has-end-button": !!props.buttonLabel || props.useButton
|
||
};
|
||
});
|
||
const {
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrs
|
||
} = useSplitAttributes(attrs, internalClasses);
|
||
const translatedSearchButtonLabel = useI18nWithOverride(
|
||
toRef(props, "buttonLabel"),
|
||
"cdx-search-input-search-button-label",
|
||
// Allow custom button label via props or fallback to a default English string.
|
||
"Search"
|
||
);
|
||
const useButtonOrLabel = computed(
|
||
() => props.useButton || props.buttonLabel.length > 0
|
||
);
|
||
const handleSubmit = () => {
|
||
emit("submit-click", wrappedModel.value);
|
||
};
|
||
return {
|
||
wrappedModel,
|
||
computedDisabled,
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrs,
|
||
handleSubmit,
|
||
searchIcon: A7,
|
||
translatedSearchButtonLabel,
|
||
useButtonOrLabel
|
||
};
|
||
},
|
||
methods: {
|
||
/**
|
||
* Focus the component's input element.
|
||
*
|
||
* @public
|
||
*/
|
||
focus() {
|
||
const textInput = this.$refs.textInput;
|
||
textInput.focus();
|
||
}
|
||
}
|
||
});
|
||
const _hoisted_1$9 = { class: "cdx-search-input__input-wrapper" };
|
||
function _sfc_render$9(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_text_input = resolveComponent("cdx-text-input");
|
||
const _component_cdx_button = resolveComponent("cdx-button");
|
||
return openBlock(), createElementBlock(
|
||
"div",
|
||
{
|
||
class: normalizeClass(["cdx-search-input", _ctx.rootClasses]),
|
||
style: normalizeStyle(_ctx.rootStyle)
|
||
},
|
||
[
|
||
createElementVNode("div", _hoisted_1$9, [
|
||
createVNode(_component_cdx_text_input, mergeProps({
|
||
ref: "textInput",
|
||
modelValue: _ctx.wrappedModel,
|
||
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => _ctx.wrappedModel = $event),
|
||
class: "cdx-search-input__text-input",
|
||
"input-type": "search",
|
||
"start-icon": _ctx.searchIcon,
|
||
disabled: _ctx.computedDisabled,
|
||
status: _ctx.status
|
||
}, _ctx.otherAttrs, {
|
||
onKeydown: withKeys(_ctx.handleSubmit, ["enter"]),
|
||
onInput: _cache[1] || (_cache[1] = ($event) => _ctx.$emit("input", $event)),
|
||
onChange: _cache[2] || (_cache[2] = ($event) => _ctx.$emit("change", $event)),
|
||
onFocus: _cache[3] || (_cache[3] = ($event) => _ctx.$emit("focus", $event)),
|
||
onBlur: _cache[4] || (_cache[4] = ($event) => _ctx.$emit("blur", $event))
|
||
}), null, 16, ["modelValue", "start-icon", "disabled", "status", "onKeydown"]),
|
||
renderSlot(_ctx.$slots, "default")
|
||
]),
|
||
_ctx.useButtonOrLabel ? (openBlock(), createBlock(_component_cdx_button, {
|
||
key: 0,
|
||
class: "cdx-search-input__end-button",
|
||
disabled: _ctx.computedDisabled,
|
||
onClick: _ctx.handleSubmit
|
||
}, {
|
||
default: withCtx(() => [
|
||
createTextVNode(
|
||
toDisplayString(_ctx.translatedSearchButtonLabel),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
]),
|
||
_: 1
|
||
/* STABLE */
|
||
}, 8, ["disabled", "onClick"])) : createCommentVNode("v-if", true)
|
||
],
|
||
6
|
||
/* CLASS, STYLE */
|
||
);
|
||
}
|
||
const CdxSearchInput = /* @__PURE__ */ _export_sfc(_sfc_main$9, [["render", _sfc_render$9]]);
|
||
const statusValidator$1 = makeStringTypeValidator(ValidationStatusTypes);
|
||
const _sfc_main$8 = defineComponent({
|
||
name: "CdxSelect",
|
||
components: {
|
||
CdxIcon,
|
||
CdxMenu
|
||
},
|
||
/**
|
||
* We want the select handle to inherit attributes, not the root element.
|
||
*/
|
||
inheritAttrs: false,
|
||
props: {
|
||
/**
|
||
* Menu items. See the MenuItemData type.
|
||
*/
|
||
menuItems: {
|
||
type: Array,
|
||
required: true
|
||
},
|
||
/**
|
||
* Value of the current selection.
|
||
*
|
||
* Must be bound with `v-model:selected`.
|
||
*
|
||
* The property should be initialized to `null` rather than using a falsy value.
|
||
*/
|
||
selected: {
|
||
type: [String, Number, null],
|
||
required: true
|
||
},
|
||
/**
|
||
* Label to display when no selection has been made.
|
||
*/
|
||
defaultLabel: {
|
||
type: String,
|
||
default: ""
|
||
},
|
||
/**
|
||
* Whether the dropdown is disabled.
|
||
*/
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Configuration for various menu features. All properties default to false.
|
||
*
|
||
* See the MenuConfig type.
|
||
* @default {}
|
||
*/
|
||
menuConfig: {
|
||
type: Object,
|
||
default: () => {
|
||
return {};
|
||
}
|
||
},
|
||
/**
|
||
* An icon at the start of the select element
|
||
* displayed when no selection has been made.
|
||
*/
|
||
defaultIcon: {
|
||
type: [String, Object],
|
||
default: void 0
|
||
},
|
||
/**
|
||
* `status` attribute of the input.
|
||
*/
|
||
status: {
|
||
type: String,
|
||
default: "default",
|
||
validator: statusValidator$1
|
||
}
|
||
},
|
||
emits: [
|
||
/**
|
||
* When the selection value changes.
|
||
*
|
||
* @property {string | number | null} modelValue The new model value
|
||
*/
|
||
"update:selected",
|
||
/**
|
||
* When the user scrolls towards the bottom of the menu.
|
||
*
|
||
* If it is possible to add or load more menu items, then now would be a good moment
|
||
* so that the user can experience infinite scrolling.
|
||
*/
|
||
"load-more"
|
||
],
|
||
setup(props, { emit, attrs }) {
|
||
const handle = ref();
|
||
const menu = ref();
|
||
const descriptionId = inject(FieldDescriptionIdKey, void 0);
|
||
const menuId = useGeneratedId("select-menu");
|
||
const expanded = ref(false);
|
||
const handleId = attrs.id || useGeneratedId("select-handle");
|
||
const {
|
||
computedDisabled,
|
||
computedStatus,
|
||
computedInputId: computedHandleId
|
||
} = useFieldData(
|
||
toRef(props, "disabled"),
|
||
toRef(props, "status"),
|
||
handleId
|
||
);
|
||
const modelWrapper = useModelWrapper(toRef(props, "selected"), emit, "update:selected");
|
||
const selectedMenuItem = computed(
|
||
() => props.menuItems.find((menuItem) => menuItem.value === props.selected)
|
||
);
|
||
const currentLabel = computed(() => {
|
||
var _a;
|
||
return selectedMenuItem.value ? (_a = selectedMenuItem.value.label) != null ? _a : selectedMenuItem.value.value : props.defaultLabel;
|
||
});
|
||
const startIcon = computed(() => {
|
||
var _a;
|
||
if (props.defaultIcon && !selectedMenuItem.value) {
|
||
return props.defaultIcon;
|
||
} else if ((_a = selectedMenuItem.value) == null ? void 0 : _a.icon) {
|
||
return selectedMenuItem.value.icon;
|
||
}
|
||
return void 0;
|
||
});
|
||
const internalClasses = computed(() => {
|
||
return {
|
||
"cdx-select-vue--enabled": !computedDisabled.value,
|
||
"cdx-select-vue--disabled": computedDisabled.value,
|
||
"cdx-select-vue--expanded": expanded.value,
|
||
"cdx-select-vue--value-selected": !!selectedMenuItem.value,
|
||
"cdx-select-vue--no-selections": !selectedMenuItem.value,
|
||
"cdx-select-vue--has-start-icon": !!startIcon.value,
|
||
["cdx-select-vue--status-".concat(computedStatus.value)]: true
|
||
};
|
||
});
|
||
const {
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrs
|
||
} = useSplitAttributes(attrs, internalClasses);
|
||
const otherAttrsMinusId = computed(() => {
|
||
const _a = otherAttrs.value, { id } = _a, everythingElse = __objRest(_a, ["id"]);
|
||
return everythingElse;
|
||
});
|
||
const highlightedId = computed(() => {
|
||
var _a, _b;
|
||
return (_b = (_a = menu.value) == null ? void 0 : _a.getHighlightedMenuItem()) == null ? void 0 : _b.id;
|
||
});
|
||
function onBlur() {
|
||
expanded.value = false;
|
||
}
|
||
function onClick() {
|
||
var _a;
|
||
if (computedDisabled.value) {
|
||
return;
|
||
}
|
||
expanded.value = !expanded.value;
|
||
(_a = handle.value) == null ? void 0 : _a.focus();
|
||
}
|
||
function onKeydown(e) {
|
||
var _a;
|
||
if (computedDisabled.value) {
|
||
return;
|
||
}
|
||
(_a = menu.value) == null ? void 0 : _a.delegateKeyNavigation(e, { characterNavigation: true });
|
||
}
|
||
useFloatingMenu(handle, menu);
|
||
return {
|
||
handle,
|
||
menu,
|
||
computedHandleId,
|
||
descriptionId,
|
||
menuId,
|
||
modelWrapper,
|
||
selectedMenuItem,
|
||
highlightedId,
|
||
expanded,
|
||
computedDisabled,
|
||
onBlur,
|
||
currentLabel,
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrsMinusId,
|
||
onClick,
|
||
onKeydown,
|
||
startIcon,
|
||
cdxIconExpand: m4
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$8 = ["aria-disabled"];
|
||
const _hoisted_2$5 = ["id", "aria-controls", "aria-activedescendant", "aria-expanded", "aria-describedby"];
|
||
function _sfc_render$8(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_icon = resolveComponent("cdx-icon");
|
||
const _component_cdx_menu = resolveComponent("cdx-menu");
|
||
return openBlock(), createElementBlock("div", {
|
||
class: normalizeClass(["cdx-select-vue", _ctx.rootClasses]),
|
||
style: normalizeStyle(_ctx.rootStyle),
|
||
"aria-disabled": _ctx.computedDisabled
|
||
}, [
|
||
createElementVNode("div", mergeProps({
|
||
id: _ctx.computedHandleId,
|
||
ref: "handle",
|
||
class: "cdx-select-vue__handle"
|
||
}, _ctx.otherAttrsMinusId, {
|
||
tabindex: "0",
|
||
role: "combobox",
|
||
"aria-controls": _ctx.menuId,
|
||
"aria-activedescendant": _ctx.highlightedId,
|
||
"aria-expanded": _ctx.expanded,
|
||
"aria-describedby": _ctx.descriptionId,
|
||
onClick: _cache[0] || (_cache[0] = (...args) => _ctx.onClick && _ctx.onClick(...args)),
|
||
onBlur: _cache[1] || (_cache[1] = (...args) => _ctx.onBlur && _ctx.onBlur(...args)),
|
||
onKeydown: _cache[2] || (_cache[2] = (...args) => _ctx.onKeydown && _ctx.onKeydown(...args))
|
||
}), [
|
||
renderSlot(_ctx.$slots, "label", {
|
||
selectedMenuItem: _ctx.selectedMenuItem,
|
||
defaultLabel: _ctx.defaultLabel
|
||
}, () => [
|
||
createTextVNode(
|
||
toDisplayString(_ctx.currentLabel),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
]),
|
||
_ctx.startIcon ? (openBlock(), createBlock(_component_cdx_icon, {
|
||
key: 0,
|
||
icon: _ctx.startIcon,
|
||
class: "cdx-select-vue__start-icon"
|
||
}, null, 8, ["icon"])) : createCommentVNode("v-if", true),
|
||
createVNode(_component_cdx_icon, {
|
||
icon: _ctx.cdxIconExpand,
|
||
class: "cdx-select-vue__indicator"
|
||
}, null, 8, ["icon"])
|
||
], 16, _hoisted_2$5),
|
||
createVNode(_component_cdx_menu, mergeProps({
|
||
id: _ctx.menuId,
|
||
ref: "menu",
|
||
selected: _ctx.modelWrapper,
|
||
"onUpdate:selected": _cache[3] || (_cache[3] = ($event) => _ctx.modelWrapper = $event),
|
||
expanded: _ctx.expanded,
|
||
"onUpdate:expanded": _cache[4] || (_cache[4] = ($event) => _ctx.expanded = $event),
|
||
"menu-items": _ctx.menuItems
|
||
}, _ctx.menuConfig, {
|
||
onLoadMore: _cache[5] || (_cache[5] = ($event) => _ctx.$emit("load-more"))
|
||
}), {
|
||
default: withCtx(({ menuItem }) => [
|
||
renderSlot(_ctx.$slots, "menu-item", { menuItem })
|
||
]),
|
||
_: 3
|
||
/* FORWARDED */
|
||
}, 16, ["id", "selected", "expanded", "menu-items"])
|
||
], 14, _hoisted_1$8);
|
||
}
|
||
const CdxSelect = /* @__PURE__ */ _export_sfc(_sfc_main$8, [["render", _sfc_render$8]]);
|
||
const _sfc_main$7 = defineComponent({
|
||
name: "CdxTablePager",
|
||
components: { CdxButton, CdxIcon, CdxSelect },
|
||
props: {
|
||
paginationSizeOptions: {
|
||
type: Array,
|
||
required: true
|
||
},
|
||
itemsPerPage: {
|
||
type: Number,
|
||
required: true
|
||
},
|
||
nextDisabled: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
prevDisabled: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
lastDisabled: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
},
|
||
emits: [
|
||
/**
|
||
* When the items per page option changes
|
||
*
|
||
* @property {number} itemsPerPage the number of items to display
|
||
*/
|
||
"update:itemsPerPage",
|
||
/**
|
||
* Emitted when the user requests the first page of data
|
||
*/
|
||
"first",
|
||
/**
|
||
* Emitted when the user requests the last page of data
|
||
*/
|
||
"last",
|
||
/**
|
||
* Emitted when the user requests the next page of data
|
||
*/
|
||
"next",
|
||
/**
|
||
* Emitted when the user requests the previous page of data
|
||
*/
|
||
"prev"
|
||
],
|
||
setup(props, { emit }) {
|
||
const wrappedItemsPerPage = useModelWrapper(
|
||
toRef(props, "itemsPerPage"),
|
||
emit,
|
||
"update:itemsPerPage"
|
||
);
|
||
const defaultItemsPerPageLabel = useI18n(
|
||
"cdx-table-pager-items-per-page-default",
|
||
"Results per page"
|
||
);
|
||
const currentItemsPerPageLabel = useI18n(
|
||
"cdx-table-pager-items-per-page-current",
|
||
(current) => "".concat(current, " rows"),
|
||
[wrappedItemsPerPage]
|
||
);
|
||
const btnLabelFirst = useI18n(
|
||
"cdx-table-pager-button-first-page",
|
||
"First page"
|
||
);
|
||
const btnLabelNext = useI18n(
|
||
"cdx-table-pager-button-next-page",
|
||
"Next page"
|
||
);
|
||
const btnLabelPrev = useI18n(
|
||
"cdx-table-pager-button-prev-page",
|
||
"Previous page"
|
||
);
|
||
const btnLabelLast = useI18n(
|
||
"cdx-table-pager-button-last-page",
|
||
"Last page"
|
||
);
|
||
return {
|
||
defaultItemsPerPageLabel,
|
||
currentItemsPerPageLabel,
|
||
btnLabelFirst,
|
||
btnLabelNext,
|
||
btnLabelPrev,
|
||
btnLabelLast,
|
||
wrappedItemsPerPage,
|
||
cdxIconPrevious: d7,
|
||
cdxIconNext: j6,
|
||
cdxIconMoveFirst: P6,
|
||
cdxIconMoveLast: N6
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$7 = { class: "cdx-table-pager" };
|
||
const _hoisted_2$4 = { class: "cdx-table-pager__start" };
|
||
const _hoisted_3$3 = { key: 0 };
|
||
const _hoisted_4$3 = { key: 1 };
|
||
const _hoisted_5$3 = { class: "cdx-table-pager__center" };
|
||
const _hoisted_6$3 = { class: "cdx-table-pager__end" };
|
||
function _sfc_render$7(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_select = resolveComponent("cdx-select");
|
||
const _component_cdx_icon = resolveComponent("cdx-icon");
|
||
const _component_cdx_button = resolveComponent("cdx-button");
|
||
return openBlock(), createElementBlock("div", _hoisted_1$7, [
|
||
createElementVNode("div", _hoisted_2$4, [
|
||
createVNode(_component_cdx_select, {
|
||
selected: _ctx.wrappedItemsPerPage,
|
||
"onUpdate:selected": _cache[0] || (_cache[0] = ($event) => _ctx.wrappedItemsPerPage = $event),
|
||
"default-label": _ctx.defaultItemsPerPageLabel,
|
||
"menu-items": _ctx.paginationSizeOptions
|
||
}, {
|
||
label: withCtx(({ selectedMenuItem, defaultLabel }) => [
|
||
selectedMenuItem ? (openBlock(), createElementBlock("span", _hoisted_3$3, [
|
||
createElementVNode(
|
||
"span",
|
||
null,
|
||
toDisplayString(_ctx.currentItemsPerPageLabel),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
])) : (openBlock(), createElementBlock(
|
||
"span",
|
||
_hoisted_4$3,
|
||
toDisplayString(defaultLabel),
|
||
1
|
||
/* TEXT */
|
||
))
|
||
]),
|
||
_: 1
|
||
/* STABLE */
|
||
}, 8, ["selected", "default-label", "menu-items"])
|
||
]),
|
||
createElementVNode("div", _hoisted_5$3, [
|
||
renderSlot(_ctx.$slots, "default")
|
||
]),
|
||
createElementVNode("div", _hoisted_6$3, [
|
||
createVNode(_component_cdx_button, {
|
||
disabled: _ctx.prevDisabled,
|
||
class: "cdx-table-pager__button-first",
|
||
weight: "quiet",
|
||
"aria-label": _ctx.btnLabelFirst,
|
||
onClick: _cache[1] || (_cache[1] = ($event) => _ctx.$emit("first"))
|
||
}, {
|
||
default: withCtx(() => [
|
||
createVNode(_component_cdx_icon, { icon: _ctx.cdxIconMoveFirst }, null, 8, ["icon"])
|
||
]),
|
||
_: 1
|
||
/* STABLE */
|
||
}, 8, ["disabled", "aria-label"]),
|
||
createVNode(_component_cdx_button, {
|
||
disabled: _ctx.prevDisabled,
|
||
class: "cdx-table-pager__button-prev",
|
||
weight: "quiet",
|
||
"aria-label": _ctx.btnLabelPrev,
|
||
onClick: _cache[2] || (_cache[2] = ($event) => _ctx.$emit("prev"))
|
||
}, {
|
||
default: withCtx(() => [
|
||
createVNode(_component_cdx_icon, { icon: _ctx.cdxIconPrevious }, null, 8, ["icon"])
|
||
]),
|
||
_: 1
|
||
/* STABLE */
|
||
}, 8, ["disabled", "aria-label"]),
|
||
createVNode(_component_cdx_button, {
|
||
disabled: _ctx.nextDisabled,
|
||
class: "cdx-table-pager__button-next",
|
||
weight: "quiet",
|
||
"aria-label": _ctx.btnLabelNext,
|
||
onClick: _cache[3] || (_cache[3] = ($event) => _ctx.$emit("next"))
|
||
}, {
|
||
default: withCtx(() => [
|
||
createVNode(_component_cdx_icon, { icon: _ctx.cdxIconNext }, null, 8, ["icon"])
|
||
]),
|
||
_: 1
|
||
/* STABLE */
|
||
}, 8, ["disabled", "aria-label"]),
|
||
createVNode(_component_cdx_button, {
|
||
disabled: _ctx.nextDisabled || _ctx.lastDisabled,
|
||
class: "cdx-table-pager__button-last",
|
||
weight: "quiet",
|
||
"aria-label": _ctx.btnLabelLast,
|
||
onClick: _cache[4] || (_cache[4] = ($event) => _ctx.$emit("last"))
|
||
}, {
|
||
default: withCtx(() => [
|
||
createVNode(_component_cdx_icon, { icon: _ctx.cdxIconMoveLast }, null, 8, ["icon"])
|
||
]),
|
||
_: 1
|
||
/* STABLE */
|
||
}, 8, ["disabled", "aria-label"])
|
||
])
|
||
]);
|
||
}
|
||
const CdxTablePager = /* @__PURE__ */ _export_sfc(_sfc_main$7, [["render", _sfc_render$7]]);
|
||
const tableTextAlignmentsValidator = makeStringTypeValidator(TableTextAlignments);
|
||
const paginationPositionValidator = makeStringTypeValidator(TablePaginationPositions);
|
||
const iconMap = {
|
||
none: T7,
|
||
asc: x8,
|
||
desc: l4
|
||
};
|
||
const sortDirectionMap = {
|
||
none: "none",
|
||
asc: "ascending",
|
||
desc: "descending"
|
||
};
|
||
const _sfc_main$6 = defineComponent({
|
||
name: "CdxTable",
|
||
components: {
|
||
CdxCheckbox,
|
||
CdxIcon,
|
||
CdxTablePager,
|
||
CdxProgressBar
|
||
},
|
||
props: {
|
||
/**
|
||
* Table caption.
|
||
*
|
||
* Required to support users of assistive technology, but can be visually hidden.
|
||
*/
|
||
caption: {
|
||
type: String,
|
||
required: true
|
||
},
|
||
/**
|
||
* Whether to visually hide the caption.
|
||
*/
|
||
hideCaption: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Column definitions.
|
||
*
|
||
* @default []
|
||
*/
|
||
columns: {
|
||
type: Array,
|
||
default: () => [],
|
||
validator: (value) => {
|
||
const ids = value.map((column) => column.id);
|
||
const hasUniqueIds = new Set(ids).size === ids.length;
|
||
if (!hasUniqueIds) {
|
||
console.warn(
|
||
'Each column in the "columns" prop of CdxTable must have a unique "id".'
|
||
);
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
},
|
||
/**
|
||
* Table data.
|
||
*
|
||
* An array of objects, with each object representing the data for a table row. Item keys
|
||
* should align with column IDs, as defined in the `columns` prop.
|
||
*
|
||
* @default []
|
||
*/
|
||
data: {
|
||
type: Array,
|
||
default: () => [],
|
||
validator: (value, props) => {
|
||
if (!Array.isArray(props.columns) || props.columns.length === 0 || value.length === 0) {
|
||
return true;
|
||
}
|
||
const hasSort = props.columns.some((column) => "allowSort" in column);
|
||
const rowsHaveIds = value.every((row) => TableRowIdentifier in row);
|
||
if (hasSort && props.useRowSelection && !rowsHaveIds) {
|
||
console.warn(
|
||
'For CdxTables with sorting and row selection, each row in the "data" prop must have a "TableRowIdentifier".'
|
||
);
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
},
|
||
/**
|
||
* Whether to use `<th>` for the first cell in each row.
|
||
*/
|
||
useRowHeaders: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether vertical borders separating columns should be rendered.
|
||
*/
|
||
showVerticalBorders: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether to enable row selection.
|
||
*/
|
||
useRowSelection: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* An array of selected row indices. Must be bound with `v-model:selected-rows`.
|
||
*
|
||
* If sorting is also enabled, this will be an array of TableRowIdentifiers.
|
||
*
|
||
* @default []
|
||
*/
|
||
selectedRows: {
|
||
type: Array,
|
||
default: () => []
|
||
},
|
||
/**
|
||
* Definition of sort order. Column(s) can be sorted ascending, descending, or not sorted.
|
||
* To display data unsorted initially, set to an empty object initially.
|
||
* Must be bound with v-model:sort
|
||
*
|
||
* @default {}
|
||
*/
|
||
sort: {
|
||
type: Object,
|
||
default: () => ({})
|
||
},
|
||
/**
|
||
* Whether the table is waiting for data to be fetched.
|
||
*/
|
||
pending: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether to enable pagination.
|
||
*/
|
||
paginate: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether the table is paginating through remote data. Setting this to
|
||
* "true" will cause the table to emit events indicating that more data
|
||
* should be loaded when the user navigates between pages.
|
||
*/
|
||
serverPagination: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* The total number of rows/results available on the server that the
|
||
* user can access via pagination. Providing this value will make for
|
||
* a better user experience when navigating through pages of remote
|
||
* data, but it is not required.
|
||
*/
|
||
totalRows: {
|
||
type: Number,
|
||
default: NaN
|
||
},
|
||
/**
|
||
* Where the pagination controls should appear relative to the table body.
|
||
*/
|
||
paginationPosition: {
|
||
type: String,
|
||
default: "bottom",
|
||
validator: paginationPositionValidator
|
||
},
|
||
/**
|
||
* Pre-defined options for how may rows should be displayed per page.
|
||
* The value of these menu items must be a number.
|
||
*
|
||
* @default [ { value: 10 }, { value: 20 }, { value: 50 } ]
|
||
*/
|
||
paginationSizeOptions: {
|
||
type: Array,
|
||
default: () => [
|
||
{ value: 10 },
|
||
{ value: 20 },
|
||
{ value: 50 }
|
||
],
|
||
validator: (value) => {
|
||
const hasNumberValue = (item) => typeof item.value === "number";
|
||
if (value.every(hasNumberValue)) {
|
||
return true;
|
||
} else {
|
||
console.warn('"value" property of all menu items in PaginationOptions must be a number.');
|
||
return false;
|
||
}
|
||
}
|
||
},
|
||
/**
|
||
* The default number of rows to show per page. For basic pagination,
|
||
* this will default to the value of the first of the pagination options
|
||
* if not provided. For server-side pagination, this will default to
|
||
* the initial number of rows if no default is provided.
|
||
*
|
||
* @default paginationSizeOptions[ 0 ].value
|
||
*/
|
||
paginationSizeDefault: {
|
||
type: Number,
|
||
default: (rawProps) => {
|
||
if (rawProps.paginate && rawProps.serverPagination) {
|
||
return rawProps.data.length;
|
||
} else {
|
||
return rawProps.paginationSizeOptions[0].value;
|
||
}
|
||
}
|
||
}
|
||
},
|
||
emits: [
|
||
/**
|
||
* When the selected row(s) changes.
|
||
*
|
||
* @property {string[]} selectedRows The new selected rows.
|
||
*/
|
||
"update:selectedRows",
|
||
/**
|
||
* When the sort order changes emit an event to update the sort order.
|
||
*
|
||
* @property {Object} sort The new sort order.
|
||
*/
|
||
"update:sort",
|
||
/**
|
||
* When the user requests another page of data from the server.
|
||
*
|
||
* @property {number} offset Index of the first visible row on the new page.
|
||
* @property {number} rows Number of rows to display.
|
||
*
|
||
*/
|
||
"load-more",
|
||
/**
|
||
* When the user requests the last page of data from the server.
|
||
*
|
||
* @property {number} rows Number of rows to display.
|
||
*/
|
||
"last"
|
||
],
|
||
setup(props, { emit }) {
|
||
const offset2 = ref(0);
|
||
const pageSize = ref(props.paginationSizeDefault);
|
||
const dataForDisplay = computed(() => {
|
||
if (props.serverPagination && props.paginate) {
|
||
return props.data;
|
||
} else if (props.paginate) {
|
||
return props.data.slice(offset2.value, pageSize.value + offset2.value);
|
||
} else {
|
||
return props.data;
|
||
}
|
||
});
|
||
const totalCount = computed(() => {
|
||
var _a;
|
||
if (props.serverPagination) {
|
||
return (_a = props.totalRows) != null ? _a : NaN;
|
||
} else {
|
||
return props.data.length;
|
||
}
|
||
});
|
||
const indeterminate = computed(() => isNaN(totalCount.value));
|
||
const currentCount = computed(() => dataForDisplay.value.length);
|
||
const firstOrdinal = computed(() => offset2.value + 1);
|
||
const lastOrdinal = computed(() => offset2.value + currentCount.value);
|
||
const lastDisabled = computed(() => indeterminate.value);
|
||
const prevDisabled = computed(() => offset2.value <= 0);
|
||
const nextDisabled = computed(() => {
|
||
if (indeterminate.value) {
|
||
return currentCount.value < pageSize.value;
|
||
} else {
|
||
return offset2.value + pageSize.value >= totalCount.value;
|
||
}
|
||
});
|
||
const paginationStatusMessageDeterminateShort = useI18n(
|
||
"cdx-table-pagination-status-message-determinate-short",
|
||
(x, y, z) => "".concat(x, "–").concat(y, " of ").concat(z),
|
||
[firstOrdinal, lastOrdinal, totalCount]
|
||
);
|
||
const paginationStatusMessageDeterminateLong = useI18n(
|
||
"cdx-table-pagination-status-message-determinate-long",
|
||
(x, y, z) => "Showing results ".concat(x, "–").concat(y, " of ").concat(z),
|
||
[firstOrdinal, lastOrdinal, totalCount]
|
||
);
|
||
const paginationStatusMessageIndeterminateShort = useI18n(
|
||
"cdx-table-pagination-status-message-indeterminate-short",
|
||
(x, y) => "".concat(x, "–").concat(y, " of many"),
|
||
[firstOrdinal, lastOrdinal]
|
||
);
|
||
const paginationStatusMessageIndeterminateLong = useI18n(
|
||
"cdx-table-pagination-status-message-indeterminate-long",
|
||
(x, y) => "Showing results ".concat(x, "–").concat(y, " of many"),
|
||
[firstOrdinal, lastOrdinal]
|
||
);
|
||
const paginationStatusMessageIndeterminateFinal = useI18n(
|
||
"cdx-table-pagination-status-message-indeterminate-final",
|
||
(x) => "Showing the last ".concat(x, " results"),
|
||
[currentCount]
|
||
);
|
||
const paginationStatusMessagePending = useI18n(
|
||
"cdx-table-pagination-status-message-pending",
|
||
"Loading results..."
|
||
);
|
||
const paginationStatusMessageShort = computed(() => {
|
||
if (props.pending) {
|
||
return paginationStatusMessagePending.value;
|
||
} else if (indeterminate.value && nextDisabled.value) {
|
||
return paginationStatusMessageIndeterminateFinal.value;
|
||
} else if (indeterminate.value) {
|
||
return paginationStatusMessageIndeterminateShort.value;
|
||
} else {
|
||
return paginationStatusMessageDeterminateShort.value;
|
||
}
|
||
});
|
||
const paginationStatusMessageLong = computed(() => {
|
||
if (props.pending) {
|
||
return paginationStatusMessagePending.value;
|
||
} else if (indeterminate.value && nextDisabled.value) {
|
||
return paginationStatusMessageIndeterminateFinal.value;
|
||
} else if (indeterminate.value) {
|
||
return paginationStatusMessageIndeterminateLong.value;
|
||
} else {
|
||
return paginationStatusMessageDeterminateLong.value;
|
||
}
|
||
});
|
||
function onNext() {
|
||
offset2.value += pageSize.value;
|
||
if (props.serverPagination) {
|
||
emit("load-more", offset2.value, pageSize.value);
|
||
}
|
||
}
|
||
function onPrev() {
|
||
if (offset2.value - pageSize.value < 1) {
|
||
onFirst();
|
||
} else {
|
||
offset2.value -= pageSize.value;
|
||
if (props.serverPagination) {
|
||
emit("load-more", offset2.value, pageSize.value);
|
||
}
|
||
}
|
||
}
|
||
function onFirst() {
|
||
offset2.value = 0;
|
||
if (props.serverPagination) {
|
||
emit("load-more", offset2.value, pageSize.value);
|
||
}
|
||
}
|
||
function onLast() {
|
||
if (totalCount.value % pageSize.value === 0) {
|
||
offset2.value = totalCount.value - pageSize.value;
|
||
emit("load-more", offset2.value, pageSize.value);
|
||
} else {
|
||
offset2.value = Math.floor(totalCount.value / pageSize.value) * pageSize.value;
|
||
emit("load-more", offset2.value, pageSize.value);
|
||
}
|
||
}
|
||
watch(pageSize, (newPageSize) => {
|
||
if (props.serverPagination) {
|
||
emit("load-more", offset2.value, newPageSize);
|
||
}
|
||
});
|
||
const wrappedSelectedRows = useModelWrapper(toRef(props, "selectedRows"), emit, "update:selectedRows");
|
||
const selectAll = ref(totalCount.value === wrappedSelectedRows.value.length);
|
||
const selectAllIndeterminate = ref(false);
|
||
const activeSortColumn = computed(() => {
|
||
return Object.keys(props.sort)[0];
|
||
});
|
||
const hasSortableColumns = computed(() => {
|
||
return props.columns.some((column) => column.allowSort);
|
||
});
|
||
const tableClasses = computed(() => {
|
||
var _a;
|
||
const useFixedLayout = (_a = props.columns) == null ? void 0 : _a.some((column) => "width" in column || "minWidth" in column);
|
||
return {
|
||
"cdx-table__table--layout-fixed": useFixedLayout,
|
||
"cdx-table__table--borders-vertical": props.showVerticalBorders
|
||
};
|
||
});
|
||
const translatedSortCaption = useI18n(
|
||
"cdx-table-sort-caption",
|
||
(caption) => "".concat(caption, ", column headers with buttons are sortable."),
|
||
[toRef(props, "caption")]
|
||
);
|
||
const translatedSelectRowLabel = (rowIndex, totalRows) => useI18n(
|
||
"cdx-table-select-row-label",
|
||
(row, total) => "Select row ".concat(row, " of ").concat(total),
|
||
[() => rowIndex, () => totalRows]
|
||
).value;
|
||
const translatedSelectAllLabel = useI18n("cdx-table-select-all-label", "Select all rows");
|
||
function getRowKey(row, index) {
|
||
return TableRowIdentifier in row ? row[TableRowIdentifier] : index;
|
||
}
|
||
function getRowClass(row, rowIndex) {
|
||
const rowKey = getRowKey(row, rowIndex);
|
||
return {
|
||
"cdx-table__row--selected": wrappedSelectedRows.value.indexOf(rowKey) !== -1
|
||
};
|
||
}
|
||
function getRowHeaderScope(columnId) {
|
||
const firstColumn = props.columns[0].id;
|
||
if (props.useRowHeaders === true && columnId === firstColumn) {
|
||
return "row";
|
||
}
|
||
}
|
||
function getCellElement(columnId) {
|
||
const firstColumn = props.columns[0].id;
|
||
if (props.useRowHeaders && columnId === firstColumn) {
|
||
return "th";
|
||
}
|
||
return "td";
|
||
}
|
||
function getCellClass(column, hasSort = false) {
|
||
if ("textAlign" in column && !tableTextAlignmentsValidator(column.textAlign)) {
|
||
console.warn("Invalid value for TableColumn textAlign property.");
|
||
return void 0;
|
||
}
|
||
return {
|
||
// Don't assign a class for the default value 'start'. Instead, we'll set
|
||
// text-align: left on the td and th elements.
|
||
["cdx-table__table__cell--align-".concat(column.textAlign)]: "textAlign" in column && column.textAlign !== "start",
|
||
"cdx-table__table__cell--has-sort": hasSort
|
||
};
|
||
}
|
||
function getCellStyle(column) {
|
||
const styles = {};
|
||
if ("width" in column) {
|
||
styles.width = column.width;
|
||
}
|
||
if ("minWidth" in column) {
|
||
styles.minWidth = column.minWidth;
|
||
}
|
||
return styles;
|
||
}
|
||
function handleRowSelection(newSelectedRows) {
|
||
if (totalCount.value === newSelectedRows.length) {
|
||
selectAll.value = true;
|
||
selectAllIndeterminate.value = false;
|
||
return;
|
||
}
|
||
selectAll.value = false;
|
||
if (totalCount.value > newSelectedRows.length) {
|
||
selectAllIndeterminate.value = true;
|
||
}
|
||
if (newSelectedRows.length === 0) {
|
||
selectAllIndeterminate.value = false;
|
||
}
|
||
}
|
||
function handleSelectAll(newValue) {
|
||
selectAllIndeterminate.value = false;
|
||
if (newValue) {
|
||
wrappedSelectedRows.value = props.data.map((row, rowIndex) => getRowKey(row, rowIndex));
|
||
} else {
|
||
wrappedSelectedRows.value = [];
|
||
}
|
||
}
|
||
function handleSort(columnId) {
|
||
var _a;
|
||
const currentSortOrder = (_a = props.sort[columnId]) != null ? _a : "none";
|
||
let newSortOrder = "asc";
|
||
if (currentSortOrder === "asc") {
|
||
newSortOrder = "desc";
|
||
}
|
||
if (currentSortOrder === "desc") {
|
||
newSortOrder = "none";
|
||
}
|
||
emit("update:sort", { [columnId]: newSortOrder });
|
||
}
|
||
function getSortIcon(columnId) {
|
||
var _a;
|
||
const currentSortOrder = (_a = props.sort[columnId]) != null ? _a : "none";
|
||
return iconMap[currentSortOrder];
|
||
}
|
||
function getSortOrder(columnId, hasSort = false) {
|
||
var _a;
|
||
if (hasSort) {
|
||
const currentSortOrder = (_a = props.sort[columnId]) != null ? _a : "none";
|
||
return currentSortOrder === "none" ? void 0 : sortDirectionMap[currentSortOrder];
|
||
}
|
||
}
|
||
return {
|
||
// pagination
|
||
dataForDisplay,
|
||
pageSize,
|
||
onNext,
|
||
onPrev,
|
||
onFirst,
|
||
onLast,
|
||
nextDisabled,
|
||
prevDisabled,
|
||
lastDisabled,
|
||
paginationStatusMessageShort,
|
||
paginationStatusMessageLong,
|
||
// Row selection constants.
|
||
wrappedSelectedRows,
|
||
selectAll,
|
||
selectAllIndeterminate,
|
||
// Sorting constants.
|
||
activeSortColumn,
|
||
hasSortableColumns,
|
||
// Template helpers.
|
||
tableClasses,
|
||
getRowKey,
|
||
getRowClass,
|
||
getRowHeaderScope,
|
||
getCellElement,
|
||
getCellClass,
|
||
getCellStyle,
|
||
// Row selection methods.
|
||
handleRowSelection,
|
||
handleSelectAll,
|
||
// Sorting methods.
|
||
handleSort,
|
||
getSortIcon,
|
||
getSortOrder,
|
||
// i18n
|
||
translatedSortCaption,
|
||
translatedSelectRowLabel,
|
||
translatedSelectAllLabel
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$6 = {
|
||
class: "cdx-table",
|
||
tabindex: "0"
|
||
};
|
||
const _hoisted_2$3 = {
|
||
key: 0,
|
||
class: "cdx-table__header"
|
||
};
|
||
const _hoisted_3$2 = ["aria-hidden"];
|
||
const _hoisted_4$2 = { class: "cdx-table__header__content" };
|
||
const _hoisted_5$2 = { class: "cdx-table__pagination-status--long" };
|
||
const _hoisted_6$2 = { class: "cdx-table__pagination-status--short" };
|
||
const _hoisted_7 = { class: "cdx-table__table-wrapper" };
|
||
const _hoisted_8 = { key: 0 };
|
||
const _hoisted_9 = {
|
||
key: 0,
|
||
class: "cdx-table__table__select-rows"
|
||
};
|
||
const _hoisted_10 = ["aria-sort"];
|
||
const _hoisted_11 = ["aria-selected", "onClick"];
|
||
const _hoisted_12 = { class: "cdx-table__table__sort-label" };
|
||
const _hoisted_13 = { key: 0 };
|
||
const _hoisted_14 = { key: 0 };
|
||
const _hoisted_15 = { key: 1 };
|
||
const _hoisted_16 = { class: "cdx-table__table__empty-state" };
|
||
const _hoisted_17 = { class: "cdx-table__table__empty-state-content" };
|
||
const _hoisted_18 = { class: "cdx-table__pagination-status--long" };
|
||
const _hoisted_19 = { class: "cdx-table__pagination-status--short" };
|
||
const _hoisted_20 = {
|
||
key: 3,
|
||
class: "cdx-table__footer"
|
||
};
|
||
function _sfc_render$6(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_table_pager = resolveComponent("cdx-table-pager");
|
||
const _component_cdx_checkbox = resolveComponent("cdx-checkbox");
|
||
const _component_cdx_icon = resolveComponent("cdx-icon");
|
||
const _component_cdx_progress_bar = resolveComponent("cdx-progress-bar");
|
||
return openBlock(), createElementBlock("div", _hoisted_1$6, [
|
||
!_ctx.hideCaption || _ctx.$slots.header && _ctx.$slots.header().length > 0 ? (openBlock(), createElementBlock("div", _hoisted_2$3, [
|
||
createElementVNode("div", {
|
||
class: "cdx-table__header__caption",
|
||
"aria-hidden": _ctx.$slots.header && _ctx.$slots.header().length > 0 ? void 0 : true
|
||
}, [
|
||
!_ctx.hideCaption ? (openBlock(), createElementBlock(
|
||
Fragment,
|
||
{ key: 0 },
|
||
[
|
||
createTextVNode(
|
||
toDisplayString(_ctx.caption),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
],
|
||
64
|
||
/* STABLE_FRAGMENT */
|
||
)) : createCommentVNode("v-if", true)
|
||
], 8, _hoisted_3$2),
|
||
createElementVNode("div", _hoisted_4$2, [
|
||
renderSlot(_ctx.$slots, "header")
|
||
])
|
||
])) : createCommentVNode("v-if", true),
|
||
_ctx.paginate && (_ctx.paginationPosition === "top" || _ctx.paginationPosition === "both") ? (openBlock(), createBlock(_component_cdx_table_pager, {
|
||
key: 1,
|
||
"items-per-page": _ctx.pageSize,
|
||
"onUpdate:itemsPerPage": _cache[0] || (_cache[0] = ($event) => _ctx.pageSize = $event),
|
||
class: "cdx-table__pagination--top",
|
||
"pagination-size-options": _ctx.paginationSizeOptions,
|
||
"prev-disabled": _ctx.prevDisabled,
|
||
"next-disabled": _ctx.nextDisabled,
|
||
"last-disabled": _ctx.lastDisabled,
|
||
onNext: _ctx.onNext,
|
||
onPrev: _ctx.onPrev,
|
||
onFirst: _ctx.onFirst,
|
||
onLast: _ctx.onLast
|
||
}, {
|
||
default: withCtx(() => [
|
||
createElementVNode(
|
||
"span",
|
||
_hoisted_5$2,
|
||
toDisplayString(_ctx.paginationStatusMessageLong),
|
||
1
|
||
/* TEXT */
|
||
),
|
||
createElementVNode(
|
||
"span",
|
||
_hoisted_6$2,
|
||
toDisplayString(_ctx.paginationStatusMessageShort),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
]),
|
||
_: 1
|
||
/* STABLE */
|
||
}, 8, ["items-per-page", "pagination-size-options", "prev-disabled", "next-disabled", "last-disabled", "onNext", "onPrev", "onFirst", "onLast"])) : createCommentVNode("v-if", true),
|
||
createElementVNode("div", _hoisted_7, [
|
||
createElementVNode(
|
||
"table",
|
||
{
|
||
class: normalizeClass(["cdx-table__table", _ctx.tableClasses])
|
||
},
|
||
[
|
||
createElementVNode("caption", null, [
|
||
!_ctx.hasSortableColumns ? (openBlock(), createElementBlock(
|
||
Fragment,
|
||
{ key: 0 },
|
||
[
|
||
createTextVNode(
|
||
toDisplayString(_ctx.caption),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
],
|
||
64
|
||
/* STABLE_FRAGMENT */
|
||
)) : (openBlock(), createElementBlock(
|
||
Fragment,
|
||
{ key: 1 },
|
||
[
|
||
createTextVNode(
|
||
toDisplayString(_ctx.translatedSortCaption),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
],
|
||
64
|
||
/* STABLE_FRAGMENT */
|
||
))
|
||
]),
|
||
renderSlot(_ctx.$slots, "thead", {}, () => [
|
||
_ctx.columns.length > 0 ? (openBlock(), createElementBlock("thead", _hoisted_8, [
|
||
createElementVNode("tr", null, [
|
||
_ctx.useRowSelection ? (openBlock(), createElementBlock("th", _hoisted_9, [
|
||
createVNode(_component_cdx_checkbox, {
|
||
modelValue: _ctx.selectAll,
|
||
"onUpdate:modelValue": [
|
||
_cache[1] || (_cache[1] = ($event) => _ctx.selectAll = $event),
|
||
_ctx.handleSelectAll
|
||
],
|
||
"hide-label": true,
|
||
indeterminate: _ctx.selectAllIndeterminate
|
||
}, {
|
||
default: withCtx(() => [
|
||
createTextVNode(
|
||
toDisplayString(_ctx.translatedSelectAllLabel),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
]),
|
||
_: 1
|
||
/* STABLE */
|
||
}, 8, ["modelValue", "indeterminate", "onUpdate:modelValue"])
|
||
])) : createCommentVNode("v-if", true),
|
||
(openBlock(true), createElementBlock(
|
||
Fragment,
|
||
null,
|
||
renderList(_ctx.columns, (column) => {
|
||
return openBlock(), createElementBlock("th", {
|
||
key: column.id,
|
||
scope: "col",
|
||
class: normalizeClass(_ctx.getCellClass(column, column.allowSort)),
|
||
"aria-sort": _ctx.getSortOrder(column.id, column.allowSort),
|
||
style: normalizeStyle(_ctx.getCellStyle(column))
|
||
}, [
|
||
column.allowSort ? (openBlock(), createElementBlock("button", {
|
||
key: 0,
|
||
"aria-selected": column.id === _ctx.activeSortColumn,
|
||
class: "cdx-table__table__sort-button",
|
||
onClick: ($event) => _ctx.handleSort(column.id)
|
||
}, [
|
||
createElementVNode(
|
||
"span",
|
||
_hoisted_12,
|
||
toDisplayString(column.label),
|
||
1
|
||
/* TEXT */
|
||
),
|
||
createVNode(_component_cdx_icon, {
|
||
icon: _ctx.getSortIcon(column.id),
|
||
size: "small",
|
||
class: "cdx-table__table__sort-icon",
|
||
"aria-hidden": "true"
|
||
}, null, 8, ["icon"])
|
||
], 8, _hoisted_11)) : (openBlock(), createElementBlock(
|
||
Fragment,
|
||
{ key: 1 },
|
||
[
|
||
createTextVNode(
|
||
toDisplayString(column.label),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
],
|
||
64
|
||
/* STABLE_FRAGMENT */
|
||
))
|
||
], 14, _hoisted_10);
|
||
}),
|
||
128
|
||
/* KEYED_FRAGMENT */
|
||
))
|
||
])
|
||
])) : createCommentVNode("v-if", true)
|
||
]),
|
||
_ctx.pending ? (openBlock(), createBlock(_component_cdx_progress_bar, {
|
||
key: 0,
|
||
inline: true,
|
||
class: "cdx-table__pending-indicator"
|
||
})) : createCommentVNode("v-if", true),
|
||
renderSlot(_ctx.$slots, "tbody", {}, () => [
|
||
_ctx.dataForDisplay.length > 0 ? (openBlock(), createElementBlock("tbody", _hoisted_13, [
|
||
(openBlock(true), createElementBlock(
|
||
Fragment,
|
||
null,
|
||
renderList(_ctx.dataForDisplay, (row, rowIndex) => {
|
||
return openBlock(), createElementBlock(
|
||
"tr",
|
||
{
|
||
key: _ctx.getRowKey(row, rowIndex),
|
||
class: normalizeClass(_ctx.getRowClass(row, rowIndex))
|
||
},
|
||
[
|
||
_ctx.useRowSelection ? (openBlock(), createElementBlock("td", _hoisted_14, [
|
||
createVNode(_component_cdx_checkbox, {
|
||
modelValue: _ctx.wrappedSelectedRows,
|
||
"onUpdate:modelValue": [
|
||
_cache[2] || (_cache[2] = ($event) => _ctx.wrappedSelectedRows = $event),
|
||
_ctx.handleRowSelection
|
||
],
|
||
"input-value": _ctx.getRowKey(row, rowIndex),
|
||
"hide-label": true
|
||
}, {
|
||
default: withCtx(() => [
|
||
createTextVNode(
|
||
toDisplayString(_ctx.translatedSelectRowLabel(
|
||
rowIndex + 1,
|
||
_ctx.dataForDisplay.length
|
||
)),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
]),
|
||
_: 2
|
||
/* DYNAMIC */
|
||
}, 1032, ["modelValue", "input-value", "onUpdate:modelValue"])
|
||
])) : createCommentVNode("v-if", true),
|
||
(openBlock(true), createElementBlock(
|
||
Fragment,
|
||
null,
|
||
renderList(_ctx.columns, (column) => {
|
||
return openBlock(), createBlock(resolveDynamicComponent(_ctx.getCellElement(column.id)), {
|
||
key: column.id,
|
||
scope: _ctx.getRowHeaderScope(column.id),
|
||
class: normalizeClass(_ctx.getCellClass(column))
|
||
}, {
|
||
default: withCtx(() => [
|
||
renderSlot(_ctx.$slots, "item-" + column.id, {
|
||
item: row[column.id],
|
||
row
|
||
}, () => [
|
||
createTextVNode(
|
||
toDisplayString(row[column.id]),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
])
|
||
]),
|
||
_: 2
|
||
/* DYNAMIC */
|
||
}, 1032, ["scope", "class"]);
|
||
}),
|
||
128
|
||
/* KEYED_FRAGMENT */
|
||
))
|
||
],
|
||
2
|
||
/* CLASS */
|
||
);
|
||
}),
|
||
128
|
||
/* KEYED_FRAGMENT */
|
||
))
|
||
])) : _ctx.$slots["empty-state"] && _ctx.$slots["empty-state"]().length > 0 ? (openBlock(), createElementBlock("tbody", _hoisted_15, [
|
||
createElementVNode("tr", _hoisted_16, [
|
||
createElementVNode("td", _hoisted_17, [
|
||
renderSlot(_ctx.$slots, "empty-state")
|
||
])
|
||
])
|
||
])) : createCommentVNode("v-if", true)
|
||
]),
|
||
renderSlot(_ctx.$slots, "tfoot")
|
||
],
|
||
2
|
||
/* CLASS */
|
||
)
|
||
]),
|
||
_ctx.paginate && (_ctx.paginationPosition === "bottom" || _ctx.paginationPosition === "both") ? (openBlock(), createBlock(_component_cdx_table_pager, {
|
||
key: 2,
|
||
"items-per-page": _ctx.pageSize,
|
||
"onUpdate:itemsPerPage": _cache[3] || (_cache[3] = ($event) => _ctx.pageSize = $event),
|
||
class: "cdx-table__pagination--bottom",
|
||
"pagination-size-options": _ctx.paginationSizeOptions,
|
||
"prev-disabled": _ctx.prevDisabled,
|
||
"next-disabled": _ctx.nextDisabled,
|
||
"last-disabled": _ctx.lastDisabled,
|
||
onNext: _ctx.onNext,
|
||
onPrev: _ctx.onPrev,
|
||
onFirst: _ctx.onFirst,
|
||
onLast: _ctx.onLast
|
||
}, {
|
||
default: withCtx(() => [
|
||
createElementVNode(
|
||
"span",
|
||
_hoisted_18,
|
||
toDisplayString(_ctx.paginationStatusMessageLong),
|
||
1
|
||
/* TEXT */
|
||
),
|
||
createElementVNode(
|
||
"span",
|
||
_hoisted_19,
|
||
toDisplayString(_ctx.paginationStatusMessageShort),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
]),
|
||
_: 1
|
||
/* STABLE */
|
||
}, 8, ["items-per-page", "pagination-size-options", "prev-disabled", "next-disabled", "last-disabled", "onNext", "onPrev", "onFirst", "onLast"])) : createCommentVNode("v-if", true),
|
||
_ctx.$slots.footer && _ctx.$slots.footer().length > 0 ? (openBlock(), createElementBlock("div", _hoisted_20, [
|
||
renderSlot(_ctx.$slots, "footer")
|
||
])) : createCommentVNode("v-if", true)
|
||
]);
|
||
}
|
||
const Table = /* @__PURE__ */ _export_sfc(_sfc_main$6, [["render", _sfc_render$6]]);
|
||
const _sfc_main$5 = defineComponent({
|
||
name: "CdxTab",
|
||
/**
|
||
* The "label" and "disabled" props are referenced by the parent Tabs
|
||
* component during the generation of a list of labels.
|
||
*/
|
||
props: {
|
||
/**
|
||
* String name of the tab, used for programmatic selection. Each Tab
|
||
* inside a layout must have a unique name. This prop will also be
|
||
* used as the tab label if no "label" prop is provided.
|
||
*/
|
||
name: {
|
||
type: String,
|
||
required: true
|
||
},
|
||
/**
|
||
* Label that corresponds to this Tab in the Tabs component's header.
|
||
* Lengthy labels will be truncated.
|
||
*/
|
||
// eslint-disable-next-line vue/no-unused-properties
|
||
label: {
|
||
type: String,
|
||
default: ""
|
||
},
|
||
/**
|
||
* Whether or not the tab is disabled. Disabled tabs cannot be accessed
|
||
* via label clicks or keyboard navigation.
|
||
*/
|
||
// eslint-disable-next-line vue/no-unused-properties
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
},
|
||
setup(props) {
|
||
var _a;
|
||
const tabsData = inject(TabsKey);
|
||
const activeTab = inject(ActiveTabKey);
|
||
if (!tabsData || !activeTab) {
|
||
throw new Error("Tab component must be used inside a Tabs component");
|
||
}
|
||
const tab = (_a = tabsData.value.get(props.name)) != null ? _a : {};
|
||
const isActive = computed(() => props.name === activeTab.value);
|
||
return {
|
||
tab,
|
||
isActive
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$5 = ["id", "aria-hidden", "aria-labelledby"];
|
||
function _sfc_render$5(_ctx, _cache, $props, $setup, $data, $options) {
|
||
return withDirectives((openBlock(), createElementBlock("section", {
|
||
id: _ctx.tab.id,
|
||
"aria-hidden": !_ctx.isActive ? true : void 0,
|
||
"aria-labelledby": "".concat(_ctx.tab.id, "-label"),
|
||
class: "cdx-tab",
|
||
role: "tabpanel",
|
||
tabindex: "-1"
|
||
}, [
|
||
renderSlot(_ctx.$slots, "default")
|
||
], 8, _hoisted_1$5)), [
|
||
[vShow, _ctx.isActive]
|
||
]);
|
||
}
|
||
const CdxTab = /* @__PURE__ */ _export_sfc(_sfc_main$5, [["render", _sfc_render$5]]);
|
||
const _sfc_main$4 = defineComponent({
|
||
name: "CdxTabs",
|
||
components: {
|
||
CdxButton,
|
||
CdxIcon
|
||
},
|
||
props: {
|
||
/**
|
||
* The `name` of the currently active Tab in the layout.
|
||
*
|
||
* This prop is optional; if it is provided, it should be bound
|
||
* using a `v-model:active` directive in the parent component.
|
||
* Two-way binding the active tab is only necessary if some tab
|
||
* other than the first should be active as soon as the component
|
||
* renders (such as in cases where the active tab is bound to URL
|
||
* params). If this prop is not provided, then the first tab will
|
||
* be active by default. Regardless, the active tab can be changed
|
||
* normally by user interaction (clicking on tab headings) or by
|
||
* using the exposed methods "select", "next", and "prev".
|
||
*/
|
||
active: {
|
||
type: String,
|
||
default: null
|
||
},
|
||
/**
|
||
* Whether or not the component should be displayed in a framed
|
||
* visual style.
|
||
*/
|
||
framed: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
},
|
||
emits: [
|
||
/**
|
||
* Emitted whenever the active tab changes, assuming that an `active`
|
||
* prop has been provided in the parent.
|
||
*
|
||
* @property {string} active The `name` of the current active tab
|
||
*/
|
||
"update:active"
|
||
],
|
||
/**
|
||
* Some methods are exposed to allow for programmatic selection of
|
||
* the active tab from outside of the component.
|
||
*/
|
||
expose: [
|
||
"select",
|
||
"next",
|
||
"prev"
|
||
],
|
||
setup(props, { slots, emit }) {
|
||
const rootElement = ref();
|
||
const tabListElement = ref();
|
||
const prevScroller = ref();
|
||
const nextScroller = ref();
|
||
const currentDirection = useComputedDirection(rootElement);
|
||
const childTabNodes = computed(() => {
|
||
const slotContents = useSlotContents(slots.default);
|
||
if (!slotContents.every(
|
||
(node) => typeof node === "object" && isComponentVNode(node, CdxTab.name)
|
||
)) {
|
||
throw new Error("Slot content may only contain CdxTab components");
|
||
}
|
||
if (slotContents.length === 0) {
|
||
throw new Error("Slot content cannot be empty");
|
||
}
|
||
return slotContents;
|
||
});
|
||
const tabsData = computed(() => {
|
||
return childTabNodes.value.reduce((map, item) => {
|
||
var _a;
|
||
if (((_a = item.props) == null ? void 0 : _a.name) && typeof item.props.name === "string") {
|
||
if (map.get(item.props.name)) {
|
||
throw new Error("Tab names must be unique");
|
||
}
|
||
map.set(item.props.name, {
|
||
name: item.props.name,
|
||
id: useGeneratedId(item.props.name),
|
||
label: item.props.label || item.props.name,
|
||
disabled: item.props.disabled
|
||
});
|
||
}
|
||
return map;
|
||
}, /* @__PURE__ */ new Map());
|
||
});
|
||
const internalRefForActiveTab = ref(Array.from(tabsData.value.keys())[0]);
|
||
const activeTab = useOptionalModelWrapper(internalRefForActiveTab, toRef(props, "active"), emit, "update:active");
|
||
const tabNames = computed(() => Array.from(tabsData.value.keys()));
|
||
const activeTabIndex = computed(() => tabNames.value.indexOf(activeTab.value));
|
||
const activeTabId = computed(() => {
|
||
var _a;
|
||
return (_a = tabsData.value.get(activeTab.value)) == null ? void 0 : _a.id;
|
||
});
|
||
provide(ActiveTabKey, activeTab);
|
||
provide(TabsKey, tabsData);
|
||
const tabButtonRefs = ref(/* @__PURE__ */ new Map());
|
||
const firstTabLabel = ref();
|
||
const lastTabLabel = ref();
|
||
const firstLabelVisible = useIntersectionObserver(firstTabLabel, { threshold: 0.95 });
|
||
const lastLabelVisible = useIntersectionObserver(lastTabLabel, { threshold: 0.95 });
|
||
function assignTemplateRefForTabButton(templateRef, index) {
|
||
const el = templateRef;
|
||
if (el) {
|
||
tabButtonRefs.value.set(index, el);
|
||
if (index === 0) {
|
||
firstTabLabel.value = el;
|
||
} else if (index === tabNames.value.length - 1) {
|
||
lastTabLabel.value = el;
|
||
}
|
||
}
|
||
}
|
||
const rootClasses = computed(() => {
|
||
return {
|
||
"cdx-tabs--framed": props.framed,
|
||
"cdx-tabs--quiet": !props.framed
|
||
};
|
||
});
|
||
function focusActiveTab() {
|
||
var _a;
|
||
(_a = tabButtonRefs.value.get(activeTabIndex.value)) == null ? void 0 : _a.focus();
|
||
}
|
||
function getScrollDistance(tabLabel) {
|
||
if (!tabListElement.value || !prevScroller.value || !nextScroller.value) {
|
||
return 0;
|
||
}
|
||
const leftScroller = currentDirection.value === "rtl" ? nextScroller.value : prevScroller.value;
|
||
const rightScroller = currentDirection.value === "rtl" ? prevScroller.value : nextScroller.value;
|
||
const labelLeft = tabLabel.offsetLeft;
|
||
const labelRight = labelLeft + tabLabel.clientWidth;
|
||
const visibleLeft = tabListElement.value.scrollLeft + leftScroller.clientWidth;
|
||
const visibleRight = tabListElement.value.scrollLeft + tabListElement.value.clientWidth - rightScroller.clientWidth;
|
||
if (labelLeft < visibleLeft) {
|
||
return labelLeft - visibleLeft;
|
||
}
|
||
if (labelRight > visibleRight) {
|
||
return labelRight - visibleRight;
|
||
}
|
||
return 0;
|
||
}
|
||
function scrollTabs(logicalDirection) {
|
||
if (!tabListElement.value || !prevScroller.value || !nextScroller.value) {
|
||
return;
|
||
}
|
||
const scrollDirection = logicalDirection === "next" && currentDirection.value === "ltr" || logicalDirection === "prev" && currentDirection.value === "rtl" ? 1 : -1;
|
||
let scrollDistance = 0;
|
||
let tabLabel = logicalDirection === "next" ? tabListElement.value.firstElementChild : tabListElement.value.lastElementChild;
|
||
while (tabLabel) {
|
||
const nextTabLabel = logicalDirection === "next" ? tabLabel.nextElementSibling : tabLabel.previousElementSibling;
|
||
scrollDistance = getScrollDistance(tabLabel);
|
||
if (Math.sign(scrollDistance) === scrollDirection) {
|
||
if (nextTabLabel && Math.abs(scrollDistance) < 0.25 * tabListElement.value.clientWidth) {
|
||
scrollDistance = getScrollDistance(nextTabLabel);
|
||
}
|
||
break;
|
||
}
|
||
tabLabel = nextTabLabel;
|
||
}
|
||
tabListElement.value.scrollBy({
|
||
left: scrollDistance,
|
||
behavior: "smooth"
|
||
});
|
||
focusActiveTab();
|
||
}
|
||
watch(activeTab, () => {
|
||
if (activeTabId.value === void 0 || !tabListElement.value || !prevScroller.value || !nextScroller.value) {
|
||
return;
|
||
}
|
||
const activeTabLabel = document.getElementById("".concat(activeTabId.value, "-label"));
|
||
if (!activeTabLabel) {
|
||
return;
|
||
}
|
||
tabListElement.value.scrollBy({
|
||
left: getScrollDistance(activeTabLabel),
|
||
behavior: "smooth"
|
||
});
|
||
});
|
||
return {
|
||
activeTab,
|
||
activeTabIndex,
|
||
activeTabId,
|
||
currentDirection,
|
||
rootElement,
|
||
tabListElement,
|
||
prevScroller,
|
||
nextScroller,
|
||
rootClasses,
|
||
tabNames,
|
||
tabsData,
|
||
firstLabelVisible,
|
||
lastLabelVisible,
|
||
assignTemplateRefForTabButton,
|
||
scrollTabs,
|
||
focusActiveTab,
|
||
cdxIconPrevious: d7,
|
||
cdxIconNext: j6
|
||
};
|
||
},
|
||
/**
|
||
* Some non-public methods are defined here rather than in setup because
|
||
* they support public methods (which *must* be defined using the Options
|
||
* API in order to show up in documentation), or are thematically related
|
||
* (such as key handlers).
|
||
*/
|
||
methods: {
|
||
/**
|
||
* Programmatically select a tab based on its "name" prop
|
||
*
|
||
* @param {string} tabName The name of the tab to select
|
||
* @param {boolean} setFocus Whether or not to also set focus to the new tab
|
||
* @public
|
||
*/
|
||
select(tabName, setFocus) {
|
||
const target = this.tabsData.get(tabName);
|
||
if (target && !(target == null ? void 0 : target.disabled)) {
|
||
this.activeTab = tabName;
|
||
if (setFocus) {
|
||
nextTick(() => {
|
||
this.focusActiveTab();
|
||
});
|
||
}
|
||
}
|
||
},
|
||
/**
|
||
* Used to select next or previous tab in the sequence, skipping
|
||
* over any tabs that are disabled. The provided increment should
|
||
* be either 1 (to move forward) or -1 (to move backwards)
|
||
*
|
||
* @param index
|
||
* @param increment
|
||
* @param setFocus
|
||
*/
|
||
selectNonDisabled(index, increment, setFocus) {
|
||
const target = this.tabsData.get(this.tabNames[index + increment]);
|
||
if (target) {
|
||
if (target.disabled) {
|
||
this.selectNonDisabled(index + increment, increment, setFocus);
|
||
} else {
|
||
this.select(target.name, setFocus);
|
||
}
|
||
}
|
||
},
|
||
/**
|
||
* Set the next tab to active, if one exists
|
||
*
|
||
* @param {boolean} setFocus
|
||
* @public
|
||
*/
|
||
next(setFocus) {
|
||
this.selectNonDisabled(this.activeTabIndex, 1, setFocus);
|
||
},
|
||
/**
|
||
* Set the previous tab to active, if one exists
|
||
*
|
||
* @param {boolean} setFocus
|
||
* @public
|
||
*/
|
||
prev(setFocus) {
|
||
this.selectNonDisabled(this.activeTabIndex, -1, setFocus);
|
||
},
|
||
/**
|
||
* Handle left arrow key navigation (based on LTR/RTL direction)
|
||
*/
|
||
onLeftArrowKeypress() {
|
||
if (this.currentDirection === "rtl") {
|
||
this.next(true);
|
||
} else {
|
||
this.prev(true);
|
||
}
|
||
},
|
||
/**
|
||
* Handle right arrow key navigation (based on LTR/RTL direction)
|
||
*/
|
||
onRightArrowKeypress() {
|
||
if (this.currentDirection === "rtl") {
|
||
this.prev(true);
|
||
} else {
|
||
this.next(true);
|
||
}
|
||
},
|
||
/**
|
||
* Handle down arrow key navigation by moving focus to the contents
|
||
* of the currently active tab
|
||
*/
|
||
onDownArrowKeypress() {
|
||
var _a;
|
||
if (this.activeTabId) {
|
||
(_a = document.getElementById(this.activeTabId)) == null ? void 0 : _a.focus();
|
||
}
|
||
}
|
||
}
|
||
});
|
||
const _hoisted_1$4 = { class: "cdx-tabs__header" };
|
||
const _hoisted_2$2 = {
|
||
ref: "prevScroller",
|
||
class: "cdx-tabs__prev-scroller"
|
||
};
|
||
const _hoisted_3$1 = {
|
||
ref: "tabListElement",
|
||
class: "cdx-tabs__list",
|
||
role: "tablist"
|
||
};
|
||
const _hoisted_4$1 = ["id", "disabled", "aria-controls", "aria-selected", "tabindex", "onClick", "onKeyup"];
|
||
const _hoisted_5$1 = {
|
||
ref: "nextScroller",
|
||
class: "cdx-tabs__next-scroller"
|
||
};
|
||
const _hoisted_6$1 = { class: "cdx-tabs__content" };
|
||
function _sfc_render$4(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_icon = resolveComponent("cdx-icon");
|
||
const _component_cdx_button = resolveComponent("cdx-button");
|
||
return openBlock(), createElementBlock(
|
||
"div",
|
||
{
|
||
ref: "rootElement",
|
||
class: normalizeClass(["cdx-tabs", _ctx.rootClasses])
|
||
},
|
||
[
|
||
createElementVNode("div", _hoisted_1$4, [
|
||
withDirectives(createElementVNode(
|
||
"div",
|
||
_hoisted_2$2,
|
||
[
|
||
createVNode(_component_cdx_button, {
|
||
class: "cdx-tabs__scroll-button",
|
||
weight: "quiet",
|
||
type: "button",
|
||
tabindex: "-1",
|
||
"aria-hidden": true,
|
||
onMousedown: _cache[0] || (_cache[0] = withModifiers(() => {
|
||
}, ["prevent"])),
|
||
onClick: _cache[1] || (_cache[1] = ($event) => _ctx.scrollTabs("prev"))
|
||
}, {
|
||
default: withCtx(() => [
|
||
createVNode(_component_cdx_icon, { icon: _ctx.cdxIconPrevious }, null, 8, ["icon"])
|
||
]),
|
||
_: 1
|
||
/* STABLE */
|
||
})
|
||
],
|
||
512
|
||
/* NEED_PATCH */
|
||
), [
|
||
[vShow, !_ctx.firstLabelVisible]
|
||
]),
|
||
createElementVNode(
|
||
"div",
|
||
_hoisted_3$1,
|
||
[
|
||
(openBlock(true), createElementBlock(
|
||
Fragment,
|
||
null,
|
||
renderList(_ctx.tabsData.values(), (tab, index) => {
|
||
return openBlock(), createElementBlock("button", {
|
||
id: "".concat(tab.id, "-label"),
|
||
key: index,
|
||
ref_for: true,
|
||
ref: (ref2) => _ctx.assignTemplateRefForTabButton(ref2, index),
|
||
disabled: tab.disabled ? true : void 0,
|
||
"aria-controls": tab.id,
|
||
"aria-selected": tab.name === _ctx.activeTab,
|
||
tabindex: tab.name === _ctx.activeTab ? void 0 : -1,
|
||
class: "cdx-tabs__list__item",
|
||
role: "tab",
|
||
onClick: withModifiers(($event) => _ctx.select(tab.name), ["prevent"]),
|
||
onKeyup: withKeys(($event) => _ctx.select(tab.name), ["enter"]),
|
||
onKeydown: [
|
||
_cache[2] || (_cache[2] = withKeys(withModifiers((...args) => _ctx.onRightArrowKeypress && _ctx.onRightArrowKeypress(...args), ["prevent"]), ["right"])),
|
||
_cache[3] || (_cache[3] = withKeys(withModifiers((...args) => _ctx.onDownArrowKeypress && _ctx.onDownArrowKeypress(...args), ["prevent"]), ["down"])),
|
||
_cache[4] || (_cache[4] = withKeys(withModifiers((...args) => _ctx.onLeftArrowKeypress && _ctx.onLeftArrowKeypress(...args), ["prevent"]), ["left"]))
|
||
]
|
||
}, [
|
||
createElementVNode(
|
||
"span",
|
||
null,
|
||
toDisplayString(tab.label),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
], 40, _hoisted_4$1);
|
||
}),
|
||
128
|
||
/* KEYED_FRAGMENT */
|
||
))
|
||
],
|
||
512
|
||
/* NEED_PATCH */
|
||
),
|
||
withDirectives(createElementVNode(
|
||
"div",
|
||
_hoisted_5$1,
|
||
[
|
||
createVNode(_component_cdx_button, {
|
||
class: "cdx-tabs__scroll-button",
|
||
weight: "quiet",
|
||
type: "button",
|
||
tabindex: "-1",
|
||
"aria-hidden": true,
|
||
onMousedown: _cache[5] || (_cache[5] = withModifiers(() => {
|
||
}, ["prevent"])),
|
||
onClick: _cache[6] || (_cache[6] = ($event) => _ctx.scrollTabs("next"))
|
||
}, {
|
||
default: withCtx(() => [
|
||
createVNode(_component_cdx_icon, { icon: _ctx.cdxIconNext }, null, 8, ["icon"])
|
||
]),
|
||
_: 1
|
||
/* STABLE */
|
||
})
|
||
],
|
||
512
|
||
/* NEED_PATCH */
|
||
), [
|
||
[vShow, !_ctx.lastLabelVisible]
|
||
])
|
||
]),
|
||
createElementVNode("div", _hoisted_6$1, [
|
||
renderSlot(_ctx.$slots, "default")
|
||
])
|
||
],
|
||
2
|
||
/* CLASS */
|
||
);
|
||
}
|
||
const Tabs = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["render", _sfc_render$4]]);
|
||
const statusValidator = makeStringTypeValidator(ValidationStatusTypes);
|
||
const _sfc_main$3 = defineComponent({
|
||
name: "CdxTextArea",
|
||
components: { CdxIcon },
|
||
inheritAttrs: false,
|
||
props: {
|
||
/**
|
||
* Current value of the textarea.
|
||
*
|
||
* Provided by `v-model` binding in the parent component.
|
||
*/
|
||
modelValue: {
|
||
type: String,
|
||
default: ""
|
||
},
|
||
/**
|
||
* `status` attribute of the textarea.
|
||
*/
|
||
status: {
|
||
type: String,
|
||
default: "default",
|
||
validator: statusValidator
|
||
},
|
||
/**
|
||
* Whether the textarea is disabled.
|
||
*/
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Describes whether the textarea grows vertically to show all text.
|
||
*
|
||
* When autosize is true, the textarea automatically grows in height (vertically).
|
||
* The height of the textarea expands while the user types in the textarea.
|
||
* The content inside the textarea is visible and there's no scroll.
|
||
*
|
||
* @values true, false
|
||
*/
|
||
autosize: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* An icon at the start of the textarea element. Similar to a `::before` pseudo-element.
|
||
*/
|
||
startIcon: {
|
||
type: [String, Object],
|
||
default: void 0
|
||
},
|
||
/**
|
||
* An icon at the end of the textarea element. Similar to an `::after` pseudo-element.
|
||
*/
|
||
endIcon: {
|
||
type: [String, Object],
|
||
default: void 0
|
||
}
|
||
},
|
||
emits: [
|
||
/**
|
||
* When the textarea value changes.
|
||
*
|
||
* @property {string} modelValue The new model value
|
||
*/
|
||
"update:modelValue"
|
||
],
|
||
setup(props, { attrs, emit }) {
|
||
const wrappedModel = useModelWrapper(toRef(props, "modelValue"), emit);
|
||
const idAttribute = attrs.id;
|
||
const {
|
||
computedDisabled,
|
||
computedStatus,
|
||
computedInputId
|
||
} = useFieldData(
|
||
toRef(props, "disabled"),
|
||
toRef(props, "status"),
|
||
idAttribute
|
||
);
|
||
const descriptionId = inject(FieldDescriptionIdKey, void 0);
|
||
const textareaClasses = computed(() => {
|
||
return {
|
||
"cdx-text-area__textarea--has-value": !!wrappedModel.value,
|
||
"cdx-text-area__textarea--is-autosize": props.autosize
|
||
};
|
||
});
|
||
const internalClasses = computed(() => {
|
||
return {
|
||
"cdx-text-area--status-default": computedStatus.value === "default",
|
||
"cdx-text-area--status-error": computedStatus.value === "error",
|
||
"cdx-text-area--has-start-icon": !!props.startIcon,
|
||
"cdx-text-area--has-end-icon": !!props.endIcon
|
||
};
|
||
});
|
||
const {
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrs
|
||
} = useSplitAttributes(attrs, internalClasses);
|
||
const otherAttrsMinusId = computed(() => {
|
||
const _a = otherAttrs.value, { id } = _a, everythingElse = __objRest(_a, ["id"]);
|
||
return everythingElse;
|
||
});
|
||
const textarea = ref();
|
||
function onInput() {
|
||
if (textarea.value && props.autosize) {
|
||
textarea.value.style.height = "auto";
|
||
textarea.value.style.height = "".concat(textarea.value.scrollHeight, "px");
|
||
}
|
||
}
|
||
return {
|
||
rootClasses,
|
||
rootStyle,
|
||
wrappedModel,
|
||
computedDisabled,
|
||
computedInputId,
|
||
descriptionId,
|
||
textareaClasses,
|
||
otherAttrsMinusId,
|
||
textarea,
|
||
onInput
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$3 = ["id", "aria-describedby", "disabled"];
|
||
function _sfc_render$3(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_icon = resolveComponent("cdx-icon");
|
||
return openBlock(), createElementBlock(
|
||
"div",
|
||
{
|
||
class: normalizeClass(["cdx-text-area", _ctx.rootClasses]),
|
||
style: normalizeStyle(_ctx.rootStyle)
|
||
},
|
||
[
|
||
withDirectives(createElementVNode("textarea", mergeProps({
|
||
id: _ctx.computedInputId,
|
||
ref: "textarea"
|
||
}, _ctx.otherAttrsMinusId, {
|
||
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => _ctx.wrappedModel = $event),
|
||
class: [_ctx.textareaClasses, "cdx-text-area__textarea"],
|
||
"aria-describedby": _ctx.descriptionId,
|
||
disabled: _ctx.computedDisabled,
|
||
onInput: _cache[1] || (_cache[1] = (...args) => _ctx.onInput && _ctx.onInput(...args))
|
||
}), null, 16, _hoisted_1$3), [
|
||
[vModelText, _ctx.wrappedModel]
|
||
]),
|
||
_ctx.startIcon ? (openBlock(), createBlock(_component_cdx_icon, {
|
||
key: 0,
|
||
icon: _ctx.startIcon,
|
||
class: "cdx-text-area__icon-vue cdx-text-area__start-icon"
|
||
}, null, 8, ["icon"])) : createCommentVNode("v-if", true),
|
||
_ctx.endIcon ? (openBlock(), createBlock(_component_cdx_icon, {
|
||
key: 1,
|
||
icon: _ctx.endIcon,
|
||
class: "cdx-text-area__icon-vue cdx-text-area__end-icon"
|
||
}, null, 8, ["icon"])) : createCommentVNode("v-if", true)
|
||
],
|
||
6
|
||
/* CLASS, STYLE */
|
||
);
|
||
}
|
||
const TextArea = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["render", _sfc_render$3]]);
|
||
const _sfc_main$2 = defineComponent({
|
||
name: "CdxToggleButtonGroup",
|
||
components: {
|
||
CdxIcon,
|
||
CdxToggleButton
|
||
},
|
||
props: {
|
||
/**
|
||
* Buttons to display. See the ButtonGroupItem type.
|
||
*/
|
||
buttons: {
|
||
type: Array,
|
||
required: true,
|
||
validator: (value) => Array.isArray(value) && value.length >= 1
|
||
},
|
||
/**
|
||
* Selected value, or array of selected values.
|
||
*
|
||
* If this is a string or number, the button whose value equals that string or number is
|
||
* selected, and only a single selection is allowed. If this is an array, the buttons whose
|
||
* values equal any of the values in the array are selected, and multiple selections are
|
||
* allowed. To select none of the buttons initially, set this to `null`
|
||
* (for single-selection groups) or to `[]` (for multi-selection groups).
|
||
*
|
||
* Must be bound with `v-model`.
|
||
*/
|
||
modelValue: {
|
||
type: [String, Number, null, Array],
|
||
required: true
|
||
},
|
||
/**
|
||
* Whether the entire group is disabled.
|
||
*
|
||
* If this is set to true, all buttons in the group are disabled. Buttons can also be
|
||
* disabled individually by setting their `disabled` property to true.
|
||
*/
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
},
|
||
emits: [
|
||
/**
|
||
* Emitted when modelValue changes.
|
||
*
|
||
* @property {string | number | ( string | number )[]} modelValue The new model value
|
||
*/
|
||
"update:modelValue"
|
||
],
|
||
setup(props, { emit }) {
|
||
const {
|
||
rootElement,
|
||
assignTemplateRef,
|
||
onFocus,
|
||
onBlur,
|
||
onKeydown
|
||
} = useButtonGroupKeyboardNav(toRef(props, "buttons"));
|
||
function isSelected(button) {
|
||
if (Array.isArray(props.modelValue)) {
|
||
return props.modelValue.indexOf(button.value) !== -1;
|
||
} else if (props.modelValue !== null) {
|
||
return props.modelValue === button.value;
|
||
}
|
||
return false;
|
||
}
|
||
function onUpdate(button, nowSelected) {
|
||
if (Array.isArray(props.modelValue)) {
|
||
const wasSelected = props.modelValue.indexOf(button.value) !== -1;
|
||
if (nowSelected && !wasSelected) {
|
||
emit("update:modelValue", props.modelValue.concat(button.value));
|
||
} else if (!nowSelected && wasSelected) {
|
||
emit("update:modelValue", props.modelValue.filter((v) => v !== button.value));
|
||
}
|
||
} else {
|
||
if (nowSelected && props.modelValue !== button.value) {
|
||
emit("update:modelValue", button.value);
|
||
}
|
||
}
|
||
}
|
||
return {
|
||
rootElement,
|
||
assignTemplateRef,
|
||
onFocus,
|
||
onBlur,
|
||
onKeydown,
|
||
getButtonLabel,
|
||
isSelected,
|
||
onUpdate
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$2 = {
|
||
ref: "rootElement",
|
||
class: "cdx-toggle-button-group"
|
||
};
|
||
function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_icon = resolveComponent("cdx-icon");
|
||
const _component_cdx_toggle_button = resolveComponent("cdx-toggle-button");
|
||
return openBlock(), createElementBlock(
|
||
"div",
|
||
_hoisted_1$2,
|
||
[
|
||
(openBlock(true), createElementBlock(
|
||
Fragment,
|
||
null,
|
||
renderList(_ctx.buttons, (button, index) => {
|
||
return openBlock(), createBlock(_component_cdx_toggle_button, {
|
||
key: button.value,
|
||
ref_for: true,
|
||
ref: (ref2) => _ctx.assignTemplateRef(ref2, index),
|
||
"model-value": _ctx.isSelected(button),
|
||
disabled: button.disabled || _ctx.disabled,
|
||
"aria-label": button.ariaLabel,
|
||
"onUpdate:modelValue": ($event) => _ctx.onUpdate(button, $event),
|
||
onFocus: ($event) => _ctx.onFocus(index),
|
||
onBlur: _ctx.onBlur,
|
||
onKeydown: _ctx.onKeydown
|
||
}, {
|
||
default: withCtx(() => [
|
||
renderSlot(_ctx.$slots, "default", {
|
||
button,
|
||
selected: _ctx.isSelected(button)
|
||
}, () => [
|
||
button.icon ? (openBlock(), createBlock(_component_cdx_icon, {
|
||
key: 0,
|
||
icon: button.icon
|
||
}, null, 8, ["icon"])) : createCommentVNode("v-if", true),
|
||
createTextVNode(
|
||
" " + toDisplayString(_ctx.getButtonLabel(button)),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
])
|
||
]),
|
||
_: 2
|
||
/* DYNAMIC */
|
||
}, 1032, ["model-value", "disabled", "aria-label", "onUpdate:modelValue", "onFocus", "onBlur", "onKeydown"]);
|
||
}),
|
||
128
|
||
/* KEYED_FRAGMENT */
|
||
))
|
||
],
|
||
512
|
||
/* NEED_PATCH */
|
||
);
|
||
}
|
||
const ToggleButtonGroup = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render$2]]);
|
||
const _sfc_main$1 = defineComponent({
|
||
name: "CdxToggleSwitch",
|
||
components: { CdxLabel },
|
||
/**
|
||
* The input element will inherit attributes, not the root element.
|
||
*/
|
||
inheritAttrs: false,
|
||
props: {
|
||
/**
|
||
* Current value of the toggle switch or toggle switch group.
|
||
*
|
||
* Provided by `v-model` binding in the parent component.
|
||
*/
|
||
modelValue: {
|
||
type: [Boolean, Array],
|
||
default: false
|
||
},
|
||
/**
|
||
* HTML "value" attribute to assign to the input element.
|
||
*
|
||
* Required for groups of ToggleSwitches. Can be omitted for single true/false switches.
|
||
*/
|
||
inputValue: {
|
||
type: [String, Number, Boolean],
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether to align the switch to the end of the container.
|
||
*
|
||
* Useful for ToggleSwitch groups, where each switch should be aligned regardless of
|
||
* label length.
|
||
*/
|
||
alignSwitch: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether the label should be visually hidden.
|
||
*
|
||
* Note that this will also hide the description.
|
||
*/
|
||
hideLabel: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether the disabled attribute should be added to the input.
|
||
*/
|
||
disabled: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
},
|
||
emits: [
|
||
/**
|
||
* Emitted when modelValue changes.
|
||
*
|
||
* @property {boolean} modelValue The new model value
|
||
*/
|
||
"update:modelValue"
|
||
],
|
||
setup(props, { emit, slots, attrs }) {
|
||
var _a;
|
||
useLabelChecker((_a = slots.default) == null ? void 0 : _a.call(slots), attrs, "CdxToggleSwitch");
|
||
const input = ref();
|
||
const inputId = useGeneratedId("toggle-switch");
|
||
const descriptionId = useGeneratedId("description");
|
||
const internalClasses = computed(() => {
|
||
return {
|
||
"cdx-toggle-switch--align-switch": props.alignSwitch
|
||
};
|
||
});
|
||
const {
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrs
|
||
} = useSplitAttributes(attrs, internalClasses);
|
||
const { computedDisabled } = useFieldData(toRef(props, "disabled"));
|
||
const wrappedModel = useModelWrapper(toRef(props, "modelValue"), emit);
|
||
const clickInput = () => {
|
||
input.value.click();
|
||
};
|
||
return {
|
||
input,
|
||
inputId,
|
||
descriptionId,
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrs,
|
||
computedDisabled,
|
||
wrappedModel,
|
||
clickInput
|
||
};
|
||
}
|
||
});
|
||
const _hoisted_1$1 = ["id", "aria-describedby", "value", "disabled"];
|
||
const _hoisted_2$1 = /* @__PURE__ */ createElementVNode(
|
||
"span",
|
||
{ class: "cdx-toggle-switch__switch" },
|
||
[
|
||
/* @__PURE__ */ createElementVNode("span", { class: "cdx-toggle-switch__switch__grip" })
|
||
],
|
||
-1
|
||
/* HOISTED */
|
||
);
|
||
function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_label = resolveComponent("cdx-label");
|
||
return openBlock(), createElementBlock(
|
||
"span",
|
||
{
|
||
class: normalizeClass(["cdx-toggle-switch", _ctx.rootClasses]),
|
||
style: normalizeStyle(_ctx.rootStyle)
|
||
},
|
||
[
|
||
withDirectives(createElementVNode("input", mergeProps({
|
||
id: _ctx.inputId,
|
||
ref: "input",
|
||
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => _ctx.wrappedModel = $event),
|
||
class: "cdx-toggle-switch__input",
|
||
type: "checkbox",
|
||
role: "switch",
|
||
"aria-describedby": _ctx.$slots.description && _ctx.$slots.description().length > 0 ? _ctx.descriptionId : void 0,
|
||
value: _ctx.inputValue,
|
||
disabled: _ctx.computedDisabled
|
||
}, _ctx.otherAttrs, {
|
||
onKeydown: _cache[1] || (_cache[1] = withKeys(withModifiers((...args) => _ctx.clickInput && _ctx.clickInput(...args), ["prevent"]), ["enter"]))
|
||
}), null, 16, _hoisted_1$1), [
|
||
[vModelCheckbox, _ctx.wrappedModel]
|
||
]),
|
||
_hoisted_2$1,
|
||
_ctx.$slots.default && _ctx.$slots.default().length ? (openBlock(), createBlock(_component_cdx_label, {
|
||
key: 0,
|
||
class: "cdx-toggle-switch__label",
|
||
"input-id": _ctx.inputId,
|
||
"description-id": _ctx.$slots.description && _ctx.$slots.description().length > 0 ? _ctx.descriptionId : void 0,
|
||
"visually-hidden": _ctx.hideLabel,
|
||
disabled: _ctx.computedDisabled
|
||
}, createSlots({
|
||
default: withCtx(() => [
|
||
renderSlot(_ctx.$slots, "default")
|
||
]),
|
||
_: 2
|
||
/* DYNAMIC */
|
||
}, [
|
||
_ctx.$slots.description && _ctx.$slots.description().length > 0 ? {
|
||
name: "description",
|
||
fn: withCtx(() => [
|
||
renderSlot(_ctx.$slots, "description")
|
||
]),
|
||
key: "0"
|
||
} : void 0
|
||
]), 1032, ["input-id", "description-id", "visually-hidden", "disabled"])) : createCommentVNode("v-if", true)
|
||
],
|
||
6
|
||
/* CLASS, STYLE */
|
||
);
|
||
}
|
||
const ToggleSwitch = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render$1]]);
|
||
class Tooltip {
|
||
constructor(referenceElement, options) {
|
||
__publicField(this, "referenceElement");
|
||
__publicField(this, "tooltipElement");
|
||
__publicField(this, "textContent");
|
||
__publicField(this, "placement");
|
||
__publicField(this, "autoUpdateCleanup");
|
||
__publicField(this, "referenceElementHandlers");
|
||
__publicField(this, "tooltipElementHandlers");
|
||
__publicField(this, "escapeHandler");
|
||
__publicField(this, "timeoutId");
|
||
var _a, _b;
|
||
const doc = referenceElement.ownerDocument;
|
||
const tooltipId = useGeneratedId("tooltip");
|
||
this.referenceElement = referenceElement;
|
||
this.textContent = options.textContent;
|
||
this.placement = (_a = options.placement) != null ? _a : "bottom";
|
||
this.timeoutId = null;
|
||
this.tooltipElement = doc.createElement("div");
|
||
this.tooltipElement.classList.add("cdx-tooltip");
|
||
this.tooltipElement.role = "tooltip";
|
||
this.tooltipElement.id = tooltipId;
|
||
this.referenceElement.setAttribute("aria-describedby", tooltipId);
|
||
this.tooltipElement.textContent = this.textContent;
|
||
(_b = this.referenceElement.parentElement) == null ? void 0 : _b.appendChild(this.tooltipElement);
|
||
this.referenceElementHandlers = {};
|
||
this.referenceElementHandlers.mouseenter = this.show.bind(this);
|
||
this.referenceElementHandlers.mouseleave = this.hideAfterDelay.bind(this);
|
||
this.referenceElementHandlers.focus = this.show.bind(this);
|
||
this.referenceElementHandlers.blur = this.hide.bind(this);
|
||
this.tooltipElementHandlers = {};
|
||
this.tooltipElementHandlers.mouseenter = this.show.bind(this);
|
||
this.tooltipElementHandlers.mouseleave = this.hideAfterDelay.bind(this);
|
||
this.escapeHandler = this.onKeyup.bind(this);
|
||
this.addEventListeners();
|
||
this.autoUpdateCleanup = autoUpdate(
|
||
this.referenceElement,
|
||
this.tooltipElement,
|
||
() => this.update()
|
||
);
|
||
}
|
||
isVisible() {
|
||
return this.tooltipElement.style.display === "block";
|
||
}
|
||
show() {
|
||
if (this.timeoutId) {
|
||
clearTimeout(this.timeoutId);
|
||
}
|
||
this.tooltipElement.style.display = "block";
|
||
this.tooltipElement.ownerDocument.addEventListener("keyup", this.escapeHandler);
|
||
}
|
||
hide() {
|
||
this.tooltipElement.style.display = "none";
|
||
this.tooltipElement.ownerDocument.removeEventListener("keyup", this.escapeHandler);
|
||
}
|
||
hideAfterDelay() {
|
||
this.timeoutId = setTimeout(this.hide.bind(this), 250);
|
||
}
|
||
onKeyup(event) {
|
||
if (event.key === "Escape" && this.isVisible()) {
|
||
this.hide();
|
||
}
|
||
}
|
||
addEventListeners() {
|
||
Object.keys(this.referenceElementHandlers).forEach((k) => {
|
||
this.referenceElement.addEventListener(k, this.referenceElementHandlers[k]);
|
||
});
|
||
Object.keys(this.tooltipElementHandlers).forEach((k) => {
|
||
this.tooltipElement.addEventListener(k, this.tooltipElementHandlers[k]);
|
||
});
|
||
}
|
||
removeEventListeners() {
|
||
Object.keys(this.referenceElementHandlers).forEach((k) => {
|
||
this.referenceElement.removeEventListener(k, this.referenceElementHandlers[k]);
|
||
});
|
||
Object.keys(this.tooltipElementHandlers).forEach((k) => {
|
||
this.tooltipElement.removeEventListener(k, this.tooltipElementHandlers[k]);
|
||
});
|
||
}
|
||
update() {
|
||
computePosition(this.referenceElement, this.tooltipElement, {
|
||
placement: this.placement,
|
||
middleware: [
|
||
offset(4),
|
||
flip(),
|
||
shift(),
|
||
hide()
|
||
]
|
||
}).then(({ x, y, middlewareData }) => {
|
||
var _a, _b, _c;
|
||
const finalPlacement = (_b = (_a = middlewareData.offset) == null ? void 0 : _a.placement) != null ? _b : this.placement;
|
||
const opposites = {
|
||
left: "right",
|
||
"left-start": "right",
|
||
"left-end": "right",
|
||
top: "bottom",
|
||
"top-start": "bottom",
|
||
"top-end": "bottom",
|
||
bottom: "top",
|
||
"bottom-start": "top",
|
||
"bottom-end": "top",
|
||
right: "left",
|
||
"right-start": "left",
|
||
"right-end": "left"
|
||
};
|
||
Object.assign(this.tooltipElement.style, {
|
||
left: "".concat(x, "px"),
|
||
top: "".concat(y, "px"),
|
||
visibility: ((_c = middlewareData.hide) == null ? void 0 : _c.referenceHidden) ? "hidden" : "visible",
|
||
transformOrigin: opposites[finalPlacement]
|
||
});
|
||
});
|
||
}
|
||
remove() {
|
||
this.tooltipElement.remove();
|
||
this.autoUpdateCleanup();
|
||
this.removeEventListeners();
|
||
}
|
||
}
|
||
const CdxTooltip = {
|
||
mounted(el, { value, arg }) {
|
||
el.tooltip = new Tooltip(el, {
|
||
textContent: String(value),
|
||
placement: arg
|
||
});
|
||
},
|
||
beforeUnmount(el) {
|
||
if (el.tooltip) {
|
||
el.tooltip.remove();
|
||
}
|
||
}
|
||
};
|
||
const _sfc_main = defineComponent({
|
||
name: "CdxTypeaheadSearch",
|
||
components: {
|
||
CdxIcon,
|
||
CdxMenu,
|
||
CdxSearchInput
|
||
},
|
||
/**
|
||
* Attributes, besides class, will be passed to the TextInput's input element.
|
||
*/
|
||
inheritAttrs: false,
|
||
props: {
|
||
/**
|
||
* ID attribute for the form.
|
||
*/
|
||
id: {
|
||
type: String,
|
||
required: true
|
||
},
|
||
/**
|
||
* Action attribute for form.
|
||
*/
|
||
formAction: {
|
||
type: String,
|
||
required: true
|
||
},
|
||
/**
|
||
* List of search results. See the SearchResult type.
|
||
*/
|
||
searchResults: {
|
||
type: Array,
|
||
required: true
|
||
},
|
||
/**
|
||
*
|
||
* Whether to display a submit button.
|
||
*/
|
||
useButton: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
// DEPRECATED: set default to 'Search' (T368444).
|
||
/**
|
||
* Custom label for the submit button.
|
||
*
|
||
* Omit this prop to use the default value, "Search".
|
||
*/
|
||
buttonLabel: {
|
||
type: String,
|
||
default: ""
|
||
},
|
||
/**
|
||
* Initial value for the text input.
|
||
*
|
||
* Triggers an initial `input` event on mount.
|
||
*/
|
||
initialInputValue: {
|
||
type: String,
|
||
default: ""
|
||
},
|
||
/**
|
||
* Link for the final menu item.
|
||
*
|
||
* This will typically be a link to the search page for the current search query.
|
||
*/
|
||
searchFooterUrl: {
|
||
type: String,
|
||
default: ""
|
||
},
|
||
/**
|
||
* Time interval for debouncing input events, in ms.
|
||
*/
|
||
debounceInterval: {
|
||
type: Number,
|
||
default: DebounceInterval
|
||
},
|
||
/**
|
||
* Whether the search query should be highlighted within a search result's title.
|
||
*/
|
||
highlightQuery: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Whether to show search results' thumbnails (or a placeholder icon).
|
||
*/
|
||
showThumbnail: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Contract the width of the input when unfocused and expand the width of
|
||
* the input when focused to accommodate the extra width of the thumbnails.
|
||
*
|
||
* This prop is ignored if showThumbnail is false.
|
||
*/
|
||
autoExpandWidth: {
|
||
type: Boolean,
|
||
default: false
|
||
},
|
||
/**
|
||
* Limit the number of menu items to display before scrolling.
|
||
*
|
||
* Setting this prop to anything falsy will show all menu items.
|
||
*
|
||
* By default, all menu items are shown.
|
||
*/
|
||
visibleItemLimit: {
|
||
type: Number,
|
||
default: null
|
||
}
|
||
},
|
||
emits: [
|
||
/**
|
||
* When the text input value changes. Debounced by default.
|
||
*
|
||
* @property {string} value The new input value
|
||
*/
|
||
"input",
|
||
/**
|
||
* When a search result is selected.
|
||
*
|
||
* @property {SearchResultClickEvent} event Data for the selected result
|
||
*/
|
||
"search-result-click",
|
||
/**
|
||
* When the form is submitted.
|
||
*
|
||
* @property {SearchResultClickEvent} event Data for the selected result
|
||
*/
|
||
"submit",
|
||
/**
|
||
* When the user scrolls towards the bottom of the menu.
|
||
*
|
||
* If it is possible to add or load more menu items, then now would be a good moment
|
||
* so that the user can experience infinite scrolling.
|
||
*/
|
||
"load-more"
|
||
],
|
||
setup(props, { attrs, emit, slots }) {
|
||
const form = ref();
|
||
const menu = ref();
|
||
const menuId = useGeneratedId("typeahead-search-menu");
|
||
const translatedSearchResultsLabel = useI18n("cdx-typeahead-search-search-results-label", "Search results");
|
||
const expanded = ref(false);
|
||
const pending = ref(false);
|
||
const showPending = ref(false);
|
||
const isActive = ref(false);
|
||
const inputValue = ref(props.initialInputValue);
|
||
const searchQuery = ref("");
|
||
const highlightedId = computed(() => {
|
||
var _a, _b;
|
||
return (_b = (_a = menu.value) == null ? void 0 : _a.getHighlightedMenuItem()) == null ? void 0 : _b.id;
|
||
});
|
||
const selection = ref(null);
|
||
const menuMessageClass = computed(() => ({
|
||
"cdx-typeahead-search__menu-message--has-thumbnail": props.showThumbnail
|
||
}));
|
||
const selectedResult = computed(
|
||
() => props.searchResults.find(
|
||
(searchResult) => searchResult.value === selection.value
|
||
)
|
||
);
|
||
const footer = computed(
|
||
() => props.searchFooterUrl ? { value: MenuFooterValue, url: props.searchFooterUrl } : void 0
|
||
);
|
||
const internalClasses = computed(() => {
|
||
return {
|
||
"cdx-typeahead-search--show-thumbnail": props.showThumbnail,
|
||
"cdx-typeahead-search--expanded": expanded.value,
|
||
"cdx-typeahead-search--auto-expand-width": props.showThumbnail && props.autoExpandWidth
|
||
};
|
||
});
|
||
const {
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrs
|
||
} = useSplitAttributes(attrs, internalClasses);
|
||
function asSearchResult(menuItem) {
|
||
return menuItem;
|
||
}
|
||
const menuConfig = computed(() => {
|
||
return {
|
||
visibleItemLimit: props.visibleItemLimit,
|
||
showThumbnail: props.showThumbnail,
|
||
// In case search queries aren't highlighted, default to a bold label.
|
||
boldLabel: true,
|
||
hideDescriptionOverflow: true
|
||
};
|
||
});
|
||
let debounceId;
|
||
let pendingDelayId;
|
||
function onUpdateInputValue(newVal, immediate = false) {
|
||
if (selectedResult.value && selectedResult.value.label !== newVal && selectedResult.value.value !== newVal) {
|
||
selection.value = null;
|
||
}
|
||
if (pendingDelayId !== void 0) {
|
||
clearTimeout(pendingDelayId);
|
||
pendingDelayId = void 0;
|
||
}
|
||
if (newVal === "") {
|
||
expanded.value = false;
|
||
} else {
|
||
pending.value = true;
|
||
if (slots["search-results-pending"]) {
|
||
pendingDelayId = setTimeout(() => {
|
||
if (isActive.value) {
|
||
expanded.value = true;
|
||
}
|
||
showPending.value = true;
|
||
}, PendingDelay);
|
||
}
|
||
}
|
||
if (debounceId !== void 0) {
|
||
clearTimeout(debounceId);
|
||
debounceId = void 0;
|
||
}
|
||
const handleUpdateInputValue = () => {
|
||
emit("input", newVal);
|
||
};
|
||
if (immediate) {
|
||
handleUpdateInputValue();
|
||
} else {
|
||
debounceId = setTimeout(() => {
|
||
handleUpdateInputValue();
|
||
}, props.debounceInterval);
|
||
}
|
||
}
|
||
function onUpdateMenuSelection(newVal) {
|
||
var _a;
|
||
if (newVal === MenuFooterValue) {
|
||
selection.value = null;
|
||
inputValue.value = searchQuery.value;
|
||
return;
|
||
}
|
||
selection.value = newVal;
|
||
if (newVal !== null) {
|
||
inputValue.value = selectedResult.value ? (_a = selectedResult.value.label) != null ? _a : String(selectedResult.value.value) : "";
|
||
}
|
||
}
|
||
function onFocus() {
|
||
isActive.value = true;
|
||
if (searchQuery.value || showPending.value) {
|
||
expanded.value = true;
|
||
}
|
||
}
|
||
function onBlur() {
|
||
isActive.value = false;
|
||
expanded.value = false;
|
||
}
|
||
function onSearchResultClick(searchResult) {
|
||
const _a = searchResult, { id } = _a, resultWithoutId = __objRest(_a, ["id"]);
|
||
if (resultWithoutId.value === MenuFooterValue) {
|
||
emit("search-result-click", {
|
||
searchResult: null,
|
||
index: props.searchResults.length,
|
||
numberOfResults: props.searchResults.length
|
||
});
|
||
return;
|
||
}
|
||
emitSearchResultClick(resultWithoutId);
|
||
}
|
||
function emitSearchResultClick(searchResult) {
|
||
const searchResultClickEvent = {
|
||
searchResult,
|
||
index: props.searchResults.findIndex(
|
||
(r) => r.value === searchResult.value
|
||
),
|
||
numberOfResults: props.searchResults.length
|
||
};
|
||
emit("search-result-click", searchResultClickEvent);
|
||
}
|
||
function onSearchResultKeyboardNavigation(searchResult) {
|
||
var _a;
|
||
if (searchResult.value === MenuFooterValue) {
|
||
inputValue.value = searchQuery.value;
|
||
return;
|
||
}
|
||
inputValue.value = searchResult.value ? (_a = searchResult.label) != null ? _a : String(searchResult.value) : "";
|
||
}
|
||
function onSearchFooterClick(footerMenuItem) {
|
||
var _a;
|
||
expanded.value = false;
|
||
(_a = menu.value) == null ? void 0 : _a.clearActive();
|
||
onSearchResultClick(footerMenuItem);
|
||
}
|
||
function onSubmit(e) {
|
||
if (selectedResult.value) {
|
||
emitSearchResultClick(selectedResult.value);
|
||
e.stopPropagation();
|
||
window.location.assign(selectedResult.value.url);
|
||
e.preventDefault();
|
||
} else {
|
||
const submitEvent = {
|
||
searchResult: null,
|
||
index: -1,
|
||
numberOfResults: props.searchResults.length
|
||
};
|
||
emit("submit", submitEvent);
|
||
}
|
||
}
|
||
function onKeydown(e) {
|
||
if (!menu.value || !searchQuery.value || e.key === " ") {
|
||
return;
|
||
}
|
||
const highlightedResult = menu.value.getHighlightedMenuItem();
|
||
const resultHighlightedViaKeyboard = menu.value.getHighlightedViaKeyboard();
|
||
switch (e.key) {
|
||
case "Enter":
|
||
if (highlightedResult) {
|
||
if (highlightedResult.value === MenuFooterValue && resultHighlightedViaKeyboard) {
|
||
window.location.assign(props.searchFooterUrl);
|
||
} else {
|
||
menu.value.delegateKeyNavigation(e, { prevent: false });
|
||
}
|
||
}
|
||
expanded.value = false;
|
||
break;
|
||
case "Tab":
|
||
expanded.value = false;
|
||
break;
|
||
default:
|
||
menu.value.delegateKeyNavigation(e);
|
||
break;
|
||
}
|
||
}
|
||
onMounted(() => {
|
||
if (props.initialInputValue) {
|
||
onUpdateInputValue(props.initialInputValue, true);
|
||
}
|
||
});
|
||
watch(toRef(props, "searchResults"), () => {
|
||
searchQuery.value = inputValue.value.trim();
|
||
if (isActive.value && pending.value && searchQuery.value.length > 0) {
|
||
expanded.value = true;
|
||
}
|
||
if (pendingDelayId !== void 0) {
|
||
clearTimeout(pendingDelayId);
|
||
pendingDelayId = void 0;
|
||
}
|
||
pending.value = false;
|
||
showPending.value = false;
|
||
});
|
||
return {
|
||
form,
|
||
menu,
|
||
menuId,
|
||
highlightedId,
|
||
selection,
|
||
menuMessageClass,
|
||
footer,
|
||
asSearchResult,
|
||
inputValue,
|
||
searchQuery,
|
||
expanded,
|
||
showPending,
|
||
rootClasses,
|
||
rootStyle,
|
||
otherAttrs,
|
||
menuConfig,
|
||
onUpdateInputValue,
|
||
onUpdateMenuSelection,
|
||
onFocus,
|
||
onBlur,
|
||
onSearchResultClick,
|
||
onSearchResultKeyboardNavigation,
|
||
onSearchFooterClick,
|
||
onSubmit,
|
||
onKeydown,
|
||
MenuFooterValue,
|
||
articleIcon: S3,
|
||
translatedSearchResultsLabel
|
||
};
|
||
},
|
||
methods: {
|
||
/**
|
||
* Focus the component's input element.
|
||
*
|
||
* @public
|
||
*/
|
||
focus() {
|
||
const searchInput = this.$refs.searchInput;
|
||
searchInput.focus();
|
||
}
|
||
}
|
||
});
|
||
const _hoisted_1 = ["id", "action"];
|
||
const _hoisted_2 = { class: "cdx-typeahead-search__menu-message__text" };
|
||
const _hoisted_3 = { class: "cdx-typeahead-search__menu-message__text" };
|
||
const _hoisted_4 = ["href", "onClickCapture"];
|
||
const _hoisted_5 = { class: "cdx-menu-item__text cdx-typeahead-search__search-footer__text" };
|
||
const _hoisted_6 = { class: "cdx-typeahead-search__search-footer__query" };
|
||
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
|
||
const _component_cdx_icon = resolveComponent("cdx-icon");
|
||
const _component_cdx_menu = resolveComponent("cdx-menu");
|
||
const _component_cdx_search_input = resolveComponent("cdx-search-input");
|
||
return openBlock(), createElementBlock(
|
||
"div",
|
||
{
|
||
class: normalizeClass(["cdx-typeahead-search", _ctx.rootClasses]),
|
||
style: normalizeStyle(_ctx.rootStyle)
|
||
},
|
||
[
|
||
createElementVNode("form", {
|
||
id: _ctx.id,
|
||
ref: "form",
|
||
class: "cdx-typeahead-search__form",
|
||
action: _ctx.formAction,
|
||
onSubmit: _cache[4] || (_cache[4] = (...args) => _ctx.onSubmit && _ctx.onSubmit(...args))
|
||
}, [
|
||
createVNode(_component_cdx_search_input, mergeProps({
|
||
ref: "searchInput",
|
||
modelValue: _ctx.inputValue,
|
||
"onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => _ctx.inputValue = $event),
|
||
"button-label": _ctx.buttonLabel,
|
||
"use-button": _ctx.useButton
|
||
}, _ctx.otherAttrs, {
|
||
class: "cdx-typeahead-search__input",
|
||
name: "search",
|
||
role: "combobox",
|
||
autocomplete: "off",
|
||
"aria-autocomplete": "list",
|
||
"aria-controls": _ctx.menuId,
|
||
"aria-expanded": _ctx.expanded,
|
||
"aria-activedescendant": _ctx.highlightedId,
|
||
"onUpdate:modelValue": _ctx.onUpdateInputValue,
|
||
onFocus: _ctx.onFocus,
|
||
onBlur: _ctx.onBlur,
|
||
onKeydown: _ctx.onKeydown
|
||
}), {
|
||
default: withCtx(() => [
|
||
createVNode(_component_cdx_menu, mergeProps({
|
||
id: _ctx.menuId,
|
||
ref: "menu",
|
||
expanded: _ctx.expanded,
|
||
"onUpdate:expanded": _cache[0] || (_cache[0] = ($event) => _ctx.expanded = $event),
|
||
class: "cdx-typeahead-search__menu",
|
||
"show-pending": _ctx.showPending,
|
||
selected: _ctx.selection,
|
||
"menu-items": _ctx.searchResults,
|
||
footer: _ctx.footer,
|
||
"search-query": _ctx.highlightQuery ? _ctx.searchQuery : "",
|
||
"show-no-results-slot": _ctx.searchQuery.length > 0 && _ctx.searchResults.length === 0 && _ctx.$slots["search-no-results-text"] && _ctx.$slots["search-no-results-text"]().length > 0
|
||
}, _ctx.menuConfig, {
|
||
"aria-label": _ctx.translatedSearchResultsLabel,
|
||
"onUpdate:selected": _ctx.onUpdateMenuSelection,
|
||
onMenuItemClick: _cache[1] || (_cache[1] = (menuItem) => _ctx.onSearchResultClick(_ctx.asSearchResult(menuItem))),
|
||
onMenuItemKeyboardNavigation: _ctx.onSearchResultKeyboardNavigation,
|
||
onLoadMore: _cache[2] || (_cache[2] = ($event) => _ctx.$emit("load-more"))
|
||
}), {
|
||
pending: withCtx(() => [
|
||
createElementVNode(
|
||
"div",
|
||
{
|
||
class: normalizeClass(["cdx-menu-item__content cdx-typeahead-search__menu-message", _ctx.menuMessageClass])
|
||
},
|
||
[
|
||
createElementVNode("span", _hoisted_2, [
|
||
renderSlot(_ctx.$slots, "search-results-pending")
|
||
])
|
||
],
|
||
2
|
||
/* CLASS */
|
||
)
|
||
]),
|
||
"no-results": withCtx(() => [
|
||
createElementVNode(
|
||
"div",
|
||
{
|
||
class: normalizeClass(["cdx-menu-item__content cdx-typeahead-search__menu-message", _ctx.menuMessageClass])
|
||
},
|
||
[
|
||
createElementVNode("span", _hoisted_3, [
|
||
renderSlot(_ctx.$slots, "search-no-results-text")
|
||
])
|
||
],
|
||
2
|
||
/* CLASS */
|
||
)
|
||
]),
|
||
default: withCtx(({ menuItem, active }) => [
|
||
menuItem.value === _ctx.MenuFooterValue ? (openBlock(), createElementBlock("a", {
|
||
key: 0,
|
||
class: normalizeClass(["cdx-menu-item__content cdx-typeahead-search__search-footer", {
|
||
"cdx-typeahead-search__search-footer__active": active
|
||
}]),
|
||
href: _ctx.asSearchResult(menuItem).url,
|
||
onClickCapture: withModifiers(($event) => _ctx.onSearchFooterClick(_ctx.asSearchResult(menuItem)), ["stop"])
|
||
}, [
|
||
createVNode(_component_cdx_icon, {
|
||
class: "cdx-menu-item__thumbnail cdx-typeahead-search__search-footer__icon",
|
||
icon: _ctx.articleIcon
|
||
}, null, 8, ["icon"]),
|
||
createElementVNode("span", _hoisted_5, [
|
||
renderSlot(_ctx.$slots, "search-footer-text", { searchQuery: _ctx.searchQuery }, () => [
|
||
createElementVNode(
|
||
"strong",
|
||
_hoisted_6,
|
||
toDisplayString(_ctx.searchQuery),
|
||
1
|
||
/* TEXT */
|
||
)
|
||
])
|
||
])
|
||
], 42, _hoisted_4)) : createCommentVNode("v-if", true)
|
||
]),
|
||
_: 3
|
||
/* FORWARDED */
|
||
}, 16, ["id", "expanded", "show-pending", "selected", "menu-items", "footer", "search-query", "show-no-results-slot", "aria-label", "onUpdate:selected", "onMenuItemKeyboardNavigation"])
|
||
]),
|
||
_: 3
|
||
/* FORWARDED */
|
||
}, 16, ["modelValue", "button-label", "use-button", "aria-controls", "aria-expanded", "aria-activedescendant", "onUpdate:modelValue", "onFocus", "onBlur", "onKeydown"]),
|
||
renderSlot(_ctx.$slots, "default")
|
||
], 40, _hoisted_1)
|
||
],
|
||
6
|
||
/* CLASS, STYLE */
|
||
);
|
||
}
|
||
const TypeaheadSearch = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]);
|
||
export {
|
||
Accordion as CdxAccordion,
|
||
CdxButton,
|
||
ButtonGroup as CdxButtonGroup,
|
||
Card as CdxCard,
|
||
CdxCheckbox,
|
||
CdxChipInput,
|
||
Combobox as CdxCombobox,
|
||
Dialog as CdxDialog,
|
||
Field as CdxField,
|
||
CdxIcon,
|
||
InfoChip as CdxInfoChip,
|
||
CdxLabel,
|
||
Lookup as CdxLookup,
|
||
CdxMenu,
|
||
MenuButton as CdxMenuButton,
|
||
CdxMenuItem,
|
||
CdxMessage,
|
||
MultiselectLookup as CdxMultiselectLookup,
|
||
CdxProgressBar,
|
||
Radio as CdxRadio,
|
||
CdxSearchInput,
|
||
CdxSearchResultTitle,
|
||
CdxSelect,
|
||
CdxTab,
|
||
Table as CdxTable,
|
||
Tabs as CdxTabs,
|
||
TextArea as CdxTextArea,
|
||
CdxTextInput,
|
||
CdxThumbnail,
|
||
CdxToggleButton,
|
||
ToggleButtonGroup as CdxToggleButtonGroup,
|
||
ToggleSwitch as CdxToggleSwitch,
|
||
CdxTooltip,
|
||
TypeaheadSearch as CdxTypeaheadSearch,
|
||
TableRowIdentifier,
|
||
stringHelpers,
|
||
useComputedDirection,
|
||
useComputedDisabled,
|
||
useComputedLanguage,
|
||
useFieldData,
|
||
useFloatingMenu,
|
||
useGeneratedId,
|
||
useI18n,
|
||
useIntersectionObserver,
|
||
useModelWrapper,
|
||
useResizeObserver,
|
||
useSlotContents,
|
||
useSplitAttributes,
|
||
useWarnOnce
|
||
};
|