UNPKG

uikit

Version:

UIkit is a lightweight and modular front-end framework for developing fast and powerful web interfaces.

311 lines (250 loc) • 7.86 kB
import { assign, camelize, data as getData, hasOwn, hyphenate, isArray, isFunction, isNumeric, isPlainObject, isString, isUndefined, mergeOptions, on, parseOptions, startsWith, toBoolean, toNumber, } from 'uikit-util'; export default function (UIkit) { let uid = 0; UIkit.prototype._init = function (options) { options = options || {}; options.data = normalizeData(options, this.constructor.options); this.$options = mergeOptions(this.constructor.options, options, this); this.$el = null; this.$props = {}; this._uid = uid++; this._initData(); this._initMethods(); this._initComputeds(); this._callHook('created'); if (options.el) { this.$mount(options.el); } }; UIkit.prototype._initData = function () { const { data = {} } = this.$options; for (const key in data) { this.$props[key] = this[key] = data[key]; } }; UIkit.prototype._initMethods = function () { const { methods } = this.$options; if (methods) { for (const key in methods) { this[key] = methods[key].bind(this); } } }; UIkit.prototype._initComputeds = function () { const { computed } = this.$options; this._computed = {}; if (computed) { for (const key in computed) { registerComputed(this, key, computed[key]); } } }; UIkit.prototype._initProps = function (props) { let key; props = props || getProps(this.$options, this.$name); for (key in props) { if (!isUndefined(props[key])) { this.$props[key] = props[key]; } } const exclude = [this.$options.computed, this.$options.methods]; for (key in this.$props) { if (key in props && notIn(exclude, key)) { this[key] = this.$props[key]; } } }; UIkit.prototype._initEvents = function () { this._events = []; for (const event of this.$options.events || []) { if (hasOwn(event, 'handler')) { registerEvent(this, event); } else { for (const key in event) { registerEvent(this, event[key], key); } } } }; UIkit.prototype._unbindEvents = function () { this._events.forEach((unbind) => unbind()); delete this._events; }; UIkit.prototype._initObservers = function () { this._observers = [initPropsObserver(this)]; if (this.$options.computed) { this.registerObserver(initChildListObserver(this)); } }; UIkit.prototype.registerObserver = function (observer) { this._observers.push(observer); }; UIkit.prototype._disconnectObservers = function () { this._observers.forEach((observer) => observer?.disconnect()); }; } function getProps(opts, name) { const data = {}; const { args = [], props = {}, el } = opts; if (!props) { return data; } for (const key in props) { const prop = hyphenate(key); let value = getData(el, prop); if (isUndefined(value)) { continue; } value = props[key] === Boolean && value === '' ? true : coerce(props[key], value); if (prop === 'target' && (!value || startsWith(value, '_'))) { continue; } data[key] = value; } const options = parseOptions(getData(el, name), args); for (const key in options) { const prop = camelize(key); if (props[prop] !== undefined) { data[prop] = coerce(props[prop], options[key]); } } return data; } function registerComputed(component, key, cb) { Object.defineProperty(component, key, { enumerable: true, get() { const { _computed, $props, $el } = component; if (!hasOwn(_computed, key)) { _computed[key] = (cb.get || cb).call(component, $props, $el); } return _computed[key]; }, set(value) { const { _computed } = component; _computed[key] = cb.set ? cb.set.call(component, value) : value; if (isUndefined(_computed[key])) { delete _computed[key]; } }, }); } export function registerEvent(component, event, key) { if (!isPlainObject(event)) { event = { name: key, handler: event }; } let { name, el, handler, capture, passive, delegate, filter, self } = event; el = isFunction(el) ? el.call(component) : el || component.$el; if (isArray(el)) { el.forEach((el) => registerEvent(component, { ...event, el }, key)); return; } if (!el || (filter && !filter.call(component))) { return; } component._events.push( on( el, name, delegate ? (isString(delegate) ? delegate : delegate.call(component)) : null, isString(handler) ? component[handler] : handler.bind(component), { passive, capture, self } ) ); } function notIn(options, key) { return options.every((arr) => !arr || !hasOwn(arr, key)); } function coerce(type, value) { if (type === Boolean) { return toBoolean(value); } else if (type === Number) { return toNumber(value); } else if (type === 'list') { return toList(value); } return type ? type(value) : value; } function toList(value) { return isArray(value) ? value : isString(value) ? value .split(/,(?![^(]*\))/) .map((value) => (isNumeric(value) ? toNumber(value) : toBoolean(value.trim()))) : [value]; } function normalizeData({ data = {} }, { args = [], props = {} }) { if (isArray(data)) { data = data.slice(0, args.length).reduce((data, value, index) => { if (isPlainObject(value)) { assign(data, value); } else { data[args[index]] = value; } return data; }, {}); } for (const key in data) { if (isUndefined(data[key])) { delete data[key]; } else if (props[key]) { data[key] = coerce(props[key], data[key]); } } return data; } function initChildListObserver(component) { const { el } = component.$options; const observer = new MutationObserver(() => component.$emit()); observer.observe(el, { childList: true, subtree: true, }); return observer; } function initPropsObserver(component) { const { $name, $options, $props } = component; const { attrs, props, el } = $options; if (!props || attrs === false) { return; } const attributes = isArray(attrs) ? attrs : Object.keys(props); const filter = attributes.map((key) => hyphenate(key)).concat($name); const observer = new MutationObserver((records) => { const data = getProps($options, $name); if ( records.some(({ attributeName }) => { const prop = attributeName.replace('data-', ''); return ( prop === $name ? attributes : [camelize(prop), camelize(attributeName)] ).some((prop) => !isUndefined(data[prop]) && data[prop] !== $props[prop]); }) ) { component.$reset(); } }); observer.observe(el, { attributes: true, attributeFilter: filter.concat(filter.map((key) => `data-${key}`)), }); return observer; }