@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
JavaScript
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
};