@crisp-ui-kit/crisp 0.53.2 → 0.53.4

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 (63) hide show
  1. package/dist/cjs/components/breadcrumb/breadcrumb.js +6 -1
  2. package/dist/cjs/components/columncalc/columncalc.js +13 -4
  3. package/dist/cjs/components/composewindow/composewindow.js +5 -1
  4. package/dist/cjs/components/contextmenu/contextmenu.js +3 -1
  5. package/dist/cjs/components/filldoc/filldoc.js +6 -1
  6. package/dist/cjs/components/heatmapchart/heatmapchart.js +24 -0
  7. package/dist/cjs/components/heatmapchart/heatmapchart.recipe.js +49 -0
  8. package/dist/cjs/components/heatmapchart/index.js +17 -0
  9. package/dist/cjs/components/spinner/spinner.js +4 -1
  10. package/dist/cjs/components/toast/toast.recipe.js +8 -4
  11. package/dist/cjs/core/i18n.js +17 -2
  12. package/dist/cjs/core/index.js +2 -1
  13. package/dist/cjs/index.js +2 -0
  14. package/dist/cjs/product/recordpage/recordpage.js +17 -2
  15. package/dist/cjs/product/recordstable/recordstable.js +14 -3
  16. package/dist/cjs/product/richtext/richtext.js +33 -4
  17. package/dist/cjs/product/statlist/index.js +17 -0
  18. package/dist/cjs/product/statlist/statlist.js +20 -0
  19. package/dist/cjs/product/statlist/statlist.recipe.js +32 -0
  20. package/dist/esm/components/breadcrumb/breadcrumb.js +6 -1
  21. package/dist/esm/components/columncalc/columncalc.js +13 -4
  22. package/dist/esm/components/composewindow/composewindow.js +6 -2
  23. package/dist/esm/components/contextmenu/contextmenu.js +4 -2
  24. package/dist/esm/components/filldoc/filldoc.js +7 -2
  25. package/dist/esm/components/heatmapchart/heatmapchart.js +21 -0
  26. package/dist/esm/components/heatmapchart/heatmapchart.recipe.js +46 -0
  27. package/dist/esm/components/heatmapchart/index.js +1 -0
  28. package/dist/esm/components/spinner/spinner.js +4 -1
  29. package/dist/esm/components/toast/toast.recipe.js +9 -5
  30. package/dist/esm/core/i18n.js +16 -2
  31. package/dist/esm/core/index.js +1 -1
  32. package/dist/esm/index.js +2 -0
  33. package/dist/esm/product/recordpage/recordpage.js +18 -3
  34. package/dist/esm/product/recordstable/recordstable.js +15 -4
  35. package/dist/esm/product/richtext/richtext.js +33 -4
  36. package/dist/esm/product/statlist/index.js +1 -0
  37. package/dist/esm/product/statlist/statlist.js +17 -0
  38. package/dist/esm/product/statlist/statlist.recipe.js +29 -0
  39. package/dist/heatmapchart.css +27 -0
  40. package/dist/heatmapchart.d.ts +7 -0
  41. package/dist/heatmapchart.layered.css +29 -0
  42. package/dist/statlist.css +8 -0
  43. package/dist/statlist.d.ts +7 -0
  44. package/dist/statlist.layered.css +10 -0
  45. package/dist/styles.css +39 -2
  46. package/dist/styles.layered.css +39 -2
  47. package/dist/toast.css +2 -2
  48. package/dist/toast.layered.css +2 -2
  49. package/dist/types/components/breadcrumb/breadcrumb.d.ts +1 -1
  50. package/dist/types/components/columncalc/columncalc.d.ts +1 -1
  51. package/dist/types/components/composewindow/composewindow.d.ts +1 -1
  52. package/dist/types/components/contextmenu/contextmenu.d.ts +1 -1
  53. package/dist/types/components/heatmapchart/heatmapchart.d.ts +51 -0
  54. package/dist/types/components/heatmapchart/heatmapchart.recipe.d.ts +3 -0
  55. package/dist/types/components/heatmapchart/index.d.ts +1 -0
  56. package/dist/types/components/spinner/spinner.d.ts +1 -1
  57. package/dist/types/core/i18n.d.ts +30 -1
  58. package/dist/types/core/index.d.ts +1 -1
  59. package/dist/types/index.d.ts +2 -0
  60. package/dist/types/product/statlist/index.d.ts +1 -0
  61. package/dist/types/product/statlist/statlist.d.ts +34 -0
  62. package/dist/types/product/statlist/statlist.recipe.d.ts +3 -0
  63. package/package.json +11 -1
