@ramme-io/ui 4.0.1 → 4.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +159 -7
  2. package/dist/blocks/ai/Actions.js +2 -2
  3. package/dist/blocks/ai/PromptInput.js +6 -6
  4. package/dist/component-registry.js +389 -359
  5. package/dist/components/ai/AIChatThread/AIChatThread.js +81 -73
  6. package/dist/components/ai/ArchitectureReferenceCard/ArchitectureReferenceCard.d.ts +28 -0
  7. package/dist/components/ai/ArchitectureReferenceCard/ArchitectureReferenceCard.js +70 -0
  8. package/dist/components/ai/ArchitectureReferenceCard/index.d.ts +2 -0
  9. package/dist/components/ai/CounterpartyGraphCard/CounterpartyGraphCard.d.ts +30 -0
  10. package/dist/components/ai/CounterpartyGraphCard/CounterpartyGraphCard.js +82 -0
  11. package/dist/components/ai/CounterpartyGraphCard/index.d.ts +2 -0
  12. package/dist/components/ai/DivergenceMetricsCard/DivergenceMetricsCard.d.ts +29 -0
  13. package/dist/components/ai/DivergenceMetricsCard/DivergenceMetricsCard.js +69 -0
  14. package/dist/components/ai/DivergenceMetricsCard/index.d.ts +2 -0
  15. package/dist/components/ai/KeyInjectionOverlay.d.ts +10 -0
  16. package/dist/components/ai/KeyInjectionOverlay.js +11 -11
  17. package/dist/components/ai/NewsSummaryCard/NewsSummaryCard.js +17 -17
  18. package/dist/components/ai/StockChart/StockChart.js +4 -4
  19. package/dist/components/auth/AuthLayout.js +4 -4
  20. package/dist/components/auth/LoginPage.d.ts +22 -0
  21. package/dist/components/auth/LoginPage.js +50 -42
  22. package/dist/components/data-display/CodeBlock.d.ts +33 -7
  23. package/dist/components/data-display/CodeBlock.js +134 -41
  24. package/dist/components/data-display/Table.js +8 -8
  25. package/dist/components/feedback/EmptyState.js +9 -9
  26. package/dist/components/feedback/ToastProvider.js +4 -4
  27. package/dist/components/forms/Checkbox.js +5 -5
  28. package/dist/components/forms/ComboBox.js +2 -2
  29. package/dist/components/forms/DatePicker.js +37 -32
  30. package/dist/components/forms/FileUpload.js +39 -37
  31. package/dist/components/forms/Input.js +19 -18
  32. package/dist/components/forms/MultiSelect.js +9 -8
  33. package/dist/components/forms/Radio.js +6 -6
  34. package/dist/components/forms/SegmentedControl.d.ts +1 -1
  35. package/dist/components/forms/SegmentedControl.js +28 -11
  36. package/dist/components/forms/Select.js +9 -8
  37. package/dist/components/forms/Textarea.d.ts +13 -11
  38. package/dist/components/forms/Textarea.js +36 -16
  39. package/dist/components/forms/ToggleSwitch.js +9 -9
  40. package/dist/components/layout/AboutRamme.d.ts +8 -0
  41. package/dist/components/layout/Accordion.js +25 -32
  42. package/dist/components/layout/AppShell.js +7 -7
  43. package/dist/components/layout/Card.js +38 -21
  44. package/dist/components/layout/DetailDrawer.js +37 -38
  45. package/dist/components/layout/Drawer.js +50 -51
  46. package/dist/components/layout/List.js +9 -9
  47. package/dist/components/layout/Modal.js +5 -5
  48. package/dist/components/layout/PageHeader.js +7 -7
  49. package/dist/components/layout/SectionHeader.js +4 -4
  50. package/dist/components/layout/SettingsContainer.d.ts +12 -0
  51. package/dist/components/layout/SettingsContainer.js +30 -18
  52. package/dist/components/layout/Sheet.d.ts +58 -0
  53. package/dist/components/layout/Sheet.js +144 -0
  54. package/dist/components/layout/Sidebar.js +1 -1
  55. package/dist/components/layout/UserNav.d.ts +13 -0
  56. package/dist/components/layout/UserNav.js +17 -17
  57. package/dist/components/molecular/ConnectorOverlay.d.ts +12 -0
  58. package/dist/components/navigation/Breadcrumbs.js +8 -8
  59. package/dist/components/navigation/Menu.js +6 -6
  60. package/dist/components/navigation/MobileNav.d.ts +44 -1
  61. package/dist/components/navigation/MobileNav.js +97 -71
  62. package/dist/components/navigation/Pagination.js +46 -41
  63. package/dist/components/navigation/Stepper.js +17 -17
  64. package/dist/components/navigation/Tabs.js +18 -11
  65. package/dist/components/theme/ThemeLab.d.ts +7 -1
  66. package/dist/components/theme/ThemeLab.js +175 -154
  67. package/dist/components/ui/icon-loader.d.ts +1 -1
  68. package/dist/contexts/ThemeContext.js +111 -82
  69. package/dist/data/mockData.d.ts +0 -3
  70. package/dist/index.css +1 -1
  71. package/dist/index.d.ts +10 -0
  72. package/dist/index.js +222 -197
  73. package/dist/lib/hooks/useBodyScrollLock.d.ts +27 -0
  74. package/dist/lib/hooks/useBodyScrollLock.js +18 -0
  75. package/dist/lib/hooks/useSheetGesture.d.ts +81 -0
  76. package/dist/lib/hooks/useSheetGesture.js +70 -0
  77. package/dist/lib/theme/themeUtils.d.ts +21 -0
  78. package/dist/lib/theme/themeUtils.js +31 -17
  79. package/dist/setupTests.d.ts +1 -0
  80. package/dist/wizardManifest.js +364 -19
  81. package/package.json +10 -4
  82. package/src/wizardManifest.ts +364 -19
  83. package/tailwind.preset.js +98 -10
