@trade-it/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/CHANGELOG.md ADDED
@@ -0,0 +1,21 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@trade-it/react` will be documented in this file.
4
+
5
+ ## 0.1.0 - 2026-02-25
6
+
7
+ Initial public SDK release (v1 foundation).
8
+
9
+ ### Added
10
+
11
+ - Responsive iframe modal for embedded Trade It connect and trade experiences
12
+ - Declarative `<TradeItModal />` API
13
+ - Imperative `useTradeIt()` hook
14
+ - `postMessage` event forwarding for embedded lifecycle and trade events
15
+ - Trade URL template support (`[ticker]`) with SDK-side query serialization
16
+ - ESM + CommonJS builds with bundled runtime dependencies (excluding peers)
17
+
18
+ ### Notes
19
+
20
+ - `@trade-it/react` depends on `@trade-it/core`
21
+ - Trade It session URLs should be created by your backend via `POST /api/session/url`
package/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # `@trade-it/react`
2
+
3
+ React SDK for embedding Trade It connect and trade experiences directly inside your app.
4
+
5
+ Use this package when you want to:
6
+
7
+ - open the Trade It connect flow in a modal
8
+ - open prefilled trade flows in a modal
9
+ - keep users on your site while they connect brokerages and place trades
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm i @trade-it/react
15
+ ```
16
+
17
+ Peer dependencies:
18
+
19
+ - `react` (`^18 || ^19`)
20
+ - `react-dom` (`^18 || ^19`)
21
+
22
+ ## Documentation
23
+
24
+ Full integration guides, examples, and API references:
25
+
26
+ - [React SDK Overview](https://docs.tradeit.app/sdk)
27
+ - [Authentication](https://docs.tradeit.app/sdk/authentication)
28
+ - [Connect a Brokerage](https://docs.tradeit.app/sdk/connect)
29
+ - [Place Trades](https://docs.tradeit.app/sdk/trades)
30
+ - [SDK Enums Reference](https://docs.tradeit.app/sdk/enums)
31
+
32
+ ## Notes
33
+
34
+ - Request Trade It session URLs from your server, not from the browser.
35
+ - `@trade-it/react` depends on `@trade-it/core`.
package/dist/index.cjs ADDED
@@ -0,0 +1,454 @@
1
+ //#region rolldown:runtime
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+
23
+ //#endregion
24
+ const __trade_it_core = __toESM(require("@trade-it/core"));
25
+ const react = __toESM(require("react"));
26
+ const react_dom = __toESM(require("react-dom"));
27
+ const react_jsx_runtime = __toESM(require("react/jsx-runtime"));
28
+
29
+ //#region src/LoaderCircleSpinner.tsx
30
+ const SPINNER_WRAPPER_SIZE = 44;
31
+ const SPINNER_ICON_SIZE = 32;
32
+ const SPINNER_WRAPPER_STYLE = {
33
+ width: SPINNER_WRAPPER_SIZE,
34
+ height: SPINNER_WRAPPER_SIZE,
35
+ display: "flex",
36
+ alignItems: "center",
37
+ justifyContent: "center",
38
+ animation: "tradeItSdkSpin 0.9s linear infinite",
39
+ transformOrigin: "50% 50%"
40
+ };
41
+ const SPINNER_SVG_STYLE = {
42
+ display: "block",
43
+ width: SPINNER_ICON_SIZE,
44
+ height: SPINNER_ICON_SIZE,
45
+ color: "#111827"
46
+ };
47
+ const SPINNER_KEYFRAMES_CSS = `
48
+ @keyframes tradeItSdkSpin {
49
+ from { transform: rotate(0deg); }
50
+ to { transform: rotate(360deg); }
51
+ }
52
+ @media (prefers-reduced-motion: reduce) {
53
+ .tradeit-sdk-spinner {
54
+ animation: none !important;
55
+ }
56
+ }
57
+ `;
58
+ function LoaderCircleSpinner() {
59
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("style", { children: SPINNER_KEYFRAMES_CSS }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
60
+ className: "tradeit-sdk-spinner",
61
+ style: SPINNER_WRAPPER_STYLE,
62
+ "aria-hidden": "true",
63
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
64
+ xmlns: "http://www.w3.org/2000/svg",
65
+ width: SPINNER_ICON_SIZE,
66
+ height: SPINNER_ICON_SIZE,
67
+ viewBox: "0 0 24 24",
68
+ fill: "none",
69
+ stroke: "currentColor",
70
+ strokeWidth: "2",
71
+ strokeLinecap: "round",
72
+ strokeLinejoin: "round",
73
+ style: SPINNER_SVG_STYLE,
74
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
75
+ })
76
+ })] });
77
+ }
78
+
79
+ //#endregion
80
+ //#region src/TradeItModalLoader.tsx
81
+ const LOADING_STYLE = {
82
+ position: "absolute",
83
+ inset: 0,
84
+ background: "#ffffff"
85
+ };
86
+ const LOADING_CONTENT_STYLE = {
87
+ position: "relative",
88
+ zIndex: 1,
89
+ display: "flex",
90
+ flexDirection: "column",
91
+ alignItems: "center",
92
+ justifyContent: "center",
93
+ width: "100%",
94
+ height: "100%"
95
+ };
96
+ function TradeItModalLoader() {
97
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
98
+ style: LOADING_STYLE,
99
+ "aria-live": "polite",
100
+ "aria-busy": "true",
101
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
102
+ style: LOADING_CONTENT_STYLE,
103
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(LoaderCircleSpinner, {})
104
+ })
105
+ });
106
+ }
107
+
108
+ //#endregion
109
+ //#region src/TradeItModal.tsx
110
+ const OVERLAY_STYLE = {
111
+ position: "fixed",
112
+ inset: 0,
113
+ background: "rgba(15, 23, 42, 0.55)",
114
+ backdropFilter: "blur(4px)",
115
+ display: "flex",
116
+ alignItems: "center",
117
+ justifyContent: "center",
118
+ padding: 12
119
+ };
120
+ const DIALOG_DESKTOP_STYLE = {
121
+ position: "relative",
122
+ width: "min(960px, calc(100vw - 24px))",
123
+ height: "min(760px, calc(100vh - 24px))",
124
+ background: "#fff",
125
+ borderRadius: 18,
126
+ boxShadow: "0 24px 80px rgba(2, 6, 23, 0.28)",
127
+ border: "1px solid rgba(148, 163, 184, 0.35)",
128
+ overflow: "hidden"
129
+ };
130
+ const DIALOG_MOBILE_STYLE = {
131
+ ...DIALOG_DESKTOP_STYLE,
132
+ width: "100vw",
133
+ height: "100vh",
134
+ borderRadius: 0,
135
+ border: 0
136
+ };
137
+ const CLOSE_BUTTON_STYLE = {
138
+ position: "absolute",
139
+ top: 10,
140
+ right: 10,
141
+ zIndex: 2,
142
+ width: 34,
143
+ height: 34,
144
+ borderRadius: 999,
145
+ border: "1px solid rgba(148,163,184,0.45)",
146
+ background: "rgba(255,255,255,0.92)",
147
+ color: "#0f172a",
148
+ cursor: "pointer",
149
+ fontSize: 18,
150
+ lineHeight: "32px",
151
+ textAlign: "center",
152
+ padding: 0
153
+ };
154
+ const IFRAME_STYLE = {
155
+ width: "100%",
156
+ height: "100%",
157
+ border: 0,
158
+ background: "#fff"
159
+ };
160
+ function useIsMobile(maxWidth = 640) {
161
+ const [isMobile, setIsMobile] = (0, react.useState)(false);
162
+ (0, react.useEffect)(() => {
163
+ if (typeof window === "undefined") return;
164
+ const mediaQuery = window.matchMedia(`(max-width: ${maxWidth}px)`);
165
+ const onChange = () => setIsMobile(mediaQuery.matches);
166
+ onChange();
167
+ if (typeof mediaQuery.addEventListener === "function") {
168
+ mediaQuery.addEventListener("change", onChange);
169
+ return () => mediaQuery.removeEventListener("change", onChange);
170
+ }
171
+ mediaQuery.addListener(onChange);
172
+ return () => mediaQuery.removeListener(onChange);
173
+ }, [maxWidth]);
174
+ return isMobile;
175
+ }
176
+ function TradeItModal(props) {
177
+ const { open, onOpenChange, onEvent, onError, iframeTitle, keepMounted = false, closeOnEscape = true, closeOnOverlayClick = true, closeOnCloseRequestedEvent = true, showCloseButton = true, zIndex = 9999, title, className } = props;
178
+ const isMobile = useIsMobile();
179
+ const [isMounted, setIsMounted] = (0, react.useState)(false);
180
+ const [iframeLoaded, setIframeLoaded] = (0, react.useState)(false);
181
+ const resolved = (0, react.useMemo)(() => {
182
+ try {
183
+ return {
184
+ url: (0, __trade_it_core.resolveLaunchUrl)(props.launch),
185
+ error: null
186
+ };
187
+ } catch (error) {
188
+ return {
189
+ url: null,
190
+ error: error instanceof Error ? error : new Error("Failed to build Trade It iframe URL")
191
+ };
192
+ }
193
+ }, [props.launch]);
194
+ (0, react.useEffect)(() => {
195
+ setIsMounted(true);
196
+ }, []);
197
+ (0, react.useEffect)(() => {
198
+ if (!resolved.error) return;
199
+ onError?.(resolved.error);
200
+ }, [resolved.error, onError]);
201
+ (0, react.useEffect)(() => {
202
+ if (!open) return;
203
+ setIframeLoaded(false);
204
+ }, [open, resolved.url]);
205
+ (0, react.useEffect)(() => {
206
+ if (!open || !closeOnEscape) return;
207
+ const onKeyDown = (event) => {
208
+ if (event.key === "Escape") onOpenChange?.(false);
209
+ };
210
+ window.addEventListener("keydown", onKeyDown);
211
+ return () => window.removeEventListener("keydown", onKeyDown);
212
+ }, [
213
+ open,
214
+ closeOnEscape,
215
+ onOpenChange
216
+ ]);
217
+ (0, react.useEffect)(() => {
218
+ if (!open) return;
219
+ const originalOverflow = document.body.style.overflow;
220
+ document.body.style.overflow = "hidden";
221
+ return () => {
222
+ document.body.style.overflow = originalOverflow;
223
+ };
224
+ }, [open]);
225
+ (0, react.useEffect)(() => {
226
+ if (!open || !resolved.url) return;
227
+ const expectedOrigin = (0, __trade_it_core.getUrlOrigin)(resolved.url);
228
+ if (!expectedOrigin) return;
229
+ const handleMessage = (event) => {
230
+ if (event.origin !== expectedOrigin) return;
231
+ if (!(0, __trade_it_core.isTradeItEvent)(event.data)) return;
232
+ onEvent?.(event.data);
233
+ if (event.data.type === "tradeit.error") onError?.(new Error((0, __trade_it_core.getTradeItErrorMessage)(event.data)));
234
+ if (event.data.type === "tradeit.close_requested" && closeOnCloseRequestedEvent) onOpenChange?.(false);
235
+ };
236
+ window.addEventListener("message", handleMessage);
237
+ return () => window.removeEventListener("message", handleMessage);
238
+ }, [
239
+ open,
240
+ resolved.url,
241
+ onEvent,
242
+ onError,
243
+ closeOnCloseRequestedEvent,
244
+ onOpenChange
245
+ ]);
246
+ if (!isMounted) return null;
247
+ if (!open && !keepMounted) return null;
248
+ if (!resolved.url) return null;
249
+ const dialogStyle = isMobile ? DIALOG_MOBILE_STYLE : DIALOG_DESKTOP_STYLE;
250
+ const overlayStyle = {
251
+ ...OVERLAY_STYLE,
252
+ zIndex
253
+ };
254
+ const dialogLabel = title ?? iframeTitle ?? "Trade It";
255
+ const modal = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
256
+ "aria-hidden": !open,
257
+ style: {
258
+ ...overlayStyle,
259
+ visibility: open ? "visible" : "hidden",
260
+ pointerEvents: open ? "auto" : "none"
261
+ },
262
+ onMouseDown: (event) => {
263
+ if (!closeOnOverlayClick) return;
264
+ if (event.target === event.currentTarget) onOpenChange?.(false);
265
+ },
266
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
267
+ role: "dialog",
268
+ "aria-modal": "true",
269
+ "aria-label": dialogLabel,
270
+ className,
271
+ style: dialogStyle,
272
+ children: [
273
+ showCloseButton && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
274
+ type: "button",
275
+ "aria-label": "Close",
276
+ onClick: () => onOpenChange?.(false),
277
+ style: CLOSE_BUTTON_STYLE,
278
+ children: "×"
279
+ }),
280
+ !iframeLoaded && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TradeItModalLoader, {}),
281
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("iframe", {
282
+ title: dialogLabel,
283
+ src: resolved.url,
284
+ style: IFRAME_STYLE,
285
+ allow: "fullscreen",
286
+ onLoad: () => setIframeLoaded(true)
287
+ })
288
+ ]
289
+ })
290
+ });
291
+ return (0, react_dom.createPortal)(modal, document.body);
292
+ }
293
+
294
+ //#endregion
295
+ //#region src/useTradeIt.ts
296
+ function useTradeIt(defaults = {}) {
297
+ const [modalState, setModalState] = (0, react.useState)(null);
298
+ const close = (0, react.useCallback)(() => {
299
+ setModalState((prev) => prev ? {
300
+ ...prev,
301
+ open: false
302
+ } : prev);
303
+ }, []);
304
+ const destroy = (0, react.useCallback)(() => {
305
+ setModalState(null);
306
+ }, []);
307
+ const openConnect = (0, react.useCallback)((options) => {
308
+ setModalState({
309
+ ...defaults,
310
+ ...options,
311
+ open: true,
312
+ launch: {
313
+ ...options.launch,
314
+ mode: "connect"
315
+ }
316
+ });
317
+ }, [defaults]);
318
+ const openTrade = (0, react.useCallback)((options) => {
319
+ setModalState({
320
+ ...defaults,
321
+ ...options,
322
+ open: true,
323
+ launch: {
324
+ ...options.launch,
325
+ mode: "trade"
326
+ }
327
+ });
328
+ }, [defaults]);
329
+ const modalProps = (0, react.useMemo)(() => {
330
+ if (!modalState) return null;
331
+ const handleOpenChange = (open) => {
332
+ setModalState((prev) => prev ? {
333
+ ...prev,
334
+ open
335
+ } : prev);
336
+ modalState.onOpenChange?.(open);
337
+ };
338
+ return {
339
+ ...modalState,
340
+ onOpenChange: handleOpenChange
341
+ };
342
+ }, [modalState]);
343
+ return {
344
+ isOpen: modalState?.open ?? false,
345
+ modalProps,
346
+ openConnect,
347
+ openTrade,
348
+ close,
349
+ destroy
350
+ };
351
+ }
352
+
353
+ //#endregion
354
+ //#region ../../lib/types/src/enums/trading.enums.ts
355
+ let TradeAction = /* @__PURE__ */ function(TradeAction$1) {
356
+ TradeAction$1["Buy"] = "buy";
357
+ TradeAction$1["Sell"] = "sell";
358
+ return TradeAction$1;
359
+ }({});
360
+ let PositionEffect = /* @__PURE__ */ function(PositionEffect$1) {
361
+ PositionEffect$1["Open"] = "open";
362
+ PositionEffect$1["Close"] = "close";
363
+ return PositionEffect$1;
364
+ }({});
365
+ let OrderType = /* @__PURE__ */ function(OrderType$1) {
366
+ OrderType$1["Market"] = "market";
367
+ OrderType$1["Limit"] = "limit";
368
+ OrderType$1["Stop"] = "stop";
369
+ OrderType$1["StopLimit"] = "stop_limit";
370
+ return OrderType$1;
371
+ }({});
372
+ let OrderDirection = /* @__PURE__ */ function(OrderDirection$1) {
373
+ OrderDirection$1["Debit"] = "debit";
374
+ OrderDirection$1["Credit"] = "credit";
375
+ return OrderDirection$1;
376
+ }({});
377
+ let TimeInForce = /* @__PURE__ */ function(TimeInForce$1) {
378
+ TimeInForce$1["Day"] = "day";
379
+ TimeInForce$1["GoodTillCanceled"] = "gtc";
380
+ TimeInForce$1["ImmediateOrCancel"] = "ioc";
381
+ TimeInForce$1["FillOrKill"] = "fok";
382
+ return TimeInForce$1;
383
+ }({});
384
+ let TradeUnit = /* @__PURE__ */ function(TradeUnit$1) {
385
+ TradeUnit$1["Dollars"] = "dollars";
386
+ TradeUnit$1["Shares"] = "shares";
387
+ return TradeUnit$1;
388
+ }({});
389
+ let LegType = /* @__PURE__ */ function(LegType$1) {
390
+ LegType$1["Option"] = "option";
391
+ LegType$1["Equity"] = "equity";
392
+ return LegType$1;
393
+ }({});
394
+ let TradeType = /* @__PURE__ */ function(TradeType$1) {
395
+ TradeType$1[TradeType$1["Simple"] = 0] = "Simple";
396
+ TradeType$1[TradeType$1["MultiLeg"] = 1] = "MultiLeg";
397
+ return TradeType$1;
398
+ }({});
399
+
400
+ //#endregion
401
+ //#region ../../lib/types/src/types/trade-link.types.ts
402
+ /**
403
+ * Short query parameter keys for trade link URLs
404
+ */
405
+ let TradeLinkParam = /* @__PURE__ */ function(TradeLinkParam$1) {
406
+ TradeLinkParam$1["TradeType"] = "tt";
407
+ TradeLinkParam$1["Action"] = "t";
408
+ TradeLinkParam$1["OrderType"] = "ot";
409
+ TradeLinkParam$1["Amount"] = "a";
410
+ TradeLinkParam$1["Unit"] = "u";
411
+ TradeLinkParam$1["LimitPrice"] = "lp";
412
+ TradeLinkParam$1["StopPrice"] = "sp";
413
+ TradeLinkParam$1["TimeInForce"] = "tif";
414
+ TradeLinkParam$1["UtmReferred"] = "ref";
415
+ TradeLinkParam$1["Legs"] = "legs";
416
+ return TradeLinkParam$1;
417
+ }({});
418
+
419
+ //#endregion
420
+ exports.LegType = LegType;
421
+ exports.OrderDirection = OrderDirection;
422
+ exports.OrderType = OrderType;
423
+ exports.PositionEffect = PositionEffect;
424
+ exports.TimeInForce = TimeInForce;
425
+ exports.TradeAction = TradeAction;
426
+ exports.TradeItModal = TradeItModal;
427
+ exports.TradeLinkParam = TradeLinkParam;
428
+ exports.TradeType = TradeType;
429
+ exports.TradeUnit = TradeUnit;
430
+ Object.defineProperty(exports, 'getTradeItErrorMessage', {
431
+ enumerable: true,
432
+ get: function () {
433
+ return __trade_it_core.getTradeItErrorMessage;
434
+ }
435
+ });
436
+ Object.defineProperty(exports, 'getUrlOrigin', {
437
+ enumerable: true,
438
+ get: function () {
439
+ return __trade_it_core.getUrlOrigin;
440
+ }
441
+ });
442
+ Object.defineProperty(exports, 'isTradeItEvent', {
443
+ enumerable: true,
444
+ get: function () {
445
+ return __trade_it_core.isTradeItEvent;
446
+ }
447
+ });
448
+ Object.defineProperty(exports, 'resolveLaunchUrl', {
449
+ enumerable: true,
450
+ get: function () {
451
+ return __trade_it_core.resolveLaunchUrl;
452
+ }
453
+ });
454
+ exports.useTradeIt = useTradeIt;
@@ -0,0 +1,222 @@
1
+ import * as react0 from "react";
2
+ import { z } from "zod";
3
+
4
+ //#region ../../lib/types/src/enums/trading.enums.d.ts
5
+ declare enum TradeAction {
6
+ Buy = "buy",
7
+ Sell = "sell",
8
+ }
9
+ declare enum PositionEffect {
10
+ Open = "open",
11
+ Close = "close",
12
+ }
13
+ declare enum OrderType {
14
+ Market = "market",
15
+ Limit = "limit",
16
+ Stop = "stop",
17
+ StopLimit = "stop_limit",
18
+ }
19
+ declare enum OrderDirection {
20
+ Debit = "debit",
21
+ Credit = "credit",
22
+ }
23
+ declare enum TimeInForce {
24
+ Day = "day",
25
+ GoodTillCanceled = "gtc",
26
+ ImmediateOrCancel = "ioc",
27
+ FillOrKill = "fok",
28
+ }
29
+ declare enum TradeUnit {
30
+ Dollars = "dollars",
31
+ Shares = "shares",
32
+ }
33
+ declare enum LegType {
34
+ Option = "option",
35
+ Equity = "equity",
36
+ }
37
+ declare enum TradeType {
38
+ Simple = 0,
39
+ MultiLeg = 1,
40
+ }
41
+ //#endregion
42
+ //#region ../../lib/types/src/schemas/trade-link.schemas.d.ts
43
+ /**
44
+ * Trade Link Leg Config - derived from TradeLeg without execution_price
45
+ */
46
+ declare const zTradeLinkLegConfig: z.ZodObject<Omit<{
47
+ type: z.ZodNativeEnum<typeof LegType>;
48
+ action: z.ZodNativeEnum<typeof TradeAction>;
49
+ position_effect: z.ZodNullable<z.ZodNativeEnum<typeof PositionEffect>>;
50
+ occ: z.ZodNullable<z.ZodString>;
51
+ quantity: z.ZodNumber;
52
+ execution_price: z.ZodNullable<z.ZodObject<{
53
+ amount: z.ZodNumber;
54
+ currency: z.ZodString;
55
+ }, "strip", z.ZodTypeAny, {
56
+ amount: number;
57
+ currency: string;
58
+ }, {
59
+ amount: number;
60
+ currency: string;
61
+ }>>;
62
+ }, "execution_price">, "strip", z.ZodTypeAny, {
63
+ type: LegType;
64
+ action: TradeAction;
65
+ position_effect: PositionEffect | null;
66
+ occ: string | null;
67
+ quantity: number;
68
+ }, {
69
+ type: LegType;
70
+ action: TradeAction;
71
+ position_effect: PositionEffect | null;
72
+ occ: string | null;
73
+ quantity: number;
74
+ }>;
75
+ /**
76
+ * Full Trade Link Config - stored in database as JSONB
77
+ * Supports both simple trades and multi-leg options trades
78
+ */
79
+ //#endregion
80
+ //#region ../../lib/types/src/types/misc.types.d.ts
81
+ type SnakeToCamelCase<S extends string> = S extends `${infer P1}_${infer P2}` ? `${P1}${Capitalize<SnakeToCamelCase<P2>>}` : S;
82
+ /** Converts a type's snake_case keys to camelCase keys */
83
+ type Camelize<T> = T extends object ? { [K in keyof T as K extends string ? SnakeToCamelCase<K> : K]: Camelize<T[K]> } : T;
84
+ //#endregion
85
+ //#region ../../lib/types/src/types/trade-link.types.d.ts
86
+ /** Configuration for a leg in a multi-leg trade link */
87
+ type TradeLinkLegConfig = z.infer<typeof zTradeLinkLegConfig>;
88
+ /** Full configuration stored in the trade link */
89
+
90
+ /**
91
+ * Short query parameter keys for trade link URLs
92
+ */
93
+ declare enum TradeLinkParam {
94
+ TradeType = "tt",
95
+ // s (simple), m (multi_leg)
96
+ Action = "t",
97
+ // b, s, bto, btc, sto, stc
98
+ OrderType = "ot",
99
+ // m, l, st, sl
100
+ Amount = "a",
101
+ // number
102
+ Unit = "u",
103
+ // d, s
104
+ LimitPrice = "lp",
105
+ // number
106
+ StopPrice = "sp",
107
+ // number
108
+ TimeInForce = "tif",
109
+ // d, gtc, ioc, fok
110
+ UtmReferred = "ref",
111
+ // string
112
+ Legs = "legs",
113
+ }
114
+ //#endregion
115
+ //#region ../trade-it-core/src/types.d.ts
116
+ type TradeItEventType = 'tradeit.ready' | 'tradeit.close_requested' | 'tradeit.brokerage_connected' | 'tradeit.trade_submitted' | 'tradeit.trade_executed' | 'tradeit.error' | 'tradeit.login_required';
117
+ interface TradeItEvent<TPayload = Record<string, unknown>> {
118
+ source: 'tradeit';
119
+ version: 1;
120
+ type: TradeItEventType;
121
+ payload?: TPayload;
122
+ }
123
+ type TradeItMode = 'connect' | 'trade';
124
+ type TradeItMultiLegTradeLegConfig = Partial<Camelize<TradeLinkLegConfig>>;
125
+ /**
126
+ * Query params accepted by Trade It's /t/[ticker] page.
127
+ * User-facing config uses descriptive keys; core converts them to the short-key
128
+ * query format used by the page parser (tt, t, ot, a, u, lp, sp, tif, ref, legs).
129
+ */
130
+ interface TradeItTradeConfigBase {
131
+ ticker: string;
132
+ orderType?: OrderType;
133
+ limitPrice?: number;
134
+ stopPrice?: number;
135
+ timeInForce?: TimeInForce;
136
+ referral?: string;
137
+ }
138
+ interface TradeItSimpleTradeConfig extends TradeItTradeConfigBase {
139
+ tradeType?: TradeType.Simple;
140
+ action?: TradeAction;
141
+ amount?: number;
142
+ unit?: TradeUnit;
143
+ }
144
+ interface TradeItMultiLegTradeConfig extends TradeItTradeConfigBase {
145
+ tradeType: TradeType.MultiLeg;
146
+ direction?: OrderDirection;
147
+ legs: TradeItMultiLegTradeLegConfig[];
148
+ }
149
+ type TradeItTradeConfig = TradeItSimpleTradeConfig | TradeItMultiLegTradeConfig;
150
+ interface TradeItLaunchConfigMap {
151
+ connect: undefined;
152
+ trade: TradeItTradeConfig;
153
+ }
154
+ type TradeItLaunchShape<TMode extends TradeItMode> = {
155
+ mode: TMode;
156
+ url: string;
157
+ } & (TMode extends 'connect' ? {
158
+ config?: undefined;
159
+ } : {
160
+ config: TradeItLaunchConfigMap[TMode];
161
+ });
162
+ type TradeItLaunch<TMode extends TradeItMode = TradeItMode> = TMode extends TradeItMode ? TradeItLaunchShape<TMode> : never;
163
+ type TradeItConnectLaunch = TradeItLaunch<'connect'>;
164
+ type TradeItTradeLaunch = TradeItLaunch<'trade'>;
165
+ //#endregion
166
+ //#region ../trade-it-core/src/events.d.ts
167
+ declare function isTradeItEvent(data: unknown): data is TradeItEvent;
168
+ declare function getTradeItErrorMessage(event: TradeItEvent): string;
169
+ //#endregion
170
+ //#region ../trade-it-core/src/url.d.ts
171
+ declare function resolveLaunchUrl(launch: TradeItLaunch): string;
172
+ declare function getUrlOrigin(url: string): string | null;
173
+ //#endregion
174
+ //#region src/types.d.ts
175
+ interface TradeItModalAppearance {
176
+ closeOnEscape?: boolean;
177
+ closeOnOverlayClick?: boolean;
178
+ closeOnCloseRequestedEvent?: boolean;
179
+ showCloseButton?: boolean;
180
+ zIndex?: number;
181
+ title?: string;
182
+ }
183
+ interface TradeItModalCallbacks {
184
+ onOpenChange?: (open: boolean) => void;
185
+ onEvent?: (event: TradeItEvent) => void;
186
+ onError?: (error: Error) => void;
187
+ }
188
+ interface TradeItModalProps extends TradeItModalAppearance, TradeItModalCallbacks {
189
+ launch: TradeItLaunch;
190
+ open: boolean;
191
+ iframeTitle?: string;
192
+ keepMounted?: boolean;
193
+ className?: string;
194
+ }
195
+ interface UseTradeItOptions extends TradeItModalAppearance, TradeItModalCallbacks {
196
+ iframeTitle?: string;
197
+ keepMounted?: boolean;
198
+ }
199
+ interface OpenConnectOptions extends Partial<TradeItModalAppearance>, Partial<TradeItModalCallbacks> {
200
+ launch: TradeItConnectLaunch;
201
+ iframeTitle?: string;
202
+ }
203
+ interface OpenTradeOptions extends Partial<TradeItModalAppearance>, Partial<TradeItModalCallbacks> {
204
+ launch: TradeItTradeLaunch;
205
+ iframeTitle?: string;
206
+ }
207
+ //#endregion
208
+ //#region src/TradeItModal.d.ts
209
+ declare function TradeItModal(props: TradeItModalProps): react0.ReactPortal | null;
210
+ //#endregion
211
+ //#region src/useTradeIt.d.ts
212
+ interface UseTradeItReturn {
213
+ isOpen: boolean;
214
+ modalProps: TradeItModalProps | null;
215
+ openConnect: (options: OpenConnectOptions) => void;
216
+ openTrade: (options: OpenTradeOptions) => void;
217
+ close: () => void;
218
+ destroy: () => void;
219
+ }
220
+ declare function useTradeIt(defaults?: UseTradeItOptions): UseTradeItReturn;
221
+ //#endregion
222
+ export { LegType, type OpenConnectOptions, type OpenTradeOptions, OrderDirection, OrderType, PositionEffect, TimeInForce, TradeAction, type TradeItConnectLaunch, type TradeItEvent, type TradeItEventType, type TradeItLaunch, TradeItModal, type TradeItModalAppearance, type TradeItModalProps, type TradeItMultiLegTradeConfig, type TradeItMultiLegTradeLegConfig, type TradeItSimpleTradeConfig, type TradeItTradeConfig, type TradeItTradeLaunch, type TradeLinkLegConfig, TradeLinkParam, TradeType, TradeUnit, type UseTradeItOptions, getTradeItErrorMessage, getUrlOrigin, isTradeItEvent, resolveLaunchUrl, useTradeIt };
@@ -0,0 +1,222 @@
1
+ import * as react0 from "react";
2
+ import { z } from "zod";
3
+
4
+ //#region ../../lib/types/src/enums/trading.enums.d.ts
5
+ declare enum TradeAction {
6
+ Buy = "buy",
7
+ Sell = "sell",
8
+ }
9
+ declare enum PositionEffect {
10
+ Open = "open",
11
+ Close = "close",
12
+ }
13
+ declare enum OrderType {
14
+ Market = "market",
15
+ Limit = "limit",
16
+ Stop = "stop",
17
+ StopLimit = "stop_limit",
18
+ }
19
+ declare enum OrderDirection {
20
+ Debit = "debit",
21
+ Credit = "credit",
22
+ }
23
+ declare enum TimeInForce {
24
+ Day = "day",
25
+ GoodTillCanceled = "gtc",
26
+ ImmediateOrCancel = "ioc",
27
+ FillOrKill = "fok",
28
+ }
29
+ declare enum TradeUnit {
30
+ Dollars = "dollars",
31
+ Shares = "shares",
32
+ }
33
+ declare enum LegType {
34
+ Option = "option",
35
+ Equity = "equity",
36
+ }
37
+ declare enum TradeType {
38
+ Simple = 0,
39
+ MultiLeg = 1,
40
+ }
41
+ //#endregion
42
+ //#region ../../lib/types/src/schemas/trade-link.schemas.d.ts
43
+ /**
44
+ * Trade Link Leg Config - derived from TradeLeg without execution_price
45
+ */
46
+ declare const zTradeLinkLegConfig: z.ZodObject<Omit<{
47
+ type: z.ZodNativeEnum<typeof LegType>;
48
+ action: z.ZodNativeEnum<typeof TradeAction>;
49
+ position_effect: z.ZodNullable<z.ZodNativeEnum<typeof PositionEffect>>;
50
+ occ: z.ZodNullable<z.ZodString>;
51
+ quantity: z.ZodNumber;
52
+ execution_price: z.ZodNullable<z.ZodObject<{
53
+ amount: z.ZodNumber;
54
+ currency: z.ZodString;
55
+ }, "strip", z.ZodTypeAny, {
56
+ amount: number;
57
+ currency: string;
58
+ }, {
59
+ amount: number;
60
+ currency: string;
61
+ }>>;
62
+ }, "execution_price">, "strip", z.ZodTypeAny, {
63
+ type: LegType;
64
+ action: TradeAction;
65
+ position_effect: PositionEffect | null;
66
+ occ: string | null;
67
+ quantity: number;
68
+ }, {
69
+ type: LegType;
70
+ action: TradeAction;
71
+ position_effect: PositionEffect | null;
72
+ occ: string | null;
73
+ quantity: number;
74
+ }>;
75
+ /**
76
+ * Full Trade Link Config - stored in database as JSONB
77
+ * Supports both simple trades and multi-leg options trades
78
+ */
79
+ //#endregion
80
+ //#region ../../lib/types/src/types/misc.types.d.ts
81
+ type SnakeToCamelCase<S extends string> = S extends `${infer P1}_${infer P2}` ? `${P1}${Capitalize<SnakeToCamelCase<P2>>}` : S;
82
+ /** Converts a type's snake_case keys to camelCase keys */
83
+ type Camelize<T> = T extends object ? { [K in keyof T as K extends string ? SnakeToCamelCase<K> : K]: Camelize<T[K]> } : T;
84
+ //#endregion
85
+ //#region ../../lib/types/src/types/trade-link.types.d.ts
86
+ /** Configuration for a leg in a multi-leg trade link */
87
+ type TradeLinkLegConfig = z.infer<typeof zTradeLinkLegConfig>;
88
+ /** Full configuration stored in the trade link */
89
+
90
+ /**
91
+ * Short query parameter keys for trade link URLs
92
+ */
93
+ declare enum TradeLinkParam {
94
+ TradeType = "tt",
95
+ // s (simple), m (multi_leg)
96
+ Action = "t",
97
+ // b, s, bto, btc, sto, stc
98
+ OrderType = "ot",
99
+ // m, l, st, sl
100
+ Amount = "a",
101
+ // number
102
+ Unit = "u",
103
+ // d, s
104
+ LimitPrice = "lp",
105
+ // number
106
+ StopPrice = "sp",
107
+ // number
108
+ TimeInForce = "tif",
109
+ // d, gtc, ioc, fok
110
+ UtmReferred = "ref",
111
+ // string
112
+ Legs = "legs",
113
+ }
114
+ //#endregion
115
+ //#region ../trade-it-core/src/types.d.ts
116
+ type TradeItEventType = 'tradeit.ready' | 'tradeit.close_requested' | 'tradeit.brokerage_connected' | 'tradeit.trade_submitted' | 'tradeit.trade_executed' | 'tradeit.error' | 'tradeit.login_required';
117
+ interface TradeItEvent<TPayload = Record<string, unknown>> {
118
+ source: 'tradeit';
119
+ version: 1;
120
+ type: TradeItEventType;
121
+ payload?: TPayload;
122
+ }
123
+ type TradeItMode = 'connect' | 'trade';
124
+ type TradeItMultiLegTradeLegConfig = Partial<Camelize<TradeLinkLegConfig>>;
125
+ /**
126
+ * Query params accepted by Trade It's /t/[ticker] page.
127
+ * User-facing config uses descriptive keys; core converts them to the short-key
128
+ * query format used by the page parser (tt, t, ot, a, u, lp, sp, tif, ref, legs).
129
+ */
130
+ interface TradeItTradeConfigBase {
131
+ ticker: string;
132
+ orderType?: OrderType;
133
+ limitPrice?: number;
134
+ stopPrice?: number;
135
+ timeInForce?: TimeInForce;
136
+ referral?: string;
137
+ }
138
+ interface TradeItSimpleTradeConfig extends TradeItTradeConfigBase {
139
+ tradeType?: TradeType.Simple;
140
+ action?: TradeAction;
141
+ amount?: number;
142
+ unit?: TradeUnit;
143
+ }
144
+ interface TradeItMultiLegTradeConfig extends TradeItTradeConfigBase {
145
+ tradeType: TradeType.MultiLeg;
146
+ direction?: OrderDirection;
147
+ legs: TradeItMultiLegTradeLegConfig[];
148
+ }
149
+ type TradeItTradeConfig = TradeItSimpleTradeConfig | TradeItMultiLegTradeConfig;
150
+ interface TradeItLaunchConfigMap {
151
+ connect: undefined;
152
+ trade: TradeItTradeConfig;
153
+ }
154
+ type TradeItLaunchShape<TMode extends TradeItMode> = {
155
+ mode: TMode;
156
+ url: string;
157
+ } & (TMode extends 'connect' ? {
158
+ config?: undefined;
159
+ } : {
160
+ config: TradeItLaunchConfigMap[TMode];
161
+ });
162
+ type TradeItLaunch<TMode extends TradeItMode = TradeItMode> = TMode extends TradeItMode ? TradeItLaunchShape<TMode> : never;
163
+ type TradeItConnectLaunch = TradeItLaunch<'connect'>;
164
+ type TradeItTradeLaunch = TradeItLaunch<'trade'>;
165
+ //#endregion
166
+ //#region ../trade-it-core/src/events.d.ts
167
+ declare function isTradeItEvent(data: unknown): data is TradeItEvent;
168
+ declare function getTradeItErrorMessage(event: TradeItEvent): string;
169
+ //#endregion
170
+ //#region ../trade-it-core/src/url.d.ts
171
+ declare function resolveLaunchUrl(launch: TradeItLaunch): string;
172
+ declare function getUrlOrigin(url: string): string | null;
173
+ //#endregion
174
+ //#region src/types.d.ts
175
+ interface TradeItModalAppearance {
176
+ closeOnEscape?: boolean;
177
+ closeOnOverlayClick?: boolean;
178
+ closeOnCloseRequestedEvent?: boolean;
179
+ showCloseButton?: boolean;
180
+ zIndex?: number;
181
+ title?: string;
182
+ }
183
+ interface TradeItModalCallbacks {
184
+ onOpenChange?: (open: boolean) => void;
185
+ onEvent?: (event: TradeItEvent) => void;
186
+ onError?: (error: Error) => void;
187
+ }
188
+ interface TradeItModalProps extends TradeItModalAppearance, TradeItModalCallbacks {
189
+ launch: TradeItLaunch;
190
+ open: boolean;
191
+ iframeTitle?: string;
192
+ keepMounted?: boolean;
193
+ className?: string;
194
+ }
195
+ interface UseTradeItOptions extends TradeItModalAppearance, TradeItModalCallbacks {
196
+ iframeTitle?: string;
197
+ keepMounted?: boolean;
198
+ }
199
+ interface OpenConnectOptions extends Partial<TradeItModalAppearance>, Partial<TradeItModalCallbacks> {
200
+ launch: TradeItConnectLaunch;
201
+ iframeTitle?: string;
202
+ }
203
+ interface OpenTradeOptions extends Partial<TradeItModalAppearance>, Partial<TradeItModalCallbacks> {
204
+ launch: TradeItTradeLaunch;
205
+ iframeTitle?: string;
206
+ }
207
+ //#endregion
208
+ //#region src/TradeItModal.d.ts
209
+ declare function TradeItModal(props: TradeItModalProps): react0.ReactPortal | null;
210
+ //#endregion
211
+ //#region src/useTradeIt.d.ts
212
+ interface UseTradeItReturn {
213
+ isOpen: boolean;
214
+ modalProps: TradeItModalProps | null;
215
+ openConnect: (options: OpenConnectOptions) => void;
216
+ openTrade: (options: OpenTradeOptions) => void;
217
+ close: () => void;
218
+ destroy: () => void;
219
+ }
220
+ declare function useTradeIt(defaults?: UseTradeItOptions): UseTradeItReturn;
221
+ //#endregion
222
+ export { LegType, type OpenConnectOptions, type OpenTradeOptions, OrderDirection, OrderType, PositionEffect, TimeInForce, TradeAction, type TradeItConnectLaunch, type TradeItEvent, type TradeItEventType, type TradeItLaunch, TradeItModal, type TradeItModalAppearance, type TradeItModalProps, type TradeItMultiLegTradeConfig, type TradeItMultiLegTradeLegConfig, type TradeItSimpleTradeConfig, type TradeItTradeConfig, type TradeItTradeLaunch, type TradeLinkLegConfig, TradeLinkParam, TradeType, TradeUnit, type UseTradeItOptions, getTradeItErrorMessage, getUrlOrigin, isTradeItEvent, resolveLaunchUrl, useTradeIt };
package/dist/index.js ADDED
@@ -0,0 +1,397 @@
1
+ import { getTradeItErrorMessage, getTradeItErrorMessage as getTradeItErrorMessage$1, getUrlOrigin, getUrlOrigin as getUrlOrigin$1, isTradeItEvent, isTradeItEvent as isTradeItEvent$1, resolveLaunchUrl, resolveLaunchUrl as resolveLaunchUrl$1 } from "@trade-it/core";
2
+ import { useCallback, useEffect, useMemo, useState } from "react";
3
+ import { createPortal } from "react-dom";
4
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
+
6
+ //#region src/LoaderCircleSpinner.tsx
7
+ const SPINNER_WRAPPER_SIZE = 44;
8
+ const SPINNER_ICON_SIZE = 32;
9
+ const SPINNER_WRAPPER_STYLE = {
10
+ width: SPINNER_WRAPPER_SIZE,
11
+ height: SPINNER_WRAPPER_SIZE,
12
+ display: "flex",
13
+ alignItems: "center",
14
+ justifyContent: "center",
15
+ animation: "tradeItSdkSpin 0.9s linear infinite",
16
+ transformOrigin: "50% 50%"
17
+ };
18
+ const SPINNER_SVG_STYLE = {
19
+ display: "block",
20
+ width: SPINNER_ICON_SIZE,
21
+ height: SPINNER_ICON_SIZE,
22
+ color: "#111827"
23
+ };
24
+ const SPINNER_KEYFRAMES_CSS = `
25
+ @keyframes tradeItSdkSpin {
26
+ from { transform: rotate(0deg); }
27
+ to { transform: rotate(360deg); }
28
+ }
29
+ @media (prefers-reduced-motion: reduce) {
30
+ .tradeit-sdk-spinner {
31
+ animation: none !important;
32
+ }
33
+ }
34
+ `;
35
+ function LoaderCircleSpinner() {
36
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("style", { children: SPINNER_KEYFRAMES_CSS }), /* @__PURE__ */ jsx("div", {
37
+ className: "tradeit-sdk-spinner",
38
+ style: SPINNER_WRAPPER_STYLE,
39
+ "aria-hidden": "true",
40
+ children: /* @__PURE__ */ jsx("svg", {
41
+ xmlns: "http://www.w3.org/2000/svg",
42
+ width: SPINNER_ICON_SIZE,
43
+ height: SPINNER_ICON_SIZE,
44
+ viewBox: "0 0 24 24",
45
+ fill: "none",
46
+ stroke: "currentColor",
47
+ strokeWidth: "2",
48
+ strokeLinecap: "round",
49
+ strokeLinejoin: "round",
50
+ style: SPINNER_SVG_STYLE,
51
+ children: /* @__PURE__ */ jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" })
52
+ })
53
+ })] });
54
+ }
55
+
56
+ //#endregion
57
+ //#region src/TradeItModalLoader.tsx
58
+ const LOADING_STYLE = {
59
+ position: "absolute",
60
+ inset: 0,
61
+ background: "#ffffff"
62
+ };
63
+ const LOADING_CONTENT_STYLE = {
64
+ position: "relative",
65
+ zIndex: 1,
66
+ display: "flex",
67
+ flexDirection: "column",
68
+ alignItems: "center",
69
+ justifyContent: "center",
70
+ width: "100%",
71
+ height: "100%"
72
+ };
73
+ function TradeItModalLoader() {
74
+ return /* @__PURE__ */ jsx("div", {
75
+ style: LOADING_STYLE,
76
+ "aria-live": "polite",
77
+ "aria-busy": "true",
78
+ children: /* @__PURE__ */ jsx("div", {
79
+ style: LOADING_CONTENT_STYLE,
80
+ children: /* @__PURE__ */ jsx(LoaderCircleSpinner, {})
81
+ })
82
+ });
83
+ }
84
+
85
+ //#endregion
86
+ //#region src/TradeItModal.tsx
87
+ const OVERLAY_STYLE = {
88
+ position: "fixed",
89
+ inset: 0,
90
+ background: "rgba(15, 23, 42, 0.55)",
91
+ backdropFilter: "blur(4px)",
92
+ display: "flex",
93
+ alignItems: "center",
94
+ justifyContent: "center",
95
+ padding: 12
96
+ };
97
+ const DIALOG_DESKTOP_STYLE = {
98
+ position: "relative",
99
+ width: "min(960px, calc(100vw - 24px))",
100
+ height: "min(760px, calc(100vh - 24px))",
101
+ background: "#fff",
102
+ borderRadius: 18,
103
+ boxShadow: "0 24px 80px rgba(2, 6, 23, 0.28)",
104
+ border: "1px solid rgba(148, 163, 184, 0.35)",
105
+ overflow: "hidden"
106
+ };
107
+ const DIALOG_MOBILE_STYLE = {
108
+ ...DIALOG_DESKTOP_STYLE,
109
+ width: "100vw",
110
+ height: "100vh",
111
+ borderRadius: 0,
112
+ border: 0
113
+ };
114
+ const CLOSE_BUTTON_STYLE = {
115
+ position: "absolute",
116
+ top: 10,
117
+ right: 10,
118
+ zIndex: 2,
119
+ width: 34,
120
+ height: 34,
121
+ borderRadius: 999,
122
+ border: "1px solid rgba(148,163,184,0.45)",
123
+ background: "rgba(255,255,255,0.92)",
124
+ color: "#0f172a",
125
+ cursor: "pointer",
126
+ fontSize: 18,
127
+ lineHeight: "32px",
128
+ textAlign: "center",
129
+ padding: 0
130
+ };
131
+ const IFRAME_STYLE = {
132
+ width: "100%",
133
+ height: "100%",
134
+ border: 0,
135
+ background: "#fff"
136
+ };
137
+ function useIsMobile(maxWidth = 640) {
138
+ const [isMobile, setIsMobile] = useState(false);
139
+ useEffect(() => {
140
+ if (typeof window === "undefined") return;
141
+ const mediaQuery = window.matchMedia(`(max-width: ${maxWidth}px)`);
142
+ const onChange = () => setIsMobile(mediaQuery.matches);
143
+ onChange();
144
+ if (typeof mediaQuery.addEventListener === "function") {
145
+ mediaQuery.addEventListener("change", onChange);
146
+ return () => mediaQuery.removeEventListener("change", onChange);
147
+ }
148
+ mediaQuery.addListener(onChange);
149
+ return () => mediaQuery.removeListener(onChange);
150
+ }, [maxWidth]);
151
+ return isMobile;
152
+ }
153
+ function TradeItModal(props) {
154
+ const { open, onOpenChange, onEvent, onError, iframeTitle, keepMounted = false, closeOnEscape = true, closeOnOverlayClick = true, closeOnCloseRequestedEvent = true, showCloseButton = true, zIndex = 9999, title, className } = props;
155
+ const isMobile = useIsMobile();
156
+ const [isMounted, setIsMounted] = useState(false);
157
+ const [iframeLoaded, setIframeLoaded] = useState(false);
158
+ const resolved = useMemo(() => {
159
+ try {
160
+ return {
161
+ url: resolveLaunchUrl$1(props.launch),
162
+ error: null
163
+ };
164
+ } catch (error) {
165
+ return {
166
+ url: null,
167
+ error: error instanceof Error ? error : new Error("Failed to build Trade It iframe URL")
168
+ };
169
+ }
170
+ }, [props.launch]);
171
+ useEffect(() => {
172
+ setIsMounted(true);
173
+ }, []);
174
+ useEffect(() => {
175
+ if (!resolved.error) return;
176
+ onError?.(resolved.error);
177
+ }, [resolved.error, onError]);
178
+ useEffect(() => {
179
+ if (!open) return;
180
+ setIframeLoaded(false);
181
+ }, [open, resolved.url]);
182
+ useEffect(() => {
183
+ if (!open || !closeOnEscape) return;
184
+ const onKeyDown = (event) => {
185
+ if (event.key === "Escape") onOpenChange?.(false);
186
+ };
187
+ window.addEventListener("keydown", onKeyDown);
188
+ return () => window.removeEventListener("keydown", onKeyDown);
189
+ }, [
190
+ open,
191
+ closeOnEscape,
192
+ onOpenChange
193
+ ]);
194
+ useEffect(() => {
195
+ if (!open) return;
196
+ const originalOverflow = document.body.style.overflow;
197
+ document.body.style.overflow = "hidden";
198
+ return () => {
199
+ document.body.style.overflow = originalOverflow;
200
+ };
201
+ }, [open]);
202
+ useEffect(() => {
203
+ if (!open || !resolved.url) return;
204
+ const expectedOrigin = getUrlOrigin$1(resolved.url);
205
+ if (!expectedOrigin) return;
206
+ const handleMessage = (event) => {
207
+ if (event.origin !== expectedOrigin) return;
208
+ if (!isTradeItEvent$1(event.data)) return;
209
+ onEvent?.(event.data);
210
+ if (event.data.type === "tradeit.error") onError?.(new Error(getTradeItErrorMessage$1(event.data)));
211
+ if (event.data.type === "tradeit.close_requested" && closeOnCloseRequestedEvent) onOpenChange?.(false);
212
+ };
213
+ window.addEventListener("message", handleMessage);
214
+ return () => window.removeEventListener("message", handleMessage);
215
+ }, [
216
+ open,
217
+ resolved.url,
218
+ onEvent,
219
+ onError,
220
+ closeOnCloseRequestedEvent,
221
+ onOpenChange
222
+ ]);
223
+ if (!isMounted) return null;
224
+ if (!open && !keepMounted) return null;
225
+ if (!resolved.url) return null;
226
+ const dialogStyle = isMobile ? DIALOG_MOBILE_STYLE : DIALOG_DESKTOP_STYLE;
227
+ const overlayStyle = {
228
+ ...OVERLAY_STYLE,
229
+ zIndex
230
+ };
231
+ const dialogLabel = title ?? iframeTitle ?? "Trade It";
232
+ const modal = /* @__PURE__ */ jsx("div", {
233
+ "aria-hidden": !open,
234
+ style: {
235
+ ...overlayStyle,
236
+ visibility: open ? "visible" : "hidden",
237
+ pointerEvents: open ? "auto" : "none"
238
+ },
239
+ onMouseDown: (event) => {
240
+ if (!closeOnOverlayClick) return;
241
+ if (event.target === event.currentTarget) onOpenChange?.(false);
242
+ },
243
+ children: /* @__PURE__ */ jsxs("div", {
244
+ role: "dialog",
245
+ "aria-modal": "true",
246
+ "aria-label": dialogLabel,
247
+ className,
248
+ style: dialogStyle,
249
+ children: [
250
+ showCloseButton && /* @__PURE__ */ jsx("button", {
251
+ type: "button",
252
+ "aria-label": "Close",
253
+ onClick: () => onOpenChange?.(false),
254
+ style: CLOSE_BUTTON_STYLE,
255
+ children: "×"
256
+ }),
257
+ !iframeLoaded && /* @__PURE__ */ jsx(TradeItModalLoader, {}),
258
+ /* @__PURE__ */ jsx("iframe", {
259
+ title: dialogLabel,
260
+ src: resolved.url,
261
+ style: IFRAME_STYLE,
262
+ allow: "fullscreen",
263
+ onLoad: () => setIframeLoaded(true)
264
+ })
265
+ ]
266
+ })
267
+ });
268
+ return createPortal(modal, document.body);
269
+ }
270
+
271
+ //#endregion
272
+ //#region src/useTradeIt.ts
273
+ function useTradeIt(defaults = {}) {
274
+ const [modalState, setModalState] = useState(null);
275
+ const close = useCallback(() => {
276
+ setModalState((prev) => prev ? {
277
+ ...prev,
278
+ open: false
279
+ } : prev);
280
+ }, []);
281
+ const destroy = useCallback(() => {
282
+ setModalState(null);
283
+ }, []);
284
+ const openConnect = useCallback((options) => {
285
+ setModalState({
286
+ ...defaults,
287
+ ...options,
288
+ open: true,
289
+ launch: {
290
+ ...options.launch,
291
+ mode: "connect"
292
+ }
293
+ });
294
+ }, [defaults]);
295
+ const openTrade = useCallback((options) => {
296
+ setModalState({
297
+ ...defaults,
298
+ ...options,
299
+ open: true,
300
+ launch: {
301
+ ...options.launch,
302
+ mode: "trade"
303
+ }
304
+ });
305
+ }, [defaults]);
306
+ const modalProps = useMemo(() => {
307
+ if (!modalState) return null;
308
+ const handleOpenChange = (open) => {
309
+ setModalState((prev) => prev ? {
310
+ ...prev,
311
+ open
312
+ } : prev);
313
+ modalState.onOpenChange?.(open);
314
+ };
315
+ return {
316
+ ...modalState,
317
+ onOpenChange: handleOpenChange
318
+ };
319
+ }, [modalState]);
320
+ return {
321
+ isOpen: modalState?.open ?? false,
322
+ modalProps,
323
+ openConnect,
324
+ openTrade,
325
+ close,
326
+ destroy
327
+ };
328
+ }
329
+
330
+ //#endregion
331
+ //#region ../../lib/types/src/enums/trading.enums.ts
332
+ let TradeAction = /* @__PURE__ */ function(TradeAction$1) {
333
+ TradeAction$1["Buy"] = "buy";
334
+ TradeAction$1["Sell"] = "sell";
335
+ return TradeAction$1;
336
+ }({});
337
+ let PositionEffect = /* @__PURE__ */ function(PositionEffect$1) {
338
+ PositionEffect$1["Open"] = "open";
339
+ PositionEffect$1["Close"] = "close";
340
+ return PositionEffect$1;
341
+ }({});
342
+ let OrderType = /* @__PURE__ */ function(OrderType$1) {
343
+ OrderType$1["Market"] = "market";
344
+ OrderType$1["Limit"] = "limit";
345
+ OrderType$1["Stop"] = "stop";
346
+ OrderType$1["StopLimit"] = "stop_limit";
347
+ return OrderType$1;
348
+ }({});
349
+ let OrderDirection = /* @__PURE__ */ function(OrderDirection$1) {
350
+ OrderDirection$1["Debit"] = "debit";
351
+ OrderDirection$1["Credit"] = "credit";
352
+ return OrderDirection$1;
353
+ }({});
354
+ let TimeInForce = /* @__PURE__ */ function(TimeInForce$1) {
355
+ TimeInForce$1["Day"] = "day";
356
+ TimeInForce$1["GoodTillCanceled"] = "gtc";
357
+ TimeInForce$1["ImmediateOrCancel"] = "ioc";
358
+ TimeInForce$1["FillOrKill"] = "fok";
359
+ return TimeInForce$1;
360
+ }({});
361
+ let TradeUnit = /* @__PURE__ */ function(TradeUnit$1) {
362
+ TradeUnit$1["Dollars"] = "dollars";
363
+ TradeUnit$1["Shares"] = "shares";
364
+ return TradeUnit$1;
365
+ }({});
366
+ let LegType = /* @__PURE__ */ function(LegType$1) {
367
+ LegType$1["Option"] = "option";
368
+ LegType$1["Equity"] = "equity";
369
+ return LegType$1;
370
+ }({});
371
+ let TradeType = /* @__PURE__ */ function(TradeType$1) {
372
+ TradeType$1[TradeType$1["Simple"] = 0] = "Simple";
373
+ TradeType$1[TradeType$1["MultiLeg"] = 1] = "MultiLeg";
374
+ return TradeType$1;
375
+ }({});
376
+
377
+ //#endregion
378
+ //#region ../../lib/types/src/types/trade-link.types.ts
379
+ /**
380
+ * Short query parameter keys for trade link URLs
381
+ */
382
+ let TradeLinkParam = /* @__PURE__ */ function(TradeLinkParam$1) {
383
+ TradeLinkParam$1["TradeType"] = "tt";
384
+ TradeLinkParam$1["Action"] = "t";
385
+ TradeLinkParam$1["OrderType"] = "ot";
386
+ TradeLinkParam$1["Amount"] = "a";
387
+ TradeLinkParam$1["Unit"] = "u";
388
+ TradeLinkParam$1["LimitPrice"] = "lp";
389
+ TradeLinkParam$1["StopPrice"] = "sp";
390
+ TradeLinkParam$1["TimeInForce"] = "tif";
391
+ TradeLinkParam$1["UtmReferred"] = "ref";
392
+ TradeLinkParam$1["Legs"] = "legs";
393
+ return TradeLinkParam$1;
394
+ }({});
395
+
396
+ //#endregion
397
+ export { LegType, OrderDirection, OrderType, PositionEffect, TimeInForce, TradeAction, TradeItModal, TradeLinkParam, TradeType, TradeUnit, getTradeItErrorMessage, getUrlOrigin, isTradeItEvent, resolveLaunchUrl, useTradeIt };
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@trade-it/react",
3
+ "version": "0.1.0",
4
+ "description": "React SDK for embedding Trade It connect and trade experiences in an iframe modal.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md",
20
+ "CHANGELOG.md"
21
+ ],
22
+ "sideEffects": false,
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "scripts": {
27
+ "build": "tsdown src/index.ts --format esm,cjs --dts --out-dir dist && node ../../scripts/fix-trade-it-sdk-dts.mjs dist/index.d.ts dist/index.d.cts",
28
+ "dev": "tsdown src/index.ts --format esm,cjs --dts --out-dir dist --watch"
29
+ },
30
+ "dependencies": {
31
+ "@trade-it/core": "*",
32
+ "zod": "^3.25.67"
33
+ },
34
+ "peerDependencies": {
35
+ "react": "^18.0.0 || ^19.0.0",
36
+ "react-dom": "^18.0.0 || ^19.0.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/react": "^19.1.1",
40
+ "@types/react-dom": "^19.1.1",
41
+ "tsdown": "^0.11.0",
42
+ "typescript": "^5.7.2"
43
+ }
44
+ }