@twentyfirstmedia/ui-core 0.5.0 → 0.6.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 (148) 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/index.d.ts +2 -1
  48. package/dist/components/index.d.ts.map +1 -1
  49. package/dist/components/index.js +2 -1
  50. package/dist/components/index.js.map +1 -1
  51. package/dist/components/inputs.d.ts.map +1 -1
  52. package/dist/components/inputs.js +33 -8
  53. package/dist/components/inputs.js.map +1 -1
  54. package/dist/components/mail.d.ts.map +1 -1
  55. package/dist/components/mail.js +5 -5
  56. package/dist/components/mail.js.map +1 -1
  57. package/dist/components/media.d.ts.map +1 -1
  58. package/dist/components/media.js +62 -11
  59. package/dist/components/media.js.map +1 -1
  60. package/dist/components/navigation.d.ts +3 -1
  61. package/dist/components/navigation.d.ts.map +1 -1
  62. package/dist/components/navigation.js +287 -20
  63. package/dist/components/navigation.js.map +1 -1
  64. package/dist/components/operations.d.ts.map +1 -1
  65. package/dist/components/operations.js +10 -10
  66. package/dist/components/operations.js.map +1 -1
  67. package/dist/components/overlays.d.ts.map +1 -1
  68. package/dist/components/overlays.js +2 -2
  69. package/dist/components/overlays.js.map +1 -1
  70. package/dist/components/previews.js +2 -2
  71. package/dist/components/previews.js.map +1 -1
  72. package/dist/components/primitives.js +2 -2
  73. package/dist/components/primitives.js.map +1 -1
  74. package/dist/components/qr-code.d.ts +8 -0
  75. package/dist/components/qr-code.d.ts.map +1 -0
  76. package/dist/components/qr-code.js +18 -0
  77. package/dist/components/qr-code.js.map +1 -0
  78. package/dist/components/real-estate.d.ts.map +1 -1
  79. package/dist/components/real-estate.js +5 -5
  80. package/dist/components/real-estate.js.map +1 -1
  81. package/dist/components/rich-text-editor.d.ts.map +1 -1
  82. package/dist/components/rich-text-editor.js +1 -1
  83. package/dist/components/rich-text-editor.js.map +1 -1
  84. package/dist/components/select-menu.js +1 -1
  85. package/dist/components/select-menu.js.map +1 -1
  86. package/dist/components/shell.d.ts +0 -4
  87. package/dist/components/shell.d.ts.map +1 -1
  88. package/dist/components/shell.js +100 -7
  89. package/dist/components/shell.js.map +1 -1
  90. package/dist/components/site.d.ts.map +1 -1
  91. package/dist/components/site.js +8 -6
  92. package/dist/components/site.js.map +1 -1
  93. package/dist/components/status.d.ts.map +1 -1
  94. package/dist/components/status.js +2 -2
  95. package/dist/components/status.js.map +1 -1
  96. package/dist/components/system.d.ts.map +1 -1
  97. package/dist/components/system.js +3 -2
  98. package/dist/components/system.js.map +1 -1
  99. package/dist/components/types.d.ts +257 -18
  100. package/dist/components/types.d.ts.map +1 -1
  101. package/dist/components/workspace.d.ts.map +1 -1
  102. package/dist/components/workspace.js +15 -12
  103. package/dist/components/workspace.js.map +1 -1
  104. package/dist/qr.d.ts +14 -0
  105. package/dist/qr.d.ts.map +1 -0
  106. package/dist/qr.js +298 -0
  107. package/dist/qr.js.map +1 -0
  108. package/dist/strings.d.ts +30 -4
  109. package/dist/strings.d.ts.map +1 -1
  110. package/dist/strings.js +15 -0
  111. package/dist/strings.js.map +1 -1
  112. package/dist/themes.css +2345 -176
  113. package/package.json +2 -1
  114. package/src/components/admin.tsx +66 -23
  115. package/src/components/agents.tsx +22 -20
  116. package/src/components/app-chrome.tsx +9 -5
  117. package/src/components/approvals.tsx +3 -1
  118. package/src/components/building-blocks.tsx +24 -13
  119. package/src/components/charts.tsx +17 -12
  120. package/src/components/chat.tsx +54 -31
  121. package/src/components/commerce.tsx +58 -24
  122. package/src/components/data-display.tsx +106 -38
  123. package/src/components/data-table.tsx +11 -7
  124. package/src/components/developer.tsx +15 -6
  125. package/src/components/feedback.tsx +5 -3
  126. package/src/components/forms.tsx +101 -23
  127. package/src/components/foundation.tsx +9 -5
  128. package/src/components/index.ts +10 -1
  129. package/src/components/inputs.tsx +44 -7
  130. package/src/components/mail.tsx +7 -3
  131. package/src/components/media.tsx +88 -16
  132. package/src/components/navigation.tsx +651 -71
  133. package/src/components/operations.tsx +17 -8
  134. package/src/components/overlays.tsx +7 -2
  135. package/src/components/previews.tsx +2 -2
  136. package/src/components/primitives.tsx +2 -2
  137. package/src/components/qr-code.tsx +30 -0
  138. package/src/components/real-estate.tsx +7 -4
  139. package/src/components/rich-text-editor.tsx +7 -1
  140. package/src/components/select-menu.tsx +1 -1
  141. package/src/components/shell.tsx +237 -11
  142. package/src/components/site.tsx +18 -4
  143. package/src/components/status.tsx +2 -0
  144. package/src/components/system.tsx +3 -2
  145. package/src/components/types.ts +261 -18
  146. package/src/components/workspace.tsx +37 -15
  147. package/src/qr.ts +300 -0
  148. package/src/strings.ts +56 -1
