@twentyfirstmedia/ui-core 0.5.0 → 0.7.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.
Files changed (163) hide show
  1. package/dist/components/admin.d.ts.map +1 -1
  2. package/dist/components/admin.js +31 -21
  3. package/dist/components/admin.js.map +1 -1
  4. package/dist/components/agents.d.ts.map +1 -1
  5. package/dist/components/agents.js +21 -18
  6. package/dist/components/agents.js.map +1 -1
  7. package/dist/components/app-chrome.d.ts.map +1 -1
  8. package/dist/components/app-chrome.js +5 -5
  9. package/dist/components/app-chrome.js.map +1 -1
  10. package/dist/components/approvals.d.ts.map +1 -1
  11. package/dist/components/approvals.js +2 -2
  12. package/dist/components/approvals.js.map +1 -1
  13. package/dist/components/building-blocks.d.ts.map +1 -1
  14. package/dist/components/building-blocks.js +13 -10
  15. package/dist/components/building-blocks.js.map +1 -1
  16. package/dist/components/charts.d.ts +2 -2
  17. package/dist/components/charts.d.ts.map +1 -1
  18. package/dist/components/charts.js +14 -11
  19. package/dist/components/charts.js.map +1 -1
  20. package/dist/components/chat.d.ts +2 -2
  21. package/dist/components/chat.d.ts.map +1 -1
  22. package/dist/components/chat.js +22 -18
  23. package/dist/components/chat.js.map +1 -1
  24. package/dist/components/commerce.d.ts.map +1 -1
  25. package/dist/components/commerce.js +20 -16
  26. package/dist/components/commerce.js.map +1 -1
  27. package/dist/components/data-display.d.ts +0 -5
  28. package/dist/components/data-display.d.ts.map +1 -1
  29. package/dist/components/data-display.js +41 -22
  30. package/dist/components/data-display.js.map +1 -1
  31. package/dist/components/data-table.d.ts +2 -2
  32. package/dist/components/data-table.d.ts.map +1 -1
  33. package/dist/components/data-table.js +3 -3
  34. package/dist/components/data-table.js.map +1 -1
  35. package/dist/components/developer.d.ts.map +1 -1
  36. package/dist/components/developer.js +7 -6
  37. package/dist/components/developer.js.map +1 -1
  38. package/dist/components/feedback.d.ts.map +1 -1
  39. package/dist/components/feedback.js +5 -4
  40. package/dist/components/feedback.js.map +1 -1
  41. package/dist/components/forms.d.ts.map +1 -1
  42. package/dist/components/forms.js +63 -21
  43. package/dist/components/forms.js.map +1 -1
  44. package/dist/components/foundation.d.ts.map +1 -1
  45. package/dist/components/foundation.js +13 -9
  46. package/dist/components/foundation.js.map +1 -1
  47. package/dist/components/household.d.ts +19 -0
  48. package/dist/components/household.d.ts.map +1 -0
  49. package/dist/components/household.js +137 -0
  50. package/dist/components/household.js.map +1 -0
  51. package/dist/components/index.d.ts +4 -1
  52. package/dist/components/index.d.ts.map +1 -1
  53. package/dist/components/index.js +4 -1
  54. package/dist/components/index.js.map +1 -1
  55. package/dist/components/inputs.d.ts.map +1 -1
  56. package/dist/components/inputs.js +33 -8
  57. package/dist/components/inputs.js.map +1 -1
  58. package/dist/components/life.d.ts +26 -0
  59. package/dist/components/life.d.ts.map +1 -0
  60. package/dist/components/life.js +186 -0
  61. package/dist/components/life.js.map +1 -0
  62. package/dist/components/mail.d.ts.map +1 -1
  63. package/dist/components/mail.js +5 -5
  64. package/dist/components/mail.js.map +1 -1
  65. package/dist/components/media.d.ts.map +1 -1
  66. package/dist/components/media.js +62 -11
  67. package/dist/components/media.js.map +1 -1
  68. package/dist/components/navigation.d.ts +3 -1
  69. package/dist/components/navigation.d.ts.map +1 -1
  70. package/dist/components/navigation.js +287 -20
  71. package/dist/components/navigation.js.map +1 -1
  72. package/dist/components/operations.d.ts.map +1 -1
  73. package/dist/components/operations.js +10 -10
  74. package/dist/components/operations.js.map +1 -1
  75. package/dist/components/overlays.d.ts.map +1 -1
  76. package/dist/components/overlays.js +2 -2
  77. package/dist/components/overlays.js.map +1 -1
  78. package/dist/components/previews.js +2 -2
  79. package/dist/components/previews.js.map +1 -1
  80. package/dist/components/primitives.js +2 -2
  81. package/dist/components/primitives.js.map +1 -1
  82. package/dist/components/qr-code.d.ts +8 -0
  83. package/dist/components/qr-code.d.ts.map +1 -0
  84. package/dist/components/qr-code.js +18 -0
  85. package/dist/components/qr-code.js.map +1 -0
  86. package/dist/components/real-estate.d.ts.map +1 -1
  87. package/dist/components/real-estate.js +5 -5
  88. package/dist/components/real-estate.js.map +1 -1
  89. package/dist/components/rich-text-editor.d.ts.map +1 -1
  90. package/dist/components/rich-text-editor.js +1 -1
  91. package/dist/components/rich-text-editor.js.map +1 -1
  92. package/dist/components/select-menu.js +1 -1
  93. package/dist/components/select-menu.js.map +1 -1
  94. package/dist/components/shell.d.ts +0 -4
  95. package/dist/components/shell.d.ts.map +1 -1
  96. package/dist/components/shell.js +100 -7
  97. package/dist/components/shell.js.map +1 -1
  98. package/dist/components/site.d.ts.map +1 -1
  99. package/dist/components/site.js +8 -6
  100. package/dist/components/site.js.map +1 -1
  101. package/dist/components/status.d.ts.map +1 -1
  102. package/dist/components/status.js +2 -2
  103. package/dist/components/status.js.map +1 -1
  104. package/dist/components/system.d.ts.map +1 -1
  105. package/dist/components/system.js +3 -2
  106. package/dist/components/system.js.map +1 -1
  107. package/dist/components/types.d.ts +589 -18
  108. package/dist/components/types.d.ts.map +1 -1
  109. package/dist/components/workspace.d.ts.map +1 -1
  110. package/dist/components/workspace.js +15 -12
  111. package/dist/components/workspace.js.map +1 -1
  112. package/dist/index.d.ts +1 -1
  113. package/dist/index.d.ts.map +1 -1
  114. package/dist/index.js +1 -1
  115. package/dist/index.js.map +1 -1
  116. package/dist/qr.d.ts +14 -0
  117. package/dist/qr.d.ts.map +1 -0
  118. package/dist/qr.js +298 -0
  119. package/dist/qr.js.map +1 -0
  120. package/dist/strings.d.ts +126 -4
  121. package/dist/strings.d.ts.map +1 -1
  122. package/dist/strings.js +88 -1
  123. package/dist/strings.js.map +1 -1
  124. package/dist/themes.css +15592 -10463
  125. package/package.json +2 -1
  126. package/src/components/admin.tsx +66 -23
  127. package/src/components/agents.tsx +22 -20
  128. package/src/components/app-chrome.tsx +9 -5
  129. package/src/components/approvals.tsx +3 -1
  130. package/src/components/building-blocks.tsx +24 -13
  131. package/src/components/charts.tsx +17 -12
  132. package/src/components/chat.tsx +54 -31
  133. package/src/components/commerce.tsx +58 -24
  134. package/src/components/data-display.tsx +106 -38
  135. package/src/components/data-table.tsx +11 -7
  136. package/src/components/developer.tsx +15 -6
  137. package/src/components/feedback.tsx +5 -3
  138. package/src/components/forms.tsx +101 -23
  139. package/src/components/foundation.tsx +9 -5
  140. package/src/components/household.tsx +489 -0
  141. package/src/components/index.ts +32 -1
  142. package/src/components/inputs.tsx +44 -7
  143. package/src/components/life.tsx +672 -0
  144. package/src/components/mail.tsx +7 -3
  145. package/src/components/media.tsx +88 -16
  146. package/src/components/navigation.tsx +651 -71
  147. package/src/components/operations.tsx +17 -8
  148. package/src/components/overlays.tsx +7 -2
  149. package/src/components/previews.tsx +2 -2
  150. package/src/components/primitives.tsx +2 -2
  151. package/src/components/qr-code.tsx +30 -0
  152. package/src/components/real-estate.tsx +7 -4
  153. package/src/components/rich-text-editor.tsx +7 -1
  154. package/src/components/select-menu.tsx +1 -1
  155. package/src/components/shell.tsx +237 -11
  156. package/src/components/site.tsx +18 -4
  157. package/src/components/status.tsx +2 -0
  158. package/src/components/system.tsx +3 -2
  159. package/src/components/types.ts +583 -18
  160. package/src/components/workspace.tsx +37 -15
  161. package/src/index.ts +2 -0
  162. package/src/qr.ts +300 -0
  163. package/src/strings.ts +297 -1
