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 extends Array ? 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 { + 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>["tiles"] + > + ) { + return ( +
+
+
+

{tile.title}

+

{tile.subtitle}

+
+
+
+ + {(["square", "horizontal", "vertical"] as const).map((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;", + })} +
+ ))} +
+ ); + } + + composeTab( + jdd_context: JDDContext, + tab: ExtractArray>, + tab_id: string + ) { + return ( +
+
+ {tab.tiles.map((tile) => this.renderTile(jdd_context, tile))} +
+
+ {tab.buttons.map((button) => ( + + {button.text} + + ))} +
+
+ ); + } + + toHTML( + { + heading, + tabs, + }: ExtractStructuredComponentArgumentsParsed, + jdd_context: JDDContext + ): FlatTemplatable { + const { value: id } = generate_id.next(); + return ( +
+

{heading}

+
    + {tabs.map((tab, index) => { + return ( +
  • + + { + /* HTML */ `` + } +
  • + ); + })} +
+ {tabs.map((tab, index) => { + return ( +
+ {this.composeTab(jdd_context, tab, `tab-${id}-${index}`)} + { + /* HTML */ `` + } + +
+ ); + })} +
+ ); + } +}