@@ -0,0 +1,81 @@
1
+ /**
2
+ * The gesture behind a bottom sheet.
3
+ *
4
+ * This is the library's first pointer-driven interaction. Nothing else in
5
+ * @ramme-io/ui uses Pointer Events at all — every interactive component is an
6
+ * HTML form element — so the rules it establishes are worth stating once:
7
+ *
8
+ * - **Pointer Events only.** One code path for touch, mouse, pen and trackpad.
9
+ * Separate `mousemove` and `touchmove` handlers are how you end up with a
10
+ * control that works on one and jitters on the other.
11
+ * - **`setPointerCapture`.** A drag must survive the finger leaving the
12
+ * element. Without capture, dragging a sheet up past its own top edge stops
13
+ * tracking, which reads as the sheet sticking.
14
+ * - **`touch-action: none`** on the drag surface, applied by the component.
15
+ * Without it the browser claims the gesture for scrolling and the sheet
16
+ * never moves.
17
+ *
18
+ * WHY THE SETTLE MATH IS A PURE FUNCTION
19
+ *
20
+ * jsdom has no layout and cannot drag, so the only honest way to test a gesture
21
+ * is to make its decisions independent of the DOM. `settleTo` below takes
22
+ * numbers and returns a number; every interesting rule — flick beats position,
23
+ * dragging below the smallest detent dismisses, a slow drag snaps to nearest —
24
+ * is checkable without a browser.
25
+ */
26
+ /** Pixels per millisecond past which a drag counts as a flick rather than a move. */
27
+ export declare const FLICK_VELOCITY = 0.5;
28
+ /**
29
+ * How far below the smallest detent the sheet must be dragged before release
30
+ * dismisses it. Expressed as a fraction of the viewport so it scales with the
31
+ * screen rather than being generous on a tablet and twitchy on a phone.
32
+ */
33
+ export declare const DISMISS_THRESHOLD = 0.12;
34
+ export interface SettleInput {
35
+ /** Current height of the sheet as a fraction of the viewport, 0–1. */
36
+ current: number;
37
+ /** Allowed resting heights, ascending fractions of the viewport. */
38
+ detents: number[];
39
+ /**
40
+ * Vertical velocity in px/ms. NEGATIVE is upward (the sheet growing), matching
41
+ * pointer coordinates where y decreases toward the top of the screen.
42
+ */
43
+ velocity: number;
44
+ }
45
+ /**
46
+ * Where the sheet should come to rest when the finger lifts.
47
+ *
48
+ * Returns the target detent, or `null` meaning "dismiss".
49
+ *
50
+ * Order matters: a flick is honoured before position, because a fast downward
51
+ * flick from near the top should close the sheet rather than snap it to the
52
+ * detent it happens to be nearest. That is what every native sheet does, and
53
+ * getting it backwards makes the sheet feel like it is fighting you.
54
+ */
55
+ export declare function settleTo({ current, detents, velocity }: SettleInput): number | null;
56
+ export interface SheetGestureOptions {
57
+ detents: number[];
58
+ initialDetent?: number;
59
+ onDismiss: () => void;
60
+ /** Skips the drag entirely; the sheet still opens and closes, just without motion. */
61
+ reducedMotion?: boolean;
62
+ }
63
+ export interface SheetGesture {
64
+ /** Current height as a fraction of the viewport. */
65
+ height: number;
66
+ /** True while a finger is down — the component uses this to drop its transition. */
67
+ dragging: boolean;
68
+ /** True when the sheet is at its tallest detent. */
69
+ expanded: boolean;
70
+ /** Attach to the grabber and header. */
71
+ onPointerDown: (e: React.PointerEvent) => void;
72
+ /**
73
+ * Attach to the scrollable body. Implements the behaviour the pattern is
74
+ * named for: at a partial detent, dragging up grows the sheet instead of
75
+ * scrolling; once expanded, the content scrolls normally; and at the top of
76
+ * the content, dragging down shrinks the sheet again.
77
+ */
78
+ onBodyPointerDown: (e: React.PointerEvent) => void;
79
+ setHeight: (h: number) => void;
80
+ }
81
+ export declare function useSheetGesture({ detents, initialDetent, onDismiss, reducedMotion, }: SheetGestureOptions): SheetGesture;
@@ -0,0 +1,70 @@
1
+ import { useState as M, useRef as u, useCallback as T, useEffect as C } from "react";
2
+ const D = 0.5, P = 0.12;
3
+ function y({ current: l, detents: f, velocity: a }) {
4
+ if (!f.length) return null;
5
+ const s = [...f].sort((r, e) => r - e), n = s[0], c = s[s.length - 1];
6
+ if (Math.abs(a) >= D) {
7
+ if (a > 0) {
8
+ const e = s.filter((g) => g < l - 0.01);
9
+ return e.length ? e[e.length - 1] : null;
10
+ }
11
+ const r = s.filter((e) => e > l + 0.01);
12
+ return r.length ? r[0] : c;
13
+ }
14
+ return l < n - P ? null : s.reduce(
15
+ (r, e) => Math.abs(e - l) < Math.abs(r - l) ? e : r
16
+ );
17
+ }
18
+ function R({
19
+ detents: l,
20
+ initialDetent: f = 0,
21
+ onDismiss: a,
22
+ reducedMotion: s = !1
23
+ }) {
24
+ const n = [...l].sort((t, i) => t - i), [c, r] = M(n[f] ?? n[0]), [e, g] = M(!1), L = u(0), S = u(0), w = u(0), v = u(0), E = u(0), d = u(null), h = T(
25
+ (t, i) => {
26
+ if (!s && t.isPrimary) {
27
+ if (d.current = i ? t.currentTarget : null, i) {
28
+ const o = t.currentTarget.scrollTop <= 0;
29
+ if (c >= n[n.length - 1] - 1e-3 && !o) return;
30
+ }
31
+ L.current = t.clientY, w.current = t.clientY, v.current = t.timeStamp, S.current = c, E.current = 0, g(!0), t.currentTarget.setPointerCapture(t.pointerId);
32
+ }
33
+ },
34
+ [c, s, n]
35
+ ), b = T((t) => h(t, !1), [h]), H = T((t) => h(t, !0), [h]);
36
+ return C(() => {
37
+ if (!e) return;
38
+ const t = (o) => {
39
+ const Y = window.innerHeight || 1, I = o.clientY - L.current, x = o.timeStamp - v.current;
40
+ x > 0 && (E.current = (o.clientY - w.current) / x), w.current = o.clientY, v.current = o.timeStamp;
41
+ let p = S.current - I / Y;
42
+ const m = n[n.length - 1];
43
+ p > m && (p = m + (p - m) * 0.25), r(Math.max(0, Math.min(p, m + 0.05))), d.current && (d.current.scrollTop = 0);
44
+ }, i = () => {
45
+ g(!1);
46
+ const o = y({
47
+ current: c,
48
+ detents: n,
49
+ velocity: E.current
50
+ });
51
+ o === null ? a() : r(o), d.current = null;
52
+ };
53
+ return window.addEventListener("pointermove", t), window.addEventListener("pointerup", i), window.addEventListener("pointercancel", i), () => {
54
+ window.removeEventListener("pointermove", t), window.removeEventListener("pointerup", i), window.removeEventListener("pointercancel", i);
55
+ };
56
+ }, [e, c, a, n]), {
57
+ height: c,
58
+ dragging: e,
59
+ expanded: c >= n[n.length - 1] - 1e-3,
60
+ onPointerDown: b,
61
+ onBodyPointerDown: H,
62
+ setHeight: r
63
+ };
64
+ }
65
+ export {
66
+ P as DISMISS_THRESHOLD,
67
+ D as FLICK_VELOCITY,
68
+ y as settleTo,
69
+ R as useSheetGesture
70
+ };
@@ -1,3 +1,4 @@
1
+ import { DEFAULT_THEME_TOKENS } from '@ramme-io/shared';
1
2
  /**
2
3
  * Advanced Contrast Guard
3
4
  * Adjusts for perceived darkness (like scanline overlays)
@@ -14,6 +15,26 @@ export declare const loadGoogleFont: (headingFont: string, bodyFont: string) =>
14
15
  * @example hexToRgbTriplet('#0D6EFD') → '13 110 253'
15
16
  */
