Update JDD and adjust the components API
This commit is contained in:
parent
19d04ff48b
commit
106fcc86c8
74
package-lock.json
generated
74
package-lock.json
generated
@ -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",
|
||||
|
@ -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",
|
||||
|
1
secrets.json
Normal file
1
secrets.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
@ -33,6 +33,7 @@ export type HTMLOptions = {
|
||||
autoRefreshCSS?: boolean;
|
||||
disableCopyEvent?: boolean;
|
||||
language?: string;
|
||||
bodyClasses?: string[];
|
||||
};
|
||||
|
||||
export default function html(
|
||||
|
@ -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<typeof component_arguments> {
|
||||
return component_arguments;
|
||||
}
|
||||
|
||||
toHTML(
|
||||
{
|
||||
title,
|
||||
interval,
|
||||
imagesPerPage,
|
||||
images,
|
||||
}: ExtractStructuredComponentArgumentsParsed<typeof component_arguments>,
|
||||
{ render_image }: JDDContext
|
||||
): FlatTemplatable {
|
||||
toHTML({
|
||||
args: { title, interval, imagesPerPage, images },
|
||||
jdd_context: { render_image },
|
||||
}: ComponentToHTMLArgs<typeof component_arguments>) {
|
||||
const imageNumberPerPage = parseInt(imagesPerPage);
|
||||
const parsedImagesArray = [];
|
||||
|
||||
|
@ -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> = T extends Array<infer X> ? X : never;
|
||||
@ -123,13 +119,10 @@ export class DynamicGrid extends Component<typeof component_arguments> {
|
||||
);
|
||||
}
|
||||
|
||||
toHTML(
|
||||
{
|
||||
heading,
|
||||
tabs,
|
||||
}: ExtractStructuredComponentArgumentsParsed<typeof component_arguments>,
|
||||
jdd_context: JDDContext
|
||||
): FlatTemplatable {
|
||||
toHTML({
|
||||
args: { heading, tabs },
|
||||
jdd_context,
|
||||
}: ComponentToHTMLArgs<typeof component_arguments>): FlatTemplatable {
|
||||
const { value: id } = generate_id.next();
|
||||
return (
|
||||
<div class="dynamic-grid-component">
|
||||
|
@ -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<typeof component_arguments> {
|
||||
return component_arguments;
|
||||
}
|
||||
|
||||
toHTML(
|
||||
{
|
||||
title,
|
||||
content,
|
||||
faq,
|
||||
button,
|
||||
}: ExtractStructuredComponentArgumentsParsed<typeof component_arguments>,
|
||||
{ render_markdown }: JDDContext
|
||||
): FlatTemplatable {
|
||||
toHTML({
|
||||
args: { title, content, faq, button },
|
||||
jdd_context: { render_markdown, language },
|
||||
}: ComponentToHTMLArgs<typeof component_arguments>): FlatTemplatable {
|
||||
const buttonText = button.buttonText.toUpperCase();
|
||||
|
||||
return (
|
||||
@ -46,7 +38,7 @@ export class FaqComponent extends Component<typeof component_arguments> {
|
||||
<div class="title-container">
|
||||
<div class="title">{title} </div>
|
||||
<div class="content">
|
||||
{render_markdown(content.text)}{" "}
|
||||
{render_markdown(language, content.text)}{" "}
|
||||
<strong>{content.number}</strong>
|
||||
</div>
|
||||
</div>
|
||||
@ -56,7 +48,7 @@ export class FaqComponent extends Component<typeof component_arguments> {
|
||||
<details class="question-container" open={index == 0}>
|
||||
<summary class="question">{element.question}</summary>
|
||||
<div class="answer">
|
||||
<p>{render_markdown(element.answer)}</p>
|
||||
<p>{render_markdown(language, element.answer)}</p>
|
||||
</div>
|
||||
</details>
|
||||
))}
|
||||
|
@ -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<typeof component_arguments> {
|
||||
return component_arguments;
|
||||
}
|
||||
|
||||
toHTML(
|
||||
{
|
||||
title,
|
||||
content,
|
||||
image_with_alt,
|
||||
button,
|
||||
}: ExtractStructuredComponentArgumentsParsed<typeof component_arguments>,
|
||||
{ render_markdown, render_image }: JDDContext
|
||||
): FlatTemplatable {
|
||||
toHTML({
|
||||
args: { title, content, image_with_alt, button },
|
||||
jdd_context: { render_markdown, render_image, language },
|
||||
}: ComponentToHTMLArgs<typeof component_arguments>): FlatTemplatable {
|
||||
const buttonText = button.text.toUpperCase();
|
||||
const titleUpperCase = title.toUpperCase();
|
||||
|
||||
@ -49,7 +41,7 @@ export class HeaderWithImage extends Component<typeof component_arguments> {
|
||||
<div class="title-wrapper">
|
||||
<h2 class="title">{titleUpperCase}</h2>
|
||||
</div>
|
||||
<div class="content">{render_markdown(content)}</div>
|
||||
<div class="content">{render_markdown(language, content)}</div>
|
||||
{button.text === "" ? null : (
|
||||
<div class="button-container">
|
||||
<a class="button" href={button.link}>
|
||||
|
@ -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<typeof component_arguments> {
|
||||
return component_arguments;
|
||||
}
|
||||
|
||||
toHTML(
|
||||
{
|
||||
image_with_alt,
|
||||
multiple_images,
|
||||
}: ExtractStructuredComponentArgumentsParsed<typeof component_arguments>,
|
||||
{ render_image }: JDDContext
|
||||
): FlatTemplatable {
|
||||
toHTML({
|
||||
args: { image_with_alt, multiple_images },
|
||||
jdd_context: { render_image },
|
||||
}: ComponentToHTMLArgs<typeof component_arguments>): FlatTemplatable {
|
||||
return (
|
||||
<div class="image-demo">
|
||||
<h2>Image with alt text</h2>
|
||||
|
@ -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<typeof component_arguments> {
|
||||
];
|
||||
}
|
||||
|
||||
toHTML({
|
||||
pins,
|
||||
}: ExtractStructuredComponentArgumentsParsed<
|
||||
typeof component_arguments
|
||||
>): FlatTemplatable {
|
||||
toHTML({ args: { pins } }: ComponentToHTMLArgs<typeof component_arguments>) {
|
||||
return (
|
||||
<div
|
||||
class="map-with-pins"
|
||||
|
0
src/back/jdd-components/map-with-pins/newFile.tsx
Normal file
0
src/back/jdd-components/map-with-pins/newFile.tsx
Normal file
@ -1,8 +1,5 @@
|
||||
import { TempstreamJSX } from "tempstream";
|
||||
import type {
|
||||
ExtractStructuredComponentArgumentsParsed,
|
||||
JDDContext,
|
||||
} from "@sealcode/jdd";
|
||||
import type { ComponentToHTMLArgs } from "@sealcode/jdd";
|
||||
import { Component, ComponentArguments } from "@sealcode/jdd";
|
||||
import type { Readable } from "stream";
|
||||
|
||||
@ -22,18 +19,15 @@ export class NiceBox extends Component<typeof component_arguments> {
|
||||
return component_arguments;
|
||||
}
|
||||
|
||||
async toHTML(
|
||||
{
|
||||
title,
|
||||
content,
|
||||
images,
|
||||
}: ExtractStructuredComponentArgumentsParsed<typeof component_arguments>,
|
||||
{ render_markdown, render_image }: JDDContext
|
||||
): Promise<Readable> {
|
||||
async toHTML({
|
||||
args: { title, content, images },
|
||||
classes,
|
||||
jdd_context: { render_markdown, render_image, language },
|
||||
}: ComponentToHTMLArgs<typeof component_arguments>): Promise<Readable> {
|
||||
return (
|
||||
<div class="nice-box">
|
||||
<div class={["nice-box", ...classes]}>
|
||||
<h2>{title}</h2>
|
||||
<div>{render_markdown(content)}</div>
|
||||
<div>{render_markdown(language, content)}</div>
|
||||
{images.map((image) =>
|
||||
render_image(image.image, {
|
||||
container: {
|
||||
|
@ -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<typeof component_arguments> {
|
||||
return component_arguments;
|
||||
}
|
||||
|
||||
toHTML({
|
||||
table,
|
||||
}: ExtractStructuredComponentArgumentsParsed<
|
||||
typeof component_arguments
|
||||
>): FlatTemplatable {
|
||||
toHTML({ args: { table } }: ComponentToHTMLArgs<typeof component_arguments>) {
|
||||
return (
|
||||
<div class="table">
|
||||
<table>
|
||||
|
@ -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) {
|
||||
|
@ -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<HTMLTextAreaElement> {
|
||||
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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,8 @@ export function ComponentInputImage<State extends JDDPageState>({
|
||||
arg,
|
||||
value,
|
||||
ctx,
|
||||
page,
|
||||
state,
|
||||
}: {
|
||||
state: State;
|
||||
arg_path: string[];
|
||||
@ -30,6 +32,7 @@ export function ComponentInputImage<State extends JDDPageState>({
|
||||
data-controller="input-image-preview"
|
||||
>
|
||||
{arg_path.at(-1) || ""}
|
||||
<div class="image-preview-container">
|
||||
{value &&
|
||||
jdd_context.render_image(value, {
|
||||
container: { width: 40, height: 40, objectFit: "cover" },
|
||||
@ -37,6 +40,8 @@ export function ComponentInputImage<State extends JDDPageState>({
|
||||
style: "height: 40px; width: 40px;",
|
||||
alt: "",
|
||||
})}
|
||||
</div>
|
||||
|
||||
<input
|
||||
type="file"
|
||||
name={`$${printArgPath(arg_path)}.new`}
|
||||
@ -54,6 +59,14 @@ export function ComponentInputImage<State extends JDDPageState>({
|
||||
autocomplete="off"
|
||||
/>
|
||||
</div>
|
||||
{page.makeActionButton(
|
||||
state,
|
||||
{
|
||||
action: "remove_file",
|
||||
label: "❌",
|
||||
},
|
||||
arg_path
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -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<any>
|
||||
>({
|
||||
ctx,
|
||||
arg_path,
|
||||
arg,
|
||||
value,
|
||||
onchange,
|
||||
}: {
|
||||
ctx: BaseContext;
|
||||
state: State;
|
||||
arg_path: string[];
|
||||
arg: T;
|
||||
value: string;
|
||||
onchange?: string;
|
||||
}): Promise<import("stream").Readable> {
|
||||
return (
|
||||
<div>
|
||||
<label>
|
||||
{arg_path.at(-1) || ""}
|
||||
<select name={`$${printArgPath(arg_path)}`} onchange={onchange}>
|
||||
{(await arg.getValues(makeJDDContext(ctx))).map((v) => (
|
||||
<option value={v.value} selected={value == v.value}>
|
||||
{v.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -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<JDDPageState, typeof ComponentPreviewActions>;
|
||||
}): Promise<import("stream").Readable> {
|
||||
if (!value) {
|
||||
value = arg.getEmptyValue();
|
||||
value = await arg.getEmptyValue(makeJDDContext(ctx));
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -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<State extends JDDPageState, T>({
|
||||
export async function ComponentInput<State extends JDDPageState, T>({
|
||||
ctx,
|
||||
state,
|
||||
arg_path,
|
||||
@ -32,9 +35,9 @@ export function ComponentInput<State extends JDDPageState, T>({
|
||||
arg: ComponentArgument<T>;
|
||||
value: T;
|
||||
page: StatefulPage<JDDPageState, typeof ComponentPreviewActions>;
|
||||
}): Readable | Promise<Readable> {
|
||||
}): Promise<Readable> {
|
||||
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<State extends JDDPageState, T>({
|
||||
});
|
||||
}
|
||||
|
||||
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<State extends JDDPageState, T>({
|
||||
<label>
|
||||
{arg_path.at(-1) || ""}
|
||||
{argType == "markdown" ? (
|
||||
<div
|
||||
class="grow-wrap"
|
||||
data-replicated-value={is(value, predicates.string) ? value : ""}
|
||||
>
|
||||
{/* putting the content in the attribute to enable autogrow */}
|
||||
<textarea
|
||||
name={`$${printArgPath(arg_path)}`}
|
||||
onblur={page.rerender()}
|
||||
cols="40"
|
||||
data-controller="autogrow-textarea submit-on-input paste-to-markdown"
|
||||
data-action="autogrow-textarea#autogrow blur->autogrow-textarea#autogrow resize->autogrow-textarea#autogrow submit-on-input#sendValues focus->submit-on-input#makePermanent blur->submit-on-input#makeNotPermanent"
|
||||
>
|
||||
{is(value, predicates.string) ? value : ""}
|
||||
</textarea>
|
||||
</div>
|
||||
) : (
|
||||
<input
|
||||
type={inputType}
|
||||
|
@ -1,3 +1,4 @@
|
||||
/* 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";
|
||||
@ -18,9 +19,13 @@ export function getComponentData(
|
||||
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 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"
|
||||
|
||||
@ -94,7 +99,7 @@ export const ComponentPreviewActions = <const>{
|
||||
|
||||
change_component: async (
|
||||
ctx: BaseContext,
|
||||
state: JDDPageState,
|
||||
_state: JDDPageState,
|
||||
inputs: Record<string, unknown>
|
||||
): Promise<JDDPageState> => {
|
||||
const component_name = inputs.component;
|
||||
@ -128,7 +133,11 @@ export const ComponentPreviewActions = <const>{
|
||||
registry
|
||||
);
|
||||
|
||||
state.components[component_index].args =
|
||||
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,
|
||||
@ -304,6 +313,9 @@ export const ComponentPreviewActions = <const>{
|
||||
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 {
|
||||
@ -339,9 +351,12 @@ export const ComponentPreviewActions = <const>{
|
||||
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]];
|
||||
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 };
|
||||
},
|
||||
|
||||
@ -352,9 +367,21 @@ export const ComponentPreviewActions = <const>{
|
||||
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]];
|
||||
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;
|
||||
},
|
||||
};
|
||||
|
124
src/back/routes/component-preview/edit-jdd-field.tsx
Normal file
124
src/back/routes/component-preview/edit-jdd-field.tsx
Normal file
@ -0,0 +1,124 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
||||
/* eslint-disable @typescript-eslint/consistent-type-assertions */
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import type Router from "@koa/router";
|
||||
import type { JDDocumentContainer, RawJDDocument } from "@sealcode/jdd";
|
||||
import {
|
||||
documentContainerFromStorage,
|
||||
documentToParsed,
|
||||
documentToStorage,
|
||||
} from "@sealcode/jdd";
|
||||
import type { BaseContext } from "koa";
|
||||
import type { Collection, CollectionItem, FieldNames } from "sealious";
|
||||
import { TempstreamJSX } from "tempstream";
|
||||
import { registry } from "../../jdd-components/registry.js";
|
||||
import { makeJDDContext } from "../../jdd-context.js";
|
||||
import JDDCreator from "./jdd-creator.js";
|
||||
import type { JDDPageState } from "./jdd-page.js";
|
||||
|
||||
export const actionName = "ArticleContentEdit";
|
||||
|
||||
export abstract class EditJDDField<C extends Collection> extends JDDCreator {
|
||||
async getID(ctx: BaseContext): Promise<string> {
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
const id = ctx.params["id"] as string;
|
||||
if (!id) {
|
||||
throw new Error("Missing URL parameter: " + "id");
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
abstract getCollection(ctx: BaseContext): C;
|
||||
|
||||
async getItem(ctx: BaseContext): Promise<CollectionItem<C>> {
|
||||
const {
|
||||
items: [item],
|
||||
} = await this.getCollection(ctx)
|
||||
.list(ctx.$context)
|
||||
.ids([await this.getID(ctx)])
|
||||
.fetch();
|
||||
if (!item) {
|
||||
throw new Error("Couldn't get item of id " + (await this.getID(ctx)));
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
abstract getJDDFieldName(): FieldNames<C["fields"]>;
|
||||
|
||||
mount(router: Router, path: string) {
|
||||
super.mount(router, path);
|
||||
|
||||
router.post(path + "save/", async (ctx) => {
|
||||
const { state } = await this.extractState(ctx);
|
||||
const item = await this.getItem(ctx);
|
||||
|
||||
item.set(
|
||||
this.getJDDFieldName(),
|
||||
(
|
||||
await documentToStorage(registry, makeJDDContext(ctx), {
|
||||
value: state.components,
|
||||
} as unknown as JDDocumentContainer<"parsed">)
|
||||
).value as any
|
||||
);
|
||||
await item.save(ctx.$context);
|
||||
ctx.type = "html";
|
||||
ctx.status = 422;
|
||||
if (!state.messages) {
|
||||
state.messages = [];
|
||||
}
|
||||
state.messages.push("Saved!");
|
||||
ctx.body = this.render(ctx, state);
|
||||
});
|
||||
}
|
||||
|
||||
async renderHeader(_ctx: BaseContext, _item: CollectionItem<C>) {
|
||||
return <h1>Edit JDD</h1>;
|
||||
}
|
||||
|
||||
async renderPreParameterButtons(ctx: BaseContext) {
|
||||
const item = await this.getItem(ctx);
|
||||
return <div>{this.renderHeader(ctx, item)}</div>;
|
||||
}
|
||||
|
||||
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.getRegistryComponents()).map((cmp) => (
|
||||
<option value={cmp}>{cmp}</option>
|
||||
))}
|
||||
</select>
|
||||
{this.makeActionButton(state, {
|
||||
action: "add_component",
|
||||
label: "Add component",
|
||||
})}
|
||||
<input type="submit" formaction="./save/" value="zapisz" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
async getInitialState(ctx: BaseContext) {
|
||||
const article = await this.getItem(ctx);
|
||||
const parsed_document = await documentToParsed(
|
||||
registry,
|
||||
makeJDDContext(ctx),
|
||||
documentContainerFromStorage(
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
(article.get(this.getJDDFieldName()) as RawJDDocument) || []
|
||||
)
|
||||
);
|
||||
return {
|
||||
components: parsed_document.value.map((e) => ({ ...e, open: true })),
|
||||
};
|
||||
}
|
||||
|
||||
// uncomment to create whitelist of allowed components
|
||||
// getAllowedComponents() {
|
||||
// return ["nice-box"];
|
||||
// }
|
||||
}
|
33
src/back/routes/component-preview/grow-wrap.css
Normal file
33
src/back/routes/component-preview/grow-wrap.css
Normal file
@ -0,0 +1,33 @@
|
||||
/* https://chriscoyier.net/2023/09/29/css-solves-auto-expanding-textareas-probably-eventually/ */
|
||||
|
||||
.grow-wrap {
|
||||
/* easy way to plop the elements on top of each other and have them both sized based on the tallest one's height */
|
||||
display: grid;
|
||||
}
|
||||
.grow-wrap::after {
|
||||
/* Note the weird space! Needed to preventy jumpy behavior */
|
||||
content: attr(data-replicated-value) " ";
|
||||
|
||||
/* This is how textarea text behaves */
|
||||
white-space: pre-wrap;
|
||||
|
||||
/* Hidden from view, clicks, and screen readers */
|
||||
visibility: hidden;
|
||||
}
|
||||
.grow-wrap > textarea {
|
||||
/* You could leave this, but after a user resizes, then it ruins the auto sizing */
|
||||
resize: none;
|
||||
|
||||
/* Firefox shows scrollbar on growth, you can hide like this. */
|
||||
overflow: hidden;
|
||||
}
|
||||
.grow-wrap > textarea,
|
||||
.grow-wrap::after {
|
||||
/* Identical styling required!! */
|
||||
border: 1px solid black;
|
||||
padding: 0.5rem;
|
||||
font: inherit;
|
||||
|
||||
/* Place on top of each other */
|
||||
grid-area: 1 / 1 / 2 / 2;
|
||||
}
|
@ -4,7 +4,12 @@ export default class InputImagePreview extends Controller {
|
||||
id: string;
|
||||
|
||||
handleChange() {
|
||||
const img = this.element.querySelector("img");
|
||||
let img = this.element.querySelector("img");
|
||||
if (!img) {
|
||||
img = document.createElement("img");
|
||||
img.setAttribute("style", "height: 40px; width: 40px");
|
||||
this.element.querySelector(".image-preview-container").appendChild(img);
|
||||
}
|
||||
window.URL.revokeObjectURL(img.src);
|
||||
const new_url = window.URL.createObjectURL(
|
||||
this.element.querySelector("input").files[0]
|
||||
|
@ -20,8 +20,8 @@ export default abstract class JDDCreator extends JDDPage {
|
||||
return [];
|
||||
}
|
||||
|
||||
getRegistryCompoments() {
|
||||
const all_components = super.getRegistryCompoments();
|
||||
getRegistryComponents() {
|
||||
const all_components = super.getRegistryComponents();
|
||||
const allowed_components = this.getAllowedComponents();
|
||||
|
||||
if (allowed_components.length > 0) {
|
||||
@ -43,7 +43,7 @@ export default abstract class JDDCreator extends JDDPage {
|
||||
<div>
|
||||
<input type="submit" value="Preview" />
|
||||
<select name="component">
|
||||
{Object.keys(this.getRegistryCompoments()).map((cmp) => (
|
||||
{Object.keys(this.getRegistryComponents()).map((cmp) => (
|
||||
<option value={cmp}>{cmp}</option>
|
||||
))}
|
||||
</select>
|
||||
@ -64,9 +64,17 @@ export default abstract class JDDCreator extends JDDPage {
|
||||
},
|
||||
component_index: number
|
||||
) {
|
||||
const checkbox_id = `component_${component_index}_open`;
|
||||
return (
|
||||
<fieldset>
|
||||
<legend>Component - {component.component_name}</legend>
|
||||
<div
|
||||
class={[
|
||||
"jdd-editor__component-block",
|
||||
"jdd-editor__component-block--number-" + component_index,
|
||||
]}
|
||||
data-component-debugger-target="componentBlock"
|
||||
data-component-index={component_index.toString()}
|
||||
>
|
||||
<summary class="jdd-editor__component-block__top_bar">
|
||||
{this.makeActionButton(
|
||||
state,
|
||||
{ action: "remove_component", label: "❌" },
|
||||
@ -98,8 +106,29 @@ export default abstract class JDDCreator extends JDDPage {
|
||||
},
|
||||
component_index
|
||||
)}
|
||||
<span>{component.component_name}</span>
|
||||
<label
|
||||
class="component-block__handle"
|
||||
for={checkbox_id}
|
||||
style="flex-grow: 1"
|
||||
data-action="click->component-debugger#labelClicked"
|
||||
>
|
||||
<span class="jdd-editor__component-block__chevron"> > </span>
|
||||
</label>
|
||||
</summary>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="component-collapse-toggle"
|
||||
name={`$[components][${component_index}][open]`}
|
||||
data-turbo-permanent
|
||||
id={checkbox_id}
|
||||
style="display:none"
|
||||
data-component-debugger-target="checkbox"
|
||||
/>
|
||||
<div class="jdd-editor__component-block__inner">
|
||||
{super.renderComponentBlock(ctx, state, component, component_index)}
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ export const actionName = "Components";
|
||||
export type JDDPageState = {
|
||||
components: RawJDDocument;
|
||||
preview_size?: string;
|
||||
messages?: string[];
|
||||
};
|
||||
|
||||
export default abstract class JDDPage extends StatefulPage<
|
||||
@ -27,14 +28,17 @@ export default abstract class JDDPage extends StatefulPage<
|
||||
|
||||
previewSizes = ["320", "600", "800", "1024", "1300", "1920"];
|
||||
|
||||
getRegistryCompoments() {
|
||||
getRegistryComponents() {
|
||||
return registry.getAll();
|
||||
}
|
||||
|
||||
async getInitialState(ctx: BaseContext) {
|
||||
const [component_name, component] = Object.entries(
|
||||
this.getRegistryCompoments()
|
||||
)[0];
|
||||
const all_components = Object.entries(this.getRegistryComponents());
|
||||
const first_component = all_components[0];
|
||||
if (!first_component) {
|
||||
throw new Error("No defined components!");
|
||||
}
|
||||
const [component_name, component] = first_component;
|
||||
const initial_state = {
|
||||
components: [
|
||||
{
|
||||
@ -60,6 +64,7 @@ export default abstract class JDDPage extends StatefulPage<
|
||||
preserveScroll: true,
|
||||
autoRefreshCSS: true,
|
||||
navbar: () => ``,
|
||||
bodyClasses: ["jdd-editor"],
|
||||
},
|
||||
(...args) =>
|
||||
tempstream`${defaultHead(...args)}${renderEarlyAssets(
|
||||
@ -96,8 +101,9 @@ export default abstract class JDDPage extends StatefulPage<
|
||||
if (!component) {
|
||||
throw new Error(`Unknown component: ${component_name}`);
|
||||
}
|
||||
const overrides_for_component =
|
||||
overrides.components[parseInt(component_index)];
|
||||
const overrides_for_component = overrides.components[
|
||||
parseInt(component_index)
|
||||
] || { args: {} };
|
||||
const promises = Object.entries(component.getArguments()).map(
|
||||
async ([arg_name, arg]) => {
|
||||
const value = overrides_for_component.args[arg_name];
|
||||
@ -126,8 +132,7 @@ export default abstract class JDDPage extends StatefulPage<
|
||||
) {
|
||||
const jdd_context = makeJDDContext(ctx);
|
||||
return (
|
||||
<fieldset class="component-preview-parameters">
|
||||
<legend>Parameters</legend>
|
||||
<div class="component-preview-parameters">
|
||||
{Object.entries(component.getArguments()).map(async ([arg_name, arg]) => (
|
||||
<ComponentInput
|
||||
{...{
|
||||
@ -144,8 +149,7 @@ export default abstract class JDDPage extends StatefulPage<
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
<input type="submit" value="Preview" />
|
||||
</fieldset>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -237,6 +241,25 @@ export default abstract class JDDPage extends StatefulPage<
|
||||
return result;
|
||||
}
|
||||
|
||||
renderPreParameterButtons(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_ctx: BaseContext,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
_state: JDDPageState
|
||||
): FlatTemplatable | Promise<FlatTemplatable> {
|
||||
return "";
|
||||
}
|
||||
|
||||
renderMessages(_ctx: BaseContext, state: JDDPageState) {
|
||||
return (
|
||||
<ul>
|
||||
{(state.messages || []).map((e) => (
|
||||
<li>{e}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
render(ctx: BaseContext, state: JDDPageState) {
|
||||
return (
|
||||
<div
|
||||
@ -246,17 +269,20 @@ export default abstract class JDDPage extends StatefulPage<
|
||||
data-controller="component-debugger"
|
||||
>
|
||||
<div class="component-arguments">
|
||||
{this.renderPreParameterButtons(ctx, state)}
|
||||
{this.renderParameterButtons(state)}
|
||||
{this.renderMessages(ctx, state)}
|
||||
{state.components.map((component, component_index) =>
|
||||
this.renderComponentBlock(ctx, state, component, component_index)
|
||||
)}
|
||||
<code>{this.serializeState(ctx, state)}</code>
|
||||
<code style="max-height: 100px; display: block; overflow: hidden; font-size: 9px; color: #a8a8a8; padding: 1rem;">
|
||||
{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{" "}
|
||||
<div class="component-preview__header">
|
||||
<span>Preview</span>
|
||||
<span data-component-debugger-target="component-width"></span>
|
||||
<select
|
||||
name="size"
|
||||
@ -274,23 +300,15 @@ export default abstract class JDDPage extends StatefulPage<
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<noscript>
|
||||
{this.makeActionButton(state, "change_size")}
|
||||
</noscript>
|
||||
</legend>
|
||||
<noscript>{this.makeActionButton(state, "change_size")}</noscript>
|
||||
</div>
|
||||
<div class="jdd-container">
|
||||
{render(
|
||||
registry,
|
||||
documentContainerFromParsed(state.components),
|
||||
makeJDDContext(ctx)
|
||||
)}
|
||||
</fieldset>
|
||||
{
|
||||
/* HTML */ `<script>
|
||||
(function () {
|
||||
const gutter = document.querySelector(".resize-gutter");
|
||||
})();
|
||||
</script>`
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -0,0 +1,23 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unnecessary-type-assertion, @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unsafe-assignment */
|
||||
import { Controller } from "stimulus";
|
||||
import TurndownService from "turndown";
|
||||
|
||||
export default class PasteToMarkdown extends Controller<HTMLTextAreaElement> {
|
||||
connect() {
|
||||
this.element.addEventListener("paste", (event) => {
|
||||
if (event.clipboardData.types.includes("text/html")) {
|
||||
const turndownService = new TurndownService({
|
||||
headingStyle: "atx",
|
||||
preformattedCode: true,
|
||||
} as any);
|
||||
event.preventDefault();
|
||||
const html = (event.clipboardData.getData("text/html") as string)
|
||||
.replaceAll("\n", " ")
|
||||
// to get rid of some of the style metadata from libreoffice
|
||||
.replace(/^<!doctype.*<body[^>]*>/i, "");
|
||||
document.execCommand("insertText", false, turndownService.turndown(html));
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/* eslint-disable @typescript-eslint/consistent-type-assertions, @typescript-eslint/no-unsafe-assignment */
|
||||
import { Controller } from "stimulus";
|
||||
import { throttle } from "throttle-debounce";
|
||||
|
||||
export default class SubmitOnInput extends Controller<HTMLTextAreaElement> {
|
||||
sendValues: () => void;
|
||||
connect() {
|
||||
this.sendValues = throttle(
|
||||
500,
|
||||
() => {
|
||||
this.element.closest("form").requestSubmit();
|
||||
},
|
||||
{ noTrailing: false }
|
||||
);
|
||||
}
|
||||
|
||||
makePermanent() {
|
||||
// this prevents morphing from overwriting the input value with previous half-dane values - https://github.com/hotwired/turbo/issues/1199
|
||||
this.element.setAttribute("data-turbo-permanent", "");
|
||||
}
|
||||
|
||||
makeNotPermanent() {
|
||||
this.element.removeAttribute("data-turbo-permanent");
|
||||
}
|
||||
}
|
@ -14,7 +14,7 @@ export const actionName = "Components";
|
||||
|
||||
export default new (class JddcomponentDebuggerPage extends JDDPage {
|
||||
renderParameterButtons(state: JDDPageState): Stringifiable {
|
||||
const all_components = super.getRegistryCompoments();
|
||||
const all_components = super.getRegistryComponents();
|
||||
return (
|
||||
<div>
|
||||
<input type="submit" value="Preview" />
|
||||
|
@ -19,10 +19,19 @@ application.register("map-with-pins", MapWithPins);
|
||||
import { default as HorizontalScroller } from "./../back/routes/common/horizontal-scroller/horizontal-scroller.stimulus.js";
|
||||
application.register("horizontal-scroller", HorizontalScroller);
|
||||
|
||||
import { default as AutogrowTextarea } from "./../back/routes/component-preview/autogrow-textarea.stimulus.js";
|
||||
application.register("autogrow-textarea", AutogrowTextarea);
|
||||
|
||||
import { default as ComponentDebugger } from "./../back/routes/component-preview/component-debugger.stimulus.js";
|
||||
application.register("component-debugger", ComponentDebugger);
|
||||
|
||||
import { default as InputImagePreview } from "./../back/routes/component-preview/input-image-preview.stimulus.js";
|
||||
application.register("input-image-preview", InputImagePreview);
|
||||
|
||||
import { default as PasteToMarkdown } from "./../back/routes/component-preview/paste-to-markdown.stimulus.js";
|
||||
application.register("paste-to-markdown", PasteToMarkdown);
|
||||
|
||||
import { default as SubmitOnInput } from "./../back/routes/component-preview/submit-on-input.stimulus.js";
|
||||
application.register("submit-on-input", SubmitOnInput);
|
||||
|
||||
export { Turbo };
|
||||
|
@ -16,7 +16,8 @@
|
||||
"allowJs": true,
|
||||
"resolveJsonModule": true,
|
||||
"sourceMap": true,
|
||||
"skipLibCheck": true
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"include": ["./src/back/*", "./src/back/**/*", "./src/back/routes/common/navbar.ts"],
|
||||
"exclude": ["./src/front", "./src/**/*.stimulus.ts"],
|
||||
|
@ -2,8 +2,9 @@
|
||||
"compilerOptions": {
|
||||
"module": "CommonJS",
|
||||
"target": "ES6",
|
||||
"lib": ["dom"],
|
||||
"skipLibCheck": true
|
||||
"lib": ["dom", "es2021"],
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"include": ["./src/front", "./src/**/*.stimulus.ts"]
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user