@v4fire/client
Version:
V4Fire client core library
227 lines (182 loc) • 4.46 kB
text/typescript
/*!
* V4Fire Client Core
* https://github.com/V4Fire/Client
*
* Released under the MIT license
* https://github.com/V4Fire/Client/blob/master/LICENSE
*/
/**
* [[include:super/i-block/modules/mods/README.md]]
* @packageDocumentation
*/
import type iBlock from 'super/i-block/i-block';
import type { ModsTable, ModsNTable } from 'super/i-block/modules/mods/interface';
export * from 'super/i-block/modules/mods/interface';
/**
* Merges old component modifiers with new modifiers
* (for functional components)
*
* @param component
* @param oldComponent
* @param key - field key
* @param link - link key
*/
export function mergeMods(
component: iBlock,
oldComponent: iBlock,
key: string,
link?: string
): void {
if (link == null) {
return;
}
const
ctx = component.unsafe,
cache = ctx.$syncLinkCache.get(link);
if (!cache) {
return;
}
const
l = cache[key];
if (!l) {
return;
}
const getFullModsProp = (o) => {
const
declMods = o.meta.component.mods,
res = {...o.$props[link]};
for (let attrs = o.$attrs, keys = Object.keys(attrs), i = 0; i < keys.length; i++) {
const
key = keys[i];
if (key in declMods) {
const
attrVal = attrs[key];
if (attrVal != null) {
res[key] = attrVal;
}
}
}
return res;
};
const
modsProp = getFullModsProp(ctx),
mods = {...oldComponent.mods};
for (let keys = Object.keys(mods), i = 0; i < keys.length; i++) {
const
key = keys[i];
if (ctx.sync.syncModCache[key]) {
delete mods[key];
}
}
if (Object.fastCompare(modsProp, getFullModsProp(oldComponent))) {
l.sync(mods);
} else {
l.sync(Object.assign(mods, modsProp));
}
}
/**
* Initializes the component modifiers
* @param component
*/
export function initMods(component: iBlock): ModsNTable {
const
ctx = component.unsafe,
declMods = ctx.meta.component.mods;
const
attrMods = <string[][]>[],
modVal = (val) => val != null ? String(val) : undefined;
for (let attrs = ctx.$attrs, keys = Object.keys(attrs), i = 0; i < keys.length; i++) {
const
key = keys[i],
modKey = key.camelize(false);
if (modKey in declMods) {
const attrVal = attrs[key];
attrs[key] = undefined;
ctx.watch(`$attrs.${key}`, (val: Dictionary = {}) => {
ctx.$el?.removeAttribute(key);
void ctx.setMod(modKey, modVal(val[key]));
});
if (attrVal == null) {
continue;
}
attrMods.push([modKey, attrVal]);
}
}
function link(propMods: CanUndef<ModsTable>): ModsNTable {
const
mods = Object.isDictionary(ctx.mods) ? ctx.mods : {...declMods};
if (propMods != null) {
for (let keys = Object.keys(propMods), i = 0; i < keys.length; i++) {
const
key = keys[i],
val = propMods[key];
if (val != null || mods[key] == null) {
mods[key] = modVal(val);
}
}
}
for (let i = 0; i < attrMods.length; i++) {
const [key, val] = attrMods[i];
mods[key] = val;
}
const
{experiments} = ctx.r.remoteState;
if (Object.isArray(experiments)) {
for (let i = 0; i < experiments.length; i++) {
const
el = experiments[i],
experimentMods = el.meta?.mods;
if (experimentMods) {
for (let keys = Object.keys(experimentMods), i = 0; i < keys.length; i++) {
const
key = keys[i],
val = experimentMods[key];
if (val != null || mods[key] == null) {
mods[key] = modVal(val);
}
}
}
}
}
for (let keys = Object.keys(mods), i = 0; i < keys.length; i++) {
const
key = keys[i],
val = modVal(mods[key]);
mods[key] = val;
if (ctx.hook !== 'beforeDataCreate') {
void ctx.setMod(key, val);
}
}
return mods;
}
return Object.cast(ctx.sync.link(link));
}
/**
* Returns a dictionary with watchable modifiers
* @param component
*/
export function getWatchableMods(component: iBlock): Readonly<ModsNTable> {
const
watchMods = {},
watchers = component.field.get<ModsNTable>('watchModsStore')!,
systemMods = component.mods;
for (let keys = Object.keys(systemMods), i = 0; i < keys.length; i++) {
const
key = keys[i];
if (key in watchers) {
watchMods[key] = systemMods[key];
} else {
Object.defineProperty(watchMods, key, {
configurable: true,
enumerable: true,
get: () => {
if (!(key in watchers)) {
Object.getPrototypeOf(watchers)[key] = systemMods[key];
}
return watchers[key];
}
});
}
}
return Object.freeze(watchMods);
}