@@ -1029,6 +1029,34 @@
1029
1029
  & .crisp-formfield-error { font-size: var(--crisp-text-sm); line-height: 16px; color: var(--crisp-bg-danger); }
1030
1030
  }
1031
1031
 
1032
+ .crisp-heatmapchart {
1033
+ display: flex; gap: var(--crisp-space-2); box-sizing: border-box; font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
1034
+ & .crisp-heatmapchart-rowaxis { display: flex; flex-direction: column; justify-content: space-between; padding: var(--crisp-space-1) 0; font-size: var(--crisp-text-xs); font-weight: var(--crisp-weight-medium); text-transform: uppercase; letter-spacing: 0.02em; color: var(--crisp-fg-neutral-subtle); }
1035
+ & .crisp-heatmapchart-rowaxis-title { writing-mode: vertical-rl; transform: rotate(180deg); align-self: center; }
1036
+ & .crisp-heatmapchart-body { flex: 1; }
1037
+ & .crisp-heatmapchart-grid { display: grid; gap: var(--crisp-space-1-5); }
1038
+ & .crisp-heatmapchart-cell { box-sizing: border-box; display: flex; align-items: center; justify-content: center; aspect-ratio: 2 / 1; border-radius: var(--crisp-radius-md); border: none; background: transparent; margin: 0; padding: 0; font-family: inherit; font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-semibold); font-variant-numeric: tabular-nums; color: var(--crisp-fg-primary); }
1039
+ & button.crisp-heatmapchart-cell { cursor: pointer; transition: filter 0.12s ease; }
1040
+ & button.crisp-heatmapchart-cell:hover { filter: brightness(0.97); }
1041
+ & button.crisp-heatmapchart-cell:focus-visible { outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: 1px; }
1042
+ & .crisp-heatmapchart-cell[data-tone="neutral"] { background: var(--crisp-bg-raised); color: var(--crisp-fg-muted); }
1043
+ & .crisp-heatmapchart-cell[data-tone="success"] { background: color-mix(in srgb, var(--crisp-bg-success) 16%, transparent); }
1044
+ & .crisp-heatmapchart-cell[data-tone="warning"] { background: color-mix(in srgb, var(--crisp-bg-warning) 16%, transparent); }
1045
+ & .crisp-heatmapchart-cell[data-tone="orange"] { background: color-mix(in srgb, var(--crisp-bg-orange) 18%, transparent); }
1046
+ & .crisp-heatmapchart-cell[data-tone="danger"] { background: color-mix(in srgb, var(--crisp-bg-danger) 16%, transparent); }
1047
+ & .crisp-heatmapchart-cell[data-ring] { box-shadow: inset 0 0 0 2px var(--crisp-bg-danger-solid); }
1048
+ & .crisp-heatmapchart-colaxis { display: flex; justify-content: space-between; margin-top: var(--crisp-space-1-5); padding: 0 var(--crisp-space-0-5); font-size: var(--crisp-text-xs); font-weight: var(--crisp-weight-medium); text-transform: uppercase; letter-spacing: 0.02em; color: var(--crisp-fg-neutral-subtle); }
1049
+ & .crisp-heatmapchart-empty { padding: var(--crisp-space-8) 0; text-align: center; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
1050
+ }
1051
+
1052
+ @media (prefers-reduced-motion: reduce) {
1053
+ .crisp-heatmapchart, .crisp-heatmapchart * {
1054
+ animation-duration: 0.01ms !important;
1055
+ animation-iteration-count: 1 !important;
1056
+ transition-duration: 0.01ms !important;
1057
+ }
1058
+ }
1059
+
1032
1060
  .crisp-input {
1033
1061
  position: relative; box-sizing: border-box; display: inline-flex; align-items: center; width: 100%; background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-ring-field); font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); line-height: 20px;
