strona-czynna/src/back/routes/component-preview/component-preview-actions.ts

425 lines
12 KiB
TypeScript

/* eslint-disable @typescript-eslint/consistent-type-assertions */
import type { Registry, TableData } from "@sealcode/jdd";
import { List, Table } from "@sealcode/jdd";
import type { BaseContext } from "koa";
import { isTableData, isTableRegularRow } from "@sealcode/jdd";
import objectPath from "object-path";
import type { JDDPageState } from "./jdd-page.js";
import { registry } from "../../jdd-components/registry.js";
import { makeJDDContext } from "../../jdd-context.js";
function moveElement<T>(array: Array<T>, fromIndex: number, toIndex: number): Array<T> {
const element = array.splice(fromIndex, 1)[0];
array.splice(toIndex, 0, element);
return array;
}
export function getComponentData(
state: JDDPageState,
arg_path: string[],
registry: Registry
) {
const index_arg = arg_path[1];
if (!index_arg) {
throw new Error("Missing component index in arg path");
}
const component_index = parseInt(index_arg);
const component_args = state.components[component_index]?.args || {};
const component_name = state.components[component_index]?.component_name || "";
const component = registry.get(component_name);
const arg_path_within_component = arg_path.slice(3); // remove "components" and the index of the component and "args"
const [argument, , argument_value] = component?.getArgumentAtPath(
arg_path_within_component,
component_args
) || [null, null, null];
return {
component_index,
component_args,
component_name,
component,
argument,
argument_value,
arg_path_within_component,
};
}
export const ComponentPreviewActions = <const>{
add_array_item: async (
ctx: BaseContext,
state: JDDPageState,
_: Record<string, string>,
arg_path: string[]
) => {
const {
component_name,
component,
argument,
arg_path_within_component,
argument_value,
} = getComponentData(state, arg_path, registry);
if (!component) {
console.error("unknown component: ", component_name);
return state;
}
if (!argument) {
console.error(
"Didn't find a list argument at this path",
arg_path_within_component
);
return state;
}
if (!(argument instanceof List)) {
throw new Error(
`Expected argument in path ${arg_path.join(
"."
)} to be an instance of List`
);
}
objectPath.insert(
state,
arg_path,
await argument.item_type.getExampleValue(makeJDDContext(ctx)),
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
Array.isArray(argument_value) ? argument_value.length : 0
);
return state;
},
remove_array_item: (
_ctx: BaseContext,
state: JDDPageState,
_: Record<string, string>,
arg_path: string[],
index_to_remove: number
): JDDPageState => {
objectPath.del(state, [...arg_path, index_to_remove]);
return state;
},
move_array_item_up: async (
_ctx: BaseContext,
state: JDDPageState,
_inputs: Record<string, string>,
arg_path: string[],
element_index: number
): Promise<JDDPageState> => {
const array_values = objectPath.get(state, arg_path);
const curr = array_values[element_index];
const prev = array_values[element_index - 1];
if (!prev || !curr) {
throw new Error("No element at such index or cannot move it up");
}
[array_values[element_index - 1], array_values[element_index]] = [curr, prev];
return state;
},
move_array_item_down: async (
_ctx: BaseContext,
state: JDDPageState,
_inputs: Record<string, string>,
arg_path: string[],
element_index: number
): Promise<JDDPageState> => {
const array_values = objectPath.get(state, arg_path);
const curr = array_values[element_index];
const next = array_values[element_index + 1];
if (!next || !curr) {
throw new Error("No element at such index or cannot move it up");
}
[array_values[element_index], array_values[element_index + 1]] = [next, curr];
return state;
},
change_component: async (
ctx: BaseContext,
_state: JDDPageState,
inputs: Record<string, unknown>
): Promise<JDDPageState> => {
const component_name = inputs.component;
if (!component_name || typeof component_name !== "string") {
throw new Error(
"Missing input: 'component' for action change_component. It should contain the name of the new component type"
);
}
const component = registry.get(component_name);
if (!component) {
throw new Error(`Unknown or disallowed component name: ${component_name}`);
}
return {
components: [
{
component_name: component_name,
args: (await component?.getExampleValues(makeJDDContext(ctx))) || {},
},
],
};
},
randomize_args: async (
ctx: BaseContext,
state: JDDPageState,
_inputs: Record<string, string>,
component_index_str: string
): Promise<JDDPageState> => {
const { component_index, component } = getComponentData(
state,
["components", component_index_str],
registry
);
const component_data = state.components[component_index];
if (!component_data) {
throw new Error("Missing component data");
}
component_data.args =
(await component?.getExampleValues(makeJDDContext(ctx))) || {};
return {
...state,
};
},
add_table_row: async (
ctx: BaseContext,
state: JDDPageState,
_: Record<string, string>,
arg_path: string[],
columns: number,
type: "header" | "row" = "row"
) => {
const jdd_context = makeJDDContext(ctx);
const { component_args, argument } = getComponentData(state, arg_path, registry);
let row;
if (!argument) {
console.error("Unknown component at path", arg_path);
return state;
}
if (!(argument instanceof Table)) {
throw new Error(
`Expected argument at path ${arg_path.join(".")} to be of type Table`
);
}
if (type == "header") {
row = {
type: "header",
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
header_content: await argument.header_type.getExampleValue(jdd_context),
};
} else {
const cells = [];
for (let i = 0; i < columns; i++) {
// eslint-disable-next-line no-await-in-loop
cells.push(await argument.cell_type.getExampleValue(jdd_context));
}
row = { type: "row", cells };
}
objectPath.insert(
state,
[...arg_path, "rows"],
row,
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
((objectPath.get(component_args, [...arg_path, "rows"]) as unknown[]) || [])
.length
);
return state;
},
add_table_column: async (
ctx: BaseContext,
state: JDDPageState,
_: Record<string, string>,
arg_path: string[]
) => {
const { argument } = getComponentData(state, arg_path, registry);
if (!argument) {
console.error("Unknown component at path", arg_path);
return state;
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const tableData: TableData<unknown, unknown> = objectPath.get(state, arg_path);
if (!isTableData(tableData)) {
throw new Error("wrong table data");
}
// eslint-disable-next-line @typescript-eslint/no-for-in-array
for (const i in tableData.rows) {
const row = tableData.rows[i];
if (isTableRegularRow(row)) {
// eslint-disable-next-line no-await-in-loop
row.cells.push(await argument.getExampleValue(makeJDDContext(ctx)));
}
}
objectPath.set(state, arg_path, tableData);
return state;
},
remove_table_column: (
_ctx: BaseContext,
state: JDDPageState,
_: Record<string, string>,
arg_path: string[],
column_index_to_remove: number
): JDDPageState => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const tableData: TableData<unknown, unknown> = objectPath.get(state, arg_path);
if (!isTableData(tableData)) {
throw new Error("wrong table data");
}
// eslint-disable-next-line @typescript-eslint/no-for-in-array
for (const i in tableData.rows) {
const row = tableData.rows[i];
if (isTableRegularRow(row)) {
row.cells = row.cells.filter((_, i) => i != column_index_to_remove);
}
}
objectPath.set(state, arg_path, tableData);
return state;
},
remove_table_row: (
_ctx: BaseContext,
state: JDDPageState,
_: Record<string, string>,
arg_path: string[],
row_index: number
): JDDPageState => {
objectPath.del(state, [...arg_path, "rows", row_index]);
return state;
},
move_table_column_right: (
_ctx: BaseContext,
state: JDDPageState,
_: Record<string, string>,
arg_path: string[],
column_index: number
) => {
const { component_args } = getComponentData(state, arg_path, registry);
const last_path_element = arg_path.at(-1);
if (!last_path_element) {
throw new Error("arg path is empty");
}
const data = objectPath.get<unknown>(component_args, last_path_element, "");
if (!isTableData(data)) {
throw new Error("Expected arg value for a table to be properly shaped");
}
for (const row of data.rows) {
if (row.type == "row") {
moveElement(row.cells, column_index, column_index + 1);
}
}
objectPath.set(state, [...arg_path, "rows"], data.rows);
return state;
},
move_table_row_down: (
_ctx: BaseContext,
state: JDDPageState,
_: Record<string, string>,
arg_path: string[],
row_index: number
) => {
const { component_args } = getComponentData(state, arg_path, registry);
const last_path_element = arg_path.at(-1);
if (!last_path_element) {
throw new Error("arg path is empty");
}
const data = objectPath.get<unknown>(component_args, last_path_element, "");
if (!isTableData(data)) {
throw new Error("Expected arg value for a table to be properly shaped");
}
moveElement(data.rows, row_index, row_index + 1);
objectPath.set(state, [...arg_path, "rows"], data.rows);
return state;
},
change_size: (
_ctx: BaseContext,
state: JDDPageState,
inputs: Record<string, string>
) => {
return {
...state,
preview_size: inputs.size,
};
},
add_component: async (
ctx: BaseContext,
state: JDDPageState,
inputs: Record<string, string>
): Promise<JDDPageState> => {
const component_name = inputs.component;
if (!component_name) {
throw new Error("Missing component name");
}
const component = registry.get(component_name);
return {
...state,
components: [
...state.components,
{
component_name: component_name,
args: (await component?.getExampleValues(makeJDDContext(ctx))) || {},
},
],
};
},
remove_component: async (
_ctx: BaseContext,
state: JDDPageState,
_inputs: Record<string, string>,
component_index: number
): Promise<JDDPageState> => {
const newComponentState = [...state.components];
newComponentState.splice(component_index, 1);
return {
...state,
components: newComponentState,
};
},
move_component_up: async (
_ctx: BaseContext,
state: JDDPageState,
_inputs: Record<string, string>,
component_index: number
): Promise<JDDPageState> => {
const newComps = [...state.components];
const prev = newComps[component_index - 1];
const curr = newComps[component_index];
if (!prev || !curr) {
throw new Error("No component at such index or cannot move it up");
}
[newComps[component_index], newComps[component_index - 1]] = [prev, curr];
return { ...state, components: newComps };
},
move_component_down: async (
_ctx: BaseContext,
state: JDDPageState,
_inputs: Record<string, string>,
component_index: number
): Promise<JDDPageState> => {
const newComps = [...state.components];
const next = newComps[component_index + 1];
const curr = newComps[component_index];
if (!next || !curr) {
throw new Error("No component at such index or cannot move it up");
}
[newComps[component_index], newComps[component_index + 1]] = [next, curr];
return { ...state, components: newComps };
},
remove_file: async (
_ctx: BaseContext,
state: JDDPageState,
_: Record<string, string>,
arg_path: string[]
): Promise<JDDPageState> => {
objectPath.set(state, arg_path, null);
return state;
},
};