@cerebro-labs/agentnava-sdk 0.53.2 → 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/README.md +3 -2
- package/agent.d.ts +57 -7
- package/agent.js +69 -9
- package/agent.js.map +1 -1
- package/attach.d.ts +3 -0
- package/attach.js.map +1 -1
- package/connection.d.ts +10 -0
- package/connection.js +13 -2
- package/connection.js.map +1 -1
- package/conversation.d.ts +37 -1
- package/conversation.js +28 -2
- package/conversation.js.map +1 -1
- package/end-user.d.ts +10 -0
- package/end-user.js +7 -0
- package/end-user.js.map +1 -1
- package/errors.d.ts +20 -0
- package/errors.js +24 -0
- package/errors.js.map +1 -1
- package/http.d.ts +4 -1
- package/http.js +21 -9
- package/http.js.map +1 -1
- package/import-tools.d.ts +5 -0
- package/import-tools.js +47 -6
- package/import-tools.js.map +1 -1
- package/index.d.ts +4 -3
- package/index.js +2 -1
- package/index.js.map +1 -1
- package/knowledge-base.d.ts +18 -18
- package/knowledge-base.js +28 -0
- package/knowledge-base.js.map +1 -1
- package/package.json +1 -1
- package/partner-app.d.ts +70 -0
- package/partner-app.js +125 -0
- package/partner-app.js.map +1 -0
- package/react.css +177 -28
- package/react.js +142 -22
- package/react.js.map +1 -1
- package/secret.d.ts +6 -0
- package/secret.js +15 -0
- package/secret.js.map +1 -1
- package/turn.js +6 -0
- package/turn.js.map +1 -1
- package/types.d.ts +144 -0
- package/types.js.map +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.js.map +1 -1
- package/workspace.d.ts +35 -1
- package/workspace.js +35 -0
- package/workspace.js.map +1 -1
package/react.css
CHANGED
|
@@ -12,17 +12,17 @@
|
|
|
12
12
|
--an-w-ink: #16181d;
|
|
13
13
|
--an-w-muted: #6b7280;
|
|
14
14
|
--an-w-line: #e5e7eb;
|
|
15
|
-
--an-w-surface: #
|
|
16
|
-
--an-w-tint: #
|
|
15
|
+
--an-w-surface: #f8f8f6;
|
|
16
|
+
--an-w-tint: #f1f0ec;
|
|
17
17
|
--an-w-accent: #1f2937;
|
|
18
18
|
--an-w-accent-ink: #ffffff;
|
|
19
|
-
--an-w-s0: #
|
|
20
|
-
--an-w-s1: #
|
|
21
|
-
--an-w-s2: #
|
|
22
|
-
--an-w-s3: #
|
|
23
|
-
--an-w-high: #
|
|
19
|
+
--an-w-s0: #2f6b55;
|
|
20
|
+
--an-w-s1: #b08a3e;
|
|
21
|
+
--an-w-s2: #4a6c8f;
|
|
22
|
+
--an-w-s3: #b4472f;
|
|
23
|
+
--an-w-high: #b4472f;
|
|
24
24
|
--an-w-medium: #d97706;
|
|
25
|
-
--an-w-good: #
|
|
25
|
+
--an-w-good: #3e7c59;
|
|
26
26
|
--an-w-info: #2563eb;
|
|
27
27
|
--an-w-radius: 8px;
|
|
28
28
|
}
|
|
@@ -87,8 +87,10 @@
|
|
|
87
87
|
.an-w-card > img {
|
|
88
88
|
display: block;
|
|
89
89
|
width: 100%;
|
|
90
|
-
aspect-ratio:
|
|
90
|
+
aspect-ratio: 1 / 1;
|
|
91
|
+
max-height: 320px;
|
|
91
92
|
object-fit: cover;
|
|
93
|
+
object-position: center 22%;
|
|
92
94
|
}
|
|
93
95
|
.an-w-card-body {
|
|
94
96
|
display: flex;
|
|
@@ -103,6 +105,35 @@
|
|
|
103
105
|
.an-w-card-link:hover .an-w-card {
|
|
104
106
|
border-color: var(--an-w-muted);
|
|
105
107
|
}
|
|
108
|
+
.an-w-card.has-action {
|
|
109
|
+
display: flex;
|
|
110
|
+
flex-direction: column;
|
|
111
|
+
}
|
|
112
|
+
.an-w-card-main {
|
|
113
|
+
display: block;
|
|
114
|
+
flex: 1;
|
|
115
|
+
color: inherit;
|
|
116
|
+
text-decoration: none;
|
|
117
|
+
}
|
|
118
|
+
.an-w-card-main > img {
|
|
119
|
+
display: block;
|
|
120
|
+
width: 100%;
|
|
121
|
+
aspect-ratio: 1 / 1;
|
|
122
|
+
max-height: 320px;
|
|
123
|
+
object-fit: cover;
|
|
124
|
+
object-position: center 22%;
|
|
125
|
+
}
|
|
126
|
+
.an-w-card-action {
|
|
127
|
+
display: block;
|
|
128
|
+
margin: 0 12px 12px;
|
|
129
|
+
padding: 8px 10px;
|
|
130
|
+
border-radius: 8px;
|
|
131
|
+
text-align: center;
|
|
132
|
+
font-weight: 600;
|
|
133
|
+
text-decoration: none;
|
|
134
|
+
background: var(--an-w-accent);
|
|
135
|
+
color: var(--an-w-accent-ink);
|
|
136
|
+
}
|
|
106
137
|
|
|
107
138
|
.an-w-text.is-title {
|
|
108
139
|
font-size: 1.15em;
|
|
@@ -125,23 +156,39 @@
|
|
|
125
156
|
.an-w-stat {
|
|
126
157
|
display: flex;
|
|
127
158
|
flex-direction: column;
|
|
128
|
-
gap:
|
|
129
|
-
padding:
|
|
130
|
-
border
|
|
131
|
-
|
|
159
|
+
gap: 4px;
|
|
160
|
+
padding: 18px 20px 16px;
|
|
161
|
+
border: 1px solid var(--an-w-line);
|
|
162
|
+
border-radius: 14px;
|
|
163
|
+
background: linear-gradient(180deg, #ffffff, var(--an-w-surface));
|
|
164
|
+
box-shadow: 0 1px 2px rgba(16, 16, 16, 0.04), 0 6px 18px rgba(16, 16, 16, 0.05);
|
|
132
165
|
}
|
|
133
|
-
.an-w-stat
|
|
134
|
-
font-size: 1.25em;
|
|
166
|
+
.an-w-stat span {
|
|
135
167
|
font-weight: 600;
|
|
136
|
-
font-
|
|
168
|
+
font-size: 0.95em;
|
|
169
|
+
color: var(--an-w-ink);
|
|
137
170
|
}
|
|
138
|
-
.an-w-stat
|
|
139
|
-
font-
|
|
171
|
+
.an-w-stat strong {
|
|
172
|
+
font-size: 2.1em;
|
|
173
|
+
line-height: 1.1;
|
|
174
|
+
font-weight: 700;
|
|
175
|
+
letter-spacing: -0.03em;
|
|
176
|
+
color: var(--an-w-ink);
|
|
177
|
+
font-variant-numeric: tabular-nums;
|
|
140
178
|
}
|
|
141
179
|
.an-w-stat small {
|
|
142
180
|
color: var(--an-w-muted);
|
|
143
|
-
font-size: 0.
|
|
144
|
-
line-height: 1.
|
|
181
|
+
font-size: 0.9em;
|
|
182
|
+
line-height: 1.35;
|
|
183
|
+
}
|
|
184
|
+
.an-w-stat small b {
|
|
185
|
+
font-weight: 600;
|
|
186
|
+
}
|
|
187
|
+
.an-w-stat small b.is-up {
|
|
188
|
+
color: var(--an-w-good);
|
|
189
|
+
}
|
|
190
|
+
.an-w-stat small b.is-down {
|
|
191
|
+
color: var(--an-w-high);
|
|
145
192
|
}
|
|
146
193
|
|
|
147
194
|
.an-w-kv dl > div {
|
|
@@ -169,6 +216,12 @@
|
|
|
169
216
|
|
|
170
217
|
.an-w-table {
|
|
171
218
|
overflow-x: auto;
|
|
219
|
+
border: 1px solid var(--an-w-line);
|
|
220
|
+
border-radius: 12px;
|
|
221
|
+
background: #fff;
|
|
222
|
+
}
|
|
223
|
+
.an-w-table > .an-w-title {
|
|
224
|
+
padding: 10px 12px 0;
|
|
172
225
|
}
|
|
173
226
|
.an-w-table table {
|
|
174
227
|
border-collapse: collapse;
|
|
@@ -177,17 +230,24 @@
|
|
|
177
230
|
.an-w-table th,
|
|
178
231
|
.an-w-table td {
|
|
179
232
|
text-align: left;
|
|
180
|
-
padding:
|
|
233
|
+
padding: 9px 12px;
|
|
181
234
|
border-bottom: 1px solid var(--an-w-line);
|
|
182
|
-
|
|
235
|
+
vertical-align: top;
|
|
236
|
+
}
|
|
237
|
+
.an-w-table tbody tr:last-child td {
|
|
238
|
+
border-bottom: 0;
|
|
183
239
|
}
|
|
184
240
|
.an-w-table th {
|
|
185
|
-
color: var(--an-w-
|
|
186
|
-
font-weight:
|
|
241
|
+
color: var(--an-w-ink);
|
|
242
|
+
font-weight: 600;
|
|
243
|
+
background: var(--an-w-surface);
|
|
244
|
+
white-space: nowrap;
|
|
187
245
|
}
|
|
246
|
+
.an-w-table th.is-num,
|
|
188
247
|
.an-w-table td.is-num {
|
|
189
248
|
text-align: right;
|
|
190
249
|
font-variant-numeric: tabular-nums;
|
|
250
|
+
white-space: nowrap;
|
|
191
251
|
}
|
|
192
252
|
|
|
193
253
|
.an-w-chart figcaption {
|
|
@@ -196,16 +256,103 @@
|
|
|
196
256
|
font-weight: 500;
|
|
197
257
|
margin-bottom: 6px;
|
|
198
258
|
}
|
|
259
|
+
.an-w-chart-head {
|
|
260
|
+
display: flex;
|
|
261
|
+
align-items: baseline;
|
|
262
|
+
flex-wrap: wrap;
|
|
263
|
+
gap: 4px 10px;
|
|
264
|
+
margin: -2px 0 6px;
|
|
265
|
+
}
|
|
266
|
+
.an-w-chart-head strong {
|
|
267
|
+
font-size: 1.9em;
|
|
268
|
+
font-weight: 700;
|
|
269
|
+
letter-spacing: -0.03em;
|
|
270
|
+
color: var(--an-w-ink);
|
|
271
|
+
font-variant-numeric: tabular-nums;
|
|
272
|
+
}
|
|
273
|
+
.an-w-chart-head span {
|
|
274
|
+
color: var(--an-w-muted);
|
|
275
|
+
font-size: 0.9em;
|
|
276
|
+
font-variant-numeric: tabular-nums;
|
|
277
|
+
}
|
|
278
|
+
.an-w-chart-box {
|
|
279
|
+
position: relative;
|
|
280
|
+
width: 100%;
|
|
281
|
+
}
|
|
199
282
|
.an-w-chart svg {
|
|
200
283
|
display: block;
|
|
201
|
-
width: 100%;
|
|
202
|
-
height: auto;
|
|
284
|
+
max-width: 100%;
|
|
203
285
|
overflow: visible;
|
|
204
286
|
}
|
|
205
287
|
.an-w-chart text {
|
|
206
|
-
font-size:
|
|
288
|
+
font-size: 11px;
|
|
207
289
|
fill: var(--an-w-muted);
|
|
290
|
+
font-variant-numeric: tabular-nums;
|
|
291
|
+
}
|
|
292
|
+
.an-w-chart text.an-w-val {
|
|
293
|
+
fill: var(--an-w-ink);
|
|
294
|
+
font-weight: 500;
|
|
295
|
+
}
|
|
296
|
+
.an-w-grid line {
|
|
297
|
+
stroke: var(--an-w-line);
|
|
298
|
+
stroke-width: 1;
|
|
299
|
+
}
|
|
300
|
+
.an-w-zero {
|
|
301
|
+
stroke: var(--an-w-muted);
|
|
302
|
+
stroke-opacity: 0.5;
|
|
303
|
+
}
|
|
304
|
+
.an-w-cross {
|
|
305
|
+
stroke: var(--an-w-muted);
|
|
306
|
+
stroke-opacity: 0.4;
|
|
307
|
+
stroke-dasharray: 3 3;
|
|
208
308
|
}
|
|
309
|
+
.an-w-chart rect.is-dim {
|
|
310
|
+
opacity: 0.45;
|
|
311
|
+
}
|
|
312
|
+
.an-w-chart circle {
|
|
313
|
+
stroke: #fff;
|
|
314
|
+
stroke-width: 2;
|
|
315
|
+
filter: drop-shadow(0 1px 3px rgba(16, 16, 16, 0.25));
|
|
316
|
+
}
|
|
317
|
+
.an-w-area {
|
|
318
|
+
stroke: none;
|
|
319
|
+
}
|
|
320
|
+
.an-w-stop.an-w-s0 { stop-color: var(--an-w-s0); }
|
|
321
|
+
.an-w-stop.an-w-s1 { stop-color: var(--an-w-s1); }
|
|
322
|
+
.an-w-stop.an-w-s2 { stop-color: var(--an-w-s2); }
|
|
323
|
+
.an-w-stop.an-w-s3 { stop-color: var(--an-w-s3); }
|
|
324
|
+
.an-w-tip {
|
|
325
|
+
position: absolute;
|
|
326
|
+
top: 8px;
|
|
327
|
+
pointer-events: none;
|
|
328
|
+
display: flex;
|
|
329
|
+
flex-direction: column;
|
|
330
|
+
gap: 2px;
|
|
331
|
+
padding: 6px 9px;
|
|
332
|
+
border: 1px solid var(--an-w-line);
|
|
333
|
+
border-radius: 8px;
|
|
334
|
+
background: rgba(255, 255, 255, 0.78);
|
|
335
|
+
-webkit-backdrop-filter: blur(14px) saturate(1.5);
|
|
336
|
+
backdrop-filter: blur(14px) saturate(1.5);
|
|
337
|
+
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.6) inset, 0 6px 20px rgba(16, 16, 16, 0.1);
|
|
338
|
+
font-size: 12px;
|
|
339
|
+
white-space: nowrap;
|
|
340
|
+
}
|
|
341
|
+
.an-w-tip b {
|
|
342
|
+
color: var(--an-w-muted);
|
|
343
|
+
font-weight: 500;
|
|
344
|
+
}
|
|
345
|
+
.an-w-tip i {
|
|
346
|
+
display: inline-block;
|
|
347
|
+
width: 8px;
|
|
348
|
+
height: 8px;
|
|
349
|
+
border-radius: 50%;
|
|
350
|
+
margin-right: 6px;
|
|
351
|
+
}
|
|
352
|
+
.an-w-tip i.an-w-s0 { background: var(--an-w-s0); }
|
|
353
|
+
.an-w-tip i.an-w-s1 { background: var(--an-w-s1); }
|
|
354
|
+
.an-w-tip i.an-w-s2 { background: var(--an-w-s2); }
|
|
355
|
+
.an-w-tip i.an-w-s3 { background: var(--an-w-s3); }
|
|
209
356
|
.an-w-chart rect.an-w-s0,
|
|
210
357
|
.an-w-chart circle.an-w-s0 {
|
|
211
358
|
fill: var(--an-w-s0);
|
|
@@ -224,7 +371,9 @@
|
|
|
224
371
|
}
|
|
225
372
|
.an-w-line {
|
|
226
373
|
fill: none;
|
|
227
|
-
stroke-width: 2;
|
|
374
|
+
stroke-width: 2.25;
|
|
375
|
+
stroke-linejoin: round;
|
|
376
|
+
stroke-linecap: round;
|
|
228
377
|
}
|
|
229
378
|
.an-w-line.an-w-s0 {
|
|
230
379
|
stroke: var(--an-w-s0);
|
package/react.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// Ready-made React views for the widgets an agent draws in its replies.
|
|
3
3
|
//
|
|
4
4
|
// import { ReplyView } from '@cerebro-labs/agentnava-sdk/react'
|
|
@@ -16,7 +16,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
16
16
|
// once, even if two containers name it; links and pictures open only over
|
|
17
17
|
// https:// (narrow it further with `allowUrl`); and a widget that throws while
|
|
18
18
|
// drawing is left out rather than breaking the screen around it.
|
|
19
|
-
import { Component, Fragment } from 'react';
|
|
19
|
+
import { Component, Fragment, useEffect, useId, useRef, useState } from 'react';
|
|
20
20
|
import { placeWidgets } from "./widgets.js";
|
|
21
21
|
export { placeWidgets } from "./widgets.js";
|
|
22
22
|
const str = (v) => (typeof v === 'string' || typeof v === 'number' ? String(v) : '');
|
|
@@ -34,7 +34,63 @@ class Boundary extends Component {
|
|
|
34
34
|
return this.state.failed ? null : this.props.children;
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
+
/** A caption's leading figure is bold; a signed one ("-1.55%", "+12") is red or green too. */
|
|
38
|
+
function caption(text) {
|
|
39
|
+
const m = /^([+\-\u2212]?[$\u20ac\u00a3\u20b9]?\d[\d,.]*[%KMBTx]?)(.*)$/s.exec(text);
|
|
40
|
+
if (!m)
|
|
41
|
+
return text;
|
|
42
|
+
// Only a signed figure says which way it moved; "12 errors" is not good news.
|
|
43
|
+
const dir = /^[-\u2212]/.test(m[1]) ? 'is-down' : m[1].startsWith('+') ? 'is-up' : '';
|
|
44
|
+
return (_jsxs(_Fragment, { children: [_jsx("b", { className: dir || undefined, children: m[1] }), m[2]] }));
|
|
45
|
+
}
|
|
46
|
+
/** Round numbers for the y axis: about `count` steps of 1, 2, 2.5 or 5 x 10^n. */
|
|
47
|
+
function niceTicks(lo, hi, count = 4) {
|
|
48
|
+
if (lo === hi) {
|
|
49
|
+
const pad = Math.abs(lo) * 0.1 || 1;
|
|
50
|
+
lo -= pad;
|
|
51
|
+
hi += pad;
|
|
52
|
+
}
|
|
53
|
+
const raw = (hi - lo) / count;
|
|
54
|
+
const mag = 10 ** Math.floor(Math.log10(raw));
|
|
55
|
+
const step = ([1, 2, 2.5, 5, 10].find((m) => m * mag >= raw) ?? 10) * mag;
|
|
56
|
+
const ticks = [];
|
|
57
|
+
for (let t = Math.floor(lo / step) * step; t <= hi + step * 1e-9; t += step)
|
|
58
|
+
ticks.push(Number(t.toPrecision(12)));
|
|
59
|
+
if (ticks[ticks.length - 1] < hi)
|
|
60
|
+
ticks.push(Number((ticks[ticks.length - 1] + step).toPrecision(12)));
|
|
61
|
+
return ticks;
|
|
62
|
+
}
|
|
63
|
+
/** Short axis numbers: 1.2K, 3.4M, 5B. */
|
|
64
|
+
function compact(n, locale) {
|
|
65
|
+
try {
|
|
66
|
+
return new Intl.NumberFormat(locale, {
|
|
67
|
+
notation: 'compact',
|
|
68
|
+
maximumFractionDigits: 1,
|
|
69
|
+
}).format(n);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return String(n);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
37
75
|
function Chart({ part, ctx }) {
|
|
76
|
+
const box = useRef(null);
|
|
77
|
+
const gid = `an-w-g${useId().replace(/:/g, '')}`;
|
|
78
|
+
// Drawn in real pixels at the box's own width, so text stays at body size
|
|
79
|
+
// however wide the card is (a scaled viewBox blew 10px labels up to 40px).
|
|
80
|
+
const [W, setW] = useState(560);
|
|
81
|
+
const [hover, setHover] = useState(null);
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
const el = box.current;
|
|
84
|
+
if (!el || typeof ResizeObserver === 'undefined')
|
|
85
|
+
return;
|
|
86
|
+
const ro = new ResizeObserver(([e]) => {
|
|
87
|
+
const w = Math.round(e.contentRect.width);
|
|
88
|
+
if (w > 0)
|
|
89
|
+
setW(w);
|
|
90
|
+
});
|
|
91
|
+
ro.observe(el);
|
|
92
|
+
return () => ro.disconnect();
|
|
93
|
+
}, []);
|
|
38
94
|
const series = list(part.series)
|
|
39
95
|
.map((s) => ({
|
|
40
96
|
name: str(s?.name),
|
|
@@ -44,26 +100,70 @@ function Chart({ part, ctx }) {
|
|
|
44
100
|
if (series.length === 0)
|
|
45
101
|
return null;
|
|
46
102
|
const xs = series[0].points.map((p) => str(p.x));
|
|
103
|
+
const n = Math.max(...series.map((s) => s.points.length));
|
|
47
104
|
const ref = part.reference;
|
|
48
105
|
const refY = typeof ref?.y === 'number' && Number.isFinite(ref.y) ? ref.y : undefined;
|
|
106
|
+
const isBar = part.type === 'bar';
|
|
49
107
|
const ys = [...series.flatMap((s) => s.points.map((p) => p.y)), ...(refY === undefined ? [] : [refY])];
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
108
|
+
// Bars measure from zero, so zero is always in range. A line shows change,
|
|
109
|
+
// so it fits its own range: a price moving 520 to 540 must not draw flat.
|
|
110
|
+
const dataLo = Math.min(...ys);
|
|
111
|
+
const dataHi = Math.max(...ys);
|
|
112
|
+
const ticks = isBar ? niceTicks(Math.min(dataLo, 0), Math.max(dataHi, 0)) : niceTicks(dataLo, dataHi);
|
|
113
|
+
const lo = ticks[0];
|
|
114
|
+
const hi = ticks[ticks.length - 1];
|
|
115
|
+
const span = hi - lo || 1;
|
|
116
|
+
const fmt = (v) => (part.unit === 'currency' ? ctx.money(v) : part.unit === 'percent' ? `${ctx.number(v)}%` : ctx.number(v));
|
|
117
|
+
const axis = (v) => {
|
|
118
|
+
const short = Math.abs(hi) >= 10000 || Math.abs(lo) >= 10000 ? compact(v, ctx.props.locale) : ctx.number(v);
|
|
119
|
+
return part.unit === 'currency' ? (fmt(0).replace(/[\d.,\s]+/g, '') || '') + short : part.unit === 'percent' ? `${short}%` : short;
|
|
120
|
+
};
|
|
121
|
+
const H = 220;
|
|
122
|
+
const top = 12;
|
|
123
|
+
const bottom = 26;
|
|
124
|
+
const left = Math.max(...ticks.map((t) => axis(t).length)) * 6.5 + 10;
|
|
125
|
+
const right = Math.max(12, Math.max(...xs.map((x) => x.length), 1) * 3.3);
|
|
126
|
+
const plotW = Math.max(W - left - right, 40);
|
|
127
|
+
const plotH = H - top - bottom;
|
|
128
|
+
const yOf = (y) => top + plotH - ((y - lo) / span) * plotH;
|
|
129
|
+
const step = plotW / Math.max(n, 1);
|
|
130
|
+
const xOf = (i) => (isBar ? left + i * step + step / 2 : left + (n === 1 ? plotW / 2 : (i / (n - 1)) * plotW));
|
|
131
|
+
const zero = yOf(Math.max(lo, Math.min(0, hi)));
|
|
132
|
+
// Thin the x labels until each fits in the room it has.
|
|
133
|
+
const longest = Math.max(...xs.map((x) => x.length), 1);
|
|
134
|
+
const every = Math.max(1, Math.ceil((longest * 6.5 + 12) / (plotW / Math.max(n, 1))));
|
|
135
|
+
const showDots = false;
|
|
136
|
+
const pick = (e) => {
|
|
137
|
+
const r = e.currentTarget.getBoundingClientRect();
|
|
138
|
+
const px = ((e.clientX - r.left) / r.width) * W;
|
|
139
|
+
let best = 0;
|
|
140
|
+
for (let i = 1; i < n; i++)
|
|
141
|
+
if (Math.abs(xOf(i) - px) < Math.abs(xOf(best) - px))
|
|
142
|
+
best = i;
|
|
143
|
+
setHover(best);
|
|
144
|
+
};
|
|
145
|
+
const tipX = hover === null ? 0 : xOf(hover);
|
|
146
|
+
// One trend line leads with where it ended and how far it moved, like a stat.
|
|
147
|
+
const only = series.length === 1 && !isBar && series[0].points.length >= 2 ? series[0].points : undefined;
|
|
148
|
+
const headline = only
|
|
149
|
+
? (() => {
|
|
150
|
+
const first = only[0].y;
|
|
151
|
+
const last = only[only.length - 1].y;
|
|
152
|
+
const delta = last - first;
|
|
153
|
+
const abs = part.unit === 'percent' ? `${ctx.number(Math.abs(delta))} pts` : fmt(Math.abs(delta)).replace('-', '');
|
|
154
|
+
const pct = part.unit !== 'percent' && first > 0 && last >= 0 ? ` (${ctx.number(Math.round((Math.abs(delta) / first) * 1000) / 10)}%)` : '';
|
|
155
|
+
return { last, delta, change: abs + pct, from: str(only[0].x) };
|
|
156
|
+
})()
|
|
157
|
+
: undefined;
|
|
158
|
+
return (_jsxs("figure", { className: "an-w-chart", children: [str(part.title) && _jsx("figcaption", { children: str(part.title) }), headline && (_jsxs("div", { className: "an-w-chart-head", children: [_jsx("strong", { children: fmt(headline.last) }), _jsxs("span", { children: [headline.delta === 0 ? 'No change' : `${headline.delta > 0 ? '\u25B2' : '\u25BC'} ${headline.change}`, " since ", headline.from] })] })), _jsxs("div", { ref: box, className: "an-w-chart-box", children: [_jsxs("svg", { width: W, height: H, viewBox: `0 0 ${W} ${H}`, role: "img", "aria-label": str(part.title) || 'Chart', onMouseMove: pick, onMouseLeave: () => setHover(null), children: [_jsx("defs", { children: [0, 1, 2, 3].map((k) => (_jsxs("linearGradient", { id: `${gid}-${k}`, x1: "0", x2: "0", y1: "0", y2: "1", children: [_jsx("stop", { offset: "0%", className: `an-w-stop an-w-s${k}`, stopOpacity: part.type === 'area' ? 0.28 : 0.18 }), _jsx("stop", { offset: "100%", className: `an-w-stop an-w-s${k}`, stopOpacity: 0 })] }, k))) }), ticks.map((t) => (_jsxs("g", { className: "an-w-grid", children: [_jsx("line", { x1: left, x2: W - right, y1: yOf(t), y2: yOf(t) }), _jsx("text", { x: left - 8, y: yOf(t), dy: "0.32em", textAnchor: "end", children: axis(t) })] }, t))), series.map((s, si) => isBar ? (s.points.map((p, i) => {
|
|
159
|
+
const bw = Math.min((step * 0.7) / series.length, 48);
|
|
160
|
+
const x = xOf(i) - (bw * series.length) / 2 + si * bw;
|
|
161
|
+
const y = yOf(p.y);
|
|
162
|
+
return (_jsxs("g", { children: [_jsx("rect", { className: `an-w-s${si % 4}${hover !== null && hover !== i ? ' is-dim' : ''}`, x: x + 1, y: Math.min(y, zero), width: Math.max(bw - 2, 1), height: Math.max(Math.abs(zero - y), 1), rx: 3 }), series.length === 1 && n <= 8 && (_jsx("text", { className: "an-w-val", x: x + bw / 2, y: Math.min(y, zero) - 6, textAnchor: "middle", children: fmt(p.y) }))] }, `${si}-${i}`));
|
|
163
|
+
})) : (_jsxs("g", { children: [(part.type === 'area' || series.length === 1) && (_jsx("path", { className: `an-w-area an-w-s${si % 4}`, fill: `url(#${gid}-${si % 4})`, d: `M${xOf(0)},${zero} ${s.points.map((p, i) => `L${xOf(i)},${yOf(p.y)}`).join(' ')} L${xOf(s.points.length - 1)},${zero}Z` })), _jsx("polyline", { className: `an-w-line an-w-s${si % 4}`, points: s.points.map((p, i) => `${xOf(i)},${yOf(p.y)}`).join(' ') }), s.points.map((p, i) => showDots || hover === i || (series.length === 1 && i === s.points.length - 1) ? (_jsx("circle", { className: `an-w-s${si % 4}`, cx: xOf(i), cy: yOf(p.y), r: hover === i ? 4.5 : 4 }, i)) : null)] }, si))), lo < 0 && hi > 0 && _jsx("line", { className: "an-w-zero", x1: left, x2: W - right, y1: zero, y2: zero }), refY !== undefined && (_jsxs("g", { className: "an-w-ref", children: [_jsx("line", { x1: left, x2: W - right, y1: yOf(refY), y2: yOf(refY) }), str(ref?.label) && (_jsx("text", { x: W - right, y: yOf(refY) - 5, textAnchor: "end", children: str(ref?.label) }))] })), xs.map((x, i) => i % every === 0 ? (_jsx("text", { x: xOf(i), y: H - 8, textAnchor: "middle", children: x }, i)) : null), hover !== null && !isBar && _jsx("line", { className: "an-w-cross", x1: tipX, x2: tipX, y1: top, y2: top + plotH })] }), hover !== null && (_jsxs("div", { className: "an-w-tip", style: {
|
|
164
|
+
left: tipX,
|
|
165
|
+
transform: `translateX(${tipX > W / 2 ? 'calc(-100% - 10px)' : '10px'})`,
|
|
166
|
+
}, children: [_jsx("b", { children: xs[hover] }), series.map((s, si) => s.points[hover] ? (_jsxs("span", { children: [_jsx("i", { className: `an-w-s${si % 4}` }), series.length > 1 && `${s.name} `, _jsx("strong", { children: fmt(s.points[hover].y) })] }, si)) : null)] }))] }), series.length > 1 && (_jsx("div", { className: "an-w-legend", children: series.map((s, si) => (_jsxs("span", { children: [_jsx("i", { className: `an-w-s${si % 4}` }), s.name] }, si))) }))] }));
|
|
67
167
|
}
|
|
68
168
|
/** The default look of one part. `kids` is its children, already drawn. */
|
|
69
169
|
function defaultPart(part, kids, ctx) {
|
|
@@ -76,6 +176,14 @@ function defaultPart(part, kids, ctx) {
|
|
|
76
176
|
case 'Card': {
|
|
77
177
|
const image = ctx.url(part.image, 'image');
|
|
78
178
|
const href = ctx.url(part.href, 'link');
|
|
179
|
+
const actionHref = ctx.url(part.actionHref, 'link');
|
|
180
|
+
const actionLabel = str(part.actionLabel);
|
|
181
|
+
if (actionHref && actionLabel) {
|
|
182
|
+
// The card's one button ("Add to cart") is a link beside the card's
|
|
183
|
+
// own link, never inside it.
|
|
184
|
+
const inner = (_jsxs(_Fragment, { children: [image && _jsx("img", { src: image, alt: str(part.imageAlt), referrerPolicy: "no-referrer", loading: "lazy" }), _jsx("div", { className: "an-w-card-body", children: kids })] }));
|
|
185
|
+
return (_jsxs("div", { className: "an-w-card has-action", children: [href ? (_jsx("a", { className: "an-w-card-main", href: href, target: "_blank", rel: "noreferrer noopener", children: inner })) : (_jsx("div", { className: "an-w-card-main", children: inner })), _jsx("a", { className: "an-w-card-action", href: actionHref, target: "_blank", rel: "noreferrer noopener", children: actionLabel })] }));
|
|
186
|
+
}
|
|
79
187
|
const body = (_jsxs("div", { className: "an-w-card", children: [image && _jsx("img", { src: image, alt: str(part.imageAlt), referrerPolicy: "no-referrer", loading: "lazy" }), _jsx("div", { className: "an-w-card-body", children: kids })] }));
|
|
80
188
|
return href ? (_jsx("a", { className: "an-w-card-link", href: href, target: "_blank", rel: "noreferrer noopener", children: body })) : (body);
|
|
81
189
|
}
|
|
@@ -88,7 +196,7 @@ function defaultPart(part, kids, ctx) {
|
|
|
88
196
|
case 'Stat':
|
|
89
197
|
if (!str(part.value))
|
|
90
198
|
return null;
|
|
91
|
-
return (_jsxs("div", { className: "an-w-stat", children: [_jsx("
|
|
199
|
+
return (_jsxs("div", { className: "an-w-stat", children: [str(part.label) && _jsx("span", { children: str(part.label) }), _jsx("strong", { children: str(part.value) }), str(part.caption) && _jsx("small", { children: caption(str(part.caption)) })] }));
|
|
92
200
|
case 'KeyValue': {
|
|
93
201
|
const rows = list(part.rows).filter((r) => str(r?.label) || str(r?.value));
|
|
94
202
|
if (rows.length === 0)
|
|
@@ -100,7 +208,15 @@ function defaultPart(part, kids, ctx) {
|
|
|
100
208
|
const rows = list(part.rows).filter(Array.isArray);
|
|
101
209
|
if (cols.length === 0 && rows.length === 0)
|
|
102
210
|
return null;
|
|
103
|
-
|
|
211
|
+
// A column is numeric when every filled cell is a figure ("521.91", "-1.55%", "$3.2B"),
|
|
212
|
+
// and then its header lines up with its numbers on the right.
|
|
213
|
+
const figure = (c) => typeof c === 'number' || (typeof c === 'string' && /^[+\-\u2212]?[$\u20ac\u00a3\u20b9]?\d[\d,.]*\s?[%KMBTx]?$/.test(c.trim()));
|
|
214
|
+
const numCol = (j) => {
|
|
215
|
+
const filled = rows.map((r) => r[j]).filter((c) => c !== null && c !== undefined && c !== '');
|
|
216
|
+
return filled.length > 0 && filled.every(figure);
|
|
217
|
+
};
|
|
218
|
+
const nums = Array.from({ length: Math.max(cols.length, ...rows.map((r) => r.length)) }, (_, j) => numCol(j));
|
|
219
|
+
return (_jsxs("div", { className: "an-w-table", children: [str(part.title) && _jsx("div", { className: "an-w-title", children: str(part.title) }), _jsxs("table", { children: [cols.length > 0 && (_jsx("thead", { children: _jsx("tr", { children: cols.map((c, j) => (_jsx("th", { className: nums[j] ? 'is-num' : undefined, children: c }, j))) }) })), _jsx("tbody", { children: rows.map((r, i) => (_jsx("tr", { children: r.map((c, j) => (_jsx("td", { className: nums[j] ? 'is-num' : undefined, children: typeof c === 'number' ? ctx.number(c) : str(c) }, j))) }, i))) })] })] }));
|
|
104
220
|
}
|
|
105
221
|
case 'Chart':
|
|
106
222
|
return _jsx(Chart, { part: part, ctx: ctx });
|
|
@@ -171,7 +287,11 @@ export function WidgetView(props) {
|
|
|
171
287
|
return null;
|
|
172
288
|
let moneyFormat;
|
|
173
289
|
try {
|
|
174
|
-
moneyFormat = new Intl.NumberFormat(locale, {
|
|
290
|
+
moneyFormat = new Intl.NumberFormat(locale, {
|
|
291
|
+
style: 'currency',
|
|
292
|
+
currency,
|
|
293
|
+
maximumFractionDigits: 0,
|
|
294
|
+
});
|
|
175
295
|
}
|
|
176
296
|
catch {
|
|
177
297
|
moneyFormat = new Intl.NumberFormat(locale, { maximumFractionDigits: 0 });
|