Update jdd. Add a linktree-component
This commit is contained in:
parent
ea72b8d016
commit
4569bd5a1a
14
package-lock.json
generated
14
package-lock.json
generated
@ -15,7 +15,7 @@
|
|||||||
"@sealcode/add-to-head": "^1.0.0",
|
"@sealcode/add-to-head": "^1.0.0",
|
||||||
"@sealcode/file-manager": "^1.0.2",
|
"@sealcode/file-manager": "^1.0.2",
|
||||||
"@sealcode/jdd": "^0.8.10",
|
"@sealcode/jdd": "^0.8.10",
|
||||||
"@sealcode/jdd-editor": "^0.2.30",
|
"@sealcode/jdd-editor": "^0.2.31",
|
||||||
"@sealcode/monaco-wrapper": "^0.0.11",
|
"@sealcode/monaco-wrapper": "^0.0.11",
|
||||||
"@sealcode/sealgen": "^0.19.21",
|
"@sealcode/sealgen": "^0.19.21",
|
||||||
"@sealcode/show-first-row": "^0.1.0",
|
"@sealcode/show-first-row": "^0.1.0",
|
||||||
@ -1380,9 +1380,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sealcode/jdd-editor": {
|
"node_modules/@sealcode/jdd-editor": {
|
||||||
"version": "0.2.30",
|
"version": "0.2.31",
|
||||||
"resolved": "https://registry.npmjs.org/@sealcode/jdd-editor/-/jdd-editor-0.2.30.tgz",
|
"resolved": "https://registry.npmjs.org/@sealcode/jdd-editor/-/jdd-editor-0.2.31.tgz",
|
||||||
"integrity": "sha512-b+f5xR4FrGc8MLSDKB9AJhehQX8c9NLP7M+towfNUIQNVVVEbecKgJuidmGtSOwrPuahcjRxF8nZqXpFEGdBlQ==",
|
"integrity": "sha512-mPxrh+YGN5q9zSSZU9fjp/M+kTnDb99MGkwEAlcB17dDPJr5enjQJHLMFanC+v/cV5+H7Qxfyj5Yi3+b7ICciQ==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@koa/router": "^13.1.0",
|
"@koa/router": "^13.1.0",
|
||||||
"@sealcode/jdd": "^0.8.10",
|
"@sealcode/jdd": "^0.8.10",
|
||||||
@ -16016,9 +16016,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@sealcode/jdd-editor": {
|
"@sealcode/jdd-editor": {
|
||||||
"version": "0.2.30",
|
"version": "0.2.31",
|
||||||
"resolved": "https://registry.npmjs.org/@sealcode/jdd-editor/-/jdd-editor-0.2.30.tgz",
|
"resolved": "https://registry.npmjs.org/@sealcode/jdd-editor/-/jdd-editor-0.2.31.tgz",
|
||||||
"integrity": "sha512-b+f5xR4FrGc8MLSDKB9AJhehQX8c9NLP7M+towfNUIQNVVVEbecKgJuidmGtSOwrPuahcjRxF8nZqXpFEGdBlQ==",
|
"integrity": "sha512-mPxrh+YGN5q9zSSZU9fjp/M+kTnDb99MGkwEAlcB17dDPJr5enjQJHLMFanC+v/cV5+H7Qxfyj5Yi3+b7ICciQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@koa/router": "^13.1.0",
|
"@koa/router": "^13.1.0",
|
||||||
"@sealcode/jdd": "^0.8.10",
|
"@sealcode/jdd": "^0.8.10",
|
||||||
|
|||||||
@ -98,7 +98,7 @@
|
|||||||
"@sealcode/add-to-head": "^1.0.0",
|
"@sealcode/add-to-head": "^1.0.0",
|
||||||
"@sealcode/file-manager": "^1.0.2",
|
"@sealcode/file-manager": "^1.0.2",
|
||||||
"@sealcode/jdd": "^0.8.10",
|
"@sealcode/jdd": "^0.8.10",
|
||||||
"@sealcode/jdd-editor": "^0.2.30",
|
"@sealcode/jdd-editor": "^0.2.31",
|
||||||
"@sealcode/monaco-wrapper": "^0.0.11",
|
"@sealcode/monaco-wrapper": "^0.0.11",
|
||||||
"@sealcode/sealgen": "^0.19.21",
|
"@sealcode/sealgen": "^0.19.21",
|
||||||
"@sealcode/show-first-row": "^0.1.0",
|
"@sealcode/show-first-row": "^0.1.0",
|
||||||
|
|||||||
@ -1,9 +1,15 @@
|
|||||||
.button {
|
.button {
|
||||||
|
--icon-width: 32px;
|
||||||
|
--gap: 8px;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
display: inline-block;
|
display: inline-flex;
|
||||||
|
flex-flow: row;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
margin: 4px;
|
margin: 4px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
color: var(--button-fg-color);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
filter: brightness(1.1);
|
filter: brightness(1.1);
|
||||||
@ -13,14 +19,25 @@
|
|||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
svg * {
|
||||||
|
fill: var(--button-fg-color) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button--centered {
|
||||||
|
span {
|
||||||
|
text-align: center;
|
||||||
|
margin-right: calc(var(--gap) + var(--icon-width));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.button--color-accent1 {
|
.button--color-accent1 {
|
||||||
background-color: var(--color-brand-accent);
|
background-color: var(--color-brand-accent);
|
||||||
color: var(--color-brand-text-on-accent);
|
--button-fg-color: var(--color-brand-text-on-accent);
|
||||||
}
|
}
|
||||||
|
|
||||||
.button--color-accent2 {
|
.button--color-accent2 {
|
||||||
background-color: var(--color-brand-accent2);
|
background-color: var(--color-brand-accent2);
|
||||||
color: var(--color-brand-text-on-accent2);
|
--button-fg-color: var(--color-brand-text-on-accent2);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,30 +0,0 @@
|
|||||||
export const button_variants = ["accent1", "accent2"] as const;
|
|
||||||
type variant = (typeof button_variants)[number];
|
|
||||||
|
|
||||||
export function button({
|
|
||||||
text,
|
|
||||||
href,
|
|
||||||
variant,
|
|
||||||
disabled = false,
|
|
||||||
submit = false,
|
|
||||||
}: {
|
|
||||||
text: string;
|
|
||||||
href: string;
|
|
||||||
variant: variant;
|
|
||||||
disabled?: boolean;
|
|
||||||
submit?: boolean;
|
|
||||||
}): JSX.Element {
|
|
||||||
const classstr = `class="button button--color-${variant}"`;
|
|
||||||
if (submit) {
|
|
||||||
return /* HTML */ `<button type="submit" formaction="${href}" ${classstr}>
|
|
||||||
${text}
|
|
||||||
</button>`;
|
|
||||||
}
|
|
||||||
return /* HTML */ `<a
|
|
||||||
${classstr}
|
|
||||||
${disabled ? "" : `href="${href}"`}
|
|
||||||
${disabled ? "disabled" : ""}
|
|
||||||
>
|
|
||||||
${text}
|
|
||||||
</a>`;
|
|
||||||
}
|
|
||||||
43
src/back/elements/button.tsx
Normal file
43
src/back/elements/button.tsx
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import { TempstreamJSX } from "tempstream";
|
||||||
|
import { icon as render_icon } from "../icons.js";
|
||||||
|
|
||||||
|
export const button_variants = ["accent1", "accent2"] as const;
|
||||||
|
type variant = (typeof button_variants)[number];
|
||||||
|
|
||||||
|
export function button({
|
||||||
|
text,
|
||||||
|
href,
|
||||||
|
variant,
|
||||||
|
disabled = false,
|
||||||
|
submit = false,
|
||||||
|
icon,
|
||||||
|
centered = false,
|
||||||
|
}: {
|
||||||
|
text: string;
|
||||||
|
href: string;
|
||||||
|
variant: variant;
|
||||||
|
disabled?: boolean;
|
||||||
|
submit?: boolean;
|
||||||
|
icon?: string;
|
||||||
|
centered: boolean;
|
||||||
|
}): JSX.Element {
|
||||||
|
const classstr = `button button--color-${variant}`;
|
||||||
|
if (submit) {
|
||||||
|
return /* HTML */ `<button type="submit" formaction="${href}" ${classstr}>
|
||||||
|
${text}
|
||||||
|
</button>`;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
class={[
|
||||||
|
"button",
|
||||||
|
`button--color-${variant}`,
|
||||||
|
{ "button--centered": centered },
|
||||||
|
]}
|
||||||
|
href={disabled ? false : href}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
{icon ? render_icon(icon) : ""} <span>{text}</span>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
60
src/back/jdd-components/linktree/linktree.css
Normal file
60
src/back/jdd-components/linktree/linktree.css
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
.linktree {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
align-items: center;
|
||||||
|
--image-width: 200px;
|
||||||
|
|
||||||
|
.linktree__photo {
|
||||||
|
picture {
|
||||||
|
border-radius: calc(var(--image-width) / 2);
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
img {
|
||||||
|
width: var(--image-width) !important;
|
||||||
|
height: var(--image-width) !important;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin-top: 16px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.linktree__sections {
|
||||||
|
width: max-content;
|
||||||
|
max-width: 100cqw;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column;
|
||||||
|
gap: 8px;
|
||||||
|
justify-content: stretch;
|
||||||
|
|
||||||
|
li {
|
||||||
|
list-style: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
a.button {
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 300px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
text-align: center;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
69
src/back/jdd-components/linktree/linktree.jdd.tsx
Normal file
69
src/back/jdd-components/linktree/linktree.jdd.tsx
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import { TempstreamJSX } from "tempstream";
|
||||||
|
import type { ComponentToHTMLArgs } from "@sealcode/jdd";
|
||||||
|
import { Component, ComponentArguments as Args, insert_nbsp } from "@sealcode/jdd";
|
||||||
|
import { getAllIconNames } from "src/back/icons.js";
|
||||||
|
import { button, button_variants } from "src/back/elements/button.js";
|
||||||
|
|
||||||
|
const component_arguments = {
|
||||||
|
photo: new Args.Image(),
|
||||||
|
title: new Args.ShortText(),
|
||||||
|
description: new Args.Markdown(),
|
||||||
|
sections: new Args.List(
|
||||||
|
new Args.Structured({
|
||||||
|
name: new Args.ShortText(),
|
||||||
|
links: new Args.List(
|
||||||
|
new Args.Structured({
|
||||||
|
href: new Args.URL("absolute"),
|
||||||
|
text: new Args.ShortText(),
|
||||||
|
icon: new Args.Enum(getAllIconNames),
|
||||||
|
variant: new Args.Enum(button_variants),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export class Linktree extends Component<typeof component_arguments> {
|
||||||
|
getArguments() {
|
||||||
|
return component_arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
async toHTML({
|
||||||
|
args: { photo, title, description, sections },
|
||||||
|
classes,
|
||||||
|
jdd_context: { render_markdown, render_image, language },
|
||||||
|
}: ComponentToHTMLArgs<typeof component_arguments>): Promise<string> {
|
||||||
|
return (
|
||||||
|
<div class={["linktree", ...classes]}>
|
||||||
|
<div class="linktree__photo">
|
||||||
|
{photo
|
||||||
|
? render_image(photo, {
|
||||||
|
sizesAttr: "200px",
|
||||||
|
|
||||||
|
container: {
|
||||||
|
width: 200,
|
||||||
|
height: 200,
|
||||||
|
objectFit: "cover",
|
||||||
|
},
|
||||||
|
alt: title || "",
|
||||||
|
})
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
{title ? <h2>{insert_nbsp(title)}</h2> : ""}
|
||||||
|
{description ? <div>{render_markdown(language, description)}</div> : ""}
|
||||||
|
<div class="linktree__sections">
|
||||||
|
{sections.map((section) => (
|
||||||
|
/* HTML */ <section>
|
||||||
|
<h3>{section.name}</h3>
|
||||||
|
<ul>
|
||||||
|
{section.links.map((link) => (
|
||||||
|
<li>{button({ ...link, centered: true })}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user