@bygghammar/cms-platform 0.73.0 → 0.75.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/CHANGELOG.md +12 -0
- package/package.json +2 -1
- package/src/backoffice/inspectors/blocks.tsx +97 -6
- package/src/backoffice/inspectors/index.tsx +1 -1
- package/src/blocks/components/CountdownBlock.tsx +8 -3
- package/src/blocks/countdown-typography.ts +158 -0
- package/src/blocks/index.ts +2 -0
- package/src/blocks/types.ts +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
|
+
## 0.75.0
|
|
2
|
+
|
|
3
|
+
- Countdown: pick the replacement global block from a dropdown instead of typing its key.
|
|
4
|
+
|
|
1
5
|
# Changelog
|
|
2
6
|
|
|
7
|
+
## 0.74.0
|
|
8
|
+
|
|
9
|
+
- `CountdownBlock`: typography controls for the heading, sub text and fine print
|
|
10
|
+
— size (small/medium/large/extra large), weight (normal → black), serif/sans,
|
|
11
|
+
letter spacing, capitals and line height, each resettable. Defaults reproduce
|
|
12
|
+
the previous appearance, so saved pages are unchanged.
|
|
13
|
+
|
|
14
|
+
|
|
3
15
|
## 0.73.0
|
|
4
16
|
|
|
5
17
|
- New **Countdown** block: timezone-aware deadline (IANA zone + wall clock),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bygghammar/cms-platform",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.75.0",
|
|
4
4
|
"description": "Shared CMS platform: blocks, block types, i18n core, UI primitives and design-token contract used by every Bygghammar/Voyara site.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"./host": "./src/host/index.tsx",
|
|
31
31
|
"./blocks": "./src/blocks/index.ts",
|
|
32
32
|
"./blocks/types": "./src/blocks/types.ts",
|
|
33
|
+
"./blocks/countdown-typography": "./src/blocks/countdown-typography.ts",
|
|
33
34
|
"./blocks/icons": "./src/blocks/icons.ts",
|
|
34
35
|
"./blocks/block-props-context": "./src/blocks/block-props-context.tsx",
|
|
35
36
|
"./blocks/components/*": "./src/blocks/components/*.tsx",
|
|
@@ -7,6 +7,17 @@
|
|
|
7
7
|
import { useEffect, useMemo, useState } from "react";
|
|
8
8
|
import { BUTTON_COLOR_DEFAULTS, type ButtonColors } from "../../blocks/button-colors";
|
|
9
9
|
import { COUNTDOWN_COLOR_DEFAULTS, type CountdownColors } from "../../blocks/countdown-colors";
|
|
10
|
+
import {
|
|
11
|
+
COUNTDOWN_TEXT_DEFAULTS,
|
|
12
|
+
COUNTDOWN_TEXT_FAMILY_OPTIONS,
|
|
13
|
+
COUNTDOWN_TEXT_LEADING_OPTIONS,
|
|
14
|
+
COUNTDOWN_TEXT_SIZE_OPTIONS,
|
|
15
|
+
COUNTDOWN_TEXT_TRACKING_OPTIONS,
|
|
16
|
+
COUNTDOWN_TEXT_TRANSFORM_OPTIONS,
|
|
17
|
+
COUNTDOWN_TEXT_WEIGHT_OPTIONS,
|
|
18
|
+
type CountdownTextSlot,
|
|
19
|
+
type CountdownTextStyle,
|
|
20
|
+
} from "../../blocks/countdown-typography";
|
|
10
21
|
import {
|
|
11
22
|
COUNTDOWN_DEFAULT_TIMEZONE,
|
|
12
23
|
COUNTDOWN_TIMEZONES,
|
|
@@ -2402,7 +2413,11 @@ export {
|
|
|
2402
2413
|
};
|
|
2403
2414
|
|
|
2404
2415
|
/* ---------------- Countdown ---------------- */
|
|
2405
|
-
export function CountdownInspector({
|
|
2416
|
+
export function CountdownInspector({
|
|
2417
|
+
block,
|
|
2418
|
+
patch,
|
|
2419
|
+
globalSections = [],
|
|
2420
|
+
}: BlockPatchProps & { globalSections?: { key: string; name: string }[] }) {
|
|
2406
2421
|
const p = (block.props ?? {}) as CountdownProps;
|
|
2407
2422
|
const update = (next: CountdownProps) => patch(next as unknown as BlockProps);
|
|
2408
2423
|
|
|
@@ -2467,6 +2482,47 @@ export function CountdownInspector({ block, patch }: BlockPatchProps) {
|
|
|
2467
2482
|
</label>
|
|
2468
2483
|
);
|
|
2469
2484
|
|
|
2485
|
+
const textStyle = (
|
|
2486
|
+
title: string,
|
|
2487
|
+
slot: CountdownTextSlot,
|
|
2488
|
+
key: "titleType" | "bodyType" | "noteType",
|
|
2489
|
+
) => {
|
|
2490
|
+
const current = (p[key] ?? {}) as CountdownTextStyle;
|
|
2491
|
+
const d = COUNTDOWN_TEXT_DEFAULTS[slot];
|
|
2492
|
+
const set = (patchStyle: CountdownTextStyle) =>
|
|
2493
|
+
update({ ...p, [key]: { ...current, ...patchStyle } });
|
|
2494
|
+
return (
|
|
2495
|
+
<div key={key} className="space-y-2 border border-ink/10 bg-paper/60 p-3">
|
|
2496
|
+
<p className="text-[0.7rem] uppercase tracking-[0.22em] text-ink/70">{title}</p>
|
|
2497
|
+
<div className="grid grid-cols-2 gap-2">
|
|
2498
|
+
{select("Size", current.size, d.size, COUNTDOWN_TEXT_SIZE_OPTIONS, (v) => set({ size: v }))}
|
|
2499
|
+
{select("Weight", current.weight, d.weight, COUNTDOWN_TEXT_WEIGHT_OPTIONS, (v) =>
|
|
2500
|
+
set({ weight: v }),
|
|
2501
|
+
)}
|
|
2502
|
+
{select("Style", current.family, d.family, COUNTDOWN_TEXT_FAMILY_OPTIONS, (v) =>
|
|
2503
|
+
set({ family: v }),
|
|
2504
|
+
)}
|
|
2505
|
+
{select("Letter spacing", current.tracking, d.tracking, COUNTDOWN_TEXT_TRACKING_OPTIONS, (v) =>
|
|
2506
|
+
set({ tracking: v }),
|
|
2507
|
+
)}
|
|
2508
|
+
{select("Capitals", current.transform, d.transform, COUNTDOWN_TEXT_TRANSFORM_OPTIONS, (v) =>
|
|
2509
|
+
set({ transform: v }),
|
|
2510
|
+
)}
|
|
2511
|
+
{select("Line height", current.leading, d.leading, COUNTDOWN_TEXT_LEADING_OPTIONS, (v) =>
|
|
2512
|
+
set({ leading: v }),
|
|
2513
|
+
)}
|
|
2514
|
+
</div>
|
|
2515
|
+
<button
|
|
2516
|
+
type="button"
|
|
2517
|
+
onClick={() => update({ ...p, [key]: {} })}
|
|
2518
|
+
className="text-xs uppercase tracking-[0.18em] text-ink/60 underline hover:text-ink"
|
|
2519
|
+
>
|
|
2520
|
+
Reset to default
|
|
2521
|
+
</button>
|
|
2522
|
+
</div>
|
|
2523
|
+
);
|
|
2524
|
+
};
|
|
2525
|
+
|
|
2470
2526
|
const toggle = (label: string, value: boolean, onPick: (v: boolean) => void) => (
|
|
2471
2527
|
<label className="flex items-center gap-2 text-sm text-ink">
|
|
2472
2528
|
<input type="checkbox" checked={value} onChange={(e) => onPick(e.target.checked)} />
|
|
@@ -2482,6 +2538,15 @@ export function CountdownInspector({ block, patch }: BlockPatchProps) {
|
|
|
2482
2538
|
<LocalizedField label="Fine print (under the timer)" value={p.note} onChange={(v) => update({ ...p, note: v })} />
|
|
2483
2539
|
</InspectorSection>
|
|
2484
2540
|
|
|
2541
|
+
<InspectorSection title="Typography">
|
|
2542
|
+
<p className="text-xs leading-relaxed text-ink/60">
|
|
2543
|
+
Size and weight for the three texts above and under the timer.
|
|
2544
|
+
</p>
|
|
2545
|
+
{textStyle("Heading", "heading", "titleType")}
|
|
2546
|
+
{textStyle("Sub text", "sub", "bodyType")}
|
|
2547
|
+
{textStyle("Fine print", "note", "noteType")}
|
|
2548
|
+
</InspectorSection>
|
|
2549
|
+
|
|
2485
2550
|
<InspectorSection title="Deadline">
|
|
2486
2551
|
<label className="block space-y-1.5">
|
|
2487
2552
|
<span className="block text-[0.7rem] uppercase tracking-[0.22em] text-ink/60">
|
|
@@ -2650,11 +2715,37 @@ export function CountdownInspector({ block, patch }: BlockPatchProps) {
|
|
|
2650
2715
|
(v) => update({ ...p, onExpire: v as CountdownProps["onExpire"] }),
|
|
2651
2716
|
)}
|
|
2652
2717
|
{p.onExpire === "replace" ? (
|
|
2653
|
-
|
|
2654
|
-
label="
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2718
|
+
globalSections.length ? (
|
|
2719
|
+
<label className="block space-y-1.5">
|
|
2720
|
+
<span className="block text-[0.7rem] uppercase tracking-[0.22em] text-ink/60">
|
|
2721
|
+
Global block to show instead
|
|
2722
|
+
</span>
|
|
2723
|
+
<select
|
|
2724
|
+
value={p.expiredSectionKey ?? ""}
|
|
2725
|
+
onChange={(e) => update({ ...p, expiredSectionKey: e.target.value })}
|
|
2726
|
+
className="w-full border border-ink/15 bg-white px-2.5 py-1.5 text-sm text-ink focus:border-sea focus:outline-none"
|
|
2727
|
+
>
|
|
2728
|
+
<option value="">— none —</option>
|
|
2729
|
+
{globalSections.map((g) => (
|
|
2730
|
+
<option key={g.key} value={g.key}>
|
|
2731
|
+
{g.name} ({g.key})
|
|
2732
|
+
</option>
|
|
2733
|
+
))}
|
|
2734
|
+
{p.expiredSectionKey &&
|
|
2735
|
+
!globalSections.some((g) => g.key === p.expiredSectionKey) ? (
|
|
2736
|
+
<option value={p.expiredSectionKey}>
|
|
2737
|
+
{p.expiredSectionKey} (missing)
|
|
2738
|
+
</option>
|
|
2739
|
+
) : null}
|
|
2740
|
+
</select>
|
|
2741
|
+
</label>
|
|
2742
|
+
) : (
|
|
2743
|
+
<TextField
|
|
2744
|
+
label="Global block key to show instead"
|
|
2745
|
+
value={p.expiredSectionKey ?? ""}
|
|
2746
|
+
onChange={(v) => update({ ...p, expiredSectionKey: v })}
|
|
2747
|
+
/>
|
|
2748
|
+
)
|
|
2658
2749
|
) : null}
|
|
2659
2750
|
{p.onExpire === "revealPage" ? (
|
|
2660
2751
|
<>
|
|
@@ -750,7 +750,7 @@ export function BlockInspector({
|
|
|
750
750
|
case "courseList":
|
|
751
751
|
return <TripListInspector block={block} patch={patch} />;
|
|
752
752
|
case "countdown":
|
|
753
|
-
return <CountdownInspector block={block} patch={patch} />;
|
|
753
|
+
return <CountdownInspector block={block} patch={patch} globalSections={globalSections} />;
|
|
754
754
|
case "ctaBanner":
|
|
755
755
|
return <CtaBannerInspector block={block} patch={patch} />;
|
|
756
756
|
case "mediaSplit":
|
|
@@ -5,6 +5,7 @@ import type { LocalizedString } from "../../i18n";
|
|
|
5
5
|
import { Reveal } from "../../ui/Reveal";
|
|
6
6
|
import { countdownColorStyle, countdownRadius } from "../countdown-colors";
|
|
7
7
|
import { countdownParts, resolveCountdownTarget } from "../countdown-time";
|
|
8
|
+
import { countdownTextClasses } from "../countdown-typography";
|
|
8
9
|
import { useCountdownReplacement } from "../countdown-replacement";
|
|
9
10
|
import type { CountdownProps } from "../types";
|
|
10
11
|
|
|
@@ -93,12 +94,16 @@ export function CountdownBlock({ props: p }: { props: CountdownProps }) {
|
|
|
93
94
|
const heading = (title?: Partial<LocalizedString>, body?: Partial<LocalizedString>) => (
|
|
94
95
|
<>
|
|
95
96
|
{t(title) ? (
|
|
96
|
-
<h2
|
|
97
|
+
<h2
|
|
98
|
+
className={`${countdownTextClasses("heading", p.titleType)} whitespace-pre-line text-[var(--cd-heading)]`}
|
|
99
|
+
>
|
|
97
100
|
{t(title)}
|
|
98
101
|
</h2>
|
|
99
102
|
) : null}
|
|
100
103
|
{t(body) ? (
|
|
101
|
-
<p
|
|
104
|
+
<p
|
|
105
|
+
className={`mt-4 max-w-2xl ${countdownTextClasses("sub", p.bodyType)} whitespace-pre-line text-[var(--cd-sub)]`}
|
|
106
|
+
>
|
|
102
107
|
{t(body)}
|
|
103
108
|
</p>
|
|
104
109
|
) : null}
|
|
@@ -179,7 +184,7 @@ export function CountdownBlock({ props: p }: { props: CountdownProps }) {
|
|
|
179
184
|
))}
|
|
180
185
|
</div>
|
|
181
186
|
{t(p.note) ? (
|
|
182
|
-
<p className=
|
|
187
|
+
<p className={`mt-5 ${countdownTextClasses("note", p.noteType)} text-[var(--cd-sub)]`}>
|
|
183
188
|
{t(p.note)}
|
|
184
189
|
</p>
|
|
185
190
|
) : null}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typography options for the Countdown block's texts (heading, sub text and
|
|
3
|
+
* fine print). Every value is optional; leaving a field unset keeps the
|
|
4
|
+
* original theme-driven look, so saved pages never change appearance.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export type CountdownTextSize = "sm" | "md" | "lg" | "xl";
|
|
8
|
+
export type CountdownTextWeight = "normal" | "medium" | "semibold" | "bold" | "black";
|
|
9
|
+
export type CountdownTextFamily = "serif" | "sans";
|
|
10
|
+
export type CountdownTextTracking = "tight" | "normal" | "wide" | "wider";
|
|
11
|
+
export type CountdownTextTransform = "none" | "uppercase" | "lowercase";
|
|
12
|
+
export type CountdownTextLeading = "tight" | "normal" | "relaxed";
|
|
13
|
+
|
|
14
|
+
export type CountdownTextStyle = {
|
|
15
|
+
size?: CountdownTextSize;
|
|
16
|
+
weight?: CountdownTextWeight;
|
|
17
|
+
family?: CountdownTextFamily;
|
|
18
|
+
tracking?: CountdownTextTracking;
|
|
19
|
+
transform?: CountdownTextTransform;
|
|
20
|
+
leading?: CountdownTextLeading;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type CountdownTextSlot = "heading" | "sub" | "note";
|
|
24
|
+
|
|
25
|
+
/** Responsive sizes per slot; `md` reproduces the original hardcoded size. */
|
|
26
|
+
const SIZE: Record<CountdownTextSlot, Record<CountdownTextSize, string>> = {
|
|
27
|
+
heading: {
|
|
28
|
+
sm: "text-[clamp(1.2rem,2.4vw,1.75rem)]",
|
|
29
|
+
md: "text-[clamp(1.6rem,3.6vw,2.9rem)]",
|
|
30
|
+
lg: "text-[clamp(2rem,5vw,4rem)]",
|
|
31
|
+
xl: "text-[clamp(2.4rem,6.5vw,5.5rem)]",
|
|
32
|
+
},
|
|
33
|
+
sub: {
|
|
34
|
+
sm: "text-sm md:text-base",
|
|
35
|
+
md: "text-base md:text-lg",
|
|
36
|
+
lg: "text-lg md:text-2xl",
|
|
37
|
+
xl: "text-xl md:text-3xl",
|
|
38
|
+
},
|
|
39
|
+
note: {
|
|
40
|
+
sm: "text-[0.7rem]",
|
|
41
|
+
md: "text-xs",
|
|
42
|
+
lg: "text-sm md:text-base",
|
|
43
|
+
xl: "text-base md:text-lg",
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const WEIGHT: Record<CountdownTextWeight, string> = {
|
|
48
|
+
normal: "font-normal",
|
|
49
|
+
medium: "font-medium",
|
|
50
|
+
semibold: "font-semibold",
|
|
51
|
+
bold: "font-bold",
|
|
52
|
+
black: "font-black",
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const FAMILY: Record<CountdownTextFamily, string> = {
|
|
56
|
+
serif: "font-serif",
|
|
57
|
+
sans: "font-sans",
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const TRACKING: Record<CountdownTextTracking, string> = {
|
|
61
|
+
tight: "tracking-[-0.02em]",
|
|
62
|
+
normal: "tracking-normal",
|
|
63
|
+
wide: "tracking-[0.08em]",
|
|
64
|
+
wider: "tracking-[0.22em]",
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const TRANSFORM: Record<CountdownTextTransform, string> = {
|
|
68
|
+
none: "normal-case",
|
|
69
|
+
uppercase: "uppercase",
|
|
70
|
+
lowercase: "lowercase",
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const LEADING: Record<CountdownTextLeading, string> = {
|
|
74
|
+
tight: "leading-[1.1]",
|
|
75
|
+
normal: "leading-snug",
|
|
76
|
+
relaxed: "leading-relaxed",
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/** Defaults matching the block's original appearance per slot. */
|
|
80
|
+
export const COUNTDOWN_TEXT_DEFAULTS: Record<CountdownTextSlot, Required<CountdownTextStyle>> = {
|
|
81
|
+
heading: {
|
|
82
|
+
size: "md",
|
|
83
|
+
weight: "normal",
|
|
84
|
+
family: "serif",
|
|
85
|
+
tracking: "tight",
|
|
86
|
+
transform: "none",
|
|
87
|
+
leading: "tight",
|
|
88
|
+
},
|
|
89
|
+
sub: {
|
|
90
|
+
size: "md",
|
|
91
|
+
weight: "normal",
|
|
92
|
+
family: "sans",
|
|
93
|
+
tracking: "normal",
|
|
94
|
+
transform: "none",
|
|
95
|
+
leading: "relaxed",
|
|
96
|
+
},
|
|
97
|
+
note: {
|
|
98
|
+
size: "md",
|
|
99
|
+
weight: "normal",
|
|
100
|
+
family: "sans",
|
|
101
|
+
tracking: "wider",
|
|
102
|
+
transform: "uppercase",
|
|
103
|
+
leading: "normal",
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
/** Tailwind classes for one countdown text, falling back to the slot default. */
|
|
108
|
+
export function countdownTextClasses(slot: CountdownTextSlot, style?: CountdownTextStyle): string {
|
|
109
|
+
const d = COUNTDOWN_TEXT_DEFAULTS[slot];
|
|
110
|
+
const s = style ?? {};
|
|
111
|
+
return [
|
|
112
|
+
FAMILY[s.family ?? d.family],
|
|
113
|
+
SIZE[slot][s.size ?? d.size],
|
|
114
|
+
WEIGHT[s.weight ?? d.weight],
|
|
115
|
+
TRACKING[s.tracking ?? d.tracking],
|
|
116
|
+
TRANSFORM[s.transform ?? d.transform],
|
|
117
|
+
LEADING[s.leading ?? d.leading],
|
|
118
|
+
].join(" ");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export const COUNTDOWN_TEXT_SIZE_OPTIONS: { value: CountdownTextSize; label: string }[] = [
|
|
122
|
+
{ value: "sm", label: "Small" },
|
|
123
|
+
{ value: "md", label: "Medium" },
|
|
124
|
+
{ value: "lg", label: "Large" },
|
|
125
|
+
{ value: "xl", label: "Extra large" },
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
export const COUNTDOWN_TEXT_WEIGHT_OPTIONS: { value: CountdownTextWeight; label: string }[] = [
|
|
129
|
+
{ value: "normal", label: "Normal" },
|
|
130
|
+
{ value: "medium", label: "Medium" },
|
|
131
|
+
{ value: "semibold", label: "Semibold" },
|
|
132
|
+
{ value: "bold", label: "Bold" },
|
|
133
|
+
{ value: "black", label: "Black" },
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
export const COUNTDOWN_TEXT_FAMILY_OPTIONS: { value: CountdownTextFamily; label: string }[] = [
|
|
137
|
+
{ value: "serif", label: "Serif (display)" },
|
|
138
|
+
{ value: "sans", label: "Sans (body)" },
|
|
139
|
+
];
|
|
140
|
+
|
|
141
|
+
export const COUNTDOWN_TEXT_TRACKING_OPTIONS: { value: CountdownTextTracking; label: string }[] = [
|
|
142
|
+
{ value: "tight", label: "Tight" },
|
|
143
|
+
{ value: "normal", label: "Normal" },
|
|
144
|
+
{ value: "wide", label: "Wide" },
|
|
145
|
+
{ value: "wider", label: "Extra wide" },
|
|
146
|
+
];
|
|
147
|
+
|
|
148
|
+
export const COUNTDOWN_TEXT_TRANSFORM_OPTIONS: { value: CountdownTextTransform; label: string }[] = [
|
|
149
|
+
{ value: "none", label: "As typed" },
|
|
150
|
+
{ value: "uppercase", label: "UPPERCASE" },
|
|
151
|
+
{ value: "lowercase", label: "lowercase" },
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
export const COUNTDOWN_TEXT_LEADING_OPTIONS: { value: CountdownTextLeading; label: string }[] = [
|
|
155
|
+
{ value: "tight", label: "Tight" },
|
|
156
|
+
{ value: "normal", label: "Normal" },
|
|
157
|
+
{ value: "relaxed", label: "Relaxed" },
|
|
158
|
+
];
|
package/src/blocks/index.ts
CHANGED
package/src/blocks/types.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ButtonColors } from "./button-colors";
|
|
2
2
|
import type { CountdownColors, CountdownRounding } from "./countdown-colors";
|
|
3
|
+
import type { CountdownTextStyle } from "./countdown-typography";
|
|
3
4
|
import type { LocalizedString } from "../i18n";
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -463,6 +464,11 @@ export type CountdownProps = {
|
|
|
463
464
|
/** Fine print under the timer. */
|
|
464
465
|
note?: Partial<LocalizedString>;
|
|
465
466
|
|
|
467
|
+
/** Typography for the heading, sub text and fine print. */
|
|
468
|
+
titleType?: CountdownTextStyle;
|
|
469
|
+
bodyType?: CountdownTextStyle;
|
|
470
|
+
noteType?: CountdownTextStyle;
|
|
471
|
+
|
|
466
472
|
/** Deadline as wall-clock time, `YYYY-MM-DDTHH:mm`. */
|
|
467
473
|
targetLocal?: string;
|
|
468
474
|
/** IANA timezone the deadline is expressed in, e.g. `Europe/Stockholm`. */
|