equilibria-react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,136 @@
1
+ # equilibria-react
2
+
3
+ React components for the [Equilibria Engine](https://github.com/Kinetonomics-Equilibria/KGJS-Equilibria) — styled, drop-in chart cards with lifecycle management, error handling, and responsive sizing.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install equilibria-react equilibria-engine-js
9
+ ```
10
+
11
+ ### Required CSS
12
+
13
+ Import the engine and component styles in your app's entry point:
14
+
15
+ ```tsx
16
+ import "equilibria-engine-js/dist/style.css"; // Engine theme
17
+ import "katex/dist/katex.min.css"; // Math typography
18
+ import "equilibria-react/dist/style.css"; // Card styles
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ### `<EquilibriaCard />` — Drop-in styled card
24
+
25
+ ```tsx
26
+ import { EquilibriaCard } from 'equilibria-react';
27
+
28
+ const config = {
29
+ params: [{ name: "price", value: 10, min: 0, max: 20, round: 0.1 }],
30
+ calcs: { revenue: "price * 5" },
31
+ layout: {
32
+ OneGraph: {
33
+ graph: {
34
+ xAxis: { title: "Quantity", min: 0, max: 20 },
35
+ yAxis: { title: "Price ($)", min: 0, max: 20 },
36
+ objects: [
37
+ { type: "Point", def: { x: "10", y: "price", color: "blue", draggable: true } }
38
+ ]
39
+ }
40
+ }
41
+ }
42
+ };
43
+
44
+ function App() {
45
+ return (
46
+ <EquilibriaCard
47
+ config={config}
48
+ title="Interactive Pricing"
49
+ description="Drag the point to adjust price"
50
+ variant="elevated"
51
+ />
52
+ );
53
+ }
54
+ ```
55
+
56
+ ### `<EquilibriaChart />` — Minimal (no card chrome)
57
+
58
+ ```tsx
59
+ import { EquilibriaChart } from 'equilibria-react';
60
+
61
+ function App() {
62
+ return (
63
+ <EquilibriaChart
64
+ config={config}
65
+ style={{ width: '100%', maxWidth: 600 }}
66
+ onReady={() => console.log('Chart rendered')}
67
+ onError={(err) => console.error(err)}
68
+ />
69
+ );
70
+ }
71
+ ```
72
+
73
+ ### `useEquilibria()` — Full control hook
74
+
75
+ ```tsx
76
+ import { useEquilibria } from 'equilibria-react';
77
+
78
+ function CustomChart({ config }) {
79
+ const { containerRef, isReady, error, retry } = useEquilibria(config);
80
+
81
+ return (
82
+ <div>
83
+ {!isReady && !error && <p>Loading...</p>}
84
+ {error && <button onClick={retry}>Retry</button>}
85
+ <div ref={containerRef} style={{ width: '100%' }} />
86
+ </div>
87
+ );
88
+ }
89
+ ```
90
+
91
+ ## API Reference
92
+
93
+ ### `<EquilibriaCard />` Props
94
+
95
+ | Prop | Type | Default | Description |
96
+ |------|------|---------|-------------|
97
+ | `config` | `object` | **required** | Engine config (JSON/parsed YAML) |
98
+ | `options` | `KineticGraphOptions` | `{}` | Engine constructor options |
99
+ | `title` | `string` | — | Card title |
100
+ | `description` | `string` | — | Subtitle text |
101
+ | `footer` | `ReactNode` | — | Footer content slot |
102
+ | `variant` | `'elevated' \| 'outlined' \| 'flat'` | `'elevated'` | Visual style |
103
+ | `loading` | `boolean` | auto | Override loading state |
104
+ | `errorFallback` | `ReactNode \| (err) => ReactNode` | built-in | Custom error UI |
105
+ | `className` | `string` | — | Additional CSS class |
106
+ | `style` | `CSSProperties` | — | Inline styles |
107
+ | `onError` | `(err) => void` | — | Error callback |
108
+ | `onReady` | `() => void` | — | Fires after mount |
109
+
110
+ ### `<EquilibriaChart />` Props
111
+
112
+ All of the above **except** `title`, `description`, `footer`, `variant`, `loading`, and `errorFallback`.
113
+
114
+ ## Theming
115
+
116
+ Override CSS custom properties to match your design system:
117
+
118
+ ```css
119
+ :root {
120
+ --eq-card-bg: #1a1a2e;
121
+ --eq-card-border: #2d2d44;
122
+ --eq-card-radius: 16px;
123
+ --eq-card-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
124
+ --eq-title-color: #e0e0e0;
125
+ --eq-description-color: #9090a0;
126
+ --eq-error-bg: #2d1b1b;
127
+ --eq-error-color: #ff6b6b;
128
+ --eq-skeleton-color: #2d2d44;
129
+ }
130
+ ```
131
+
132
+ These properties are scoped to the `equilibria-react` components and won't affect the engine's own `--kg-*` variables.
133
+
134
+ ## License
135
+
136
+ MIT
@@ -0,0 +1,9 @@
1
+ import { EquilibriaCardProps } from './types';
2
+
3
+ /**
4
+ * Styled card component wrapping the Equilibria engine.
5
+ * Includes title, description, loading skeleton, error state, and footer slot.
6
+ *
7
+ * This is the recommended "drop-in" component for rendering charts.
8
+ */
9
+ export declare function EquilibriaCard({ config, options, className, style, onError: _onError, onReady: _onReady, title, description, footer, loading: loadingOverride, errorFallback, variant, }: EquilibriaCardProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,11 @@
1
+ import { EquilibriaChartProps } from './types';
2
+
3
+ /**
4
+ * Minimal chart component — mounts the Equilibria engine into a div container
5
+ * with no additional chrome. Use this when you want full control over the
6
+ * surrounding UI and only need the graph itself.
7
+ *
8
+ * For a styled wrapper with title, description, loading state, and error
9
+ * handling, use `<EquilibriaCard />` instead.
10
+ */
11
+ export declare function EquilibriaChart({ config, options, className, style, onError, onReady, }: EquilibriaChartProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});require("equilibria-engine-js/dist/style.css");require("katex/dist/katex.min.css");const n=require("react/jsx-runtime"),a=require("react"),N=require("equilibria-engine-js");function y(d,m){const h=a.useRef(null),s=a.useRef(null),[f,i]=a.useState(null),[u,t]=a.useState(null),[l,o]=a.useState(!1),c=a.useCallback(()=>{if(s.current){try{s.current.destroy()}catch{}s.current=null,i(null),o(!1)}t(null);const k=h.current;if(!(!k||!d))try{const r=new N.KineticGraph(d,m);r.on("error",_=>{t(_ instanceof Error?_:new Error(String(_)))}),r.mount(k),s.current=r,i(r),o(!0)}catch(r){const _=r instanceof Error?r:new Error(String(r));t(_),o(!1)}},[d,m]);a.useEffect(()=>(c(),()=>{if(s.current){try{s.current.destroy()}catch{}s.current=null,i(null),o(!1)}}),[c]);const C=a.useCallback(()=>{c()},[c]);return{containerRef:h,instance:f,error:u,isReady:l,retry:C}}const g="_chartContainer_k1m9p_61",x="_chartContainerLoading_k1m9p_69",q="_chartContainerReady_k1m9p_75",v="_card_k1m9p_83",b="_header_k1m9p_130",S="_title_k1m9p_134",B="_description_k1m9p_144",I="_chartArea_k1m9p_153",L="_skeleton_k1m9p_163",M="_skeletonHidden_k1m9p_175",P="_skeletonPulse_k1m9p_180",$="_errorContainer_k1m9p_202",A="_errorIcon_k1m9p_226",H="_errorMessage_k1m9p_233",w="_retryButton_k1m9p_243",G="_footer_k1m9p_271",e={chartContainer:g,chartContainerLoading:x,chartContainerReady:q,card:v,"card--elevated":"_card--elevated_k1m9p_98","card--outlined":"_card--outlined_k1m9p_106","card--flat":"_card--flat_k1m9p_117",header:b,title:S,description:B,chartArea:I,skeleton:L,skeletonHidden:M,skeletonPulse:P,"eq-shimmer":"_eq-shimmer_k1m9p_1",errorContainer:$,"fade-in":"_fade-in_k1m9p_1",errorIcon:A,errorMessage:H,retryButton:w,footer:G};function J({config:d,options:m,className:h,style:s,onError:f,onReady:i}){const{containerRef:u,error:t,isReady:l}=y(d,m);a.useEffect(()=>{t&&f&&f(t)},[t,f]),a.useEffect(()=>{l&&i&&i()},[l,i]);const o=[e.chartContainer,!l||t?e.chartContainerLoading:e.chartContainerReady,h].filter(Boolean).join(" ");return n.jsx("div",{ref:u,className:o,style:s})}function K({config:d,options:m,className:h,style:s,onError:f,onReady:i,title:u,description:t,footer:l,loading:o,errorFallback:c,variant:C="elevated"}){const{containerRef:k,error:r,isReady:_,retry:R}=y(d,m),j=o!==void 0?o:!_&&!r,E=[e.card,e[`card--${C}`],h].filter(Boolean).join(" ");let p=null;return r&&(c?p=typeof c=="function"?c(r):c:p=n.jsxs("div",{className:e.errorContainer,children:[n.jsx("div",{className:e.errorIcon,children:"⚠"}),n.jsx("p",{className:e.errorMessage,children:r.message||"Failed to render chart"}),n.jsx("button",{className:e.retryButton,onClick:R,children:"Retry"})]})),n.jsxs("div",{className:E,style:s,children:[(u||t)&&n.jsxs("div",{className:e.header,children:[u&&n.jsx("h3",{className:e.title,children:u}),t&&n.jsx("p",{className:e.description,children:t})]}),n.jsxs("div",{className:e.chartArea,children:[n.jsx("div",{className:`${e.skeleton} ${!j||r?e.skeletonHidden:""}`,children:n.jsx("div",{className:e.skeletonPulse})}),r&&p,n.jsx("div",{ref:k,className:`${e.chartContainer} ${j||r?e.chartContainerLoading:e.chartContainerReady}`})]}),l&&n.jsx("div",{className:e.footer,children:l})]})}exports.EquilibriaCard=K;exports.EquilibriaChart=J;exports.useEquilibria=y;
@@ -0,0 +1,140 @@
1
+ import "equilibria-engine-js/dist/style.css";
2
+ import "katex/dist/katex.min.css";
3
+ import { jsx as a, jsxs as k } from "react/jsx-runtime";
4
+ import { useRef as v, useState as y, useCallback as g, useEffect as N } from "react";
5
+ import { KineticGraph as L } from "equilibria-engine-js";
6
+ function E(l, m) {
7
+ const u = v(null), t = v(null), [h, c] = y(null), [d, n] = y(null), [i, o] = y(!1), s = g(() => {
8
+ if (t.current) {
9
+ try {
10
+ t.current.destroy();
11
+ } catch {
12
+ }
13
+ t.current = null, c(null), o(!1);
14
+ }
15
+ n(null);
16
+ const f = u.current;
17
+ if (!(!f || !l))
18
+ try {
19
+ const r = new L(l, m);
20
+ r.on("error", (_) => {
21
+ n(_ instanceof Error ? _ : new Error(String(_)));
22
+ }), r.mount(f), t.current = r, c(r), o(!0);
23
+ } catch (r) {
24
+ const _ = r instanceof Error ? r : new Error(String(r));
25
+ n(_), o(!1);
26
+ }
27
+ }, [l, m]);
28
+ N(() => (s(), () => {
29
+ if (t.current) {
30
+ try {
31
+ t.current.destroy();
32
+ } catch {
33
+ }
34
+ t.current = null, c(null), o(!1);
35
+ }
36
+ }), [s]);
37
+ const p = g(() => {
38
+ s();
39
+ }, [s]);
40
+ return { containerRef: u, instance: h, error: d, isReady: i, retry: p };
41
+ }
42
+ const q = "_chartContainer_k1m9p_61", $ = "_chartContainerLoading_k1m9p_69", j = "_chartContainerReady_k1m9p_75", A = "_card_k1m9p_83", H = "_header_k1m9p_130", M = "_title_k1m9p_134", P = "_description_k1m9p_144", w = "_chartArea_k1m9p_153", x = "_skeleton_k1m9p_163", S = "_skeletonHidden_k1m9p_175", b = "_skeletonPulse_k1m9p_180", G = "_errorContainer_k1m9p_202", K = "_errorIcon_k1m9p_226", z = "_errorMessage_k1m9p_233", D = "_retryButton_k1m9p_243", J = "_footer_k1m9p_271", e = {
43
+ chartContainer: q,
44
+ chartContainerLoading: $,
45
+ chartContainerReady: j,
46
+ card: A,
47
+ "card--elevated": "_card--elevated_k1m9p_98",
48
+ "card--outlined": "_card--outlined_k1m9p_106",
49
+ "card--flat": "_card--flat_k1m9p_117",
50
+ header: H,
51
+ title: M,
52
+ description: P,
53
+ chartArea: w,
54
+ skeleton: x,
55
+ skeletonHidden: S,
56
+ skeletonPulse: b,
57
+ "eq-shimmer": "_eq-shimmer_k1m9p_1",
58
+ errorContainer: G,
59
+ "fade-in": "_fade-in_k1m9p_1",
60
+ errorIcon: K,
61
+ errorMessage: z,
62
+ retryButton: D,
63
+ footer: J
64
+ };
65
+ function X({
66
+ config: l,
67
+ options: m,
68
+ className: u,
69
+ style: t,
70
+ onError: h,
71
+ onReady: c
72
+ }) {
73
+ const { containerRef: d, error: n, isReady: i } = E(l, m);
74
+ N(() => {
75
+ n && h && h(n);
76
+ }, [n, h]), N(() => {
77
+ i && c && c();
78
+ }, [i, c]);
79
+ const o = [
80
+ e.chartContainer,
81
+ !i || n ? e.chartContainerLoading : e.chartContainerReady,
82
+ u
83
+ ].filter(Boolean).join(" ");
84
+ return /* @__PURE__ */ a(
85
+ "div",
86
+ {
87
+ ref: d,
88
+ className: o,
89
+ style: t
90
+ }
91
+ );
92
+ }
93
+ function Y({
94
+ config: l,
95
+ options: m,
96
+ className: u,
97
+ style: t,
98
+ onError: h,
99
+ onReady: c,
100
+ title: d,
101
+ description: n,
102
+ footer: i,
103
+ loading: o,
104
+ errorFallback: s,
105
+ variant: p = "elevated"
106
+ }) {
107
+ const { containerRef: f, error: r, isReady: _, retry: B } = E(l, m), R = o !== void 0 ? o : !_ && !r, I = [
108
+ e.card,
109
+ e[`card--${p}`],
110
+ u
111
+ ].filter(Boolean).join(" ");
112
+ let C = null;
113
+ return r && (s ? C = typeof s == "function" ? s(r) : s : C = /* @__PURE__ */ k("div", { className: e.errorContainer, children: [
114
+ /* @__PURE__ */ a("div", { className: e.errorIcon, children: "⚠" }),
115
+ /* @__PURE__ */ a("p", { className: e.errorMessage, children: r.message || "Failed to render chart" }),
116
+ /* @__PURE__ */ a("button", { className: e.retryButton, onClick: B, children: "Retry" })
117
+ ] })), /* @__PURE__ */ k("div", { className: I, style: t, children: [
118
+ (d || n) && /* @__PURE__ */ k("div", { className: e.header, children: [
119
+ d && /* @__PURE__ */ a("h3", { className: e.title, children: d }),
120
+ n && /* @__PURE__ */ a("p", { className: e.description, children: n })
121
+ ] }),
122
+ /* @__PURE__ */ k("div", { className: e.chartArea, children: [
123
+ /* @__PURE__ */ a("div", { className: `${e.skeleton} ${!R || r ? e.skeletonHidden : ""}`, children: /* @__PURE__ */ a("div", { className: e.skeletonPulse }) }),
124
+ r && C,
125
+ /* @__PURE__ */ a(
126
+ "div",
127
+ {
128
+ ref: f,
129
+ className: `${e.chartContainer} ${R || r ? e.chartContainerLoading : e.chartContainerReady}`
130
+ }
131
+ )
132
+ ] }),
133
+ i && /* @__PURE__ */ a("div", { className: e.footer, children: i })
134
+ ] });
135
+ }
136
+ export {
137
+ Y as EquilibriaCard,
138
+ X as EquilibriaChart,
139
+ E as useEquilibria
140
+ };
@@ -0,0 +1,6 @@
1
+
2
+ export { EquilibriaChart } from './EquilibriaChart';
3
+ export { EquilibriaCard } from './EquilibriaCard';
4
+ export { useEquilibria } from './useEquilibria';
5
+ export type { EquilibriaChartProps, EquilibriaCardProps, CardVariant } from './types';
6
+ export type { UseEquilibriaReturn, UseEquilibriaOptions } from './useEquilibria';
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ :root{--eq-card-bg: rgba(255, 255, 255, .75);--eq-card-border: rgba(255, 255, 255, .5);--eq-card-shadow-color: rgba(99, 102, 241, .08);--eq-card-shadow: 0 8px 32px var(--eq-card-shadow-color), inset 0 0 0 1px var(--eq-card-border);--eq-card-shadow-hover: 0 14px 48px rgba(99, 102, 241, .15), inset 0 0 0 1px var(--eq-card-border);--eq-card-blur: 16px;--eq-card-radius: 20px;--eq-card-padding: 1.75rem;--eq-font-family: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;--eq-title-color: #0f172a;--eq-title-size: 1.2rem;--eq-title-weight: 600;--eq-description-color: #475569;--eq-description-size: .95rem;--eq-error-bg: rgba(254, 226, 226, .8);--eq-error-color: #991b1b;--eq-error-border: rgba(248, 113, 113, .3);--eq-skeleton-bg: rgba(241, 245, 249, .6);--eq-skeleton-color-1: rgba(99, 102, 241, .08);--eq-skeleton-color-2: rgba(168, 85, 247, .08);--eq-skeleton-color-3: rgba(236, 72, 153, .08)}@media (prefers-color-scheme: dark){:root{--eq-card-bg: rgba(15, 23, 42, .65);--eq-card-border: rgba(255, 255, 255, .06);--eq-card-shadow-color: rgba(0, 0, 0, .4);--eq-card-shadow: 0 8px 32px var(--eq-card-shadow-color), inset 0 0 0 1px var(--eq-card-border);--eq-card-shadow-hover: 0 14px 48px rgba(0, 0, 0, .6), inset 0 0 0 1px rgba(255, 255, 255, .12);--eq-title-color: #f8fafc;--eq-description-color: #94a3b8;--eq-error-bg: rgba(127, 29, 29, .4);--eq-error-color: #fca5a5;--eq-error-border: rgba(248, 113, 113, .2);--eq-skeleton-bg: rgba(30, 41, 59, .5);--eq-skeleton-color-1: rgba(99, 102, 241, .15);--eq-skeleton-color-2: rgba(168, 85, 247, .15);--eq-skeleton-color-3: rgba(236, 72, 153, .15)}}._chartContainer_k1m9p_61{width:100%;min-height:120px;position:relative;transition:opacity .8s cubic-bezier(.16,1,.3,1),filter .8s cubic-bezier(.16,1,.3,1)}._chartContainerLoading_k1m9p_69{opacity:0!important;filter:blur(8px)!important;pointer-events:none!important}._chartContainerReady_k1m9p_75{opacity:1!important;filter:blur(0)!important;pointer-events:auto!important}._card_k1m9p_83{font-family:var(--eq-font-family);background:var(--eq-card-bg);backdrop-filter:blur(var(--eq-card-blur));-webkit-backdrop-filter:blur(var(--eq-card-blur));border-radius:var(--eq-card-radius);overflow:hidden;transition:box-shadow .4s cubic-bezier(.16,1,.3,1),transform .4s cubic-bezier(.16,1,.3,1)}._card_k1m9p_83:hover{transform:translateY(-2px)}._card--elevated_k1m9p_98{box-shadow:var(--eq-card-shadow)}._card--elevated_k1m9p_98:hover{box-shadow:var(--eq-card-shadow-hover)}._card--outlined_k1m9p_106{box-shadow:inset 0 0 0 1px var(--eq-card-border);background:transparent;backdrop-filter:none;-webkit-backdrop-filter:none}._card--outlined_k1m9p_106:hover{box-shadow:inset 0 0 0 1px #6366f180}._card--flat_k1m9p_117{box-shadow:none;background:transparent;backdrop-filter:none;-webkit-backdrop-filter:none}._card--flat_k1m9p_117:hover{transform:none}._header_k1m9p_130{padding:var(--eq-card-padding) var(--eq-card-padding) 0}._title_k1m9p_134{margin:0;font-family:var(--eq-font-family);font-size:var(--eq-title-size);font-weight:var(--eq-title-weight);color:var(--eq-title-color);line-height:1.4;letter-spacing:-.02em}._description_k1m9p_144{margin:.4rem 0 0;font-size:var(--eq-description-size);color:var(--eq-description-color);line-height:1.6}._chartArea_k1m9p_153{position:relative;padding:var(--eq-card-padding);min-height:200px;display:flex;flex-direction:column}._skeleton_k1m9p_163{position:absolute;inset:var(--eq-card-padding);border-radius:12px;overflow:hidden;background:var(--eq-skeleton-bg);opacity:1;transition:opacity .6s cubic-bezier(.16,1,.3,1),transform .6s cubic-bezier(.16,1,.3,1);z-index:2;pointer-events:none}._skeletonHidden_k1m9p_175{opacity:0;transform:scale(.98)}._skeletonPulse_k1m9p_180{width:100%;height:100%;background:linear-gradient(100deg,transparent 20%,var(--eq-skeleton-color-1) 35%,var(--eq-skeleton-color-2) 50%,var(--eq-skeleton-color-3) 65%,transparent 80%);background-size:300% 100%;animation:_eq-shimmer_k1m9p_1 2.5s cubic-bezier(.4,0,.2,1) infinite}@keyframes _eq-shimmer_k1m9p_1{0%{background-position:200% 0}to{background-position:-100% 0}}._errorContainer_k1m9p_202{position:absolute;inset:var(--eq-card-padding);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1rem;padding:2rem 1.5rem;background:var(--eq-error-bg);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);border:1px solid var(--eq-error-border);border-radius:12px;text-align:center;z-index:3;animation:_fade-in_k1m9p_1 .4s ease-out}@keyframes _fade-in_k1m9p_1{0%{opacity:0;transform:translateY(5px)}to{opacity:1;transform:translateY(0)}}._errorIcon_k1m9p_226{font-size:2rem;line-height:1;color:var(--eq-error-color);filter:drop-shadow(0 0 8px rgba(248,113,113,.4))}._errorMessage_k1m9p_233{margin:0;font-family:var(--eq-font-family);font-size:.95rem;color:var(--eq-error-color);max-width:320px;word-break:break-word;font-weight:500}._retryButton_k1m9p_243{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:var(--eq-error-color);color:#fff;font-family:var(--eq-font-family);font-size:.85rem;font-weight:600;padding:.5rem 1.2rem;border:none;border-radius:8px;cursor:pointer;box-shadow:0 4px 12px #991b1b33;transition:transform .2s ease,box-shadow .2s ease,background .2s ease}._retryButton_k1m9p_243:hover{background:#7f1d1d;transform:translateY(-1px);box-shadow:0 6px 16px #991b1b4d}._retryButton_k1m9p_243:active{transform:translateY(1px);box-shadow:0 2px 8px #991b1b33}._footer_k1m9p_271{padding:0 var(--eq-card-padding) var(--eq-card-padding);font-family:var(--eq-font-family);font-size:var(--eq-description-size);color:var(--eq-description-color);border-top:1px solid var(--eq-card-border);margin-top:0;padding-top:var(--eq-card-padding)}
@@ -0,0 +1,41 @@
1
+ import { CSSProperties, ReactNode } from 'react';
2
+ import { KineticGraphOptions } from 'equilibria-engine-js';
3
+
4
+ /**
5
+ * Props for the minimal EquilibriaChart component.
6
+ */
7
+ export interface EquilibriaChartProps {
8
+ /** Engine configuration object (parsed JSON/YAML). */
9
+ config: Record<string, unknown>;
10
+ /** Engine options passed to the KineticGraph constructor. */
11
+ options?: KineticGraphOptions;
12
+ /** Additional CSS class name applied to the chart container. */
13
+ className?: string;
14
+ /** Inline styles applied to the chart container. */
15
+ style?: CSSProperties;
16
+ /** Callback fired when the engine encounters an error during mount. */
17
+ onError?: (error: Error) => void;
18
+ /** Callback fired after the engine successfully mounts and renders. */
19
+ onReady?: () => void;
20
+ }
21
+ /**
22
+ * Card style variants.
23
+ */
24
+ export type CardVariant = 'elevated' | 'outlined' | 'flat';
25
+ /**
26
+ * Props for the styled EquilibriaCard component.
27
+ */
28
+ export interface EquilibriaCardProps extends EquilibriaChartProps {
29
+ /** Card title displayed above the chart. */
30
+ title?: string;
31
+ /** Subtitle / description displayed below the title. */
32
+ description?: string;
33
+ /** Footer content rendered below the chart. */
34
+ footer?: ReactNode;
35
+ /** Override the loading state (auto-detected by default). */
36
+ loading?: boolean;
37
+ /** Custom error UI. Can be a ReactNode or a render function receiving the error. */
38
+ errorFallback?: ReactNode | ((error: Error) => ReactNode);
39
+ /** Card container style variant. Default: 'elevated'. */
40
+ variant?: CardVariant;
41
+ }
@@ -0,0 +1,25 @@
1
+ import { KineticGraph, KineticGraphOptions } from 'equilibria-engine-js';
2
+
3
+ export interface UseEquilibriaOptions extends KineticGraphOptions {
4
+ }
5
+ export interface UseEquilibriaReturn {
6
+ /** Ref to attach to the container div element. */
7
+ containerRef: React.RefObject<HTMLDivElement>;
8
+ /** The KineticGraph instance (null until mounted). */
9
+ instance: KineticGraph | null;
10
+ /** Error caught during mount, if any. */
11
+ error: Error | null;
12
+ /** Whether the engine has successfully mounted. */
13
+ isReady: boolean;
14
+ /** Manually retry mounting after an error. */
15
+ retry: () => void;
16
+ }
17
+ /**
18
+ * Core hook that manages the KineticGraph lifecycle.
19
+ *
20
+ * - Creates and mounts the engine when the ref is attached
21
+ * - Destroys the engine on unmount
22
+ * - Re-mounts when the config identity changes
23
+ * - Surfaces errors and loading state
24
+ */
25
+ export declare function useEquilibria(config: Record<string, unknown>, options?: UseEquilibriaOptions): UseEquilibriaReturn;
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "equilibria-react",
3
+ "version": "0.1.0",
4
+ "description": "React components for the Equilibria Engine — styled, drop-in chart cards with lifecycle management",
5
+ "type": "module",
6
+ "main": "dist/equilibria-react.cjs.js",
7
+ "module": "dist/equilibria-react.es.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/equilibria-react.es.js",
12
+ "require": "./dist/equilibria-react.cjs.js",
13
+ "types": "./dist/index.d.ts"
14
+ },
15
+ "./dist/style.css": "./dist/style.css"
16
+ },
17
+ "scripts": {
18
+ "dev": "vite",
19
+ "build": "vite build",
20
+ "preview": "vite preview"
21
+ },
22
+ "peerDependencies": {
23
+ "react": "^18.0.0 || ^19.0.0",
24
+ "react-dom": "^18.0.0 || ^19.0.0",
25
+ "equilibria-engine-js": "^1.0.0"
26
+ },
27
+ "devDependencies": {
28
+ "@types/react": "^18.2.0",
29
+ "@types/react-dom": "^18.2.0",
30
+ "react": "^18.2.0",
31
+ "react-dom": "^18.2.0",
32
+ "equilibria-engine-js": "file:../equilibria-engine-js",
33
+ "typescript": "^5.2.2",
34
+ "vite": "^4.4.9",
35
+ "vite-plugin-dts": "^3.5.3"
36
+ },
37
+ "files": [
38
+ "dist",
39
+ "README.md"
40
+ ],
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/Kinetonomics-Equilibria/equilibria-react.git"
44
+ },
45
+ "author": "Jarryd Bentley",
46
+ "license": "MIT",
47
+ "keywords": [
48
+ "equilibria",
49
+ "react",
50
+ "charts",
51
+ "economics",
52
+ "graphs",
53
+ "d3",
54
+ "interactive"
55
+ ]
56
+ }