Page MenuHomeSealhub

nested-component.ts
No OneTemporary

nested-component.ts

import { is, predicates } from "@sealcode/ts-predicates";
import { ComponentArgument } from "./component-argument.js";
import { ContainerArgument } from "./container-argument.js";
import { Registry } from "../registry.js";
import { Enum } from "./enum.js";
import { JDDContext } from "../jdd-context.js";
import { Structured } from "./structured.js";
import { componentNameToCSSClump } from "../clumps.js";
export class NestedComponent extends ContainerArgument<
Record<string, unknown>,
Record<string, unknown>
> {
getTypeName() {
return "nested-component";
}
getComponentArgument(registry: Registry) {
const dropdown = new Enum(Object.keys(registry.getAll()));
dropdown.parent_argument = this;
return dropdown;
}
getInnerStructure(
registry: Registry,
component_name: string
): Record<string, ComponentArgument<unknown>> {
return registry.get(component_name)?.getArguments() || {};
}
getStructure(registry: Registry, component_name: string) {
return {
component_name: this.getComponentArgument(registry),
[`${component_name}:values`]: new Structured(
this.getInnerStructure(registry, component_name)
),
};
}
getSubArgument(
ctx: JDDContext,
[key, ...rest]: string[],
value: Record<string, unknown>
// eslint-disable-next-line @typescript-eslint/no-explicit-any
): readonly [ComponentArgument<any> | null, string[], unknown] {
if (key == "component_name") {
const dropdown = this.getComponentArgument(ctx.registry);
return [dropdown, rest, {}];
}
// the other key in the path can only be the component name + a colon:
if (key.endsWith(":values")) {
const component_name = key.replace(":values", "");
const structured = new Structured(
this.getInnerStructure(ctx.registry, component_name)
);
return <const>[
structured || null,
rest,
structured
? value[key]
? structured.receivedToParsed(
ctx,
value[key] as Record<string, unknown>
)
: structured.getExampleValue(ctx)
: {},
];
}
// invalid argument
console.error(
"Invalid argument for component jdd arg:",
key,
rest,
value
);
return [null, [], {}];
}
countWords(ctx: JDDContext, value: Record<string, unknown>): number {
const component_name = value.component_name as string;
const component_values = value[`${component_name}:values`] as Record<
string,
unknown
>;
return Object.entries(component_values).reduce((acc, [key, val]) => {
if (!this.getInnerStructure(ctx.registry, component_name)[key]) {
console.warn(`Key ${key} doesn't exist in structured argument`);
return acc + 0;
}
return (
acc +
this.getInnerStructure(ctx.registry, component_name)[
key
].countWords(ctx, val)
);
}, 0);
}
async getEmptyValue(ctx: JDDContext): Promise<Record<string, unknown>> {
const component_name = Object.keys(ctx.registry.getAll())[0];
return {
component_name,
[`${component_name}:values`]: Object.fromEntries(
await Promise.all(
Object.entries(
this.getInnerStructure(ctx.registry, component_name)
).map(async ([name, arg]) => [
name,
await arg.getEmptyValue(ctx),
])
)
) as Record<string, unknown>,
};
}
async getExampleValue(context: JDDContext) {
const component_name = Object.keys(context.registry.getAll())[0];
if (this.example_values.length) {
return super.getExampleValue(context);
}
const result = {
component_name,
[`${component_name}:values`]: Object.fromEntries(
await Promise.all(
Object.entries(
this.getInnerStructure(context.registry, component_name)
).map(async ([name, arg]) => [
name,
await arg.getExampleValue(context),
])
)
) as Record<string, unknown>,
};
return result;
}
async processAllSubarguments(
context: JDDContext,
input: unknown,
processing_function: (
argument: ComponentArgument<unknown>,
value: unknown
) => Promise<unknown>
): Promise<Record<string, unknown> | Record<string, unknown>[]> {
if (!is(input, predicates.object)) {
throw new Error(`Not an object: ${input as string}`);
}
const component_name = input.component_name as string;
const result: Record<string, unknown> = {};
const subinput = input[`${component_name}:values`] || {};
if (!is(subinput, predicates.object)) {
throw new Error(`Not an object: ${subinput as string}`);
}
await Promise.all(
Object.entries(subinput).map(async ([obj_key, obj_value]) => {
const nested_arg_type: ComponentArgument<unknown> =
this.getInnerStructure(context.registry, component_name)[
obj_key
];
if (!nested_arg_type) {
return [obj_key, null];
}
const new_value = await processing_function(
nested_arg_type,
obj_value
);
result[obj_key] = new_value;
})
);
// if we're in a list and any of the values return an array, we will multiply the object
if (this.hasParent("list")) {
const keys_with_unexpected_arrays = Object.entries(result)
.filter(([key, value]) => {
const nested_arg_type: ComponentArgument<unknown> =
this.getInnerStructure(
context.registry,
component_name
)[key];
return (
nested_arg_type.getTypeName() !== "list" &&
Array.isArray(value)
);
})
.map(([key]) => key);
if (keys_with_unexpected_arrays.length > 1) {
throw new Error(
"Multiplying on more than one field at the same time is not implemented yet"
);
}
if (keys_with_unexpected_arrays.length == 1) {
const key = keys_with_unexpected_arrays[0];
const old_result = result;
const array = old_result[key];
if (!Array.isArray(array)) {
throw new Error("expected an array");
}
return array.map((value: unknown) => ({
...old_result,
[key]: value,
}));
} else {
return {
component_name,
[`${component_name}:values`]: result,
};
}
} else {
return { component_name, [`${component_name}:values`]: result };
}
}
static render({
jdd_context,
data,
classes = [],
index = -1,
}: {
jdd_context: JDDContext;
data: Record<string, unknown>;
classes?: string[];
index?: number;
}) {
const component_name = data.component_name as string;
const component = jdd_context.registry.get(component_name);
if (!component) {
return "Unknown component: ${component_name}";
}
const args = data[`${component_name}:values`] as Record<
string,
unknown
>;
return component.toHTML({ args, classes, jdd_context, index });
}
static getCSSClumpsForNested(
jdd_context: JDDContext,
data: Record<string, unknown>
): string[] {
const component_name = data.component_name as string;
const component = jdd_context.registry.get(component_name);
if (!component) {
console.error("Unknown component: ${component_name}");
return [];
}
const args = data[`${component_name}:values`] as Record<
string,
unknown
>;
return [
componentNameToCSSClump(component_name),
...component.getCSSClumps(jdd_context, args),
];
}
async receivedToParsed(
context: JDDContext,
value: Record<string, unknown>
): Promise<Record<string, unknown>> {
const result = await super.receivedToParsed(context, value);
const component_name = result.component_name;
if (!is(component_name, predicates.string)) {
throw new Error(`Not an object: ${component_name as string}`);
}
if (component_name) {
// delete previous values after changing the component
for (const invalid_key of Object.keys(result).filter(
(e) => e.endsWith(":values") && e !== `${component_name}:values`
)) {
delete result[invalid_key];
}
// generate new example values for the new component
if (
Object.keys(
result[`${component_name}:values`] as Record<
string,
unknown
>
).length == 0
) {
result[`${component_name}:values`] = await context.registry
.get(component_name)
?.getExampleValues(context);
}
}
return result;
}
}

File Metadata

Mime Type
text/x-Algol68
Expires
Mon, Jul 21, 00:20 (1 d, 5 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
837930
Default Alt Text
nested-component.ts (7 KB)

Event Timeline