@kwtv/components 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -0
- package/package.json +32 -0
- package/src/astro/Accordion.astro +25 -0
- package/src/astro/AccordionItem.astro +77 -0
- package/src/astro/ActiveFilterTags.astro +65 -0
- package/src/astro/Alert.astro +93 -0
- package/src/astro/Badge.astro +138 -0
- package/src/astro/Band.astro +83 -0
- package/src/astro/Breadcrumb.astro +74 -0
- package/src/astro/Button.astro +217 -0
- package/src/astro/Card.astro +115 -0
- package/src/astro/CartLine.astro +112 -0
- package/src/astro/Checkbox.astro +89 -0
- package/src/astro/Chip.astro +130 -0
- package/src/astro/ChipRail.astro +24 -0
- package/src/astro/DotGrid.astro +62 -0
- package/src/astro/EmptyState.astro +53 -0
- package/src/astro/FilterGroup.astro +58 -0
- package/src/astro/Filters.astro +84 -0
- package/src/astro/Footer.astro +237 -0
- package/src/astro/FooterColumn.astro +64 -0
- package/src/astro/Header.astro +116 -0
- package/src/astro/Icon.astro +72 -0
- package/src/astro/IconButton.astro +117 -0
- package/src/astro/Marquee.astro +72 -0
- package/src/astro/NavDropdown.astro +164 -0
- package/src/astro/NavLink.astro +94 -0
- package/src/astro/Newsletter.astro +118 -0
- package/src/astro/Pager.astro +195 -0
- package/src/astro/PickCard.astro +142 -0
- package/src/astro/ProductCard.astro +195 -0
- package/src/astro/Radio.astro +89 -0
- package/src/astro/Skeleton.astro +80 -0
- package/src/astro/Social.astro +55 -0
- package/src/astro/StepIndicator.astro +104 -0
- package/src/astro/Sticker.astro +80 -0
- package/src/astro/Swatch.astro +100 -0
- package/src/astro/SwipeCarousel.astro +14 -0
- package/src/astro/Table.astro +117 -0
- package/src/astro/TableCard.astro +63 -0
- package/src/astro/TextField.astro +231 -0
- package/src/astro/Totals.astro +92 -0
- package/src/react/Drawer.css +67 -0
- package/src/react/Drawer.tsx +115 -0
- package/src/react/Modal.css +116 -0
- package/src/react/Modal.tsx +152 -0
- package/src/react/NewsCarousel.css +86 -0
- package/src/react/NewsCarousel.tsx +97 -0
- package/src/react/QuantityStepper.css +93 -0
- package/src/react/QuantityStepper.tsx +143 -0
- package/src/react/Select.css +183 -0
- package/src/react/Select.tsx +389 -0
- package/src/react/Toast.css +140 -0
- package/src/react/Toast.tsx +157 -0
- package/src/styles/base.css +39 -0
- package/src/styles/controls.css +211 -0
- package/src/styles/motion.css +238 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Icon from "./Icon.astro";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A social link. 40 px target with the platform named in aria-label, since the
|
|
6
|
+
* mark alone carries no text.
|
|
7
|
+
*/
|
|
8
|
+
interface Props {
|
|
9
|
+
platform: "instagram" | "facebook" | "youtube";
|
|
10
|
+
href: string;
|
|
11
|
+
label?: string;
|
|
12
|
+
class?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const labels: Record<Props["platform"], string> = {
|
|
16
|
+
instagram: "Instagram",
|
|
17
|
+
facebook: "Facebook",
|
|
18
|
+
youtube: "YouTube",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const { platform, href, label, class: className, ...rest } = Astro.props;
|
|
22
|
+
const classes = ["ui-social", className].filter(Boolean).join(" ");
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
<a class={classes} href={href} aria-label={label ?? labels[platform]} {...rest}>
|
|
26
|
+
<Icon name={platform} size={18} stroke={1.7} />
|
|
27
|
+
</a>
|
|
28
|
+
|
|
29
|
+
<style>
|
|
30
|
+
.ui-social {
|
|
31
|
+
display: inline-flex;
|
|
32
|
+
align-items: center;
|
|
33
|
+
justify-content: center;
|
|
34
|
+
width: 40px;
|
|
35
|
+
height: 40px;
|
|
36
|
+
border-radius: var(--radius-md);
|
|
37
|
+
color: var(--ivory-light);
|
|
38
|
+
transition: color var(--duration-fast) var(--ease-standard),
|
|
39
|
+
background-color var(--duration-fast) var(--ease-standard);
|
|
40
|
+
}
|
|
41
|
+
.ui-social:hover {
|
|
42
|
+
color: var(--ivory-xlight);
|
|
43
|
+
background: rgba(255, 255, 255, 0.08);
|
|
44
|
+
}
|
|
45
|
+
.ui-social:focus-visible {
|
|
46
|
+
outline: 2px solid var(--ivory-xlight);
|
|
47
|
+
outline-offset: 2px;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@media (prefers-reduced-motion: reduce) {
|
|
51
|
+
.ui-social {
|
|
52
|
+
transition: none;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
</style>
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Icon from "./Icon.astro";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Step indicator. Checkout only. The nav label states the position in words
|
|
6
|
+
* because on mobile only the current step keeps its name. The current step
|
|
7
|
+
* carries aria-current="step"; dots are decorative.
|
|
8
|
+
*/
|
|
9
|
+
interface Step {
|
|
10
|
+
label: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface Props {
|
|
14
|
+
steps: Step[];
|
|
15
|
+
/** Zero-based index of the current step. Past steps render as done. */
|
|
16
|
+
current: number;
|
|
17
|
+
/** Spoken position, e.g. "Afrekenen, stap 2 van 3". */
|
|
18
|
+
label: string;
|
|
19
|
+
mobile?: boolean;
|
|
20
|
+
class?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const { steps, current, label, mobile = false, class: className, ...rest } = Astro.props;
|
|
24
|
+
|
|
25
|
+
const classes = ["ui-steps", mobile && "ui-steps--mobile", className].filter(Boolean).join(" ");
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
<nav class={classes} aria-label={label} {...rest}>
|
|
29
|
+
{steps.map((step, i) => {
|
|
30
|
+
const state = i < current ? "is-done" : i === current ? "is-current" : "";
|
|
31
|
+
return (
|
|
32
|
+
<>
|
|
33
|
+
{i > 0 && <span class="ui-steps__sep"></span>}
|
|
34
|
+
<span class={["ui-step", state].filter(Boolean).join(" ")} aria-current={i === current ? "step" : undefined}>
|
|
35
|
+
<span class="ui-step__dot" aria-hidden="true">
|
|
36
|
+
{i < current ? <Icon name="check" size={11} stroke={3} /> : i + 1}
|
|
37
|
+
</span>
|
|
38
|
+
<span class="ui-step__label">{step.label}</span>
|
|
39
|
+
</span>
|
|
40
|
+
</>
|
|
41
|
+
);
|
|
42
|
+
})}
|
|
43
|
+
</nav>
|
|
44
|
+
|
|
45
|
+
<style>
|
|
46
|
+
.ui-steps {
|
|
47
|
+
display: flex;
|
|
48
|
+
align-items: center;
|
|
49
|
+
gap: var(--space-2);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.ui-step {
|
|
53
|
+
display: inline-flex;
|
|
54
|
+
align-items: center;
|
|
55
|
+
gap: var(--space-2);
|
|
56
|
+
font-size: var(--text-sm);
|
|
57
|
+
color: var(--text-muted);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.ui-step__dot {
|
|
61
|
+
display: inline-flex;
|
|
62
|
+
align-items: center;
|
|
63
|
+
justify-content: center;
|
|
64
|
+
width: 22px;
|
|
65
|
+
height: 22px;
|
|
66
|
+
border-radius: var(--radius-full);
|
|
67
|
+
border: 1px solid var(--border);
|
|
68
|
+
font-size: var(--text-xs);
|
|
69
|
+
font-weight: var(--weight-semibold);
|
|
70
|
+
background: var(--surface);
|
|
71
|
+
color: var(--text-muted);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.ui-step.is-current {
|
|
75
|
+
color: var(--text);
|
|
76
|
+
font-weight: var(--weight-semibold);
|
|
77
|
+
}
|
|
78
|
+
.ui-step.is-current .ui-step__dot {
|
|
79
|
+
border-color: var(--primary);
|
|
80
|
+
background: var(--primary);
|
|
81
|
+
color: var(--primary-fg);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.ui-step.is-done {
|
|
85
|
+
color: var(--text);
|
|
86
|
+
}
|
|
87
|
+
.ui-step.is-done .ui-step__dot {
|
|
88
|
+
border-color: var(--primary);
|
|
89
|
+
background: var(--primary);
|
|
90
|
+
color: var(--primary-fg);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.ui-steps__sep {
|
|
94
|
+
flex: 1;
|
|
95
|
+
min-width: var(--space-4);
|
|
96
|
+
height: 1px;
|
|
97
|
+
background: var(--border);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* Mobile: only the current step keeps its label. */
|
|
101
|
+
.ui-steps--mobile .ui-step:not(.is-current) .ui-step__label {
|
|
102
|
+
display: none;
|
|
103
|
+
}
|
|
104
|
+
</style>
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
---
|
|
2
|
+
/** Decorative campaign sticker with a rotating ring and optional arrow. */
|
|
3
|
+
interface Props {
|
|
4
|
+
size?: "md" | "sm";
|
|
5
|
+
arrow?: boolean;
|
|
6
|
+
class?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { size = "md", arrow = true, class: className, ...rest } = Astro.props;
|
|
10
|
+
const classes = ["pl-sticker", `pl-sticker--${size}`, className]
|
|
11
|
+
.filter(Boolean)
|
|
12
|
+
.join(" ");
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<span class={classes} aria-hidden="true" {...rest}>
|
|
16
|
+
<svg class="pl-sticker__disc" viewBox="0 0 112 112">
|
|
17
|
+
<circle cx="56" cy="56" r="52" fill="var(--sage-xdark)"></circle>
|
|
18
|
+
<circle class="pl-sticker__ring" cx="56" cy="56" r="47" fill="none" stroke="var(--sage-light)" stroke-width="1.5" stroke-dasharray="5 7"></circle>
|
|
19
|
+
</svg>
|
|
20
|
+
<span class="pl-sticker__content">
|
|
21
|
+
<span><slot /></span>
|
|
22
|
+
{arrow && (
|
|
23
|
+
<svg class="pl-sticker__arrow" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round">
|
|
24
|
+
<path d="M12 5v14M6 13l6 6 6-6"></path>
|
|
25
|
+
</svg>
|
|
26
|
+
)}
|
|
27
|
+
</span>
|
|
28
|
+
</span>
|
|
29
|
+
|
|
30
|
+
<style>
|
|
31
|
+
.pl-sticker {
|
|
32
|
+
position: relative;
|
|
33
|
+
display: inline-block;
|
|
34
|
+
width: 112px;
|
|
35
|
+
height: 112px;
|
|
36
|
+
flex: none;
|
|
37
|
+
}
|
|
38
|
+
.pl-sticker--sm {
|
|
39
|
+
width: 92px;
|
|
40
|
+
height: 92px;
|
|
41
|
+
}
|
|
42
|
+
.pl-sticker__disc {
|
|
43
|
+
position: absolute;
|
|
44
|
+
inset: 0;
|
|
45
|
+
display: block;
|
|
46
|
+
width: 100%;
|
|
47
|
+
height: 100%;
|
|
48
|
+
}
|
|
49
|
+
.pl-sticker__ring {
|
|
50
|
+
transform-origin: 56px 56px;
|
|
51
|
+
animation: pl-spin 18s linear infinite;
|
|
52
|
+
}
|
|
53
|
+
.pl-sticker__content {
|
|
54
|
+
position: absolute;
|
|
55
|
+
inset: 0;
|
|
56
|
+
display: flex;
|
|
57
|
+
flex-direction: column;
|
|
58
|
+
align-items: center;
|
|
59
|
+
justify-content: center;
|
|
60
|
+
gap: var(--space-1);
|
|
61
|
+
color: var(--ivory-light);
|
|
62
|
+
text-align: center;
|
|
63
|
+
font-size: var(--text-xs);
|
|
64
|
+
font-weight: var(--weight-semibold);
|
|
65
|
+
line-height: 1.2;
|
|
66
|
+
letter-spacing: 0.02em;
|
|
67
|
+
}
|
|
68
|
+
.pl-sticker--sm .pl-sticker__content {
|
|
69
|
+
font-size: var(--text-2xs);
|
|
70
|
+
}
|
|
71
|
+
.pl-sticker__arrow {
|
|
72
|
+
animation: pl-bob 1.8s ease-in-out infinite;
|
|
73
|
+
}
|
|
74
|
+
@media (prefers-reduced-motion: reduce) {
|
|
75
|
+
.pl-sticker__ring,
|
|
76
|
+
.pl-sticker__arrow {
|
|
77
|
+
animation: none;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
</style>
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* Colour swatch. 30 px circle, 32 px on touch. A <button> carrying the colour
|
|
4
|
+
* name in `label` (→ aria-label), because the circle alone tells a colour-blind
|
|
5
|
+
* reader nothing. Selection adds a 2 px ring so it reads without colour.
|
|
6
|
+
*/
|
|
7
|
+
interface Props {
|
|
8
|
+
/** CSS colour value for the fill. */
|
|
9
|
+
color: string;
|
|
10
|
+
/** Colour name. Required: goes to aria-label. */
|
|
11
|
+
label: string;
|
|
12
|
+
selected?: boolean;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
type?: "button" | "submit";
|
|
15
|
+
class?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const {
|
|
19
|
+
color,
|
|
20
|
+
label,
|
|
21
|
+
selected = false,
|
|
22
|
+
disabled = false,
|
|
23
|
+
type = "button",
|
|
24
|
+
class: className,
|
|
25
|
+
...rest
|
|
26
|
+
} = Astro.props;
|
|
27
|
+
|
|
28
|
+
const classes = [
|
|
29
|
+
"ui-swatch",
|
|
30
|
+
selected && "is-selected",
|
|
31
|
+
disabled && "is-disabled",
|
|
32
|
+
className,
|
|
33
|
+
]
|
|
34
|
+
.filter(Boolean)
|
|
35
|
+
.join(" ");
|
|
36
|
+
|
|
37
|
+
const ariaLabel = selected ? `${label}, geselecteerd` : label;
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
<button
|
|
41
|
+
class={classes}
|
|
42
|
+
type={type}
|
|
43
|
+
style={`--swatch: ${color}`}
|
|
44
|
+
aria-label={ariaLabel}
|
|
45
|
+
aria-pressed={String(selected)}
|
|
46
|
+
disabled={disabled}
|
|
47
|
+
{...rest}
|
|
48
|
+
></button>
|
|
49
|
+
|
|
50
|
+
<style>
|
|
51
|
+
.ui-swatch {
|
|
52
|
+
width: 30px;
|
|
53
|
+
height: 30px;
|
|
54
|
+
padding: 0;
|
|
55
|
+
background: var(--swatch);
|
|
56
|
+
border: 1px solid var(--border);
|
|
57
|
+
border-radius: var(--radius-full);
|
|
58
|
+
cursor: pointer;
|
|
59
|
+
transition: box-shadow var(--duration-fast) var(--ease-standard);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.ui-swatch:hover,
|
|
63
|
+
.ui-swatch.is-hover {
|
|
64
|
+
box-shadow: 0 0 0 2px var(--border-strong);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.ui-swatch:focus-visible,
|
|
68
|
+
.ui-swatch.is-focus {
|
|
69
|
+
outline: 2px solid var(--focus);
|
|
70
|
+
outline-offset: 2px;
|
|
71
|
+
box-shadow: 0 0 0 4px var(--focus-outer);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* Selected: 2 px ring, readable without colour. */
|
|
75
|
+
.ui-swatch.is-selected,
|
|
76
|
+
.ui-swatch[aria-pressed="true"] {
|
|
77
|
+
box-shadow:
|
|
78
|
+
0 0 0 2px var(--surface),
|
|
79
|
+
0 0 0 4px var(--border-strong);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.ui-swatch.is-disabled,
|
|
83
|
+
.ui-swatch:disabled {
|
|
84
|
+
opacity: 0.5;
|
|
85
|
+
cursor: not-allowed;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@media (prefers-reduced-motion: reduce) {
|
|
89
|
+
.ui-swatch {
|
|
90
|
+
transition: none;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@media (max-width: 700px), (pointer: coarse) {
|
|
95
|
+
.ui-swatch {
|
|
96
|
+
width: 32px;
|
|
97
|
+
height: 32px;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
</style>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
/** Native horizontal scroll-snap rail for compact product cards. */
|
|
3
|
+
interface Props {
|
|
4
|
+
label?: string;
|
|
5
|
+
class?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { label = "Uitgelichte artikelen", class: className, ...rest } = Astro.props;
|
|
9
|
+
const classes = ["pl-carousel", className].filter(Boolean).join(" ");
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
<div class={classes} role="region" aria-label={label} tabindex="0" {...rest}>
|
|
13
|
+
<slot />
|
|
14
|
+
</div>
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* Order history and admin lists. Header cells use scope="col". Money columns
|
|
4
|
+
* get tabular numerals and align right when the column is marked numeric, via
|
|
5
|
+
* the --numeric modifier. Rows highlight on hover.
|
|
6
|
+
*
|
|
7
|
+
* Pass `columns` for the head. Put the <tr> body rows in the default slot. On
|
|
8
|
+
* mobile the same data is better shown as stacked TableCard rows, since a wide
|
|
9
|
+
* table would need horizontal scrolling: render a TableCard list alongside and
|
|
10
|
+
* toggle the two with the `.ui-table-responsive` / `.ui-tablecards` wrappers,
|
|
11
|
+
* or let the consumer switch on a breakpoint.
|
|
12
|
+
*
|
|
13
|
+
* Status is a word plus a tone (use a Badge), never a coloured dot.
|
|
14
|
+
*/
|
|
15
|
+
interface Column {
|
|
16
|
+
key: string;
|
|
17
|
+
label: string;
|
|
18
|
+
numeric?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface Props {
|
|
22
|
+
columns: Column[];
|
|
23
|
+
/** Adds tabular numerals across the table. */
|
|
24
|
+
numeric?: boolean;
|
|
25
|
+
busy?: boolean;
|
|
26
|
+
caption?: string;
|
|
27
|
+
class?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const { columns, numeric = false, busy = false, caption, class: className, ...rest } =
|
|
31
|
+
Astro.props;
|
|
32
|
+
|
|
33
|
+
const classes = ["ui-table", numeric && "ui-table--numeric", className]
|
|
34
|
+
.filter(Boolean)
|
|
35
|
+
.join(" ");
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
<table class={classes} aria-busy={busy ? "true" : undefined} {...rest}>
|
|
39
|
+
{caption && <caption class="ui-sr-only">{caption}</caption>}
|
|
40
|
+
<thead>
|
|
41
|
+
<tr>
|
|
42
|
+
{columns.map((col) => (
|
|
43
|
+
<th scope="col" class={col.numeric ? "is-numeric" : undefined}>
|
|
44
|
+
{col.label}
|
|
45
|
+
</th>
|
|
46
|
+
))}
|
|
47
|
+
</tr>
|
|
48
|
+
</thead>
|
|
49
|
+
<tbody>
|
|
50
|
+
<slot />
|
|
51
|
+
</tbody>
|
|
52
|
+
</table>
|
|
53
|
+
|
|
54
|
+
<style>
|
|
55
|
+
.ui-table {
|
|
56
|
+
width: 100%;
|
|
57
|
+
border-collapse: collapse;
|
|
58
|
+
font-family: var(--font-sans);
|
|
59
|
+
font-size: var(--text-sm);
|
|
60
|
+
color: var(--text);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.ui-table :global(th),
|
|
64
|
+
.ui-table :global(td) {
|
|
65
|
+
padding: var(--space-3) var(--space-4);
|
|
66
|
+
text-align: left;
|
|
67
|
+
border-bottom: 1px solid var(--border);
|
|
68
|
+
vertical-align: middle;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.ui-table :global(th) {
|
|
72
|
+
font-size: var(--text-xs);
|
|
73
|
+
font-weight: var(--weight-semibold);
|
|
74
|
+
color: var(--text-muted);
|
|
75
|
+
white-space: nowrap;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.ui-table :global(tbody tr) {
|
|
79
|
+
transition: background-color var(--duration-fast) var(--ease-standard);
|
|
80
|
+
}
|
|
81
|
+
.ui-table :global(tbody tr:hover) {
|
|
82
|
+
background: var(--surface-hover);
|
|
83
|
+
}
|
|
84
|
+
.ui-table :global(tbody tr:last-child td) {
|
|
85
|
+
border-bottom: 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.ui-table :global(a) {
|
|
89
|
+
color: var(--link);
|
|
90
|
+
text-decoration: none;
|
|
91
|
+
font-weight: var(--weight-medium);
|
|
92
|
+
}
|
|
93
|
+
.ui-table :global(a:hover) {
|
|
94
|
+
text-decoration: underline;
|
|
95
|
+
}
|
|
96
|
+
.ui-table :global(a:focus-visible) {
|
|
97
|
+
outline: 2px solid var(--focus);
|
|
98
|
+
outline-offset: 2px;
|
|
99
|
+
border-radius: var(--radius-sm);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* Numeric: the last column and any marked cell use tabular numerals and
|
|
103
|
+
align right. */
|
|
104
|
+
.ui-table--numeric :global(th:last-child),
|
|
105
|
+
.ui-table--numeric :global(td:last-child),
|
|
106
|
+
.ui-table :global(th.is-numeric),
|
|
107
|
+
.ui-table :global(td.is-numeric) {
|
|
108
|
+
text-align: right;
|
|
109
|
+
font-variant-numeric: tabular-nums;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@media (prefers-reduced-motion: reduce) {
|
|
113
|
+
.ui-table :global(tbody tr) {
|
|
114
|
+
transition: none;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
</style>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* The mobile stacked row for a table. Order and date on the left (`main` slot),
|
|
4
|
+
* total and status on the right (`end` slot). Repeats the label in each row
|
|
5
|
+
* rather than relying on column position. Render a list of these below 700 px
|
|
6
|
+
* in place of the wide table.
|
|
7
|
+
*/
|
|
8
|
+
interface Props {
|
|
9
|
+
busy?: boolean;
|
|
10
|
+
class?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { busy = false, class: className, ...rest } = Astro.props;
|
|
14
|
+
const classes = ["ui-tablecard", className].filter(Boolean).join(" ");
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
<div class={classes} aria-hidden={busy ? "true" : undefined} {...rest}>
|
|
18
|
+
<span class="ui-tablecard__main"><slot name="main" /></span>
|
|
19
|
+
<span class="ui-tablecard__end"><slot name="end" /></span>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<style>
|
|
23
|
+
.ui-tablecard {
|
|
24
|
+
display: flex;
|
|
25
|
+
align-items: flex-start;
|
|
26
|
+
justify-content: space-between;
|
|
27
|
+
gap: var(--space-4);
|
|
28
|
+
padding: var(--space-4) 0;
|
|
29
|
+
border-bottom: 1px solid var(--border);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.ui-tablecard__main {
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
gap: var(--space-1);
|
|
36
|
+
min-width: 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.ui-tablecard__end {
|
|
40
|
+
display: flex;
|
|
41
|
+
flex-direction: column;
|
|
42
|
+
align-items: flex-end;
|
|
43
|
+
gap: var(--space-1);
|
|
44
|
+
text-align: right;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.ui-tablecard__end :global(b) {
|
|
48
|
+
font-variant-numeric: tabular-nums;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.ui-tablecard :global(a) {
|
|
52
|
+
color: var(--link);
|
|
53
|
+
text-decoration: none;
|
|
54
|
+
}
|
|
55
|
+
.ui-tablecard :global(a:hover) {
|
|
56
|
+
text-decoration: underline;
|
|
57
|
+
}
|
|
58
|
+
.ui-tablecard :global(a:focus-visible) {
|
|
59
|
+
outline: 2px solid var(--focus);
|
|
60
|
+
outline-offset: 2px;
|
|
61
|
+
border-radius: var(--radius-sm);
|
|
62
|
+
}
|
|
63
|
+
</style>
|