Sreact page for editing JDD
Summary: Ref T2764 Reviewers: #testers, kuba-orlik Reviewed By: #testers, kuba-orlik Subscribers: kuba-orlik, jenkins-user Maniphest Tasks: T2764 Differential Revision: https://hub.sealcode.org/D1406
This commit is contained in:
parent
a3965ae47e
commit
bec96ac604
@ -16,6 +16,7 @@ module.exports = {
|
|||||||
project: "./tsconfig-back.json",
|
project: "./tsconfig-back.json",
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
|
"no-unused-vars": [2, { varsIgnorePattern: "TempstreamJSX" }],
|
||||||
"@typescript-eslint/no-unused-vars": [2, { varsIgnorePattern: "TempstreamJSX" }],
|
"@typescript-eslint/no-unused-vars": [2, { varsIgnorePattern: "TempstreamJSX" }],
|
||||||
"no-unused-vars": 0,
|
"no-unused-vars": 0,
|
||||||
"@typescript-eslint/require-await": 0,
|
"@typescript-eslint/require-await": 0,
|
||||||
|
619
package-lock.json
generated
619
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -43,8 +43,8 @@
|
|||||||
"@koa/router": "^12.0.1",
|
"@koa/router": "^12.0.1",
|
||||||
"@playwright/test": "^1.36.1",
|
"@playwright/test": "^1.36.1",
|
||||||
"@sealcode/file-manager": "^1.0.2",
|
"@sealcode/file-manager": "^1.0.2",
|
||||||
"@sealcode/jdd": "^0.4.7",
|
"@sealcode/jdd": "^0.4.10",
|
||||||
"@sealcode/sealgen": "^0.15.15",
|
"@sealcode/sealgen": "^0.15.16",
|
||||||
"@sealcode/ts-predicates": "^0.6.2",
|
"@sealcode/ts-predicates": "^0.6.2",
|
||||||
"@types/kill-port": "^2.0.0",
|
"@types/kill-port": "^2.0.0",
|
||||||
"@types/leaflet": "^1.9.8",
|
"@types/leaflet": "^1.9.8",
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import type { Context } from "koa";
|
import type { Context } from "koa";
|
||||||
import { TempstreamJSX } from "tempstream";
|
import { tempstream, TempstreamJSX } from "tempstream";
|
||||||
import { Page } from "@sealcode/sealgen";
|
import { Page } from "@sealcode/sealgen";
|
||||||
import html from "../html.js";
|
import html, { defaultHead } from "../html.js";
|
||||||
import { registry } from "../jdd-components/components.js";
|
import { registry } from "../jdd-components/components.js";
|
||||||
import { documentContainerFromParsed, render } from "@sealcode/jdd";
|
import { documentContainerFromParsed, render, renderEarlyAssets } from "@sealcode/jdd";
|
||||||
import { shuffle } from "../util.js";
|
import { shuffle } from "../util.js";
|
||||||
import { makeJDDContext } from "../jdd-context.js";
|
import { makeJDDContext } from "../jdd-context.js";
|
||||||
|
|
||||||
@ -33,7 +33,14 @@ export default new (class AllComponentsPage extends Page {
|
|||||||
"AllComponents",
|
"AllComponents",
|
||||||
<div>
|
<div>
|
||||||
{render(registry, documentContainerFromParsed(document), jdd_context)}
|
{render(registry, documentContainerFromParsed(document), jdd_context)}
|
||||||
</div>
|
</div>,
|
||||||
|
{},
|
||||||
|
(...args) =>
|
||||||
|
tempstream`${defaultHead(...args)}${renderEarlyAssets(
|
||||||
|
registry,
|
||||||
|
documentContainerFromParsed(document),
|
||||||
|
jdd_context
|
||||||
|
)}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
@ -18,10 +18,7 @@ export function ComponentInputEnum<State, S extends string, T extends Enum<S>>({
|
|||||||
<div>
|
<div>
|
||||||
<label>
|
<label>
|
||||||
{arg_path.at(-1) || ""}
|
{arg_path.at(-1) || ""}
|
||||||
<select
|
<select name={`$${printArgPath(arg_path)}`} onchange={onchange}>
|
||||||
name={`$.component_args${printArgPath(arg_path)}`}
|
|
||||||
onchange={onchange}
|
|
||||||
>
|
|
||||||
{arg.values.map((v: S) => (
|
{arg.values.map((v: S) => (
|
||||||
<option value={v} selected={value == v}>
|
<option value={v} selected={value == v}>
|
||||||
{v}
|
{v}
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
import type { Image } from "@sealcode/jdd";
|
|
||||||
import type { BaseContext } from "koa";
|
import type { BaseContext } from "koa";
|
||||||
import type { StatefulPage } from "@sealcode/sealgen";
|
|
||||||
import { TempstreamJSX } from "tempstream";
|
import { TempstreamJSX } from "tempstream";
|
||||||
import type { ComponentPreviewState } from "../components.sreact.js";
|
import type { FilePointer } from "@sealcode/file-manager";
|
||||||
|
import type { Image } from "@sealcode/jdd";
|
||||||
|
import type { StatefulPage } from "@sealcode/sealgen";
|
||||||
import type { ComponentPreviewActions } from "./component-preview-actions.js";
|
import type { ComponentPreviewActions } from "./component-preview-actions.js";
|
||||||
|
import type { JDDPageState } from "./jdd-page.js";
|
||||||
|
import { makeJDDContext } from "../../jdd-context.js";
|
||||||
import { printArgPath } from "./print-arg-path.js";
|
import { printArgPath } from "./print-arg-path.js";
|
||||||
import { htmlEscape } from "escape-goat";
|
import { htmlEscape } from "escape-goat";
|
||||||
import type { FilePointer } from "@sealcode/file-manager";
|
|
||||||
import { makeJDDContext } from "../../jdd-context.js";
|
|
||||||
|
|
||||||
export function ComponentInputImage<State extends ComponentPreviewState>({
|
export function ComponentInputImage<State extends JDDPageState>({
|
||||||
arg_path,
|
arg_path,
|
||||||
arg,
|
arg,
|
||||||
value,
|
value,
|
||||||
@ -19,9 +19,9 @@ export function ComponentInputImage<State extends ComponentPreviewState>({
|
|||||||
arg_path: string[];
|
arg_path: string[];
|
||||||
arg: Image;
|
arg: Image;
|
||||||
value: FilePointer | null;
|
value: FilePointer | null;
|
||||||
page: StatefulPage<ComponentPreviewState, typeof ComponentPreviewActions>;
|
page: StatefulPage<JDDPageState, typeof ComponentPreviewActions>;
|
||||||
ctx: BaseContext;
|
ctx: BaseContext;
|
||||||
}) {
|
}): JSX.Element {
|
||||||
const jdd_context = makeJDDContext(ctx);
|
const jdd_context = makeJDDContext(ctx);
|
||||||
return (
|
return (
|
||||||
<div style="margin-bottom: 10px">
|
<div style="margin-bottom: 10px">
|
||||||
@ -38,7 +38,7 @@ export function ComponentInputImage<State extends ComponentPreviewState>({
|
|||||||
})}
|
})}
|
||||||
<input
|
<input
|
||||||
type="file"
|
type="file"
|
||||||
name={`$.component_args${printArgPath(arg_path)}.new`}
|
name={`$${printArgPath(arg_path)}.new`}
|
||||||
value=""
|
value=""
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
data-action="change->input-image-preview#handleChange"
|
data-action="change->input-image-preview#handleChange"
|
||||||
@ -48,7 +48,7 @@ export function ComponentInputImage<State extends ComponentPreviewState>({
|
|||||||
<div>
|
<div>
|
||||||
<input
|
<input
|
||||||
type="hidden"
|
type="hidden"
|
||||||
name={`$.component_args${printArgPath(arg_path)}.old`}
|
name={`$${printArgPath(arg_path)}.old`}
|
||||||
value={htmlEscape(value?.token || "")}
|
value={htmlEscape(value?.token || "")}
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
/>
|
/>
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
import type { ComponentArgument, List } from "@sealcode/jdd";
|
|
||||||
import type { StatefulPage } from "@sealcode/sealgen";
|
|
||||||
import type { BaseContext } from "koa";
|
import type { BaseContext } from "koa";
|
||||||
import { TempstreamJSX } from "tempstream";
|
import { TempstreamJSX } from "tempstream";
|
||||||
import type { ComponentPreviewState } from "../components.sreact.js";
|
import type { ComponentArgument, List } from "@sealcode/jdd";
|
||||||
|
import type { JDDPageState } from "./jdd-page.js";
|
||||||
|
import type { StatefulPage } from "@sealcode/sealgen";
|
||||||
|
import type { Readable } from "node:stream";
|
||||||
import { ComponentInput } from "./component-input.js";
|
import { ComponentInput } from "./component-input.js";
|
||||||
import type { ComponentPreviewActions } from "./component-preview-actions.js";
|
import type { ComponentPreviewActions } from "./component-preview-actions.js";
|
||||||
|
|
||||||
export async function ComponentInputList<
|
export async function ComponentInputList<
|
||||||
State extends ComponentPreviewState,
|
State extends JDDPageState,
|
||||||
T extends ComponentArgument<unknown>
|
T extends ComponentArgument<unknown>
|
||||||
>({
|
>({
|
||||||
state,
|
state,
|
||||||
@ -20,10 +21,11 @@ export async function ComponentInputList<
|
|||||||
state: State;
|
state: State;
|
||||||
ctx: BaseContext;
|
ctx: BaseContext;
|
||||||
arg_path: string[];
|
arg_path: string[];
|
||||||
arg: List<T>;
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
arg: List<T, any>;
|
||||||
value: T[];
|
value: T[];
|
||||||
page: StatefulPage<ComponentPreviewState, typeof ComponentPreviewActions>;
|
page: StatefulPage<JDDPageState, typeof ComponentPreviewActions>;
|
||||||
}) {
|
}): Promise<Readable> {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
value = [];
|
value = [];
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ import type { BaseContext } from "koa";
|
|||||||
import type { ComponentArgument, Structured } from "@sealcode/jdd";
|
import type { ComponentArgument, Structured } from "@sealcode/jdd";
|
||||||
import type { StatefulPage } from "@sealcode/sealgen";
|
import type { StatefulPage } from "@sealcode/sealgen";
|
||||||
import { TempstreamJSX } from "tempstream";
|
import { TempstreamJSX } from "tempstream";
|
||||||
import type { ComponentPreviewState } from "../components.sreact.js";
|
|
||||||
import { ComponentInput } from "./component-input.js";
|
import { ComponentInput } from "./component-input.js";
|
||||||
|
import type { JDDPageState } from "./jdd-page.js";
|
||||||
import type { ComponentPreviewActions } from "./component-preview-actions.js";
|
import type { ComponentPreviewActions } from "./component-preview-actions.js";
|
||||||
|
|
||||||
export function ComponentInputStructured<
|
export function ComponentInputStructured<
|
||||||
@ -17,13 +17,13 @@ export function ComponentInputStructured<
|
|||||||
rerender_callback,
|
rerender_callback,
|
||||||
page,
|
page,
|
||||||
}: {
|
}: {
|
||||||
state: ComponentPreviewState;
|
state: JDDPageState;
|
||||||
ctx: BaseContext;
|
ctx: BaseContext;
|
||||||
arg_path: string[];
|
arg_path: string[];
|
||||||
arg: T;
|
arg: T;
|
||||||
value: Record<string, unknown>;
|
value: Record<string, unknown>;
|
||||||
rerender_callback?: string;
|
rerender_callback?: string;
|
||||||
page: StatefulPage<ComponentPreviewState, typeof ComponentPreviewActions>;
|
page: StatefulPage<JDDPageState, typeof ComponentPreviewActions>;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<fieldset>
|
<fieldset>
|
||||||
|
@ -3,10 +3,9 @@ import { isTableHeader } from "@sealcode/jdd";
|
|||||||
import type { StatefulPage } from "@sealcode/sealgen";
|
import type { StatefulPage } from "@sealcode/sealgen";
|
||||||
import type { BaseContext } from "koa";
|
import type { BaseContext } from "koa";
|
||||||
import { TempstreamJSX } from "tempstream";
|
import { TempstreamJSX } from "tempstream";
|
||||||
import type { ComponentPreviewState } from "../components.sreact.js";
|
|
||||||
import { ComponentInput } from "./component-input.js";
|
import { ComponentInput } from "./component-input.js";
|
||||||
import type { ComponentPreviewActions } from "./component-preview-actions.js";
|
import type { ComponentPreviewActions } from "./component-preview-actions.js";
|
||||||
|
import type { JDDPageState } from "./jdd-page.js";
|
||||||
import add_column_right_icon from "./table-add-column-right.svg";
|
import add_column_right_icon from "./table-add-column-right.svg";
|
||||||
import add_row_below_icon from "./table-add-row-below.svg";
|
import add_row_below_icon from "./table-add-row-below.svg";
|
||||||
import add_column_header_icon from "./table-add-row-header-below.svg";
|
import add_column_header_icon from "./table-add-row-header-below.svg";
|
||||||
@ -16,7 +15,7 @@ import move_column_right_icon from "./table-move-column-right.svg";
|
|||||||
import move_row_down_icon from "./table-move-row-down.svg";
|
import move_row_down_icon from "./table-move-row-down.svg";
|
||||||
|
|
||||||
export async function ComponentInputTable<
|
export async function ComponentInputTable<
|
||||||
State extends ComponentPreviewState,
|
State extends JDDPageState,
|
||||||
CellType,
|
CellType,
|
||||||
HeaderType
|
HeaderType
|
||||||
>({
|
>({
|
||||||
@ -32,8 +31,8 @@ export async function ComponentInputTable<
|
|||||||
arg_path: string[];
|
arg_path: string[];
|
||||||
arg: Table<CellType, HeaderType>;
|
arg: Table<CellType, HeaderType>;
|
||||||
value: TableData<CellType, HeaderType>;
|
value: TableData<CellType, HeaderType>;
|
||||||
page: StatefulPage<ComponentPreviewState, typeof ComponentPreviewActions>;
|
page: StatefulPage<JDDPageState, typeof ComponentPreviewActions>;
|
||||||
}) {
|
}): Promise<import("stream").Readable> {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
value = arg.getEmptyValue();
|
value = arg.getEmptyValue();
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,24 @@
|
|||||||
import type { FilePointer } from "@sealcode/file-manager";
|
import { printArgPath } from "./print-arg-path.js";
|
||||||
|
import type { BaseContext } from "koa";
|
||||||
import type { ComponentArgument, TableData } from "@sealcode/jdd";
|
import type { ComponentArgument, TableData } from "@sealcode/jdd";
|
||||||
import { ComponentArguments, Enum, Image, List, Structured, Table } from "@sealcode/jdd";
|
import { ComponentArguments, Enum, Image, List, Structured, Table } from "@sealcode/jdd";
|
||||||
|
import { ComponentInputStructured } from "./component-input-structured.js";
|
||||||
import type { StatefulPage } from "@sealcode/sealgen";
|
import type { StatefulPage } from "@sealcode/sealgen";
|
||||||
import { is, predicates } from "@sealcode/ts-predicates";
|
import type { ComponentPreviewActions } from "./component-preview-actions.js";
|
||||||
import type { BaseContext } from "koa";
|
import type { Readable } from "node:stream";
|
||||||
import { TempstreamJSX } from "tempstream";
|
import { ComponentInputList } from "./component-input-list.js";
|
||||||
import type { ComponentPreviewState } from "../components.sreact.js";
|
import type { JDDPageState } from "./jdd-page.js";
|
||||||
import { ComponentInputEnum } from "./component-input-enum.js";
|
import { ComponentInputEnum } from "./component-input-enum.js";
|
||||||
import { ComponentInputImage } from "./component-input-image.js";
|
import { ComponentInputImage } from "./component-input-image.js";
|
||||||
import { ComponentInputList } from "./component-input-list.js";
|
|
||||||
import { ComponentInputStructured } from "./component-input-structured.js";
|
|
||||||
import { ComponentInputTable } from "./component-input-table.js";
|
import { ComponentInputTable } from "./component-input-table.js";
|
||||||
import type { ComponentPreviewActions } from "./component-preview-actions.js";
|
import { TempstreamJSX } from "tempstream";
|
||||||
import { printArgPath } from "./print-arg-path.js";
|
import type { FilePointer } from "@sealcode/file-manager";
|
||||||
|
import { is, predicates } from "@sealcode/ts-predicates";
|
||||||
|
|
||||||
export const actionName = "Components";
|
export const actionName = "Components";
|
||||||
const absoluteUrlPattern = "http(s?)(://)((www.)?)(([^.]+).)?([a-zA-z0-9-_]+)";
|
const absoluteUrlPattern = "http(s?)(://)((www.)?)(([^.]+).)?([a-zA-z0-9-_]+)";
|
||||||
|
|
||||||
export function ComponentInput<State extends ComponentPreviewState, T>({
|
export function ComponentInput<State extends JDDPageState, T>({
|
||||||
ctx,
|
ctx,
|
||||||
state,
|
state,
|
||||||
arg_path,
|
arg_path,
|
||||||
@ -30,8 +31,8 @@ export function ComponentInput<State extends ComponentPreviewState, T>({
|
|||||||
arg_path: string[];
|
arg_path: string[];
|
||||||
arg: ComponentArgument<T>;
|
arg: ComponentArgument<T>;
|
||||||
value: T;
|
value: T;
|
||||||
page: StatefulPage<ComponentPreviewState, typeof ComponentPreviewActions>;
|
page: StatefulPage<JDDPageState, typeof ComponentPreviewActions>;
|
||||||
}) {
|
}): Readable | Promise<Readable> {
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
value = arg.getEmptyValue();
|
value = arg.getEmptyValue();
|
||||||
}
|
}
|
||||||
@ -106,7 +107,7 @@ export function ComponentInput<State extends ComponentPreviewState, T>({
|
|||||||
{arg_path.at(-1) || ""}
|
{arg_path.at(-1) || ""}
|
||||||
{argType == "markdown" ? (
|
{argType == "markdown" ? (
|
||||||
<textarea
|
<textarea
|
||||||
name={`$.component_args${printArgPath(arg_path)}`}
|
name={`$${printArgPath(arg_path)}`}
|
||||||
onblur={page.rerender()}
|
onblur={page.rerender()}
|
||||||
cols="40"
|
cols="40"
|
||||||
>
|
>
|
||||||
@ -115,7 +116,7 @@ export function ComponentInput<State extends ComponentPreviewState, T>({
|
|||||||
) : (
|
) : (
|
||||||
<input
|
<input
|
||||||
type={inputType}
|
type={inputType}
|
||||||
name={`$.component_args${printArgPath(arg_path)}`}
|
name={`$${printArgPath(arg_path)}`}
|
||||||
value={is(value, predicates.string) ? value : ""}
|
value={is(value, predicates.string) ? value : ""}
|
||||||
size="40"
|
size="40"
|
||||||
pattern={isUrlAbsolute ? absoluteUrlPattern : undefined}
|
pattern={isUrlAbsolute ? absoluteUrlPattern : undefined}
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import type { ComponentArgument, List, Table, TableData } from "@sealcode/jdd";
|
import type { Registry, TableData } from "@sealcode/jdd";
|
||||||
|
import { List, Table } from "@sealcode/jdd";
|
||||||
import type { BaseContext } from "koa";
|
import type { BaseContext } from "koa";
|
||||||
import { isTableData, isTableRegularRow } from "@sealcode/jdd";
|
import { isTableData, isTableRegularRow } from "@sealcode/jdd";
|
||||||
import objectPath from "object-path";
|
import objectPath from "object-path";
|
||||||
import { registry } from "../../jdd-components/components.js";
|
import type { JDDPageState } from "./jdd-page.js";
|
||||||
import type { ComponentPreviewState } from "../components.sreact.js";
|
import { registry } from "../../jdd-components/registry.js";
|
||||||
import { makeJDDContext } from "../../jdd-context.js";
|
import { makeJDDContext } from "../../jdd-context.js";
|
||||||
|
|
||||||
function moveElement<T>(array: Array<T>, fromIndex: number, toIndex: number): Array<T> {
|
function moveElement<T>(array: Array<T>, fromIndex: number, toIndex: number): Array<T> {
|
||||||
@ -12,109 +13,152 @@ function moveElement<T>(array: Array<T>, fromIndex: number, toIndex: number): Ar
|
|||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getComponentData(
|
||||||
|
state: JDDPageState,
|
||||||
|
arg_path: string[],
|
||||||
|
registry: Registry
|
||||||
|
) {
|
||||||
|
const component_index = parseInt(arg_path[1]);
|
||||||
|
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>{
|
export const ComponentPreviewActions = <const>{
|
||||||
add_array_item: async (
|
add_array_item: async (
|
||||||
ctx: BaseContext,
|
ctx: BaseContext,
|
||||||
state: ComponentPreviewState,
|
state: JDDPageState,
|
||||||
_: Record<string, string>,
|
_: Record<string, string>,
|
||||||
arg_path: string[]
|
arg_path: string[]
|
||||||
) => {
|
) => {
|
||||||
const component_args = state.component_args;
|
const {
|
||||||
const component = registry.get(state.component);
|
component_name,
|
||||||
|
component,
|
||||||
|
argument,
|
||||||
|
arg_path_within_component,
|
||||||
|
argument_value,
|
||||||
|
} = getComponentData(state, arg_path, registry);
|
||||||
if (!component) {
|
if (!component) {
|
||||||
console.error("unknown component: ", state.component);
|
console.error("unknown component: ", component_name);
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
||||||
const argument = component.getArgumentAtPath(
|
|
||||||
arg_path,
|
|
||||||
state.component_args
|
|
||||||
) as List<ComponentArgument<unknown>> | null;
|
|
||||||
if (!argument) {
|
if (!argument) {
|
||||||
console.error("Didn't find a list argument at this path", arg_path);
|
console.error(
|
||||||
|
"Didn't find a list argument at this path",
|
||||||
|
arg_path_within_component
|
||||||
|
);
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
if (!(argument instanceof List)) {
|
||||||
|
throw new Error(
|
||||||
|
`Expected argument in path ${arg_path.join(
|
||||||
|
"."
|
||||||
|
)} to be an instance of List`
|
||||||
|
);
|
||||||
|
}
|
||||||
objectPath.insert(
|
objectPath.insert(
|
||||||
component_args,
|
state,
|
||||||
arg_path,
|
arg_path,
|
||||||
await argument.item_type.getExampleValue(makeJDDContext(ctx)),
|
await argument.item_type.getExampleValue(makeJDDContext(ctx)),
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
((objectPath.get(component_args, arg_path) as unknown[]) || []).length
|
Array.isArray(argument_value) ? argument_value.length : 0
|
||||||
);
|
);
|
||||||
return {
|
return state;
|
||||||
...state,
|
|
||||||
component_args,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
remove_array_item: (
|
remove_array_item: (
|
||||||
_ctx: BaseContext,
|
_ctx: BaseContext,
|
||||||
state: ComponentPreviewState,
|
state: JDDPageState,
|
||||||
_: Record<string, string>,
|
_: Record<string, string>,
|
||||||
arg_path: string[],
|
arg_path: string[],
|
||||||
index_to_remove: number
|
index_to_remove: number
|
||||||
) => {
|
): JDDPageState => {
|
||||||
const component_args = state.component_args;
|
objectPath.del(state, [...arg_path, index_to_remove]);
|
||||||
objectPath.del(component_args, [...arg_path, index_to_remove]);
|
return state;
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
component_args,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
|
|
||||||
change_component: async (
|
change_component: async (
|
||||||
ctx: BaseContext,
|
ctx: BaseContext,
|
||||||
state: ComponentPreviewState,
|
state: JDDPageState,
|
||||||
inputs: Record<string, string>
|
inputs: Record<string, unknown>
|
||||||
) => {
|
): Promise<JDDPageState> => {
|
||||||
const component_name = inputs.component;
|
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);
|
const component = registry.get(component_name);
|
||||||
|
if (!component) {
|
||||||
|
throw new Error(`Unknown or disallowed component name: ${component_name}`);
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
...state,
|
components: [
|
||||||
component: component_name,
|
{
|
||||||
component_args:
|
component_name: component_name,
|
||||||
(await component?.getExampleValues(makeJDDContext(ctx))) || {},
|
args: (await component?.getExampleValues(makeJDDContext(ctx))) || {},
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
randomize_args: async (
|
randomize_args: async (
|
||||||
ctx: BaseContext,
|
ctx: BaseContext,
|
||||||
state: ComponentPreviewState,
|
state: JDDPageState,
|
||||||
inputs: Record<string, string>
|
_inputs: Record<string, string>,
|
||||||
) => {
|
component_index_str: string
|
||||||
const component_name = inputs.component;
|
): Promise<JDDPageState> => {
|
||||||
const component = registry.get(component_name);
|
const { component_index, component } = getComponentData(
|
||||||
|
state,
|
||||||
|
["components", component_index_str],
|
||||||
|
registry
|
||||||
|
);
|
||||||
|
|
||||||
|
state.components[component_index].args =
|
||||||
|
(await component?.getExampleValues(makeJDDContext(ctx))) || {};
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
component_args:
|
|
||||||
(await component?.getExampleValues(makeJDDContext(ctx))) || {},
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
add_table_row: async (
|
add_table_row: async (
|
||||||
ctx: BaseContext,
|
ctx: BaseContext,
|
||||||
state: ComponentPreviewState,
|
state: JDDPageState,
|
||||||
_: Record<string, string>,
|
_: Record<string, string>,
|
||||||
arg_path: string[],
|
arg_path: string[],
|
||||||
columns: number,
|
columns: number,
|
||||||
type: "header" | "row" = "row"
|
type: "header" | "row" = "row"
|
||||||
) => {
|
) => {
|
||||||
const jdd_context = makeJDDContext(ctx);
|
const jdd_context = makeJDDContext(ctx);
|
||||||
const component_args = state.component_args;
|
const { component_args, argument } = getComponentData(state, arg_path, registry);
|
||||||
let row;
|
let row;
|
||||||
const component = registry.get(state.component);
|
|
||||||
if (!component) {
|
|
||||||
console.error("Unknown component: ", state.component);
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
||||||
const argument = component.getArgumentAtPath(
|
|
||||||
arg_path,
|
|
||||||
state.component_args
|
|
||||||
) as Table<unknown, unknown> | null;
|
|
||||||
if (!argument) {
|
if (!argument) {
|
||||||
console.error("Unknown component at path", arg_path);
|
console.error("Unknown component at path", arg_path);
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
if (!(argument instanceof Table)) {
|
||||||
|
throw new Error(
|
||||||
|
`Expected argument at path ${arg_path.join(".")} to be of type Table`
|
||||||
|
);
|
||||||
|
}
|
||||||
if (type == "header") {
|
if (type == "header") {
|
||||||
row = {
|
row = {
|
||||||
type: "header",
|
type: "header",
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
header_content: await argument.header_type.getExampleValue(jdd_context),
|
header_content: await argument.header_type.getExampleValue(jdd_context),
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
@ -126,42 +170,28 @@ export const ComponentPreviewActions = <const>{
|
|||||||
row = { type: "row", cells };
|
row = { type: "row", cells };
|
||||||
}
|
}
|
||||||
objectPath.insert(
|
objectPath.insert(
|
||||||
component_args,
|
state,
|
||||||
[...arg_path, "rows"],
|
[...arg_path, "rows"],
|
||||||
row,
|
row,
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
((objectPath.get(component_args, [...arg_path, "rows"]) as unknown[]) || [])
|
((objectPath.get(component_args, [...arg_path, "rows"]) as unknown[]) || [])
|
||||||
.length
|
.length
|
||||||
);
|
);
|
||||||
const result = {
|
return state;
|
||||||
...state,
|
|
||||||
component_args,
|
|
||||||
};
|
|
||||||
return result;
|
|
||||||
},
|
},
|
||||||
add_table_column: async (
|
add_table_column: async (
|
||||||
ctx: BaseContext,
|
ctx: BaseContext,
|
||||||
state: ComponentPreviewState,
|
state: JDDPageState,
|
||||||
_: Record<string, string>,
|
_: Record<string, string>,
|
||||||
arg_path: string[]
|
arg_path: string[]
|
||||||
) => {
|
) => {
|
||||||
const jdd_context = makeJDDContext(ctx);
|
const { argument } = getComponentData(state, arg_path, registry);
|
||||||
const component_args = state.component_args;
|
|
||||||
const component = registry.get(state.component);
|
|
||||||
if (!component) {
|
|
||||||
console.error("Unknown component: ", state.component);
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
const argument = component.getArgumentAtPath(arg_path, state.component_args);
|
|
||||||
if (!argument) {
|
if (!argument) {
|
||||||
console.error("Unknown component at path", arg_path);
|
console.error("Unknown component at path", arg_path);
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
const tableData: TableData<unknown, unknown> = objectPath.get(
|
const tableData: TableData<unknown, unknown> = objectPath.get(state, arg_path);
|
||||||
component_args,
|
|
||||||
arg_path
|
|
||||||
);
|
|
||||||
if (!isTableData(tableData)) {
|
if (!isTableData(tableData)) {
|
||||||
throw new Error("wrong table data");
|
throw new Error("wrong table data");
|
||||||
}
|
}
|
||||||
@ -170,29 +200,22 @@ export const ComponentPreviewActions = <const>{
|
|||||||
const row = tableData.rows[i];
|
const row = tableData.rows[i];
|
||||||
if (isTableRegularRow(row)) {
|
if (isTableRegularRow(row)) {
|
||||||
// eslint-disable-next-line no-await-in-loop
|
// eslint-disable-next-line no-await-in-loop
|
||||||
row.cells.push(await argument.getExampleValue(jdd_context));
|
row.cells.push(await argument.getExampleValue(makeJDDContext(ctx)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
objectPath.set(component_args, arg_path, tableData);
|
objectPath.set(state, arg_path, tableData);
|
||||||
return {
|
return state;
|
||||||
...state,
|
|
||||||
component_args,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
|
|
||||||
remove_table_column: (
|
remove_table_column: (
|
||||||
_ctx: BaseContext,
|
_ctx: BaseContext,
|
||||||
state: ComponentPreviewState,
|
state: JDDPageState,
|
||||||
_: Record<string, string>,
|
_: Record<string, string>,
|
||||||
arg_path: string[],
|
arg_path: string[],
|
||||||
column_index_to_remove: number
|
column_index_to_remove: number
|
||||||
) => {
|
): JDDPageState => {
|
||||||
const component_args = state.component_args;
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
const tableData: TableData<unknown, unknown> = objectPath.get(
|
const tableData: TableData<unknown, unknown> = objectPath.get(state, arg_path);
|
||||||
component_args,
|
|
||||||
arg_path
|
|
||||||
);
|
|
||||||
if (!isTableData(tableData)) {
|
if (!isTableData(tableData)) {
|
||||||
throw new Error("wrong table data");
|
throw new Error("wrong table data");
|
||||||
}
|
}
|
||||||
@ -203,84 +226,135 @@ export const ComponentPreviewActions = <const>{
|
|||||||
row.cells = row.cells.filter((_, i) => i != column_index_to_remove);
|
row.cells = row.cells.filter((_, i) => i != column_index_to_remove);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
objectPath.set(component_args, arg_path, tableData);
|
objectPath.set(state, arg_path, tableData);
|
||||||
return {
|
return state;
|
||||||
...state,
|
|
||||||
component_args,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
|
|
||||||
remove_table_row: (
|
remove_table_row: (
|
||||||
_ctx: BaseContext,
|
_ctx: BaseContext,
|
||||||
state: ComponentPreviewState,
|
state: JDDPageState,
|
||||||
_: Record<string, string>,
|
_: Record<string, string>,
|
||||||
arg_path: string[],
|
arg_path: string[],
|
||||||
row_index: number
|
row_index: number
|
||||||
) => {
|
): JDDPageState => {
|
||||||
const component_args = state.component_args;
|
objectPath.del(state, [...arg_path, "rows", row_index]);
|
||||||
objectPath.del(component_args, [...arg_path, "rows", row_index]);
|
return state;
|
||||||
const result = {
|
|
||||||
...state,
|
|
||||||
component_args,
|
|
||||||
};
|
|
||||||
return result;
|
|
||||||
},
|
},
|
||||||
move_table_column_right: (
|
move_table_column_right: (
|
||||||
_ctx: BaseContext,
|
_ctx: BaseContext,
|
||||||
state: ComponentPreviewState,
|
state: JDDPageState,
|
||||||
_: Record<string, string>,
|
_: Record<string, string>,
|
||||||
arg_path: string[],
|
arg_path: string[],
|
||||||
column_index: number
|
column_index: number
|
||||||
) => {
|
) => {
|
||||||
const component_args = state.component_args;
|
const { component_args } = getComponentData(state, arg_path, registry);
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
||||||
const data = objectPath.get(component_args, arg_path) as TableData<
|
const last_path_element = arg_path.at(-1);
|
||||||
unknown,
|
if (!last_path_element) {
|
||||||
unknown
|
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) {
|
for (const row of data.rows) {
|
||||||
if (row.type == "row") {
|
if (row.type == "row") {
|
||||||
moveElement(row.cells, column_index, column_index + 1);
|
moveElement(row.cells, column_index, column_index + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
objectPath.set(component_args, [...arg_path, "rows"], data.rows);
|
objectPath.set(state, [...arg_path, "rows"], data.rows);
|
||||||
const result = {
|
return state;
|
||||||
...state,
|
|
||||||
component_args,
|
|
||||||
};
|
|
||||||
return result;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
move_table_row_down: (
|
move_table_row_down: (
|
||||||
_ctx: BaseContext,
|
_ctx: BaseContext,
|
||||||
state: ComponentPreviewState,
|
state: JDDPageState,
|
||||||
_: Record<string, string>,
|
_: Record<string, string>,
|
||||||
arg_path: string[],
|
arg_path: string[],
|
||||||
row_index: number
|
row_index: number
|
||||||
) => {
|
) => {
|
||||||
const component_args = state.component_args;
|
const { component_args } = getComponentData(state, arg_path, registry);
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
const last_path_element = arg_path.at(-1);
|
||||||
const data = objectPath.get(component_args, arg_path) as TableData<
|
if (!last_path_element) {
|
||||||
unknown,
|
throw new Error("arg path is empty");
|
||||||
unknown
|
}
|
||||||
>;
|
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);
|
moveElement(data.rows, row_index, row_index + 1);
|
||||||
objectPath.set(component_args, [...arg_path, "rows"], data.rows);
|
objectPath.set(state, [...arg_path, "rows"], data.rows);
|
||||||
const result = {
|
return state;
|
||||||
...state,
|
|
||||||
component_args,
|
|
||||||
};
|
|
||||||
return result;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
change_size: (
|
change_size: (
|
||||||
_ctx: BaseContext,
|
_ctx: BaseContext,
|
||||||
state: ComponentPreviewState,
|
state: JDDPageState,
|
||||||
inputs: Record<string, string>
|
inputs: Record<string, string>
|
||||||
) => {
|
) => {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
...inputs,
|
preview_size: inputs.size,
|
||||||
current_size: inputs.size,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
add_component: async (
|
||||||
|
ctx: BaseContext,
|
||||||
|
state: JDDPageState,
|
||||||
|
inputs: Record<string, string>
|
||||||
|
): Promise<JDDPageState> => {
|
||||||
|
const component_name = inputs.component;
|
||||||
|
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];
|
||||||
|
// prettier-ignore
|
||||||
|
[newComps[component_index], newComps[component_index - 1]] =
|
||||||
|
[newComps[component_index - 1], newComps[component_index]];
|
||||||
|
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];
|
||||||
|
// prettier-ignore
|
||||||
|
[newComps[component_index], newComps[component_index + 1]] =
|
||||||
|
[newComps[component_index + 1], newComps[component_index]];
|
||||||
|
return { ...state, components: newComps };
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
105
src/back/routes/component-preview/jdd-creator.tsx
Normal file
105
src/back/routes/component-preview/jdd-creator.tsx
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
import type { BaseContext } from "koa";
|
||||||
|
import { TempstreamJSX } from "tempstream";
|
||||||
|
import { ComponentPreviewActions } from "./component-preview-actions.js";
|
||||||
|
import type { JDDPageState } from "./jdd-page.js";
|
||||||
|
import JDDPage from "./jdd-page.js";
|
||||||
|
|
||||||
|
import move_row_down_icon from "./table-move-row-down.svg";
|
||||||
|
import move_row_up_icon from "./table-move-row-up.svg";
|
||||||
|
|
||||||
|
export default abstract class JDDCreator extends JDDPage {
|
||||||
|
actions = ComponentPreviewActions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns list of components allowed in JDD Editor instance.
|
||||||
|
* If list is empty it will allow all of the components in registry,
|
||||||
|
* if you overide this function you can decide on what components should
|
||||||
|
* available.
|
||||||
|
*/
|
||||||
|
getAllowedComponents(): string[] {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
getRegistryCompoments() {
|
||||||
|
const all_components = super.getRegistryCompoments();
|
||||||
|
const allowed_components = this.getAllowedComponents();
|
||||||
|
|
||||||
|
if (allowed_components.length > 0) {
|
||||||
|
return Object.fromEntries(
|
||||||
|
Object.entries(all_components).filter(([name]) =>
|
||||||
|
allowed_components.includes(name)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return all_components;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderParameterButtons(state: JDDPageState) {
|
||||||
|
{
|
||||||
|
/*The below button has to be here in order for it to be the default behavior */
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<input type="submit" value="Preview" />
|
||||||
|
<select name="component">
|
||||||
|
{Object.keys(this.getRegistryCompoments()).map((cmp) => (
|
||||||
|
<option value={cmp}>{cmp}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
{this.makeActionButton(state, {
|
||||||
|
action: "add_component",
|
||||||
|
label: "Add component",
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderComponentBlock(
|
||||||
|
ctx: BaseContext,
|
||||||
|
state: JDDPageState,
|
||||||
|
component: {
|
||||||
|
component_name: string;
|
||||||
|
args: Record<string, unknown>;
|
||||||
|
},
|
||||||
|
component_index: number
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<fieldset>
|
||||||
|
<legend>Component - {component.component_name}</legend>
|
||||||
|
{this.makeActionButton(
|
||||||
|
state,
|
||||||
|
{ action: "remove_component", label: "❌" },
|
||||||
|
component_index
|
||||||
|
)}
|
||||||
|
{this.makeActionButton(
|
||||||
|
state,
|
||||||
|
{
|
||||||
|
action: "move_component_up",
|
||||||
|
label: "Move this row up",
|
||||||
|
content: /* HTML */ `<img
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
src="${move_row_up_icon.url}"
|
||||||
|
/>`,
|
||||||
|
},
|
||||||
|
component_index
|
||||||
|
)}
|
||||||
|
{this.makeActionButton(
|
||||||
|
state,
|
||||||
|
{
|
||||||
|
action: "move_component_down",
|
||||||
|
label: "Move this row down",
|
||||||
|
content: /* HTML */ `<img
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
src="${move_row_down_icon.url}"
|
||||||
|
/>`,
|
||||||
|
},
|
||||||
|
component_index
|
||||||
|
)}
|
||||||
|
{super.renderComponentBlock(ctx, state, component, component_index)}
|
||||||
|
</fieldset>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
298
src/back/routes/component-preview/jdd-page.tsx
Normal file
298
src/back/routes/component-preview/jdd-page.tsx
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
import type { Component, RawJDDocument } from "@sealcode/jdd";
|
||||||
|
import { documentContainerFromParsed } from "@sealcode/jdd";
|
||||||
|
import { render, renderEarlyAssets } from "@sealcode/jdd";
|
||||||
|
import { StatefulPage } from "@sealcode/sealgen";
|
||||||
|
import { hasFieldOfType, hasShape, predicates } from "@sealcode/ts-predicates";
|
||||||
|
import type { BaseContext } from "koa";
|
||||||
|
import type { FlatTemplatable, Templatable } from "tempstream";
|
||||||
|
import { tempstream, TempstreamJSX } from "tempstream";
|
||||||
|
import html, { defaultHead } from "../../html.js";
|
||||||
|
import { registry } from "../../jdd-components/components.js";
|
||||||
|
import { makeJDDContext } from "../../jdd-context.js";
|
||||||
|
import { ComponentInput } from "./component-input.js";
|
||||||
|
import { ComponentPreviewActions } from "./component-preview-actions.js";
|
||||||
|
|
||||||
|
export const actionName = "Components";
|
||||||
|
|
||||||
|
export type JDDPageState = {
|
||||||
|
components: RawJDDocument;
|
||||||
|
preview_size?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default abstract class JDDPage extends StatefulPage<
|
||||||
|
JDDPageState,
|
||||||
|
typeof ComponentPreviewActions
|
||||||
|
> {
|
||||||
|
actions = ComponentPreviewActions;
|
||||||
|
|
||||||
|
previewSizes = ["320", "600", "800", "1024", "1300", "1920"];
|
||||||
|
|
||||||
|
getRegistryCompoments() {
|
||||||
|
return registry.getAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getInitialState(ctx: BaseContext) {
|
||||||
|
const [component_name, component] = Object.entries(
|
||||||
|
this.getRegistryCompoments()
|
||||||
|
)[0];
|
||||||
|
const initial_state = {
|
||||||
|
components: [
|
||||||
|
{
|
||||||
|
component_name: component_name,
|
||||||
|
args: await component.getExampleValues(makeJDDContext(ctx)),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
return initial_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapInLayout(
|
||||||
|
ctx: BaseContext,
|
||||||
|
content: Templatable,
|
||||||
|
state: JDDPageState
|
||||||
|
): Templatable {
|
||||||
|
return html(
|
||||||
|
ctx,
|
||||||
|
"Components",
|
||||||
|
content,
|
||||||
|
{
|
||||||
|
morphing: true,
|
||||||
|
preserveScroll: true,
|
||||||
|
autoRefreshCSS: true,
|
||||||
|
navbar: () => ``,
|
||||||
|
},
|
||||||
|
(...args) =>
|
||||||
|
tempstream`${defaultHead(...args)}${renderEarlyAssets(
|
||||||
|
registry,
|
||||||
|
documentContainerFromParsed(state.components),
|
||||||
|
makeJDDContext(ctx)
|
||||||
|
)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async preprocessOverrides(
|
||||||
|
_ctx: BaseContext,
|
||||||
|
state: JDDPageState,
|
||||||
|
overrides: Record<string, unknown>
|
||||||
|
) {
|
||||||
|
const jdd_context = makeJDDContext(_ctx);
|
||||||
|
if (
|
||||||
|
!hasFieldOfType(
|
||||||
|
"components",
|
||||||
|
overrides,
|
||||||
|
predicates.array(
|
||||||
|
predicates.shape({
|
||||||
|
args: predicates.object,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
for (const [component_index, { component_name }] of Object.entries(
|
||||||
|
state.components
|
||||||
|
)) {
|
||||||
|
const component = registry.get(component_name);
|
||||||
|
if (!component) {
|
||||||
|
throw new Error(`Unknown component: ${component_name}`);
|
||||||
|
}
|
||||||
|
const overrides_for_component =
|
||||||
|
overrides.components[parseInt(component_index)];
|
||||||
|
const promises = Object.entries(component.getArguments()).map(
|
||||||
|
async ([arg_name, arg]) => {
|
||||||
|
const value = overrides_for_component.args[arg_name];
|
||||||
|
if (value) {
|
||||||
|
const new_value = await arg.receivedToParsed(jdd_context, value);
|
||||||
|
overrides_for_component.args[arg_name] = new_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
await Promise.all(promises);
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
|
return overrides;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
abstract renderParameterButtons(_state: JDDPageState): FlatTemplatable;
|
||||||
|
|
||||||
|
renderComponentArgs<C extends Component>(
|
||||||
|
ctx: BaseContext,
|
||||||
|
state: JDDPageState,
|
||||||
|
component: C,
|
||||||
|
args: Record<string, unknown>,
|
||||||
|
index: number
|
||||||
|
) {
|
||||||
|
const jdd_context = makeJDDContext(ctx);
|
||||||
|
return (
|
||||||
|
<fieldset class="component-preview-parameters">
|
||||||
|
<legend>Parameters</legend>
|
||||||
|
{Object.entries(component.getArguments()).map(async ([arg_name, arg]) => (
|
||||||
|
<ComponentInput
|
||||||
|
{...{
|
||||||
|
state,
|
||||||
|
arg_path: ["components", index.toString(), "args", arg_name],
|
||||||
|
ctx,
|
||||||
|
arg,
|
||||||
|
value:
|
||||||
|
args[arg_name] === undefined
|
||||||
|
? await arg.getExampleValue(jdd_context)
|
||||||
|
: args[arg_name],
|
||||||
|
onblur: this.rerender(),
|
||||||
|
page: this,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<input type="submit" value="Preview" />
|
||||||
|
</fieldset>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderComponentBlock(
|
||||||
|
ctx: BaseContext,
|
||||||
|
state: JDDPageState,
|
||||||
|
{
|
||||||
|
component_name,
|
||||||
|
args: component_args,
|
||||||
|
}: {
|
||||||
|
component_name: string;
|
||||||
|
args: Record<string, unknown>;
|
||||||
|
},
|
||||||
|
component_index: number
|
||||||
|
) {
|
||||||
|
const component = registry.get(component_name);
|
||||||
|
if (!component) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return this.renderComponentArgs(
|
||||||
|
ctx,
|
||||||
|
state,
|
||||||
|
component,
|
||||||
|
component_args,
|
||||||
|
component_index
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async serializeState(ctx: BaseContext, state: JDDPageState) {
|
||||||
|
const serialized_components = await Promise.all(
|
||||||
|
state.components.map(async ({ component_name, args }) => {
|
||||||
|
const component = registry.get(component_name);
|
||||||
|
const single_result = {
|
||||||
|
component_name,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||||
|
args: component
|
||||||
|
? await component.convertParsedToStorage(
|
||||||
|
makeJDDContext(ctx),
|
||||||
|
args
|
||||||
|
)
|
||||||
|
: {},
|
||||||
|
};
|
||||||
|
return single_result;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const serialized_state = JSON.stringify({ components: serialized_components });
|
||||||
|
return serialized_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
async deserializeState(ctx: BaseContext, state_string: string) {
|
||||||
|
const jdd_context = makeJDDContext(ctx);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
const raw = JSON.parse(state_string);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
|
||||||
|
const components_storage = raw.components;
|
||||||
|
if (!Array.isArray(components_storage)) {
|
||||||
|
throw new Error(
|
||||||
|
"'components' key is not an array, got ${components_storage}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
|
const components_parsed = await Promise.all(
|
||||||
|
components_storage.map(async (entry) => {
|
||||||
|
if (
|
||||||
|
!hasShape(
|
||||||
|
{
|
||||||
|
component_name: predicates.string,
|
||||||
|
args: predicates.object,
|
||||||
|
},
|
||||||
|
entry
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
throw new Error(
|
||||||
|
`Expected components[] items to be objects with 'component_name' and 'args' keys, got ${entry}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const { component_name, args } = entry;
|
||||||
|
const component = registry.get(component_name);
|
||||||
|
if (!component) {
|
||||||
|
throw new Error("Unknown component: ${component_name}");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
component_name,
|
||||||
|
args: await component.convertStorageToParsed(jdd_context, args),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const result = { components: components_parsed };
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
render(ctx: BaseContext, state: JDDPageState) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
class="two-column"
|
||||||
|
id="component-debugger"
|
||||||
|
style="--resizable-column-width: 50vw"
|
||||||
|
data-controller="component-debugger"
|
||||||
|
>
|
||||||
|
<div class="component-arguments">
|
||||||
|
{this.renderParameterButtons(state)}
|
||||||
|
{state.components.map((component, component_index) =>
|
||||||
|
this.renderComponentBlock(ctx, state, component, component_index)
|
||||||
|
)}
|
||||||
|
<code>{this.serializeState(ctx, state)}</code>
|
||||||
|
</div>
|
||||||
|
<div class="resize-gutter" data-component-debugger-target="gutter"></div>
|
||||||
|
<div class="component-preview" data-component-debugger-target="preview">
|
||||||
|
<fieldset>
|
||||||
|
<legend>
|
||||||
|
Preview{" "}
|
||||||
|
<span data-component-debugger-target="component-width"></span>
|
||||||
|
<select
|
||||||
|
name="size"
|
||||||
|
autocomplete="off"
|
||||||
|
class="component-preview-size-select"
|
||||||
|
data-component-debugger-target="size-select"
|
||||||
|
data-action="change->component-debugger#handleWidthDropdown"
|
||||||
|
>
|
||||||
|
{this.previewSizes.map((size) => (
|
||||||
|
<option
|
||||||
|
value={size}
|
||||||
|
selected={size === (state.preview_size || "800")}
|
||||||
|
>
|
||||||
|
{`${size} px`}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
<noscript>
|
||||||
|
{this.makeActionButton(state, "change_size")}
|
||||||
|
</noscript>
|
||||||
|
</legend>
|
||||||
|
{render(
|
||||||
|
registry,
|
||||||
|
documentContainerFromParsed(state.components),
|
||||||
|
makeJDDContext(ctx)
|
||||||
|
)}
|
||||||
|
</fieldset>
|
||||||
|
{
|
||||||
|
/* HTML */ `<script>
|
||||||
|
(function () {
|
||||||
|
const gutter = document.querySelector(".resize-gutter");
|
||||||
|
})();
|
||||||
|
</script>`
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
26
src/back/routes/component-preview/table-move-row-up.svg
Normal file
26
src/back/routes/component-preview/table-move-row-up.svg
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
fill="#000000"
|
||||||
|
width="800"
|
||||||
|
height="800"
|
||||||
|
viewBox="0 0 14 14"
|
||||||
|
role="img"
|
||||||
|
focusable="false"
|
||||||
|
aria-hidden="true"
|
||||||
|
version="1.1"
|
||||||
|
id="svg2"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<path
|
||||||
|
d="M 14,13.474609 C 14,13.76453 13.831094,14 13.621094,14 H 0.378906 C 0.168806,14 0,13.76453 0,13.474609 V 8.6835938 C 0,8.3935724 0.168806,8.1601563 0.378906,8.1601562 h 13.242188 c 0.21,0 0.378906,0.2334162 0.378906,0.5234376 z M 12.941406,12.791016 V 9.3710938 H 9.7265626 v 3.4199222 z m -4.2832028,0 V 9.3710938 H 5.4433594 v 3.4199222 z m -4.2832031,0 V 9.3710938 H 1.158203 v 3.4199222 z"
|
||||||
|
id="path1" />
|
||||||
|
<path
|
||||||
|
fill="green"
|
||||||
|
d="m 2.968353,4.030706 q 0,0.2639283 0.1915,0.4656736 l 0.3882,0.3880946 q 0.1966,0.1964467 0.4709002,0.1964467 0.2795,0 0.4658,-0.1964467 l 1.5217,-1.5159882 v 3.642611 q 0,0.269027 0.194,0.4370814 0.1941,0.1682543 0.4684,0.1682543 h 0.6625 q 0.2743,0 0.4684,-0.1682543 0.1941,-0.1680544 0.1941,-0.4370814 V 3.368486 l 1.5216,1.5159882 q 0.1863,0.1964467 0.4658,0.1964467 0.2794998,0 0.4657998,-0.1964467 l 0.3882,-0.3880946 q 0.1966,-0.1966467 0.1966,-0.4656736 0,-0.274126 -0.1966,-0.470872 L 7.4659532,0.191548 Q 7.2847532,0 7.0001532,0 q -0.2795,0 -0.471,0.191548 L 3.159853,3.559834 q -0.1915,0.201845 -0.1915,0.470872 z"
|
||||||
|
id="path2"
|
||||||
|
style="fill:#0086be;fill-opacity:1" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
@ -1,34 +1,52 @@
|
|||||||
import { documentContainerFromParsed, render, renderEarlyAssets } from "@sealcode/jdd";
|
import { documentContainerFromParsed, renderEarlyAssets } from "@sealcode/jdd";
|
||||||
import { StatefulPage } from "@sealcode/sealgen";
|
|
||||||
import { hasShape, predicates } from "@sealcode/ts-predicates";
|
|
||||||
import type { BaseContext } from "koa";
|
import type { BaseContext } from "koa";
|
||||||
import type { Templatable } from "tempstream";
|
import type { Templatable } from "tempstream";
|
||||||
import { tempstream, TempstreamJSX } from "tempstream";
|
import { tempstream, TempstreamJSX } from "tempstream";
|
||||||
|
import type { Stringifiable } from "tempstream/@types/stringify.js";
|
||||||
import html, { defaultHead } from "../html.js";
|
import html, { defaultHead } from "../html.js";
|
||||||
import { registry } from "../jdd-components/components.js";
|
import { registry } from "../jdd-components/components.js";
|
||||||
import { makeJDDContext } from "../jdd-context.js";
|
import { makeJDDContext } from "../jdd-context.js";
|
||||||
import { ComponentInput } from "./component-preview/component-input.js";
|
|
||||||
import { ComponentPreviewActions } from "./component-preview/component-preview-actions.js";
|
import { ComponentPreviewActions } from "./component-preview/component-preview-actions.js";
|
||||||
|
import type { JDDPageState } from "./component-preview/jdd-page.js";
|
||||||
|
import JDDPage from "./component-preview/jdd-page.js";
|
||||||
|
|
||||||
export const actionName = "Components";
|
export const actionName = "Components";
|
||||||
|
|
||||||
export type ComponentPreviewState = {
|
export default new (class JddcomponentDebuggerPage extends JDDPage {
|
||||||
component: string;
|
renderParameterButtons(state: JDDPageState): Stringifiable {
|
||||||
component_args: Record<string, unknown>;
|
const all_components = super.getRegistryCompoments();
|
||||||
current_size?: string;
|
return (
|
||||||
};
|
<div>
|
||||||
|
<input type="submit" value="Preview" />
|
||||||
export default new (class ComponentsPage extends StatefulPage<
|
<select
|
||||||
ComponentPreviewState,
|
name="component"
|
||||||
typeof ComponentPreviewActions
|
onchange={this.makeActionCallback("change_component")}
|
||||||
> {
|
autocomplete="off"
|
||||||
|
>
|
||||||
|
{Object.entries(all_components).map(([name]) => (
|
||||||
|
<option
|
||||||
|
value={name}
|
||||||
|
selected={name == state.components[0].component_name}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
{this.makeActionButton(state, "randomize_args", "0")}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
actions = ComponentPreviewActions;
|
actions = ComponentPreviewActions;
|
||||||
|
|
||||||
async getInitialState(ctx: BaseContext) {
|
async getInitialState(ctx: BaseContext) {
|
||||||
const [component_name, component] = Object.entries(registry.getAll())[0];
|
const [component_name, component] = Object.entries(registry.getAll())[0];
|
||||||
const initial_state = {
|
const initial_state = {
|
||||||
component: component_name,
|
components: [
|
||||||
component_args: await component.getExampleValues(makeJDDContext(ctx)),
|
{
|
||||||
|
component_name: component_name,
|
||||||
|
args: await component.getExampleValues(makeJDDContext(ctx)),
|
||||||
|
},
|
||||||
|
],
|
||||||
};
|
};
|
||||||
return initial_state;
|
return initial_state;
|
||||||
}
|
}
|
||||||
@ -73,7 +91,7 @@ export default new (class ComponentsPage extends StatefulPage<
|
|||||||
wrapInLayout(
|
wrapInLayout(
|
||||||
ctx: BaseContext,
|
ctx: BaseContext,
|
||||||
content: Templatable,
|
content: Templatable,
|
||||||
state: ComponentPreviewState
|
state: JDDPageState
|
||||||
): Templatable {
|
): Templatable {
|
||||||
return html(
|
return html(
|
||||||
ctx,
|
ctx,
|
||||||
@ -88,147 +106,11 @@ export default new (class ComponentsPage extends StatefulPage<
|
|||||||
(...args) =>
|
(...args) =>
|
||||||
tempstream`${defaultHead(...args)}${renderEarlyAssets(
|
tempstream`${defaultHead(...args)}${renderEarlyAssets(
|
||||||
registry,
|
registry,
|
||||||
documentContainerFromParsed([
|
documentContainerFromParsed(state.components),
|
||||||
{
|
|
||||||
component_name: state.component,
|
|
||||||
args: state.component_args,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
makeJDDContext(ctx)
|
makeJDDContext(ctx)
|
||||||
)}`
|
)}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async preprocessOverrides(
|
|
||||||
ctx: BaseContext,
|
|
||||||
state: ComponentPreviewState,
|
|
||||||
overrides: Record<string, unknown>
|
|
||||||
) {
|
|
||||||
const jdd_context = makeJDDContext(ctx);
|
|
||||||
const component_name = state.component;
|
|
||||||
if (!component_name) {
|
|
||||||
throw new Error("Unspecified component name");
|
|
||||||
}
|
|
||||||
const component = registry.get(component_name);
|
|
||||||
if (!component) {
|
|
||||||
throw new Error(`Unknown component: ${component_name}`);
|
|
||||||
}
|
|
||||||
if (!hasShape({ component_args: predicates.object }, overrides)) {
|
|
||||||
return overrides;
|
|
||||||
}
|
|
||||||
const promises = Object.entries(component.getArguments()).map(
|
|
||||||
async ([arg_name, arg]) => {
|
|
||||||
const value = overrides.component_args[arg_name];
|
|
||||||
if (value) {
|
|
||||||
const new_value = await arg.receivedToParsed(jdd_context, value);
|
|
||||||
overrides.component_args[arg_name] = new_value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
await Promise.all(promises);
|
|
||||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
||||||
return overrides;
|
|
||||||
}
|
|
||||||
|
|
||||||
containerSizes = ["320", "600", "800", "1024", "1300", "1920"];
|
containerSizes = ["320", "600", "800", "1024", "1300", "1920"];
|
||||||
|
|
||||||
render(ctx: BaseContext, state: ComponentPreviewState) {
|
|
||||||
const jdd_context = makeJDDContext(ctx);
|
|
||||||
const all_components = registry.getAll();
|
|
||||||
const component =
|
|
||||||
registry.get(state.component) || Object.values(all_components)[0];
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
class="two-column"
|
|
||||||
id="component-debugger"
|
|
||||||
style="--resizable-column-width: 50vw"
|
|
||||||
data-controller="component-debugger"
|
|
||||||
>
|
|
||||||
<div class="component-arguments">
|
|
||||||
{/*The below button has to be here in order for it to be the default behavior */}
|
|
||||||
<input type="submit" value="Preview" />
|
|
||||||
<select
|
|
||||||
name="component"
|
|
||||||
onchange={this.makeActionCallback("change_component")}
|
|
||||||
autocomplete="off"
|
|
||||||
>
|
|
||||||
{Object.entries(all_components).map(([name]) => (
|
|
||||||
<option value={name} selected={name == state.component}>
|
|
||||||
{name}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
{this.makeActionButton(state, "randomize_args")}
|
|
||||||
<fieldset class="component-preview-parameters">
|
|
||||||
<legend>Parameters</legend>
|
|
||||||
{Object.entries(component.getArguments()).map(
|
|
||||||
async ([arg_name, arg]) => (
|
|
||||||
<ComponentInput
|
|
||||||
{...{
|
|
||||||
state,
|
|
||||||
ctx,
|
|
||||||
arg_path: [arg_name],
|
|
||||||
arg,
|
|
||||||
value:
|
|
||||||
state.component_args[arg_name] === undefined
|
|
||||||
? await arg.getExampleValue(jdd_context)
|
|
||||||
: state.component_args[arg_name],
|
|
||||||
onblur: this.rerender(),
|
|
||||||
page: this,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
<input type="submit" value="Preview" />
|
|
||||||
</fieldset>
|
|
||||||
<code>{this.serializeState(ctx, state)}</code>
|
|
||||||
</div>
|
|
||||||
<div class="resize-gutter" data-component-debugger-target="gutter"></div>
|
|
||||||
<div class="component-preview" data-component-debugger-target="preview">
|
|
||||||
<fieldset>
|
|
||||||
<legend>
|
|
||||||
Preview{" "}
|
|
||||||
<span data-component-debugger-target="component-width"></span>
|
|
||||||
<select
|
|
||||||
name="size"
|
|
||||||
autocomplete="off"
|
|
||||||
class="component-preview-size-select"
|
|
||||||
data-component-debugger-target="size-select"
|
|
||||||
data-action="change->component-debugger#handleWidthDropdown"
|
|
||||||
>
|
|
||||||
{this.containerSizes.map((size) => (
|
|
||||||
<option
|
|
||||||
value={size}
|
|
||||||
selected={size === (state.current_size || "800")}
|
|
||||||
>
|
|
||||||
{`${size} px`}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
<noscript>
|
|
||||||
{this.makeActionButton(state, "change_size")}
|
|
||||||
</noscript>
|
|
||||||
</legend>
|
|
||||||
{render(
|
|
||||||
registry,
|
|
||||||
documentContainerFromParsed([
|
|
||||||
{
|
|
||||||
component_name: state.component,
|
|
||||||
args: state.component_args,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
jdd_context
|
|
||||||
)}
|
|
||||||
</fieldset>
|
|
||||||
{
|
|
||||||
/* HTML */ `<script>
|
|
||||||
(function () {
|
|
||||||
const gutter = document.querySelector(".resize-gutter");
|
|
||||||
})();
|
|
||||||
</script>`
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})();
|
})();
|
||||||
|
10
src/back/routes/jdd-preview.sreact.tsx
Normal file
10
src/back/routes/jdd-preview.sreact.tsx
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import JDDCreator from "./component-preview/jdd-creator.js";
|
||||||
|
|
||||||
|
export const actionName = "JDDPreview";
|
||||||
|
|
||||||
|
export default new (class JDDCreatePreviewPage extends JDDCreator {
|
||||||
|
// uncomment to create whitelist of allowed components
|
||||||
|
// getAllowedComponents() {
|
||||||
|
// return ["nice-box"];
|
||||||
|
// }
|
||||||
|
})();
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"module": "node16",
|
"module": "es2022",
|
||||||
"moduleResolution": "node16",
|
"moduleResolution": "node16",
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"noImplicitThis": true,
|
"noImplicitThis": true,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user