1034
1062
  &::after { content: ""; position: absolute; inset: 0; border-radius: inherit; border: 2px solid transparent; transition: border-color .1s ease; pointer-events: none; }
@@ -1759,8 +1787,8 @@
1759
1787
 
1760
1788
  .crisp-toast {
1761
1789
  display: contents; font-family: var(--crisp-font-sans);
1762
- & .crisp-toast-viewport { position: fixed; z-index: 80; inset-inline-end: var(--crisp-space-4); bottom: var(--crisp-space-4); display: flex; flex-direction: column; gap: var(--crisp-space-2); width: 360px; max-width: calc(100vw - var(--crisp-space-8)); pointer-events: none; }
1763
- & .crisp-toast { pointer-events: auto; display: flex; align-items: flex-start; gap: var(--crisp-space-2-5); box-sizing: border-box; padding: var(--crisp-space-3); border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-raised); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 5%, transparent), 0 8px 28px -6px rgba(28,40,64,0.16), 0 2px 6px -2px rgba(28,40,64,0.10); opacity: 1; transform: translateY(0); transition: opacity 0.15s ease, transform 0.15s ease; @starting-style { opacity: 0; transform: translateY(8px); } }
1790
+ & .crisp-toast-viewport { position: fixed; z-index: 80; inset-inline-end: var(--crisp-space-4); top: var(--crisp-space-4); display: flex; flex-direction: column; gap: var(--crisp-space-2); width: 360px; max-width: calc(100vw - var(--crisp-space-8)); pointer-events: none; }
1791
+ & .crisp-toast { pointer-events: auto; display: flex; align-items: flex-start; gap: var(--crisp-space-2-5); box-sizing: border-box; padding: var(--crisp-space-2-5); border-radius: var(--crisp-radius-md); background: var(--crisp-bg-popover); box-shadow: inset 0 0 0 1px var(--crisp-ring-popover), 0 0 0 1px var(--crisp-shadow-popover1), 0 4px 8px -4px var(--crisp-shadow-popover2), 0 4px 12px -2px var(--crisp-shadow-popover3); opacity: 1; transform: translateY(0); transition: opacity 0.15s ease, transform 0.15s ease; @starting-style { opacity: 0; transform: translateY(8px); } }
1764
1792
  & .crisp-toast-icon { flex-shrink: 0; margin-top: 1px; display: inline-flex; color: var(--crisp-fg-muted); }
1765
1793
  & .crisp-toast[data-tone="info"] .crisp-toast-icon { color: var(--crisp-bg-brand-solid); }
1766
1794
  & .crisp-toast[data-tone="success"] .crisp-toast-icon { color: var(--crisp-bg-success); }
@@ -2730,6 +2758,15 @@
2730
2758
  }
2731
2759
  }
2732
2760
 
