@diagrammo/dgmo 0.49.0 → 0.50.1
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/.cursorrules +6 -2
- package/.github/copilot-instructions.md +6 -2
- package/.windsurfrules +6 -2
- package/SKILL.md +6 -2
- package/dist/advanced.js +557 -195
- package/dist/auto.js +129 -119
- package/dist/auto.mjs +19496 -12761
- package/dist/block.js +145 -48
- package/dist/{charts-d3-EW3YQTEA.js → charts-d3-LR6D3D7A.js} +5 -5
- package/dist/{chunk-5FGDJIJX.js → chunk-2C56LEML.js} +3 -3
- package/dist/{chunk-XJ7J6WOO.js → chunk-2KGXHFYK.js} +4 -4
- package/dist/{chunk-T7IKPCMI.js → chunk-3LH5CLP4.js} +3 -3
- package/dist/{chunk-ZDUQGL3N.js → chunk-3LKM6FMW.js} +2 -2
- package/dist/{chunk-4FIV4K4Z.js → chunk-3NODSS6A.js} +7 -7
- package/dist/{chunk-4BD7EVPK.js → chunk-3P2DKJCG.js} +2 -2
- package/dist/{chunk-TC3YWBFO.js → chunk-3UIKSPFB.js} +35 -9
- package/dist/{chunk-SJEGO2QL.js → chunk-3XGZF2QJ.js} +4 -4
- package/dist/chunk-3XPUDB7B.js +420 -0
- package/dist/{chunk-O5MQPK7E.js → chunk-3Y2DRD7S.js} +3 -3
- package/dist/chunk-3YPY3OXD.js +354 -0
- package/dist/{chunk-N4J5KGWG.js → chunk-4MJ7M6YL.js} +8 -8
- package/dist/{chunk-JFPY3UJ7.js → chunk-4S52SK6G.js} +8 -8
- package/dist/{chunk-ALRHIR6I.js → chunk-5GJYGP5Z.js} +4 -4
- package/dist/{chunk-SQDFK5ZQ.js → chunk-5OWVC2A6.js} +3 -3
- package/dist/{chunk-7VLUGEMJ.js → chunk-66NVRO4K.js} +4 -4
- package/dist/{chunk-ZREZOIGS.js → chunk-66RUL3XF.js} +6 -6
- package/dist/{chunk-MJCG5K2O.js → chunk-6BFZKBGD.js} +2 -2
- package/dist/{chunk-2WMBWZY2.js → chunk-6HJBFVLO.js} +3 -3
- package/dist/{chunk-XCC2RENT.js → chunk-6IAA2QQ2.js} +2 -2
- package/dist/{chunk-B57V3DWN.js → chunk-7L3R6I3U.js} +3 -3
- package/dist/chunk-7PQYTZMA.js +681 -0
- package/dist/{chunk-6SA7FJE4.js → chunk-7QKLIMRZ.js} +10 -10
- package/dist/{chunk-YTN572Q2.js → chunk-7TV5Y53Y.js} +2 -2
- package/dist/{chunk-6CLKNXVP.js → chunk-BBGDM4NQ.js} +457 -78
- package/dist/{chunk-3NFJWFNP.js → chunk-BDXC7MDK.js} +1045 -124
- package/dist/{chunk-D5AVQCDN.js → chunk-BH3UYK36.js} +3 -3
- package/dist/chunk-BTUTZMPT.js +482 -0
- package/dist/{chunk-LVHPTBO3.js → chunk-C2CVAM3U.js} +4 -4
- package/dist/{chunk-BCT5G2VN.js → chunk-DFTFPYUD.js} +5 -5
- package/dist/{chunk-KV6Y2HE6.js → chunk-DSWVNQVS.js} +4 -4
- package/dist/{chunk-4YNP6O47.js → chunk-DZXYF2F5.js} +13 -3
- package/dist/{chunk-FUCAB3KQ.js → chunk-EAMEADTV.js} +3 -3
- package/dist/chunk-FA6K3FWM.js +940 -0
- package/dist/{chunk-HBVD67XG.js → chunk-FJUNPCYI.js} +2 -2
- package/dist/{chunk-6VE3VLWM.js → chunk-GBE54KY6.js} +6 -5
- package/dist/{chunk-YRVLP7UU.js → chunk-GKC3HILQ.js} +4 -4
- package/dist/{chunk-ZXL6MB4R.js → chunk-GL5UZ6NY.js} +3 -3
- package/dist/{chunk-GB3YZTAM.js → chunk-H73FP42Q.js} +5 -5
- package/dist/{chunk-4UDKKGHH.js → chunk-HEVYAHLW.js} +2 -2
- package/dist/{chunk-DDTJ5N7Q.js → chunk-HLJVEAOF.js} +6 -6
- package/dist/{chunk-CLR4A2N2.js → chunk-HTMN2Z6U.js} +4 -4
- package/dist/{chunk-TLKMTQYL.js → chunk-I2WKW7C7.js} +3 -3
- package/dist/{chunk-IUK7AGD4.js → chunk-I7F2A35O.js} +2 -2
- package/dist/{chunk-IBC35BTK.js → chunk-IUMPEA64.js} +2 -2
- package/dist/{chunk-5XCM7UUW.js → chunk-J6K7RP6P.js} +100 -36
- package/dist/{chunk-AJ2PEY7J.js → chunk-JJUOHGX2.js} +2 -2
- package/dist/{chunk-CEHZOPGM.js → chunk-KFZYNWWO.js} +2 -2
- package/dist/{chunk-4NQ3IKUM.js → chunk-KHEYDLMT.js} +5 -5
- package/dist/{chunk-YM2BSPYW.js → chunk-LE4MYMLI.js} +2 -2
- package/dist/{chunk-PU3AUQPA.js → chunk-M5RXSRO2.js} +2 -2
- package/dist/{chunk-XKTN7CZF.js → chunk-MCZ5LBZD.js} +4 -4
- package/dist/{chunk-35BBZDFE.js → chunk-NU3XLTGP.js} +4 -4
- package/dist/{chunk-BPIJLRYE.js → chunk-NWED7CYO.js} +3 -3
- package/dist/{chunk-YYX3SU6H.js → chunk-OMNGQPEZ.js} +257 -101
- package/dist/{chunk-BGJV5ATD.js → chunk-ONA5KPV3.js} +2 -2
- package/dist/{chunk-ZNQX6QWH.js → chunk-OTAL43EH.js} +6 -2
- package/dist/{chunk-F743CM2N.js → chunk-OTQKUHRS.js} +3 -3
- package/dist/{chunk-KUBBJ2CD.js → chunk-P7CE7LXL.js} +6 -6
- package/dist/{chunk-UVCXZEGU.js → chunk-PFHVU7UA.js} +2 -2
- package/dist/{chunk-5ZZLLBFK.js → chunk-PGEGPSCZ.js} +2 -2
- package/dist/chunk-Q4V4XQWV.js +158 -0
- package/dist/{chunk-IHLSHCPZ.js → chunk-QJP6PG7F.js} +5 -5
- package/dist/{chunk-EYN7VFKL.js → chunk-QROYX7TT.js} +3 -3
- package/dist/chunk-QXKWYZNR.js +379 -0
- package/dist/{chunk-VC3TR4NL.js → chunk-RZ6YKEUJ.js} +3 -3
- package/dist/{chunk-K6HNAY5V.js → chunk-SCHXJTZL.js} +2 -2
- package/dist/{chunk-7OVSJI2V.js → chunk-SQLNBA64.js} +3 -3
- package/dist/{chunk-65RTDUZH.js → chunk-SXRTRMEE.js} +3 -3
- package/dist/{chunk-3BIAFYD2.js → chunk-T4VOVZT5.js} +2 -2
- package/dist/{chunk-UWDO3O52.js → chunk-TN35C5KM.js} +2 -2
- package/dist/{chunk-IFK3GIUW.js → chunk-TSA237IM.js} +2 -2
- package/dist/{chunk-DKEPS5LH.js → chunk-U2HGPLZS.js} +36 -2
- package/dist/{chunk-4HVNZ7WF.js → chunk-VBYSIOOY.js} +3 -3
- package/dist/{chunk-ZTLWVRNS.js → chunk-VDOOQZUD.js} +2 -2
- package/dist/{chunk-RTERS6S5.js → chunk-VUJGMJE6.js} +4 -4
- package/dist/{chunk-X2CH42BN.js → chunk-VVW3F3MD.js} +7 -7
- package/dist/{chunk-RI4NIMFP.js → chunk-VVZRV54S.js} +75 -35
- package/dist/{chunk-LG4TWW4U.js → chunk-WW32K72E.js} +8 -8
- package/dist/{chunk-NVT6C2VE.js → chunk-X6EZPSW6.js} +4 -4
- package/dist/{chunk-KQAEKMIX.js → chunk-XDXNDSKO.js} +2 -2
- package/dist/{chunk-FMKTYMP4.js → chunk-XJLKLU4D.js} +3 -3
- package/dist/chunk-Y4RNXEAO.js +952 -0
- package/dist/{chunk-26RVGM44.js → chunk-YIA4LEX4.js} +15 -7
- package/dist/chunk-YNRDWYZP.js +295 -0
- package/dist/{chunk-SGHSY3LU.js → chunk-Z6XJQ6KO.js} +81 -6
- package/dist/{chunk-SVDWQGX2.js → chunk-ZAD4HV2G.js} +139 -78
- package/dist/cli.cjs +153 -144
- package/dist/clock.d.ts +10 -0
- package/dist/clock.js +235 -0
- package/dist/countdown.d.ts +12 -0
- package/dist/countdown.js +450 -0
- package/dist/dgmo-router-YTU3ENAX.js +84 -0
- package/dist/{dimensions-WSIJXT5Q.js → dimensions-OTBKC5M6.js} +5 -5
- package/dist/editor.js +39 -1
- package/dist/element.js +131 -121
- package/dist/element.mjs +19496 -12761
- package/dist/{flowchart-parser-IVZN7CQN.js → flowchart-parser-OE4WHLTF.js} +4 -4
- package/dist/{flowchart-renderer-GMI2U3DD.js → flowchart-renderer-GPCSMTOR.js} +6 -6
- package/dist/highlight.d.ts +17 -1
- package/dist/highlight.js +156 -5
- package/dist/index.js +50 -45
- package/dist/{layout-HQMBPJVS.js → layout-46366KBT.js} +2 -2
- package/dist/{layout-D42XE4RE.js → layout-5BG6DFCU.js} +3 -3
- package/dist/{layout-K2ZN3Q42.js → layout-7MZ5NF2N.js} +4 -4
- package/dist/{layout-4Z5JM7PT.js → layout-FWODLIMP.js} +4 -4
- package/dist/{layout-GZ2CWONU.js → layout-J36454XU.js} +4 -4
- package/dist/{layout-S3EK4IYO.js → layout-LD6XTLD6.js} +4 -4
- package/dist/{parser-CLMFZWCC.js → parser-36HVKU5U.js} +5 -5
- package/dist/{parser-MCJWF5G3.js → parser-4AI5ZN5F.js} +3 -3
- package/dist/{parser-DERPY5KC.js → parser-4NO6VZ6S.js} +4 -4
- package/dist/parser-4WQQQXCG.js +13 -0
- package/dist/{parser-4ROBKVUR.js → parser-5OXGPNBF.js} +5 -5
- package/dist/{parser-XWUDFJ7P.js → parser-AGVYTQXV.js} +4 -4
- package/dist/{parser-TITK4YW6.js → parser-AXKBDGNO.js} +5 -5
- package/dist/parser-B5FVS7O6.js +11 -0
- package/dist/parser-CSFUN3CT.js +14 -0
- package/dist/parser-CVMEDULU.js +15 -0
- package/dist/{parser-KPMN5KKE.js → parser-D5VILGN2.js} +4 -4
- package/dist/{parser-IKZFTLOJ.js → parser-DFFEDKHH.js} +3 -3
- package/dist/{parser-JD7OQ2E6.js → parser-H3XEPFPV.js} +4 -4
- package/dist/{parser-UBGPTIZD.js → parser-HYRTF3DO.js} +3 -3
- package/dist/{parser-K647FRI4.js → parser-IBWNAPY4.js} +3 -3
- package/dist/{parser-4BKIFG2C.js → parser-IJBMRXMC.js} +5 -5
- package/dist/{parser-MBJSBTL5.js → parser-JJR52VAT.js} +5 -5
- package/dist/{parser-SMBJO3XN.js → parser-K3XXLMUW.js} +4 -4
- package/dist/{parser-OBQG655V.js → parser-KCR2WS3V.js} +4 -4
- package/dist/{parser-HVJFTOXL.js → parser-LWKKBNSA.js} +3 -3
- package/dist/{parser-ZYC3XGNS.js → parser-MPCE5JBG.js} +4 -4
- package/dist/{parser-QJYS7KQQ.js → parser-MTOMTWTD.js} +4 -4
- package/dist/{parser-GFSOPDB7.js → parser-O4HIUN6G.js} +4 -4
- package/dist/{parser-BL7Z72EY.js → parser-PSA4UBSP.js} +4 -4
- package/dist/{parser-TNYX5S7E.js → parser-RV4M6AS2.js} +4 -4
- package/dist/parser-RZPVAQVX.js +11 -0
- package/dist/{parser-UGA23PYK.js → parser-TGQZJ5OT.js} +4 -4
- package/dist/parser-TXND652E.js +13 -0
- package/dist/{parser-WISPH7LW.js → parser-VKOJM7KS.js} +4 -4
- package/dist/{parser-ZKF5K2H2.js → parser-W4PQUGX3.js} +4 -4
- package/dist/{parser-MRBPXXIV.js → parser-WAV5JRPT.js} +4 -4
- package/dist/{parser-D6ONJQFR.js → parser-YS7TOS2K.js} +3 -3
- package/dist/pert.d.ts +8 -0
- package/dist/{renderer-VFUVRNLK.js → renderer-2AZ5HJFK.js} +4 -4
- package/dist/{renderer-V3JEMWMM.js → renderer-2QPMJ2DA.js} +6 -6
- package/dist/renderer-45PSOL3E.js +19 -0
- package/dist/{renderer-BB5PREMY.js → renderer-4ITCHBT7.js} +4 -4
- package/dist/{renderer-XEKIRPI6.js → renderer-5DSEWF74.js} +5 -5
- package/dist/{renderer-QNHUXCRL.js → renderer-6OKF44KS.js} +9 -9
- package/dist/renderer-72LGR2YF.js +14 -0
- package/dist/{renderer-YHXPN3GF.js → renderer-7SD6MSBS.js} +7 -7
- package/dist/renderer-A5VE5BCI.js +24 -0
- package/dist/{renderer-A5VFZQQK.js → renderer-AUW2BE6G.js} +6 -6
- package/dist/{renderer-LWGJFT3D.js → renderer-B5TBNZJE.js} +5 -5
- package/dist/{renderer-4IUPINAE.js → renderer-BSR2O2H6.js} +7 -7
- package/dist/{renderer-DKESNMU5.js → renderer-CYB4LK3G.js} +6 -6
- package/dist/{renderer-MFJ7HY2H.js → renderer-FF24KZCI.js} +5 -5
- package/dist/{renderer-CGW7QPNA.js → renderer-FPE3J5VZ.js} +7 -7
- package/dist/{renderer-6T3KK4S3.js → renderer-FYSVXL7M.js} +7 -7
- package/dist/{renderer-THD6V5TL.js → renderer-HDCCZURL.js} +9 -7
- package/dist/{renderer-TPIBXNOS.js → renderer-JCCHNECZ.js} +6 -6
- package/dist/{renderer-QL5KO6SK.js → renderer-KS7YZJE2.js} +7 -7
- package/dist/{renderer-SQ3JUAI7.js → renderer-NGXZTRK6.js} +6 -6
- package/dist/{renderer-MJDDU27W.js → renderer-ODUUOPVT.js} +10 -10
- package/dist/renderer-PJM35LJN.js +17 -0
- package/dist/{renderer-TYXRHMRI.js → renderer-PPIHO5FI.js} +9 -9
- package/dist/{renderer-W5RMOURB.js → renderer-PSMMZCKH.js} +6 -6
- package/dist/{renderer-UZF3JROD.js → renderer-QAOBLRXV.js} +8 -8
- package/dist/{renderer-ROV756WT.js → renderer-R2WNIC2C.js} +5 -5
- package/dist/{renderer-VICQ4WNY.js → renderer-SBS3YWU5.js} +5 -5
- package/dist/{renderer-6L3NBEY5.js → renderer-UYMQQCJK.js} +6 -6
- package/dist/{renderer-radial-MHO2VDXN.js → renderer-radial-F7X5F2VU.js} +6 -6
- package/dist/{state-parser-RX3GXOIB.js → state-parser-3REXRK3R.js} +4 -4
- package/dist/{state-renderer-AJUKGNWU.js → state-renderer-XYLNRWXR.js} +6 -6
- package/docs/ai-integration.md +6 -2
- package/docs/language-reference.md +253 -22
- package/gallery/fixtures/body.dgmo +17 -0
- package/gallery/fixtures/bracket.dgmo +30 -0
- package/gallery/fixtures/clock.dgmo +8 -0
- package/gallery/fixtures/countdown.dgmo +3 -0
- package/gallery/fixtures/goal.dgmo +4 -0
- package/package.json +12 -4
- package/dist/advanced.d.ts +0 -6162
- package/dist/auto.d.ts +0 -54
- package/dist/block.d.ts +0 -123
- package/dist/dgmo-router-ODKPEJ3P.js +0 -79
- package/dist/element.d.ts +0 -62
- package/dist/index.d.ts +0 -166
- package/dist/parser-667RGSUW.js +0 -13
- package/dist/parser-J3P2D6IP.js +0 -13
- package/dist/themes-1-CuKpeH.d.ts +0 -908
|
@@ -0,0 +1,952 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseInlineMarkdown,
|
|
3
|
+
renderInlineText
|
|
4
|
+
} from "./chunk-3HDBAXF5.js";
|
|
5
|
+
import {
|
|
6
|
+
wrapDescriptionLines
|
|
7
|
+
} from "./chunk-BMR4CIEO.js";
|
|
8
|
+
import {
|
|
9
|
+
DAY_MS,
|
|
10
|
+
applyOrdinalTemplate,
|
|
11
|
+
formatCompound,
|
|
12
|
+
formatCount,
|
|
13
|
+
formatDateShort,
|
|
14
|
+
formatFooter,
|
|
15
|
+
formatHuman,
|
|
16
|
+
formatWordsDetail,
|
|
17
|
+
ordinalFor
|
|
18
|
+
} from "./chunk-YNRDWYZP.js";
|
|
19
|
+
import {
|
|
20
|
+
FONT_FAMILY
|
|
21
|
+
} from "./chunk-6KFWC4CO.js";
|
|
22
|
+
import {
|
|
23
|
+
getSeriesColors,
|
|
24
|
+
mix,
|
|
25
|
+
themeBaseBg
|
|
26
|
+
} from "./chunk-P6T66CCZ.js";
|
|
27
|
+
import {
|
|
28
|
+
resolveColor
|
|
29
|
+
} from "./chunk-TLVIHFRO.js";
|
|
30
|
+
|
|
31
|
+
// src/countdown/renderer.ts
|
|
32
|
+
import * as d3Selection from "d3-selection";
|
|
33
|
+
function bakedHero(parsed, now) {
|
|
34
|
+
const resolved = parsed.resolvedMs;
|
|
35
|
+
if (resolved === null) return "\u2014";
|
|
36
|
+
const remaining = resolved - now;
|
|
37
|
+
if (!parsed.rule && remaining <= 0) {
|
|
38
|
+
if (parsed.expired !== null) return parsed.expired;
|
|
39
|
+
const elapsed = -remaining;
|
|
40
|
+
if (elapsed <= 0) return "Now!";
|
|
41
|
+
if (parsed.units === "compound" || parsed.units === "human")
|
|
42
|
+
return `${formatCompound(resolved, now)} ago`;
|
|
43
|
+
const bu = parsed.units === "full" || parsed.units === "clock" ? "days" : parsed.units;
|
|
44
|
+
return `${formatCount(elapsed, { units: bu, round: parsed.round, fields: parsed.fields })} ago`;
|
|
45
|
+
}
|
|
46
|
+
if (parsed.rule && sameLocalDay(resolved, now))
|
|
47
|
+
return parsed.onDay ?? "Today!";
|
|
48
|
+
if (parsed.units === "human") {
|
|
49
|
+
const [hn, ht] = parsed.hasTime ? [now, resolved] : [DAY_START(now), DAY_START(resolved)];
|
|
50
|
+
return formatHuman(hn, ht).big;
|
|
51
|
+
}
|
|
52
|
+
if (parsed.units === "compound") return formatCompound(now, resolved);
|
|
53
|
+
const bakeUnits = parsed.units === "full" || parsed.units === "clock" ? "days" : parsed.units;
|
|
54
|
+
return formatCount(Math.max(0, remaining), {
|
|
55
|
+
units: bakeUnits,
|
|
56
|
+
round: parsed.round,
|
|
57
|
+
fields: parsed.fields
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function sameLocalDay(a, b) {
|
|
61
|
+
const x = new Date(a);
|
|
62
|
+
const y = new Date(b);
|
|
63
|
+
return x.getFullYear() === y.getFullYear() && x.getMonth() === y.getMonth() && x.getDate() === y.getDate();
|
|
64
|
+
}
|
|
65
|
+
function stampRecur(sel, parsed) {
|
|
66
|
+
const r = parsed.rule;
|
|
67
|
+
if (!r) return;
|
|
68
|
+
sel.attr("data-dgmo-recur-kind", r.kind);
|
|
69
|
+
if (r.month !== void 0) sel.attr("data-dgmo-recur-month", r.month);
|
|
70
|
+
if (r.day !== void 0) sel.attr("data-dgmo-recur-day", r.day);
|
|
71
|
+
if (r.nth !== void 0) sel.attr("data-dgmo-recur-nth", r.nth);
|
|
72
|
+
if (r.weekday !== void 0) sel.attr("data-dgmo-recur-weekday", r.weekday);
|
|
73
|
+
sel.attr("data-dgmo-recur-hour", r.hour);
|
|
74
|
+
sel.attr("data-dgmo-recur-minute", r.minute);
|
|
75
|
+
sel.attr("data-dgmo-recur-allday", r.allDay ? "1" : "0");
|
|
76
|
+
if (r.intervalN !== void 0)
|
|
77
|
+
sel.attr("data-dgmo-recur-interval-n", r.intervalN);
|
|
78
|
+
if (r.intervalUnit !== void 0)
|
|
79
|
+
sel.attr("data-dgmo-recur-interval-unit", r.intervalUnit);
|
|
80
|
+
if (r.anchorMs !== void 0) sel.attr("data-dgmo-recur-anchor", r.anchorMs);
|
|
81
|
+
}
|
|
82
|
+
var MON_ABBR = [
|
|
83
|
+
"Jan",
|
|
84
|
+
"Feb",
|
|
85
|
+
"Mar",
|
|
86
|
+
"Apr",
|
|
87
|
+
"May",
|
|
88
|
+
"Jun",
|
|
89
|
+
"Jul",
|
|
90
|
+
"Aug",
|
|
91
|
+
"Sep",
|
|
92
|
+
"Oct",
|
|
93
|
+
"Nov",
|
|
94
|
+
"Dec"
|
|
95
|
+
];
|
|
96
|
+
var WD_ABBR = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
|
|
97
|
+
var DAY_START = (ms) => {
|
|
98
|
+
const d = new Date(ms);
|
|
99
|
+
return new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
|
|
100
|
+
};
|
|
101
|
+
var dayDelta = (a, b) => Math.round((DAY_START(b) - DAY_START(a)) / DAY_MS);
|
|
102
|
+
var keyOf = (ms) => {
|
|
103
|
+
const d = new Date(ms);
|
|
104
|
+
return d.getFullYear() * 1e4 + d.getMonth() * 100 + d.getDate();
|
|
105
|
+
};
|
|
106
|
+
function bandColors(palette, accent, muted, faint) {
|
|
107
|
+
const blue = resolveColor("blue", palette) ?? mix(palette.text, palette.bg, 45);
|
|
108
|
+
const collides = accent.toLowerCase() === blue.toLowerCase();
|
|
109
|
+
const now = collides ? resolveColor("teal", palette) ?? resolveColor("cyan", palette) ?? mix(palette.text, palette.bg, 55) : blue;
|
|
110
|
+
const mid = mix(accent, now, 50);
|
|
111
|
+
return {
|
|
112
|
+
bg: palette.bg,
|
|
113
|
+
text: palette.text,
|
|
114
|
+
muted,
|
|
115
|
+
faint,
|
|
116
|
+
rule: mix(palette.text, palette.bg, 14),
|
|
117
|
+
now,
|
|
118
|
+
event: accent,
|
|
119
|
+
midSoft: mix(mid, palette.bg, 24),
|
|
120
|
+
inert: mix(palette.text, palette.bg, 7),
|
|
121
|
+
inertBorder: mix(palette.text, palette.bg, 22)
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function roleStyle(C, role) {
|
|
125
|
+
switch (role) {
|
|
126
|
+
case "past":
|
|
127
|
+
return { fill: C.inert, stroke: C.inertBorder, text: C.faint };
|
|
128
|
+
case "future":
|
|
129
|
+
return { fill: C.inert, stroke: C.inertBorder, text: C.muted };
|
|
130
|
+
case "today":
|
|
131
|
+
return { fill: C.now, stroke: C.now, text: C.bg };
|
|
132
|
+
case "event":
|
|
133
|
+
return { fill: C.event, stroke: C.event, text: C.bg };
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function gradStyle(C, frac) {
|
|
137
|
+
const t = Math.max(0, Math.min(1, frac));
|
|
138
|
+
const f = mix(C.event, C.now, Math.round(t * 100));
|
|
139
|
+
return { fill: f, stroke: f, text: C.bg };
|
|
140
|
+
}
|
|
141
|
+
function aText(svg, x, y, size, fill, weight, anchor, txt, extra) {
|
|
142
|
+
const t = svg.append("text").attr("x", x).attr("y", y).attr("text-anchor", anchor).attr("font-size", size).attr("font-weight", weight).attr("fill", fill).attr("font-family", FONT_FAMILY).attr("font-variant-numeric", "tabular-nums").text(txt);
|
|
143
|
+
if (extra) for (const k in extra) t.attr(k, extra[k]);
|
|
144
|
+
}
|
|
145
|
+
function aRect(svg, x, y, w, h, rx, fill, stroke, sw) {
|
|
146
|
+
const r = svg.append("rect").attr("x", x).attr("y", y).attr("width", w).attr("height", h).attr("rx", rx).attr("fill", fill);
|
|
147
|
+
if (stroke) r.attr("stroke", stroke).attr("stroke-width", sw ?? 1);
|
|
148
|
+
}
|
|
149
|
+
function aLine(svg, x1, y1, x2, y2, stroke, sw, opacity) {
|
|
150
|
+
const l = svg.append("line").attr("x1", x1).attr("y1", y1).attr("x2", x2).attr("y2", y2).attr("stroke", stroke).attr("stroke-width", sw);
|
|
151
|
+
if (opacity != null) l.attr("opacity", opacity);
|
|
152
|
+
}
|
|
153
|
+
var REPEAT_ICON_PATH = "m17 2 4 4-4 4 M3 11v-1a4 4 0 0 1 4-4h14 m7 22-4-4 4-4 M21 13v1a4 4 0 0 1-4 4H3";
|
|
154
|
+
function recurGlyph(svg, cx, cy, r, color) {
|
|
155
|
+
const side = r * 2.4;
|
|
156
|
+
const scale = side / 24;
|
|
157
|
+
const swPx = Math.max(1.5, r * 0.34);
|
|
158
|
+
svg.append("path").attr("d", REPEAT_ICON_PATH).attr(
|
|
159
|
+
"transform",
|
|
160
|
+
`translate(${cx - side / 2} ${cy - side / 2}) scale(${scale})`
|
|
161
|
+
).attr("data-embed-ignore-bbox", "").attr("fill", "none").attr("stroke", color).attr("stroke-width", swPx / scale).attr("stroke-linecap", "round").attr("stroke-linejoin", "round");
|
|
162
|
+
}
|
|
163
|
+
function haloRect(svg, x, y, w, h, rx, C) {
|
|
164
|
+
aRect(svg, x - 1, y - 1, w + 2, h + 2, rx + 1, "none", C.bg, 2);
|
|
165
|
+
aRect(svg, x - 3, y - 3, w + 6, h + 6, rx + 3, "none", C.event, 2);
|
|
166
|
+
}
|
|
167
|
+
function pickBand(nowMs, resolvedMs, hasTime) {
|
|
168
|
+
const days = dayDelta(nowMs, resolvedMs);
|
|
169
|
+
if (hasTime && days <= 0) return "clock";
|
|
170
|
+
if (days > 365) return "year";
|
|
171
|
+
if (days > 92) return "months";
|
|
172
|
+
if (days > 18) return "monthgrid";
|
|
173
|
+
if (days >= 1) return "weekstrip";
|
|
174
|
+
if (days === 0) return "today";
|
|
175
|
+
return "after";
|
|
176
|
+
}
|
|
177
|
+
function monthSpan(nowMs, resolvedMs) {
|
|
178
|
+
const n = new Date(nowMs);
|
|
179
|
+
const t = new Date(resolvedMs);
|
|
180
|
+
return (t.getFullYear() - n.getFullYear()) * 12 + (t.getMonth() - n.getMonth()) + 1;
|
|
181
|
+
}
|
|
182
|
+
var MONTH_TARGET_PX = 200;
|
|
183
|
+
function monthGridLayout(nowMs, resolvedMs, contentW) {
|
|
184
|
+
const now = new Date(nowMs);
|
|
185
|
+
const ev = new Date(resolvedMs);
|
|
186
|
+
const realSpan = monthSpan(nowMs, resolvedMs);
|
|
187
|
+
const shown = Math.max(realSpan, Math.round(contentW / MONTH_TARGET_PX));
|
|
188
|
+
const padBefore = Math.floor((shown - realSpan) / 2);
|
|
189
|
+
const nowMk = now.getFullYear() * 12 + now.getMonth();
|
|
190
|
+
const evMk = ev.getFullYear() * 12 + ev.getMonth();
|
|
191
|
+
const months = [];
|
|
192
|
+
const dim = [];
|
|
193
|
+
for (let i = 0; i < shown; i++) {
|
|
194
|
+
const d = new Date(now.getFullYear(), now.getMonth() - padBefore + i, 1);
|
|
195
|
+
const mk = d.getFullYear() * 12 + d.getMonth();
|
|
196
|
+
months.push(d);
|
|
197
|
+
dim.push(mk < nowMk || mk > evMk);
|
|
198
|
+
}
|
|
199
|
+
return { months, dim };
|
|
200
|
+
}
|
|
201
|
+
function bandHeightFor(kind, nowMs, resolvedMs, contentW) {
|
|
202
|
+
const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
203
|
+
switch (kind) {
|
|
204
|
+
case "year": {
|
|
205
|
+
const R = new Date(resolvedMs).getFullYear() - new Date(nowMs).getFullYear() + 1;
|
|
206
|
+
const yearW = 66;
|
|
207
|
+
const gap = 8;
|
|
208
|
+
const cellW = (contentW - yearW - 11 * gap) / 12;
|
|
209
|
+
const cellH = clamp(cellW * 0.6, 28, 44);
|
|
210
|
+
const rowGap = 12;
|
|
211
|
+
return R * cellH + (R - 1) * rowGap;
|
|
212
|
+
}
|
|
213
|
+
case "months": {
|
|
214
|
+
const realSpan = monthSpan(nowMs, resolvedMs);
|
|
215
|
+
const perRow = 6;
|
|
216
|
+
const shown = Math.max(perRow, Math.ceil(realSpan / perRow) * perRow);
|
|
217
|
+
const rows = shown / perRow;
|
|
218
|
+
const cellW = (contentW - 16 * (perRow - 1)) / perRow;
|
|
219
|
+
const cellH = clamp(cellW * 0.5, 58, 84);
|
|
220
|
+
return rows * cellH + (rows - 1) * 16;
|
|
221
|
+
}
|
|
222
|
+
case "monthgrid": {
|
|
223
|
+
const { months } = monthGridLayout(nowMs, resolvedMs, contentW);
|
|
224
|
+
const nM = months.length;
|
|
225
|
+
const mw = (contentW - 20 * (nM - 1)) / nM;
|
|
226
|
+
const cellW = (mw - 20) / 7;
|
|
227
|
+
const cellH = clamp(cellW * 0.8, 15, 22);
|
|
228
|
+
return 26 + 6 * cellH + 8;
|
|
229
|
+
}
|
|
230
|
+
case "weekstrip": {
|
|
231
|
+
const n = Math.max(7, dayDelta(nowMs, resolvedMs) + 1);
|
|
232
|
+
const cw = (contentW - 10 * (n - 1)) / n;
|
|
233
|
+
return clamp(cw * 1.5, 116, 170);
|
|
234
|
+
}
|
|
235
|
+
case "clock":
|
|
236
|
+
return clamp(contentW * 0.26, 150, 205);
|
|
237
|
+
case "today":
|
|
238
|
+
case "after":
|
|
239
|
+
return clamp(contentW * 0.22, 125, 170);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
function vizYear(svg, g, nowMs, resolvedMs, C) {
|
|
243
|
+
const now = new Date(nowMs);
|
|
244
|
+
const ev = new Date(resolvedMs);
|
|
245
|
+
const y0 = now.getFullYear();
|
|
246
|
+
const y1 = ev.getFullYear();
|
|
247
|
+
const years = [];
|
|
248
|
+
for (let y = y0; y <= y1; y++) years.push(y);
|
|
249
|
+
const R = years.length;
|
|
250
|
+
const yearW = 66;
|
|
251
|
+
const gap = 8;
|
|
252
|
+
const rowGap = 12;
|
|
253
|
+
const rowH = (g.height - rowGap * (R - 1)) / R;
|
|
254
|
+
const cellH = rowH;
|
|
255
|
+
const cellW = (g.contentW - yearW - 11 * gap) / 12;
|
|
256
|
+
const nowY = now.getFullYear();
|
|
257
|
+
const nowM = now.getMonth();
|
|
258
|
+
const evY = ev.getFullYear();
|
|
259
|
+
const evM = ev.getMonth();
|
|
260
|
+
const nowOrd = nowY * 12 + nowM;
|
|
261
|
+
const evOrd = evY * 12 + evM;
|
|
262
|
+
years.forEach((yr, r) => {
|
|
263
|
+
const ry = g.top + r * (rowH + rowGap);
|
|
264
|
+
aText(
|
|
265
|
+
svg,
|
|
266
|
+
g.left + yearW - 12,
|
|
267
|
+
ry + rowH / 2,
|
|
268
|
+
Math.min(22, rowH * 0.6),
|
|
269
|
+
C.muted,
|
|
270
|
+
800,
|
|
271
|
+
"end",
|
|
272
|
+
String(yr),
|
|
273
|
+
{ "dominant-baseline": "central" }
|
|
274
|
+
);
|
|
275
|
+
for (let m = 0; m < 12; m++) {
|
|
276
|
+
const before = yr < nowY || yr === nowY && m < nowM;
|
|
277
|
+
const isNow = yr === nowY && m === nowM;
|
|
278
|
+
const isEv = yr === evY && m === evM;
|
|
279
|
+
const between = (yr > nowY || yr === nowY && m > nowM) && (yr < evY || yr === evY && m < evM);
|
|
280
|
+
const anchor = isEv || isNow;
|
|
281
|
+
const st = isEv ? roleStyle(C, "event") : isNow ? roleStyle(C, "today") : between ? gradStyle(C, (yr * 12 + m - nowOrd) / (evOrd - nowOrd)) : roleStyle(C, before ? "past" : "future");
|
|
282
|
+
const x = g.left + yearW + m * (cellW + gap);
|
|
283
|
+
const rx = Math.min(5, Math.min(cellW, cellH) * 0.22);
|
|
284
|
+
aRect(svg, x, ry, cellW, cellH, rx, st.fill, st.stroke, 1);
|
|
285
|
+
if (isEv) haloRect(svg, x, ry, cellW, cellH, rx, C);
|
|
286
|
+
const cx = x + cellW / 2;
|
|
287
|
+
const mFont = Math.min(11, cellW * 0.32);
|
|
288
|
+
if (anchor) {
|
|
289
|
+
aText(
|
|
290
|
+
svg,
|
|
291
|
+
cx,
|
|
292
|
+
ry + cellH * 0.36,
|
|
293
|
+
mFont,
|
|
294
|
+
st.text,
|
|
295
|
+
700,
|
|
296
|
+
"middle",
|
|
297
|
+
MON_ABBR[m],
|
|
298
|
+
{ "dominant-baseline": "central" }
|
|
299
|
+
);
|
|
300
|
+
const dnum = isEv ? ev.getDate() : now.getDate();
|
|
301
|
+
aText(
|
|
302
|
+
svg,
|
|
303
|
+
cx,
|
|
304
|
+
ry + cellH * 0.72,
|
|
305
|
+
Math.min(14, cellW * 0.42),
|
|
306
|
+
st.text,
|
|
307
|
+
800,
|
|
308
|
+
"middle",
|
|
309
|
+
String(dnum),
|
|
310
|
+
{ "dominant-baseline": "central" }
|
|
311
|
+
);
|
|
312
|
+
} else {
|
|
313
|
+
aText(
|
|
314
|
+
svg,
|
|
315
|
+
cx,
|
|
316
|
+
ry + cellH / 2,
|
|
317
|
+
mFont,
|
|
318
|
+
st.text,
|
|
319
|
+
700,
|
|
320
|
+
"middle",
|
|
321
|
+
MON_ABBR[m],
|
|
322
|
+
{ "dominant-baseline": "central" }
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
function vizMonths(svg, g, nowMs, resolvedMs, C) {
|
|
329
|
+
const now = new Date(nowMs);
|
|
330
|
+
const ev = new Date(resolvedMs);
|
|
331
|
+
const realSpan = monthSpan(nowMs, resolvedMs);
|
|
332
|
+
const perRow = 6;
|
|
333
|
+
const shown = Math.max(perRow, Math.ceil(realSpan / perRow) * perRow);
|
|
334
|
+
const padBefore = Math.floor((shown - realSpan) / 2);
|
|
335
|
+
const rows = shown / perRow;
|
|
336
|
+
const gap = 16;
|
|
337
|
+
const rowGap = 16;
|
|
338
|
+
const labelH = 27;
|
|
339
|
+
const cellW = (g.contentW - gap * (perRow - 1)) / perRow;
|
|
340
|
+
const cellH = (g.height - rowGap * (rows - 1)) / rows;
|
|
341
|
+
const nowMk = now.getFullYear() * 12 + now.getMonth();
|
|
342
|
+
const evMk = ev.getFullYear() * 12 + ev.getMonth();
|
|
343
|
+
for (let i = 0; i < shown; i++) {
|
|
344
|
+
const d = new Date(now.getFullYear(), now.getMonth() - padBefore + i, 1);
|
|
345
|
+
const mk = d.getFullYear() * 12 + d.getMonth();
|
|
346
|
+
const isNow = mk === nowMk;
|
|
347
|
+
const isEv = mk === evMk;
|
|
348
|
+
const isContext = mk < nowMk || mk > evMk;
|
|
349
|
+
const anchor = isNow || isEv;
|
|
350
|
+
const col = i % perRow;
|
|
351
|
+
const row = Math.floor(i / perRow);
|
|
352
|
+
const x = g.left + col * (cellW + gap);
|
|
353
|
+
const y = g.top + row * (cellH + rowGap);
|
|
354
|
+
const mt = isContext ? svg.append("g").attr("opacity", 0.42) : svg;
|
|
355
|
+
const st = isEv ? roleStyle(C, "event") : isNow ? roleStyle(C, "today") : mk > nowMk && mk < evMk ? gradStyle(C, (mk - nowMk) / (evMk - nowMk)) : roleStyle(C, mk < nowMk ? "past" : "future");
|
|
356
|
+
const rxM = Math.min(12, cellH * 0.14);
|
|
357
|
+
aRect(mt, x, y, cellW, cellH, rxM, st.fill, st.stroke, 1.25);
|
|
358
|
+
if (isEv) haloRect(mt, x, y, cellW, cellH, rxM, C);
|
|
359
|
+
aText(
|
|
360
|
+
mt,
|
|
361
|
+
x + cellW / 2,
|
|
362
|
+
y + 19,
|
|
363
|
+
16,
|
|
364
|
+
st.text,
|
|
365
|
+
700,
|
|
366
|
+
"middle",
|
|
367
|
+
MON_ABBR[d.getMonth()]
|
|
368
|
+
);
|
|
369
|
+
aLine(
|
|
370
|
+
mt,
|
|
371
|
+
x,
|
|
372
|
+
y + labelH,
|
|
373
|
+
x + cellW,
|
|
374
|
+
y + labelH,
|
|
375
|
+
st.text,
|
|
376
|
+
1,
|
|
377
|
+
anchor ? 0.5 : 0.3
|
|
378
|
+
);
|
|
379
|
+
if (anchor) {
|
|
380
|
+
const dnum = isEv ? ev.getDate() : now.getDate();
|
|
381
|
+
const dFont = Math.min(cellH - labelH - 6, cellW * 0.42, 44);
|
|
382
|
+
aText(
|
|
383
|
+
mt,
|
|
384
|
+
x + cellW / 2,
|
|
385
|
+
y + labelH + (cellH - labelH) / 2 + 1,
|
|
386
|
+
dFont,
|
|
387
|
+
st.text,
|
|
388
|
+
800,
|
|
389
|
+
"middle",
|
|
390
|
+
String(dnum),
|
|
391
|
+
{ "dominant-baseline": "central" }
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
function vizMonthGrid(svg, g, nowMs, resolvedMs, C) {
|
|
397
|
+
const { months, dim: isContext } = monthGridLayout(
|
|
398
|
+
nowMs,
|
|
399
|
+
resolvedMs,
|
|
400
|
+
g.contentW
|
|
401
|
+
);
|
|
402
|
+
const nM = months.length;
|
|
403
|
+
const gap = 20;
|
|
404
|
+
const mw = (g.contentW - gap * (nM - 1)) / nM;
|
|
405
|
+
const padX = 10;
|
|
406
|
+
const gridLeft = padX;
|
|
407
|
+
const gridW = mw - 2 * padX;
|
|
408
|
+
const cols = 7;
|
|
409
|
+
const cellW = gridW / cols;
|
|
410
|
+
const top = g.top;
|
|
411
|
+
const headH = 26;
|
|
412
|
+
const cellH = (g.height - headH - 8) / 6;
|
|
413
|
+
const gridH = 6 * cellH;
|
|
414
|
+
const weekendShade = mix(C.text, C.bg, 8);
|
|
415
|
+
const todayKey = keyOf(nowMs);
|
|
416
|
+
const evKey = keyOf(resolvedMs);
|
|
417
|
+
months.forEach((m, mi) => {
|
|
418
|
+
const mt = isContext[mi] ? svg.append("g").attr("opacity", 0.42) : svg;
|
|
419
|
+
const mx = g.left + mi * (mw + gap);
|
|
420
|
+
aRect(mt, mx, top, mw, g.height, 14, C.inert, C.inertBorder, 1.25);
|
|
421
|
+
const gy = top + headH;
|
|
422
|
+
for (const wcol of [0, 6])
|
|
423
|
+
aRect(
|
|
424
|
+
mt,
|
|
425
|
+
mx + gridLeft + wcol * cellW,
|
|
426
|
+
gy,
|
|
427
|
+
cellW,
|
|
428
|
+
gridH,
|
|
429
|
+
6,
|
|
430
|
+
weekendShade
|
|
431
|
+
);
|
|
432
|
+
aText(
|
|
433
|
+
mt,
|
|
434
|
+
mx + mw / 2,
|
|
435
|
+
top + 16,
|
|
436
|
+
14,
|
|
437
|
+
C.text,
|
|
438
|
+
700,
|
|
439
|
+
"middle",
|
|
440
|
+
`${MON_ABBR[m.getMonth()]} ${m.getFullYear()}`
|
|
441
|
+
);
|
|
442
|
+
aLine(mt, mx, top + headH - 6, mx + mw, top + headH - 6, C.rule, 1.25);
|
|
443
|
+
const first = new Date(m.getFullYear(), m.getMonth(), 1).getDay();
|
|
444
|
+
const dim = new Date(m.getFullYear(), m.getMonth() + 1, 0).getDate();
|
|
445
|
+
for (let day = 1; day <= dim; day++) {
|
|
446
|
+
const pos = first + day - 1;
|
|
447
|
+
const wk = Math.floor(pos / 7);
|
|
448
|
+
const dk = keyOf(new Date(m.getFullYear(), m.getMonth(), day).getTime());
|
|
449
|
+
const wd = pos % 7;
|
|
450
|
+
const cx = mx + gridLeft + wd * cellW;
|
|
451
|
+
const cy = gy + wk * cellH;
|
|
452
|
+
const isEvent = dk === evKey;
|
|
453
|
+
const isToday = dk === todayKey;
|
|
454
|
+
const between = dk > todayKey && dk < evKey;
|
|
455
|
+
const anchor = isEvent || isToday;
|
|
456
|
+
const dtMs = new Date(m.getFullYear(), m.getMonth(), day).getTime();
|
|
457
|
+
const st = isEvent ? roleStyle(C, "event") : isToday ? roleStyle(C, "today") : between ? gradStyle(C, dayDelta(nowMs, dtMs) / dayDelta(nowMs, resolvedMs)) : roleStyle(C, dk < todayKey ? "past" : "future");
|
|
458
|
+
if (anchor) {
|
|
459
|
+
const s = Math.min(cellW, cellH) * 1.02;
|
|
460
|
+
const sx = cx + (cellW - s) / 2;
|
|
461
|
+
const sy = cy + (cellH - s) / 2;
|
|
462
|
+
aRect(mt, sx, sy, s, s, 7, st.fill, st.stroke, 2);
|
|
463
|
+
if (isEvent) haloRect(mt, sx, sy, s, s, 7, C);
|
|
464
|
+
aText(
|
|
465
|
+
mt,
|
|
466
|
+
cx + cellW / 2,
|
|
467
|
+
cy + cellH / 2,
|
|
468
|
+
Math.min(16, cellH * 0.62),
|
|
469
|
+
st.text,
|
|
470
|
+
750,
|
|
471
|
+
"middle",
|
|
472
|
+
String(day),
|
|
473
|
+
{ "dominant-baseline": "central" }
|
|
474
|
+
);
|
|
475
|
+
} else {
|
|
476
|
+
const s = Math.min(cellW, cellH) * 0.5;
|
|
477
|
+
aRect(
|
|
478
|
+
mt,
|
|
479
|
+
cx + (cellW - s) / 2,
|
|
480
|
+
cy + (cellH - s) / 2,
|
|
481
|
+
s,
|
|
482
|
+
s,
|
|
483
|
+
Math.min(3, s * 0.3),
|
|
484
|
+
st.fill,
|
|
485
|
+
st.stroke,
|
|
486
|
+
1
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
function vizWeekStrip(svg, g, nowMs, resolvedMs, C) {
|
|
493
|
+
const now = new Date(nowMs);
|
|
494
|
+
const nReal = dayDelta(nowMs, resolvedMs) + 1;
|
|
495
|
+
const n = Math.max(7, nReal);
|
|
496
|
+
const padBefore = Math.floor((n - nReal) / 2);
|
|
497
|
+
const gap = n <= 7 ? 14 : n <= 12 ? 10 : 7;
|
|
498
|
+
const cw = (g.contentW - gap * (n - 1)) / n;
|
|
499
|
+
const cellTop = g.top;
|
|
500
|
+
const ch = g.height;
|
|
501
|
+
const showWd = cw >= 30;
|
|
502
|
+
const showTag = cw >= 44;
|
|
503
|
+
const nowOrd = DAY_START(nowMs);
|
|
504
|
+
const evOrd = DAY_START(resolvedMs);
|
|
505
|
+
for (let i = 0; i < n; i++) {
|
|
506
|
+
const dt = new Date(
|
|
507
|
+
now.getFullYear(),
|
|
508
|
+
now.getMonth(),
|
|
509
|
+
now.getDate() + i - padBefore
|
|
510
|
+
);
|
|
511
|
+
const dtOrd = DAY_START(dt.getTime());
|
|
512
|
+
const x = g.left + i * (cw + gap);
|
|
513
|
+
const isToday = dtOrd === nowOrd;
|
|
514
|
+
const isEvent = dtOrd === evOrd;
|
|
515
|
+
const isContext = dtOrd < nowOrd || dtOrd > evOrd;
|
|
516
|
+
const st = isEvent ? roleStyle(C, "event") : isToday ? roleStyle(C, "today") : isContext ? roleStyle(C, dtOrd < nowOrd ? "past" : "future") : gradStyle(C, (dtOrd - nowOrd) / (evOrd - nowOrd));
|
|
517
|
+
const ct = isContext ? svg.append("g").attr("opacity", 0.5) : svg;
|
|
518
|
+
const sw = isToday || isEvent ? 2 : 1.25;
|
|
519
|
+
aRect(ct, x, cellTop, cw, ch, 14, st.fill, st.stroke, sw);
|
|
520
|
+
if (isEvent) haloRect(ct, x, cellTop, cw, ch, 14, C);
|
|
521
|
+
if (showWd) {
|
|
522
|
+
aText(
|
|
523
|
+
ct,
|
|
524
|
+
x + cw / 2,
|
|
525
|
+
cellTop + 18,
|
|
526
|
+
Math.min(12, cw * 0.3),
|
|
527
|
+
st.text,
|
|
528
|
+
700,
|
|
529
|
+
"middle",
|
|
530
|
+
WD_ABBR[dt.getDay()],
|
|
531
|
+
{ "letter-spacing": "0.04em" }
|
|
532
|
+
);
|
|
533
|
+
aLine(
|
|
534
|
+
ct,
|
|
535
|
+
x,
|
|
536
|
+
cellTop + 27,
|
|
537
|
+
x + cw,
|
|
538
|
+
cellTop + 27,
|
|
539
|
+
st.text,
|
|
540
|
+
1,
|
|
541
|
+
isToday || isEvent ? 0.5 : 0.3
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
aText(
|
|
545
|
+
ct,
|
|
546
|
+
x + cw / 2,
|
|
547
|
+
cellTop + (showWd ? ch * 0.6 : ch * 0.54),
|
|
548
|
+
Math.min(44, cw * 0.62),
|
|
549
|
+
st.text,
|
|
550
|
+
750,
|
|
551
|
+
"middle",
|
|
552
|
+
String(dt.getDate())
|
|
553
|
+
);
|
|
554
|
+
if (showTag && isToday)
|
|
555
|
+
aText(
|
|
556
|
+
ct,
|
|
557
|
+
x + cw / 2,
|
|
558
|
+
cellTop + ch - 13,
|
|
559
|
+
10,
|
|
560
|
+
st.text,
|
|
561
|
+
700,
|
|
562
|
+
"middle",
|
|
563
|
+
"TODAY",
|
|
564
|
+
{ "letter-spacing": "0.04em" }
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
function vizToday(svg, g, resolvedMs, C) {
|
|
569
|
+
const ev = new Date(resolvedMs);
|
|
570
|
+
const cx = g.left + g.contentW / 2;
|
|
571
|
+
const cy = g.top + g.height / 2;
|
|
572
|
+
aLine(svg, g.left, cy, g.left + g.contentW, cy, C.rule, 2);
|
|
573
|
+
aLine(svg, cx, cy, g.left + g.contentW, cy, C.midSoft, 2);
|
|
574
|
+
const rays = 16;
|
|
575
|
+
const r0 = g.height * 0.3;
|
|
576
|
+
const r1 = g.height * 0.44;
|
|
577
|
+
for (let i = 0; i < rays; i++) {
|
|
578
|
+
const a = i / rays * Math.PI * 2;
|
|
579
|
+
svg.append("line").attr("x1", cx + Math.cos(a) * r0).attr("y1", cy + Math.sin(a) * r0).attr("x2", cx + Math.cos(a) * r1).attr("y2", cy + Math.sin(a) * r1).attr("stroke", C.event).attr("stroke-width", 2.5).attr("stroke-linecap", "round").attr("opacity", 0.45);
|
|
580
|
+
}
|
|
581
|
+
const w = Math.min(120, g.contentW * 0.16);
|
|
582
|
+
const h = g.height * 0.52;
|
|
583
|
+
aRect(svg, cx - w / 2, cy - h / 2, w, h, 18, C.event, C.event, 2);
|
|
584
|
+
aText(
|
|
585
|
+
svg,
|
|
586
|
+
cx,
|
|
587
|
+
cy - h * 0.14,
|
|
588
|
+
14,
|
|
589
|
+
C.bg,
|
|
590
|
+
700,
|
|
591
|
+
"middle",
|
|
592
|
+
MON_ABBR[ev.getMonth()].toUpperCase(),
|
|
593
|
+
{ "letter-spacing": "0.08em" }
|
|
594
|
+
);
|
|
595
|
+
aText(
|
|
596
|
+
svg,
|
|
597
|
+
cx,
|
|
598
|
+
cy + h * 0.22,
|
|
599
|
+
Math.min(48, h * 0.4),
|
|
600
|
+
C.bg,
|
|
601
|
+
800,
|
|
602
|
+
"middle",
|
|
603
|
+
String(ev.getDate())
|
|
604
|
+
);
|
|
605
|
+
aText(
|
|
606
|
+
svg,
|
|
607
|
+
cx,
|
|
608
|
+
cy + h / 2 + 26,
|
|
609
|
+
13,
|
|
610
|
+
C.event,
|
|
611
|
+
700,
|
|
612
|
+
"middle",
|
|
613
|
+
"THE DAY IS HERE",
|
|
614
|
+
{ "letter-spacing": "0.12em" }
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
function vizAfter(svg, g, nowMs, resolvedMs, C) {
|
|
618
|
+
const ev = new Date(resolvedMs);
|
|
619
|
+
const elapsed = dayDelta(resolvedMs, nowMs);
|
|
620
|
+
const n = elapsed + 1;
|
|
621
|
+
const gap = 10;
|
|
622
|
+
const cw = (g.contentW - gap * (n - 1)) / n;
|
|
623
|
+
const ch = Math.min(cw * 1.1, g.height * 0.62);
|
|
624
|
+
const top = g.top + (g.height - ch) / 2 - 8;
|
|
625
|
+
for (let i = 0; i < n; i++) {
|
|
626
|
+
const dt = new Date(ev);
|
|
627
|
+
dt.setDate(ev.getDate() + i);
|
|
628
|
+
const x = g.left + i * (cw + gap);
|
|
629
|
+
const isEvent = i === 0;
|
|
630
|
+
const isToday = i === n - 1;
|
|
631
|
+
const st = roleStyle(C, isEvent ? "event" : "past");
|
|
632
|
+
const fill = st.fill;
|
|
633
|
+
const stroke = st.stroke;
|
|
634
|
+
const tcol = st.text;
|
|
635
|
+
const sw = isEvent ? 2.5 : 1;
|
|
636
|
+
aRect(svg, x, top, cw, ch, 12, fill, stroke, sw);
|
|
637
|
+
if (isEvent) haloRect(svg, x, top, cw, ch, 12, C);
|
|
638
|
+
aText(
|
|
639
|
+
svg,
|
|
640
|
+
x + cw / 2,
|
|
641
|
+
top + ch * 0.46,
|
|
642
|
+
Math.min(34, cw * 0.34),
|
|
643
|
+
tcol,
|
|
644
|
+
isEvent ? 750 : 500,
|
|
645
|
+
"middle",
|
|
646
|
+
String(dt.getDate())
|
|
647
|
+
);
|
|
648
|
+
aText(
|
|
649
|
+
svg,
|
|
650
|
+
x + cw / 2,
|
|
651
|
+
top + ch * 0.72,
|
|
652
|
+
11,
|
|
653
|
+
tcol,
|
|
654
|
+
600,
|
|
655
|
+
"middle",
|
|
656
|
+
MON_ABBR[dt.getMonth()]
|
|
657
|
+
);
|
|
658
|
+
if (isEvent)
|
|
659
|
+
aText(
|
|
660
|
+
svg,
|
|
661
|
+
x + cw / 2,
|
|
662
|
+
top - 8,
|
|
663
|
+
11,
|
|
664
|
+
C.event,
|
|
665
|
+
700,
|
|
666
|
+
"middle",
|
|
667
|
+
"IT HAPPENED",
|
|
668
|
+
{ "letter-spacing": "0.05em" }
|
|
669
|
+
);
|
|
670
|
+
if (isToday)
|
|
671
|
+
aText(svg, x + cw / 2, top - 8, 11, C.now, 700, "middle", "TODAY", {
|
|
672
|
+
"letter-spacing": "0.05em"
|
|
673
|
+
});
|
|
674
|
+
}
|
|
675
|
+
aText(
|
|
676
|
+
svg,
|
|
677
|
+
g.left,
|
|
678
|
+
top + ch + 28,
|
|
679
|
+
14,
|
|
680
|
+
C.muted,
|
|
681
|
+
400,
|
|
682
|
+
"start",
|
|
683
|
+
`${elapsed} day${elapsed === 1 ? "" : "s"} ago \u2014 the day has passed`
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
function polar(cx, cy, r, deg) {
|
|
687
|
+
const a = (deg - 90) * Math.PI / 180;
|
|
688
|
+
return [cx + r * Math.cos(a), cy + r * Math.sin(a)];
|
|
689
|
+
}
|
|
690
|
+
function arcPath(cx, cy, r, frac) {
|
|
691
|
+
frac = Math.max(0, Math.min(0.9999, frac));
|
|
692
|
+
const [x0, y0] = polar(cx, cy, r, 0);
|
|
693
|
+
const [x1, y1] = polar(cx, cy, r, frac * 360);
|
|
694
|
+
return `M ${x0} ${y0} A ${r} ${r} 0 ${frac > 0.5 ? 1 : 0} 1 ${x1} ${y1}`;
|
|
695
|
+
}
|
|
696
|
+
function vizClock(svg, g, nowMs, targetMs, C) {
|
|
697
|
+
const ahead = targetMs >= nowMs;
|
|
698
|
+
const rem = Math.abs(targetMs - nowMs);
|
|
699
|
+
const h = Math.floor(rem / 36e5);
|
|
700
|
+
const m = Math.floor(rem % 36e5 / 6e4);
|
|
701
|
+
const s = Math.floor(rem % 6e4 / 1e3);
|
|
702
|
+
const R = Math.min(g.contentW / 3 * 0.3, g.height * 0.34);
|
|
703
|
+
const cy = g.top + g.height * 0.42;
|
|
704
|
+
const sw = Math.max(8, R * 0.17);
|
|
705
|
+
const gauges = [
|
|
706
|
+
{ frac: h % 24 / 24, val: h, label: "HOURS", col: C.event, key: "h" },
|
|
707
|
+
{ frac: m / 60, val: m, label: "MINUTES", col: C.event, key: "m" },
|
|
708
|
+
{ frac: s / 60, val: s, label: "SECONDS", col: C.event, key: "s" }
|
|
709
|
+
];
|
|
710
|
+
gauges.forEach((gg, i) => {
|
|
711
|
+
const cx = g.left + g.contentW * (i + 0.5) / 3;
|
|
712
|
+
svg.append("circle").attr("cx", cx).attr("cy", cy).attr("r", R).attr("fill", "none").attr("stroke", C.inert).attr("stroke-width", sw);
|
|
713
|
+
const arc = svg.append("path").attr("d", gg.frac > 1e-3 ? arcPath(cx, cy, R, gg.frac) : "").attr("fill", "none").attr("stroke", gg.col).attr("stroke-width", sw).attr("stroke-linecap", "round").attr("data-dgmo-gauge-arc", gg.key).attr("data-cx", cx).attr("data-cy", cy).attr("data-r", R);
|
|
714
|
+
void arc;
|
|
715
|
+
aText(
|
|
716
|
+
svg,
|
|
717
|
+
cx,
|
|
718
|
+
cy + R * 0.22,
|
|
719
|
+
R * 0.62,
|
|
720
|
+
C.text,
|
|
721
|
+
800,
|
|
722
|
+
"middle",
|
|
723
|
+
pad2(gg.val),
|
|
724
|
+
{
|
|
725
|
+
"data-dgmo-gauge-val": gg.key
|
|
726
|
+
}
|
|
727
|
+
);
|
|
728
|
+
aText(svg, cx, cy + R + 22, 12, C.muted, 700, "middle", gg.label, {
|
|
729
|
+
"letter-spacing": "0.1em"
|
|
730
|
+
});
|
|
731
|
+
});
|
|
732
|
+
const cxMid = g.left + g.contentW * 0.5;
|
|
733
|
+
aText(
|
|
734
|
+
svg,
|
|
735
|
+
cxMid,
|
|
736
|
+
cy - R - 14,
|
|
737
|
+
13,
|
|
738
|
+
ahead ? C.event : C.muted,
|
|
739
|
+
700,
|
|
740
|
+
"middle",
|
|
741
|
+
ahead ? "TO GO" : "AGO",
|
|
742
|
+
{
|
|
743
|
+
"letter-spacing": "0.12em",
|
|
744
|
+
"data-dgmo-gauge-caption": ""
|
|
745
|
+
}
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
function pad2(n) {
|
|
749
|
+
return n < 10 ? "0" + n : String(n);
|
|
750
|
+
}
|
|
751
|
+
function drawBand(svg, kind, g, nowMs, resolvedMs, C) {
|
|
752
|
+
switch (kind) {
|
|
753
|
+
case "year":
|
|
754
|
+
return vizYear(svg, g, nowMs, resolvedMs, C);
|
|
755
|
+
case "months":
|
|
756
|
+
return vizMonths(svg, g, nowMs, resolvedMs, C);
|
|
757
|
+
case "monthgrid":
|
|
758
|
+
return vizMonthGrid(svg, g, nowMs, resolvedMs, C);
|
|
759
|
+
case "weekstrip":
|
|
760
|
+
return vizWeekStrip(svg, g, nowMs, resolvedMs, C);
|
|
761
|
+
case "today":
|
|
762
|
+
return vizToday(svg, g, resolvedMs, C);
|
|
763
|
+
case "after":
|
|
764
|
+
return vizAfter(svg, g, nowMs, resolvedMs, C);
|
|
765
|
+
case "clock":
|
|
766
|
+
return vizClock(svg, g, nowMs, resolvedMs, C);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
function renderCountdown(container, parsed, palette, isDark, exportDims) {
|
|
770
|
+
d3Selection.select(container).selectAll(":not([data-d3-tooltip])").remove();
|
|
771
|
+
const width = exportDims?.width ?? container.clientWidth;
|
|
772
|
+
const height = exportDims?.height ?? container.clientHeight;
|
|
773
|
+
if (width <= 0 || height <= 0) return;
|
|
774
|
+
const now = Date.now();
|
|
775
|
+
const resolved = parsed.resolvedMs;
|
|
776
|
+
const label = parsed.sinceLabel ?? parsed.title ?? "";
|
|
777
|
+
const ordinal = parsed.since !== null && resolved !== null ? ordinalFor(resolved, parsed.since) : null;
|
|
778
|
+
const expiredFrozen = !parsed.rule && parsed.expired !== null && resolved !== null && resolved - now <= 0;
|
|
779
|
+
const bandKind = resolved === null || parsed.noVisual || expiredFrozen ? null : pickBand(now, resolved, parsed.hasTime);
|
|
780
|
+
let hero = bakedHero(parsed, now);
|
|
781
|
+
if (bandKind === "clock" && resolved !== null) {
|
|
782
|
+
const clock = formatCount(Math.abs(resolved - now), {
|
|
783
|
+
units: "clock",
|
|
784
|
+
round: parsed.round,
|
|
785
|
+
fields: parsed.fields
|
|
786
|
+
});
|
|
787
|
+
hero = resolved - now < 0 ? `${clock} ago` : clock;
|
|
788
|
+
}
|
|
789
|
+
const seriesAccent = getSeriesColors(palette)[0];
|
|
790
|
+
const defaultAccent = resolveColor("red", palette) ?? seriesAccent;
|
|
791
|
+
const manualColor = parsed.color && resolveColor(parsed.color, palette) || parsed.color || null;
|
|
792
|
+
const accent = manualColor ?? defaultAccent;
|
|
793
|
+
const muted = mix(palette.text, themeBaseBg(palette, isDark), 55);
|
|
794
|
+
const banked = resolveColor("green", palette) ?? muted;
|
|
795
|
+
const faint = mix(palette.text, themeBaseBg(palette, isDark), 72);
|
|
796
|
+
const svg = d3Selection.select(container).append("svg").attr("width", width).attr("preserveAspectRatio", "xMidYMid meet").attr("xmlns", "http://www.w3.org/2000/svg").attr("role", "img").attr("aria-label", `${parsed.title ?? "Countdown"}: ${hero}`).style("font-family", FONT_FAMILY);
|
|
797
|
+
const bgRect = svg.append("rect").attr("width", width).attr("fill", palette.bg);
|
|
798
|
+
const estWidth = (t, fs) => t.length * fs * 0.58;
|
|
799
|
+
const fitFont = (t, base, maxW, min) => {
|
|
800
|
+
const w = estWidth(t, base);
|
|
801
|
+
return w <= maxW ? base : Math.max(min, Math.floor(base * maxW / w));
|
|
802
|
+
};
|
|
803
|
+
const dispLen = (s) => parseInlineMarkdown(s).reduce((n, sp) => n + sp.text.length, 0);
|
|
804
|
+
const padX = Math.max(30, Math.round(width * 0.045));
|
|
805
|
+
const padY = Math.max(26, Math.round(width * 0.03));
|
|
806
|
+
const contentW = width - 2 * padX;
|
|
807
|
+
const leftX = padX;
|
|
808
|
+
const remainingNow = resolved === null ? 0 : Math.max(0, resolved - now);
|
|
809
|
+
const dayCount = Math.ceil(remainingNow / DAY_MS);
|
|
810
|
+
const heroSizeStr = parsed.units === "full" ? `${dayCount > 0 ? `${dayCount}d ` : ""}00:00:00` : parsed.units === "clock" ? formatCount(remainingNow, {
|
|
811
|
+
units: "clock",
|
|
812
|
+
round: parsed.round,
|
|
813
|
+
fields: parsed.fields
|
|
814
|
+
}) : hero;
|
|
815
|
+
const heroMaxW = contentW * 0.44;
|
|
816
|
+
const heroFont = fitFont(heroSizeStr, 96, heroMaxW, 26);
|
|
817
|
+
const heroW = Math.min(heroMaxW, estWidth(heroSizeStr, heroFont));
|
|
818
|
+
const gapMid = Math.max(28, Math.round(width * 0.03));
|
|
819
|
+
const leftW = Math.max(contentW * 0.42, contentW - heroW - gapMid);
|
|
820
|
+
const titleBase = 40;
|
|
821
|
+
let titleFont = 0;
|
|
822
|
+
let titleLines = [];
|
|
823
|
+
if (parsed.title) {
|
|
824
|
+
const longestWord = parsed.title.split(/\s+/).reduce((a, b) => b.length > a.length ? b : a, "");
|
|
825
|
+
titleFont = fitFont(longestWord, titleBase, leftW, 22);
|
|
826
|
+
const cpl = Math.max(6, Math.floor(leftW / (titleFont * 0.58)));
|
|
827
|
+
titleLines = wrapDescriptionLines([parsed.title], cpl).map((l) => l.text);
|
|
828
|
+
}
|
|
829
|
+
const eyebrowText = ordinal !== null ? applyOrdinalTemplate(parsed.sinceLabel ?? `Nth ${label}`, ordinal) : null;
|
|
830
|
+
const eyebrowFont = 16;
|
|
831
|
+
const footerText = resolved !== null ? formatFooter(resolved, parsed.hasTime) : null;
|
|
832
|
+
const footerFont = 17;
|
|
833
|
+
const noteFont = 19;
|
|
834
|
+
const asofFont = 13;
|
|
835
|
+
const noteCharsPerLine = Math.max(8, Math.floor(leftW / (noteFont * 0.5)));
|
|
836
|
+
const noteSource = parsed.note ? parsed.note.split("\n").map((l) => l.replace(/^[-*]\s+/, "\u2022 ")) : [];
|
|
837
|
+
const noteLines = noteSource.length ? wrapDescriptionLines(noteSource, noteCharsPerLine, dispLen) : [];
|
|
838
|
+
const bulletPx = noteFont * 0.95;
|
|
839
|
+
let y = padY;
|
|
840
|
+
const drawText = (text, yTop, fs, fill, weight) => svg.append("text").attr("x", leftX).attr("y", yTop + fs).attr("text-anchor", "start").attr("dominant-baseline", "alphabetic").attr("fill", fill).attr("font-family", FONT_FAMILY).attr("font-size", fs).attr("font-weight", weight).text(text);
|
|
841
|
+
if (parsed.title) {
|
|
842
|
+
titleLines.forEach((tl, i) => {
|
|
843
|
+
const t = drawText(tl, y, titleFont, palette.text, 700);
|
|
844
|
+
if (i === 0) t.attr("data-line-number", parsed.titleLineNumber);
|
|
845
|
+
y += i < titleLines.length - 1 ? Math.round(titleFont * 1.12) : titleFont;
|
|
846
|
+
});
|
|
847
|
+
y += Math.round(titleFont * 0.45);
|
|
848
|
+
const heroLeft = width - padX - heroW;
|
|
849
|
+
const ruleRight = Math.max(leftX + 40, heroLeft - 20);
|
|
850
|
+
svg.append("line").attr("x1", leftX).attr("x2", ruleRight).attr("y1", y).attr("y2", y).attr("stroke", mix(palette.text, palette.bg, 14)).attr("stroke-width", 1.25);
|
|
851
|
+
y += Math.round(titleFont * 0.5);
|
|
852
|
+
}
|
|
853
|
+
if (eyebrowText) {
|
|
854
|
+
drawText(eyebrowText, y, eyebrowFont, banked, 700).attr(
|
|
855
|
+
"data-dgmo-countdown-eyebrow",
|
|
856
|
+
""
|
|
857
|
+
);
|
|
858
|
+
y += eyebrowFont + 8;
|
|
859
|
+
}
|
|
860
|
+
if (footerText) {
|
|
861
|
+
const fx = parsed.rule ? leftX + 22 : leftX;
|
|
862
|
+
if (parsed.rule)
|
|
863
|
+
recurGlyph(svg, leftX + 8, y + footerFont * 0.52, 6.5, muted);
|
|
864
|
+
svg.append("text").attr("x", fx).attr("y", y + footerFont).attr("text-anchor", "start").attr("dominant-baseline", "alphabetic").attr("fill", muted).attr("font-family", FONT_FAMILY).attr("font-size", footerFont).attr("font-weight", 500).attr("data-dgmo-countdown-footer", "").text(footerText);
|
|
865
|
+
y += footerFont + 8;
|
|
866
|
+
}
|
|
867
|
+
if (noteLines.length) {
|
|
868
|
+
y += 4;
|
|
869
|
+
for (const nl of noteLines) {
|
|
870
|
+
const bx = nl.kind === "plain" ? leftX : leftX + bulletPx;
|
|
871
|
+
if (nl.kind === "bullet-first") {
|
|
872
|
+
svg.append("text").attr("x", leftX).attr("y", y + noteFont).attr("fill", muted).attr("font-family", FONT_FAMILY).attr("font-size", noteFont).text("\u2022");
|
|
873
|
+
}
|
|
874
|
+
const t = svg.append("text").attr("x", bx).attr("y", y + noteFont).attr("fill", palette.text).attr("font-family", FONT_FAMILY).attr("font-size", noteFont).attr("font-weight", 400);
|
|
875
|
+
renderInlineText(
|
|
876
|
+
t,
|
|
877
|
+
nl.text,
|
|
878
|
+
palette,
|
|
879
|
+
noteFont
|
|
880
|
+
);
|
|
881
|
+
y += Math.round(noteFont * 1.35);
|
|
882
|
+
}
|
|
883
|
+
y += 2;
|
|
884
|
+
}
|
|
885
|
+
drawText(`as of ${formatDateShort(now)}`, y, asofFont, faint, 400).attr(
|
|
886
|
+
"data-dgmo-countdown-asof",
|
|
887
|
+
""
|
|
888
|
+
);
|
|
889
|
+
y += asofFont;
|
|
890
|
+
const leftBottom = y;
|
|
891
|
+
const detailFont = 18;
|
|
892
|
+
const remainingSub = resolved !== null ? resolved - now : 0;
|
|
893
|
+
const subText = resolved === null || remainingSub <= 0 ? null : parsed.units === "words" ? formatWordsDetail(remainingSub) : parsed.units === "human" && bandKind !== "clock" ? parsed.hasTime ? formatCount(remainingSub, {
|
|
894
|
+
units: "clock",
|
|
895
|
+
round: parsed.round,
|
|
896
|
+
fields: parsed.fields
|
|
897
|
+
}) : formatHuman(DAY_START(now), DAY_START(resolved)).sub || null : null;
|
|
898
|
+
const heroCapTop = padY + 0.28 * titleFont;
|
|
899
|
+
const heroBaseline = heroCapTop + 0.72 * heroFont;
|
|
900
|
+
const heroBlockBottom = heroBaseline + (subText ? heroFont * 0.28 + detailFont : 0);
|
|
901
|
+
const contentBottom = Math.max(leftBottom, heroBlockBottom);
|
|
902
|
+
const hasBand = bandKind !== null;
|
|
903
|
+
const topSection = contentBottom;
|
|
904
|
+
const bandTop = topSection + 26;
|
|
905
|
+
const BAND_H = hasBand && resolved !== null ? Math.round(bandHeightFor(bandKind, now, resolved, width - 2 * padX)) : 0;
|
|
906
|
+
const bannerH = hasBand ? bandTop + BAND_H + padY : Math.max(
|
|
907
|
+
contentBottom + padY,
|
|
908
|
+
heroFont * 1.25 + 2 * padY,
|
|
909
|
+
Math.round(width * 0.28)
|
|
910
|
+
);
|
|
911
|
+
svg.attr("height", bannerH).attr("viewBox", `0 0 ${width} ${bannerH}`);
|
|
912
|
+
bgRect.attr("height", bannerH);
|
|
913
|
+
if (hasBand && resolved !== null) {
|
|
914
|
+
drawBand(
|
|
915
|
+
svg,
|
|
916
|
+
bandKind,
|
|
917
|
+
{ left: leftX, top: bandTop, contentW: width - 2 * padX, height: BAND_H },
|
|
918
|
+
now,
|
|
919
|
+
resolved,
|
|
920
|
+
bandColors(palette, accent, muted, faint)
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
const value = svg.append("text").attr("class", "countdown-value").attr("x", width - padX).attr("y", heroBaseline).attr("text-anchor", "end").attr("dominant-baseline", "alphabetic").attr("fill", accent).attr("font-family", FONT_FAMILY).attr("font-size", heroFont).attr("font-weight", 800).attr("data-dgmo-countdown-units", parsed.units).attr("data-dgmo-countdown-round", parsed.round).attr("data-dgmo-countdown-fields", parsed.fields.join(",")).attr("data-dgmo-countdown-expired", parsed.expired).attr("aria-label", `${parsed.title ?? "Countdown"}: ${hero}`).text(hero);
|
|
924
|
+
if (parsed.rule && resolved !== null) {
|
|
925
|
+
value.attr("data-dgmo-countdown", new Date(resolved).toISOString());
|
|
926
|
+
} else if (parsed.target) {
|
|
927
|
+
value.attr("data-dgmo-countdown", parsed.target);
|
|
928
|
+
}
|
|
929
|
+
if (parsed.title) value.attr("data-dgmo-countdown-title", parsed.title);
|
|
930
|
+
if (parsed.onDay) value.attr("data-dgmo-countdown-onday", parsed.onDay);
|
|
931
|
+
if (parsed.hasTime) value.attr("data-dgmo-countdown-hastime", "1");
|
|
932
|
+
if (parsed.since !== null) {
|
|
933
|
+
value.attr("data-dgmo-countdown-since", parsed.since);
|
|
934
|
+
value.attr(
|
|
935
|
+
"data-dgmo-countdown-since-label",
|
|
936
|
+
parsed.sinceLabel ?? `Nth ${parsed.title ?? ""}`
|
|
937
|
+
);
|
|
938
|
+
}
|
|
939
|
+
stampRecur(value, parsed);
|
|
940
|
+
if (subText) {
|
|
941
|
+
svg.append("text").attr("x", width - padX).attr("y", heroBaseline + heroFont * 0.28 + detailFont).attr("text-anchor", "end").attr("dominant-baseline", "alphabetic").attr("fill", muted).attr("font-family", FONT_FAMILY).attr("font-size", detailFont).attr("font-weight", 500).attr("data-dgmo-countdown-detail", "").text(subText);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
function renderCountdownForExport(container, parsed, palette, isDark, exportDims) {
|
|
945
|
+
renderCountdown(container, parsed, palette, isDark, exportDims);
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
export {
|
|
949
|
+
renderCountdown,
|
|
950
|
+
renderCountdownForExport
|
|
951
|
+
};
|
|
952
|
+
//# sourceMappingURL=chunk-Y4RNXEAO.js.map
|