16
17
  export declare const hexToRgbTriplet: (hex: string) => string;
18
+ /**
19
+ * Every CSS variable `tokensToVars` writes.
20
+ *
21
+ * Exported so the code that removes them cannot drift from the code that adds
22
+ * them — RULE-005 asks for symmetrical purging, and these six were injected on
23
+ * to documentElement.style and never removed by anything.
24
+ */
25
+ export declare const THEME_TOKEN_VARS: readonly ["--app-primary-color", "--app-secondary-color", "--app-success-color", "--app-warning-color", "--app-danger-color", "--app-border-radius-md"];
26
+ /**
27
+ * Has the visitor actually themed anything?
28
+ *
29
+ * `activeTheme`/`savedTheme` are always populated — they are seeded with
30
+ * DEFAULT_THEME_TOKENS — so their presence proves nothing. Only a value that
31
+ * differs from those defaults means a real choice was made.
32
+ *
33
+ * Every place that decides whether to paint the user's layer must agree, or the
34
+ * layers fight: injecting unconditionally painted the framework's defaults over
35
+ * the app's own theme for every first-time visitor.
36
+ */
37
+ export declare const isCustomTheme: (tokens: Partial<Record<keyof typeof DEFAULT_THEME_TOKENS, string>> | null | undefined) => boolean;
17
38
  export declare const tokensToVars: (t: Partial<{
18
39
  primary: string;
19
40
  neutral: string;
@@ -1,17 +1,18 @@
1
+ import { DEFAULT_THEME_TOKENS as d } from "@ramme-io/shared";
1
2
  const m = (o, p) => {
2
3
  const a = o.split(" ").map(Number);
3
4
  if (a.length !== 3) return o;
4
5
  const r = (t) => Math.max(0, Math.min(255, t));
5
6
  return `${r(a[0] + p)} ${r(a[1] + p)} ${r(a[2] + p)}`;
6
- }, d = (o) => {
7
+ }, g = (o) => {
7
8
  const [p, a, r] = o.split(" ").map(Number);
8
9
  return (0.2126 * p + 0.7152 * a + 0.0722 * r) / 255 > 0.45 ? "15 23 42" : "255 255 255";
9
- }, h = (o, p) => {
10
+ }, f = (o, p) => {
10
11
  if (!o || typeof o != "object") return;
11
12
  const a = p || document.documentElement, r = { ...o }, t = r["--app-primary-color"];
12
- t && (r["--app-primary-foreground-color"] = d(t));
13
+ t && (r["--app-primary-foreground-color"] = g(t));
13
14
  const l = r["--app-bg-color"];
14
- l && (r["--app-text-color"] = d(l)), Object.entries({
15
+ l && (r["--app-text-color"] = g(l)), Object.entries({
15
16
  "--primary": "--app-primary-color",
16
17
  "--bg": "--app-bg-color",
17
18
  "--text": "--app-text-color",
@@ -32,33 +33,44 @@ const m = (o, p) => {
32
33
  ].join(", "), r["--app-secondary-color"] || (r["--app-secondary-color"] = "100 116 139"), r["--app-accent-color"] || (r["--app-accent-color"] = r["--app-primary-color"]), r["--app-input-bg-color"] || (r["--app-input-bg-color"] = r["--app-card-bg-color"]), r["--app-input-border-color"] || (r["--app-input-border-color"] = r["--app-border-color"]), r["--app-border-radius-md"] || (r["--app-border-radius-md"] = "0.375rem"), Object.entries(r).forEach(([s, i]) => {
33
34
  s.startsWith("--app-") && a.style.setProperty(s, String(i));
34
35
  });
35
- }, u = (o, p) => {
36
+ }, b = (o, p) => {
36
37
  const a = document.head, r = "dynamic-google-fonts", t = document.getElementById(r);
37
38
  t && a.removeChild(t);
38
39
  const l = `${o.replace(/ /g, "+")}:wght@400;700&family=${p.replace(/ /g, "+")}:wght@400;500;700`, n = document.createElement("link");
39
40
  n.id = r, n.rel = "stylesheet", n.crossOrigin = "anonymous", n.href = `https://fonts.googleapis.com/css2?family=${l}&display=swap`, a.insertBefore(n, a.firstChild);
40
41
  const e = document.documentElement;
41
42
  e.style.setProperty("--app-font-sans", `"${p}", sans-serif`), e.style.setProperty("--app-font-serif", `"${o}", serif`), console.log(`[FontEngine] Applied: ${o} (H) / ${p} (B)`);
42
- }, f = (o) => {
43
+ }, y = (o) => {
43
44
  const p = o.replace("#", ""), a = parseInt(p.substring(0, 2), 16), r = parseInt(p.substring(2, 4), 16), t = parseInt(p.substring(4, 6), 16);
44
45
  return `${a} ${r} ${t}`;
45
- }, g = {
46
+ }, u = {
46
47
  none: "0",
47
48
  sm: "0.25rem",
48
49
  md: "0.375rem",
49
50
  lg: "0.5rem",
50
51
  full: "9999px"
51
- }, b = (o) => ({
52
+ }, $ = [
53
+ "--app-primary-color",
54
+ "--app-secondary-color",
55
+ "--app-success-color",
56
+ "--app-warning-color",
57
+ "--app-danger-color",
58
+ "--app-border-radius-md"
59
+ ], E = (o) => o ? Object.keys(d).some((p) => o[p] !== void 0 && o[p] !== d[p]) : !1, M = (o) => ({
52
60
  "--app-primary-color": o.primary ?? "13 110 253",
53
61
  "--app-secondary-color": o.neutral ?? "24 24 27",
54
62
  "--app-success-color": o.success ?? "34 197 94",
55
63
  "--app-warning-color": o.warning ?? "234 179 8",
56
- "--app-destructive-color": o.destructive ?? "239 68 68",
57
- "--app-border-radius-md": g[o.radius ?? "md"] ?? "0.375rem"
64
+ // NOT `--app-destructive-color`, which this wrote until now: that variable
65
+ // is defined nowhere in index.css and read by nothing, so the Destructive
66
+ // swatch in ThemeLab changed a value no stylesheet consumed. The token the
67
+ // preset actually maps `destructive` on to is `--app-danger-color`.
68
+ "--app-danger-color": o.destructive ?? "239 68 68",
69
+ "--app-border-radius-md": u[o.radius ?? "md"] ?? "0.375rem"
58
70
  }), c = (o = 0, p = 255) => {
59
71
  const a = Math.floor(Math.random() * (p - o) + o), r = Math.floor(Math.random() * (p - o) + o), t = Math.floor(Math.random() * (p - o) + o);
60
72
  return `${a} ${r} ${t}`;
61
- }, y = () => {
73
+ }, T = () => {
62
74
  const o = Math.random() > 0.5, p = o ? c(10, 40) : c(240, 255), a = o ? c(220, 255) : c(20, 60), r = c(50, 200);
63
75
  return {
64
76
  "--app-primary-color": r,
@@ -73,11 +85,13 @@ const m = (o, p) => {
73
85
  };
74
86
  };
75
87
  export {
76
- h as applyThemeToDom,
77
- y as generateRandomTheme,
78
- d as getContrastColor,
79
- f as hexToRgbTriplet,
80
- u as loadGoogleFont,
88
+ $ as THEME_TOKEN_VARS,
89
+ f as applyThemeToDom,
90
+ T as generateRandomTheme,
91
+ g as getContrastColor,
92
+ y as hexToRgbTriplet,
93
+ E as isCustomTheme,
94
+ b as loadGoogleFont,
81
95
  c as randomRgb,
82
- b as tokensToVars
96
+ M as tokensToVars
83
97
  };
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom/vitest';