diff --git a/.base_url b/.base_url new file mode 100644 index 0000000..5fc5092 --- /dev/null +++ b/.base_url @@ -0,0 +1 @@ +https://dep.sealco.de diff --git a/package-lock.json b/package-lock.json index 598d0b7..41cd3e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,11 +13,12 @@ "@hotwired/turbo": "^8.0.2", "@koa/router": "^12.0.1", "@sealcode/file-manager": "^1.0.2", - "@sealcode/jdd": "^0.4.10", + "@sealcode/jdd": "^0.5.0", "@sealcode/sealgen": "^0.15.45", "@sealcode/ts-predicates": "^0.6.2", "@types/kill-port": "^2.0.0", "@types/leaflet": "^1.9.8", + "@types/turndown": "^5.0.4", "escape-goat": "^4.0.0", "get-port": "^7.0.0", "js-convert-case": "^4.2.0", @@ -29,7 +30,9 @@ "qs": "^6.12.0", "sealious": "^0.19.9", "stimulus": "^3.2.2", - "tempstream": "^0.3.16" + "tempstream": "^0.3.16", + "throttle-debounce": "^5.0.2", + "turndown": "^7.2.0" }, "devDependencies": { "@playwright/test": "^1.44.1", @@ -38,6 +41,7 @@ "@types/node": "^20.8.4", "@types/object-path": "^0.11.4", "@types/tedious": "^4.0.7", + "@types/throttle-debounce": "^5.0.2", "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "7.4", "@typescript-eslint/parser": "7.4", @@ -731,6 +735,11 @@ "node": ">= 12" } }, + "node_modules/@mixmark-io/domino": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@mixmark-io/domino/-/domino-2.2.0.tgz", + "integrity": "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==" + }, "node_modules/@mongodb-js/saslprep": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.5.tgz", @@ -824,19 +833,24 @@ } }, "node_modules/@sealcode/jdd": { - "version": "0.4.10", - "resolved": "https://registry.npmjs.org/@sealcode/jdd/-/jdd-0.4.10.tgz", - "integrity": "sha512-WAZcE0hMDzs29nhrlhH5k5OQrIPV8zSg44pA5FA1uONpBjg4JSb8Y04gzadP5rTQflV68kOAEjmqJs4MFT4JvA==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@sealcode/jdd/-/jdd-0.5.0.tgz", + "integrity": "sha512-5HkFzV7pHfmLg2Ta9+TocueLJUpQrodJ+n1ymY2RkiAzEuVSCnZr5ZAXpreDVbcz2PxDUq94ygVpOPMtS7pkGQ==", "dependencies": { "@sealcode/file-manager": "^1.0.2", "@sealcode/ts-predicates": "^0.5.3", "escape-goat": "^4.0.0", - "koa-responsive-image-router": "^0.2.19", + "hyphenopoly": "^5.3.0", + "koa-responsive-image-router": "^0.2.29", "locreq": "^3.0.0", "marked": "^12.0.0", "mri": "^1.2.0", - "tempstream": "^0.3.4", + "prettier": "^2.7.1", + "tempstream": "^0.3.18", "uuid": "^9.0.1" + }, + "peerDependencies": { + "sealious": "^0.19.18" } }, "node_modules/@sealcode/jdd/node_modules/@sealcode/ts-predicates": { @@ -1347,6 +1361,18 @@ "@types/node": "*" } }, + "node_modules/@types/throttle-debounce": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/throttle-debounce/-/throttle-debounce-5.0.2.tgz", + "integrity": "sha512-pDzSNulqooSKvSNcksnV72nk8p7gRqN8As71Sp28nov1IgmPKWbOEIwAWvBME5pPTtaXJAvG3O4oc76HlQ4kqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/turndown": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/turndown/-/turndown-5.0.4.tgz", + "integrity": "sha512-28GI33lCCkU4SGH1GvjDhFgOVr+Tym4PXGBIU1buJUa6xQolniPArtUT+kv42RR2N9MsMLInkr904Aq+ESHBJg==" + }, "node_modules/@types/uuid": { "version": "9.0.8", "license": "MIT" @@ -4742,6 +4768,14 @@ "node": ">= 0.8" } }, + "node_modules/hyphenopoly": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/hyphenopoly/-/hyphenopoly-5.3.0.tgz", + "integrity": "sha512-9GajH50TuO+c25VzYUq2luYpyOybpVcDQ4B7fNWy+n3yQ3dSLRFcEC9oJqG96C/rE5Z3zVP8L/K/3ilzBa49PA==", + "engines": { + "node": ">=16" + } + }, "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -8736,8 +8770,9 @@ } }, "node_modules/sealious": { - "version": "0.19.9", - "license": "BSD-2-Clause", + "version": "0.19.21", + "resolved": "https://registry.npmjs.org/sealious/-/sealious-0.19.21.tgz", + "integrity": "sha512-2LVVD30q9X5MhgmpKnGMQ4351I0awr243DzSFy6sk3ZCsfPo63TZ/5vZbBwhK84Lf+BPRbP9pCkz+m2BdG8AHQ==", "dependencies": { "@koa/router": "^12.0.1", "@sealcode/file-manager": "^1.0.1", @@ -9504,8 +9539,9 @@ } }, "node_modules/tempstream": { - "version": "0.3.16", - "license": "ISC", + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/tempstream/-/tempstream-0.3.18.tgz", + "integrity": "sha512-0V/efjocOnjQtBFC6tR8twlI+ygRNgpisOLneVd+Uma9Oic6fo67OqCwEdrIN2DHs3H72St4ACPqHmnlI1YRag==", "dependencies": { "classnames": "^2.5.1" } @@ -9583,6 +9619,14 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/throttle-debounce": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz", + "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==", + "engines": { + "node": ">=12.22" + } + }, "node_modules/tiny-glob": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", @@ -9864,6 +9908,14 @@ "node": "*" } }, + "node_modules/turndown": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/turndown/-/turndown-7.2.0.tgz", + "integrity": "sha512-eCZGBN4nNNqM9Owkv9HAtWRYfLA4h909E/WGAWWBpmB275ehNhZyk87/Tpvjbp0jjNl9XwCsbe6bm6CqFsgD+A==", + "dependencies": { + "@mixmark-io/domino": "^2.2.0" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index 9366715..95e5cfe 100644 --- a/package.json +++ b/package.json @@ -44,11 +44,12 @@ "@hotwired/turbo": "^8.0.2", "@koa/router": "^12.0.1", "@sealcode/file-manager": "^1.0.2", - "@sealcode/jdd": "^0.4.10", + "@sealcode/jdd": "^0.5.0", "@sealcode/sealgen": "^0.15.45", "@sealcode/ts-predicates": "^0.6.2", "@types/kill-port": "^2.0.0", "@types/leaflet": "^1.9.8", + "@types/turndown": "^5.0.4", "escape-goat": "^4.0.0", "get-port": "^7.0.0", "js-convert-case": "^4.2.0", @@ -60,7 +61,9 @@ "qs": "^6.12.0", "sealious": "^0.19.9", "stimulus": "^3.2.2", - "tempstream": "^0.3.16" + "tempstream": "^0.3.16", + "throttle-debounce": "^5.0.2", + "turndown": "^7.2.0" }, "devDependencies": { "@playwright/test": "^1.44.1", @@ -69,6 +72,7 @@ "@types/node": "^20.8.4", "@types/object-path": "^0.11.4", "@types/tedious": "^4.0.7", + "@types/throttle-debounce": "^5.0.2", "@types/uuid": "^9.0.8", "@typescript-eslint/eslint-plugin": "7.4", "@typescript-eslint/parser": "7.4", diff --git a/secrets.json b/secrets.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/secrets.json @@ -0,0 +1 @@ +{} diff --git a/src/back/html.ts b/src/back/html.ts index 1c82468..ad4fd1e 100644 --- a/src/back/html.ts +++ b/src/back/html.ts @@ -33,6 +33,7 @@ export type HTMLOptions = { autoRefreshCSS?: boolean; disableCopyEvent?: boolean; language?: string; + bodyClasses?: string[]; }; export default function html( diff --git a/src/back/jdd-components/autoscrolling-images/autoscrolling-images.jdd.tsx b/src/back/jdd-components/autoscrolling-images/autoscrolling-images.jdd.tsx index 70c8331..72fbe66 100644 --- a/src/back/jdd-components/autoscrolling-images/autoscrolling-images.jdd.tsx +++ b/src/back/jdd-components/autoscrolling-images/autoscrolling-images.jdd.tsx @@ -1,9 +1,5 @@ -import type { FlatTemplatable } from "tempstream"; import { TempstreamJSX } from "tempstream"; -import type { - ExtractStructuredComponentArgumentsParsed, - JDDContext, -} from "@sealcode/jdd"; +import type { ComponentToHTMLArgs } from "@sealcode/jdd"; import { Component, ComponentArguments } from "@sealcode/jdd"; import arrow from "./autoscrolling-images-arrow.svg"; @@ -25,15 +21,10 @@ export class AutoscrollingImages extends Component { return component_arguments; } - toHTML( - { - title, - interval, - imagesPerPage, - images, - }: ExtractStructuredComponentArgumentsParsed, - { render_image }: JDDContext - ): FlatTemplatable { + toHTML({ + args: { title, interval, imagesPerPage, images }, + jdd_context: { render_image }, + }: ComponentToHTMLArgs) { const imageNumberPerPage = parseInt(imagesPerPage); const parsedImagesArray = []; diff --git a/src/back/jdd-components/dynamic-grid/dynamic-grid.jdd.tsx b/src/back/jdd-components/dynamic-grid/dynamic-grid.jdd.tsx index 12564aa..48e7667 100644 --- a/src/back/jdd-components/dynamic-grid/dynamic-grid.jdd.tsx +++ b/src/back/jdd-components/dynamic-grid/dynamic-grid.jdd.tsx @@ -1,10 +1,6 @@ import type { FlatTemplatable } from "tempstream"; import { TempstreamJSX } from "tempstream"; -import type { - ExtractParsed, - ExtractStructuredComponentArgumentsParsed, - JDDContext, -} from "@sealcode/jdd"; +import type { ComponentToHTMLArgs, ExtractParsed, JDDContext } from "@sealcode/jdd"; import { Component, ComponentArguments } from "@sealcode/jdd"; type ExtractArray = T extends Array ? X : never; @@ -123,13 +119,10 @@ export class DynamicGrid extends Component { ); } - toHTML( - { - heading, - tabs, - }: ExtractStructuredComponentArgumentsParsed, - jdd_context: JDDContext - ): FlatTemplatable { + toHTML({ + args: { heading, tabs }, + jdd_context, + }: ComponentToHTMLArgs): FlatTemplatable { const { value: id } = generate_id.next(); return (
diff --git a/src/back/jdd-components/faq-component/faq-component.jdd.tsx b/src/back/jdd-components/faq-component/faq-component.jdd.tsx index 7bb0629..52dd37a 100644 --- a/src/back/jdd-components/faq-component/faq-component.jdd.tsx +++ b/src/back/jdd-components/faq-component/faq-component.jdd.tsx @@ -1,9 +1,6 @@ import type { FlatTemplatable } from "tempstream"; import { TempstreamJSX } from "tempstream"; -import type { - ExtractStructuredComponentArgumentsParsed, - JDDContext, -} from "@sealcode/jdd"; +import type { ComponentToHTMLArgs } from "@sealcode/jdd"; import { Component, ComponentArguments } from "@sealcode/jdd"; const component_arguments = { @@ -29,15 +26,10 @@ export class FaqComponent extends Component { return component_arguments; } - toHTML( - { - title, - content, - faq, - button, - }: ExtractStructuredComponentArgumentsParsed, - { render_markdown }: JDDContext - ): FlatTemplatable { + toHTML({ + args: { title, content, faq, button }, + jdd_context: { render_markdown, language }, + }: ComponentToHTMLArgs): FlatTemplatable { const buttonText = button.buttonText.toUpperCase(); return ( @@ -46,7 +38,7 @@ export class FaqComponent extends Component {
{title}
- {render_markdown(content.text)}{" "} + {render_markdown(language, content.text)}{" "} {content.number}
@@ -56,7 +48,7 @@ export class FaqComponent extends Component {
{element.question}
-

{render_markdown(element.answer)}

+

{render_markdown(language, element.answer)}

))} diff --git a/src/back/jdd-components/header-with-image/header-with-image.jdd.tsx b/src/back/jdd-components/header-with-image/header-with-image.jdd.tsx index 6bd88f0..8c8916b 100644 --- a/src/back/jdd-components/header-with-image/header-with-image.jdd.tsx +++ b/src/back/jdd-components/header-with-image/header-with-image.jdd.tsx @@ -1,9 +1,6 @@ import type { FlatTemplatable } from "tempstream"; import { TempstreamJSX } from "tempstream"; -import type { - ExtractStructuredComponentArgumentsParsed, - JDDContext, -} from "@sealcode/jdd"; +import type { ComponentToHTMLArgs } from "@sealcode/jdd"; import { Component, ComponentArguments } from "@sealcode/jdd"; const component_arguments = { @@ -24,15 +21,10 @@ export class HeaderWithImage extends Component { return component_arguments; } - toHTML( - { - title, - content, - image_with_alt, - button, - }: ExtractStructuredComponentArgumentsParsed, - { render_markdown, render_image }: JDDContext - ): FlatTemplatable { + toHTML({ + args: { title, content, image_with_alt, button }, + jdd_context: { render_markdown, render_image, language }, + }: ComponentToHTMLArgs): FlatTemplatable { const buttonText = button.text.toUpperCase(); const titleUpperCase = title.toUpperCase(); @@ -49,7 +41,7 @@ export class HeaderWithImage extends Component {

{titleUpperCase}

-
{render_markdown(content)}
+
{render_markdown(language, content)}
{button.text === "" ? null : (
diff --git a/src/back/jdd-components/image-demo/image-demo.jdd.tsx b/src/back/jdd-components/image-demo/image-demo.jdd.tsx index 38d5862..9eaddf1 100644 --- a/src/back/jdd-components/image-demo/image-demo.jdd.tsx +++ b/src/back/jdd-components/image-demo/image-demo.jdd.tsx @@ -1,9 +1,6 @@ import type { FlatTemplatable } from "tempstream"; import { TempstreamJSX } from "tempstream"; -import type { - ExtractStructuredComponentArgumentsParsed, - JDDContext, -} from "@sealcode/jdd"; +import type { ComponentToHTMLArgs } from "@sealcode/jdd"; import { Component, ComponentArguments } from "@sealcode/jdd"; const component_arguments = { @@ -19,13 +16,10 @@ export class ImageDemo extends Component { return component_arguments; } - toHTML( - { - image_with_alt, - multiple_images, - }: ExtractStructuredComponentArgumentsParsed, - { render_image }: JDDContext - ): FlatTemplatable { + toHTML({ + args: { image_with_alt, multiple_images }, + jdd_context: { render_image }, + }: ComponentToHTMLArgs): FlatTemplatable { return (

Image with alt text

diff --git a/src/back/jdd-components/map-with-pins/map-with-pins.jdd.tsx b/src/back/jdd-components/map-with-pins/map-with-pins.jdd.tsx index 7894799..034046c 100644 --- a/src/back/jdd-components/map-with-pins/map-with-pins.jdd.tsx +++ b/src/back/jdd-components/map-with-pins/map-with-pins.jdd.tsx @@ -1,6 +1,5 @@ -import type { FlatTemplatable } from "tempstream"; import { TempstreamJSX } from "tempstream"; -import type { ExtractStructuredComponentArgumentsParsed } from "@sealcode/jdd"; +import type { ComponentToHTMLArgs } from "@sealcode/jdd"; import { Component, ComponentArguments } from "@sealcode/jdd"; const coordinates = new ComponentArguments.ShortText(); @@ -49,11 +48,7 @@ export class MapWithPins extends Component { ]; } - toHTML({ - pins, - }: ExtractStructuredComponentArgumentsParsed< - typeof component_arguments - >): FlatTemplatable { + toHTML({ args: { pins } }: ComponentToHTMLArgs) { return (
{ return component_arguments; } - async toHTML( - { - title, - content, - images, - }: ExtractStructuredComponentArgumentsParsed, - { render_markdown, render_image }: JDDContext - ): Promise { + async toHTML({ + args: { title, content, images }, + classes, + jdd_context: { render_markdown, render_image, language }, + }: ComponentToHTMLArgs): Promise { return ( -
+

{title}

-
{render_markdown(content)}
+
{render_markdown(language, content)}
{images.map((image) => render_image(image.image, { container: { diff --git a/src/back/jdd-components/table/table.jdd.tsx b/src/back/jdd-components/table/table.jdd.tsx index 1e7c3a3..ab0b41a 100644 --- a/src/back/jdd-components/table/table.jdd.tsx +++ b/src/back/jdd-components/table/table.jdd.tsx @@ -1,6 +1,5 @@ -import type { FlatTemplatable } from "tempstream"; import { TempstreamJSX } from "tempstream"; -import type { ExtractStructuredComponentArgumentsParsed } from "@sealcode/jdd"; +import type { ComponentToHTMLArgs } from "@sealcode/jdd"; import { Component, ComponentArguments, isTableHeader } from "@sealcode/jdd"; const component_arguments = { @@ -23,11 +22,7 @@ export class Table extends Component { return component_arguments; } - toHTML({ - table, - }: ExtractStructuredComponentArgumentsParsed< - typeof component_arguments - >): FlatTemplatable { + toHTML({ args: { table } }: ComponentToHTMLArgs) { return (
diff --git a/src/back/jdd-context.ts b/src/back/jdd-context.ts index 750b338..432afb2 100644 --- a/src/back/jdd-context.ts +++ b/src/back/jdd-context.ts @@ -7,6 +7,7 @@ import { imageRouter } from "./image-router.js"; export function makeJDDContext(ctx: BaseContext): JDDContext { return { + language: "pl", ...makeSimpleJDDContext(TheFileManager), render_image: async (image: string | FilePointer | null, args) => { if (!image) { diff --git a/src/back/routes/component-preview/autogrow-textarea.stimulus.ts b/src/back/routes/component-preview/autogrow-textarea.stimulus.ts new file mode 100644 index 0000000..c0ace01 --- /dev/null +++ b/src/back/routes/component-preview/autogrow-textarea.stimulus.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unsafe-assignment */ +import { Controller } from "stimulus"; + +export default class AutogrowTextarea extends Controller { + connect() { + this.autogrow(); + } + + autogrow() { + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access + (this.element.parentNode as any).dataset.replicatedValue = this.element.value; + } +} diff --git a/src/back/routes/component-preview/component-debugger.stimulus.ts b/src/back/routes/component-preview/component-debugger.stimulus.ts index 8597496..fafe78c 100644 --- a/src/back/routes/component-preview/component-debugger.stimulus.ts +++ b/src/back/routes/component-preview/component-debugger.stimulus.ts @@ -1,6 +1,16 @@ +/* eslint-disable @typescript-eslint/consistent-type-assertions */ +/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */ import { Controller } from "stimulus"; export default class ComponentDebugger extends Controller { + declare gutterTarget: HTMLDivElement; + declare checkboxTarget: HTMLInputElement; + declare checkboxTargets: HTMLInputElement[]; + declare previewTarget: HTMLDivElement; + declare componentBlockTargets: HTMLDivElement[]; + declare componentBlockTargetDisconnected: (e: HTMLDivElement) => void; + static targets = ["gutter", "componentBlock", "checkbox", "preview"]; + id: string; main_form: HTMLFormElement; is_resizing = false; @@ -26,7 +36,7 @@ export default class ComponentDebugger extends Controller { document.addEventListener("turbo:render", () => this.update_width_display()); // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - const gutter = this.targets.find("gutter") as HTMLDivElement; + const gutter = this.gutterTarget; gutter.addEventListener("mousedown", (e) => { this.is_resizing = true; this.origin_x = e.clientX; @@ -74,4 +84,82 @@ export default class ComponentDebugger extends Controller { const value = dropdown.value; this.setPreviewWidth(parseInt(value)); } + + componentBlockTargetConnected(block_element: HTMLDivElement) { + const index = parseInt(block_element.getAttribute("data-component-index")); + block_element.addEventListener("focusin", () => { + this.scrollToComponentPreview(index); + }); + } + + previewTargetConnected(preview_element: HTMLDivElement) { + preview_element.addEventListener("click", ({ target }) => { + if (!(target instanceof HTMLElement)) { + return; + } + const closest = target.closest(".jdd-component"); + if (!closest) { + return; + } + const index = parseInt( + Array.from(closest.classList) + .find((c) => c.startsWith("component-number-")) + ?.replace("component-number-", "") + ); + if (isNaN(index)) { + return; + } + this.focusComponentBlock(index); + }); + } + + focusComponentBlock(index: number) { + const block = this.componentBlockTargets[index]; + if (!block) { + return; + } + this.checkboxTargets[index].checked = true; + block.scrollIntoView({ behavior: "smooth" }); + ( + block.querySelector(".component-preview-parameters input") as HTMLInputElement + )?.focus(); + } + + getIndex(block_element: HTMLDivElement) { + const index = parseInt(block_element.getAttribute("data-component-index")); + return index; + } + + labelClicked(element: MouseEvent) { + const block_element = (element.target as HTMLDivElement).closest( + `[data-component-debugger-target="componentBlock"]` + ) as HTMLDivElement; + const index = this.getIndex(block_element); + if (!this.checkboxTargets?.[index].checked) { + this.scrollToComponentPreview(index); + } + } + + getPreviewElementForComponentIndex(index: number) { + const element = this.element.querySelector( + `.component-number-${index}` + ) as HTMLDialogElement; + return element; + } + + scrollToComponentPreview(index: number) { + const element = this.getPreviewElementForComponentIndex(index); + if (!element) { + return; + } + const preview_element = this.element.querySelector(".component-preview"); + if (element.clientHeight > preview_element.clientHeight) { + preview_element.scrollTop = element.offsetTop - 44; + } else { + preview_element.scrollTop = + element.offsetTop - + (preview_element.clientHeight - element.clientHeight) / 2 - + 44; + } + } } diff --git a/src/back/routes/component-preview/component-input-image.tsx b/src/back/routes/component-preview/component-input-image.tsx index e1cf3d3..081ccdf 100644 --- a/src/back/routes/component-preview/component-input-image.tsx +++ b/src/back/routes/component-preview/component-input-image.tsx @@ -14,6 +14,8 @@ export function ComponentInputImage({ arg, value, ctx, + page, + state, }: { state: State; arg_path: string[]; @@ -30,13 +32,16 @@ export function ComponentInputImage({ data-controller="input-image-preview" > {arg_path.at(-1) || ""} - {value && - jdd_context.render_image(value, { - container: { width: 40, height: 40, objectFit: "cover" }, - crop: { width: 40, height: 40 }, - style: "height: 40px; width: 40px;", - alt: "", - })} +
+ {value && + jdd_context.render_image(value, { + container: { width: 40, height: 40, objectFit: "cover" }, + crop: { width: 40, height: 40 }, + style: "height: 40px; width: 40px;", + alt: "", + })} +
+ ({ autocomplete="off" /> + {page.makeActionButton( + state, + { + action: "remove_file", + label: "❌", + }, + arg_path + )} ); } diff --git a/src/back/routes/component-preview/component-input-single-reference.tsx b/src/back/routes/component-preview/component-input-single-reference.tsx new file mode 100644 index 0000000..93aaf7f --- /dev/null +++ b/src/back/routes/component-preview/component-input-single-reference.tsx @@ -0,0 +1,39 @@ +import type { BaseContext } from "koa"; +import type { SingleReference } from "@sealcode/jdd"; +import { TempstreamJSX } from "tempstream"; +import { makeJDDContext } from "../../jdd-context.js"; +import { printArgPath } from "./print-arg-path.js"; + +export async function ComponentInputSingleReference< + State, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + T extends SingleReference +>({ + ctx, + arg_path, + arg, + value, + onchange, +}: { + ctx: BaseContext; + state: State; + arg_path: string[]; + arg: T; + value: string; + onchange?: string; +}): Promise { + return ( +
+ +
+ ); +} diff --git a/src/back/routes/component-preview/component-input-table.tsx b/src/back/routes/component-preview/component-input-table.tsx index 11e3221..3b218c8 100644 --- a/src/back/routes/component-preview/component-input-table.tsx +++ b/src/back/routes/component-preview/component-input-table.tsx @@ -3,6 +3,7 @@ import { isTableHeader } from "@sealcode/jdd"; import type { StatefulPage } from "@sealcode/sealgen"; import type { BaseContext } from "koa"; import { TempstreamJSX } from "tempstream"; +import { makeJDDContext } from "../../jdd-context.js"; import { ComponentInput } from "./component-input.js"; import type { ComponentPreviewActions } from "./component-preview-actions.js"; import type { JDDPageState } from "./jdd-page.js"; @@ -34,7 +35,7 @@ export async function ComponentInputTable< page: StatefulPage; }): Promise { if (!value) { - value = arg.getEmptyValue(); + value = await arg.getEmptyValue(makeJDDContext(ctx)); } return ( diff --git a/src/back/routes/component-preview/component-input.tsx b/src/back/routes/component-preview/component-input.tsx index 6e1b167..354c738 100644 --- a/src/back/routes/component-preview/component-input.tsx +++ b/src/back/routes/component-preview/component-input.tsx @@ -1,6 +1,7 @@ import { printArgPath } from "./print-arg-path.js"; import type { BaseContext } from "koa"; import type { ComponentArgument, TableData } from "@sealcode/jdd"; +import { SingleReference } 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"; @@ -14,11 +15,13 @@ import { ComponentInputTable } from "./component-input-table.js"; import { TempstreamJSX } from "tempstream"; import type { FilePointer } from "@sealcode/file-manager"; import { is, predicates } from "@sealcode/ts-predicates"; +import { makeJDDContext } from "../../jdd-context.js"; +import { ComponentInputSingleReference } from "./component-input-single-reference.js"; export const actionName = "Components"; const absoluteUrlPattern = "http(s?)(://)((www.)?)(([^.]+).)?([a-zA-z0-9-_]+)"; -export function ComponentInput({ +export async function ComponentInput({ ctx, state, arg_path, @@ -32,9 +35,9 @@ export function ComponentInput({ arg: ComponentArgument; value: T; page: StatefulPage; -}): Readable | Promise { +}): Promise { if (value === undefined) { - value = arg.getEmptyValue(); + value = await arg.getEmptyValue(makeJDDContext(ctx)); } if (arg instanceof List) { // eslint-disable-next-line @typescript-eslint/consistent-type-assertions @@ -66,6 +69,18 @@ export function ComponentInput({ }); } + if (arg instanceof SingleReference) { + return ComponentInputSingleReference({ + ctx, + state, + arg_path, + arg, + // eslint-disable-next-line @typescript-eslint/consistent-type-assertions + value: value as string, + onchange: page.rerender(), + }); + } + if (arg instanceof Enum) { return ComponentInputEnum({ state, @@ -106,13 +121,21 @@ export function ComponentInput({