Add Subtree and related components

This commit is contained in:
Kuba Orlik 2026-08-10 17:35:03 +02:00
parent 5b5c7b2e4b
commit 6031795a4b
9 changed files with 229 additions and 2 deletions

View File

@ -0,0 +1,31 @@
.buttons {
display: flex;
&.buttons--mode-row {
flex-flow: row wrap;
}
&.buttons--mode-column {
flex-flow: column;
}
&.buttons--gap-none {
gap: 0;
}
&.buttons--gap-narrow {
gap: 8px;
}
&.buttons--gap-medium {
gap: 16px;
}
&.buttons--gap-wide {
gap: 24px;
}
.button {
margin: 0;
}
}

View File

@ -0,0 +1,40 @@
import { TempstreamJSX } from "tempstream";
import type { ComponentToHTMLArgs } from "@sealcode/jdd";
import { Component, ComponentArguments, insert_nbsp } from "@sealcode/jdd";
import { button, button_variants } from "src/back/elements/button.js";
const component_arguments = {
mode: new ComponentArguments.Enum(["column", "row"]),
gap: new ComponentArguments.Enum(["none", "narrow", "medium", "wide"]),
buttons: new ComponentArguments.List(
new ComponentArguments.Structured({
variant: new ComponentArguments.Enum(button_variants),
text: new ComponentArguments.ShortText(),
href: new ComponentArguments.ShortText(),
})
),
} as const;
export class Buttons extends Component<typeof component_arguments> {
getArguments() {
return component_arguments;
}
async toHTML({
args: { buttons, mode, gap },
classes,
}: ComponentToHTMLArgs<typeof component_arguments>): Promise<string> {
return (
<div
class={[
"buttons",
`buttons--mode-${mode}`,
`buttons--gap-${gap}`,
...classes,
]}
>
{buttons.map((b) => button({ ...b, text: insert_nbsp(b.text) }))}
</div>
);
}
}

View File

@ -0,0 +1,22 @@
import { TempstreamJSX } from "tempstream";
import type { ComponentToHTMLArgs } from "@sealcode/jdd";
import { Component, ComponentArguments } from "@sealcode/jdd";
const component_arguments = {
size: new ComponentArguments.Number(),
} as const;
export class EmptySpace extends Component<typeof component_arguments> {
getArguments() {
return component_arguments;
}
async toHTML({
args: { size },
classes,
}: ComponentToHTMLArgs<typeof component_arguments>): Promise<string> {
return (
<div class={["empty-space", ...classes]} style={`height: ${size}px`}></div>
);
}
}

View File

@ -0,0 +1,26 @@
.heading {
& > * {
margin-top: 0;
margin-bottom: 0;
}
&.heading--font-size-tiny > * {
font-size: 20px;
}
&.heading--font-size-small > * {
font-size: 24px;
}
&.heading--font-size-medium > * {
font-size: 32px;
}
&.heading--font-size-large > * {
font-size: 48px;
}
&.heading--font-size-extra_large > * {
font-size: 64px;
}
}

View File

@ -0,0 +1,32 @@
import { TempstreamJSX } from "tempstream";
import type { ComponentToHTMLArgs } from "@sealcode/jdd";
import { Component, ComponentArguments, insert_nbsp } from "@sealcode/jdd";
const component_arguments = {
title: new ComponentArguments.ShortText(),
level: new ComponentArguments.Enum(["h1", "h2", "h3", "h4", "h5", "h6"]),
font_size: new ComponentArguments.Enum([
"tiny",
"small",
"medium",
"large",
"extra_large",
]),
} as const;
export class Heading extends Component<typeof component_arguments> {
getArguments() {
return component_arguments;
}
async toHTML({
args: { title, level, font_size },
classes,
}: ComponentToHTMLArgs<typeof component_arguments>): Promise<string> {
return (
<div class={["heading", `heading--font-size-${font_size}`, ...classes]}>
{`<${level}>${insert_nbsp(title)}</${level}>`}
</div>
);
}
}

View File

@ -0,0 +1,20 @@
.subtree {
display: flex;
flex-flow: column;
&.subtree--gap-none {
gap: 0px;
}
&.subtree--gap-narrow {
gap: 8px;
}
&.subtree--gap-medium {
gap: 16px;
}
&.subtree--gap-wide {
gap: 24px;
}
}

View File

@ -0,0 +1,40 @@
import { TempstreamJSX } from "tempstream";
import type {
ComponentToHTMLArgs,
ExtractStructuredComponentArgumentsParsed,
JDDContext,
} from "@sealcode/jdd";
import { Component, ComponentArguments } from "@sealcode/jdd";
const component_arguments = {
gap: new ComponentArguments.Enum(["none", "narrow", "medium", "wide"]),
components: new ComponentArguments.List(new ComponentArguments.NestedComponent()),
} as const;
export class Subtree extends Component<typeof component_arguments> {
getArguments() {
return component_arguments;
}
getCSSClumps(
jdd_context: JDDContext,
args: ExtractStructuredComponentArgumentsParsed<typeof component_arguments>
) {
const result = (args.components || [])
.map((component) => component.getCSSClumps(jdd_context))
.flat();
return result;
}
async toHTML({
args: { components, gap },
classes,
jdd_context,
}: ComponentToHTMLArgs<typeof component_arguments>): Promise<string> {
return (
<div class={["subtree", `subtree--gap-${gap}`, ...classes]}>
{components?.map((component) => component.render(jdd_context))}
</div>
);
}
}

View File

@ -3,6 +3,14 @@
font-size: 16px;
line-height: 24px;
&.tekst--size-small {
font-size: 14px;
}
&.tekst--size-large {
font-size: 20px;
}
p {
margin: 0;
}

View File

@ -9,6 +9,9 @@ import { Component, ComponentArguments } from "@sealcode/jdd";
const component_arguments = {
color: new ComponentArguments.Enum(["transparent", "normal", "accent", "accent2"]),
size: new ComponentArguments.Enum(["small", "normal", "large"]).setExampleValues([
"normal",
]),
content: new ComponentArguments.Markdown(),
side_content: new ComponentArguments.NestedComponent(),
} as const;
@ -34,7 +37,7 @@ export class Tekst extends Component<typeof component_arguments> {
}
toHTML({
args: { color, content, side_content },
args: { color, content, side_content, size },
classes,
jdd_context,
index,
@ -42,7 +45,12 @@ export class Tekst extends Component<typeof component_arguments> {
const { render_markdown, language } = jdd_context;
return (
<div
class={["tekst markdown", ...classes, `tekst--color-${color}`]}
class={[
"tekst markdown",
...classes,
`tekst--color-${color}`,
`tekst--size-${size}`,
]}
style={`--jdd-index: ${index}`}
>
{side_content.isEmpty() ? (