Add JDD: Dynamic grid with tabs
Summary: Ref T2770 Reviewers: #testers, kuba-orlik Reviewed By: #testers, kuba-orlik Subscribers: kuba-orlik, jenkins-user Maniphest Tasks: T2770 Differential Revision: https://hub.sealcode.org/D1476
This commit is contained in:
parent
acef3b1e46
commit
07b115e2b7
248
src/back/jdd-components/dynamic-grid/dynamic-grid.css
Normal file
248
src/back/jdd-components/dynamic-grid/dynamic-grid.css
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
182
src/back/jdd-components/dynamic-grid/dynamic-grid.jdd.tsx
Normal file
182
src/back/jdd-components/dynamic-grid/dynamic-grid.jdd.tsx
Normal file
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user