feat(ui): refine timeline and blockquote layouts
This commit is contained in:
@@ -12,6 +12,7 @@ component Blockquote {
|
||||
variant: string = "default"
|
||||
quoteMark: boolean = true
|
||||
italic: boolean = true
|
||||
maxWidth: string = "xl"
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
@@ -24,6 +25,7 @@ component Blockquote {
|
||||
data-align="{align}"
|
||||
data-variant="{variant}"
|
||||
data-italic="{italic}"
|
||||
data-max-width="{maxWidth}"
|
||||
>
|
||||
<blockquote cite="{citationUrl}">
|
||||
{#if quoteMark}
|
||||
@@ -73,10 +75,17 @@ component Blockquote {
|
||||
display: grid;
|
||||
gap: 1.25rem;
|
||||
width: 100%;
|
||||
max-width: var(--wrn-blockquote-max-width, 48rem);
|
||||
color: var(--wrn-color-text);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.wrn-next--blockquote[data-max-width="sm"] { --wrn-blockquote-max-width: 32rem; }
|
||||
.wrn-next--blockquote[data-max-width="md"] { --wrn-blockquote-max-width: 40rem; }
|
||||
.wrn-next--blockquote[data-max-width="lg"] { --wrn-blockquote-max-width: 48rem; }
|
||||
.wrn-next--blockquote[data-max-width="xl"] { --wrn-blockquote-max-width: 64rem; }
|
||||
.wrn-next--blockquote[data-max-width="full"] { --wrn-blockquote-max-width: 100%; }
|
||||
|
||||
.wrn-next--blockquote[data-size="xs"] {
|
||||
--wrn-blockquote-font-size: 0.875rem;
|
||||
--wrn-blockquote-mark-size: 3rem;
|
||||
@@ -189,8 +198,19 @@ component Blockquote {
|
||||
|
||||
.wrn-next--blockquote[data-align="center"] {
|
||||
text-align: center;
|
||||
margin-inline: auto;
|
||||
}
|
||||
|
||||
.wrn-next--blockquote:is([data-variant="contained"], [data-variant="spotlight"], [data-variant="card"]) {
|
||||
padding: clamp(1.5rem, 4vw, 3rem);
|
||||
border: 1px solid color-mix(in srgb, var(--wrn-blockquote-color) 24%, var(--wrn-color-border));
|
||||
border-radius: calc(var(--wrn-radius) * 2);
|
||||
background: linear-gradient(145deg, color-mix(in srgb, var(--wrn-blockquote-color) 8%, var(--wrn-color-surface-raised)), var(--wrn-color-surface-raised));
|
||||
box-shadow: var(--wrn-shadow-1);
|
||||
}
|
||||
|
||||
.wrn-next--blockquote[data-variant="spotlight"] > blockquote { padding-top: calc(var(--wrn-blockquote-mark-size) * 0.55); }
|
||||
|
||||
.wrn-next--blockquote[data-align="right"],
|
||||
.wrn-next--blockquote[data-align="end"] {
|
||||
text-align: right;
|
||||
|
||||
@@ -10,6 +10,7 @@ component Timeline {
|
||||
description: string = ""
|
||||
items: unknown[] = []
|
||||
variant: string = "default"
|
||||
numbered: boolean = false
|
||||
class: string = ""
|
||||
}
|
||||
|
||||
@@ -18,6 +19,9 @@ component Timeline {
|
||||
data-ui-component="Timeline"
|
||||
aria-label='{title}'
|
||||
class='wrn-timeline {class}'
|
||||
data-size='{size}'
|
||||
data-color='{color}'
|
||||
data-variant='{variant}'
|
||||
>
|
||||
{#if title || description}
|
||||
<header class="wrn-timeline__header">
|
||||
@@ -34,13 +38,18 @@ component Timeline {
|
||||
{#each items as item, index}
|
||||
<li
|
||||
class="wrn-timeline__item"
|
||||
data-interactive='{item.href ? "true" : "false"}'
|
||||
@click='output.select({ item: item, index: index })'
|
||||
>
|
||||
<span
|
||||
class="wrn-timeline__marker"
|
||||
data-status='{item.status || "default"}'
|
||||
>
|
||||
<span class='{item.icon || "icon-[lucide--circle]"}' aria-hidden="true"></span>
|
||||
{#if numbered}
|
||||
<span class="wrn-timeline__number" aria-hidden="true">{index + 1}</span>
|
||||
{:else}
|
||||
<span class='{item.icon || "icon-[lucide--circle-check]"}' aria-hidden="true"></span>
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
<article
|
||||
@@ -81,22 +90,28 @@ component Timeline {
|
||||
}
|
||||
|
||||
style {
|
||||
.wrn-timeline { width: 100%; }
|
||||
.wrn-timeline { --wrn-timeline-accent: var(--wrn-color-primary); width: 100%; min-width: 0; color: var(--wrn-color-text); }
|
||||
.wrn-timeline[data-color="success"] { --wrn-timeline-accent: var(--wrn-color-success); }
|
||||
.wrn-timeline[data-color="info"] { --wrn-timeline-accent: var(--wrn-color-info); }
|
||||
.wrn-timeline__header { margin-bottom: 1.5rem; }
|
||||
.wrn-timeline__title { color: var(--wrn-color-text); font-size: 1.25rem; font-weight: 700; }
|
||||
.wrn-timeline__description { max-width: 42rem; margin-top: 0.5rem; color: var(--wrn-color-text-muted); line-height: 1.75rem; }
|
||||
.wrn-timeline__list { position: relative; margin-left: 1rem; border-left: 1px solid var(--wrn-color-border); }
|
||||
.wrn-timeline__item { position: relative; padding: 0 0 2rem 2rem; cursor: pointer; }
|
||||
.wrn-timeline__list { position: relative; display: grid; gap: 1rem; margin: 0; padding: 0 0 0 3rem; list-style: none; }
|
||||
.wrn-timeline__list::before { position: absolute; inset-block: 1rem; inset-inline-start: 1rem; width: 2px; border-radius: 999px; background: color-mix(in srgb, var(--wrn-timeline-accent) 30%, var(--wrn-color-border)); content: ""; }
|
||||
.wrn-timeline__item { position: relative; min-width: 0; }
|
||||
.wrn-timeline__item:last-child { padding-bottom: 0; }
|
||||
.wrn-timeline__marker {
|
||||
position: absolute; top: 0; left: -1.05rem; display: flex; width: 2rem; height: 2rem;
|
||||
position: absolute; z-index: 1; top: 1rem; inset-inline-start: -3rem; display: flex; width: 2rem; height: 2rem;
|
||||
align-items: center; justify-content: center; border: 4px solid var(--wrn-color-background);
|
||||
border-radius: 50%; color: var(--wrn-color-on-primary); background: var(--wrn-color-primary); box-shadow: var(--wrn-shadow-1);
|
||||
border-radius: 50%; color: var(--wrn-color-on-primary); background: var(--wrn-timeline-accent); box-shadow: var(--wrn-shadow-1);
|
||||
}
|
||||
.wrn-timeline__marker:is([data-status="complete"], [data-status="success"]) { background: var(--wrn-color-success); }
|
||||
.wrn-timeline__marker:is([data-status="warning"], [data-status="pending"]) { background: var(--wrn-color-warning); }
|
||||
.wrn-timeline__marker:is([data-status="danger"], [data-status="failed"]) { background: var(--wrn-color-danger); }
|
||||
.wrn-timeline__card { padding: 1.25rem; border: 1px solid var(--wrn-color-border); border-radius: calc(var(--wrn-radius) * 1.5); background: var(--wrn-color-surface-raised); box-shadow: var(--wrn-shadow-1); }
|
||||
.wrn-timeline__number { font-size: 0.75rem; font-weight: 800; }
|
||||
.wrn-timeline__card { min-width: 0; padding: 1.25rem; border: 1px solid var(--wrn-color-border); border-radius: calc(var(--wrn-radius) * 1.5); background: var(--wrn-color-surface-raised); box-shadow: var(--wrn-shadow-1); transition: border-color 160ms ease, transform 160ms ease, box-shadow 160ms ease; }
|
||||
.wrn-timeline__item[data-interactive="true"] { cursor: pointer; }
|
||||
.wrn-timeline__item[data-interactive="true"]:hover .wrn-timeline__card { border-color: color-mix(in srgb, var(--wrn-timeline-accent) 45%, var(--wrn-color-border)); transform: translateY(-2px); box-shadow: var(--wrn-shadow-2); }
|
||||
.wrn-timeline__card[data-variant="minimal"] { padding: 0; border-color: transparent; background: transparent; box-shadow: none; }
|
||||
.wrn-timeline__card-header { display: flex; flex-direction: column; gap: 0.5rem; }
|
||||
.wrn-timeline__item-title { color: var(--wrn-color-text); font-weight: 700; }
|
||||
@@ -110,5 +125,11 @@ component Timeline {
|
||||
@media (min-width: 40rem) {
|
||||
.wrn-timeline__card-header { flex-direction: row; align-items: flex-start; justify-content: space-between; }
|
||||
}
|
||||
@media (max-width: 39.99rem) {
|
||||
.wrn-timeline__list { padding-inline-start: 2.5rem; }
|
||||
.wrn-timeline__list::before { inset-inline-start: 0.75rem; }
|
||||
.wrn-timeline__marker { inset-inline-start: -2.5rem; }
|
||||
.wrn-timeline__card { padding: 1rem; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,21 @@ describe("@wrnexus/ui redesign component pack", () => {
|
||||
expect(source).toContain('.wrn-hero[data-full-bleed="true"]');
|
||||
});
|
||||
|
||||
test("Timeline supports numbered data items without styling every row as interactive", async () => {
|
||||
const source = await readFile(join(componentsDir, "Timeline.wrn"), "utf8");
|
||||
expect(source).toContain("numbered: boolean = false");
|
||||
expect(source).toContain("data-interactive");
|
||||
expect(source).toContain('data-interactive="true"');
|
||||
expect(source).toContain('.wrn-timeline__item[data-interactive="true"] { cursor: pointer; }');
|
||||
});
|
||||
|
||||
test("Blockquote provides constrained contained presentation", async () => {
|
||||
const source = await readFile(join(componentsDir, "Blockquote.wrn"), "utf8");
|
||||
expect(source).toContain('maxWidth: string = "xl"');
|
||||
expect(source).toContain('data-variant="spotlight"');
|
||||
expect(source).toContain('data-max-width="full"');
|
||||
});
|
||||
|
||||
test("contains every replacement and new component", async () => {
|
||||
const files = new Set(await readdir(componentsDir));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user