Page MenuHomeSealhub

No OneTemporary

diff --git a/src/back/jdd-components/dynamic-grid/dynamic-grid.css b/src/back/jdd-components/dynamic-grid/dynamic-grid.css
new file mode 100644
index 0000000..352231c
--- /dev/null
+++ b/src/back/jdd-components/dynamic-grid/dynamic-grid.css
@@ -0,0 +1,248 @@
+.dynamic-grid-component {
+ .dynamic-grid-title {
+ text-align: center;
+ }
+
+ .tabs-menu {
+ display: flex;
+ align-items: stretch;
+ list-style: none;
+ padding: 0;
+ border-bottom: 1px solid #ccc;
+ margin-bottom: 36px;
+ .tabs-menu-button {
+ display: flex;
+ margin-bottom: -1px;
+ border: 1px solid #ccc;
+ background: #eee;
+ color: #666;
+ font-size: 12px;
+ cursor: pointer;
+ transition: border 200ms, color 200ms;
+
+ label {
+ padding: 12px 15px;
+ }
+ }
+ .tabs-menu-button:hover {
+ border-top-color: #333;
+ color: #333;
+ }
+ }
+
+ .tabs-menu-radio:checked ~ .tabs-menu-button label {
+ border-bottom-color: #fff;
+ border-top-color: #b721ff;
+ background: #fff;
+ color: #222;
+ }
+
+ .tabs-menu-radio {
+ display: none;
+ }
+
+ .tab-container {
+ display: none;
+
+ .tiles-container {
+ display: grid;
+ grid-gap: 24px;
+ grid-template-columns: repeat(3, 1fr);
+ grid-auto-rows: 1fr;
+ grid-auto-flow: row dense;
+
+ .tile {
+ position: relative;
+ overflow: hidden;
+
+ .tile-content {
+ position: absolute;
+ background-color: #c3c3c3;
+ width: 100%;
+ opacity: 0.9;
+ padding: 8px;
+ z-index: 1;
+ overflow: hidden;
+ bottom: 0;
+ box-sizing: border-box;
+
+ .tile-content-wrapper {
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 4;
+ line-height: 20px;
+ height: 103px;
+ overflow: hidden;
+ .tile-title {
+ font-weight: bold;
+ line-height: 20px;
+ margin: 10px 0;
+ }
+
+ .tile-subtitle {
+ line-height: 20px;
+ }
+ }
+
+ .spacer {
+ height: 5px;
+ }
+ }
+
+ .tile-image {
+ z-index: 0;
+ width: 100%;
+ height: 100%;
+ picture {
+ height: 100%;
+ width: 100% !important;
+ }
+ }
+ }
+
+ .tile.square {
+ grid-column: span 1;
+ grid-row: span 1;
+
+ .tile-image--horizontal {
+ display: none;
+ }
+
+ .tile-image--vertical {
+ display: none;
+ }
+
+ .tile-image--square {
+ picture img {
+ display: block !important;
+ }
+ }
+ }
+
+ .tile.horizontal {
+ grid-column: span 2;
+ grid-row: span 1;
+
+ .tile-image--vertical {
+ display: none;
+ }
+
+ .tile-image--square {
+ display: none;
+ }
+
+ .tile-image--horizontal {
+ picture img {
+ display: block !important;
+ }
+ }
+ }
+
+ .tile.vertical {
+ grid-row: span 2;
+ grid-column: span 1;
+
+ .tile-image--horizontal {
+ display: none;
+ }
+
+ .tile-image--square {
+ display: none;
+ }
+
+ .tile-image--vertical {
+ picture img {
+ display: block !important;
+ }
+ }
+ }
+ }
+
+ .buttons-container {
+ margin-top: 36px;
+ display: flex;
+ justify-content: center;
+ flex-flow: row wrap;
+ column-gap: 18px;
+ row-gap: 16px;
+
+ .button {
+ display: inline-block;
+ text-align: center;
+ padding: 8px 24px;
+ border: 1px solid #000000;
+ color: var(--text-color);
+ min-width: 240px;
+ line-height: 23px;
+ font-size: 16px;
+ &.dark {
+ background-color: #000000;
+ --text-color: #ffffff;
+ }
+ &.bright {
+ background-color: #ffffff;
+ --text-color: #000000;
+ }
+ }
+ }
+ }
+ @container (max-width: 950px) {
+ .tab-container {
+ .tiles-container {
+ grid-template-columns: repeat(2, 1fr);
+
+ .tile.vertical {
+ grid-row: span 1;
+ grid-column: span 1;
+
+ .tile-image--vertical {
+ display: none;
+ }
+
+ .tile-image--horizontal {
+ display: none;
+ }
+
+ .tile-image--square {
+ display: block;
+
+ picture img {
+ display: block !important;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @container (max-width: 700px) {
+ .tab-container {
+ .tiles-container {
+ grid-template-columns: 1fr;
+ max-width: 450px;
+ margin: 0 auto;
+
+ .tile.horizontal {
+ grid-column: span 1;
+ grid-row: span 1;
+
+ .tile-image--vertical {
+ display: none;
+ }
+
+ .tile-image--horizontal {
+ display: none;
+ }
+
+ .tile-image--square {
+ display: block;
+
+ picture img {
+ display: block !important;
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/back/jdd-components/dynamic-grid/dynamic-grid.jdd.tsx b/src/back/jdd-components/dynamic-grid/dynamic-grid.jdd.tsx
new file mode 100644
index 0000000..12564aa
--- /dev/null
+++ b/src/back/jdd-components/dynamic-grid/dynamic-grid.jdd.tsx
@@ -0,0 +1,182 @@
+import type { FlatTemplatable } from "tempstream";
+import { TempstreamJSX } from "tempstream";
+import type {
+ ExtractParsed,
+ ExtractStructuredComponentArgumentsParsed,
+ JDDContext,
+} from "@sealcode/jdd";
+import { Component, ComponentArguments } from "@sealcode/jdd";
+
+type ExtractArray<T> = T extends Array<infer X> ? X : never;
+
+const generate_id = (function* () {
+ while (true) {
+ for (let i = 1; i <= 10000; i++) {
+ yield i;
+ }
+ }
+})();
+
+const component_arguments = {
+ heading: new ComponentArguments.ShortText(),
+ tabs: new ComponentArguments.List(
+ new ComponentArguments.Structured({
+ name: new ComponentArguments.ShortText().setExampleValues([
+ "Tab 1",
+ "Tab 2",
+ "Tab 3",
+ ]),
+ tiles: new ComponentArguments.List(
+ new ComponentArguments.Structured({
+ title: new ComponentArguments.ShortText(),
+ subtitle: new ComponentArguments.ShortText(),
+ url: new ComponentArguments.ShortText(),
+ photo: new ComponentArguments.Structured({
+ image: new ComponentArguments.Image(),
+ alt: new ComponentArguments.ShortText(),
+ }),
+ shape: new ComponentArguments.Enum([
+ "square",
+ "horizontal",
+ "vertical",
+ ]),
+ })
+ ),
+ buttons: new ComponentArguments.List(
+ new ComponentArguments.Structured({
+ text: new ComponentArguments.ShortText().setExampleValues([
+ "Button 1",
+ "Button 2",
+ "Button 3",
+ ]),
+ color: new ComponentArguments.Enum(["dark", "bright"] as const),
+ link: new ComponentArguments.ShortText(),
+ })
+ ),
+ })
+ ),
+} as const;
+
+export class DynamicGrid extends Component<typeof component_arguments> {
+ getArguments() {
+ return component_arguments;
+ }
+
+ public image_sizes = {
+ square: { width: 400, height: 400 },
+ horizontal: { width: 824, height: 400 },
+ vertical: { width: 400, height: 824 },
+ };
+
+ renderTile(
+ jdd_context: JDDContext,
+ tile: ExtractArray<
+ ExtractArray<ExtractParsed<typeof component_arguments.tabs>>["tiles"]
+ >
+ ) {
+ return (
+ <div class={["tile", tile.shape]}>
+ <div class="tile-content">
+ <div class="tile-content-wrapper">
+ <h3 class="tile-title">{tile.title}</h3>
+ <p class="tile-subtitle">{tile.subtitle}</p>
+ </div>
+ <div class="spacer"></div>
+ </div>
+
+ {(["square", "horizontal", "vertical"] as const).map((shape) => (
+ <div class={["tile-image", `tile-image--${shape}`]}>
+ {jdd_context.render_image(tile.photo.image, {
+ sizesAttr: `${this.image_sizes[shape].width}px`,
+ alt: tile.photo.alt,
+ container: {
+ ...this.image_sizes[shape],
+ objectFit: "cover",
+ },
+ crop: this.image_sizes[shape],
+ imgStyle: "display: none;",
+ })}
+ </div>
+ ))}
+ </div>
+ );
+ }
+
+ composeTab(
+ jdd_context: JDDContext,
+ tab: ExtractArray<ExtractParsed<typeof component_arguments.tabs>>,
+ tab_id: string
+ ) {
+ return (
+ <div class="tab-container" id={tab_id}>
+ <div class="tiles-container">
+ {tab.tiles.map((tile) => this.renderTile(jdd_context, tile))}
+ </div>
+ <div class="buttons-container">
+ {tab.buttons.map((button) => (
+ <a class={["button", button.color]} href={button.link}>
+ {button.text}
+ </a>
+ ))}
+ </div>
+ </div>
+ );
+ }
+
+ toHTML(
+ {
+ heading,
+ tabs,
+ }: ExtractStructuredComponentArgumentsParsed<typeof component_arguments>,
+ jdd_context: JDDContext
+ ): FlatTemplatable {
+ const { value: id } = generate_id.next();
+ return (
+ <div class="dynamic-grid-component">
+ <h2 class="dynamic-grid-title">{heading}</h2>
+ <ul class="tabs-menu">
+ {tabs.map((tab, index) => {
+ return (
+ <li class="tabs-menu-button">
+ <label for={`input-${id}-${index}`}>{tab.name}</label>
+ {
+ /* HTML */ `<style>
+ body:has(#input-${id}-${index}:checked)
+ li:has([for="input-${id}-${index}"]) {
+ border-bottom-color: #fff;
+ border-top-color: #b721ff;
+ background: #fff;
+ color: #222;
+ }
+ </style>`
+ }
+ </li>
+ );
+ })}
+ </ul>
+ {tabs.map((tab, index) => {
+ return (
+ <section>
+ {this.composeTab(jdd_context, tab, `tab-${id}-${index}`)}
+ {
+ /* HTML */ `<style>
+ body:has(#input-${id}-${index}:checked)
+ .tab-container#tab-${id}-${index} {
+ display: block;
+ }
+ </style>`
+ }
+ <input
+ name={`tabs-menu-${id}`}
+ class="tabs-menu-radio"
+ type="radio"
+ id={`input-${id}-${index}`}
+ checked={index == 0}
+ />
+ </section>
+ );
+ })}
+ </div>
+ );
+ }
+}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 8, 03:27 (15 h, 10 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1032429
Default Alt Text
(9 KB)

Event Timeline