@nitrogenbuilder/client-core
Version:
Nitrogen Builder Core Client
316 lines (275 loc) • 8.28 kB
text/typescript
import type {
BuilderModule,
ComponentSettings,
ComponentSettingsToProps,
PropArray,
} from '@nitrogenbuilder/types';
import { nitrogen } from '../index.js';
import { getDefault } from './getDefault.js';
import { resolve } from './resolve.js';
import { set } from './set.js';
type UpdateWithDefaultPropsInput = {
page: BuilderModule[];
dynamicData?: any;
requestedData?: any;
};
type ProcessedModule = {
id: string;
module: string | { name: string };
props: Record<string, any>;
children?: ProcessedModule[] | Record<string, ProcessedModule[]>;
requestedData?: any;
};
function processChildren(
mod: BuilderModule,
dynamicData: any = {},
requestedData: any = {}
): ProcessedModule[] | Record<string, ProcessedModule[]> | undefined {
if (!mod.props.children) return undefined;
if (Array.isArray(mod.props.children)) {
return mod.props.children.map((child: any) => {
return processModule(child, dynamicData, requestedData);
});
} else if (typeof mod.props.children === 'object') {
return Object.keys(mod.props.children).reduce((acc, slot) => {
if (Array.isArray(mod.props.children[slot])) {
return {
...acc,
[slot]: mod.props.children[slot].map((child: any) => {
return processModule(child, dynamicData, requestedData);
}),
};
}
return acc;
}, {});
}
return undefined;
}
type DeepPartial<T> = T extends object
? {
[P in keyof T]?: DeepPartial<T[P]>;
}
: T;
export function renderModuleProps<T extends ComponentSettings | string>(
props: T extends ComponentSettings
? DeepPartial<ComponentSettingsToProps<T>>
: Record<string, any>,
settings: T,
dynamicData: any = {},
requestedData: any = {}
): T extends ComponentSettings ? ComponentSettingsToProps<T> : any {
let _settings: ComponentSettings;
if (typeof settings === 'string') {
const module = nitrogen.getModule(settings);
if (!module) return {} as any;
_settings = {
categories: module.categories,
options: module.options,
} as ComponentSettings;
} else {
_settings = settings;
}
let spreadProps = { ...(props ?? {}) } as any;
delete spreadProps.children;
function recursiveArrayPropDefault(prop: PropArray, keys: string[]) {
if (prop.props) {
Object.entries(prop.props).forEach(([subPropK, subPropV]) => {
if (subPropV.type === 'array') {
let indexes = Object.keys(resolve(keys.join('.'), spreadProps) ?? {});
for (let i = 0; i < indexes.length; i++) {
recursiveArrayPropDefault(subPropV as PropArray, [
...keys,
`${indexes[i]}`,
subPropK,
]);
}
} else {
let indexes = Object.keys(resolve(keys.join('.'), spreadProps) ?? {});
for (let i = 0; i < indexes.length; i++) {
set(
spreadProps,
getDefault(
resolve(
[...keys, `${indexes[i]}`, subPropK].join('.'),
spreadProps
),
subPropV
),
[...keys, `${indexes[i]}`, subPropK]
);
}
}
});
}
}
Object.values(_settings.categories).forEach((category) => {
Object.entries(category.groups).forEach(([groupK, group]) => {
Object.entries(group.props).forEach(([propK, propV]) => {
if (spreadProps[groupK] === undefined) {
spreadProps[groupK] = {};
}
if (propV.type === 'array') {
recursiveArrayPropDefault(propV as PropArray, [groupK, propK]);
} else {
spreadProps[groupK][propK] = getDefault(
spreadProps[groupK][propK],
propV
);
}
});
});
});
function recursiveDynamicProps(keys: string[]) {
let props = resolve(keys.join('.'), spreadProps);
if (!props) return;
let propChildrenKeys = Object.keys(props);
for (let j = 0; j < propChildrenKeys.length; j++) {
let propChildrenKey = propChildrenKeys[j];
let propChild = props[propChildrenKey];
if (propChildrenKey.endsWith('___dynamic')) {
const dynamicVal =
resolve(propChild.key, dynamicData) || propChild.fallback || '';
props[propChildrenKey.replace('___dynamic', '')] =
typeof dynamicVal === 'boolean'
? dynamicVal
: (propChild.before || '') + dynamicVal + (propChild.after || '');
} else if (typeof props[propChildrenKey] === 'object') {
recursiveDynamicProps([...keys, propChildrenKey]);
}
}
}
let groupKeys = Object.keys(spreadProps);
for (let i = 0; i < groupKeys.length; i++) {
let groupKey = groupKeys[i];
if (typeof groupKey === 'string') {
if (typeof spreadProps[groupKey] === 'object') {
// check children properties for if the end in ___dynamic
recursiveDynamicProps([groupKey]);
}
}
}
return spreadProps as any;
}
function processModule(
mod: BuilderModule,
dynamicData: any = {},
requestedData: any = {},
includeChildren: boolean = true
): ProcessedModule {
const module =
typeof mod.module === 'object'
? mod.module
: nitrogen.getModule(mod.module);
let spreadProps = { ...(mod?.props ?? {}) };
delete spreadProps.children;
if (typeof module === 'object') {
function recursiveArrayPropDefault(prop: PropArray, keys: string[]) {
if (prop.props) {
Object.entries(prop.props).forEach(([subPropK, subPropV]) => {
if (subPropV.type === 'array') {
let indexes = Object.keys(
resolve(keys.join('.'), spreadProps) ?? {}
);
for (let i = 0; i < indexes.length; i++) {
recursiveArrayPropDefault(subPropV as PropArray, [
...keys,
`${indexes[i]}`,
subPropK,
]);
}
} else {
let indexes = Object.keys(
resolve(keys.join('.'), spreadProps) ?? {}
);
for (let i = 0; i < indexes.length; i++) {
set(
spreadProps,
getDefault(
resolve(
[...keys, `${indexes[i]}`, subPropK].join('.'),
spreadProps
),
subPropV
),
[...keys, `${indexes[i]}`, subPropK]
);
}
}
});
}
}
Object.values(module.categories).forEach((category) => {
Object.entries(category.groups).forEach(([groupK, group]) => {
Object.entries(group.props).forEach(([propK, propV]) => {
if (spreadProps[groupK] === undefined) {
spreadProps[groupK] = {};
}
if (propV.type === 'array') {
recursiveArrayPropDefault(propV as PropArray, [groupK, propK]);
} else {
spreadProps[groupK][propK] = getDefault(
spreadProps[groupK][propK],
propV
);
}
});
});
});
}
function recursiveDynamicProps(keys: string[]) {
let props = resolve(keys.join('.'), spreadProps);
if (!props) return;
let propChildrenKeys = Object.keys(props);
for (let j = 0; j < propChildrenKeys.length; j++) {
let propChildrenKey = propChildrenKeys[j];
let propChild = props[propChildrenKey];
if (propChildrenKey.endsWith('___dynamic')) {
const dynamicVal =
resolve(propChild.key, dynamicData) || propChild.fallback || '';
props[propChildrenKey.replace('___dynamic', '')] =
typeof dynamicVal === 'boolean'
? dynamicVal
: (propChild.before || '') + dynamicVal + (propChild.after || '');
} else if (typeof props[propChildrenKey] === 'object') {
recursiveDynamicProps([...keys, propChildrenKey]);
}
}
}
let groupKeys = Object.keys(spreadProps);
for (let i = 0; i < groupKeys.length; i++) {
let groupKey = groupKeys[i];
if (typeof groupKey === 'string') {
if (typeof spreadProps[groupKey] === 'object') {
// check children properties for if the end in ___dynamic
recursiveDynamicProps([groupKey]);
}
}
}
const children = includeChildren
? processChildren(mod, dynamicData, requestedData)
: undefined;
return {
id: mod.id,
module: mod.module,
props: spreadProps,
...(children && { children }),
...(requestedData[mod.id] && { requestedData: requestedData[mod.id] }),
};
}
export function updateWithDefaultProps({
page,
dynamicData,
requestedData,
}: UpdateWithDefaultPropsInput): ProcessedModule[] {
return page.map((block: BuilderModule) => {
return processModule(block, dynamicData, requestedData);
});
}
export function updateModuleProps(
module: BuilderModule,
dynamicData: any = {},
requestedData: any = {},
includeChildren: boolean = false
): ProcessedModule {
return processModule(module, dynamicData, requestedData, includeChildren);
}