2761
+ .crisp-statlist {
2762
+ display: flex; flex-direction: column; box-sizing: border-box; font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
2763
+ & .crisp-statlist-row { display: flex; align-items: center; justify-content: space-between; gap: var(--crisp-space-3); padding: var(--crisp-space-2-5) 0; border-top: 1px solid var(--crisp-border-subtle); font-size: var(--crisp-text-md); color: inherit; text-decoration: none; }
2764
+ & .crisp-statlist-row:first-child { border-top: none; }
2765
+ & a.crisp-statlist-row:hover .crisp-statlist-label { color: var(--crisp-fg-brand-soft); }
2766
+ & .crisp-statlist-label { color: var(--crisp-fg-muted); }
2767
+ & .crisp-statlist-empty { padding: var(--crisp-space-4) 0; text-align: center; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
2768
+ }
2769
+
2733
2770
  .crisp-tasklist {
2734
2771
  display: flex; flex-direction: column; box-sizing: border-box; font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
2735
2772
  & .crisp-tasklist-group { display: flex; align-items: center; gap: var(--crisp-space-1-5); height: 36px; padding: 0 var(--crisp-space-2); font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); }
package/dist/toast.css CHANGED
@@ -1,7 +1,7 @@
1
1
  .crisp-toast {
2
2
  display: contents; font-family: var(--crisp-font-sans);
3
- & .crisp-toast-viewport { position: fixed; z-index: 80; inset-inline-end: var(--crisp-space-4); bottom: var(--crisp-space-4); display: flex; flex-direction: column; gap: var(--crisp-space-2); width: 360px; max-width: calc(100vw - var(--crisp-space-8)); pointer-events: none; }
4
- & .crisp-toast { pointer-events: auto; display: flex; align-items: flex-start; gap: var(--crisp-space-2-5); box-sizing: border-box; padding: var(--crisp-space-3); border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-raised); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 5%, transparent), 0 8px 28px -6px rgba(28,40,64,0.16), 0 2px 6px -2px rgba(28,40,64,0.10); opacity: 1; transform: translateY(0); transition: opacity 0.15s ease, transform 0.15s ease; @starting-style { opacity: 0; transform: translateY(8px); } }
3
+ & .crisp-toast-viewport { position: fixed; z-index: 80; inset-inline-end: var(--crisp-space-4); top: var(--crisp-space-4); display: flex; flex-direction: column; gap: var(--crisp-space-2); width: 360px; max-width: calc(100vw - var(--crisp-space-8)); pointer-events: none; }
4
+ & .crisp-toast { pointer-events: auto; display: flex; align-items: flex-start; gap: var(--crisp-space-2-5); box-sizing: border-box; padding: var(--crisp-space-2-5); border-radius: var(--crisp-radius-md); background: var(--crisp-bg-popover); box-shadow: inset 0 0 0 1px var(--crisp-ring-popover), 0 0 0 1px var(--crisp-shadow-popover1), 0 4px 8px -4px var(--crisp-shadow-popover2), 0 4px 12px -2px var(--crisp-shadow-popover3); opacity: 1; transform: translateY(0); transition: opacity 0.15s ease, transform 0.15s ease; @starting-style { opacity: 0; transform: translateY(8px); } }
5
5
  & .crisp-toast-icon { flex-shrink: 0; margin-top: 1px; display: inline-flex; color: var(--crisp-fg-muted); }
6
6
  & .crisp-toast[data-tone="info"] .crisp-toast-icon { color: var(--crisp-bg-brand-solid); }
7
7
  & .crisp-toast[data-tone="success"] .crisp-toast-icon { color: var(--crisp-bg-success); }
@@ -1,8 +1,8 @@
1
1
  @layer crisp-components {
2
2
  .crisp-toast {
3
3
  display: contents; font-family: var(--crisp-font-sans);
4
- & .crisp-toast-viewport { position: fixed; z-index: 80; inset-inline-end: var(--crisp-space-4); bottom: var(--crisp-space-4); display: flex; flex-direction: column; gap: var(--crisp-space-2); width: 360px; max-width: calc(100vw - var(--crisp-space-8)); pointer-events: none; }
5
- & .crisp-toast { pointer-events: auto; display: flex; align-items: flex-start; gap: var(--crisp-space-2-5); box-sizing: border-box; padding: var(--crisp-space-3); border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-raised); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 5%, transparent), 0 8px 28px -6px rgba(28,40,64,0.16), 0 2px 6px -2px rgba(28,40,64,0.10); opacity: 1; transform: translateY(0); transition: opacity 0.15s ease, transform 0.15s ease; @starting-style { opacity: 0; transform: translateY(8px); } }
4
+ & .crisp-toast-viewport { position: fixed; z-index: 80; inset-inline-end: var(--crisp-space-4); top: var(--crisp-space-4); display: flex; flex-direction: column; gap: var(--crisp-space-2); width: 360px; max-width: calc(100vw - var(--crisp-space-8)); pointer-events: none; }
5
+ & .crisp-toast { pointer-events: auto; display: flex; align-items: flex-start; gap: var(--crisp-space-2-5); box-sizing: border-box; padding: var(--crisp-space-2-5); border-radius: var(--crisp-radius-md); background: var(--crisp-bg-popover); box-shadow: inset 0 0 0 1px var(--crisp-ring-popover), 0 0 0 1px var(--crisp-shadow-popover1), 0 4px 8px -4px var(--crisp-shadow-popover2), 0 4px 12px -2px var(--crisp-shadow-popover3); opacity: 1; transform: translateY(0); transition: opacity 0.15s ease, transform 0.15s ease; @starting-style { opacity: 0; transform: translateY(8px); } }
6
6
  & .crisp-toast-icon { flex-shrink: 0; margin-top: 1px; display: inline-flex; color: var(--crisp-fg-muted); }
