buefy
Version:
Lightweight UI components for Vue.js (v3) based on Bulma
1,453 lines (1,439 loc) • 83.5 kB
JavaScript
/*! Buefy v3.0.7 | 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.Taginput = {}, global.Vue));
})(this, (function (exports, vue) { 'use strict';
function getValueByPath(obj, path) {
return path.split(".").reduce((o, i) => o ? o[i] : null, obj);
}
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 isCustomElement(vm) {
return vm.$root != null && "shadowRoot" in vm.$root.$options;
}
let config = {
defaultIconPack: "mdi",
defaultIconComponent: null,
defaultLocale: void 0,
defaultInputAutocomplete: "on",
defaultInputHasCounter: true,
defaultCompatFallthrough: true,
defaultTaginputHasCounter: true,
defaultUseHtml5Validation: true,
defaultStatusIcon: true};
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;
};
const defaultIconAliases = {
checkboxOn: {
type: "svg",
viewBox: "0 0 1 1",
path: "M 0.04038059,0.6267767 0.14644661,0.52071068 0.42928932,0.80355339 0.3232233,0.90961941 z M 0.21715729,0.80355339 0.85355339,0.16715729 0.95961941,0.2732233 0.3232233,0.90961941 z"
},
checkboxIndeterminate: {
type: "svg",
viewBox: "0 0 1 1",
path: "M 0.15,0.4 L 0.85,0.4 L 0.85,0.6 L 0.15,0.6 Z"
},
chevronLeft: {
type: "svg",
path: "M15.41,16.58L10.83,12L15.41,7.41L14,6L8,12L14,18L15.41,16.58Z"
},
chevronRight: {
type: "svg",
path: "M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z"
},
chevronDown: {
type: "svg",
path: "M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z"
},
chevronUp: {
type: "svg",
path: "M7.41,15.41L12,10.83L16.59,15.41L18,14L12,8L6,14L7.41,15.41Z"
},
menuDown: {
type: "svg",
path: "M7,10L12,15L17,10H7Z"
},
menuUp: {
type: "svg",
path: "M7,14L12,9L17,14H7Z"
},
check: {
type: "svg",
path: "M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"
},
alertCircle: {
type: "svg",
path: "M13,13H11V7H13M13,17H11V15H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z"
},
alert: {
type: "svg",
path: "M13,14H11V10H13M13,18H11V16H13M1,21H23L12,2L1,21Z"
},
information: {
type: "svg",
path: "M13,9H11V7H13M13,17H11V11H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z"
},
close: {
type: "svg",
path: "M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"
},
closeCircle: {
type: "svg",
path: "M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,6.47 6.47,2 12,2M15.59,7L12,10.59L8.41,7L7,8.41L10.59,12L7,15.59L8.41,17L12,13.41L15.59,17L17,15.59L13.41,12L17,8.41L15.59,7Z"
},
eye: {
type: "svg",
path: "M12,9A3,3 0 0,0 9,12A3,3 0 0,0 12,15A3,3 0 0,0 15,12A3,3 0 0,0 12,9M12,17A5,5 0 0,1 7,12A5,5 0 0,1 12,7A5,5 0 0,1 17,12A5,5 0 0,1 12,17M12,4.5C7,4.5 2.73,7.61 1,12C2.73,16.39 7,19.5 12,19.5C17,19.5 21.27,16.39 23,12C21.27,7.61 17,4.5 12,4.5Z"
},
eyeOff: {
type: "svg",
path: "M11.83,9L15,12.16C15,12.11 15,12.05 15,12A3,3 0 0,0 12,9C11.94,9 11.89,9 11.83,9M7.53,9.8L9.08,11.35C9.03,11.56 9,11.77 9,12A3,3 0 0,0 12,15C12.22,15 12.44,14.97 12.65,14.92L14.2,16.47C13.53,16.8 12.79,17 12,17A5,5 0 0,1 7,12C7,11.21 7.2,10.47 7.53,9.8M2,4.27L4.28,6.55L4.73,7C3.08,8.3 1.78,10 1,12C2.73,16.39 7,19.5 12,19.5C13.55,19.5 15.03,19.18 16.38,18.6L16.81,19L19.73,22L21,20.73L3.27,3M12,7A5,5 0 0,1 17,12C17,12.64 16.87,13.26 16.64,13.82L19.57,16.75C21.07,15.5 22.27,13.86 23,12C21.27,7.61 17,4.5 12,4.5C10.6,4.5 9.26,4.75 8,5.2L10.17,7.35C10.74,7.13 11.35,7 12,7Z"
}
};
function getIconAliases() {
return defaultIconAliases;
}
function resolveAlias(name) {
return getIconAliases()[name];
}
var _sfc_main$4 = vue.defineComponent({
name: "BIcon",
props: {
type: [String, Object],
component: String,
pack: String,
icon: {
type: String,
required: false,
default: void 0
},
/** Key into IconAliases (e.g. "chevronLeft"). Takes precedence over icon/pack. */
alias: String,
size: String,
customSize: String,
customClass: String,
both: Boolean
// This is used internally to show both MDI and FA icon
},
computed: {
resolvedAlias() {
var _a;
if (!this.alias) return null;
return (_a = resolveAlias(this.alias)) != null ? _a : null;
},
isSvgAlias() {
var _a;
return ((_a = this.resolvedAlias) == null ? void 0 : _a.type) === "svg";
},
isComponentAlias() {
var _a;
return ((_a = this.resolvedAlias) == null ? void 0 : _a.type) === "component";
},
// Typed accessors used in the template
resolvedAliasSvg() {
return this.resolvedAlias;
},
resolvedAliasComponent() {
return this.resolvedAlias;
},
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() {
var _a;
return `${this.iconPrefix}${this.getEquivalentIconOf((_a = this.icon) != null ? _a : "")}`;
},
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;
}
}
});
var _export_sfc = (sfc, props) => {
const target = sfc.__vccOpts || sfc;
for (const [key, val] of props) {
target[key] = val;
}
return target;
};
const _hoisted_1$4 = ["viewBox"];
const _hoisted_2$4 = ["d"];
function _sfc_render$4(_ctx, _cache, $props, $setup, $data, $options) {
return vue.openBlock(), vue.createElementBlock(
"span",
{
class: vue.normalizeClass(["icon", [_ctx.newType, _ctx.size]])
},
[
_ctx.isSvgAlias ? (vue.openBlock(), vue.createElementBlock("svg", {
key: 0,
viewBox: _ctx.resolvedAliasSvg.viewBox || "0 0 24 24",
xmlns: "http://www.w3.org/2000/svg",
class: vue.normalizeClass(_ctx.customClass),
"aria-hidden": "true"
}, [
vue.createElementVNode("path", {
fill: "currentColor",
d: _ctx.resolvedAliasSvg.path
}, null, 8, _hoisted_2$4)
], 10, _hoisted_1$4)) : _ctx.isComponentAlias ? (vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(_ctx.resolvedAliasComponent.component), {
key: 1,
icon: _ctx.resolvedAliasComponent.icon,
class: vue.normalizeClass(_ctx.customClass)
}, null, 8, ["icon", "class"])) : !_ctx.useIconComponent ? (vue.openBlock(), vue.createElementBlock(
"i",
{
key: 2,
class: vue.normalizeClass([_ctx.newPack, _ctx.newIcon, _ctx.newCustomSize, _ctx.customClass])
},
null,
2
/* CLASS */
)) : (vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(_ctx.useIconComponent), {
key: 3,
icon: [_ctx.newPack, _ctx.newIcon],
size: _ctx.newCustomSize,
class: vue.normalizeClass([_ctx.customClass])
}, null, 8, ["icon", "size", "class"]))
],
2
/* CLASS */
);
}
var BIcon = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["render", _sfc_render$4]]);
var _sfc_main$3 = vue.defineComponent({
name: "BTag",
components: { BIcon },
props: {
attached: Boolean,
closable: Boolean,
type: [String, Object],
size: String,
rounded: Boolean,
disabled: Boolean,
ellipsis: Boolean,
tabstop: {
type: Boolean,
default: true
},
ariaCloseLabel: String,
icon: String,
iconType: String,
iconPack: String,
closeType: String,
closeIcon: String,
closeIconPack: String,
closeIconType: String
},
emits: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
click: (_) => true,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
close: (_) => true
},
computed: {
// setting a boolean attribute `false` does not remove it on Vue 3.
// `null` or `undefined` has to be given to remove it.
disabledOrUndefined() {
return this.disabled || void 0;
}
},
methods: {
/*
* Emit close event when delete button is clicked
* or delete key is pressed.
*/
close(event) {
if (this.disabled) return;
this.$emit("close", event);
},
/*
* Emit click event when tag is clicked.
*/
click(event) {
if (this.disabled) return;
this.$emit("click", event);
}
}
});
const _hoisted_1$3 = {
key: 0,
class: "tags has-addons inline-tags"
};
const _hoisted_2$3 = ["aria-label", "tabindex", "disabled"];
const _hoisted_3$2 = ["aria-label", "disabled", "tabindex"];
function _sfc_render$3(_ctx, _cache, $props, $setup, $data, $options) {
const _component_b_icon = vue.resolveComponent("b-icon");
return _ctx.attached && _ctx.closable ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_1$3, [
vue.createElementVNode(
"span",
{
class: vue.normalizeClass(["tag", [_ctx.type, _ctx.size, { "is-rounded": _ctx.rounded }]])
},
[
_ctx.icon ? (vue.openBlock(), vue.createBlock(_component_b_icon, {
key: 0,
icon: _ctx.icon,
size: _ctx.size,
type: _ctx.iconType,
pack: _ctx.iconPack
}, null, 8, ["icon", "size", "type", "pack"])) : vue.createCommentVNode("v-if", true),
vue.createElementVNode(
"span",
{
class: vue.normalizeClass({ "has-ellipsis": _ctx.ellipsis }),
onClick: _cache[0] || (_cache[0] = (...args) => _ctx.click && _ctx.click(...args))
},
[
vue.renderSlot(_ctx.$slots, "default")
],
2
/* CLASS */
)
],
2
/* CLASS */
),
vue.createElementVNode("a", {
class: vue.normalizeClass(["tag", [
_ctx.size,
_ctx.closeType,
{ "is-rounded": _ctx.rounded },
_ctx.closeIcon ? "has-delete-icon" : "is-delete"
]]),
role: "button",
"aria-label": _ctx.ariaCloseLabel,
tabindex: _ctx.tabstop ? 0 : void 0,
disabled: _ctx.disabledOrUndefined,
onClick: _cache[1] || (_cache[1] = (...args) => _ctx.close && _ctx.close(...args)),
onKeyup: _cache[2] || (_cache[2] = vue.withKeys(vue.withModifiers((...args) => _ctx.close && _ctx.close(...args), ["prevent"]), ["delete"]))
}, [
_ctx.closeIcon ? (vue.openBlock(), vue.createBlock(_component_b_icon, {
key: 0,
"custom-class": "",
icon: _ctx.closeIcon,
size: _ctx.size,
type: _ctx.closeIconType,
pack: _ctx.closeIconPack
}, null, 8, ["icon", "size", "type", "pack"])) : vue.createCommentVNode("v-if", true)
], 42, _hoisted_2$3)
])) : (vue.openBlock(), vue.createElementBlock(
"span",
{
key: 1,
class: vue.normalizeClass(["tag", [_ctx.type, _ctx.size, { "is-rounded": _ctx.rounded }]])
},
[
_ctx.icon ? (vue.openBlock(), vue.createBlock(_component_b_icon, {
key: 0,
icon: _ctx.icon,
size: _ctx.size,
type: _ctx.iconType,
pack: _ctx.iconPack
}, null, 8, ["icon", "size", "type", "pack"])) : vue.createCommentVNode("v-if", true),
vue.createElementVNode(
"span",
{
class: vue.normalizeClass({ "has-ellipsis": _ctx.ellipsis }),
onClick: _cache[3] || (_cache[3] = (...args) => _ctx.click && _ctx.click(...args))
},
[
vue.renderSlot(_ctx.$slots, "default")
],
2
/* CLASS */
),
_ctx.closable ? (vue.openBlock(), vue.createElementBlock("a", {
key: 1,
role: "button",
"aria-label": _ctx.ariaCloseLabel,
class: vue.normalizeClass(["delete is-small", _ctx.closeType]),
disabled: _ctx.disabledOrUndefined,
tabindex: _ctx.tabstop ? 0 : void 0,
onClick: _cache[4] || (_cache[4] = (...args) => _ctx.close && _ctx.close(...args)),
onKeyup: _cache[5] || (_cache[5] = vue.withKeys(vue.withModifiers((...args) => _ctx.close && _ctx.close(...args), ["prevent"]), ["delete"]))
}, null, 42, _hoisted_3$2)) : vue.createCommentVNode("v-if", true)
],
2
/* CLASS */
));
}
var BTag = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["render", _sfc_render$3]]);
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 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;
}
}
});
var _sfc_main$2 = vue.defineComponent({
name: "BInput",
components: { BIcon },
mixins: [CompatFallthroughMixin, FormElementMixin],
props: {
modelValue: {
type: [Number, String]
},
type: {
type: String,
default: "text"
},
lazy: {
type: Boolean,
default: false
},
passwordReveal: Boolean,
iconClickable: Boolean,
hasCounter: {
type: Boolean,
default: () => config.defaultInputHasCounter
},
customClass: {
type: String,
default: ""
},
iconRight: String,
iconRightClickable: Boolean,
iconRightType: String,
// Native options to use in HTML5 validation
autocomplete: String
},
emits: {
/* eslint-disable @typescript-eslint/no-unused-vars */
"icon-click": (event) => true,
"icon-right-click": (event) => true,
"update:modelValue": (value) => true
/* eslint-enable @typescript-eslint/no-unused-vars */
},
data() {
return {
newValue: this.modelValue,
newType: this.type,
newAutocomplete: this.autocomplete || config.defaultInputAutocomplete,
isPasswordVisible: false,
_elementRef: this.type === "textarea" ? "textarea" : "input"
};
},
computed: {
computedValue: {
get() {
return this.newValue;
},
set(value) {
this.newValue = value;
this.$emit("update:modelValue", value);
}
},
rootClasses() {
return [
this.iconPosition,
this.size,
{
"is-expanded": this.expanded,
"is-loading": this.loading,
"is-clearfix": !this.hasMessage
}
];
},
inputClasses() {
return [
this.statusType,
this.size,
{ "is-rounded": this.rounded }
];
},
hasIconRight() {
return this.passwordReveal || this.loading || this.statusIcon && this.statusTypeIcon || this.iconRight;
},
rightIcon() {
if (this.passwordReveal) {
return this.passwordVisibleIcon;
} else if (this.iconRight) {
return this.iconRight;
}
return this.statusTypeIcon;
},
rightIconType() {
if (this.passwordReveal) {
return "is-primary";
} else if (this.iconRight) {
return this.iconRightType || void 0;
}
return this.statusType;
},
/*
* Position of the icon or if it's both sides.
*/
iconPosition() {
let iconClasses = "";
if (this.icon) {
iconClasses += "has-icons-left ";
}
if (this.hasIconRight) {
iconClasses += "has-icons-right";
}
return iconClasses;
},
/*
* Icon name (MDI) based on the type.
*/
statusTypeIcon() {
switch (this.statusType) {
case "is-success":
return "check";
case "is-danger":
return "alert-circle";
case "is-info":
return "information";
case "is-warning":
return "alert";
default:
return void 0;
}
},
/*
* Check if have any message prop from parent if it's a Field.
*/
hasMessage() {
return !!this.statusMessage;
},
/*
* Current password-reveal icon name.
*/
passwordVisibleIcon() {
return !this.isPasswordVisible ? "eye" : "eye-off";
},
/*
* Get value length
*/
valueLength() {
if (typeof this.computedValue === "string") {
return Array.from(this.computedValue).length;
} else if (typeof this.computedValue === "number") {
return this.computedValue.toString().length;
}
return 0;
}
},
watch: {
/*
* When v-model is changed:
* 1. Set internal value.
* 2. Validate it if the value came from outside;
* i.e., not equal to computedValue
*/
modelValue(value) {
const fromOutside = this.computedValue != value;
this.newValue = value;
if (fromOutside) {
this.$nextTick(() => {
!this.isValid && this.checkHtml5Validity();
});
}
},
type(type) {
this.newType = type;
}
},
methods: {
/*
* Toggle the visibility of a password-reveal input
* by changing the type and focus the input right away.
*/
togglePasswordVisibility() {
this.isPasswordVisible = !this.isPasswordVisible;
this.newType = this.isPasswordVisible ? "text" : "password";
this.$nextTick(() => {
this.focus();
});
},
iconClick(emit, event) {
this.$emit(emit, event);
this.$nextTick(() => {
this.focus();
});
},
rightIconClick(event) {
if (this.passwordReveal) {
this.togglePasswordVisibility();
} else if (this.iconRightClickable) {
this.iconClick("icon-right-click", event);
}
},
onInput() {
if (!this.lazy) {
this.revalidate();
}
},
onChange() {
if (this.lazy) {
this.revalidate();
}
},
revalidate() {
!this.isValid && this.checkHtml5Validity();
}
}
});
const _hoisted_1$2 = ["type", "autocomplete", "maxlength"];
const _hoisted_2$2 = ["maxlength"];
const _hoisted_3$1 = ["type", "autocomplete", "maxlength"];
const _hoisted_4$1 = ["maxlength"];
function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) {
const _component_b_icon = vue.resolveComponent("b-icon");
return vue.openBlock(), vue.createElementBlock(
"div",
vue.mergeProps({
class: ["control", _ctx.rootClasses]
}, _ctx.rootAttrs),
[
_ctx.lazy ? (vue.openBlock(), vue.createElementBlock(
vue.Fragment,
{ key: 0 },
[
_ctx.type !== "textarea" ? vue.withDirectives((vue.openBlock(), vue.createElementBlock("input", vue.mergeProps({
key: 0,
ref: "input",
class: ["input", [_ctx.inputClasses, _ctx.customClass]],
type: _ctx.newType,
autocomplete: _ctx.newAutocomplete,
maxlength: _ctx.maxlength,
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => _ctx.computedValue = $event)
}, _ctx.fallthroughAttrs, {
onInput: _cache[1] || (_cache[1] = (...args) => _ctx.onInput && _ctx.onInput(...args)),
onChange: _cache[2] || (_cache[2] = (...args) => _ctx.onChange && _ctx.onChange(...args)),
onBlur: _cache[3] || (_cache[3] = (...args) => _ctx.onBlur && _ctx.onBlur(...args)),
onFocus: _cache[4] || (_cache[4] = (...args) => _ctx.onFocus && _ctx.onFocus(...args))
}), null, 16, _hoisted_1$2)), [
[
vue.vModelDynamic,
_ctx.computedValue,
void 0,
{ lazy: true }
]
]) : vue.withDirectives((vue.openBlock(), vue.createElementBlock("textarea", vue.mergeProps({
key: 1,
ref: "textarea",
class: ["textarea", [_ctx.inputClasses, _ctx.customClass]],
maxlength: _ctx.maxlength,
"onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => _ctx.computedValue = $event)
}, _ctx.fallthroughAttrs, {
onInput: _cache[6] || (_cache[6] = (...args) => _ctx.onInput && _ctx.onInput(...args)),
onChange: _cache[7] || (_cache[7] = (...args) => _ctx.onChange && _ctx.onChange(...args)),
onBlur: _cache[8] || (_cache[8] = (...args) => _ctx.onBlur && _ctx.onBlur(...args)),
onFocus: _cache[9] || (_cache[9] = (...args) => _ctx.onFocus && _ctx.onFocus(...args))
}), null, 16, _hoisted_2$2)), [
[
vue.vModelText,
_ctx.computedValue,
void 0,
{ lazy: true }
]
])
],
64
/* STABLE_FRAGMENT */
)) : (vue.openBlock(), vue.createElementBlock(
vue.Fragment,
{ key: 1 },
[
_ctx.type !== "textarea" ? vue.withDirectives((vue.openBlock(), vue.createElementBlock("input", vue.mergeProps({
key: 0,
ref: "input",
class: ["input", [_ctx.inputClasses, _ctx.customClass]],
type: _ctx.newType,
autocomplete: _ctx.newAutocomplete,
maxlength: _ctx.maxlength,
"onUpdate:modelValue": _cache[10] || (_cache[10] = ($event) => _ctx.computedValue = $event)
}, _ctx.fallthroughAttrs, {
onInput: _cache[11] || (_cache[11] = (...args) => _ctx.onInput && _ctx.onInput(...args)),
onChange: _cache[12] || (_cache[12] = (...args) => _ctx.onChange && _ctx.onChange(...args)),
onBlur: _cache[13] || (_cache[13] = (...args) => _ctx.onBlur && _ctx.onBlur(...args)),
onFocus: _cache[14] || (_cache[14] = (...args) => _ctx.onFocus && _ctx.onFocus(...args))
}), null, 16, _hoisted_3$1)), [
[vue.vModelDynamic, _ctx.computedValue]
]) : vue.withDirectives((vue.openBlock(), vue.createElementBlock("textarea", vue.mergeProps({
key: 1,
ref: "textarea",
class: ["textarea", [_ctx.inputClasses, _ctx.customClass]],
maxlength: _ctx.maxlength,
"onUpdate:modelValue": _cache[15] || (_cache[15] = ($event) => _ctx.computedValue = $event)
}, _ctx.fallthroughAttrs, {
onInput: _cache[16] || (_cache[16] = (...args) => _ctx.onInput && _ctx.onInput(...args)),
onChange: _cache[17] || (_cache[17] = (...args) => _ctx.onChange && _ctx.onChange(...args)),
onBlur: _cache[18] || (_cache[18] = (...args) => _ctx.onBlur && _ctx.onBlur(...args)),
onFocus: _cache[19] || (_cache[19] = (...args) => _ctx.onFocus && _ctx.onFocus(...args))
}), null, 16, _hoisted_4$1)), [
[vue.vModelText, _ctx.computedValue]
])
],
64
/* STABLE_FRAGMENT */
)),
_ctx.icon ? (vue.openBlock(), vue.createBlock(_component_b_icon, {
key: 2,
class: vue.normalizeClass(["is-left", { "is-clickable": _ctx.iconClickable }]),
icon: _ctx.icon,
pack: _ctx.iconPack,
size: _ctx.iconSize,
onClick: _cache[20] || (_cache[20] = ($event) => _ctx.iconClick("icon-click", $event))
}, null, 8, ["class", "icon", "pack", "size"])) : vue.createCommentVNode("v-if", true),
!_ctx.loading && _ctx.hasIconRight && _ctx.rightIcon ? (vue.openBlock(), vue.createBlock(_component_b_icon, {
key: 3,
class: vue.normalizeClass(["is-right", { "is-clickable": _ctx.passwordReveal || _ctx.iconRightClickable }]),
icon: _ctx.rightIcon,
pack: _ctx.iconPack,
size: _ctx.iconSize,
type: _ctx.rightIconType,
both: "",
onClick: _ctx.rightIconClick
}, null, 8, ["class", "icon", "pack", "size", "type", "onClick"])) : vue.createCommentVNode("v-if", true),
_ctx.maxlength && _ctx.hasCounter && _ctx.type !== "number" ? (vue.openBlock(), vue.createElementBlock(
"small",
{
key: 4,
class: vue.normalizeClass(["help counter", { "is-invisible": !_ctx.isFocused }])
},
vue.toDisplayString(_ctx.valueLength) + " / " + vue.toDisplayString(_ctx.maxlength),
3
/* TEXT, CLASS */
)) : vue.createCommentVNode("v-if", true)
],
16
/* FULL_PROPS */
);
}
var BInput = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render$2]]);
var _sfc_main$1 = vue.defineComponent({
name: "BAutocomplete",
components: { BInput },
mixins: [CompatFallthroughMixin, FormElementMixin],
props: {
modelValue: [Number, String, null],
data: {
type: Array,
default: () => []
},
field: {
type: String,
default: "value"
},
keepFirst: Boolean,
clearOnSelect: Boolean,
openOnFocus: Boolean,
customFormatter: {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
type: Function
},
checkInfiniteScroll: Boolean,
keepOpen: Boolean,
selectOnClickOutside: Boolean,
clearable: Boolean,
maxHeight: [String, Number],
dropdownPosition: {
type: String,
default: "auto"
},
groupField: String,
groupOptions: String,
iconRight: String,
iconRightClickable: Boolean,
appendToBody: Boolean,
type: {
type: String,
default: "text"
},
confirmKeys: {
type: Array,
default: () => ["Tab", "Enter"]
},
selectableHeader: Boolean,
selectableFooter: Boolean,
// Native options to use in HTML5 validation
autocomplete: String
},
emits: {
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any */
active: (active) => true,
blur: (event) => true,
focus: (event) => true,
"icon-click": (event) => true,
"icon-right-click": (event) => true,
"infinite-scroll": () => true,
select: (selected, event) => true,
"select-footer": (event) => true,
"select-header": (event) => true,
typing: (value) => true,
"update:modelValue": (value) => true
/* eslint-enable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any */
},
data() {
return {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
selected: null,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
hovered: null,
headerHovered: null,
footerHovered: null,
isActive: false,
newValue: this.modelValue,
newAutocomplete: this.autocomplete || "off",
ariaAutocomplete: this.keepFirst ? "both" : "list",
isListInViewportVertically: true,
hasFocus: false,
style: {},
_isAutocomplete: true,
_elementRef: "input",
_bodyEl: void 0,
// Used to append to body
timeOutID: void 0
};
},
computed: {
computedData() {
const { groupField, groupOptions } = this;
if (groupField) {
if (groupOptions) {
const newData = [];
this.data.forEach((option) => {
const group = getValueByPath(option, groupField);
const items = getValueByPath(option, groupOptions);
newData.push({ group, items });
});
return newData;
} else {
const tmp = {};
this.data.forEach((option) => {
const group = getValueByPath(option, groupField);
if (!tmp[group]) tmp[group] = [];
tmp[group].push(option);
});
const newData = [];
Object.keys(tmp).forEach((group) => {
newData.push({ group, items: tmp[group] });
});
return newData;
}
}
return [{ items: this.data }];
},
isEmpty() {
if (!this.computedData) return true;
return !this.computedData.some(
(element) => element.items && element.items.length
);
},
/*
* White-listed items to not close when clicked.
* Add input, dropdown and all children.
*/
whiteList() {
var _a;
this.computedData;
const whiteList = [];
whiteList.push(this.$refs.input.$el.querySelector("input"));
whiteList.push(this.$refs.dropdown);
if (this.$refs.dropdown != null) {
const children = this.$refs.dropdown.querySelectorAll("*");
for (const child of children) {
whiteList.push(child);
}
}
if (((_a = this.$parent) == null ? void 0 : _a.$data)._isTaginput) {
whiteList.push(this.$parent.$el);
const tagInputChildren = this.$parent.$el.querySelectorAll("*");
for (const tagInputChild of tagInputChildren) {
whiteList.push(tagInputChild);
}
}
return whiteList;
},
/*
* Check if exists default slot
*/
hasDefaultSlot() {
return !!this.$slots.default;
},
/*
* Check if exists group slot
*/
hasGroupSlot() {
return !!this.$slots.group;
},
/*
* Check if exists "empty" slot
*/
hasEmptySlot() {
return !!this.$slots.empty;
},
/*
* Check if exists "header" slot
*/
hasHeaderSlot() {
return !!this.$slots.header;
},
/*
* Check if exists "footer" slot
*/
hasFooterSlot() {
return !!this.$slots.footer;
},
/*
* Apply dropdownPosition property
*/
isOpenedTop() {
return this.dropdownPosition === "top" || this.dropdownPosition === "auto" && !this.isListInViewportVertically;
},
newIconRight() {
if (this.clearable && this.newValue) {
return "close-circle";
}
return this.iconRight;
},
newIconRightClickable() {
if (this.clearable) {
return true;
}
return this.iconRightClickable;
},
contentStyle() {
return {
maxHeight: toCssWidth(this.maxHeight) || void 0
};
}
},
watch: {
/*
* When dropdown is toggled, check the visibility to know when
* to open upwards.
*/
isActive(active) {
if (this.dropdownPosition === "auto") {
if (active) {
this.calcDropdownInViewportVertical();
} else {
this.timeOutID = setTimeout(() => {
this.calcDropdownInViewportVertical();
}, 100);
}
}
this.$nextTick(() => {
this.$emit("active", active);
});
},
/*
* When checkInfiniteScroll property changes scroll event should be removed or added
*/
checkInfiniteScroll(checkInfiniteScroll) {
if (!this.$refs.dropdown) return;
const list = this.$refs.dropdown.querySelector(
".dropdown-content"
);
if (!list) return;
if (checkInfiniteScroll === true) {
list.addEventListener(
"scroll",
this.checkIfReachedTheEndOfScroll
);
return;
}
list.removeEventListener(
"scroll",
this.checkIfReachedTheEndOfScroll
);
},
/*
* When updating input's value
* 1. Emit changes
* 2. If value isn't the same as selected, set null
* 3. Close dropdown if value is clear or else open it
*/
newValue(value) {
this.$emit("update:modelValue", value);
const currentValue = this.getValue(this.selected);
if (currentValue !== void 0 && currentValue !== null && currentValue !== value) {
this.setSelected(null, false);
}
if (this.hasFocus && (!this.openOnFocus || value !== "")) {
this.isActive = value !== "" && value !== void 0 && value !== null;
}
},
/*
* When v-model is changed:
* 1. Update internal value.
* 2. If it's invalid, validate again.
*/
modelValue(value) {
this.newValue = value;
},
keepFirst(value) {
this.ariaAutocomplete = value ? "both" : "list";
},
/*
* Select first option if "keep-first
*/
data() {
if (this.keepFirst) {
this.$nextTick(() => {
if (this.isActive) {
this.selectFirstOption(this.computedData);
} else {
this.setHovered(null);
}
});
} else {
if (this.hovered) {
const hoveredValue = this.getValue(this.hovered);
const data = this.computedData.map((d) => d.items).reduce((a, b) => [...a, ...b], []);
if (!data.some((d) => this.getValue(d) === hoveredValue)) {
this.setHovered(null);
}
}
}
},
/*
* When appendToBody property changes, handle the transition properly
*/
appendToBody(newValue, oldValue) {
if (newValue && !oldValue) {
if (this.isActive && this.$refs.dropdown && !this.$data._bodyEl) {
this.$data._bodyEl = createAbsoluteElement(
this.$refs.dropdown
);
this.updateAppendToBody();
}
} else if (!newValue && oldValue) {
if (this.$data._bodyEl) {
removeElement(this.$data._bodyEl);
this.$data._bodyEl = void 0;
}
}
}
},
methods: {
/*
* Set which option is currently hovered.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
setHovered(option) {
if (option === void 0) return;
this.hovered = option;
},
/*
* Set which option is currently selected, update v-model,
* update input value and close dropdown.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
setSelected(option, closeDropdown = true, event) {
if (option === void 0) return;
this.selected = option;
this.$emit("select", this.selected, event);
if (this.selected !== null) {
if (this.clearOnSelect) {
this.newValue = "";
} else {
this.newValue = this.getValue(this.selected);
}
this.setHovered(null);
}
closeDropdown && this.$nextTick(() => {
this.isActive = false;
});
this.checkValidity();
},
/*
* Select first option
*/
selectFirstOption(computedData) {
this.$nextTick(() => {
const nonEmptyElements = computedData.filter(
(element) => element.items && element.items.length
);
if (nonEmptyElements.length) {
const option = nonEmptyElements[0].items[0];
this.setHovered(option);
} else {
this.setHovered(null);
}
});
},
/*
* Find index of hovered item in data array by comparing display values
* instead of object references. This fixes the bug with computed data
* where proxy objects cause indexOf to fail.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
findHoveredIndex(data) {
if (this.hovered === null || this.hovered === void 0) {
return -1;
}
const exactIndex = data.indexOf(this.hovered);
if (exactIndex !== -1) {
return exactIndex;
}
const hoveredValue = this.getValue(this.hovered);
if (hoveredValue === null || hoveredValue === void 0) {
return -1;
}
return data.findIndex((item) => {
if (item === null || item === void 0) {
return hoveredValue === null || hoveredValue === void 0;
}
return this.getValue(item) === hoveredValue;
});
},
keydown(event) {
const { key } = event;
if (key === "Enter") event.preventDefault();
if (key === "Escape" || key === "Tab") {
this.isActive = false;
}
if (this.confirmKeys.indexOf(key) >= 0) {
if (key === ",") event.preventDefault();
const closeDropdown = !this.keepOpen || key === "Tab";
if (this.hovered === null) {
this.checkIfHeaderOrFooterSelected(
event,
null,
closeDropdown
);
return;
}
this.setSelected(this.hovered, closeDropdown, event);
}
},
selectHeaderOrFoterByClick(event, origin) {
this.checkIfHeaderOrFooterSelected(event, { origin });
},
/*
*