`\n inheritAttrs: false,\n props,\n computed: {\n isTableSimple() {\n return false;\n },\n // Layout related computed props\n isResponsive() {\n const {\n responsive\n } = this;\n return responsive === '' ? true : responsive;\n },\n isStickyHeader() {\n let {\n stickyHeader\n } = this;\n stickyHeader = stickyHeader === '' ? true : stickyHeader;\n return this.isStacked ? false : stickyHeader;\n },\n wrapperClasses() {\n const {\n isResponsive\n } = this;\n return [this.isStickyHeader ? 'b-table-sticky-header' : '', isResponsive === true ? 'table-responsive' : isResponsive ? `table-responsive-${this.responsive}` : ''].filter(identity);\n },\n wrapperStyles() {\n const {\n isStickyHeader\n } = this;\n return isStickyHeader && !isBoolean(isStickyHeader) ? {\n maxHeight: isStickyHeader\n } : {};\n },\n tableClasses() {\n let {\n hover,\n tableVariant,\n selectableTableClasses,\n stackedTableClasses,\n tableClass,\n computedBusy\n } = safeVueInstance(this);\n hover = this.isTableSimple ? hover : hover && this.computedItems.length > 0 && !computedBusy;\n return [\n // User supplied classes\n tableClass,\n // Styling classes\n {\n 'table-striped': this.striped,\n 'table-hover': hover,\n 'table-dark': this.dark,\n 'table-bordered': this.bordered,\n 'table-borderless': this.borderless,\n 'table-sm': this.small,\n // The following are b-table custom styles\n 'gl-border': this.outlined,\n 'b-table-fixed': this.fixed,\n 'b-table-caption-top': this.captionTop,\n 'b-table-no-border-collapse': this.noBorderCollapse\n }, tableVariant ? `${this.dark ? 'bg' : 'table'}-${tableVariant}` : '',\n // Stacked table classes\n stackedTableClasses,\n // Selectable classes\n selectableTableClasses];\n },\n tableAttrs() {\n const {\n computedItems: items,\n filteredItems,\n computedFields: fields,\n selectableTableAttrs,\n computedBusy\n } = safeVueInstance(this);\n const ariaAttrs = this.isTableSimple ? {} : {\n 'aria-busy': toString(computedBusy),\n 'aria-colcount': toString(fields.length),\n // Preserve user supplied `aria-describedby`, if provided\n 'aria-describedby': this.bvAttrs['aria-describedby'] || this.$refs.caption ? this.captionId : null\n };\n const rowCount = items && filteredItems && filteredItems.length > items.length ? toString(filteredItems.length) : null;\n return {\n // We set `aria-rowcount` before merging in `$attrs`,\n // in case user has supplied their own\n 'aria-rowcount': rowCount,\n // Merge in user supplied `$attrs` if any\n ...this.bvAttrs,\n // Now we can override any `$attrs` here\n id: this.safeId(),\n role: this.bvAttrs.role || 'table',\n ...ariaAttrs,\n ...selectableTableAttrs\n };\n }\n },\n render(h) {\n const {\n wrapperClasses,\n renderCaption,\n renderColgroup,\n renderThead,\n renderTbody,\n renderTfoot\n } = safeVueInstance(this);\n const $content = [];\n if (this.isTableSimple) {\n $content.push(this.normalizeSlot());\n } else {\n // Build the `
` (from caption mixin)\n $content.push(renderCaption ? renderCaption() : null);\n\n // Build the ``\n $content.push(renderColgroup ? renderColgroup() : null);\n\n // Build the ``\n $content.push(renderThead ? renderThead() : null);\n\n // Build the ``\n $content.push(renderTbody ? renderTbody() : null);\n\n // Build the ``\n $content.push(renderTfoot ? renderTfoot() : null);\n }\n\n // Assemble ``\n const $table = h('table', {\n staticClass: 'table b-table',\n class: this.tableClasses,\n attrs: this.tableAttrs,\n key: 'b-table'\n }, $content.filter(identity));\n\n // Add responsive/sticky wrapper if needed and return table\n return wrapperClasses.length > 0 ? h('div', {\n class: wrapperClasses,\n style: this.wrapperStyles,\n key: 'wrap'\n }, [$table]) : $table;\n }\n});\n\nexport { props, tableRendererMixin };\n","var getTag = require('./_getTag'),\n isObjectLike = require('./isObjectLike');\n\n/** `Object#toString` result references. */\nvar mapTag = '[object Map]';\n\n/**\n * The base implementation of `_.isMap` without Node.js optimizations.\n *\n * @private\n * @param {*} value The value to check.\n * @returns {boolean} Returns `true` if `value` is a map, else `false`.\n */\nfunction baseIsMap(value) {\n return isObjectLike(value) && getTag(value) == mapTag;\n}\n\nmodule.exports = baseIsMap;\n","var createWrap = require('./_createWrap');\n\n/** Used to compose bitmasks for function metadata. */\nvar WRAP_ARY_FLAG = 128;\n\n/**\n * Creates a function that invokes `func`, with up to `n` arguments,\n * ignoring any additional arguments.\n *\n * @static\n * @memberOf _\n * @since 3.0.0\n * @category Function\n * @param {Function} func The function to cap arguments for.\n * @param {number} [n=func.length] The arity cap.\n * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.\n * @returns {Function} Returns the new capped function.\n * @example\n *\n * _.map(['6', '8', '10'], _.ary(parseInt, 1));\n * // => [6, 8, 10]\n */\nfunction ary(func, n, guard) {\n n = guard ? undefined : n;\n n = (func && n == null) ? func.length : n;\n return createWrap(func, WRAP_ARY_FLAG, undefined, undefined, undefined, undefined, n);\n}\n\nmodule.exports = ary;\n","import { Wormhole, PortalTarget } from 'portal-vue';\nimport { extend } from '../../vue';\nimport { NAME_TOASTER } from '../../constants/components';\nimport { EVENT_NAME_DESTROYED } from '../../constants/events';\nimport { PROP_TYPE_STRING } from '../../constants/props';\nimport { requestAF, removeClass } from '../../utils/dom';\nimport { getRootEventName } from '../../utils/events';\nimport { makePropsConfigurable, makeProp } from '../../utils/props';\nimport { warn } from '../../utils/warn';\nimport { listenOnRootMixin } from '../../mixins/listen-on-root';\nimport { normalizeSlotMixin } from '../../mixins/normalize-slot';\n\n// --- Helper components ---\n\n// @vue/component\nconst DefaultTransition = /*#__PURE__*/extend({\n mixins: [normalizeSlotMixin],\n data() {\n return {\n // Transition classes base name\n name: 'b-toaster'\n };\n },\n methods: {\n onAfterEnter(el) {\n // Work around a Vue.js bug where `*-enter-to` class is not removed\n // See: https://github.com/vuejs/vue/pull/7901\n // The `*-move` class is also stuck on elements that moved,\n // but there are no JavaScript hooks to handle after move\n // See: https://github.com/vuejs/vue/pull/7906\n requestAF(() => {\n removeClass(el, `${this.name}-enter-to`);\n });\n }\n },\n render(h) {\n return h('transition-group', {\n props: {\n tag: 'div',\n name: this.name\n },\n on: {\n afterEnter: this.onAfterEnter\n }\n }, this.normalizeSlot());\n }\n});\n\n// --- Props ---\n\nconst props = makePropsConfigurable({\n // Allowed: 'true' or 'false' or `null`\n ariaAtomic: makeProp(PROP_TYPE_STRING),\n ariaLive: makeProp(PROP_TYPE_STRING),\n name: makeProp(PROP_TYPE_STRING, undefined, true),\n // Required\n // Aria role\n role: makeProp(PROP_TYPE_STRING)\n}, NAME_TOASTER);\n\n// --- Main component ---\n\n// @vue/component\nconst BToaster = /*#__PURE__*/extend({\n name: NAME_TOASTER,\n mixins: [listenOnRootMixin],\n props,\n data() {\n return {\n // We don't render on SSR or if a an existing target found\n doRender: false,\n dead: false,\n // Toaster names cannot change once created\n staticName: this.name\n };\n },\n beforeMount() {\n const {\n name\n } = this;\n this.staticName = name;\n\n /* istanbul ignore if */\n if (Wormhole.hasTarget(name)) {\n warn(`A \"\" with name \"${name}\" already exists in the document.`, NAME_TOASTER);\n this.dead = true;\n } else {\n this.doRender = true;\n }\n },\n beforeDestroy() {\n // Let toasts made with `this.$bvToast.toast()` know that this toaster\n // is being destroyed and should should also destroy/hide themselves\n if (this.doRender) {\n this.emitOnRoot(getRootEventName(NAME_TOASTER, EVENT_NAME_DESTROYED), this.name);\n }\n },\n destroyed() {\n // Remove from DOM if needed\n const {\n $el\n } = this;\n /* istanbul ignore next: difficult to test */\n if ($el && $el.parentNode) {\n $el.parentNode.removeChild($el);\n }\n },\n render(h) {\n let $toaster = h('div', {\n class: ['gl-hidden', {\n 'b-dead-toaster': this.dead\n }]\n });\n if (this.doRender) {\n const $target = h(PortalTarget, {\n staticClass: 'b-toaster-slot',\n props: {\n name: this.staticName,\n multiple: true,\n tag: 'div',\n slim: false,\n // transition: this.transition || DefaultTransition\n transition: DefaultTransition\n }\n });\n $toaster = h('div', {\n staticClass: 'b-toaster',\n class: [this.staticName],\n attrs: {\n id: this.staticName,\n // Fallback to null to make sure attribute doesn't exist\n role: this.role || null,\n 'aria-live': this.ariaLive,\n 'aria-atomic': this.ariaAtomic\n }\n }, [$target]);\n }\n return $toaster;\n }\n});\n\nexport { BToaster, DefaultTransition, props };\n","import { Wormhole, Portal } from 'portal-vue';\nimport { extend, COMPONENT_UID_KEY } from '../../vue';\nimport { NAME_TOAST, NAME_TOASTER } from '../../constants/components';\nimport { EVENT_NAME_CHANGE, EVENT_NAME_SHOW, EVENT_NAME_HIDE, EVENT_NAME_DESTROYED, EVENT_OPTIONS_NO_CAPTURE, EVENT_NAME_SHOWN, EVENT_NAME_HIDDEN } from '../../constants/events';\nimport { PROP_TYPE_BOOLEAN, PROP_TYPE_NUMBER_STRING, PROP_TYPE_ARRAY_OBJECT_STRING, PROP_TYPE_STRING } from '../../constants/props';\nimport { SLOT_NAME_TOAST_TITLE, SLOT_NAME_DEFAULT } from '../../constants/slots';\nimport { BvEvent } from '../../utils/bv-event.class';\nimport { requestAF } from '../../utils/dom';\nimport { getRootActionEventName, getRootEventName, eventOnOff } from '../../utils/events';\nimport { mathMax } from '../../utils/math';\nimport { makeModelMixin } from '../../utils/model';\nimport { toInteger } from '../../utils/number';\nimport { pick, sortKeys } from '../../utils/object';\nimport { makePropsConfigurable, makeProp, pluckProps } from '../../utils/props';\nimport { isLink } from '../../utils/router';\nimport { createNewChildComponent } from '../../utils/create-new-child-component';\nimport { attrsMixin } from '../../mixins/attrs';\nimport { props as props$2, idMixin } from '../../mixins/id';\nimport { listenOnRootMixin } from '../../mixins/listen-on-root';\nimport { normalizeSlotMixin } from '../../mixins/normalize-slot';\nimport { scopedStyleMixin } from '../../mixins/scoped-style';\nimport { props as props$1, BLink } from '../link/link';\nimport { BVTransition } from '../transition/bv-transition';\nimport { BToaster } from './toaster';\n\n// --- Constants ---\n\nconst {\n mixin: modelMixin,\n props: modelProps,\n prop: MODEL_PROP_NAME,\n event: MODEL_EVENT_NAME\n} = makeModelMixin('visible', {\n type: PROP_TYPE_BOOLEAN,\n defaultValue: false,\n event: EVENT_NAME_CHANGE\n});\nconst MIN_DURATION = 1000;\n\n// --- Props ---\n\nconst linkProps = pick(props$1, ['href', 'to']);\nconst props = makePropsConfigurable(sortKeys({\n ...props$2,\n ...modelProps,\n ...linkProps,\n appendToast: makeProp(PROP_TYPE_BOOLEAN, false),\n autoHideDelay: makeProp(PROP_TYPE_NUMBER_STRING, 5000),\n bodyClass: makeProp(PROP_TYPE_ARRAY_OBJECT_STRING),\n headerClass: makeProp(PROP_TYPE_ARRAY_OBJECT_STRING),\n headerTag: makeProp(PROP_TYPE_STRING, 'header'),\n // Switches role to 'status' and aria-live to 'polite'\n isStatus: makeProp(PROP_TYPE_BOOLEAN, false),\n noAutoHide: makeProp(PROP_TYPE_BOOLEAN, false),\n noFade: makeProp(PROP_TYPE_BOOLEAN, false),\n noHoverPause: makeProp(PROP_TYPE_BOOLEAN, false),\n solid: makeProp(PROP_TYPE_BOOLEAN, false),\n // Render the toast in place, rather than in a portal-target\n static: makeProp(PROP_TYPE_BOOLEAN, false),\n title: makeProp(PROP_TYPE_STRING),\n toastClass: makeProp(PROP_TYPE_ARRAY_OBJECT_STRING),\n toaster: makeProp(PROP_TYPE_STRING, 'b-toaster-top-right'),\n variant: makeProp(PROP_TYPE_STRING)\n}), NAME_TOAST);\n\n// --- Main component ---\n\n// @vue/component\nconst BToast = /*#__PURE__*/extend({\n name: NAME_TOAST,\n mixins: [attrsMixin, idMixin, modelMixin, listenOnRootMixin, normalizeSlotMixin, scopedStyleMixin],\n inheritAttrs: false,\n props,\n data() {\n return {\n isMounted: false,\n doRender: false,\n localShow: false,\n isTransitioning: false,\n isHiding: false,\n order: 0,\n dismissStarted: 0,\n resumeDismiss: 0\n };\n },\n computed: {\n toastClasses() {\n const {\n appendToast,\n variant\n } = this;\n return {\n 'b-toast-solid': this.solid,\n 'b-toast-append': appendToast,\n 'b-toast-prepend': !appendToast,\n [`b-toast-${variant}`]: variant\n };\n },\n slotScope() {\n const {\n hide\n } = this;\n return {\n hide\n };\n },\n computedDuration() {\n // Minimum supported duration is 1 second\n return mathMax(toInteger(this.autoHideDelay, 0), MIN_DURATION);\n },\n computedToaster() {\n return String(this.toaster);\n },\n transitionHandlers() {\n return {\n beforeEnter: this.onBeforeEnter,\n afterEnter: this.onAfterEnter,\n beforeLeave: this.onBeforeLeave,\n afterLeave: this.onAfterLeave\n };\n },\n computedAttrs() {\n return {\n ...this.bvAttrs,\n id: this.safeId(),\n tabindex: '0'\n };\n }\n },\n watch: {\n [MODEL_PROP_NAME](newValue) {\n this[newValue ? 'show' : 'hide']();\n },\n localShow(newValue) {\n if (newValue !== this[MODEL_PROP_NAME]) {\n this.$emit(MODEL_EVENT_NAME, newValue);\n }\n },\n /* istanbul ignore next */\n toaster() {\n // If toaster target changed, make sure toaster exists\n this.$nextTick(this.ensureToaster);\n },\n /* istanbul ignore next */\n static(newValue) {\n // If static changes to true, and the toast is showing,\n // ensure the toaster target exists\n if (newValue && this.localShow) {\n this.ensureToaster();\n }\n }\n },\n created() {\n // Create private non-reactive props\n this.$_dismissTimer = null;\n },\n mounted() {\n this.isMounted = true;\n this.$nextTick(() => {\n if (this[MODEL_PROP_NAME]) {\n requestAF(() => {\n this.show();\n });\n }\n });\n // Listen for global $root show events\n this.listenOnRoot(getRootActionEventName(NAME_TOAST, EVENT_NAME_SHOW), id => {\n if (id === this.safeId()) {\n this.show();\n }\n });\n // Listen for global $root hide events\n this.listenOnRoot(getRootActionEventName(NAME_TOAST, EVENT_NAME_HIDE), id => {\n if (!id || id === this.safeId()) {\n this.hide();\n }\n });\n // Make sure we hide when toaster is destroyed\n /* istanbul ignore next: difficult to test */\n this.listenOnRoot(getRootEventName(NAME_TOASTER, EVENT_NAME_DESTROYED), toaster => {\n /* istanbul ignore next */\n if (toaster === this.computedToaster) {\n this.hide();\n }\n });\n },\n beforeDestroy() {\n this.clearDismissTimer();\n },\n methods: {\n show() {\n if (!this.localShow) {\n this.ensureToaster();\n const showEvent = this.buildEvent(EVENT_NAME_SHOW);\n this.emitEvent(showEvent);\n this.dismissStarted = this.resumeDismiss = 0;\n this.order = Date.now() * (this.appendToast ? 1 : -1);\n this.isHiding = false;\n this.doRender = true;\n this.$nextTick(() => {\n // We show the toast after we have rendered the portal and b-toast wrapper\n // so that screen readers will properly announce the toast\n requestAF(() => {\n this.localShow = true;\n });\n });\n }\n },\n hide() {\n if (this.localShow) {\n const hideEvent = this.buildEvent(EVENT_NAME_HIDE);\n this.emitEvent(hideEvent);\n this.setHoverHandler(false);\n this.dismissStarted = this.resumeDismiss = 0;\n this.clearDismissTimer();\n this.isHiding = true;\n requestAF(() => {\n this.localShow = false;\n });\n }\n },\n buildEvent(type) {\n let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n return new BvEvent(type, {\n cancelable: false,\n target: this.$el || null,\n relatedTarget: null,\n ...options,\n vueTarget: this,\n componentId: this.safeId()\n });\n },\n emitEvent(bvEvent) {\n const {\n type\n } = bvEvent;\n this.emitOnRoot(getRootEventName(NAME_TOAST, type), bvEvent);\n this.$emit(type, bvEvent);\n },\n ensureToaster() {\n if (this.static) {\n return;\n }\n const {\n computedToaster\n } = this;\n if (!Wormhole.hasTarget(computedToaster)) {\n const div = document.createElement('div');\n document.body.appendChild(div);\n const toaster = createNewChildComponent(this.bvEventRoot, BToaster, {\n propsData: {\n name: computedToaster\n }\n });\n toaster.$mount(div);\n }\n },\n startDismissTimer() {\n this.clearDismissTimer();\n if (!this.noAutoHide) {\n this.$_dismissTimer = setTimeout(this.hide, this.resumeDismiss || this.computedDuration);\n this.dismissStarted = Date.now();\n this.resumeDismiss = 0;\n }\n },\n clearDismissTimer() {\n clearTimeout(this.$_dismissTimer);\n this.$_dismissTimer = null;\n },\n setHoverHandler(on) {\n const el = this.$refs['b-toast'];\n eventOnOff(on, el, 'mouseenter', this.onPause, EVENT_OPTIONS_NO_CAPTURE);\n eventOnOff(on, el, 'mouseleave', this.onUnPause, EVENT_OPTIONS_NO_CAPTURE);\n },\n onPause() {\n // Determine time remaining, and then pause timer\n if (this.noAutoHide || this.noHoverPause || !this.$_dismissTimer || this.resumeDismiss) {\n return;\n }\n const passed = Date.now() - this.dismissStarted;\n if (passed > 0) {\n this.clearDismissTimer();\n this.resumeDismiss = mathMax(this.computedDuration - passed, MIN_DURATION);\n }\n },\n onUnPause() {\n // Restart timer with max of time remaining or 1 second\n if (this.noAutoHide || this.noHoverPause || !this.resumeDismiss) {\n this.resumeDismiss = this.dismissStarted = 0;\n return;\n }\n this.startDismissTimer();\n },\n onLinkClick() {\n // We delay the close to allow time for the\n // browser to process the link click\n this.$nextTick(() => {\n requestAF(() => {\n this.hide();\n });\n });\n },\n onBeforeEnter() {\n this.isTransitioning = true;\n },\n onAfterEnter() {\n this.isTransitioning = false;\n const hiddenEvent = this.buildEvent(EVENT_NAME_SHOWN);\n this.emitEvent(hiddenEvent);\n this.startDismissTimer();\n this.setHoverHandler(true);\n },\n onBeforeLeave() {\n this.isTransitioning = true;\n },\n onAfterLeave() {\n this.isTransitioning = false;\n this.order = 0;\n this.resumeDismiss = this.dismissStarted = 0;\n const hiddenEvent = this.buildEvent(EVENT_NAME_HIDDEN);\n this.emitEvent(hiddenEvent);\n this.doRender = false;\n },\n // Render helper for generating the toast\n makeToast(h) {\n const {\n slotScope\n } = this;\n const link = isLink(this);\n const $headerContent = [];\n const $title = this.normalizeSlot(SLOT_NAME_TOAST_TITLE, slotScope);\n if ($title) {\n $headerContent.push($title);\n }\n let $header = h();\n if ($headerContent.length > 0) {\n $header = h(this.headerTag, {\n staticClass: 'toast-header',\n class: this.headerClass\n }, $headerContent);\n }\n const $body = h(link ? BLink : 'div', {\n staticClass: 'toast-body',\n class: this.bodyClass,\n props: link ? pluckProps(linkProps, this) : {},\n on: link ? {\n click: this.onLinkClick\n } : {}\n }, this.normalizeSlot(SLOT_NAME_DEFAULT, slotScope));\n return h('div', {\n staticClass: 'toast',\n class: this.toastClass,\n attrs: this.computedAttrs,\n key: `toast-${this[COMPONENT_UID_KEY]}`,\n ref: 'toast'\n }, [$header, $body]);\n }\n },\n render(h) {\n if (!this.doRender || !this.isMounted) {\n return h();\n }\n const {\n order,\n static: isStatic,\n isHiding,\n isStatus\n } = this;\n const name = `b-toast-${this[COMPONENT_UID_KEY]}`;\n const $toast = h('div', {\n staticClass: 'b-toast',\n class: this.toastClasses,\n attrs: {\n // If scoped styles are applied and the toast is not static,\n // make sure the scoped style data attribute is applied\n ...(isStatic ? {} : this.scopedStyleAttrs),\n id: this.safeId('_toast_outer'),\n role: isHiding ? null : isStatus ? 'status' : 'alert',\n 'aria-live': isHiding ? null : isStatus ? 'polite' : 'assertive',\n 'aria-atomic': isHiding ? null : 'true'\n },\n key: name,\n ref: 'b-toast'\n }, [h(BVTransition, {\n props: {\n noFade: this.noFade\n },\n on: this.transitionHandlers\n }, [this.localShow ? this.makeToast(h) : h()])]);\n return h(Portal, {\n props: {\n name,\n to: this.computedToaster,\n order,\n slim: true,\n disabled: isStatic\n }\n }, [$toast]);\n }\n});\n\nexport { BToast, props };\n","import { NAME_TOAST_POP, NAME_TOASTER, NAME_TOAST } from '../../../constants/components';\nimport { HOOK_EVENT_NAME_DESTROYED, EVENT_NAME_HIDDEN, EVENT_NAME_DESTROYED, EVENT_NAME_SHOW, EVENT_NAME_HIDE } from '../../../constants/events';\nimport { useParentMixin } from '../../../mixins/use-parent';\nimport { concat } from '../../../utils/array';\nimport { getComponentConfig } from '../../../utils/config';\nimport { requestAF } from '../../../utils/dom';\nimport { getRootEventName, getRootActionEventName } from '../../../utils/events';\nimport { isUndefined } from '../../../utils/inspect';\nimport { keys, omit, hasOwnProperty, defineProperty, assign, defineProperties, readonlyDescriptor } from '../../../utils/object';\nimport { pluginFactory } from '../../../utils/plugins';\nimport { warn, warnNotClient } from '../../../utils/warn';\nimport { createNewChildComponent } from '../../../utils/create-new-child-component';\nimport { getEventRoot } from '../../../utils/get-event-root';\nimport { props, BToast } from '../toast';\n\n/**\n * Plugin for adding `$bvToast` property to all Vue instances\n */\n\n// --- Constants ---\n\nconst PROP_NAME = '$bvToast';\nconst PROP_NAME_PRIV = '_bv__toast';\n\n// Base toast props that are allowed\n// Some may be ignored or overridden on some message boxes\n// Prop ID is allowed, but really only should be used for testing\n// We need to add it in explicitly as it comes from the `idMixin`\nconst BASE_PROPS = ['id', ...keys(omit(props, ['static', 'visible']))];\n\n// Map prop names to toast slot names\nconst propsToSlots = {\n toastContent: 'default',\n title: 'toast-title'\n};\n\n// --- Helper methods ---\n\n// Method to filter only recognized props that are not undefined\nconst filterOptions = options => {\n return BASE_PROPS.reduce((memo, key) => {\n if (!isUndefined(options[key])) {\n memo[key] = options[key];\n }\n return memo;\n }, {});\n};\n\n// Method to install `$bvToast` VM injection\nconst plugin = Vue => {\n // Create a private sub-component constructor that\n // extends BToast and self-destructs after hidden\n // @vue/component\n const BVToastPop = Vue.extend({\n name: NAME_TOAST_POP,\n extends: BToast,\n mixins: [useParentMixin],\n destroyed() {\n // Make sure we not in document any more\n const {\n $el\n } = this;\n if ($el && $el.parentNode) {\n $el.parentNode.removeChild($el);\n }\n },\n mounted() {\n // Self destruct handler\n const handleDestroy = () => {\n // Ensure the toast has been force hidden\n this.localShow = false;\n this.doRender = false;\n this.$nextTick(() => {\n this.$nextTick(() => {\n // In a `requestAF()` to release control back to application\n // and to allow the portal-target time to remove the content\n requestAF(() => {\n this.$destroy();\n });\n });\n });\n };\n // Self destruct if parent destroyed\n this.bvParent.$once(HOOK_EVENT_NAME_DESTROYED, handleDestroy);\n // Self destruct after hidden\n this.$once(EVENT_NAME_HIDDEN, handleDestroy);\n // Self destruct when toaster is destroyed\n this.listenOnRoot(getRootEventName(NAME_TOASTER, EVENT_NAME_DESTROYED), toaster => {\n /* istanbul ignore next: hard to test */\n if (toaster === this.toaster) {\n handleDestroy();\n }\n });\n }\n });\n\n // Private method to generate the on-demand toast\n const makeToast = (props, parent) => {\n if (warnNotClient(PROP_NAME)) {\n /* istanbul ignore next */\n return;\n }\n // Create an instance of `BVToastPop` component\n const toast = createNewChildComponent(parent, BVToastPop, {\n // We set parent as the local VM so these toasts can emit events on the\n // app `$root`, and it ensures `BToast` is destroyed when parent is destroyed\n propsData: {\n ...filterOptions(getComponentConfig(NAME_TOAST)),\n // Add in (filtered) user supplied props\n ...omit(props, keys(propsToSlots)),\n // Props that can't be overridden\n static: false,\n visible: true\n }\n });\n // Convert certain props to slots\n keys(propsToSlots).forEach(prop => {\n const value = props[prop];\n if (!isUndefined(value)) {\n toast.$slots[propsToSlots[prop]] = concat(value);\n }\n });\n // Create a mount point (a DIV) and mount it (which triggers the show)\n const div = document.createElement('div');\n document.body.appendChild(div);\n toast.$mount(div);\n };\n\n // Declare BvToast instance property class\n class BvToast {\n constructor(vm) {\n // Assign the new properties to this instance\n assign(this, {\n _vm: vm,\n _root: getEventRoot(vm)\n });\n // Set these properties as read-only and non-enumerable\n defineProperties(this, {\n _vm: readonlyDescriptor(),\n _root: readonlyDescriptor()\n });\n }\n\n // --- Public Instance methods ---\n\n // Opens a user defined toast and returns immediately\n toast(content) {\n let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n if (!content || warnNotClient(PROP_NAME)) {\n /* istanbul ignore next */\n return;\n }\n makeToast({\n ...filterOptions(options),\n toastContent: content\n }, this._vm);\n }\n\n // shows a `` component with the specified ID\n show(id) {\n if (id) {\n this._root.$emit(getRootActionEventName(NAME_TOAST, EVENT_NAME_SHOW), id);\n }\n }\n\n // Hide a toast with specified ID, or if not ID all toasts\n hide() {\n let id = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;\n this._root.$emit(getRootActionEventName(NAME_TOAST, EVENT_NAME_HIDE), id);\n }\n }\n\n // Add our instance mixin\n Vue.mixin({\n beforeCreate() {\n // Because we need access to `$root` for `$emits`, and VM for parenting,\n // we have to create a fresh instance of `BvToast` for each VM\n this[PROP_NAME_PRIV] = new BvToast(this);\n }\n });\n\n // Define our read-only `$bvToast` instance property\n // Placed in an if just in case in HMR mode\n if (!hasOwnProperty(Vue.prototype, PROP_NAME)) {\n defineProperty(Vue.prototype, PROP_NAME, {\n get() {\n /* istanbul ignore next */\n if (!this || !this[PROP_NAME_PRIV]) {\n warn(`\"${PROP_NAME}\" must be accessed from a Vue instance \"this\" context.`, NAME_TOAST);\n }\n return this[PROP_NAME_PRIV];\n }\n });\n }\n};\nconst BVToastPlugin = /*#__PURE__*/pluginFactory({\n plugins: {\n plugin\n }\n});\n\nexport { BVToastPlugin };\n","import { BVToastPlugin } from './helpers/bv-toast';\nimport { BToast } from './toast';\nexport { BToast } from './toast';\nimport { BToaster } from './toaster';\nexport { BToaster } from './toaster';\nimport { pluginFactory } from '../../utils/plugins';\n\nconst ToastPlugin = /*#__PURE__*/pluginFactory({\n components: {\n BToast,\n BToaster\n },\n // $bvToast injection\n plugins: {\n BVToastPlugin\n }\n});\n\nexport { ToastPlugin };\n","import isFunction from 'lodash/isFunction';\nimport { ToastPlugin } from '../../../vendor/bootstrap-vue/src/components/toast/index';\nimport CloseButton from '../../shared_components/close_button/close_button';\n\n/* eslint-disable import/no-default-export */\nconst DEFAULT_OPTIONS = {\n autoHideDelay: 5000,\n toastClass: 'gl-toast',\n isStatus: true,\n toaster: 'b-toaster-bottom-left'\n};\nlet toastsCount = 0;\nfunction renderTitle(h, toast, options) {\n const nodes = [h(CloseButton, {\n class: ['gl-toast-close-button'],\n on: {\n click: toast.hide\n }\n })];\n if (options.action) {\n nodes.splice(0, 0, h('a', {\n role: 'button',\n class: ['gl-toast-action'],\n on: {\n click: e => options.action.onClick(e, toast)\n }\n }, options.action.text));\n }\n return nodes;\n}\nfunction showToast(message) {\n let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};\n // eslint-disable-next-line @gitlab/tailwind-no-interpolation -- Not a CSS utility\n const id = `gl-toast-${toastsCount}`;\n toastsCount += 1;\n const hide = () => {\n this.$bvToast.hide(id);\n };\n const toast = {\n id,\n hide\n };\n if (isFunction(options.onComplete)) {\n const toastHiddenCallback = e => {\n if (e.componentId === id) {\n this.$root.$off('bv::toast:hidden', toastHiddenCallback);\n options.onComplete(e);\n }\n };\n this.$root.$on('bv::toast:hidden', toastHiddenCallback);\n }\n const updatedAutoHideDelay = !Number.isNaN(options === null || options === void 0 ? void 0 : options.autoHideDelay) ? {\n autoHideDelay: options.autoHideDelay\n } : null;\n this.$bvToast.toast(message, {\n ...DEFAULT_OPTIONS,\n ...updatedAutoHideDelay,\n id,\n title: renderTitle(this.$createElement, toast, options)\n });\n return toast;\n}\n\n/**\n * Note: This is not a typical Vue component and needs to be registered before instantiating a Vue app.\n * Once registered, the toast will be globally available throughout your app.\n *\n * See https://gitlab-org.gitlab.io/gitlab-ui/ for detailed documentation.\n */\nvar toast = {\n install(Vue) {\n Vue.use(ToastPlugin);\n Vue.mixin({\n beforeCreate() {\n if (this.$toast) {\n return;\n }\n this.$toast = {\n show: showToast.bind(this)\n };\n }\n });\n }\n};\n\nexport default toast;\n","import Vue from 'vue'\n\nexport default function ({\n idProp = vm => vm.item.id,\n} = {}) {\n const store = {}\n const vm = new Vue({\n data () {\n return {\n store,\n }\n },\n })\n\n // @vue/component\n return {\n data () {\n return {\n idState: null,\n }\n },\n\n created () {\n this.$_id = null\n if (typeof idProp === 'function') {\n this.$_getId = () => idProp.call(this, this)\n } else {\n this.$_getId = () => this[idProp]\n }\n this.$watch(this.$_getId, {\n handler (value) {\n this.$nextTick(() => {\n this.$_id = value\n })\n },\n immediate: true,\n })\n this.$_updateIdState()\n },\n\n beforeUpdate () {\n this.$_updateIdState()\n },\n\n methods: {\n /**\n * Initialize an idState\n * @param {number|string} id Unique id for the data\n */\n $_idStateInit (id) {\n const factory = this.$options.idState\n if (typeof factory === 'function') {\n const data = factory.call(this, this)\n vm.$set(store, id, data)\n this.$_id = id\n return data\n } else {\n throw new Error('[mixin IdState] Missing `idState` function on component definition.')\n }\n },\n\n /**\n * Ensure idState is created and up-to-date\n */\n $_updateIdState () {\n const id = this.$_getId()\n if (id == null) {\n console.warn(`No id found for IdState with idProp: '${idProp}'.`)\n }\n if (id !== this.$_id) {\n if (!store[id]) {\n this.$_idStateInit(id)\n }\n this.idState = store[id]\n }\n },\n },\n }\n}\n","/** Used to match `RegExp` flags from their coerced string values. */\nvar reFlags = /\\w*$/;\n\n/**\n * Creates a clone of `regexp`.\n *\n * @private\n * @param {Object} regexp The regexp to clone.\n * @returns {Object} Returns the cloned regexp.\n */\nfunction cloneRegExp(regexp) {\n var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));\n result.lastIndex = regexp.lastIndex;\n return result;\n}\n\nmodule.exports = cloneRegExp;\n","import { extend } from '../../../vue';\nimport { SLOT_NAME_BOTTOM_ROW } from '../../../constants/slots';\nimport { isFunction } from '../../../utils/inspect';\nimport { BTr } from '../tr';\n\n// --- Props ---\n\nconst props = {};\n\n// --- Mixin ---\n\n// @vue/component\nconst bottomRowMixin = extend({\n props,\n methods: {\n renderBottomRow() {\n const {\n computedFields: fields,\n stacked,\n tbodyTrClass,\n tbodyTrAttr\n } = this;\n const h = this.$createElement;\n\n // Static bottom row slot (hidden in visibly stacked mode as we can't control the data-label)\n // If in *always* stacked mode, we don't bother rendering the row\n if (!this.hasNormalizedSlot(SLOT_NAME_BOTTOM_ROW) || stacked === true || stacked === '') {\n return h();\n }\n return h(BTr, {\n staticClass: 'b-table-bottom-row',\n class: [isFunction(tbodyTrClass) ? /* istanbul ignore next */tbodyTrClass(null, 'row-bottom') : tbodyTrClass],\n attrs: isFunction(tbodyTrAttr) ? /* istanbul ignore next */tbodyTrAttr(null, 'row-bottom') : tbodyTrAttr,\n key: 'b-bottom-row'\n }, this.normalizeSlot(SLOT_NAME_BOTTOM_ROW, {\n columns: fields.length,\n fields\n }));\n }\n }\n});\n\nexport { bottomRowMixin, props };\n","import { extend } from '../../../vue';\nimport { MODEL_EVENT_NAME_PREFIX } from '../../../constants/events';\nimport { PROP_TYPE_BOOLEAN } from '../../../constants/props';\nimport { SLOT_NAME_TABLE_BUSY } from '../../../constants/slots';\nimport { stopEvent } from '../../../utils/events';\nimport { isFunction } from '../../../utils/inspect';\nimport { makeProp } from '../../../utils/props';\nimport { BTr } from '../tr';\nimport { BTd } from '../td';\n\n// --- Constants ---\n\nconst MODEL_PROP_NAME_BUSY = 'busy';\nconst MODEL_EVENT_NAME_BUSY = MODEL_EVENT_NAME_PREFIX + MODEL_PROP_NAME_BUSY;\n\n// --- Props ---\n\nconst props = {\n [MODEL_PROP_NAME_BUSY]: makeProp(PROP_TYPE_BOOLEAN, false)\n};\n\n// --- Mixin ---\n\n// @vue/component\nconst busyMixin = extend({\n props,\n data() {\n return {\n localBusy: false\n };\n },\n computed: {\n computedBusy() {\n return this[MODEL_PROP_NAME_BUSY] || this.localBusy;\n }\n },\n watch: {\n localBusy(newValue, oldValue) {\n if (newValue !== oldValue) {\n this.$emit(MODEL_EVENT_NAME_BUSY, newValue);\n }\n }\n },\n methods: {\n // Event handler helper\n stopIfBusy(event) {\n // If table is busy (via provider) then don't propagate\n if (this.computedBusy) {\n stopEvent(event);\n return true;\n }\n return false;\n },\n // Render the busy indicator or return `null` if not busy\n renderBusy() {\n const {\n tbodyTrClass,\n tbodyTrAttr\n } = this;\n const h = this.$createElement;\n\n // Return a busy indicator row, or `null` if not busy\n if (this.computedBusy && this.hasNormalizedSlot(SLOT_NAME_TABLE_BUSY)) {\n return h(BTr, {\n staticClass: 'b-table-busy-slot',\n class: [isFunction(tbodyTrClass) ? /* istanbul ignore next */tbodyTrClass(null, SLOT_NAME_TABLE_BUSY) : tbodyTrClass],\n attrs: isFunction(tbodyTrAttr) ? /* istanbul ignore next */tbodyTrAttr(null, SLOT_NAME_TABLE_BUSY) : tbodyTrAttr,\n key: 'table-busy-slot'\n }, [h(BTd, {\n props: {\n colspan: this.computedFields.length || null\n }\n }, [this.normalizeSlot(SLOT_NAME_TABLE_BUSY)])]);\n }\n\n // We return `null` here so that we can determine if we need to\n // render the table items rows or not\n return null;\n }\n }\n});\n\nexport { busyMixin, props };\n","import { extend } from '../../../vue';\nimport { PROP_TYPE_STRING, PROP_TYPE_BOOLEAN } from '../../../constants/props';\nimport { SLOT_NAME_TABLE_BUSY, SLOT_NAME_EMPTYFILTERED, SLOT_NAME_EMPTY } from '../../../constants/slots';\nimport { htmlOrText } from '../../../utils/html';\nimport { isFunction } from '../../../utils/inspect';\nimport { makeProp } from '../../../utils/props';\nimport { safeVueInstance } from '../../../utils/safe-vue-instance';\nimport { BTr } from '../tr';\nimport { BTd } from '../td';\n\n// --- Props ---\n\nconst props = {\n emptyFilteredHtml: makeProp(PROP_TYPE_STRING),\n emptyFilteredText: makeProp(PROP_TYPE_STRING, 'There are no records matching your request'),\n emptyHtml: makeProp(PROP_TYPE_STRING),\n emptyText: makeProp(PROP_TYPE_STRING, 'There are no records to show'),\n showEmpty: makeProp(PROP_TYPE_BOOLEAN, false)\n};\n\n// --- Mixin ---\n\n// @vue/component\nconst emptyMixin = extend({\n props,\n methods: {\n renderEmpty() {\n const {\n computedItems: items,\n computedBusy\n } = safeVueInstance(this);\n const h = this.$createElement;\n let $empty = h();\n if (this.showEmpty && (!items || items.length === 0) && !(computedBusy && this.hasNormalizedSlot(SLOT_NAME_TABLE_BUSY))) {\n const {\n computedFields: fields,\n isFiltered,\n emptyText,\n emptyHtml,\n emptyFilteredText,\n emptyFilteredHtml,\n tbodyTrClass,\n tbodyTrAttr\n } = this;\n $empty = this.normalizeSlot(isFiltered ? SLOT_NAME_EMPTYFILTERED : SLOT_NAME_EMPTY, {\n emptyFilteredHtml,\n emptyFilteredText,\n emptyHtml,\n emptyText,\n fields,\n // Not sure why this is included, as it will always be an empty array\n items\n });\n if (!$empty) {\n $empty = h('div', {\n class: ['gl-text-center', 'gl-my-3'],\n domProps: isFiltered ? htmlOrText(emptyFilteredHtml, emptyFilteredText) : htmlOrText(emptyHtml, emptyText)\n });\n }\n $empty = h(BTd, {\n props: {\n colspan: fields.length || null\n }\n }, [h('div', {\n attrs: {\n role: 'alert',\n 'aria-live': 'polite'\n }\n }, [$empty])]);\n $empty = h(BTr, {\n staticClass: 'b-table-empty-row',\n class: [isFunction(tbodyTrClass) ? /* istanbul ignore next */tbodyTrClass(null, 'row-empty') : tbodyTrClass],\n attrs: isFunction(tbodyTrAttr) ? /* istanbul ignore next */tbodyTrAttr(null, 'row-empty') : tbodyTrAttr,\n key: isFiltered ? 'b-empty-filtered-row' : 'b-empty-row'\n }, [$empty]);\n }\n return $empty;\n }\n }\n});\n\nexport { emptyMixin, props };\n","import { isUndefinedOrNull, isObject, isDate } from './inspect';\nimport { keys } from './object';\nimport { toString } from './string';\n\n// Recursively stringifies the values of an object, space separated, in an\n// SSR safe deterministic way (keys are sorted before stringification)\n//\n// ex:\n// { b: 3, c: { z: 'zzz', d: null, e: 2 }, d: [10, 12, 11], a: 'one' }\n// becomes\n// 'one 3 2 zzz 10 12 11'\n//\n// Strings are returned as-is\n// Numbers get converted to string\n// `null` and `undefined` values are filtered out\n// Dates are converted to their native string format\nconst stringifyObjectValues = value => {\n if (isUndefinedOrNull(value)) {\n return '';\n }\n // Arrays are also object, and keys just returns the array indexes\n // Date objects we convert to strings\n if (isObject(value) && !isDate(value)) {\n return keys(value).sort() // Sort to prevent SSR issues on pre-rendered sorted tables\n .map(k => stringifyObjectValues(value[k])).filter(v => !!v) // Ignore empty strings\n .join(' ');\n }\n return toString(value);\n};\n\nexport { stringifyObjectValues };\n","import { arrayIncludes } from '../../../utils/array';\nimport { isFunction, isArray } from '../../../utils/inspect';\nimport { keys, clone, pick } from '../../../utils/object';\nimport { IGNORED_FIELD_KEYS } from './constants';\n\n// Return a copy of a row after all reserved fields have been filtered out\nconst sanitizeRow = function (row, ignoreFields, includeFields) {\n let fieldsObj = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};\n // We first need to format the row based on the field configurations\n // This ensures that we add formatted values for keys that may not\n // exist in the row itself\n const formattedRow = keys(fieldsObj).reduce((result, key) => {\n const field = fieldsObj[key];\n const {\n filterByFormatted\n } = field;\n const formatter = isFunction(filterByFormatted) ? /* istanbul ignore next */filterByFormatted : filterByFormatted ? /* istanbul ignore next */field.formatter : null;\n if (isFunction(formatter)) {\n result[key] = formatter(row[key], key, row);\n }\n return result;\n }, clone(row));\n\n // Determine the allowed keys:\n // - Ignore special fields that start with `_`\n // - Ignore fields in the `ignoreFields` array\n // - Include only fields in the `includeFields` array\n const allowedKeys = keys(formattedRow).filter(key => !IGNORED_FIELD_KEYS[key] && !(isArray(ignoreFields) && ignoreFields.length > 0 && arrayIncludes(ignoreFields, key)) && !(isArray(includeFields) && includeFields.length > 0 && !arrayIncludes(includeFields, key)));\n return pick(formattedRow, allowedKeys);\n};\n\nexport { sanitizeRow };\n","import { extend } from '../../../vue';\nimport { NAME_TABLE } from '../../../constants/components';\nimport { EVENT_NAME_FILTERED } from '../../../constants/events';\nimport { PROP_TYPE_ARRAY_OBJECT_STRING, PROP_TYPE_REG_EXP, PROP_TYPE_NUMBER_STRING, PROP_TYPE_FUNCTION, PROP_TYPE_ARRAY } from '../../../constants/props';\nimport { RX_DIGITS, RX_SPACES } from '../../../constants/regex';\nimport { concat } from '../../../utils/array';\nimport { cloneDeep } from '../../../utils/clone-deep';\nimport { identity } from '../../../utils/identity';\nimport { isString, isRegExp, isFunction } from '../../../utils/inspect';\nimport { looseEqual } from '../../../utils/loose-equal';\nimport { toInteger } from '../../../utils/number';\nimport { makeProp, hasPropFunction } from '../../../utils/props';\nimport { escapeRegExp } from '../../../utils/string';\nimport { warn } from '../../../utils/warn';\nimport { stringifyRecordValues } from './stringify-record-values';\n\n// --- Constants ---\n\nconst DEBOUNCE_DEPRECATED_MSG = 'Prop \"filter-debounce\" is deprecated. Use the debounce feature of \"\" instead.';\n\n// --- Props ---\n\nconst props = {\n filter: makeProp([...PROP_TYPE_ARRAY_OBJECT_STRING, PROP_TYPE_REG_EXP]),\n filterDebounce: makeProp(PROP_TYPE_NUMBER_STRING, 0, value => {\n return RX_DIGITS.test(String(value));\n }),\n filterFunction: makeProp(PROP_TYPE_FUNCTION),\n filterIgnoredFields: makeProp(PROP_TYPE_ARRAY, []),\n filterIncludedFields: makeProp(PROP_TYPE_ARRAY, [])\n};\n\n// --- Mixin ---\n\n// @vue/component\nconst filteringMixin = extend({\n props,\n data() {\n return {\n // Flag for displaying which empty slot to show and some event triggering\n isFiltered: false,\n // Where we store the copy of the filter criteria after debouncing\n // We pre-set it with the sanitized filter value\n localFilter: this.filterSanitize(this.filter)\n };\n },\n computed: {\n computedFilterIgnored() {\n return concat(this.filterIgnoredFields || []).filter(identity);\n },\n computedFilterIncluded() {\n return concat(this.filterIncludedFields || []).filter(identity);\n },\n computedFilterDebounce() {\n const ms = toInteger(this.filterDebounce, 0);\n /* istanbul ignore next */\n if (ms > 0) {\n warn(DEBOUNCE_DEPRECATED_MSG, NAME_TABLE);\n }\n return ms;\n },\n localFiltering() {\n return this.hasProvider ? !!this.noProviderFiltering : true;\n },\n // For watching changes to `filteredItems` vs `localItems`\n filteredCheck() {\n const {\n filteredItems,\n localItems,\n localFilter\n } = this;\n return {\n filteredItems,\n localItems,\n localFilter\n };\n },\n // Sanitized/normalize filter-function prop\n localFilterFn() {\n // Return `null` to signal to use internal filter function\n const {\n filterFunction\n } = this;\n return hasPropFunction(filterFunction) ? filterFunction : null;\n },\n // Returns the records in `localItems` that match the filter criteria\n // Returns the original `localItems` array if not sorting\n filteredItems() {\n // Note the criteria is debounced and sanitized\n const {\n localItems: items,\n localFilter: criteria\n } = this;\n\n // Resolve the filtering function, when requested\n // We prefer the provided filtering function and fallback to the internal one\n // When no filtering criteria is specified the filtering factories will return `null`\n const filterFn = this.localFiltering ? this.filterFnFactory(this.localFilterFn, criteria) || this.defaultFilterFnFactory(criteria) : null;\n\n // We only do local filtering when requested and there are records to filter\n return filterFn && items.length > 0 ? items.filter(filterFn) : items;\n }\n },\n watch: {\n // Watch for debounce being set to 0\n computedFilterDebounce(newValue) {\n if (!newValue && this.$_filterTimer) {\n this.clearFilterTimer();\n this.localFilter = this.filterSanitize(this.filter);\n }\n },\n // Watch for changes to the filter criteria, and debounce if necessary\n filter: {\n // We need a deep watcher in case the user passes\n // an object when using `filter-function`\n deep: true,\n handler(newCriteria) {\n const timeout = this.computedFilterDebounce;\n this.clearFilterTimer();\n if (timeout && timeout > 0) {\n // If we have a debounce time, delay the update of `localFilter`\n this.$_filterTimer = setTimeout(() => {\n this.localFilter = this.filterSanitize(newCriteria);\n }, timeout);\n } else {\n // Otherwise, immediately update `localFilter` with `newFilter` value\n this.localFilter = this.filterSanitize(newCriteria);\n }\n }\n },\n // Watch for changes to the filter criteria and filtered items vs `localItems`\n // Set visual state and emit events as required\n filteredCheck(_ref) {\n let {\n filteredItems,\n localFilter\n } = _ref;\n // Determine if the dataset is filtered or not\n let isFiltered = false;\n if (!localFilter) {\n // If filter criteria is falsey\n isFiltered = false;\n } else if (looseEqual(localFilter, []) || looseEqual(localFilter, {})) {\n // If filter criteria is an empty array or object\n isFiltered = false;\n } else if (localFilter) {\n // If filter criteria is truthy\n isFiltered = true;\n }\n if (isFiltered) {\n this.$emit(EVENT_NAME_FILTERED, filteredItems, filteredItems.length);\n }\n this.isFiltered = isFiltered;\n },\n isFiltered(newValue, oldValue) {\n if (newValue === false && oldValue === true) {\n // We need to emit a filtered event if `isFiltered` transitions from `true` to\n // `false` so that users can update their pagination controls\n const {\n localItems\n } = this;\n this.$emit(EVENT_NAME_FILTERED, localItems, localItems.length);\n }\n }\n },\n created() {\n // Create private non-reactive props\n this.$_filterTimer = null;\n // If filter is \"pre-set\", set the criteria\n // This will trigger any watchers/dependents\n // this.localFilter = this.filterSanitize(this.filter)\n // Set the initial filtered state in a `$nextTick()` so that\n // we trigger a filtered event if needed\n this.$nextTick(() => {\n this.isFiltered = Boolean(this.localFilter);\n });\n },\n beforeDestroy() {\n this.clearFilterTimer();\n },\n methods: {\n clearFilterTimer() {\n clearTimeout(this.$_filterTimer);\n this.$_filterTimer = null;\n },\n filterSanitize(criteria) {\n // Sanitizes filter criteria based on internal or external filtering\n if (this.localFiltering && !this.localFilterFn && !(isString(criteria) || isRegExp(criteria))) {\n // If using internal filter function, which only accepts string or RegExp,\n // return '' to signify no filter\n return '';\n }\n\n // Could be a string, object or array, as needed by external filter function\n // We use `cloneDeep` to ensure we have a new copy of an object or array\n // without Vue's reactive observers\n return cloneDeep(criteria);\n },\n // Filter Function factories\n filterFnFactory(filterFn, criteria) {\n // Wrapper factory for external filter functions\n // Wrap the provided filter-function and return a new function\n // Returns `null` if no filter-function defined or if criteria is falsey\n // Rather than directly grabbing `this.computedLocalFilterFn` or `this.filterFunction`\n // we have it passed, so that the caller computed prop will be reactive to changes\n // in the original filter-function (as this routine is a method)\n if (!filterFn || !isFunction(filterFn) || !criteria || looseEqual(criteria, []) || looseEqual(criteria, {})) {\n return null;\n }\n\n // Build the wrapped filter test function, passing the criteria to the provided function\n const fn = item => {\n // Generated function returns true if the criteria matches part\n // of the serialized data, otherwise false\n return filterFn(item, criteria);\n };\n\n // Return the wrapped function\n return fn;\n },\n defaultFilterFnFactory(criteria) {\n // Generates the default filter function, using the given filter criteria\n // Returns `null` if no criteria or criteria format not supported\n if (!criteria || !(isString(criteria) || isRegExp(criteria))) {\n // Built in filter can only support strings or RegExp criteria (at the moment)\n return null;\n }\n\n // Build the RegExp needed for filtering\n let regExp = criteria;\n if (isString(regExp)) {\n // Escape special RegExp characters in the string and convert contiguous\n // whitespace to \\s+ matches\n const pattern = escapeRegExp(criteria).replace(RX_SPACES, '\\\\s+');\n // Build the RegExp (no need for global flag, as we only need\n // to find the value once in the string)\n regExp = new RegExp(`.*${pattern}.*`, 'i');\n }\n\n // Generate the wrapped filter test function to use\n const fn = item => {\n // This searches all row values (and sub property values) in the entire (excluding\n // special `_` prefixed keys), because we convert the record to a space-separated\n // string containing all the value properties (recursively), even ones that are\n // not visible (not specified in this.fields)\n // Users can ignore filtering on specific fields, or on only certain fields,\n // and can optionall specify searching results of fields with formatter\n //\n // TODO: Enable searching on scoped slots (optional, as it will be SLOW)\n //\n // Generated function returns true if the criteria matches part of\n // the serialized data, otherwise false\n //\n // We set `lastIndex = 0` on the `RegExp` in case someone specifies the `/g` global flag\n regExp.lastIndex = 0;\n return regExp.test(stringifyRecordValues(item, this.computedFilterIgnored, this.computedFilterIncluded, this.computedFieldsObj));\n };\n\n // Return the generated function\n return fn;\n }\n }\n});\n\nexport { filteringMixin, props };\n","import { isObject } from '../../../utils/inspect';\nimport { stringifyObjectValues } from '../../../utils/stringify-object-values';\nimport { sanitizeRow } from './sanitize-row';\n\n// Stringifies the values of a record, ignoring any special top level field keys\n// TODO: Add option to stringify `scopedSlot` items\nconst stringifyRecordValues = (row, ignoreFields, includeFields, fieldsObj) => {\n return isObject(row) ? stringifyObjectValues(sanitizeRow(row, ignoreFields, includeFields, fieldsObj)) : /* istanbul ignore next */'';\n};\n\nexport { stringifyRecordValues };\n","import { extend } from '../../../vue';\nimport { PROP_TYPE_NUMBER_STRING } from '../../../constants/props';\nimport { mathMax } from '../../../utils/math';\nimport { toInteger } from '../../../utils/number';\nimport { makeProp } from '../../../utils/props';\nimport { safeVueInstance } from '../../../utils/safe-vue-instance';\n\n// --- Props ---\n\nconst props = {\n currentPage: makeProp(PROP_TYPE_NUMBER_STRING, 1),\n perPage: makeProp(PROP_TYPE_NUMBER_STRING, 0)\n};\n\n// --- Mixin ---\n\n// @vue/component\nconst paginationMixin = extend({\n props,\n computed: {\n localPaging() {\n return this.hasProvider ? !!this.noProviderPaging : true;\n },\n paginatedItems() {\n const {\n sortedItems,\n filteredItems,\n localItems\n } = safeVueInstance(this);\n let items = sortedItems || filteredItems || localItems || [];\n const currentPage = mathMax(toInteger(this.currentPage, 1), 1);\n const perPage = mathMax(toInteger(this.perPage, 0), 0);\n // Apply local pagination\n if (this.localPaging && perPage) {\n // Grab the current page of data (which may be past filtered items limit)\n items = items.slice((currentPage - 1) * perPage, currentPage * perPage);\n }\n // Return the items to display in the table\n return items;\n }\n }\n});\n\nexport { paginationMixin, props };\n","import { extend } from '../../../vue';\nimport { NAME_TABLE } from '../../../constants/components';\nimport { EVENT_NAME_REFRESHED, EVENT_NAME_REFRESH } from '../../../constants/events';\nimport { PROP_TYPE_STRING, PROP_TYPE_ARRAY_FUNCTION, PROP_TYPE_BOOLEAN } from '../../../constants/props';\nimport { getRootEventName, getRootActionEventName } from '../../../utils/events';\nimport { isFunction, isArray, isPromise } from '../../../utils/inspect';\nimport { looseEqual } from '../../../utils/loose-equal';\nimport { clone } from '../../../utils/object';\nimport { makeProp } from '../../../utils/props';\nimport { safeVueInstance } from '../../../utils/safe-vue-instance';\nimport { warn } from '../../../utils/warn';\nimport { listenOnRootMixin } from '../../../mixins/listen-on-root';\n\n// --- Constants ---\n\nconst ROOT_EVENT_NAME_REFRESHED = getRootEventName(NAME_TABLE, EVENT_NAME_REFRESHED);\nconst ROOT_ACTION_EVENT_NAME_REFRESH = getRootActionEventName(NAME_TABLE, EVENT_NAME_REFRESH);\n\n// --- Props ---\n\nconst props = {\n // Passed to the context object\n // Not used by `` directly\n apiUrl: makeProp(PROP_TYPE_STRING),\n // Adds in 'Function' support\n items: makeProp(PROP_TYPE_ARRAY_FUNCTION, []),\n noProviderFiltering: makeProp(PROP_TYPE_BOOLEAN, false),\n noProviderPaging: makeProp(PROP_TYPE_BOOLEAN, false),\n noProviderSorting: makeProp(PROP_TYPE_BOOLEAN, false)\n};\n\n// --- Mixin ---\n\n// @vue/component\nconst providerMixin = extend({\n mixins: [listenOnRootMixin],\n props,\n computed: {\n hasProvider() {\n return isFunction(this.items);\n },\n providerTriggerContext() {\n // Used to trigger the provider function via a watcher. Only the fields that\n // are needed for triggering a provider update are included. Note that the\n // regular this.context is sent to the provider during fetches though, as they\n // may need all the prop info.\n const ctx = {\n apiUrl: this.apiUrl,\n filter: null,\n sortBy: null,\n sortDesc: null,\n perPage: null,\n currentPage: null\n };\n if (!this.noProviderFiltering) {\n // Either a string, or could be an object or array.\n ctx.filter = this.localFilter;\n }\n if (!this.noProviderSorting) {\n ctx.sortBy = this.localSortBy;\n ctx.sortDesc = this.localSortDesc;\n }\n if (!this.noProviderPaging) {\n ctx.perPage = this.perPage;\n ctx.currentPage = this.currentPage;\n }\n return clone(ctx);\n }\n },\n watch: {\n // Provider update triggering\n items(newValue) {\n // If a new provider has been specified, trigger an update\n if (this.hasProvider || isFunction(newValue)) {\n this.$nextTick(this._providerUpdate);\n }\n },\n providerTriggerContext(newValue, oldValue) {\n // Trigger the provider to update as the relevant context values have changed.\n if (!looseEqual(newValue, oldValue)) {\n this.$nextTick(this._providerUpdate);\n }\n }\n },\n mounted() {\n // Call the items provider if necessary\n if (this.hasProvider && (!this.localItems || this.localItems.length === 0)) {\n // Fetch on mount if localItems is empty\n this._providerUpdate();\n }\n // Listen for global messages to tell us to force refresh the table\n this.listenOnRoot(ROOT_ACTION_EVENT_NAME_REFRESH, id => {\n if (id === this.id || id === this) {\n this.refresh();\n }\n });\n },\n methods: {\n refresh() {\n const {\n items,\n refresh,\n computedBusy\n } = safeVueInstance(this);\n\n // Public Method: Force a refresh of the provider function\n this.$off(EVENT_NAME_REFRESHED, refresh);\n if (computedBusy) {\n // Can't force an update when forced busy by user (busy prop === true)\n if (this.localBusy && this.hasProvider) {\n // But if provider running (localBusy), re-schedule refresh once `refreshed` emitted\n this.$on(EVENT_NAME_REFRESHED, refresh);\n }\n } else {\n this.clearSelected();\n if (this.hasProvider) {\n this.$nextTick(this._providerUpdate);\n } else {\n /* istanbul ignore next */\n this.localItems = isArray(items) ? items.slice() : [];\n }\n }\n },\n // Provider related methods\n _providerSetLocal(items) {\n this.localItems = isArray(items) ? items.slice() : [];\n this.localBusy = false;\n this.$emit(EVENT_NAME_REFRESHED);\n // New root emit\n if (this.id) {\n this.emitOnRoot(ROOT_EVENT_NAME_REFRESHED, this.id);\n }\n },\n _providerUpdate() {\n // Refresh the provider function items.\n if (!this.hasProvider) {\n // Do nothing if no provider\n return;\n }\n // If table is busy, wait until refreshed before calling again\n if (safeVueInstance(this).computedBusy) {\n // Schedule a new refresh once `refreshed` is emitted\n this.$nextTick(this.refresh);\n return;\n }\n\n // Set internal busy state\n this.localBusy = true;\n\n // Call provider function with context and optional callback after DOM is fully updated\n this.$nextTick(() => {\n try {\n // Call provider function passing it the context and optional callback\n const data = this.items(this.context, this._providerSetLocal);\n if (isPromise(data)) {\n // Provider returned Promise\n data.then(items => {\n // Provider resolved with items\n this._providerSetLocal(items);\n });\n } else if (isArray(data)) {\n // Provider returned Array data\n this._providerSetLocal(data);\n } else {\n /* istanbul ignore if */\n if (this.items.length !== 2) {\n // Check number of arguments provider function requested\n // Provider not using callback (didn't request second argument), so we clear\n // busy state as most likely there was an error in the provider function\n /* istanbul ignore next */\n warn(\"Provider function didn't request callback and did not return a promise or data.\", NAME_TABLE);\n this.localBusy = false;\n }\n }\n } catch (e) /* istanbul ignore next */{\n // Provider function borked on us, so we spew out a warning\n // and clear the busy state\n warn(`Provider function error [${e.name}] ${e.message}.`, NAME_TABLE);\n this.localBusy = false;\n this.$off(EVENT_NAME_REFRESHED, this.refresh);\n }\n });\n }\n }\n});\n\nexport { props, providerMixin };\n","import { extend } from '../../../vue';\nimport { EVENT_NAME_ROW_SELECTED, EVENT_NAME_ROW_CLICKED, EVENT_NAME_FILTERED, EVENT_NAME_CONTEXT_CHANGED } from '../../../constants/events';\nimport { PROP_TYPE_BOOLEAN, PROP_TYPE_STRING } from '../../../constants/props';\nimport { arrayIncludes, createArray } from '../../../utils/array';\nimport { identity } from '../../../utils/identity';\nimport { isArray, isNumber } from '../../../utils/inspect';\nimport { looseEqual } from '../../../utils/loose-equal';\nimport { mathMin, mathMax } from '../../../utils/math';\nimport { makeProp } from '../../../utils/props';\nimport { toString } from '../../../utils/string';\nimport { sanitizeRow } from './sanitize-row';\n\n// --- Constants ---\n\nconst SELECT_MODES = ['range', 'multi', 'single'];\nconst ROLE_GRID = 'grid';\n\n// --- Props ---\n\nconst props = {\n // Disable use of click handlers for row selection\n noSelectOnClick: makeProp(PROP_TYPE_BOOLEAN, false),\n selectMode: makeProp(PROP_TYPE_STRING, 'multi', value => {\n return arrayIncludes(SELECT_MODES, value);\n }),\n selectable: makeProp(PROP_TYPE_BOOLEAN, false),\n selectedVariant: makeProp(PROP_TYPE_STRING, 'active')\n};\n\n// --- Mixin ---\n\n// @vue/component\nconst selectableMixin = extend({\n props,\n data() {\n return {\n selectedRows: [],\n selectedLastRow: -1\n };\n },\n computed: {\n isSelectable() {\n return this.selectable && this.selectMode;\n },\n hasSelectableRowClick() {\n return this.isSelectable && !this.noSelectOnClick;\n },\n supportsSelectableRows() {\n return true;\n },\n selectableHasSelection() {\n const {\n selectedRows\n } = this;\n return this.isSelectable && selectedRows && selectedRows.length > 0 && selectedRows.some(identity);\n },\n selectableIsMultiSelect() {\n return this.isSelectable && arrayIncludes(['range', 'multi'], this.selectMode);\n },\n selectableTableClasses() {\n const {\n isSelectable\n } = this;\n return {\n 'b-table-selectable': isSelectable,\n [`b-table-select-${this.selectMode}`]: isSelectable,\n 'b-table-selecting': this.selectableHasSelection,\n 'b-table-selectable-no-click': isSelectable && !this.hasSelectableRowClick\n };\n },\n selectableTableAttrs() {\n if (!this.isSelectable) {\n return {};\n }\n const role = this.bvAttrs.role || ROLE_GRID;\n return {\n role,\n // TODO:\n // Should this attribute not be included when `no-select-on-click` is set\n // since this attribute implies keyboard navigation?\n 'aria-multiselectable': role === ROLE_GRID ? toString(this.selectableIsMultiSelect) : null\n };\n }\n },\n watch: {\n computedItems(newValue, oldValue) {\n // Reset for selectable\n let equal = false;\n if (this.isSelectable && this.selectedRows.length > 0) {\n // Quick check against array length\n equal = isArray(newValue) && isArray(oldValue) && newValue.length === oldValue.length;\n for (let i = 0; equal && i < newValue.length; i++) {\n // Look for the first non-loosely equal row, after ignoring reserved fields\n equal = looseEqual(sanitizeRow(newValue[i]), sanitizeRow(oldValue[i]));\n }\n }\n if (!equal) {\n this.clearSelected();\n }\n },\n selectable(newValue) {\n this.clearSelected();\n this.setSelectionHandlers(newValue);\n },\n selectMode() {\n this.clearSelected();\n },\n hasSelectableRowClick(newValue) {\n this.clearSelected();\n this.setSelectionHandlers(!newValue);\n },\n selectedRows(selectedRows, oldValue) {\n if (this.isSelectable && !looseEqual(selectedRows, oldValue)) {\n const items = [];\n // `.forEach()` skips over non-existent indices (on sparse arrays)\n selectedRows.forEach((v, idx) => {\n if (v) {\n items.push(this.computedItems[idx]);\n }\n });\n this.$emit(EVENT_NAME_ROW_SELECTED, items);\n }\n }\n },\n beforeMount() {\n // Set up handlers if needed\n if (this.isSelectable) {\n this.setSelectionHandlers(true);\n }\n },\n methods: {\n // Public methods\n selectRow(index) {\n // Select a particular row (indexed based on computedItems)\n if (this.isSelectable && isNumber(index) && index >= 0 && index < this.computedItems.length && !this.isRowSelected(index)) {\n const selectedRows = this.selectableIsMultiSelect ? this.selectedRows.slice() : [];\n selectedRows[index] = true;\n this.selectedLastClicked = -1;\n this.selectedRows = selectedRows;\n }\n },\n unselectRow(index) {\n // Un-select a particular row (indexed based on `computedItems`)\n if (this.isSelectable && isNumber(index) && this.isRowSelected(index)) {\n const selectedRows = this.selectedRows.slice();\n selectedRows[index] = false;\n this.selectedLastClicked = -1;\n this.selectedRows = selectedRows;\n }\n },\n selectAllRows() {\n const length = this.computedItems.length;\n if (this.isSelectable && length > 0) {\n this.selectedLastClicked = -1;\n this.selectedRows = this.selectableIsMultiSelect ? createArray(length, true) : [true];\n }\n },\n isRowSelected(index) {\n // Determine if a row is selected (indexed based on `computedItems`)\n return !!(isNumber(index) && this.selectedRows[index]);\n },\n clearSelected() {\n // Clear any active selected row(s)\n this.selectedLastClicked = -1;\n this.selectedRows = [];\n },\n // Internal private methods\n selectableRowClasses(index) {\n if (this.isSelectable && this.isRowSelected(index)) {\n const variant = this.selectedVariant;\n return {\n 'b-table-row-selected': true,\n [`${this.dark ? 'bg' : 'table'}-${variant}`]: variant\n };\n }\n return {};\n },\n selectableRowAttrs(index) {\n return {\n 'aria-selected': !this.isSelectable ? null : this.isRowSelected(index) ? 'true' : 'false'\n };\n },\n setSelectionHandlers(on) {\n const method = on && !this.noSelectOnClick ? '$on' : '$off';\n // Handle row-clicked event\n this[method](EVENT_NAME_ROW_CLICKED, this.selectionHandler);\n // Clear selection on filter, pagination, and sort changes\n this[method](EVENT_NAME_FILTERED, this.clearSelected);\n this[method](EVENT_NAME_CONTEXT_CHANGED, this.clearSelected);\n },\n selectionHandler(item, index, event) {\n /* istanbul ignore if: should never happen */\n if (!this.isSelectable || this.noSelectOnClick) {\n // Don't do anything if table is not in selectable mode\n this.clearSelected();\n return;\n }\n const {\n selectMode,\n selectedLastRow\n } = this;\n let selectedRows = this.selectedRows.slice();\n let selected = !selectedRows[index];\n // Note 'multi' mode needs no special event handling\n if (selectMode === 'single') {\n selectedRows = [];\n } else if (selectMode === 'range') {\n if (selectedLastRow > -1 && event.shiftKey) {\n // range\n for (let idx = mathMin(selectedLastRow, index); idx <= mathMax(selectedLastRow, index); idx++) {\n selectedRows[idx] = true;\n }\n selected = true;\n } else {\n if (!(event.ctrlKey || event.metaKey)) {\n // Clear range selection if any\n selectedRows = [];\n selected = true;\n }\n if (selected) this.selectedLastRow = index;\n }\n }\n selectedRows[index] = selected;\n this.selectedRows = selectedRows;\n }\n }\n});\n\nexport { props, selectableMixin };\n","import { get } from '../../../utils/get';\nimport { isFunction, isDate, isNumber, isUndefinedOrNull, isNumeric } from '../../../utils/inspect';\nimport { toFloat } from '../../../utils/number';\nimport { stringifyObjectValues } from '../../../utils/stringify-object-values';\n\nconst normalizeValue = value => {\n if (isUndefinedOrNull(value)) {\n return '';\n }\n if (isNumeric(value)) {\n return toFloat(value, value);\n }\n return value;\n};\n\n// Default sort compare routine\n//\n// TODO:\n// Add option to sort by multiple columns (tri-state per column,\n// plus order of columns in sort) where `sortBy` could be an array\n// of objects `[ {key: 'foo', sortDir: 'asc'}, {key:'bar', sortDir: 'desc'} ...]`\n// or an array of arrays `[ ['foo','asc'], ['bar','desc'] ]`\n// Multisort will most likely be handled in `mixin-sort.js` by\n// calling this method for each sortBy\nconst defaultSortCompare = function (a, b) {\n let {\n sortBy = null,\n formatter = null,\n locale = undefined,\n localeOptions = {},\n nullLast = false\n } = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};\n // Get the value by `sortBy`\n let aa = get(a, sortBy, null);\n let bb = get(b, sortBy, null);\n\n // Apply user-provided formatter\n if (isFunction(formatter)) {\n aa = formatter(aa, sortBy, a);\n bb = formatter(bb, sortBy, b);\n }\n\n // Internally normalize value\n // `null` / `undefined` => ''\n // `'0'` => `0`\n aa = normalizeValue(aa);\n bb = normalizeValue(bb);\n if (isDate(aa) && isDate(bb) || isNumber(aa) && isNumber(bb)) {\n // Special case for comparing dates and numbers\n // Internally dates are compared via their epoch number values\n return aa < bb ? -1 : aa > bb ? 1 : 0;\n } else if (nullLast && aa === '' && bb !== '') {\n // Special case when sorting `null` / `undefined` / '' last\n return 1;\n } else if (nullLast && aa !== '' && bb === '') {\n // Special case when sorting `null` / `undefined` / '' last\n return -1;\n }\n\n // Do localized string comparison\n return stringifyObjectValues(aa).localeCompare(stringifyObjectValues(bb), locale, localeOptions);\n};\n\nexport { defaultSortCompare };\n","import { extend } from '../../../vue';\nimport { EVENT_NAME_HEAD_CLICKED, EVENT_NAME_SORT_CHANGED, MODEL_EVENT_NAME_PREFIX } from '../../../constants/events';\nimport { PROP_TYPE_STRING, PROP_TYPE_BOOLEAN, PROP_TYPE_FUNCTION, PROP_TYPE_ARRAY_STRING, PROP_TYPE_OBJECT } from '../../../constants/props';\nimport { arrayIncludes } from '../../../utils/array';\nimport { isFunction, isUndefinedOrNull } from '../../../utils/inspect';\nimport { makeProp } from '../../../utils/props';\nimport { safeVueInstance } from '../../../utils/safe-vue-instance';\nimport { stableSort } from '../../../utils/stable-sort';\nimport { trim } from '../../../utils/string';\nimport { defaultSortCompare } from './default-sort-compare';\n\n// --- Constants ---\n\nconst MODEL_PROP_NAME_SORT_BY = 'sortBy';\nconst MODEL_EVENT_NAME_SORT_BY = MODEL_EVENT_NAME_PREFIX + MODEL_PROP_NAME_SORT_BY;\nconst MODEL_PROP_NAME_SORT_DESC = 'sortDesc';\nconst MODEL_EVENT_NAME_SORT_DESC = MODEL_EVENT_NAME_PREFIX + MODEL_PROP_NAME_SORT_DESC;\nconst SORT_DIRECTION_ASC = 'asc';\nconst SORT_DIRECTION_DESC = 'desc';\nconst SORT_DIRECTION_LAST = 'last';\nconst SORT_DIRECTIONS = [SORT_DIRECTION_ASC, SORT_DIRECTION_DESC, SORT_DIRECTION_LAST];\n\n// --- Props ---\n\nconst props = {\n labelSortAsc: makeProp(PROP_TYPE_STRING, 'Click to sort ascending'),\n labelSortClear: makeProp(PROP_TYPE_STRING, 'Click to clear sorting'),\n labelSortDesc: makeProp(PROP_TYPE_STRING, 'Click to sort descending'),\n noFooterSorting: makeProp(PROP_TYPE_BOOLEAN, false),\n noLocalSorting: makeProp(PROP_TYPE_BOOLEAN, false),\n // Another prop that should have had a better name\n // It should be `noSortClear` (on non-sortable headers)\n // We will need to make sure the documentation is clear on what\n // this prop does (as well as in the code for future reference)\n noSortReset: makeProp(PROP_TYPE_BOOLEAN, false),\n [MODEL_PROP_NAME_SORT_BY]: makeProp(PROP_TYPE_STRING),\n sortCompare: makeProp(PROP_TYPE_FUNCTION),\n // String: locale code\n // Array: array of Locale strings\n sortCompareLocale: makeProp(PROP_TYPE_ARRAY_STRING),\n // Supported localCompare options, see `options` section of:\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare\n sortCompareOptions: makeProp(PROP_TYPE_OBJECT, {\n numeric: true\n }),\n // TODO: Make this tri-state: `true`, `false`, `null`\n [MODEL_PROP_NAME_SORT_DESC]: makeProp(PROP_TYPE_BOOLEAN, false),\n // This prop is named incorrectly\n // It should be `initialSortDirection` as it is a bit misleading\n // (not to mention it screws up the ARIA label on the headers)\n sortDirection: makeProp(PROP_TYPE_STRING, SORT_DIRECTION_ASC, value => {\n return arrayIncludes(SORT_DIRECTIONS, value);\n }),\n // Place the sorting icon on the left of the header cells\n sortIconLeft: makeProp(PROP_TYPE_BOOLEAN, false),\n // Sort null and undefined to appear last\n sortNullLast: makeProp(PROP_TYPE_BOOLEAN, false)\n};\n\n// --- Mixin ---\n\n// @vue/component\nconst sortingMixin = extend({\n props,\n data() {\n return {\n localSortBy: this[MODEL_PROP_NAME_SORT_BY] || '',\n localSortDesc: this[MODEL_PROP_NAME_SORT_DESC] || false\n };\n },\n computed: {\n localSorting() {\n return this.hasProvider ? !!this.noProviderSorting : !this.noLocalSorting;\n },\n isSortable() {\n return this.computedFields.some(f => f.sortable);\n },\n // Sorts the filtered items and returns a new array of the sorted items\n // When not sorted, the original items array will be returned\n sortedItems() {\n const {\n localSortBy: sortBy,\n localSortDesc: sortDesc,\n sortCompareLocale: locale,\n sortNullLast: nullLast,\n sortCompare,\n localSorting,\n filteredItems,\n localItems\n } = safeVueInstance(this);\n const items = (filteredItems || localItems || []).slice();\n const localeOptions = {\n ...this.sortCompareOptions,\n usage: 'sort'\n };\n if (sortBy && localSorting) {\n const field = this.computedFieldsObj[sortBy] || {};\n const sortByFormatted = field.sortByFormatted;\n const formatter = isFunction(sortByFormatted) ? /* istanbul ignore next */sortByFormatted : sortByFormatted ? this.getFieldFormatter(sortBy) : undefined;\n\n // `stableSort` returns a new array, and leaves the original array intact\n return stableSort(items, (a, b) => {\n let result = null;\n // Call user provided `sortCompare` routine first\n if (isFunction(sortCompare)) {\n // TODO:\n // Change the `sortCompare` signature to the one of `defaultSortCompare`\n // with the next major version bump\n result = sortCompare(a, b, sortBy, sortDesc, formatter, localeOptions, locale);\n }\n // Fallback to built-in `defaultSortCompare` if `sortCompare`\n // is not defined or returns `null`/`false`\n if (isUndefinedOrNull(result) || result === false) {\n result = defaultSortCompare(a, b, {\n sortBy,\n formatter,\n locale,\n localeOptions,\n nullLast\n });\n }\n // Negate result if sorting in descending order\n return (result || 0) * (sortDesc ? -1 : 1);\n });\n }\n return items;\n }\n },\n watch: {\n /* istanbul ignore next: pain in the butt to test */\n isSortable(newValue) {\n if (newValue) {\n if (this.isSortable) {\n this.$on(EVENT_NAME_HEAD_CLICKED, this.handleSort);\n }\n } else {\n this.$off(EVENT_NAME_HEAD_CLICKED, this.handleSort);\n }\n },\n [MODEL_PROP_NAME_SORT_DESC](newValue) {\n /* istanbul ignore next */\n if (newValue === this.localSortDesc) {\n return;\n }\n this.localSortDesc = newValue || false;\n },\n [MODEL_PROP_NAME_SORT_BY](newValue) {\n /* istanbul ignore next */\n if (newValue === this.localSortBy) {\n return;\n }\n this.localSortBy = newValue || '';\n },\n // Update .sync props\n localSortDesc(newValue, oldValue) {\n // Emit update to sort-desc.sync\n if (newValue !== oldValue) {\n this.$emit(MODEL_EVENT_NAME_SORT_DESC, newValue);\n }\n },\n localSortBy(newValue, oldValue) {\n if (newValue !== oldValue) {\n this.$emit(MODEL_EVENT_NAME_SORT_BY, newValue);\n }\n }\n },\n created() {\n if (this.isSortable) {\n this.$on(EVENT_NAME_HEAD_CLICKED, this.handleSort);\n }\n },\n methods: {\n // Handlers\n // Need to move from thead-mixin\n handleSort(key, field, event, isFoot) {\n if (!this.isSortable) {\n /* istanbul ignore next */\n return;\n }\n if (isFoot && this.noFooterSorting) {\n return;\n }\n // TODO: make this tri-state sorting\n // cycle desc => asc => none => desc => ...\n let sortChanged = false;\n const toggleLocalSortDesc = () => {\n const sortDirection = field.sortDirection || this.sortDirection;\n if (sortDirection === SORT_DIRECTION_ASC) {\n this.localSortDesc = false;\n } else if (sortDirection === SORT_DIRECTION_DESC) {\n this.localSortDesc = true;\n } else ;\n };\n if (field.sortable) {\n const sortKey = !this.localSorting && field.sortKey ? field.sortKey : key;\n if (this.localSortBy === sortKey) {\n // Change sorting direction on current column\n this.localSortDesc = !this.localSortDesc;\n } else {\n // Start sorting this column ascending\n this.localSortBy = sortKey;\n // this.localSortDesc = false\n toggleLocalSortDesc();\n }\n sortChanged = true;\n } else if (this.localSortBy && !this.noSortReset) {\n this.localSortBy = '';\n toggleLocalSortDesc();\n sortChanged = true;\n }\n if (sortChanged) {\n // Sorting parameters changed\n this.$emit(EVENT_NAME_SORT_CHANGED, this.context);\n }\n },\n // methods to compute classes and attrs for thead>th cells\n sortTheadThClasses(key, field, isFoot) {\n return {\n // If sortable and sortIconLeft are true, then place sort icon on the left\n 'b-table-sort-icon-left': field.sortable && this.sortIconLeft && !(isFoot && this.noFooterSorting)\n };\n },\n sortTheadThAttrs(key, field, isFoot) {\n var _field$sortKey;\n const {\n isSortable,\n noFooterSorting,\n localSortDesc,\n localSortBy,\n localSorting\n } = this;\n if (!isSortable || isFoot && noFooterSorting) {\n // No attributes if not a sortable table\n return {};\n }\n const sortable = field.sortable;\n const sortKey = !localSorting ? (_field$sortKey = field.sortKey) !== null && _field$sortKey !== void 0 ? _field$sortKey : key : key;\n\n // Assemble the aria-sort attribute value\n const ariaSort = sortable && localSortBy === sortKey ? localSortDesc ? 'descending' : 'ascending' : sortable ? 'none' : null;\n // Return the attribute\n return {\n 'aria-sort': ariaSort\n };\n },\n // A label to be placed in an `.gl-sr-only` element in the header cell\n sortTheadThLabel(key, field, isFoot) {\n // No label if not a sortable table\n if (!this.isSortable || isFoot && this.noFooterSorting) {\n return null;\n }\n const {\n localSortBy,\n localSortDesc,\n labelSortAsc,\n labelSortDesc\n } = this;\n const {\n sortable\n } = field;\n // The correctness of these labels is very important for screen reader users\n let labelSorting = '';\n if (sortable) {\n if (localSortBy === key) {\n // Currently sorted sortable column\n labelSorting = localSortDesc ? labelSortAsc : labelSortDesc;\n } else {\n // Not currently sorted sortable column\n // Not using nested ternary's here for clarity/readability\n // Default for `aria-label`\n labelSorting = localSortDesc ? labelSortDesc : labelSortAsc;\n // Handle `sortDirection` setting\n const sortDirection = this.sortDirection || field.sortDirection;\n if (sortDirection === SORT_DIRECTION_ASC) {\n labelSorting = labelSortAsc;\n } else if (sortDirection === SORT_DIRECTION_DESC) {\n labelSorting = labelSortDesc;\n }\n }\n } else if (!this.noSortReset) {\n // Non sortable column\n labelSorting = localSortBy ? this.labelSortClear : '';\n }\n // Return the `.gl-sr-only` sort label or `null` if no label\n return trim(labelSorting) || null;\n }\n }\n});\n\nexport { props, sortingMixin };\n","import { extend } from '../../../vue';\nimport { SLOT_NAME_TOP_ROW } from '../../../constants/slots';\nimport { isFunction } from '../../../utils/inspect';\nimport { BTr } from '../tr';\n\n// --- Props ---\n\nconst props = {};\n\n// --- Mixin ---\n\n// @vue/component\nconst topRowMixin = extend({\n methods: {\n renderTopRow() {\n const {\n computedFields: fields,\n stacked,\n tbodyTrClass,\n tbodyTrAttr\n } = this;\n const h = this.$createElement;\n\n // Add static Top Row slot (hidden in visibly stacked mode as we can't control the data-label)\n // If in *always* stacked mode, we don't bother rendering the row\n if (!this.hasNormalizedSlot(SLOT_NAME_TOP_ROW) || stacked === true || stacked === '') {\n return h();\n }\n return h(BTr, {\n staticClass: 'b-table-top-row',\n class: [isFunction(tbodyTrClass) ? tbodyTrClass(null, 'row-top') : tbodyTrClass],\n attrs: isFunction(tbodyTrAttr) ? tbodyTrAttr(null, 'row-top') : tbodyTrAttr,\n key: 'b-top-row'\n }, [this.normalizeSlot(SLOT_NAME_TOP_ROW, {\n columns: fields.length,\n fields\n })]);\n }\n }\n});\n\nexport { props, topRowMixin };\n","import { extend } from '../../vue';\nimport { NAME_TABLE } from '../../constants/components';\nimport { sortKeys } from '../../utils/object';\nimport { makePropsConfigurable } from '../../utils/props';\nimport { attrsMixin } from '../../mixins/attrs';\nimport { hasListenerMixin } from '../../mixins/has-listener';\nimport { props as props$1, idMixin } from '../../mixins/id';\nimport { normalizeSlotMixin } from '../../mixins/normalize-slot';\nimport { props as props$2, bottomRowMixin } from './helpers/mixin-bottom-row';\nimport { props as props$3, busyMixin } from './helpers/mixin-busy';\nimport { props as props$4, captionMixin } from './helpers/mixin-caption';\nimport { props as props$5, colgroupMixin } from './helpers/mixin-colgroup';\nimport { props as props$6, emptyMixin } from './helpers/mixin-empty';\nimport { props as props$7, filteringMixin } from './helpers/mixin-filtering';\nimport { props as props$8, itemsMixin } from './helpers/mixin-items';\nimport { props as props$9, paginationMixin } from './helpers/mixin-pagination';\nimport { props as props$a, providerMixin } from './helpers/mixin-provider';\nimport { props as props$b, selectableMixin } from './helpers/mixin-selectable';\nimport { props as props$c, sortingMixin } from './helpers/mixin-sorting';\nimport { props as props$d, stackedMixin } from './helpers/mixin-stacked';\nimport { props as props$e, tableRendererMixin } from './helpers/mixin-table-renderer';\nimport { props as props$f, tbodyMixin } from './helpers/mixin-tbody';\nimport { props as props$g, tfootMixin } from './helpers/mixin-tfoot';\nimport { props as props$h, theadMixin } from './helpers/mixin-thead';\nimport { props as props$i, topRowMixin } from './helpers/mixin-top-row';\n\n// --- Props ---\n\nconst props = makePropsConfigurable(sortKeys({\n ...props$1,\n ...props$2,\n ...props$3,\n ...props$4,\n ...props$5,\n ...props$6,\n ...props$7,\n ...props$8,\n ...props$9,\n ...props$a,\n ...props$b,\n ...props$c,\n ...props$d,\n ...props$e,\n ...props$f,\n ...props$g,\n ...props$h,\n ...props$i\n}), NAME_TABLE);\n\n// --- Main component ---\n\n// @vue/component\nconst BTable = /*#__PURE__*/extend({\n name: NAME_TABLE,\n // Order of mixins is important!\n // They are merged from first to last, followed by this component\n mixins: [\n // General mixins\n attrsMixin, hasListenerMixin, idMixin, normalizeSlotMixin,\n // Required table mixins\n itemsMixin, tableRendererMixin, stackedMixin, theadMixin, tfootMixin, tbodyMixin,\n // Table features mixins\n stackedMixin, filteringMixin, sortingMixin, paginationMixin, captionMixin, colgroupMixin, selectableMixin, emptyMixin, topRowMixin, bottomRowMixin, busyMixin, providerMixin],\n props\n // Render function is provided by `tableRendererMixin`\n});\n\nexport { BTable, props };\n","const tableFullSlots = ['bottom-row', 'empty', 'emptyfiltered', 'table-busy', 'thead-top', 'top-row'];\nconst tableFullProps = ['api-url', 'busy', 'current-page', 'empty-filtered-html', 'empty-filtered-text', 'empty-html', 'empty-text', 'filter', 'filter-debounce', 'filter-function', 'filter-ignored-fields', 'filter-included-fields', 'label-sort-asc', 'label-sort-clear', 'label-sort-desc', 'no-footer-sorting', 'no-local-sorting', 'no-provider-filtering', 'no-provider-paging', 'no-provider-sorting', 'no-select-on-click', 'per-page', 'select-mode', 'selectable', 'selected-variant', 'show-empty', 'sort-by', 'sort-compare', 'sort-compare-locale', 'sort-compare-options', 'sort-desc', 'sort-direction', 'sort-icon-left', 'sort-null-last'];\nconst glTableLiteWarning = 'This GlTable could be a GlTableLite component, please consider using GlTableLite instead of GlTable to reduce the page bundlesize more about this here: https://gitlab-org.gitlab.io/gitlab-ui/?path=/docs/base-table-table-lite--default';\n\nexport { glTableLiteWarning, tableFullProps, tableFullSlots };\n","import { BTable } from '../../../vendor/bootstrap-vue/src/components/table/table';\nimport { isDev, logWarning } from '../../../utils/utils';\nimport { tableFullProps, tableFullSlots, glTableLiteWarning } from './constants';\nimport __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';\n\n//\nconst shouldUseFullTable = _ref => {\n let {\n $attrs,\n $scopedSlots\n } = _ref;\n return tableFullProps.some(prop => $attrs[prop] !== undefined) || tableFullSlots.some(slot => $scopedSlots[slot] !== undefined);\n};\nconst {\n tableClass\n} = BTable.options.props;\nvar script = {\n name: 'GlTable',\n components: {\n BTable\n },\n inheritAttrs: false,\n props: {\n tableClass,\n fields: {\n type: Array,\n required: false,\n default: null\n },\n stickyHeader: {\n type: Boolean,\n default: false,\n required: false\n },\n sortBy: {\n type: String,\n required: false,\n default: undefined\n },\n sortDesc: {\n type: Boolean,\n required: false,\n default: false\n }\n },\n data() {\n return {\n localSortBy: this.sortBy,\n localSortDesc: this.sortDesc\n };\n },\n computed: {\n stickyHeaderClass() {\n return this.stickyHeader ? 'gl-table--sticky-header' : null;\n },\n localTableClass() {\n return ['gl-table', this.tableClass, this.stickyHeaderClass];\n },\n headSlots() {\n return ['head()', ...Object.keys(this.$scopedSlots).filter(slotName => slotName.startsWith('head('))];\n },\n computedFields() {\n var _this$fields;\n return (_this$fields = this.fields) === null || _this$fields === void 0 ? void 0 : _this$fields.map(field => {\n if (typeof field === 'string') {\n return field;\n }\n const {\n thAlignRight,\n thClass = '',\n ...rest\n } = field;\n const computedThClass = Array.isArray(thClass) ? thClass : thClass.split(' ');\n if (thAlignRight) {\n computedThClass.push('gl-table-th-align-right');\n }\n return {\n ...rest,\n thClass: computedThClass\n };\n });\n }\n },\n mounted() {\n // logWarning will call isDev before logging any message\n // this additional call to isDev is being made to exit the condition early when run in production\n if (isDev() && !shouldUseFullTable(this)) {\n logWarning(glTableLiteWarning, this.$el);\n }\n },\n methods: {\n isSortable(_ref2) {\n let {\n field\n } = _ref2;\n return field === null || field === void 0 ? void 0 : field.sortable;\n },\n activeSortingColumn(_ref3) {\n let {\n field\n } = _ref3;\n return this.localSortBy === (field === null || field === void 0 ? void 0 : field.key);\n },\n getSortingIcon(_ref4) {\n let {\n field\n } = _ref4;\n if (this.activeSortingColumn({\n field\n })) {\n if (this.localSortDesc) {\n return '↓';\n }\n return '↑';\n }\n if (this.$attrs['sort-direction'] === 'desc') {\n return '↓';\n }\n return '↑';\n }\n }\n};\n\n/* script */\nconst __vue_script__ = script;\n\n/* template */\nvar __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('b-table',_vm._g(_vm._b({attrs:{\"table-class\":_vm.localTableClass,\"fields\":_vm.computedFields,\"sort-by\":_vm.localSortBy,\"sort-desc\":_vm.localSortDesc,\"no-sort-reset\":\"\"},on:{\"update:sortBy\":function($event){_vm.localSortBy=$event;},\"update:sort-by\":function($event){_vm.localSortBy=$event;},\"update:sortDesc\":function($event){_vm.localSortDesc=$event;},\"update:sort-desc\":function($event){_vm.localSortDesc=$event;}},scopedSlots:_vm._u([_vm._l((Object.keys(_vm.$scopedSlots)),function(slotName){return {key:slotName,fn:function(scope){return [_vm._t(slotName,null,null,scope)]}}}),_vm._l((_vm.headSlots),function(headSlotName){return {key:headSlotName,fn:function(scope){return [_c('div',{key:headSlotName,staticClass:\"gl-flex\"},[_vm._t(headSlotName,function(){return [_c('span',[_vm._v(_vm._s(scope.label))])]},null,scope),(_vm.isSortable(scope))?[_c('div',{staticClass:\"gl-table-th-sort-icon-wrapper gl-flex gl-w-5 gl-justify-center\"},[_c('span',{class:{ 'gl-hidden': !_vm.activeSortingColumn(scope) },attrs:{\"name\":\"sort-icon\",\"data-testid\":\"sort-icon\"}},[_vm._v(\"\\n \"+_vm._s(_vm.getSortingIcon(scope))+\"\\n \")])])]:_vm._e()],2)]}}}),{key:\"empty\",fn:function(scope){return [_vm._t(\"empty\",function(){return [_c('p',{staticClass:\"gl-mb-0 gl-py-2 gl-text-subtle\"},[_vm._v(_vm._s(scope.emptyText))])]},null,scope)]}}],null,true)},'b-table',_vm.$attrs,false),_vm.$listeners))};\nvar __vue_staticRenderFns__ = [];\n\n /* style */\n const __vue_inject_styles__ = undefined;\n /* scoped */\n const __vue_scope_id__ = undefined;\n /* module identifier */\n const __vue_module_identifier__ = undefined;\n /* functional template */\n const __vue_is_functional_template__ = false;\n /* style inject */\n \n /* style inject SSR */\n \n /* style inject shadow dom */\n \n\n \n const __vue_component__ = __vue_normalize__(\n { render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ },\n __vue_inject_styles__,\n __vue_script__,\n __vue_scope_id__,\n __vue_is_functional_template__,\n __vue_module_identifier__,\n false,\n undefined,\n undefined,\n undefined\n );\n\nexport default __vue_component__;\n"],"sourceRoot":""}