UNPKG

@gitlab/ui

Version:
181 lines (171 loc) • 6.17 kB
import { isVue3 } from '../vue'; function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } // Regex to detect event handler props: onSomething or onSomethingOnce const EVENT_HANDLER_RE = /^on([A-Z][a-zA-Z]*)$/; const ONCE_SUFFIX = 'Once'; /** * Converts an event name to a handler prop name. * Use as computed property key with event constants: [eventProp(EVENT_NAME_HIDDEN)] * @param {string} eventName - The event name (e.g., 'hidden', 'show', 'mouseenter') * @param {Object} options - Options object * @param {boolean} options.once - If true, returns the "once" variant (e.g., 'onHiddenOnce') * @returns {string} The handler prop name (e.g., 'onHidden', 'onHiddenOnce') */ const eventProp = function (eventName) { let _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, _ref$once = _ref.once, once = _ref$once === void 0 ? false : _ref$once; const capitalized = eventName.charAt(0).toUpperCase() + eventName.slice(1); return `on${capitalized}${once ? ONCE_SUFFIX : ''}`; }; /** * Parses a handler prop name to extract event name and once flag. * Examples: * onShow -> { eventName: 'show', once: false } * onShowOnce -> { eventName: 'show', once: true } * onMouseenter -> { eventName: 'mouseenter', once: false } */ const parseEventHandlerProp = propName => { const match = propName.match(EVENT_HANDLER_RE); if (!match) return null; let eventPart = match[1]; const once = eventPart.endsWith(ONCE_SUFFIX); if (once) eventPart = eventPart.slice(0, -ONCE_SUFFIX.length); const eventName = eventPart.charAt(0).toLowerCase() + eventPart.slice(1); return { eventName, once }; }; /** * Extracts event handlers from config and separates them from other props. * @param {Object} config - The config object with potential event handler props * @returns {{ handlers: Array<{eventName: string, handler: Function, once: boolean}>, cleanConfig: Object }} */ const extractEventHandlers = config => { const handlers = []; const cleanConfig = { ...config }; if (cleanConfig.propsData) { const cleanPropsData = {}; Object.keys(cleanConfig.propsData).forEach(propName => { const parsed = parseEventHandlerProp(propName); if (parsed && typeof cleanConfig.propsData[propName] === 'function') { handlers.push({ ...parsed, handler: cleanConfig.propsData[propName] }); } else { cleanPropsData[propName] = cleanConfig.propsData[propName]; } }); cleanConfig.propsData = cleanPropsData; } return { handlers, cleanConfig }; }; const getVue3Constructor = parent => { return parent.$.appContext.config.globalProperties.constructor; }; const BUILTIN_DIRECTIVES = ['show', 'model', 'bind', 'cloak', 'else', 'else-if', 'for', 'html', 'if', 'memo', 'on', 'once', 'pre', 'slot', 'text']; const resolveComponentOptions = Component => { const opts = typeof Component === 'function' && Component.options ? { ...Component.options } : { ...Component }; if (opts.directives) { opts.directives = Object.fromEntries(Object.entries(opts.directives).filter(_ref2 => { let _ref3 = _slicedToArray(_ref2, 1), key = _ref3[0]; return !BUILTIN_DIRECTIVES.includes(key); })); } return opts; }; const createNewChildComponent = function (parent, Component) { let config = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; const bvEventRoot = parent.$root ? parent.$root.$options.bvEventRoot || parent.$root : null; // Vue 3: resolve component options and instantiate via the Vue constructor // to ensure the component runs through the Vue 3 runtime path if (isVue3(parent)) { const Vue = getVue3Constructor(parent); const componentOptions = resolveComponentOptions(Component); return new Vue({ ...componentOptions, ...config, parent, bvParent: parent, bvEventRoot }); } // Vue 2: extract handlers and subscribe manually after creation const _extractEventHandlers = extractEventHandlers(config), handlers = _extractEventHandlers.handlers, cleanConfig = _extractEventHandlers.cleanConfig; const instance = new Component({ ...cleanConfig, parent, bvParent: parent, bvEventRoot }); // Subscribe to events using $on/$once handlers.forEach(_ref4 => { let eventName = _ref4.eventName, handler = _ref4.handler, once = _ref4.once; instance[once ? '$once' : '$on'](eventName, handler); }); return instance; }; export { createNewChildComponent, eventProp };