@@ -8,6 +8,7 @@ import { cx } from "../cx.js";
8
8
  import { Icon } from "../Icon.js";
9
9
  import { CountUp } from "./data-display.js";
10
10
  import type {
11
+ BaseProps,
11
12
  AreaChartProps,
12
13
  BarChartProps,
13
14
  DonutProps,
@@ -37,7 +38,7 @@ export function BarChart(p: BarChartProps) {
37
38
  bw = Math.max(1, Math.min(p.barWidth || 28, gw * 0.62)),
38
39
  ticks = [0, 0.5, 1];
39
40
  return (
40
- <div ref={mw[0]} className="tf-chart-wrap">
41
+ <div style={p.style} ref={mw[0]} className={cx("tf-chart-wrap", p.className)}>
41
42
  <svg
42
43
  className="tf-chart"
43
44
  width={W}
@@ -117,7 +118,7 @@ export function LineChart(p: LineChartProps) {
117
118
  return vals.map((v, i) => (i ? "L" : "M") + X(i).toFixed(1) + " " + Y(v).toFixed(1)).join(" ");
118
119
  }
119
120
  return (
120
- <div ref={mw[0]} className="tf-chart-wrap">
121
+ <div style={p.style} ref={mw[0]} className={cx("tf-chart-wrap", p.className)}>
121
122
  <svg
122
123
  className="tf-chart"
123
124
  width={W}
@@ -202,7 +203,8 @@ export function Sparkline(p: SparklineProps) {
202
203
  .join(" ");
203
204
  return (
204
205
  <svg
205
- className="tf-chart"
206
+ style={p.style}
207
+ className={cx("tf-chart", p.className)}
206
208
  viewBox={"0 0 " + W + " " + Hh}
207
209
  width={W}
208
210
  height={Hh}
@@ -233,9 +235,10 @@ export function Donut(p: DonutProps) {
233
235
  const gap = data.length > 1 ? 6 : 0;
234
236
  return (
235
237
  <div
236
- className="tf-donut"
238
+ className={cx("tf-donut", p.className)}
237
239
  style={{
238
240
  gap: 24,
241
+ ...p.style,
239
242
  }}
240
243
  >
241
244
  <div
@@ -323,7 +326,7 @@ export function Donut(p: DonutProps) {
323
326
  }
324
327
 
325
328
  /* ── More charts ── */
326
- export interface ChartTipProps {
329
+ export interface ChartTipProps extends BaseProps {
327
330
  x: number;
328
331
  y: number;
329
332
  title: React.ReactNode;
@@ -336,10 +339,11 @@ export interface ChartTipProps {
336
339
  export function ChartTip(p: ChartTipProps) {
337
340
  return (
338
341
  <div
339
- className="tf-chart-tip"
342
+ className={cx("tf-chart-tip", p.className)}
340
343
  style={{
341
344
  left: p.x,
342
345
  top: p.y,
346
+ ...p.style,
343
347
  }}
344
348
  >
345
349
  <b>{p.title}</b>
@@ -355,7 +359,7 @@ export function ChartTip(p: ChartTipProps) {
355
359
  }
356
360
  export function Legend(p: LegendProps) {
357
361
  return (
358
- <div className="tf-legend">
362
+ <div style={p.style} className={cx("tf-legend", p.className)}>
359
363
  {(p.items || []).map((s) => (
360
364
  <span key={s.name || s.label}>
361
365
  <i className={"tf-sw tf-sw-" + (s.tint || "ink")} />
@@ -381,7 +385,7 @@ export function StackedBarChart(p: StackedBarChartProps) {
381
385
  gw = Math.max(1, (W - pad) / n),
382
386
  bw = Math.max(1, Math.min(p.barWidth || 28, gw * 0.62));
383
387
  return (
384
- <div ref={mw[0]} className="tf-chart-wrap tf-chart-hover">
388
+ <div style={p.style} ref={mw[0]} className={cx("tf-chart-wrap tf-chart-hover", p.className)}>
385
389
  <svg
386
390
  className="tf-chart"
387
391
  width={W}
@@ -573,7 +577,7 @@ export function AreaChart(p: AreaChartProps) {
573
577
  return Hh - (v / max) * (Hh - 12);
574
578
  }
575
579
  return (
576
- <div ref={mw[0]} className="tf-chart-wrap tf-chart-hover">
580
+ <div style={p.style} ref={mw[0]} className={cx("tf-chart-wrap tf-chart-hover", p.className)}>
577
581
  <svg
578
582
  className="tf-chart"
579
583
  width={W}
@@ -691,7 +695,7 @@ export function Heatmap(p: HeatmapProps) {
691
695
  return v <= 0 ? 0 : Math.min(4, Math.ceil((v / max) * 4));
692
696
  }
693
697
  return (
694
- <div className="tf-heat-wrap">
698
+ <div style={p.style} className={cx("tf-heat-wrap", p.className)}>
695
699
  <div
696
700
  className="tf-heat"
697
701
  style={{
@@ -763,7 +767,7 @@ export function Funnel(p: FunnelProps) {
763
767
  f = p.format || ((v) => formatNumber(v)),
764
768
  on = useMounted(120);
765
769
  return (
766
- <div className="tf-funnel">
770
+ <div style={p.style} className={cx("tf-funnel", p.className)}>
767
771
  {steps.map((s, i) => {
768
772
  const pct = (s.value / top) * 100,
769
773
  conv = i ? (s.value / (steps[i - 1].value || 1)) * 100 : null;
@@ -819,9 +823,10 @@ export function Gauge(p: GaugeProps) {
819
823
  : "ink");
820
824
  return (
821
825
  <div
822
- className="tf-gauge"
826
+ className={cx("tf-gauge", p.className)}
823
827
  style={{
824
828
  width: size,
829
+ ...p.style,
825
830
  }}
826
831
  >
827
832
  <svg
@@ -8,6 +8,7 @@ import { useControlled } from "../hooks.js";
8
8
  import { Avatar, Badge, TintIcon } from "./foundation.js";
9
9
  import { Popover, Tooltip } from "./overlays.js";
10
10
  import type {
11
+ BaseProps,
11
12
  AgentMarkProps,
12
13
  AgentStatusProps,
13
14
  ChatMessageProps,
@@ -123,7 +124,7 @@ export function RichText(p: RichTextProps) {
123
124
  });
124
125
  });
125
126
  return (
126
- <div className={cx("tf-rt", p.className)}>
127
+ <div style={p.style} className={cx("tf-rt", p.className)}>
127
128
  {blocks}
128
129
  {p.streaming && <span className="tf-caret" aria-hidden />}
129
130
  </div>
@@ -167,7 +168,7 @@ export function CodeBlock(p: CodeBlockProps) {
167
168
  }, 1400);
168
169
  }
169
170
  return (
170
- <div className="tf-code">
171
+ <div style={p.style} className={cx("tf-code", p.className)}>
171
172
  <div className="tf-code-head">
172
173
  <span>{p.lang || "text"}</span>
173
174
  <button type="button" className="tf-code-copy" onClick={copy} aria-label={t.copyCode}>
@@ -192,10 +193,15 @@ export function CodeBlock(p: CodeBlockProps) {
192
193
  }
193
194
 
194
195
  /* ── Chat building blocks ── */
195
- export function TypingDots(p: { label?: string }) {
196
+ export function TypingDots(p: BaseProps & { label?: string }) {
196
197
  const t = useStrings(CHAT_STRINGS);
197
198
  return (
198
- <span className="tf-typing" role="status" aria-label={p.label || t.working}>
199
+ <span
200
+ style={p.style}
201
+ className={cx("tf-typing", p.className)}
202
+ role="status"
203
+ aria-label={p.label || t.working}
204
+ >
199
205
  <i />
200
206
  <i />
201
207
  <i />
@@ -211,6 +217,7 @@ export function AgentMark(p: AgentMarkProps) {
211
217
  style={{
212
218
  width: s,
213
219
  height: s,
220
+ ...p.style,
214
221
  }}
215
222
  aria-hidden
216
223
  >
@@ -242,7 +249,7 @@ export function ChatMessage(p: ChatMessageProps) {
242
249
  }, 1400);
243
250
  }
244
251
  return (
245
- <div className={cx("tf-msg", me ? "me" : "ai", p.className)}>
252
+ <div style={p.style} className={cx("tf-msg", me ? "me" : "ai", p.className)}>
246
253
  {me ? (
247
254
  <Avatar name={p.name || t.you} size={32} />
248
255
  ) : (
@@ -282,25 +289,35 @@ export function ChatMessage(p: ChatMessageProps) {
282
289
  )}
283
290
  {p.sources && p.sources.length > 0 && (
284
291
  <div className="tf-msg-sources">
285
- {p.sources.map((s, i) => (
286
- <a
287
- key={i}
288
- className="tf-source"
289
- href={s.url || "#"}
290
- onClick={
291
- s.onClick
292
- ? (e) => {
293
- e.preventDefault();
294
- s.onClick?.();
295
- }
296
- : undefined
297
- }
298
- >
299
- <span className="tf-source-n">{i + 1}</span>
300
- <Icon name={s.icon || "book"} size={12} />
301
- {s.title}
302
- </a>
303
- ))}
292
+ {p.sources.map((s, i) => {
293
+ const chip = (
294
+ <a
295
+ key={i}
296
+ className="tf-source"
297
+ href={s.url || "#"}
298
+ title={s.detail ? undefined : s.title}
299
+ onClick={
300
+ s.onClick
301
+ ? (e) => {
302
+ e.preventDefault();
303
+ s.onClick?.();
304
+ }
305
+ : undefined
306
+ }
307
+ >
308
+ <span className="tf-source-n">{i + 1}</span>
309
+ <Icon name={s.icon || "book"} size={12} />
310
+ <span className="tf-source-title">{s.title}</span>
311
+ </a>
312
+ );
313
+ return s.detail ? (
314
+ <Tooltip key={i} label={s.detail}>
315
+ {chip}
316
+ </Tooltip>
317
+ ) : (
318
+ chip
319
+ );
320
+ })}
304
321
  </div>
305
322
  )}
306
323
  {!me && !p.streaming && p.actions !== false && (
@@ -371,7 +388,7 @@ export function ToolCall(p: ToolCallProps) {
371
388
  const open = React.useState(!!p.defaultOpen),
372
389
  st = p.status || "done";
373
390
  return (
374
- <div className={cx("tf-tool", "is-" + st, open[0] && "open")}>
391
+ <div style={p.style} className={cx("tf-tool", "is-" + st, open[0] && "open", p.className)}>
375
392
  <button
376
393
  type="button"
377
394
  className="tf-tool-head"
@@ -419,7 +436,10 @@ export function Reasoning(p: ReasoningProps) {
419
436
  const t = useStrings(CHAT_STRINGS);
420
437
  const open = React.useState(!!p.defaultOpen);
421
438
  return (
422
- <div className={cx("tf-reason", open[0] && "open", p.streaming && "live")}>
439
+ <div
440
+ style={p.style}
441
+ className={cx("tf-reason", open[0] && "open", p.streaming && "live", p.className)}
442
+ >
423
443
  <button
424
444
  type="button"
425
445
  className="tf-reason-head"
@@ -451,7 +471,7 @@ export function Reasoning(p: ReasoningProps) {
451
471
 
452
472
  export function PromptStarters(p: PromptStartersProps) {
453
473
  return (
454
- <div className="tf-starters">
474
+ <div style={p.style} className={cx("tf-starters", p.className)}>
455
475
  {(p.items || []).map((s, i) => (
456
476
  <button
457
477
  key={i}
@@ -481,6 +501,8 @@ export function ModelPicker(p: ModelPickerProps) {
481
501
  cur = models.find((m) => m.id === p.value) || models[0] || {};
482
502
  return (
483
503
  <Popover
504
+ style={p.style}
505
+ className={p.className}
484
506
  side={p.side || "top"}
485
507
  width={300}
486
508
  flush
@@ -555,7 +577,8 @@ export function Composer(p: ComposerProps) {
555
577
  }
556
578
  return (
557
579
  <div
558
- className={cx("tf-composer", p.busy && "busy")}
580
+ style={p.style}
581
+ className={cx("tf-composer", p.busy && "busy", p.className)}
559
582
  onDragOver={(e) => {
560
583
  if (p.onAttach) e.preventDefault();
561
584
  }}
@@ -723,7 +746,7 @@ export function AgentStatus(p: AgentStatusProps) {
723
746
  const t = useStrings(CHAT_STRINGS);
724
747
  const s = p.status || "Idle";
725
748
  return (
726
- <span className="tf-agentstatus">
749
+ <span style={p.style} className={cx("tf-agentstatus", p.className)}>
727
750
  <Badge tone={AGENT_TONE[s] || "neutral"} live={s === "Running" || s === "Thinking"}>
728
751
  {t.agentStatus[s] ?? s}
729
752
  </Badge>
@@ -754,7 +777,7 @@ export function RunTrace(p: RunTraceProps) {
754
777
  : `${formatNumber(ms)} ms`;
755
778
  }
756
779
  return (
757
- <div className="tf-trace">
780
+ <div style={p.style} className={cx("tf-trace", p.className)}>
758
781
  <div className="tf-trace-head">
759
782
  <span>{t.step}</span>
760
783
  <span>{t.timeline(fms(total))}</span>
@@ -807,7 +830,7 @@ export function UsageMeter(p: UsageMeterProps) {
807
830
  const pct = Math.min(100, ((p.used || 0) / (p.limit || 1)) * 100),
808
831
  tone = pct >= 90 ? "blush" : pct >= 70 ? "butter" : "mint";
809
832
  return (
810
- <div className="tf-usage">
833
+ <div style={p.style} className={cx("tf-usage", p.className)}>
811
834
  <div className="tf-usage-top">
812
835
  <span>{p.label || t.context}</span>
813
836
  <b>{(p.format || String)(p.used) + " / " + (p.format || String)(p.limit)}</b>
@@ -39,14 +39,15 @@ export function ProductArt(p: ProductArtProps) {
39
39
  return (
40
40
  <span
41
41
  className={cx("tf-art", "tf-bg-" + (p.tint || "sky"), p.className)}
42
- style={
43
- size
42
+ style={{
43
+ ...(size
44
44
  ? {
45
45
  width: size,
46
46
  height: size,
47
47
  }
48
- : undefined
49
- }
48
+ : undefined),
49
+ ...p.style,
50
+ }}
50
51
  role={p.label ? "img" : undefined}
51
52
  aria-label={p.label}
52
53
  aria-hidden={p.label ? undefined : true}
@@ -82,7 +83,7 @@ export function Price(p: PriceProps) {
82
83
  const { formatPercent } = useFormat();
83
84
  const sale = p.compareAt && p.compareAt > p.value;
84
85
  return (
85
- <span className={cx("tf-price", p.size && "tf-price-" + p.size)}>
86
+ <span style={p.style} className={cx("tf-price", p.size && "tf-price-" + p.size, p.className)}>
86
87
  <b>{money(p.value, p.currency)}</b>
87
88
  {sale && <s>{money(p.compareAt ?? 0, p.currency)}</s>}
88
89
  {sale && p.showSave !== false && (
@@ -100,7 +101,12 @@ export function Rating(p: RatingProps) {
100
101
  const v = p.value || 0,
101
102
  shown = formatNumber(v, { minimumFractionDigits: 1, maximumFractionDigits: 1 });
102
103
  return (
103
- <span className="tf-rating" role="img" aria-label={t.ratingLabel(shown, p.count)}>
104
+ <span
105
+ style={p.style}
106
+ className={cx("tf-rating", p.className)}
107
+ role="img"
108
+ aria-label={t.ratingLabel(shown, p.count)}
109
+ >
104
110
  <span className="tf-stars" aria-hidden>
105
111
  {[0, 1, 2, 3, 4].map((i) => {
106
112
  const f = Math.max(0, Math.min(1, v - i));
@@ -141,7 +147,7 @@ export function StockBadge(p: StockBadgeProps) {
141
147
  export function ProductCard(p: ProductCardProps) {
142
148
  const t = useStrings(COMMERCE_STRINGS);
143
149
  return (
144
- <div className={cx("tf-product", p.className)} onClick={p.onClick}>
150
+ <div style={p.style} className={cx("tf-product", p.className)} onClick={p.onClick}>
145
151
  <div className="tf-product-media">
146
152
  <ProductArt kind={p.art} tint={p.tint} />
147
153
  {p.badge && (
@@ -202,7 +208,7 @@ export function VariantPicker(p: VariantPickerProps) {
202
208
  color = p.type === "color";
203
209
  const cur = opts.find((o) => o.value === v[0]);
204
210
  return (
205
- <fieldset className="tf-variants">
211
+ <fieldset style={p.style} className={cx("tf-variants", p.className)}>
206
212
  <legend className="tf-field-label">
207
213
  {p.label}
208
214
  {cur && <span className="tf-variants-cur">{cur.label || cur.value}</span>}
@@ -247,7 +253,7 @@ export function VariantPicker(p: VariantPickerProps) {
247
253
  export function CartLine(p: CartLineProps) {
248
254
  const t = useStrings(COMMERCE_STRINGS);
249
255
  return (
250
- <div className="tf-cartline">
256
+ <div style={p.style} className={cx("tf-cartline", p.className)}>
251
257
  <ProductArt kind={p.art} tint={p.tint} size={64} />
252
258
  <div className="tf-cartline-text">
253
259
  <b>{p.name}</b>
@@ -302,7 +308,7 @@ export function OrderSummary(p: OrderSummaryProps) {
302
308
  err = React.useState<string | null>(null),
303
309
  toFree = (p.freeShippingFrom || 75) - (sub - disc);
304
310
  return (
305
- <div className="tf-summary">
311
+ <div style={p.style} className={cx("tf-summary", p.className)}>
306
312
  {p.title !== false && <b className="tf-summary-title">{p.title || t.orderSummary}</b>}
307
313
  {p.showLines && (
308
314
  <div
@@ -425,7 +431,11 @@ export function CheckoutSteps(p: CheckoutStepsProps) {
425
431
  const steps = p.steps || t.checkoutSteps,
426
432
  cur = p.current || 0;
427
433
  return (
428
- <ol className="tf-csteps" aria-label={t.checkoutProgress}>
434
+ <ol
435
+ style={p.style}
436
+ className={cx("tf-csteps", p.compact && "compact", p.className)}
437
+ aria-label={t.checkoutProgress}
438
+ >
429
439
  {steps.map((s, i) => (
430
440
  <li
431
441
  key={s}
@@ -440,7 +450,7 @@ export function CheckoutSteps(p: CheckoutStepsProps) {
440
450
  }}
441
451
  >
442
452
  <span className="tf-cstep-n">{i < cur ? <Icon name="check" size={12} /> : i + 1}</span>
443
- {s}
453
+ <span className="tf-cstep-label">{s}</span>
444
454
  </button>
445
455
  </li>
446
456
  ))}
@@ -458,7 +468,7 @@ export function OrderTimeline(p: OrderTimelineProps) {
458
468
  times = p.times || {},
459
469
  cancelled = p.status === "Cancelled" || p.status === "Refunded";
460
470
  return (
461
- <div className={cx("tf-otl", cancelled && "cancelled")}>
471
+ <div style={p.style} className={cx("tf-otl", cancelled && "cancelled", p.className)}>
462
472
  {ORDER_STEPS.map((s, i) => {
463
473
  const done = !cancelled && i <= cur;
464
474
  return (
@@ -490,34 +500,58 @@ export function OrderTimeline(p: OrderTimelineProps) {
490
500
  export function PlanCard(p: PlanCardProps) {
491
501
  const t = useStrings(COMMERCE_STRINGS);
492
502
  const money = useMoney();
503
+ const Link = (p.linkAs || "a") as React.ElementType;
493
504
  return (
494
- <div className={cx("tf-plan", p.featured && "featured", p.current && "current")}>
505
+ <div
506
+ style={p.style}
507
+ className={cx("tf-plan", p.featured && "featured", p.current && "current", p.className)}
508
+ >
495
509
  {p.featured && <span className="tf-plan-flag">{p.flag || t.mostPopular}</span>}
496
510
  <div className="tf-plan-head">
497
511
  <b>{p.name}</b>
512
+ {p.current && (
513
+ <Badge tone="success" className="tf-plan-current">
514
+ {t.currentPlan}
515
+ </Badge>
516
+ )}
498
517
  {p.text && <span>{p.text}</span>}
499
518
  </div>
500
519
  <div className="tf-plan-price">
520
+ {p.pricePrefix && <span className="tf-plan-prefix">{p.pricePrefix}</span>}
501
521
  {p.price === 0 ? (
502
522
  <b>{t.free}</b>
503
- ) : p.price == null ? (
523
+ ) : p.price === null || p.price === undefined ? (
504
524
  <b>{t.letsTalk}</b>
505
- ) : (
525
+ ) : typeof p.price === "number" ? (
506
526
  <>
507
527
  <b>{money(p.price)}</b>
508
528
  <span>{p.per || t.perSeatMonth}</span>
509
529
  </>
530
+ ) : (
531
+ <>
532
+ <b>{p.price}</b>
533
+ {p.per && <span>{p.per}</span>}
534
+ </>
510
535
  )}
511
536
  {p.was && <s>{money(p.was)}</s>}
512
537
  </div>
513
- <Button
514
- variant={p.current ? "soft" : p.featured ? "primary" : "secondary"}
515
- disabled={p.current || p.disabled}
516
- onClick={p.onPick}
517
- className="tf-plan-cta"
518
- >
519
- {p.current ? t.currentPlan : p.cta || t.choosePlan(p.name)}
520
- </Button>
538
+ {p.priceNote && <p className="tf-plan-note">{p.priceNote}</p>}
539
+ {p.href && !p.current && !p.disabled ? (
540
+ <Button variant={p.featured ? "primary" : "secondary"} className="tf-plan-cta" asChild>
541
+ <Link href={p.href} onClick={p.onPick}>
542
+ {p.cta || t.choosePlan(p.name)}
543
+ </Link>
544
+ </Button>
545
+ ) : (
546
+ <Button
547
+ variant={p.current ? "soft" : p.featured ? "primary" : "secondary"}
548
+ disabled={p.current || p.disabled}
549
+ onClick={p.onPick}
550
+ className="tf-plan-cta"
551
+ >
552
+ {p.current ? t.currentPlan : p.cta || t.choosePlan(p.name)}
553
+ </Button>
554
+ )}
521
555
  <ul className="tf-plan-feats">
522
556
  {(p.features || []).map((f, i) => (
523
557
  <li key={i} className={f[0] === "-" ? "no" : undefined}>