7
7
  & .crisp-toast[data-tone="info"] .crisp-toast-icon { color: var(--crisp-bg-brand-solid); }
8
8
  & .crisp-toast[data-tone="success"] .crisp-toast-icon { color: var(--crisp-bg-success); }
@@ -16,7 +16,7 @@ export type BreadcrumbProps = React.ComponentPropsWithoutRef<"nav"> & {
16
16
  * A breadcrumb trail — measured from a record-page nav. The last item is
17
17
  * marked `aria-current="page"`; earlier items render as links or buttons.
18
18
  */
19
- export declare function Breadcrumb({ items, separator, "aria-label": ariaLabel, className, ...rest }: BreadcrumbProps): React.JSX.Element;
19
+ export declare function Breadcrumb({ items, separator, "aria-label": ariaLabelProp, className, ...rest }: BreadcrumbProps): React.JSX.Element;
20
20
  export declare namespace Breadcrumb {
21
21
  var displayName: string;
22
22
  }
@@ -50,4 +50,4 @@ export type ColumnCalcProps<T> = Omit<React.ComponentPropsWithoutRef<"span">, "v
50
50
  align?: "left" | "right";
51
51
  className?: string;
52
52
  };
53
- export declare function ColumnCalc<T>({ rows, value, numeric, defaultOp, op: opProp, onOpChange, addLabel, sectionLabel, format, align, opLabels, className, ...rest }: ColumnCalcProps<T>): React.JSX.Element;
53
+ export declare function ColumnCalc<T>({ rows, value, numeric, defaultOp, op: opProp, onOpChange, addLabel: addLabelProp, sectionLabel: sectionLabelProp, format, align, opLabels, className, ...rest }: ColumnCalcProps<T>): React.JSX.Element;
@@ -70,7 +70,7 @@ export type ComposeWindowProps = Omit<React.ComponentPropsWithoutRef<"div">, "ch
70
70
  * measured screenshot. `trapFocus` follows the same call — a picture-in-
71
71
  * picture window, not a modal, so Tab is not caught inside it.
72
72
  */
73
- export declare function ComposeWindow({ open, onOpenChange, breadcrumb, minimized: minimizedProp, defaultMinimized, onMinimizedChange, pinned: pinnedProp, defaultPinned, onPinnedChange, width, "aria-label": ariaLabel, minimizeLabel, pinLabel, closeLabel, children, className, style, "aria-describedby": describedBy, ...rest }: ComposeWindowProps): React.JSX.Element | null;
73
+ export declare function ComposeWindow({ open, onOpenChange, breadcrumb, minimized: minimizedProp, defaultMinimized, onMinimizedChange, pinned: pinnedProp, defaultPinned, onPinnedChange, width, "aria-label": ariaLabel, minimizeLabel: minimizeLabelProp, pinLabel: pinLabelProp, closeLabel: closeLabelProp, children, className, style, "aria-describedby": describedBy, ...rest }: ComposeWindowProps): React.JSX.Element | null;
74
74
  export declare namespace ComposeWindow {
75
75
  var displayName: string;
76
76
  }
