buefy
Version:
Lightweight UI components for Vue.js (v3) based on Bulma
1,512 lines (1,496 loc) • 152 kB
JavaScript
/*! Buefy v3.0.2 | MIT License | github.com/buefy/buefy */
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('vue')) :
typeof define === 'function' && define.amd ? define(['exports', 'vue'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Datepicker = {}, global.Vue));
})(this, (function (exports, vue) { 'use strict';
let config = {
defaultIconPack: "mdi",
defaultIconComponent: null,
defaultIconPrev: "chevron-left",
defaultIconNext: "chevron-right",
defaultLocale: void 0,
defaultInputAutocomplete: "on",
defaultDayNames: null,
defaultMonthNames: null,
defaultUnselectableDaysOfWeek: null,
defaultDatepickerMobileNative: true,
defaultInputHasCounter: true,
defaultCompatFallthrough: true,
defaultUseHtml5Validation: true,
defaultDropdownMobileModal: true,
defaultFieldLabelPosition: null,
defaultDatepickerYearsRange: [-100, 10],
defaultDatepickerNearbyMonthDays: true,
defaultDatepickerNearbySelectableMonthDays: false,
defaultDatepickerShowWeekNumber: false,
defaultDatepickerWeekNumberClickable: false,
defaultDatepickerMobileModal: true,
defaultTrapFocus: true,
defaultStatusIcon: true};
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
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 CompatFallthroughMixin = vue.defineComponent({
inheritAttrs: false,
props: {
compatFallthrough: {
type: Boolean,
default: () => config.defaultCompatFallthrough
}
},
computed: {
rootAttrs() {
return this.compatFallthrough ? {
class: this.$attrs.class,
style: this.$attrs.style,
id: this.$attrs.id
} : {};
},
fallthroughAttrs() {
if (this.compatFallthrough) {
const _a = this.$attrs, { style: _1, class: _2, id: _3 } = _a, rest = __objRest(_a, ["style", "class", "id"]);
return rest;
} else {
return this.$attrs;
}
}
}
});
const isMobile = {
Android: function() {
return typeof window !== "undefined" && window.navigator.userAgent.match(/Android/i);
},
BlackBerry: function() {
return typeof window !== "undefined" && window.navigator.userAgent.match(/BlackBerry/i);
},
iOS: function() {
return typeof window !== "undefined" && (window.navigator.userAgent.match(/iPhone|iPad|iPod/i) || window.navigator.platform === "MacIntel" && window.navigator.maxTouchPoints > 1);
},
Opera: function() {
return typeof window !== "undefined" && window.navigator.userAgent.match(/Opera Mini/i);
},
Windows: function() {
return typeof window !== "undefined" && window.navigator.userAgent.match(/IEMobile/i);
},
any: function() {
return isMobile.Android() || isMobile.BlackBerry() || isMobile.iOS() || isMobile.Opera() || isMobile.Windows();
}
};
function removeElement(el) {
if (typeof el.remove !== "undefined") {
el.remove();
} else if (typeof el.parentNode !== "undefined" && el.parentNode !== null) {
el.parentNode.removeChild(el);
}
}
function createAbsoluteElement(el) {
const root = document.createElement("div");
root.style.position = "absolute";
root.style.left = "0px";
root.style.top = "0px";
root.style.width = "100%";
const wrapper = document.createElement("div");
root.appendChild(wrapper);
wrapper.appendChild(el);
document.body.appendChild(root);
return root;
}
function toCssWidth(width) {
return width === void 0 ? null : isNaN(+width) ? `${width}` : width + "px";
}
function getMonthNames(locale, format = "long") {
const dates = [];
for (let i = 0; i < 12; i++) {
dates.push(new Date(2e3, i, 15));
}
const dtf = new Intl.DateTimeFormat(locale, {
month: format
});
return dates.map((d) => dtf.format(d));
}
function getWeekdayNames(locale, format = "narrow") {
const dates = [];
for (let i = 0; i < 7; i++) {
const dt = new Date(2e3, 0, i + 1);
dates[dt.getDay()] = dt;
}
const dtf = new Intl.DateTimeFormat(locale, { weekday: format });
return dates.map((d) => dtf.format(d));
}
function matchWithGroups(pattern, str) {
const matches = str.match(pattern);
const groupNames = pattern.toString().match(/<(.+?)>/g);
if (groupNames == null) {
throw new RangeError("pattern must contain at least one group");
}
return groupNames.map((group) => {
const groupMatches = group.match(/<(.+)>/);
return groupMatches[1];
}).reduce((acc, curr, index) => {
if (matches && matches.length > index) {
acc[curr] = matches[index + 1];
} else {
acc[curr] = null;
}
return acc;
}, {});
}
function isCustomElement(vm) {
return vm.$root != null && "shadowRoot" in vm.$root.$options;
}
const isDefined = (d) => d !== void 0;
function isTag(vnode) {
return vnode.type !== vue.Comment && vnode.type !== vue.Text && vnode.type !== vue.Static;
}
var _sfc_main$9 = vue.defineComponent({
name: "BFieldBody",
inject: {
parent: {
from: "BField",
default: null
}
},
props: {
message: {
type: [String, Array]
},
type: {
type: [String, Object]
}
},
render() {
let first = true;
let children = typeof this.$slots.default === "function" ? this.$slots.default() : this.$slots.default;
if (children != null && children.length === 1 && children[0].type === vue.Fragment) {
children = children[0].children;
}
return vue.h(
"div",
{ class: "field-body" },
{
default: () => {
return children != null && children.map((element) => {
if (element.type === vue.Comment || element.type === vue.Text) {
return element;
}
let message;
if (first) {
message = this.message;
first = false;
}
const parentField = this.parent;
return vue.h(
// parentField.$.type is supposed to be BField
// it falls back to `resolveComponent('b-field')`
// but won't work unless `BField` is globally registered
// should not be a problem as long as `BFieldBody` is properly used
parentField ? parentField.$.type : vue.resolveComponent("b-field"),
{
type: this.type,
message
},
() => element
);
});
}
}
);
}
});
const Field = vue.defineComponent({
name: "BField",
components: { BFieldBody: _sfc_main$9 },
provide() {
return {
BField: this
};
},
inject: {
parent: {
from: "BField",
default: false
}
},
// Used internally only when using Field in Field
props: {
type: {
type: [String, Object],
default: void 0
},
label: String,
labelFor: String,
message: {
type: [String, Array, Object],
default: void 0
},
grouped: Boolean,
groupMultiline: Boolean,
position: String,
expanded: Boolean,
horizontal: Boolean,
addons: {
type: Boolean,
default: true
},
customClass: String,
labelPosition: {
type: String,
default: () => {
return config.defaultFieldLabelPosition;
}
}
},
data() {
return {
newType: this.type,
newMessage: this.message,
fieldLabelSize: null,
numberInputClasses: [],
_isField: true
// Used internally by Input and Select
};
},
computed: {
rootClasses() {
return [
{
"is-expanded": this.expanded,
"is-horizontal": this.horizontal,
"is-floating-in-label": this.hasLabel && !this.horizontal && this.labelPosition === "inside",
"is-floating-label": this.hasLabel && !this.horizontal && this.labelPosition === "on-border"
},
this.numberInputClasses
];
},
innerFieldClasses() {
return [
this.fieldType(),
this.newPosition,
{
"is-grouped-multiline": this.groupMultiline
}
];
},
hasInnerField() {
return this.grouped || this.groupMultiline || this.hasAddons();
},
/*
* Correct Bulma class for the side of the addon or group.
*
* This is not kept like the others (is-small, etc.),
* because since 'has-addons' is set automatically it
* doesn't make sense to teach users what addons are exactly.
*/
newPosition() {
if (this.position === void 0) return;
const position = this.position.split("-");
if (position.length < 1) return;
const prefix = this.grouped ? "is-grouped-" : "has-addons-";
if (this.position) return prefix + position[1];
return void 0;
},
/*
* Formatted message in case it's an array
* (each element is separated by <br> tag)
*/
formattedMessage() {
const parentField = this.parent;
if (parentField && parentField.hasInnerField) {
return "";
}
if (typeof this.newMessage === "string") {
return [this.newMessage];
}
const messages = [];
if (Array.isArray(this.newMessage)) {
this.newMessage.forEach((message) => {
if (typeof message === "string") {
messages.push(message);
} else {
for (const key in message) {
if (message[key]) {
messages.push(key);
}
}
}
});
} else {
for (const key in this.newMessage) {
if (this.newMessage[key]) {
messages.push(key);
}
}
}
return messages.filter((m) => !!m);
},
hasLabel() {
return this.label || this.$slots.label;
},
hasMessage() {
const parentField = this.parent;
return (!parentField || !parentField.hasInnerField) && this.newMessage || this.$slots.message;
}
},
watch: {
/*
* Set internal type when prop change.
*/
type(value) {
this.newType = value;
},
/*
* Set internal message when prop change.
*/
message(value) {
if (JSON.stringify(value) !== JSON.stringify(this.newMessage)) {
this.newMessage = value;
}
},
/*
* Set parent message if we use Field in Field.
*/
newMessage(value) {
const parentField = this.parent;
if (parentField && parentField.hasInnerField) {
if (!parentField.type) {
parentField.newType = this.newType;
}
if (!parentField.message) {
parentField.newMessage = value;
}
}
}
},
methods: {
/*
* Field has addons if there are more than one slot
* (element / component) in the Field.
* Or is grouped when prop is set.
* Is a method to be called when component re-render.
*/
fieldType() {
if (this.grouped) return "is-grouped";
if (this.hasAddons()) return "has-addons";
},
hasAddons() {
let renderedNode = 0;
if (this.$slots.default) {
renderedNode = this.$slots.default().reduce((i, node) => isTag(node) ? i + 1 : i, 0);
}
return renderedNode > 1 && this.addons && !this.horizontal;
},
// called by a number input if it is a direct child.
wrapNumberinput({ controlsPosition, size }) {
const classes = ["has-numberinput"];
if (controlsPosition) {
classes.push(`has-numberinput-${controlsPosition}`);
}
if (size) {
classes.push(`has-numberinput-${size}`);
}
this.numberInputClasses = classes;
}
},
mounted() {
if (this.horizontal) {
const elements = this.$el.querySelectorAll(".input, .select, .button, .textarea, .b-slider");
if (elements.length > 0) {
this.fieldLabelSize = "is-normal";
}
}
}
});
var _export_sfc = (sfc, props) => {
const target = sfc.__vccOpts || sfc;
for (const [key, val] of props) {
target[key] = val;
}
return target;
};
const _hoisted_1$8 = ["for"];
const _hoisted_2$8 = ["for"];
const _hoisted_3$6 = {
key: 3,
class: "field-body"
};
function _sfc_render$9(_ctx, _cache, $props, $setup, $data, $options) {
const _component_b_field_body = vue.resolveComponent("b-field-body");
const _component_b_field = vue.resolveComponent("b-field");
return vue.openBlock(), vue.createElementBlock(
"div",
{
class: vue.normalizeClass(["field", _ctx.rootClasses])
},
[
_ctx.horizontal ? (vue.openBlock(), vue.createElementBlock(
"div",
{
key: 0,
class: vue.normalizeClass(["field-label", [_ctx.customClass, _ctx.fieldLabelSize]])
},
[
_ctx.hasLabel ? (vue.openBlock(), vue.createElementBlock("label", {
key: 0,
for: _ctx.labelFor,
class: vue.normalizeClass([_ctx.customClass, "label"])
}, [
_ctx.$slots.label ? vue.renderSlot(_ctx.$slots, "label", { key: 0 }) : (vue.openBlock(), vue.createElementBlock(
vue.Fragment,
{ key: 1 },
[
vue.createTextVNode(
vue.toDisplayString(_ctx.label),
1
/* TEXT */
)
],
64
/* STABLE_FRAGMENT */
))
], 10, _hoisted_1$8)) : vue.createCommentVNode("v-if", true)
],
2
/* CLASS */
)) : (vue.openBlock(), vue.createElementBlock(
vue.Fragment,
{ key: 1 },
[
_ctx.hasLabel ? (vue.openBlock(), vue.createElementBlock("label", {
key: 0,
for: _ctx.labelFor,
class: vue.normalizeClass([_ctx.customClass, "label"])
}, [
_ctx.$slots.label ? vue.renderSlot(_ctx.$slots, "label", { key: 0 }) : (vue.openBlock(), vue.createElementBlock(
vue.Fragment,
{ key: 1 },
[
vue.createTextVNode(
vue.toDisplayString(_ctx.label),
1
/* TEXT */
)
],
64
/* STABLE_FRAGMENT */
))
], 10, _hoisted_2$8)) : vue.createCommentVNode("v-if", true)
],
64
/* STABLE_FRAGMENT */
)),
_ctx.horizontal ? (vue.openBlock(), vue.createBlock(_component_b_field_body, {
key: 2,
message: _ctx.newMessage ? _ctx.formattedMessage : "",
type: _ctx.newType
}, {
default: vue.withCtx(() => [
vue.renderSlot(_ctx.$slots, "default")
]),
_: 3
/* FORWARDED */
}, 8, ["message", "type"])) : _ctx.hasInnerField ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_3$6, [
vue.createVNode(_component_b_field, {
addons: false,
type: _ctx.type,
class: vue.normalizeClass(_ctx.innerFieldClasses)
}, {
default: vue.withCtx(() => [
vue.renderSlot(_ctx.$slots, "default")
]),
_: 3
/* FORWARDED */
}, 8, ["type", "class"])
])) : vue.renderSlot(_ctx.$slots, "default", { key: 4 }),
_ctx.hasMessage && !_ctx.horizontal ? (vue.openBlock(), vue.createElementBlock(
"p",
{
key: 5,
class: vue.normalizeClass(["help", _ctx.newType])
},
[
_ctx.$slots.message ? vue.renderSlot(_ctx.$slots, "message", {
key: 0,
messages: _ctx.formattedMessage
}) : (vue.openBlock(true), vue.createElementBlock(
vue.Fragment,
{ key: 1 },
vue.renderList(_ctx.formattedMessage, (mess, i) => {
return vue.openBlock(), vue.createElementBlock(
vue.Fragment,
null,
[
vue.createTextVNode(
vue.toDisplayString(mess) + " ",
1
/* TEXT */
),
i + 1 < _ctx.formattedMessage.length ? (vue.openBlock(), vue.createElementBlock("br", { key: i })) : vue.createCommentVNode("v-if", true)
],
64
/* STABLE_FRAGMENT */
);
}),
256
/* UNKEYED_FRAGMENT */
))
],
2
/* CLASS */
)) : vue.createCommentVNode("v-if", true)
],
2
/* CLASS */
);
}
var BField = /* @__PURE__ */ _export_sfc(Field, [["render", _sfc_render$9]]);
const FormElementMixin = vue.defineComponent({
props: {
size: String,
expanded: Boolean,
loading: Boolean,
rounded: Boolean,
icon: String,
iconPack: String,
maxlength: [Number, String],
useHtml5Validation: {
type: Boolean,
default: () => config.defaultUseHtml5Validation
},
validationMessage: String,
locale: {
type: [String, Array],
default: () => {
return config.defaultLocale;
}
},
statusIcon: {
type: Boolean,
default: () => {
return config.defaultStatusIcon;
}
}
},
emits: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
blur: (event) => true,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
focus: (event) => true
},
data() {
return {
isValid: true,
isFocused: false,
newIconPack: this.iconPack || config.defaultIconPack,
// host component must override this
_elementRef: ""
};
},
computed: {
/*
* Find parent Field, max 3 levels deep.
*/
parentField() {
let parent = this.$parent;
for (let i = 0; i < 3; i++) {
if (parent && !parent.$data._isField) {
parent = parent.$parent;
}
}
return parent;
},
/*
* Get the type prop from parent if it's a Field.
*/
statusType() {
const { newType } = this.parentField || {};
if (!newType) return;
if (typeof newType === "string") {
return newType;
} else {
for (const key in newType) {
if (newType[key]) {
return key;
}
}
}
return void 0;
},
/*
* Get the message prop from parent if it's a Field.
*/
statusMessage() {
if (!this.parentField) return;
return this.parentField.newMessage || this.parentField.$slots.message;
},
/*
* Fix icon size for inputs, large was too big
*/
iconSize() {
switch (this.size) {
case "is-small":
return this.size;
case "is-medium":
return;
case "is-large":
return this.newIconPack === "mdi" ? "is-medium" : "";
}
return void 0;
}
},
methods: {
/*
* Focus method that work dynamically depending on the component.
*/
focus() {
const el = this.getElement();
if (el === void 0) return;
this.$nextTick(() => {
if (el) el.focus();
});
},
onBlur($event) {
this.isFocused = false;
this.$emit("blur", $event);
this.checkHtml5Validity();
},
onFocus($event) {
this.isFocused = true;
this.$emit("focus", $event);
},
getElement() {
let el = this.$refs[this.$data._elementRef];
while (el != null && typeof el === "object" && "$refs" in el) {
const form = el;
el = form.$refs[form.$data._elementRef];
}
return el;
},
setInvalid() {
const type = "is-danger";
const message = this.validationMessage || this.getElement().validationMessage;
this.setValidity(type, message);
},
setValidity(type, message) {
this.$nextTick(() => {
if (this.parentField) {
if (!this.parentField.type) {
this.parentField.newType = type;
}
if (!this.parentField.message) {
this.parentField.newMessage = message;
}
}
});
},
/*
* Check HTML5 validation, set isValid property.
* If validation fail, send 'is-danger' type,
* and error message to parent if it's a Field.
*/
checkHtml5Validity() {
if (!this.useHtml5Validation) {
return false;
}
const el = this.getElement();
if (el == null) {
return false;
}
if (!el.checkValidity()) {
this.setInvalid();
this.isValid = false;
} else {
this.setValidity(null, null);
this.isValid = true;
}
return this.isValid;
}
}
});
const findFocusable = (element, programmatic = false) => {
if (!element) {
return null;
}
if (programmatic) {
return element.querySelectorAll('*[tabindex="-1"]');
}
return element.querySelectorAll(`a[href]:not([tabindex="-1"]),
area[href],
input:not([disabled]),
select:not([disabled]),
textarea:not([disabled]),
button:not([disabled]),
iframe,
object,
embed,
*[tabindex]:not([tabindex="-1"]),
*[contenteditable]`);
};
let onKeyDown;
const beforeMount = (el, { value = true }) => {
if (value) {
let focusable = findFocusable(el);
let focusableProg = findFocusable(el, true);
if (focusable && focusable.length > 0) {
onKeyDown = (event) => {
focusable = findFocusable(el);
focusableProg = findFocusable(el, true);
const firstFocusable = focusable[0];
const lastFocusable = focusable[focusable.length - 1];
if (event.target === firstFocusable && event.shiftKey && event.key === "Tab") {
event.preventDefault();
lastFocusable.focus();
} else if ((event.target === lastFocusable || Array.from(focusableProg).indexOf(event.target) >= 0) && !event.shiftKey && event.key === "Tab") {
event.preventDefault();
firstFocusable.focus();
}
};
el.addEventListener("keydown", onKeyDown);
}
}
};
const unmounted = (el) => {
el.removeEventListener("keydown", onKeyDown);
};
const directive = {
beforeMount,
unmounted
};
const DEFAULT_CLOSE_OPTIONS = ["escape", "outside"];
const DROPDOWN_INJECTION_KEY = Symbol("bdropdown");
var _sfc_main$8 = vue.defineComponent({
name: "BDropdown",
directives: {
trapFocus: directive
},
provide() {
return {
[DROPDOWN_INJECTION_KEY]: this
};
},
props: {
modelValue: {
type: [
String,
Number,
Boolean,
Object,
Array,
Function
],
default: null
},
disabled: Boolean,
inline: Boolean,
scrollable: Boolean,
maxHeight: {
type: [String, Number],
default: 200
},
position: {
type: String,
validator(value) {
return [
"is-top-right",
"is-top-left",
"is-bottom-left",
"is-bottom-right"
].indexOf(value) > -1;
}
},
triggers: {
type: Array,
default: () => ["click"]
},
mobileModal: {
type: Boolean,
default: () => {
return config.defaultDropdownMobileModal;
}
},
ariaRole: {
type: String,
validator(value) {
return [
"menu",
"list",
"dialog"
].indexOf(value) > -1;
},
default: null
},
animation: {
type: String,
default: "fade"
},
multiple: Boolean,
trapFocus: {
type: Boolean,
default: () => {
return config.defaultTrapFocus;
}
},
closeOnClick: {
type: Boolean,
default: true
},
canClose: {
type: [Array, Boolean],
default: true
},
expanded: Boolean,
appendToBody: Boolean,
appendToBodyCopyParent: Boolean,
triggerTabindex: {
type: Number,
default: 0
}
},
emits: {
/* eslint-disable @typescript-eslint/no-unused-vars */
"active-change": (_isActive) => true,
change: (_selected) => true,
"update:modelValue": (_value) => true
/* eslint-enable @typescript-eslint/no-unused-vars */
},
data() {
return {
selected: this.modelValue,
style: {},
isActive: false,
isHoverable: false,
maybeTap: false,
isTouchEnabled: false,
_bodyEl: void 0,
// Used to append to body
timeOutID: void 0,
timeOutID2: void 0
};
},
computed: {
rootClasses() {
return [this.position, {
"is-disabled": this.disabled,
"is-hoverable": this.hoverable,
"is-inline": this.inline,
"is-active": this.isActive || this.inline,
"is-mobile-modal": this.isMobileModal,
"is-expanded": this.expanded,
"is-touch-enabled": this.isTouchEnabled
}];
},
isMobileModal() {
return this.mobileModal && !this.inline;
},
cancelOptions() {
return typeof this.canClose === "boolean" ? this.canClose ? DEFAULT_CLOSE_OPTIONS : [] : this.canClose;
},
contentStyle() {
var _a;
return {
maxHeight: this.scrollable ? (_a = toCssWidth(this.maxHeight)) != null ? _a : void 0 : void 0,
overflow: this.scrollable ? "auto" : void 0
};
},
hoverable() {
return this.triggers.indexOf("hover") >= 0;
}
},
watch: {
/*
* When v-model is changed set the new selected item.
*/
modelValue(value) {
this.selected = value;
},
/*
* Emit event when isActive value is changed.
*
* Also resets `isTouchEnabled` when it turns inactive.
*/
isActive(value) {
this.$emit("active-change", value);
if (!value) {
this.timeOutID = setTimeout(() => {
if (!this.isActive) {
this.isTouchEnabled = false;
}
}, 250);
}
this.handleScroll();
if (this.appendToBody) {
this.$nextTick(() => {
this.updateAppendToBody();
});
}
},
isHoverable(value) {
if (this.hoverable) {
this.$emit("active-change", value);
}
}
},
methods: {
handleScroll() {
if (typeof window === "undefined") return;
if (this.isMobileModal) {
if (this.isActive) {
document.documentElement.classList.add("is-clipped-touch");
} else {
document.documentElement.classList.remove("is-clipped-touch");
}
}
},
/*
* Click listener from DropdownItem.
* 1. Set new selected item.
* 2. Emit input event to update the user v-model.
* 3. Close the dropdown.
*/
selectItem(value) {
if (this.multiple) {
if (this.selected) {
const selected = this.selected;
if (selected.indexOf(value) === -1) {
this.selected = [...selected, value];
} else {
this.selected = selected.filter((val) => val !== value);
}
} else {
this.selected = [value];
}
this.$emit("change", this.selected);
} else {
if (this.selected !== value) {
this.selected = value;
this.$emit("change", this.selected);
}
}
this.$emit("update:modelValue", this.selected);
if (!this.multiple) {
this.isActive = !this.closeOnClick;
if (this.hoverable && this.closeOnClick) {
this.isHoverable = false;
}
}
},
/*
* White-listed items to not close when clicked.
*/
isInWhiteList(el) {
if (el === this.$refs.dropdownMenu) return true;
if (el === this.$refs.trigger) return true;
if (this.$refs.dropdownMenu != null) {
const children = this.$refs.dropdownMenu.querySelectorAll("*");
for (const child of children) {
if (el === child) {
return true;
}
}
}
if (this.$refs.trigger != null) {
const children = this.$refs.trigger.querySelectorAll("*");
for (const child of children) {
if (el === child) {
return true;
}
}
}
return false;
},
/*
* Close dropdown if clicked outside.
*/
clickedOutside(event) {
if (this.cancelOptions.indexOf("outside") < 0) return;
if (this.inline) return;
const target = isCustomElement(this) ? event.composedPath()[0] : event.target;
if (!this.isInWhiteList(target)) this.isActive = false;
},
/*
* Keypress event that is bound to the document
*/
keyPress({ key }) {
if (this.isActive && (key === "Escape" || key === "Esc")) {
if (this.cancelOptions.indexOf("escape") < 0) return;
this.isActive = false;
}
},
onClick() {
if (this.triggers.indexOf("hover") !== -1) return;
if (this.triggers.indexOf("click") < 0) return;
this.toggle();
},
onContextMenu() {
if (this.triggers.indexOf("contextmenu") < 0) return;
this.toggle();
},
onHover() {
if (this.triggers.indexOf("hover") < 0) return;
if (this.isTouchEnabled) return;
this.isHoverable = true;
},
// takes care of touch-enabled devices
// - does nothing if hover trigger is disabled
// - suppresses hover trigger by setting isTouchEnabled
// - handles only a tap; i.e., touchstart on the trigger immediately
// folowed by touchend
onTouchStart() {
this.maybeTap = true;
},
onTouchMove() {
this.maybeTap = false;
},
onTouchEnd(e) {
if (this.triggers.indexOf("hover") === -1) return;
if (!this.maybeTap) return;
e.preventDefault();
this.maybeTap = false;
this.isTouchEnabled = true;
this.toggle();
},
onFocus() {
if (this.triggers.indexOf("focus") < 0) return;
this.toggle();
},
/*
* Toggle dropdown if it's not disabled.
*/
toggle() {
if (this.disabled) return;
if (!this.isActive) {
this.timeOutID2 = setTimeout(() => {
const value = !this.isActive;
this.isActive = value;
});
} else {
this.isActive = !this.isActive;
}
},
updateAppendToBody() {
const dropdown = this.$refs.dropdown;
const dropdownMenu = this.$refs.dropdownMenu;
const trigger = this.$refs.trigger;
if (dropdownMenu && trigger) {
const dropdownWrapper = this.$data._bodyEl.children[0];
dropdownWrapper.classList.forEach((item) => dropdownWrapper.classList.remove(item));
dropdownWrapper.classList.add("dropdown");
dropdownWrapper.classList.add("dropdown-menu-animation");
this.rootClasses.forEach((item) => {
if (item && typeof item === "object") {
for (const key in item) {
if (item[key]) {
dropdownWrapper.classList.add(key);
}
}
}
});
if (this.appendToBodyCopyParent) {
const parentNode = this.$refs.dropdown.parentNode;
const parent = this.$data._bodyEl;
parent.classList.forEach((item) => parent.classList.remove(item));
parentNode.classList.forEach((item) => {
parent.classList.add(item);
});
}
const rect = trigger.getBoundingClientRect();
let top = rect.top + window.scrollY;
let left = rect.left + window.scrollX;
if (!this.position || this.position.indexOf("bottom") >= 0) {
top += trigger.clientHeight;
} else {
top -= dropdownMenu.clientHeight;
}
if (this.position && this.position.indexOf("left") >= 0) {
left -= dropdownMenu.clientWidth - trigger.clientWidth;
}
this.style = {
position: "absolute",
top: `${top}px`,
left: `${left}px`,
zIndex: "99",
width: this.expanded ? `${dropdown.offsetWidth}px` : void 0
};
}
}
},
mounted() {
if (this.appendToBody) {
this.$data._bodyEl = createAbsoluteElement(this.$refs.dropdownMenu);
this.updateAppendToBody();
}
},
created() {
if (typeof window !== "undefined") {
document.addEventListener("click", this.clickedOutside);
document.addEventListener("keyup", this.keyPress);
}
},
beforeUnmount() {
if (typeof window !== "undefined") {
document.removeEventListener("click", this.clickedOutside);
document.removeEventListener("keyup", this.keyPress);
}
if (this.appendToBody) {
removeElement(this.$data._bodyEl);
}
clearTimeout(this.timeOutID);
clearTimeout(this.timeOutID2);
}
});
const _hoisted_1$7 = ["tabindex"];
const _hoisted_2$7 = ["aria-hidden"];
const _hoisted_3$5 = ["aria-hidden"];
const _hoisted_4$4 = ["role", "aria-modal"];
function _sfc_render$8(_ctx, _cache, $props, $setup, $data, $options) {
const _directive_trap_focus = vue.resolveDirective("trap-focus");
return vue.openBlock(), vue.createElementBlock(
"div",
{
class: vue.normalizeClass(["dropdown dropdown-menu-animation", _ctx.rootClasses]),
ref: "dropdown",
onMouseleave: _cache[7] || (_cache[7] = ($event) => _ctx.isHoverable = false)
},
[
!_ctx.inline ? (vue.openBlock(), vue.createElementBlock("div", {
key: 0,
tabindex: _ctx.disabled ? void 0 : _ctx.triggerTabindex,
ref: "trigger",
class: "dropdown-trigger",
onClick: _cache[0] || (_cache[0] = (...args) => _ctx.onClick && _ctx.onClick(...args)),
onContextmenu: _cache[1] || (_cache[1] = vue.withModifiers((...args) => _ctx.onContextMenu && _ctx.onContextMenu(...args), ["prevent"])),
onMouseenter: _cache[2] || (_cache[2] = (...args) => _ctx.onHover && _ctx.onHover(...args)),
onFocusCapture: _cache[3] || (_cache[3] = (...args) => _ctx.onFocus && _ctx.onFocus(...args)),
onTouchstart: _cache[4] || (_cache[4] = (...args) => _ctx.onTouchStart && _ctx.onTouchStart(...args)),
onTouchmove: _cache[5] || (_cache[5] = (...args) => _ctx.onTouchMove && _ctx.onTouchMove(...args)),
onTouchend: _cache[6] || (_cache[6] = (...args) => _ctx.onTouchEnd && _ctx.onTouchEnd(...args)),
"aria-haspopup": "true"
}, [
vue.renderSlot(_ctx.$slots, "trigger", { active: _ctx.isActive })
], 40, _hoisted_1$7)) : vue.createCommentVNode("v-if", true),
vue.createVNode(vue.Transition, { name: _ctx.animation }, {
default: vue.withCtx(() => [
_ctx.isMobileModal ? vue.withDirectives((vue.openBlock(), vue.createElementBlock("div", {
key: 0,
class: "background",
"aria-hidden": !_ctx.isActive
}, null, 8, _hoisted_2$7)), [
[vue.vShow, _ctx.isActive]
]) : vue.createCommentVNode("v-if", true)
]),
_: 1
/* STABLE */
}, 8, ["name"]),
vue.createVNode(vue.Transition, {
name: _ctx.animation,
persisted: ""
}, {
default: vue.withCtx(() => [
vue.withDirectives((vue.openBlock(), vue.createElementBlock("div", {
ref: "dropdownMenu",
class: "dropdown-menu",
style: vue.normalizeStyle(_ctx.style),
"aria-hidden": !_ctx.isActive
}, [
vue.createElementVNode("div", {
class: "dropdown-content",
role: _ctx.ariaRole,
"aria-modal": !_ctx.inline,
style: vue.normalizeStyle(_ctx.contentStyle)
}, [
vue.renderSlot(_ctx.$slots, "default")
], 12, _hoisted_4$4)
], 12, _hoisted_3$5)), [
[vue.vShow, !_ctx.disabled && (_ctx.isActive || _ctx.isHoverable) || _ctx.inline],
[_directive_trap_focus, _ctx.trapFocus]
])
]),
_: 3
/* FORWARDED */
}, 8, ["name"])
],
34
/* CLASS, NEED_HYDRATION */
);
}
var BDropdown = /* @__PURE__ */ _export_sfc(_sfc_main$8, [["render", _sfc_render$8]]);
var _sfc_main$7 = vue.defineComponent({
name: "BDropdownItem",
inject: {
parent: {
from: DROPDOWN_INJECTION_KEY,
default: void 0
}
},
props: {
value: {
type: [String, Number, Boolean, Object, Array, Function],
default: null
},
separator: Boolean,
disabled: Boolean,
custom: Boolean,
focusable: {
type: Boolean,
default: true
},
paddingless: Boolean,
hasLink: Boolean,
ariaRole: {
type: String,
default: ""
}
},
emits: {
click: () => true
},
computed: {
anchorClasses() {
return {
"is-disabled": this.parent.disabled || this.disabled,
"is-paddingless": this.paddingless,
"is-active": this.isActive
};
},
itemClasses() {
return {
"dropdown-item": !this.hasLink,
"is-disabled": this.disabled,
"is-paddingless": this.paddingless,
"is-active": this.isActive,
"has-link": this.hasLink
};
},
ariaRoleItem() {
return this.ariaRole === "menuitem" || this.ariaRole === "listitem" ? this.ariaRole : void 0;
},
isClickable() {
return !this.parent.disabled && !this.separator && !this.disabled && !this.custom;
},
isActive() {
if (this.parent.selected === null) return false;
if (this.parent.multiple) {
return this.parent.selected.indexOf(this.value) >= 0;
}
return this.value === this.parent.selected;
},
isFocusable() {
return this.hasLink ? false : this.focusable;
}
},
methods: {
/*
* Click listener, select the item.
*/
selectItem() {
if (!this.isClickable) return;
this.parent.selectItem(this.value);
this.$emit("click");
}
}
});
const _hoisted_1$6 = {
key: 0,
class: "dropdown-divider"
};
const _hoisted_2$6 = ["role", "tabindex"];
const _hoisted_3$4 = ["role", "tabindex"];
function _sfc_render$7(_ctx, _cache, $props, $setup, $data, $options) {
return _ctx.separator ? (vue.openBlock(), vue.createElementBlock("hr", _hoisted_1$6)) : !_ctx.custom && !_ctx.hasLink ? (vue.openBlock(), vue.createElementBlock("a", {
key: 1,
class: vue.normalizeClass(["dropdown-item", _ctx.anchorClasses]),
onClick: _cache[0] || (_cache[0] = (...args) => _ctx.selectItem && _ctx.selectItem(...args)),
role: _ctx.ariaRoleItem,
tabindex: _ctx.isFocusable ? 0 : void 0
}, [
vue.renderSlot(_ctx.$slots, "default")
], 10, _hoisted_2$6)) : (vue.openBlock(), vue.createElementBlock("div", {
key: 2,
class: vue.normalizeClass(_ctx.itemClasses),
onClick: _cache[1] || (_cache[1] = (...args) => _ctx.selectItem && _ctx.selectItem(...args)),
role: _ctx.ariaRoleItem,
tabindex: _ctx.isFocusable ? 0 : void 0
}, [
vue.renderSlot(_ctx.$slots, "default")
], 10, _hoisted_3$4));
}
var BDropdownItem = /* @__PURE__ */ _export_sfc(_sfc_main$7, [["render", _sfc_render$7]]);
const mdiIcons = {
sizes: {
default: "mdi-24px",
"is-small": null,
"is-medium": "mdi-36px",
"is-large": "mdi-48px"
},
iconPrefix: "mdi-"
};
const faIcons = () => {
const faIconPrefix = "fa-";
return {
sizes: {
default: null,
"is-small": null,
"is-medium": faIconPrefix + "lg",
"is-large": faIconPrefix + "2x"
},
iconPrefix: faIconPrefix,
internalIcons: {
information: "info-circle",
alert: "exclamation-triangle",
"alert-circle": "exclamation-circle",
"chevron-right": "angle-right",
"chevron-left": "angle-left",
"chevron-down": "angle-down",
"eye-off": "eye-slash",
"menu-down": "caret-down",
"menu-up": "caret-up",
"close-circle": "times-circle"
}
};
};
const getIcons = () => {
let icons = {
mdi: mdiIcons,
fa: faIcons(),
fas: faIcons(),
far: faIcons(),
fad: faIcons(),
fab: faIcons(),
fal: faIcons(),
"fa-solid": faIcons(),
"fa-regular": faIcons(),
"fa-light": faIcons(),
"fa-thin": faIcons(),
"fa-duotone": faIcons(),
"fa-brands": faIcons()
};
return icons;
};
var _sfc_main$6 = vue.defineComponent({
name: "BIcon",
props: {
type: [String, Object],
component: String,
pack: String,
icon: {
type: String,
required: true
},
size: String,
customSize: String,
customClass: String,
both: Boolean
// This is used internally to show both MDI and FA icon
},
computed: {
iconConfig() {
const allIcons = getIcons();
return allIcons[this.newPack];
},
iconPrefix() {
if (this.iconConfig && this.iconConfig.iconPrefix) {
return this.iconConfig.iconPrefix;
}
return "";
},
/*
* Internal icon name based on the pack.
* If pack is 'fa', gets the equivalent FA icon name of the MDI,
* internal icons are always MDI.
*/
newIcon() {
return `${this.iconPrefix}${this.getEquivalentIconOf(this.icon)}`;
},
newPack() {
return this.pack || config.defaultIconPack;
},
newType() {
if (!this.type) return;
let splitType = [];
if (typeof this.type === "string") {
splitType = this.type.split("-");
} else {
for (const key in this.type) {
if (this.type[key]) {
splitType = key.split("-");
break;
}
}
}
if (splitType.length <= 1) return;
const [, ...type] = splitType;
return `has-text-${type.join("-")}`;
},
newCustomSize() {
return this.customSize || this.customSizeByPack;
},
customSizeByPack() {
if (this.iconConfig && this.iconConfig.sizes) {
if (this.size && this.iconConfig.sizes[this.size] !== void 0) {
return this.iconConfig.sizes[this.size];
} else if (this.iconConfig.sizes.default) {
return this.iconConfig.sizes.default;
}
}
return null;
},
useIconComponent() {
return this.component || config.defaultIconComponent;
}
},
methods: {
/*
* Equivalent icon name of the MDI.
*/
getEquivalentIconOf(value) {
if (!this.both) {
return value;
}
if (this.iconConfig == null) {
return value;
}
const maybeInternal = this.iconConfig;
if (maybeInternal && maybeInternal.internalIcons && maybeInternal.internalIcons[value]) {
return maybeInternal.internalIcons[value];
}
return value;
}
}
});
function _sfc_render$6(_ctx, _cache, $props, $setup, $data, $options) {
return vue.openBlock(), vue.createElementBlock(
"span",
{
class: vue.normalizeClass(["icon", [_ctx.newType, _ctx.size]])
},
[
!_ctx.useIconComponent ? (vue.openBlock(), vue.createElementBlock(
"i",
{
key: 0,
class: vue.normalizeClass([_ctx.newPack, _ctx.newIcon, _ctx.newCustomSize, _ctx.customClass])
},
null,
2
/* CLASS */
)) : (vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(_ctx.useIconComponent), {
key: 1,
icon: [_ctx.newPack, _ctx.newIcon],
size: _ctx.newCustomSize,
class: vue.normalizeClass([_ctx.customClass])
}, null, 8, ["icon", "size", "class"]))
],
2
/* C