UNPKG

@razaman2/reactive-view

Version:

This library enables you to build vue apps in an object oriented way. It provides a convenient approach to extend and override ui components. It provides a built in eventing system along with component data management.

517 lines (510 loc) 18 kB
var __defProp = Object.defineProperty; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __objRest = (source, exclude) => { var target = {}; for (var prop in source) if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0) target[prop] = source[prop]; if (source != null && __getOwnPropSymbols) for (var prop of __getOwnPropSymbols(source)) { if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop)) target[prop] = source[prop]; } return target; }; // src/Subscription.ts var Subscription = class _Subscription { constructor() { this.subscriptions = []; this.data = {}; } static create() { return new _Subscription(); } subscribe(name2, handler, data) { if (this.isNameAvailable(name2)) { this.subscriptions.push({ name: name2, handler }); this.data[name2] = data; } return this; } replace(name2, handler, data) { this.unsubscribe(name2); return this.subscribe(name2, handler, data); } unsubscribe(param1) { if (!Array.isArray(param1)) { param1 = param1 ? [param1] : []; } const log = (name2) => { return console.log(`%cUnsubscribed From Subscription (${name2})`, "background-color: yellow; color: green; font-weight: bold; padding: 3px;"); }; if (param1.length) { param1.forEach((name2) => { const subscription = this.find(name2); if (subscription) { subscription.handler(); this.remove(subscription); log(name2); } }); } else { this.subscriptions.forEach((subscription) => { subscription.handler(); log(subscription.name); }); this.subscriptions = []; } return this; } size() { return this.subscriptions.length; } hasSubscription(name2) { return Boolean(this.find(name2)); } remove(subscription) { this.subscriptions.splice(this.subscriptions.indexOf(subscription), 1); } find(name2) { return this.subscriptions.find((subscription) => { return subscription.name === name2; }); } isNameAvailable(name2) { if (this.hasSubscription(name2)) { throw new Error(`There is already a subscription called "${name2}".`); } else { return true; } } registrations() { return this.subscriptions; } get(name2) { const subscription = this.find(name2); if (subscription) { return subscription; } else { throw new Error(`Subscription "${name2}" doesn't exist!`); } } }; // src/Subscriptions.ts var _Subscriptions = class _Subscriptions extends Subscription { static get() { return this.subscriptions; } }; _Subscriptions.subscriptions = _Subscriptions.create(); var Subscriptions = _Subscriptions; // src/ReactiveView.ts import ObjectManager from "@razaman2/object-manager"; import DataManager from "@razaman2/data-manager"; import { ref, reactive, watch, isRef, isReactive, createVNode, getCurrentInstance } from "vue"; // package.json var name = "@razaman2/reactive-view"; var version = "0.0.33"; // src/ReactiveView.ts var setup = { type: Function, default: (parent = {}, self = {}) => self, ReactiveView: true }; var ReactiveView_default = { props: { setup: {}, ReactiveView: { type: Boolean, default: true }, instance: { default: ref() }, notifications: Object, subscriptions: Object, beforeSetData: Function, data: { default: {} }, defaultData: {}, getDefaultData: { type: Function, default: (data) => data }, model: {}, defer: {}, logging: { validator: (logging) => { return ["Boolean"].includes(logging.constructor.name); } }, modelName: { type: String, default: "ReactiveView" }, debug: { type: Boolean, default: false } }, setup(props, context) { var _a, _b, _c, _d, _e, _f, _g; const template = (component2, vue2) => { return createVNode( "div", context.slots.default ? context.attrs : __spreadValues({ style: { color: "red", textAlign: "center" } }, context.attrs), context.slots.default ? context.slots.default({ component: component2, vue: vue2, props, context }) : `${props.modelName}: ${name}@${version}` ); }; const isValid = ref(false); const ready = ref(false); const vue = getCurrentInstance(); const defer = ref(typeof props.defer === "function" ? props.defer() : props.defer); const data = typeof props.data === "function" ? props.data() : props.data; const async = data instanceof Promise; if (props.debug) { console.log("[ReactiveView]:", { props, context, isValid, ready, vue, defer, data, async }); } if (async && !((_a = vue.vnode.props) == null ? void 0 : _a.hasOwnProperty("defaultData"))) { console.error("[ReactiveView]: defaultData is required for async data."); } const defaultData = props.getDefaultData( ((_b = vue.vnode.props) == null ? void 0 : _b.hasOwnProperty("defaultData")) ? vue.vnode.props.defaultData : typeof ((_c = vue.vnode.props) == null ? void 0 : _c.data) === "object" ? Array.isArray(vue.vnode.props.data) ? [] : {} : ((_d = vue.vnode.props) == null ? void 0 : _d.hasOwnProperty("data")) ? (_e = vue.vnode.props) == null ? void 0 : _e.data : {} ); const datatype = Array.isArray(defaultData) ? [] : {}; const config = { data: reactive( ObjectManager.on(async ? datatype : ((_f = vue.vnode.props) == null ? void 0 : _f.hasOwnProperty("data")) ? typeof data === "function" ? datatype : data : datatype).clone() ), defaultData: ObjectManager.on(defaultData).clone(), notifications: props.notifications, subscriptions: props.subscriptions, logging: props.logging, name: props.modelName }; const model = new Proxy(props.model ? typeof props.model === "function" ? props.model(config) : props.model : new DataManager(config), { get(target, key, receiver) { if (key === "setData" || key === "replaceData") { return (...params) => { var _a2; const beforeSetData = (_a2 = access(component).beforeSetData) != null ? _a2 : props.beforeSetData; if (typeof beforeSetData === "function") { const after = ObjectManager.on(datatype).set(...params.length ? params : [defaultData]).get(); const before = target.getData(); beforeSetData({ before, after }, target); } else { target[key](...params); } return target; }; } else { return Reflect.get(target, key, receiver); } } }); const watchDataProp = (dataProp) => { var _a2, _b2, _c2; const config2 = typeof context.attrs["onUpdate:data"] === "function" ? { callback: context.attrs["onUpdate:data"] } : { callback: (_b2 = ((_a2 = context.attrs["onUpdate:data"]) != null ? _a2 : {}).callback) != null ? _b2 : context.attrs["data:callback"], options: Object.assign({ immediate: context.attrs["data:immediate"], deep: context.attrs["data:deep"], once: context.attrs["data:once"] }, ((_c2 = context.attrs["onUpdate:data"]) != null ? _c2 : {}).options) }; if (context.attrs["data:log"]) { console.log(`[data:prop]:`, config2); } watch(typeof dataProp === "function" || isRef(dataProp) || isReactive(dataProp) ? dataProp : () => dataProp, (after, before) => { var _a3; const diff = { before: ObjectManager.on(before).get(), after: ObjectManager.on(after).get() }; if (typeof config2.callback === "function") { model.replaceData((_a3 = config2.callback(diff, { component })) != null ? _a3 : after); } else { model.replaceData(after); } }, config2.options); }; setTimeout(async () => { var _a2; const [dataProp] = await Promise.all([data, (_a2 = defer.value) != null ? _a2 : true]); watchDataProp(dataProp); }); setTimeout(async () => { var _a2; ready.value = (_a2 = await defer.value) != null ? _a2 : true; }); if (context.attrs["onUpdate:model"]) { const config2 = typeof context.attrs["onUpdate:model"] === "function" ? { callback: context.attrs["onUpdate:model"] } : (_g = context.attrs["onUpdate:model"]) != null ? _g : {}; watch(() => model.getData(), (after, before) => { const diff = { before: ObjectManager.on(before).get(), after: ObjectManager.on(after).get() }; config2.callback(diff, { component }); }, config2.options); } const component = ref({ parent: { self: vue.proxy }, self: { template, model, isValid } }); component.value = [access(component)].reduce((options, parent) => { var _a2; while (parent) { if (typeof parent.setup === "function") { options = { parent: options, self: (_a2 = parent.setup(component, options)) != null ? _a2 : {} }; if (access(parent.$parent).ReactiveView) { break; } else { parent = access(parent.$parent); } } else { break; } } return options; }, component.value); props.instance.value = typeof props.instance === "function" ? props.instance(component.value) : component.value; return (vue2) => { var _a2, _b2; if (ready.value) { return access(component).template(component, vue2); } else { return (_b2 = (_a2 = context.slots).loading) == null ? void 0 : _b2.call(_a2, { component }); } }; } }; // src/index.ts import { createVNode as createVNode2, isRef as isRef2, h } from "vue"; import { formatInTimeZone } from "date-fns-tz"; var index_default = ReactiveView_default; function safeRequest(request) { return new Promise(async (resolve) => { var _a, _b, _c, _d; const { message } = (_a = request.loading) != null ? _a : {}; if (request.loading) { request.loading.status = true; if (request.message) { request.loading.message = request.message; } } try { resolve(await request.try()); } catch (e) { if ((_b = request.alternative) != null ? _b : true) { resolve(request.catch ? await request.catch(e) : console.log(e)); } } finally { await ((_c = request.finally) == null ? void 0 : _c.call(request)); if (request.loading) { request.loading.status = false; } if (request.loading && message) { request.loading.message = message; } await ((_d = request.complete) == null ? void 0 : _d.call(request)); } }); } function getProps(props, param2) { var _a; const exclude = Array.isArray(param2) || typeof param2 === "string" ? param2 : param2.exclude; const exclusions = (Array.isArray(exclude) ? exclude : [exclude]).join("|"); const include = (_a = param2.include) != null ? _a : {}; return Object.entries(include).reduce((props2, [key, val]) => { props2[key] = val; return props2; }, Object.entries(props).reduce((props2, [key, val]) => { if (!RegExp(`(^|\\|)${key}($|\\|)`, "i").test(exclusions)) { props2[key] = val; } return props2; }, {})); } function getReactiveViewComponent(component, options = {}) { const model = (component2) => { try { return (component2.type || component2).props.hasOwnProperty("model"); } catch (e) { return false; } }; const props = getProps(options, "setup"); const slots = typeof component === "function" ? component : () => { return (component.type || component).setup ? createVNode2(component, component.setup ? {} : props) : component; }; return model(component) ? createVNode2(component, props) : createVNode2(ReactiveView_default, props, slots); } function getDate(param1, param2) { var _a, _b; const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; const format = "MM/dd/yyyy h:mm a"; const options = typeof param2 === "string" ? { format: param2, timezone } : { format: (_a = param2 == null ? void 0 : param2.format) != null ? _a : format, timezone: (_b = param2 == null ? void 0 : param2.timezone) != null ? _b : timezone }; const datetime = () => { try { return param1 instanceof Date ? param1 : param1.toDate(); } catch (e) { return /* @__PURE__ */ new Date(); } }; return formatInTimeZone(datetime(), options.timezone, options.format); } function access(view = {}, alternative) { const resolve = (target) => { return new Proxy(target, { get(target2, key) { const component = { tree: target2 }; do { if (key in component.tree) { return component.tree[key]; } else if ("self" in component.tree && key in component.tree.self) { return component.tree.self[key]; } else { component.tree = "parent" in component.tree && component.tree.parent; } } while (component.tree); try { return new alternative(); } catch (e) { return alternative; } } }); }; try { const component = typeof view === "function" ? view() : view; const ref2 = isRef2(component) ? component.value : component; return resolve(ref2.ReactiveView ? access(ref2.instance) : ref2); } catch (e) { return resolve(view); } } function useSubscription() { const subscriptions = []; const subscription = Subscriptions.get(); return { addSubscription(name2, handler = () => false, data) { subscription.subscribe(name2, handler, data); subscriptions.push(() => subscription.unsubscribe(name2)); }, replaceSubscription(name2, handler = () => false, data) { subscription.replace(name2, handler, data); subscriptions.push(() => subscription.unsubscribe(name2)); }, removeSubscriptions() { subscriptions.forEach((subscription2) => safeRequest({ try: () => subscription2() })); }, removeSubscription(name2) { subscription.unsubscribe(name2); }, hasSubscription(name2) { return subscription.hasSubscription(name2); }, subscriptions, subscription }; } function StyleParser(styles = {}) { const transform = (style, status = true) => { const remove = style.split(/(-{[^}]+})/); return remove.reduce((object, style2) => { var _a; const remove2 = /-{(.+?)}/.exec(style2); (remove2 ? (_a = remove2[1]) != null ? _a : style2 : style2).split(/\s+/).forEach((item) => { if (item) { Object.assign(object, { [item]: remove2 ? false : status }); } }); return object; }, {}); }; return Array.isArray(styles) ? styles.reduce((styles2, style) => Object.assign(transform(style), styles2), {}) : typeof styles === "string" ? transform(styles) : Object.entries(styles).reduce((acc, [styles2, value]) => { return __spreadValues(__spreadValues({}, acc), transform(styles2, value)); }, {}); } function MergeStyles(...params) { return new Proxy(params.reduce((styles, style, index, items) => { var _a; const item = (_a = items[items.length - index - 1]) != null ? _a : {}; return __spreadValues(__spreadValues({}, StyleParser(typeof item === "function" ? item(items.slice(0, index - 1)) : item)), styles); }, {}), { get: (target, key, receiver) => { const styles = Object.entries(target).reduce((styles2, [key2, val]) => { return val ? styles2.concat(key2) : styles2; }, []); if (key === "string") { return styles.join(" "); } else if (key === "array") { return styles; } else { return Reflect.get(target, key, receiver); } } }); } var extendVnode = (component, element) => { return new Proxy(access(component), { get: (target, key) => { const getVnode = () => { try { return target[key](); } catch (e) { throw new Error(`${key} does not exist as vnode on component.`); } }; const vnode = getVnode(); return (props = {}, slots) => { var _a; const _b = getProps((_a = vnode.props) != null ? _a : {}, []), { class: classes, style: styles } = _b, rest = __objRest(_b, ["class", "style"]); const finalClasses = MergeStyles(classes, typeof props.class === "function" ? props.class(classes) : props.class); const functionalProps = Object.entries(props).reduce((props2, [key2, value]) => { if (![ "class" /*, "style"*/ ].includes(key2) && typeof value === "function") { props2[key2] = value(vnode.props[key2]); } return props2; }, props); const finalProps = Object.assign(rest, functionalProps, { class: finalClasses /*, style: finalStyles*/ }); const finalSlots = typeof slots === "function" ? slots(vnode.children) : vnode.children; return h(["string", "undefined"].includes(typeof element) ? vnode : element, finalProps, finalSlots); }; } }); }; export { MergeStyles, StyleParser, access, index_default as default, extendVnode, getDate, getProps, getReactiveViewComponent, safeRequest, setup, useSubscription };