@@ -34,18 +34,26 @@ import type {
34
34
  * in de-DE, "$1,305.50" in en-US): the number animates, the text around it stays, and decimals and
35
35
  * grouping are kept. The final text is always `value` itself.
36
36
  */
37
+ const useIsoLayoutEffect = typeof window === "undefined" ? React.useEffect : React.useLayoutEffect;
38
+
37
39
  export function CountUp(p: CountUpProps) {
38
40
  const { locale } = useFormat();
39
41
  const o = parseFormatted(p.value, locale),
40
42
  fmt = (n: number, shape: FormattedNumber) => formatLike(n, shape, locale),
41
43
  // numbers are shown in the locale too; strings end exactly as given
42
44
  final = o && typeof p.value === "number" ? fmt(p.value, o) : String(p.value),
43
- st = React.useState(o ? fmt(0, o) : final),
45
+ // the server (and readers without scripts, print, share images) get the final value; the
46
+ // client starts at zero before the first paint and counts up
47
+ st = React.useState(final),
44
48
  last = React.useRef(0),
45
49
  first = React.useRef(true),
46
50
  el = React.useRef<HTMLSpanElement>(null);
51
+ useIsoLayoutEffect(() => {
52
+ if (o && p.animate !== false && !reduced(el.current)) st[1](fmt(0, o));
53
+ // eslint-disable-next-line react-hooks/exhaustive-deps -- only before the first count
54
+ }, []);
47
55
  React.useEffect(() => {
48
- if (!o || reduced(el.current)) {
56
+ if (!o || p.animate === false || reduced(el.current)) {
49
57
  st[1](final);
50
58
  return;
51
59
  }
@@ -77,7 +85,7 @@ export function CountUp(p: CountUpProps) {
77
85
  };
78
86
  }, [p.value, locale]);
79
87
  return (
80
- <span ref={el} className={p.className}>
88
+ <span style={p.style} ref={el} className={p.className}>
81
89
  {st[0]}
82
90
  </span>
83
91
  );
@@ -86,6 +94,7 @@ export function StatTile(p: StatTileProps) {
86
94
  const tint = p.tint || "sky";
87
95
  return (
88
96
  <div
97
+ style={p.style}
89
98
  className={cx(
90
99
  "tf-stat",
91
100
  tint === "placeholder" ? "tf-stat-placeholder" : "tf-bg-" + tint,
@@ -150,7 +159,7 @@ export function EventCard(p: EventCardProps) {
150
159
  const k = KIND[p.kind || "meeting"] || (KIND.meeting as [IconName, Tint]),
151
160
  tint = p.tint || k[1];
152
161
  return (
153
- <div className={cx("tf-event", "tf-bg-" + tint, p.className)}>
162
+ <div style={p.style} className={cx("tf-event", "tf-bg-" + tint, p.className)}>
154
163
  <div className="tf-event-head">
155
164
  <span className="tf-event-kind">
156
165
  <Icon name={p.icon || k[0]} size={14} />
@@ -195,7 +204,7 @@ export function Panel(p: PanelProps) {
195
204
  }
196
205
  export function PageHeader(p: PageHeaderProps) {
197
206
  return (
198
- <header className="tf-header">
207
+ <header style={p.style} className={cx("tf-header", p.className)}>
199
208
  <div>
200
209
  {p.crumbs && (
201
210
  <div
@@ -203,7 +212,7 @@ export function PageHeader(p: PageHeaderProps) {
203
212
  marginBottom: 8,
204
213
  }}
205
214
  >
206
- <Breadcrumb items={p.crumbs} />
215
+ <Breadcrumb items={p.crumbs} linkAs={p.crumbLinkAs} />
207
216
  </div>
208
217
  )}
209
218
  <h1 className="tf-header-title">{p.title}</h1>
@@ -255,8 +264,12 @@ export function Table(p: TableProps) {
255
264
  const box = React.useRef<HTMLDivElement>(null);
256
265
  useScrollFocus(box);
257
266
  return (
258
- <div ref={box} className="tf-table-scroll">
259
- <table className="tf-table">
267
+ <div
268
+ style={p.style}
269
+ ref={box}
270
+ className={cx("tf-table-scroll", p.stack && "tf-table-stackbox", p.className)}
271
+ >
272
+ <table className={cx("tf-table", p.stack && "stack")}>
260
273
  <thead>
261
274
  <tr>
262
275
  {cols.map((c) => (
@@ -287,7 +300,11 @@ export function Table(p: TableProps) {
287
300
  {(p.rows || []).map((r, i) => (
288
301
  <tr key={i}>
289
302
  {cols.map((c) => (
290
- <td key={c.key} className={c.align === "right" ? "tf-num" : undefined}>
303
+ <td
304
+ key={c.key}
305
+ className={c.align === "right" ? "tf-num" : undefined}
306
+ data-label={p.stack ? c.label : undefined}
307
+ >
291
308
  {c.render ? c.render(r) : r[c.key]}
292
309
  </td>
293
310
  ))}
@@ -300,34 +317,42 @@ export function Table(p: TableProps) {
300
317
  }
301
318
  export function ListRow(p: ListRowProps) {
302
319
  const t = useStrings(DATA_DISPLAY_STRINGS);
303
- return (
304
- <button
305
- type="button"
306
- className="tf-row-item"
307
- aria-current={p.active ? "true" : undefined}
308
- onClick={p.onClick}
309
- >
320
+ const Link = (p.linkAs || "a") as React.ElementType;
321
+ const interactive = !!(p.onClick || p.href);
322
+ const lines = (n: number | undefined) =>
323
+ n ? ({ "--lines": n } as React.CSSProperties) : undefined;
324
+ const title = (
325
+ <span className={cx("tf-row-title", p.titleLines && "clamp")} style={lines(p.titleLines)}>
326
+ {p.unread && <span className="tf-unread" role="img" aria-label={t.unread} />}
327
+ {p.titleLines ? <span className="tf-row-clamp">{p.title}</span> : p.title}
328
+ </span>
329
+ );
330
+ const content = (hit: boolean) => (
331
+ <>
310
332
  {p.leading ||
311
333
  (p.avatar ? (
312
334
  <Avatar name={p.avatar} size={36} />
313
335
  ) : (
314
336
  <TintIcon icon={p.icon || "mail"} tint={p.tint || "plain"} size={36} />
315
337
  ))}
316
- <span
317
- style={{
318
- minWidth: 0,
319
- }}
320
- >
321
- <span className="tf-row-title">
322
- {p.unread && <span className="tf-unread" role="img" aria-label={t.unread} />}
323
- {p.title}
324
- </span>
338
+ <span className="tf-row-text">
339
+ {hit && interactive ? (
340
+ p.href ? (
341
+ <Link className="tf-row-hit" href={p.href} onClick={p.onClick}>
342
+ {title}
343
+ </Link>
344
+ ) : (
345
+ <button type="button" className="tf-row-hit" onClick={p.onClick}>
346
+ {title}
347
+ </button>
348
+ )
349
+ ) : (
350
+ title
351
+ )}
325
352
  {p.subtitle && (
326
353
  <span
327
- className="tf-row-sub"
328
- style={{
329
- display: "block",
330
- }}
354
+ className={cx("tf-row-sub", (p.subtitleLines || 1) > 1 && "clamp")}
355
+ style={lines(p.subtitleLines)}
331
356
  >
332
357
  {p.subtitle}
333
358
  </span>
@@ -337,18 +362,59 @@ export function ListRow(p: ListRowProps) {
337
362
  {p.meta}
338
363
  {p.trailing}
339
364
  </span>
365
+ {p.actions && <span className="tf-row-actions">{p.actions}</span>}
366
+ </>
367
+ );
368
+ // own buttons, or nothing to click: a plain row (with a stretched hit area when interactive)
369
+ if (p.actions || !interactive)
370
+ return (
371
+ <div
372
+ style={p.style}
373
+ className={cx(
374
+ "tf-row-item",
375
+ interactive ? "has-hit" : "static",
376
+ p.actions && "has-actions",
377
+ p.className,
378
+ )}
379
+ aria-current={p.active ? "true" : undefined}
380
+ >
381
+ {content(true)}
382
+ </div>
383
+ );
384
+ if (p.href)
385
+ return (
386
+ <Link
387
+ href={p.href}
388
+ style={p.style}
389
+ className={cx("tf-row-item", p.className)}
390
+ aria-current={p.active ? "true" : undefined}
391
+ onClick={p.onClick}
392
+ >
393
+ {content(false)}
394
+ </Link>
395
+ );
396
+ return (
397
+ <button
398
+ style={p.style}
399
+ type="button"
400
+ className={cx("tf-row-item", p.className)}
401
+ aria-current={p.active ? "true" : undefined}
402
+ onClick={p.onClick}
403
+ >
404
+ {content(false)}
340
405
  </button>
341
406
  );
342
407
  }
343
408
  export function Timeline(p: TimelineProps) {
344
409
  return (
345
410
  <ol
346
- className="tf-timeline tf-stagger"
347
- style={
348
- {
411
+ className={cx("tf-timeline tf-stagger", p.className)}
412
+ style={{
413
+ ...({
349
414
  "--base": (p.delay || 200) + "ms",
350
- } as React.CSSProperties
351
- }
415
+ } as React.CSSProperties),
416
+ ...p.style,
417
+ }}
352
418
  >
353
419
  {(p.items || []).map((it, i) => (
354
420
  <li key={i} className="tf-tl-item">
@@ -377,7 +443,7 @@ export function FileRow(p: FileRowProps) {
377
443
  const strings = useStrings(DATA_DISPLAY_STRINGS);
378
444
  const t = FT[p.kind || "doc"] || (FT.doc as [IconName, Tint]);
379
445
  return (
380
- <div className="tf-file">
446
+ <div style={p.style} className={cx("tf-file", p.className)}>
381
447
  <TintIcon icon={t[0]} tint={t[1]} />
382
448
  <div
383
449
  style={{
@@ -395,7 +461,7 @@ export function FileRow(p: FileRowProps) {
395
461
  export function KanbanColumn(p: KanbanColumnProps) {
396
462
  const t = useStrings(DATA_DISPLAY_STRINGS);
397
463
  return (
398
- <section className="tf-kanban" aria-label={p.title}>
464
+ <section style={p.style} className={cx("tf-kanban", p.className)} aria-label={p.title}>
399
465
  <div className="tf-kanban-head">
400
466
  <span
401
467
  className="tf-row"
@@ -442,7 +508,8 @@ export function Progress(p: ProgressProps) {
442
508
  const v = Math.max(0, Math.min(100, p.value || 0));
443
509
  return (
444
510
  <div
445
- className={cx("tf-progress", p.tint && "tf-progress-" + p.tint)}
511
+ style={p.style}
512
+ className={cx("tf-progress", p.tint && "tf-progress-" + p.tint, p.className)}
446
513
  role="progressbar"
447
514
  aria-valuenow={v}
448
515
  aria-valuemin={0}
@@ -468,10 +535,11 @@ export function ProgressRing(p: ProgressRingProps) {
468
535
  on = useMounted(150);
469
536
  return (
470
537
  <div
471
- className="tf-ring"
538
+ className={cx("tf-ring", p.className)}
472
539
  style={{
473
540
  width: size,
474
541
  height: size,
542
+ ...p.style,
475
543
  }}
476
544
  role="img"
477
545
  aria-label={(p.label || t.progress) + " " + formatPercent(v, { maximumFractionDigits: 2 })}
@@ -7,7 +7,7 @@ import { cx } from "../cx.js";
7
7
  import { SearchField } from "./inputs.js";
8
8
  import { Popover, Tooltip } from "./overlays.js";
9
9
  import { Menu } from "./feedback.js";
10
- import type { DataTableProps, FilterBarProps, FilterDef } from "./types.js";
10
+ import type { BaseProps, DataTableProps, FilterBarProps, FilterDef } from "./types.js";
11
11
 
12
12
  /* ── Data table: sort, select + bulk bar, column toggles, sticky head, expandable rows ── */
13
13
  export function FilterBar(p: FilterBarProps) {
@@ -22,7 +22,7 @@ export function FilterBar(p: FilterBarProps) {
22
22
  }
23
23
  const avail = (p.filters || []).filter((f) => keys.indexOf(f.key) < 0);
24
24
  return (
25
- <div className="tf-filterbar">
25
+ <div style={p.style} className={cx("tf-filterbar", p.className)}>
26
26
  {p.search && (
27
27
  <SearchField
28
28
  placeholder={p.search}
@@ -120,10 +120,12 @@ export function FilterBar(p: FilterBarProps) {
120
120
  );
121
121
  }
122
122
 
123
- export function AddFilterMenu(p: {
124
- avail: FilterDef[];
125
- onPick?: (key: string, option: string) => void;
126
- }) {
123
+ export function AddFilterMenu(
124
+ p: BaseProps & {
125
+ avail: FilterDef[];
126
+ onPick?: (key: string, option: string) => void;
127
+ },
128
+ ) {
127
129
  const t = useStrings(DATA_TABLE_STRINGS);
128
130
  const step = React.useState<FilterDef | null>(null);
129
131
  const cur = step[0];
@@ -150,6 +152,8 @@ export function AddFilterMenu(p: {
150
152
  );
151
153
  return (
152
154
  <Menu
155
+ style={p.style}
156
+ className={p.className}
153
157
  items={p.avail.map((f) => ({
154
158
  label: f.label,
155
159
  icon: f.icon || "filter",
@@ -224,7 +228,7 @@ export function DataTable<R extends Record<string, any> = Record<string, any>>(
224
228
  const span = vis.length + (p.selectable ? 1 : 0) + (p.renderExpanded ? 1 : 0);
225
229
  const selRows = rows.filter((r) => selected.indexOf(r[rk]) >= 0);
226
230
  return (
227
- <div className={cx("tf-dt", p.className)}>
231
+ <div className={cx("tf-dt", p.className)} style={p.style}>
228
232
  {(p.toolbar || p.columnToggle !== false) && (
229
233
  <div className="tf-dt-toolbar">
230
234
  {p.toolbar}
@@ -38,6 +38,7 @@ export function LogView(p: LogViewProps) {
38
38
  className={cx("tf-log", p.className)}
39
39
  style={{
40
40
  maxHeight: p.maxHeight,
41
+ ...p.style,
41
42
  }}
42
43
  >
43
44
  {p.title && (
@@ -90,7 +91,7 @@ export function SecretField(p: SecretFieldProps) {
90
91
  copied = React.useState(false);
91
92
  const masked = (p.prefix || "") + "••••••••" + (p.last4 || "");
92
93
  return (
93
- <div className="tf-secret">
94
+ <div style={p.style} className={cx("tf-secret", p.className)}>
94
95
  <Icon name="key" size={15} />
95
96
  <span className="tf-mono tf-secret-val">{shown[0] && p.value ? p.value : masked}</span>
96
97
  {p.meta && <span className="tf-secret-meta">{p.meta}</span>}
@@ -173,7 +174,15 @@ export function MoneyInput(p: MoneyInputProps) {
173
174
  }
174
175
  const sym = formatMoney(0, p.currency).replace(/[\d.,\s]/g, "");
175
176
  return (
176
- <FormField id={id} label={p.label} hint={p.hint} error={p.error} required={p.required}>
177
+ <FormField
178
+ style={p.style}
179
+ className={p.className}
180
+ id={id}
181
+ label={p.label}
182
+ hint={p.hint}
183
+ error={p.error}
184
+ required={p.required}
185
+ >
177
186
  <div className="tf-money">
178
187
  <span
179
188
  className="tf-inputwrap"
@@ -248,7 +257,7 @@ export function MoneyInput(p: MoneyInputProps) {
248
257
 
249
258
  export function SettingsLayout(p: SettingsLayoutProps) {
250
259
  return (
251
- <div className="tf-settings">
260
+ <div style={p.style} className={cx("tf-settings", p.className)}>
252
261
  {(p.sections || []).map((s, i) => (
253
262
  <section key={s.id || i} className="tf-settings-row">
254
263
  <div className="tf-settings-label">
@@ -321,7 +330,7 @@ export function StatCell(p: StatCellProps) {
321
330
  const up = p.delta != null && p.delta >= 0,
322
331
  good = p.invert ? !up : up;
323
332
  return (
324
- <div className={cx("tf-statcell", p.tint && "tf-bg-" + p.tint, p.className)}>
333
+ <div style={p.style} className={cx("tf-statcell", p.tint && "tf-bg-" + p.tint, p.className)}>
325
334
  <div className="tf-statcell-top">
326
335
  <span className="tf-statcell-label">{p.label}</span>
327
336
  {p.icon && <TintIcon icon={p.icon} tint={p.iconTint || "plain"} />}
@@ -363,7 +372,7 @@ export function UptimeBar(p: UptimeBarProps) {
363
372
  const days = (p.days || []).map((d) => (typeof d === "string" ? { status: d } : d)),
364
373
  hov = React.useState<number | null>(null);
365
374
  return (
366
- <div className="tf-uptime">
375
+ <div style={p.style} className={cx("tf-uptime", p.className)}>
367
376
  <div
368
377
  className="tf-uptime-bar"
369
378
  onPointerLeave={() => {
@@ -408,7 +417,7 @@ export function Meter(p: MeterProps) {
408
417
  tone =
409
418
  p.tone || (pct >= (p.danger || 90) ? "blush" : pct >= (p.warn || 75) ? "butter" : "mint");
410
419
  return (
411
- <div className="tf-meter">
420
+ <div style={p.style} className={cx("tf-meter", p.className)}>
412
421
  <div className="tf-usage-top">
413
422
  <span>{p.label}</span>
414
423
  <b>{p.text || formatPercent(pct)}</b>
@@ -38,6 +38,7 @@ export function Skeleton(p: SkeletonProps) {
38
38
  style={{
39
39
  width: p.width,
40
40
  height: p.height || (v === "circle" ? p.width : undefined),
41
+ ...p.style,
41
42
  }}
42
43
  />
43
44
  );
@@ -185,7 +186,7 @@ export function Modal(p: ModalProps) {
185
186
  const pr = usePresence(p.open ?? true);
186
187
  if (!pr.mounted) return null;
187
188
  return (
188
- <div {...leaving(pr.closing)} className="tf-modal-wrap" style={p.style}>
189
+ <div {...leaving(pr.closing)} className={cx("tf-modal-wrap", p.className)} style={p.style}>
189
190
  <div className="tf-backdrop" aria-hidden />
190
191
  <div className="tf-modal" role="dialog" aria-modal="true" aria-label={p.title}>
191
192
  <span className="tf-modal-close">
@@ -214,7 +215,7 @@ export function Modal(p: ModalProps) {
214
215
 
215
216
  export function Toast(p: ToastProps) {
216
217
  return (
217
- <div className="tf-toast" role="status">
218
+ <div style={p.style} className={cx("tf-toast", p.className)} role="status">
218
219
  <TintIcon icon={p.icon || "check"} tint={p.tint || "mint"} />
219
220
  {p.children}
220
221
  {p.action && (
@@ -228,7 +229,7 @@ export function Toast(p: ToastProps) {
228
229
 
229
230
  export function EmptyState(p: EmptyStateProps) {
230
231
  return (
231
- <div className="tf-empty tf-in">
232
+ <div style={p.style} className={cx("tf-empty tf-in", p.className)}>
232
233
  <TintIcon icon={p.icon || "sparkle"} tint={p.tint || "sky"} size={44} />
233
234
  <p className="tf-empty-title">{p.title}</p>
234
235
  {p.text && <p className="tf-empty-text">{p.text}</p>}
@@ -258,6 +259,7 @@ export function Alert(p: AlertProps) {
258
259
  const tone = p.tone || "info";
259
260
  return (
260
261
  <div
262
+ style={p.style}
261
263
  className={cx("tf-alert", "tf-alert-" + tone, p.className)}
262
264
  role={tone === "danger" || tone === "warning" ? "alert" : "status"}
263
265
  >