@@ -33,7 +33,7 @@ export type ContextMenuProps = {
33
33
  * Wrap the target; right-clicking it opens the menu (flipping at the viewport
34
34
  * edges), closing on select, outside pointer-down, and Escape.
35
35
  */
36
- export declare function ContextMenu({ children, items, menuLabel, className, asChild, }: ContextMenuProps): React.JSX.Element;
36
+ export declare function ContextMenu({ children, items, menuLabel: menuLabelProp, className, asChild, }: ContextMenuProps): React.JSX.Element;
37
37
  export declare namespace ContextMenu {
38
38
  var displayName: string;
39
39
  }
@@ -0,0 +1,51 @@
1
+ import type * as React from "react";
2
+ export type HeatmapCell = {
3
+ /** The raw value a consumer's own severity/intensity scale maps from. */
4
+ value: number;
5
+ /** Cell background. "neutral" (the default) is a plain muted tint —
6
+ * typically used for a value of 0. */
7
+ tone?: "neutral" | "success" | "warning" | "orange" | "danger";
8
+ /** Draws an inset danger ring around the cell (e.g. "over the appetite
9
+ * threshold"), independent of — and layered on top of — `tone`. */
10
+ ring?: boolean;
11
+ };
12
+ /** An axis's own end labels + an optional rotated (row) or inline (column)
13
+ * title between them — a 5x5 heatmap reads its axis by its two extremes
14
+ * and what they mean, not a tick per row/column. */
15
+ export type HeatmapAxis = {
16
+ low: React.ReactNode;
17
+ high: React.ReactNode;
18
+ title?: React.ReactNode;
19
+ };
20
+ export type HeatmapChartProps = Omit<React.ComponentPropsWithoutRef<"div">, "children" | "onClick"> & {
21
+ /** `data[row][col]`, row 0 at the top (the axis's `high` end). */
22
+ data: HeatmapCell[][];
23
+ rowAxis: HeatmapAxis;
24
+ colAxis: HeatmapAxis;
25
+ /** Cell display text — default `cell.value || ""` (a 0 renders blank, the
26
+ * same "an empty cell says nothing happened here" reading GRC's own
27
+ * heatmap already used). */
28
+ format?: (cell: HeatmapCell, row: number, col: number) => React.ReactNode;
29
+ /** Hover/focus tooltip text for a cell. */
30
+ cellTitle?: (cell: HeatmapCell, row: number, col: number) => string;
31
+ /** Only cells this returns true for become a real `<button>` — a cell
32
+ * with nothing behind it stays a plain, non-interactive `<div>`. */
33
+ onCellClick?: (row: number, col: number, cell: HeatmapCell) => void;
34
+ isCellClickable?: (cell: HeatmapCell, row: number, col: number) => boolean;
35
+ /** Renders the empty-state message instead of the grid. */
36
+ empty?: boolean;
37
+ emptyLabel?: React.ReactNode;
38
+ "aria-label"?: string;
39
+ className?: string;
40
+ };
41
+ /**
42
+ * An NxM cell-matrix chart — GRC's own risk heatmap (impact x likelihood)
43
+ * is the first consumer. Severity/intensity mapping (`tone`, `ring`) is
44
+ * entirely the caller's own data, not a scale this component invents; see
45
+ * the recipe's own doc comment for why (and for its measurement — or lack
46
+ * of one — against Attio).
47
+ */
48
+ export declare function HeatmapChart({ data, rowAxis, colAxis, format, cellTitle, onCellClick, isCellClickable, empty, emptyLabel, "aria-label": ariaLabel, className, ...rest }: HeatmapChartProps): React.JSX.Element;
49
+ export declare namespace HeatmapChart {
50
+ var displayName: string;
51
+ }
@@ -0,0 +1,3 @@
1
+ declare const config: import("../..").RecipeConfig<{}>;
2
+ export declare const heatmapchartRecipe: import("../..").RecipeFn<{}>;
3
+ export default config;
@@ -0,0 +1 @@
1
+ export * from "./heatmapchart";
@@ -5,7 +5,7 @@ export type SpinnerProps = React.ComponentPropsWithoutRef<"span"> & {
5
5
  className?: string;
6
6
  };
7
7
  /** A spinning ring indicator. Reports `role="status"` for assistive tech. */
8
- export declare function Spinner({ size, "aria-label": ariaLabel, className, ...rest }: SpinnerProps): import("react").JSX.Element;
8
+ export declare function Spinner({ size, "aria-label": ariaLabelProp, className, ...rest }: SpinnerProps): import("react").JSX.Element;
9
9
  export declare namespace Spinner {
10
10
  var displayName: string;
11
11
  }
@@ -28,8 +28,27 @@ import * as React from "react";
28
28
  * before this existed.
29
29
  */
30
30
  export type CrispMessages = Record<string, string>;
31
- export declare function CrispMessagesProvider({ messages, children, }: {
31
+ /**
32
+ * A handful of product components (`RecordPage`, `RecordsTable`) already
33
+ * take their own translatable text as one big `labels` object rather than a
34
+ * prop per string — and several of ITS entries are formatter FUNCTIONS
35
+ * (`domainSetTo(value)`, `sortedBy(first, extra)`), not plain strings, since
36
+ * word order and pluralisation differ too much across languages for a
37
+ * template (the same reason `TableToolbar.sortTriggerLabel` is a function).
38
+ * `CrispMessages` can't hold those — it's `Record<string,string>` — so those
39
+ * two components get their own whole-object slot instead of a per-string
40
+ * swap through `messages`. `componentDefaults` is deliberately untyped here
41
+ * (`Record<string, unknown>`, cast back to the real shape by
42
+ * `useCrispComponentDefaults<T>` at the CALL site): `core/` sits underneath
43
+ * `product/`, so it cannot import `RecordPageLabels`/`RecordsTableLabels`
44
+ * without a circular dependency. Keyed by the component's own name
45
+ * ("recordPage", "recordsTable") — a small, closed set, not a general
46
+ * registry, since only these two currently need it.
47
+ */
48
+ export type CrispComponentDefaults = Record<string, unknown>;
49
+ export declare function CrispMessagesProvider({ messages, componentDefaults, children, }: {
32
50
  messages: CrispMessages;
51
+ componentDefaults?: CrispComponentDefaults;
33
52
  children?: React.ReactNode;
34
53
  }): React.ReactElement;
35
54
  /**
@@ -56,3 +75,13 @@ export declare function useCrispMessage(explicit: string | undefined, englishDef
56
75
  * per fragment, same fallback order, no hook-in-a-loop.
57
76
  */
58
77
  export declare function useCrispMessages(): CrispMessages;
78
+ /**
79
+ * Reads one component's own whole-object defaults override, set via
80
+ * `CrispMessagesProvider`'s `componentDefaults` prop (see its own doc for
81
+ * why this exists separately from `useCrispMessage`). `T` is the
82
+ * component's own labels TYPE (e.g. `Partial<RecordPageLabels>`) — this
83
+ * hook itself only knows `name`, a plain string key; the caller supplies
84
+ * and trusts the real shape, the same way `useContext` always has for an
85
+ * untyped-at-the-provider value.
86
+ */
87
+ export declare function useCrispComponentDefaults<T>(name: string): T | undefined;
@@ -7,7 +7,7 @@ export { isRtlElement } from "./direction";
7
7
  export { FloatingPortal } from "./floating-portal";
8
8
  export { hasOpenFloatingLayer, isInsideLaterFloatingLayer, registerFloatingLayer, } from "./floating-stack";
9
9
  export { formValueStyle } from "./form-value";
10
- export { type CrispMessages, CrispMessagesProvider, useCrispMessage, useCrispMessages, } from "./i18n";
10
+ export { type CrispComponentDefaults, type CrispMessages, CrispMessagesProvider, useCrispComponentDefaults, useCrispMessage, useCrispMessages, } from "./i18n";
11
11
  export { type MentionAnchor, type MentionMatch, matchMentionTrigger, } from "./mention";
12
12
  export { type FloatingAlign, type PlaceFloatingOptions, placeFloating, } from "./place-floating";
13
13
  export { Primitive } from "./primitive";
@@ -25,6 +25,7 @@ export * from "./components/fileupload";
25
25
  export * from "./components/filldoc";
26
26
  export * from "./components/fillsheet";
27
27
  export * from "./components/formfield";
28
+ export * from "./components/heatmapchart";
28
29
  export * from "./components/input";
29
30
  export * from "./components/kbd";
30
31
  export * from "./components/linechart";
@@ -70,6 +71,7 @@ export * from "./product/richtext";
70
71
  export * from "./product/settingssection";
71
72
  export * from "./product/settingsshell";
72
73
  export * from "./product/sidebar";
74
+ export * from "./product/statlist";
73
75
  export * from "./product/tasklist";
74
76
  export * from "./product/trialbanner";
75
77
  export * from "./product/viewtoggle";
@@ -0,0 +1 @@
1
+ export * from "./statlist";
@@ -0,0 +1,34 @@
1
+ import type * as React from "react";
2
+ export type StatListItem = {
3
+ id: string;
4
+ label: React.ReactNode;
5
+ /** The trailing count. Rendered as a Badge; 0 always reads "neutral",
6
+ * regardless of `tone` — a zero is never itself a finding. */
7
+ count: number;
8
+ /** Badge tone once `count > 0`. Default "neutral". */
9
+ tone?: "neutral" | "success" | "warning" | "danger" | "brand";
10
+ /** Real anchor href — gives right-click / middle-click / ctrl-click their
11
+ * ordinary browser behaviour. Omit for a non-navigable row. */
12
+ href?: string;
13
+ /** Called on a plain left click; pair with `href` and `event.preventDefault()`
14
+ * for client-side routing (e.g. react-router), the same division of
15
+ * labour its own `Link` uses internally. */
16
+ onClick?: (event: React.MouseEvent<HTMLAnchorElement>) => void;
17
+ };
18
+ export type StatListProps = Omit<React.ComponentPropsWithoutRef<"div">, "children"> & {
19
+ items: StatListItem[];
20
+ /** Shown when `items` is empty. */
21
+ emptyLabel?: React.ReactNode;
22
+ className?: string;
23
+ };
24
+ /**
25
+ * A list of label + trailing-count rows — a dashboard's own findings
26
+ * summary ("Open risks with no owner: 3") or an audit-readiness check list,
27
+ * each row optionally a link. Not measured against one specific Attio
28
+ * widget (see the recipe's own doc comment for why); composed from
29
+ * already-measured crisp values throughout.
30
+ */
31
+ export declare function StatList({ items, emptyLabel, className, ...rest }: StatListProps): React.JSX.Element;
32
+ export declare namespace StatList {
33
+ var displayName: string;
34
+ }
@@ -0,0 +1,3 @@
1
+ declare const config: import("../..").RecipeConfig<{}>;
2
+ export declare const statlistRecipe: import("../..").RecipeFn<{}>;
3
+ export default config;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crisp-ui-kit/crisp",
3
- "version": "0.53.2",
3
+ "version": "0.53.4",
4
4
  "description": "A lean React design system, measured pixel-for-pixel from Attio.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -168,6 +168,11 @@
168
168
  "import": "./dist/esm/components/formfield/index.js",
169
169
  "require": "./dist/cjs/components/formfield/index.js"
170
170
  },
171
+ "./heatmapchart": {
172
+ "types": "./dist/types/components/heatmapchart/index.d.ts",
173
+ "import": "./dist/esm/components/heatmapchart/index.js",
174
+ "require": "./dist/cjs/components/heatmapchart/index.js"
175
+ },
171
176
  "./input": {
172
177
  "types": "./dist/types/components/input/index.d.ts",
173
178
  "import": "./dist/esm/components/input/index.js",
@@ -388,6 +393,11 @@
388
393
  "import": "./dist/esm/product/sidebar/index.js",
389
394
  "require": "./dist/cjs/product/sidebar/index.js"
390
395
  },
396
+ "./statlist": {
397
+ "types": "./dist/types/product/statlist/index.d.ts",
398
+ "import": "./dist/esm/product/statlist/index.js",
399
+ "require": "./dist/cjs/product/statlist/index.js"
400
+ },
391
401
  "./tasklist": {
392
402
  "types": "./dist/types/product/tasklist/index.d.ts",
393
403
  "import": "./dist/esm/product/tasklist/index.js",