@luxfi/ui 6.1.2 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (240) hide show
  1. package/dist/accordion.cjs +213 -0
  2. package/dist/accordion.d.cts +74 -0
  3. package/dist/accordion.d.ts +74 -0
  4. package/dist/accordion.js +186 -0
  5. package/dist/alert.cjs +553 -0
  6. package/dist/alert.d.cts +30 -0
  7. package/dist/alert.d.ts +30 -0
  8. package/dist/alert.js +531 -0
  9. package/dist/avatar.cjs +149 -0
  10. package/dist/avatar.d.cts +25 -0
  11. package/dist/avatar.d.ts +25 -0
  12. package/dist/avatar.js +125 -0
  13. package/dist/badge.cjs +611 -0
  14. package/dist/badge.d.cts +23 -0
  15. package/dist/badge.d.ts +23 -0
  16. package/dist/badge.js +589 -0
  17. package/dist/bank.cjs +272 -0
  18. package/dist/bank.d.cts +76 -0
  19. package/dist/bank.d.ts +76 -0
  20. package/dist/bank.js +258 -0
  21. package/dist/button.cjs +689 -0
  22. package/dist/button.d.cts +52 -0
  23. package/dist/button.d.ts +52 -0
  24. package/dist/button.js +664 -0
  25. package/dist/checkbox.cjs +265 -0
  26. package/dist/checkbox.d.cts +31 -0
  27. package/dist/checkbox.d.ts +31 -0
  28. package/dist/checkbox.js +241 -0
  29. package/dist/close-button.cjs +73 -0
  30. package/dist/close-button.d.cts +9 -0
  31. package/dist/close-button.d.ts +9 -0
  32. package/dist/close-button.js +51 -0
  33. package/dist/collapsible.cjs +702 -0
  34. package/dist/collapsible.d.cts +23 -0
  35. package/dist/collapsible.d.ts +23 -0
  36. package/dist/collapsible.js +679 -0
  37. package/dist/color-mode.cjs +96 -0
  38. package/dist/color-mode.d.cts +25 -0
  39. package/dist/color-mode.d.ts +25 -0
  40. package/dist/color-mode.js +72 -0
  41. package/dist/dialog.cjs +279 -0
  42. package/dist/dialog.d.cts +66 -0
  43. package/dist/dialog.d.ts +66 -0
  44. package/dist/dialog.js +246 -0
  45. package/dist/drawer.cjs +207 -0
  46. package/dist/drawer.d.cts +36 -0
  47. package/dist/drawer.d.ts +36 -0
  48. package/dist/drawer.js +175 -0
  49. package/dist/empty-state.cjs +93 -0
  50. package/dist/empty-state.d.cts +13 -0
  51. package/dist/empty-state.d.ts +13 -0
  52. package/dist/empty-state.js +71 -0
  53. package/dist/expiration-selector.cjs +138 -0
  54. package/dist/expiration-selector.d.cts +16 -0
  55. package/dist/expiration-selector.d.ts +16 -0
  56. package/dist/expiration-selector.js +116 -0
  57. package/dist/field.cjs +183 -0
  58. package/dist/field.d.cts +24 -0
  59. package/dist/field.d.ts +24 -0
  60. package/dist/field.js +160 -0
  61. package/dist/greeks-display.cjs +114 -0
  62. package/dist/greeks-display.d.cts +14 -0
  63. package/dist/greeks-display.d.ts +14 -0
  64. package/dist/greeks-display.js +92 -0
  65. package/dist/heading.cjs +46 -0
  66. package/dist/heading.d.cts +10 -0
  67. package/dist/heading.d.ts +10 -0
  68. package/dist/heading.js +40 -0
  69. package/dist/icon-button.cjs +491 -0
  70. package/dist/icon-button.d.cts +33 -0
  71. package/dist/icon-button.d.ts +33 -0
  72. package/dist/icon-button.js +470 -0
  73. package/dist/image.cjs +572 -0
  74. package/dist/image.d.cts +57 -0
  75. package/dist/image.d.ts +57 -0
  76. package/dist/image.js +551 -0
  77. package/dist/index.cjs +7316 -0
  78. package/dist/index.d.cts +58 -0
  79. package/dist/index.d.ts +58 -0
  80. package/dist/index.js +7137 -0
  81. package/dist/input-group.cjs +155 -0
  82. package/dist/input-group.d.cts +17 -0
  83. package/dist/input-group.d.ts +17 -0
  84. package/dist/input-group.js +133 -0
  85. package/dist/input.cjs +65 -0
  86. package/dist/input.d.cts +11 -0
  87. package/dist/input.d.ts +11 -0
  88. package/dist/input.js +59 -0
  89. package/dist/link.cjs +630 -0
  90. package/dist/link.d.cts +37 -0
  91. package/dist/link.d.ts +37 -0
  92. package/dist/link.js +606 -0
  93. package/dist/menu.cjs +305 -0
  94. package/dist/menu.d.cts +84 -0
  95. package/dist/menu.d.ts +84 -0
  96. package/dist/menu.js +269 -0
  97. package/dist/option-chain.cjs +221 -0
  98. package/dist/option-chain.d.cts +37 -0
  99. package/dist/option-chain.d.ts +37 -0
  100. package/dist/option-chain.js +199 -0
  101. package/dist/option-position.cjs +164 -0
  102. package/dist/option-position.d.cts +29 -0
  103. package/dist/option-position.d.ts +29 -0
  104. package/dist/option-position.js +142 -0
  105. package/dist/pin-input.cjs +182 -0
  106. package/dist/pin-input.d.cts +25 -0
  107. package/dist/pin-input.d.ts +25 -0
  108. package/dist/pin-input.js +160 -0
  109. package/dist/pnl-diagram.cjs +453 -0
  110. package/dist/pnl-diagram.d.cts +11 -0
  111. package/dist/pnl-diagram.d.ts +11 -0
  112. package/dist/pnl-diagram.js +431 -0
  113. package/dist/popover.cjs +327 -0
  114. package/dist/popover.d.cts +73 -0
  115. package/dist/popover.d.ts +73 -0
  116. package/dist/popover.js +294 -0
  117. package/dist/progress-circle.cjs +152 -0
  118. package/dist/progress-circle.d.cts +47 -0
  119. package/dist/progress-circle.d.ts +47 -0
  120. package/dist/progress-circle.js +128 -0
  121. package/dist/progress.cjs +117 -0
  122. package/dist/progress.d.cts +28 -0
  123. package/dist/progress.d.ts +28 -0
  124. package/dist/progress.js +94 -0
  125. package/dist/provider.cjs +62 -0
  126. package/dist/provider.d.cts +7 -0
  127. package/dist/provider.d.ts +7 -0
  128. package/dist/provider.js +40 -0
  129. package/dist/radio.cjs +177 -0
  130. package/dist/radio.d.cts +53 -0
  131. package/dist/radio.d.ts +53 -0
  132. package/dist/radio.js +153 -0
  133. package/dist/rating.cjs +80 -0
  134. package/dist/rating.d.cts +14 -0
  135. package/dist/rating.d.ts +14 -0
  136. package/dist/rating.js +58 -0
  137. package/dist/select.cjs +791 -0
  138. package/dist/select.d.cts +130 -0
  139. package/dist/select.d.ts +130 -0
  140. package/dist/select.js +757 -0
  141. package/dist/separator.cjs +57 -0
  142. package/dist/separator.d.cts +13 -0
  143. package/dist/separator.d.ts +13 -0
  144. package/dist/separator.js +51 -0
  145. package/dist/skeleton.cjs +370 -0
  146. package/dist/skeleton.d.cts +60 -0
  147. package/dist/skeleton.d.ts +60 -0
  148. package/dist/skeleton.js +346 -0
  149. package/dist/slider.cjs +138 -0
  150. package/dist/slider.d.cts +24 -0
  151. package/dist/slider.d.ts +24 -0
  152. package/dist/slider.js +115 -0
  153. package/dist/strategy-builder.cjs +373 -0
  154. package/dist/strategy-builder.d.cts +26 -0
  155. package/dist/strategy-builder.d.ts +26 -0
  156. package/dist/strategy-builder.js +351 -0
  157. package/dist/switch.cjs +163 -0
  158. package/dist/switch.d.cts +27 -0
  159. package/dist/switch.d.ts +27 -0
  160. package/dist/switch.js +140 -0
  161. package/dist/table.cjs +1044 -0
  162. package/dist/table.d.cts +107 -0
  163. package/dist/table.d.ts +107 -0
  164. package/dist/table.js +1013 -0
  165. package/dist/tabs.cjs +240 -0
  166. package/dist/tabs.d.cts +78 -0
  167. package/dist/tabs.d.ts +78 -0
  168. package/dist/tabs.js +213 -0
  169. package/dist/tag.cjs +651 -0
  170. package/dist/tag.d.cts +21 -0
  171. package/dist/tag.d.ts +21 -0
  172. package/dist/tag.js +628 -0
  173. package/dist/textarea.cjs +65 -0
  174. package/dist/textarea.d.cts +11 -0
  175. package/dist/textarea.d.ts +11 -0
  176. package/dist/textarea.js +59 -0
  177. package/dist/toaster.cjs +99 -0
  178. package/dist/toaster.d.cts +29 -0
  179. package/dist/toaster.d.ts +29 -0
  180. package/dist/toaster.js +96 -0
  181. package/dist/tooltip.cjs +171 -0
  182. package/dist/tooltip.d.cts +39 -0
  183. package/dist/tooltip.d.ts +39 -0
  184. package/dist/tooltip.js +148 -0
  185. package/dist/utils.cjs +11 -0
  186. package/dist/utils.d.cts +5 -0
  187. package/dist/utils.d.ts +5 -0
  188. package/dist/utils.js +9 -0
  189. package/package.json +284 -85
  190. package/src/accordion.tsx +285 -0
  191. package/src/alert.tsx +221 -0
  192. package/src/avatar.tsx +174 -0
  193. package/src/badge.tsx +158 -0
  194. package/src/bank.tsx +325 -0
  195. package/src/button.tsx +411 -0
  196. package/src/checkbox.tsx +307 -0
  197. package/src/close-button.tsx +51 -0
  198. package/src/collapsible.tsx +126 -0
  199. package/src/color-mode.tsx +125 -0
  200. package/src/dialog.tsx +356 -0
  201. package/src/drawer.tsx +186 -0
  202. package/src/empty-state.tsx +97 -0
  203. package/src/expiration-selector.tsx +163 -0
  204. package/src/field.tsx +202 -0
  205. package/src/greeks-display.tsx +138 -0
  206. package/src/heading.tsx +55 -0
  207. package/src/icon-button.tsx +192 -0
  208. package/src/image.tsx +280 -0
  209. package/src/index.ts +268 -0
  210. package/src/input-group.tsx +159 -0
  211. package/src/input.tsx +60 -0
  212. package/src/link.tsx +326 -0
  213. package/src/menu.tsx +471 -0
  214. package/src/option-chain.tsx +261 -0
  215. package/src/option-position.tsx +200 -0
  216. package/src/pin-input.tsx +187 -0
  217. package/src/pnl-diagram.tsx +458 -0
  218. package/src/popover.tsx +400 -0
  219. package/src/progress-circle.tsx +180 -0
  220. package/src/progress.tsx +109 -0
  221. package/src/provider.tsx +12 -0
  222. package/src/radio.tsx +175 -0
  223. package/src/rating.tsx +79 -0
  224. package/src/select.tsx +696 -0
  225. package/src/separator.tsx +59 -0
  226. package/src/skeleton.tsx +302 -0
  227. package/src/slider.tsx +152 -0
  228. package/src/strategy-builder.tsx +466 -0
  229. package/src/switch.tsx +158 -0
  230. package/src/table.tsx +621 -0
  231. package/src/tabs.tsx +354 -0
  232. package/src/tag.tsx +159 -0
  233. package/src/textarea.tsx +60 -0
  234. package/src/toaster.tsx +117 -0
  235. package/src/tokens.css +438 -0
  236. package/src/tooltip.tsx +184 -0
  237. package/src/utils/cn.ts +7 -0
  238. package/src/utils.ts +6 -0
  239. package/tokens.css +438 -0
  240. package/README.md +0 -109
@@ -0,0 +1,351 @@
1
+ "use client";
2
+ import * as React from 'react';
3
+ import { clsx } from 'clsx';
4
+ import { twMerge } from 'tailwind-merge';
5
+ import { jsxs, jsx } from 'react/jsx-runtime';
6
+
7
+ function cn(...inputs) {
8
+ return twMerge(clsx(inputs));
9
+ }
10
+ function findATM(strikes, spot) {
11
+ if (strikes.length === 0) return spot;
12
+ return strikes.reduce(
13
+ (prev, curr) => Math.abs(curr - spot) < Math.abs(prev - spot) ? curr : prev
14
+ );
15
+ }
16
+ function findOTM(strikes, spot, type, offset) {
17
+ const sorted = [...strikes].sort((a, b) => a - b);
18
+ const atm = findATM(strikes, spot);
19
+ const atIdx = sorted.indexOf(atm);
20
+ if (type === "call") {
21
+ return sorted[Math.min(atIdx + offset, sorted.length - 1)] ?? atm;
22
+ }
23
+ return sorted[Math.max(atIdx - offset, 0)] ?? atm;
24
+ }
25
+ var STRATEGY_PRESETS = [
26
+ {
27
+ label: "Bull Call Spread",
28
+ value: "bull_call_spread",
29
+ description: "Buy lower strike call, sell higher strike call",
30
+ buildLegs: (strikes, spot, exp) => {
31
+ const lower = findATM(strikes, spot);
32
+ const upper = findOTM(strikes, spot, "call", 2);
33
+ return [
34
+ { action: "buy", type: "call", strike: lower, expiration: exp, quantity: 1 },
35
+ { action: "sell", type: "call", strike: upper, expiration: exp, quantity: 1 }
36
+ ];
37
+ }
38
+ },
39
+ {
40
+ label: "Bear Put Spread",
41
+ value: "bear_put_spread",
42
+ description: "Buy higher strike put, sell lower strike put",
43
+ buildLegs: (strikes, spot, exp) => {
44
+ const upper = findATM(strikes, spot);
45
+ const lower = findOTM(strikes, spot, "put", 2);
46
+ return [
47
+ { action: "buy", type: "put", strike: upper, expiration: exp, quantity: 1 },
48
+ { action: "sell", type: "put", strike: lower, expiration: exp, quantity: 1 }
49
+ ];
50
+ }
51
+ },
52
+ {
53
+ label: "Iron Condor",
54
+ value: "iron_condor",
55
+ description: "Sell OTM put + call, buy further OTM put + call",
56
+ buildLegs: (strikes, spot, exp) => {
57
+ const sellPut = findOTM(strikes, spot, "put", 1);
58
+ const buyPut = findOTM(strikes, spot, "put", 3);
59
+ const sellCall = findOTM(strikes, spot, "call", 1);
60
+ const buyCall = findOTM(strikes, spot, "call", 3);
61
+ return [
62
+ { action: "buy", type: "put", strike: buyPut, expiration: exp, quantity: 1 },
63
+ { action: "sell", type: "put", strike: sellPut, expiration: exp, quantity: 1 },
64
+ { action: "sell", type: "call", strike: sellCall, expiration: exp, quantity: 1 },
65
+ { action: "buy", type: "call", strike: buyCall, expiration: exp, quantity: 1 }
66
+ ];
67
+ }
68
+ },
69
+ {
70
+ label: "Butterfly",
71
+ value: "butterfly",
72
+ description: "Buy 1 low, sell 2 middle, buy 1 high",
73
+ buildLegs: (strikes, spot, exp) => {
74
+ const mid = findATM(strikes, spot);
75
+ const low = findOTM(strikes, spot, "put", 2);
76
+ const high = findOTM(strikes, spot, "call", 2);
77
+ return [
78
+ { action: "buy", type: "call", strike: low, expiration: exp, quantity: 1 },
79
+ { action: "sell", type: "call", strike: mid, expiration: exp, quantity: 2 },
80
+ { action: "buy", type: "call", strike: high, expiration: exp, quantity: 1 }
81
+ ];
82
+ }
83
+ },
84
+ {
85
+ label: "Straddle",
86
+ value: "straddle",
87
+ description: "Buy call + put at same strike",
88
+ buildLegs: (strikes, spot, exp) => {
89
+ const atm = findATM(strikes, spot);
90
+ return [
91
+ { action: "buy", type: "call", strike: atm, expiration: exp, quantity: 1 },
92
+ { action: "buy", type: "put", strike: atm, expiration: exp, quantity: 1 }
93
+ ];
94
+ }
95
+ },
96
+ {
97
+ label: "Strangle",
98
+ value: "strangle",
99
+ description: "Buy OTM call + OTM put",
100
+ buildLegs: (strikes, spot, exp) => {
101
+ const otmCall = findOTM(strikes, spot, "call", 2);
102
+ const otmPut = findOTM(strikes, spot, "put", 2);
103
+ return [
104
+ { action: "buy", type: "put", strike: otmPut, expiration: exp, quantity: 1 },
105
+ { action: "buy", type: "call", strike: otmCall, expiration: exp, quantity: 1 }
106
+ ];
107
+ }
108
+ },
109
+ {
110
+ label: "Custom",
111
+ value: "custom",
112
+ description: "Build your own multi-leg strategy",
113
+ buildLegs: (_strikes, spot, exp) => [
114
+ { action: "buy", type: "call", strike: spot, expiration: exp, quantity: 1 }
115
+ ]
116
+ }
117
+ ];
118
+ function LegRow({ leg, index, strikes, expirations, removable, onChange, onRemove }) {
119
+ const update = (patch) => {
120
+ onChange(index, { ...leg, ...patch });
121
+ };
122
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-md bg-zinc-800/50 px-2 py-1.5", children: [
123
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-mono text-zinc-500 w-4 shrink-0", children: index + 1 }),
124
+ /* @__PURE__ */ jsx(
125
+ "button",
126
+ {
127
+ type: "button",
128
+ className: cn(
129
+ "px-2 py-0.5 rounded text-xs font-semibold transition-colors",
130
+ leg.action === "buy" ? "bg-emerald-900/60 text-emerald-400" : "bg-red-900/60 text-red-400"
131
+ ),
132
+ onClick: () => update({ action: leg.action === "buy" ? "sell" : "buy" }),
133
+ children: leg.action === "buy" ? "BUY" : "SELL"
134
+ }
135
+ ),
136
+ /* @__PURE__ */ jsx(
137
+ "button",
138
+ {
139
+ type: "button",
140
+ className: cn(
141
+ "px-2 py-0.5 rounded text-xs font-medium transition-colors",
142
+ leg.type === "call" ? "bg-blue-900/40 text-blue-400" : "bg-orange-900/40 text-orange-400"
143
+ ),
144
+ onClick: () => update({ type: leg.type === "call" ? "put" : "call" }),
145
+ children: leg.type === "call" ? "CALL" : "PUT"
146
+ }
147
+ ),
148
+ /* @__PURE__ */ jsx(
149
+ "select",
150
+ {
151
+ value: leg.strike,
152
+ onChange: (e) => update({ strike: Number(e.target.value) }),
153
+ className: "bg-zinc-900 border border-zinc-700 rounded px-1.5 py-0.5 text-xs font-mono text-zinc-200 outline-none focus:border-zinc-500 min-w-[72px]",
154
+ children: strikes.map((s) => /* @__PURE__ */ jsx("option", { value: s, children: s.toFixed(2) }, s))
155
+ }
156
+ ),
157
+ /* @__PURE__ */ jsx(
158
+ "select",
159
+ {
160
+ value: leg.expiration,
161
+ onChange: (e) => update({ expiration: e.target.value }),
162
+ className: "bg-zinc-900 border border-zinc-700 rounded px-1.5 py-0.5 text-xs text-zinc-200 outline-none focus:border-zinc-500 min-w-[80px]",
163
+ children: expirations.map((exp) => /* @__PURE__ */ jsx("option", { value: exp, children: exp }, exp))
164
+ }
165
+ ),
166
+ /* @__PURE__ */ jsx(
167
+ "input",
168
+ {
169
+ type: "number",
170
+ min: 1,
171
+ max: 999,
172
+ value: leg.quantity,
173
+ onChange: (e) => update({ quantity: Math.max(1, parseInt(e.target.value, 10) || 1) }),
174
+ className: "bg-zinc-900 border border-zinc-700 rounded px-1.5 py-0.5 text-xs font-mono text-zinc-200 outline-none focus:border-zinc-500 w-[48px] text-center"
175
+ }
176
+ ),
177
+ leg.premium !== void 0 && /* @__PURE__ */ jsxs("span", { className: cn(
178
+ "font-mono tabular-nums text-xs ml-auto",
179
+ leg.premium >= 0 ? "text-emerald-400" : "text-red-400"
180
+ ), children: [
181
+ leg.premium >= 0 ? "+" : "",
182
+ leg.premium.toFixed(2)
183
+ ] }),
184
+ removable && /* @__PURE__ */ jsx(
185
+ "button",
186
+ {
187
+ type: "button",
188
+ onClick: () => onRemove(index),
189
+ className: "ml-auto p-0.5 text-zinc-500 hover:text-red-400 transition-colors",
190
+ "aria-label": "Remove leg",
191
+ children: /* @__PURE__ */ jsx("svg", { className: "h-3.5 w-3.5", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M18 6L6 18M6 6l12 12", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) })
192
+ }
193
+ )
194
+ ] });
195
+ }
196
+ var StrategyBuilder = React.forwardRef(
197
+ function StrategyBuilder2(props, ref) {
198
+ const {
199
+ underlying,
200
+ expirations,
201
+ strikes,
202
+ spotPrice,
203
+ onSubmit,
204
+ className,
205
+ ...rest
206
+ } = props;
207
+ const defaultExp = expirations[0] ?? "";
208
+ const [strategyType, setStrategyType] = React.useState("bull_call_spread");
209
+ const [legs, setLegs] = React.useState(() => {
210
+ const preset = STRATEGY_PRESETS.find((p) => p.value === "bull_call_spread");
211
+ return preset ? preset.buildLegs(strikes, spotPrice, defaultExp) : [];
212
+ });
213
+ const handleStrategyChange = React.useCallback(
214
+ (e) => {
215
+ const type = e.target.value;
216
+ setStrategyType(type);
217
+ const preset = STRATEGY_PRESETS.find((p) => p.value === type);
218
+ if (preset) {
219
+ setLegs(preset.buildLegs(strikes, spotPrice, defaultExp));
220
+ }
221
+ },
222
+ [strikes, spotPrice, defaultExp]
223
+ );
224
+ const handleLegChange = React.useCallback(
225
+ (index, leg) => {
226
+ setLegs((prev) => {
227
+ const next = [...prev];
228
+ next[index] = leg;
229
+ return next;
230
+ });
231
+ },
232
+ []
233
+ );
234
+ const handleLegRemove = React.useCallback(
235
+ (index) => {
236
+ setLegs((prev) => prev.filter((_, i) => i !== index));
237
+ },
238
+ []
239
+ );
240
+ const handleAddLeg = React.useCallback(() => {
241
+ if (legs.length >= 4) return;
242
+ const atm = findATM(strikes, spotPrice);
243
+ setLegs((prev) => [
244
+ ...prev,
245
+ { action: "buy", type: "call", strike: atm, expiration: defaultExp, quantity: 1 }
246
+ ]);
247
+ }, [legs.length, strikes, spotPrice, defaultExp]);
248
+ const netPremium = React.useMemo(() => {
249
+ return legs.reduce((sum, leg) => {
250
+ if (leg.premium === void 0) return sum;
251
+ const sign = leg.action === "buy" ? -1 : 1;
252
+ return sum + sign * leg.premium * leg.quantity;
253
+ }, 0);
254
+ }, [legs]);
255
+ const handleSubmit = React.useCallback(() => {
256
+ if (!onSubmit || legs.length === 0) return;
257
+ onSubmit({
258
+ type: strategyType,
259
+ legs,
260
+ netPremium
261
+ });
262
+ }, [onSubmit, strategyType, legs, netPremium]);
263
+ const currentPreset = STRATEGY_PRESETS.find((p) => p.value === strategyType);
264
+ return /* @__PURE__ */ jsxs(
265
+ "div",
266
+ {
267
+ ref,
268
+ className: cn(
269
+ "flex flex-col gap-3 rounded-lg border border-zinc-800 bg-zinc-900/50 p-3",
270
+ className
271
+ ),
272
+ ...rest,
273
+ children: [
274
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
275
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
276
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold uppercase tracking-wider text-zinc-500", children: "Strategy" }),
277
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-zinc-200", children: underlying })
278
+ ] }),
279
+ /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-zinc-500", children: [
280
+ "Spot: ",
281
+ /* @__PURE__ */ jsx("span", { className: "font-mono tabular-nums text-zinc-300", children: spotPrice.toFixed(2) })
282
+ ] })
283
+ ] }),
284
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx(
285
+ "select",
286
+ {
287
+ value: strategyType,
288
+ onChange: handleStrategyChange,
289
+ className: "flex-1 bg-zinc-900 border border-zinc-700 rounded-md px-2.5 py-1.5 text-sm text-zinc-200 outline-none focus:border-zinc-500",
290
+ children: STRATEGY_PRESETS.map((preset) => /* @__PURE__ */ jsx("option", { value: preset.value, children: preset.label }, preset.value))
291
+ }
292
+ ) }),
293
+ currentPreset && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-zinc-500 -mt-1", children: currentPreset.description }),
294
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1.5", children: legs.map((leg, i) => /* @__PURE__ */ jsx(
295
+ LegRow,
296
+ {
297
+ leg,
298
+ index: i,
299
+ strikes,
300
+ expirations,
301
+ removable: strategyType === "custom" && legs.length > 1,
302
+ onChange: handleLegChange,
303
+ onRemove: handleLegRemove
304
+ },
305
+ i
306
+ )) }),
307
+ strategyType === "custom" && legs.length < 4 && /* @__PURE__ */ jsxs(
308
+ "button",
309
+ {
310
+ type: "button",
311
+ onClick: handleAddLeg,
312
+ className: "flex items-center justify-center gap-1 rounded-md border border-dashed border-zinc-700 py-1 text-xs text-zinc-500 hover:text-zinc-300 hover:border-zinc-500 transition-colors",
313
+ children: [
314
+ /* @__PURE__ */ jsx("svg", { className: "h-3 w-3", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M12 5v14M5 12h14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }),
315
+ "Add Leg"
316
+ ]
317
+ }
318
+ ),
319
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pt-1 border-t border-zinc-800", children: [
320
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
321
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-zinc-500", children: "Net:" }),
322
+ /* @__PURE__ */ jsxs("span", { className: cn(
323
+ "font-mono tabular-nums text-sm font-semibold",
324
+ netPremium > 0 ? "text-emerald-400" : netPremium < 0 ? "text-red-400" : "text-zinc-400"
325
+ ), children: [
326
+ netPremium > 0 ? "Credit " : netPremium < 0 ? "Debit " : "",
327
+ netPremium !== 0 ? `$${Math.abs(netPremium).toFixed(2)}` : "-"
328
+ ] })
329
+ ] }),
330
+ /* @__PURE__ */ jsx(
331
+ "button",
332
+ {
333
+ type: "button",
334
+ onClick: handleSubmit,
335
+ disabled: legs.length === 0,
336
+ className: cn(
337
+ "px-4 py-1.5 rounded-md text-xs font-semibold transition-colors",
338
+ "bg-blue-600 text-white hover:bg-blue-500",
339
+ "disabled:opacity-40 disabled:cursor-not-allowed"
340
+ ),
341
+ children: "Review Order"
342
+ }
343
+ )
344
+ ] })
345
+ ]
346
+ }
347
+ );
348
+ }
349
+ );
350
+
351
+ export { StrategyBuilder };
@@ -0,0 +1,163 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var RadixSwitch = require('@radix-ui/react-switch');
5
+ var React = require('react');
6
+ var clsx = require('clsx');
7
+ var tailwindMerge = require('tailwind-merge');
8
+ var jsxRuntime = require('react/jsx-runtime');
9
+
10
+ function _interopNamespace(e) {
11
+ if (e && e.__esModule) return e;
12
+ var n = Object.create(null);
13
+ if (e) {
14
+ Object.keys(e).forEach(function (k) {
15
+ if (k !== 'default') {
16
+ var d = Object.getOwnPropertyDescriptor(e, k);
17
+ Object.defineProperty(n, k, d.get ? d : {
18
+ enumerable: true,
19
+ get: function () { return e[k]; }
20
+ });
21
+ }
22
+ });
23
+ }
24
+ n.default = e;
25
+ return Object.freeze(n);
26
+ }
27
+
28
+ var RadixSwitch__namespace = /*#__PURE__*/_interopNamespace(RadixSwitch);
29
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
30
+
31
+ // src/switch.tsx
32
+ function cn(...inputs) {
33
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
34
+ }
35
+ var NOOP = () => {
36
+ };
37
+ var SIZE_CLASSES = {
38
+ sm: {
39
+ root: "h-4 w-7",
40
+ thumb: "h-3 w-3 data-[state=checked]:translate-x-3",
41
+ label: "text-xs"
42
+ },
43
+ md: {
44
+ root: "h-5 w-9",
45
+ thumb: "h-4 w-4 data-[state=checked]:translate-x-4",
46
+ label: "text-sm"
47
+ },
48
+ lg: {
49
+ root: "h-6 w-11",
50
+ thumb: "h-5 w-5 data-[state=checked]:translate-x-5",
51
+ label: "text-base"
52
+ }
53
+ };
54
+ var SwitchBase = React__namespace.forwardRef(
55
+ function Switch(props, ref) {
56
+ const {
57
+ inputProps,
58
+ children,
59
+ rootRef,
60
+ labelProps,
61
+ trackLabel,
62
+ thumbLabel,
63
+ checked: checkedProp,
64
+ defaultChecked,
65
+ onCheckedChange,
66
+ onChange,
67
+ disabled = false,
68
+ size = "md",
69
+ direction,
70
+ className,
71
+ ...rest
72
+ } = props;
73
+ const [internalChecked, setInternalChecked] = React__namespace.useState(defaultChecked ?? false);
74
+ const isControlled = checkedProp !== void 0;
75
+ const checkedState = isControlled ? checkedProp : internalChecked;
76
+ const handleCheckedChange = React__namespace.useCallback(
77
+ (nextChecked) => {
78
+ if (!isControlled) {
79
+ setInternalChecked(nextChecked);
80
+ }
81
+ if (onCheckedChange) {
82
+ onCheckedChange({ checked: nextChecked });
83
+ }
84
+ },
85
+ [isControlled, onCheckedChange]
86
+ );
87
+ const sizeClasses = SIZE_CLASSES[size];
88
+ const isRtl = direction === "rtl";
89
+ return /* @__PURE__ */ jsxRuntime.jsxs(
90
+ "label",
91
+ {
92
+ ref: rootRef,
93
+ className: cn(
94
+ "inline-flex items-center cursor-pointer select-none gap-2",
95
+ isRtl && "flex-row-reverse",
96
+ disabled && "opacity-50 cursor-not-allowed",
97
+ className
98
+ ),
99
+ onChange,
100
+ "data-disabled": disabled || void 0,
101
+ ...rest,
102
+ children: [
103
+ /* @__PURE__ */ jsxRuntime.jsxs(
104
+ RadixSwitch__namespace.Root,
105
+ {
106
+ checked: checkedState,
107
+ onCheckedChange: handleCheckedChange,
108
+ disabled,
109
+ className: cn(
110
+ "relative inline-flex shrink-0 items-center rounded-full",
111
+ "bg-gray-300 dark:bg-gray-600",
112
+ "data-[state=checked]:bg-gray-800 dark:bg-white",
113
+ "focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-500",
114
+ "transition-colors duration-200",
115
+ sizeClasses.root
116
+ ),
117
+ children: [
118
+ trackLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inset-0 flex items-center justify-center text-[8px] font-bold text-white pointer-events-none", children: checkedState ? trackLabel.on : trackLabel.off }),
119
+ /* @__PURE__ */ jsxRuntime.jsx(
120
+ RadixSwitch__namespace.Thumb,
121
+ {
122
+ className: cn(
123
+ "block rounded-full bg-white shadow-sm",
124
+ "transition-transform duration-200",
125
+ "translate-x-0.5",
126
+ sizeClasses.thumb
127
+ ),
128
+ children: thumbLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex items-center justify-center h-full w-full text-[8px]", children: checkedState ? thumbLabel.on : thumbLabel.off })
129
+ }
130
+ )
131
+ ]
132
+ }
133
+ ),
134
+ /* @__PURE__ */ jsxRuntime.jsx(
135
+ "input",
136
+ {
137
+ ref,
138
+ type: "checkbox",
139
+ className: "sr-only",
140
+ checked: checkedState,
141
+ disabled,
142
+ tabIndex: -1,
143
+ "aria-hidden": true,
144
+ onChange: NOOP,
145
+ ...inputProps
146
+ }
147
+ ),
148
+ children != null && /* @__PURE__ */ jsxRuntime.jsx(
149
+ "span",
150
+ {
151
+ ...labelProps,
152
+ className: cn(sizeClasses.label, labelProps?.className),
153
+ children
154
+ }
155
+ )
156
+ ]
157
+ }
158
+ );
159
+ }
160
+ );
161
+ var Switch2 = SwitchBase;
162
+
163
+ exports.Switch = Switch2;
@@ -0,0 +1,27 @@
1
+ import * as React from 'react';
2
+
3
+ interface SwitchProps extends Omit<React.ComponentPropsWithoutRef<'label'>, 'onChange' | 'dir'> {
4
+ inputProps?: React.InputHTMLAttributes<HTMLInputElement>;
5
+ labelProps?: React.ComponentPropsWithoutRef<'span'>;
6
+ rootRef?: React.Ref<HTMLLabelElement>;
7
+ trackLabel?: {
8
+ on: React.ReactNode;
9
+ off: React.ReactNode;
10
+ };
11
+ thumbLabel?: {
12
+ on: React.ReactNode;
13
+ off: React.ReactNode;
14
+ };
15
+ checked?: boolean;
16
+ defaultChecked?: boolean;
17
+ onCheckedChange?: (details: {
18
+ checked: boolean;
19
+ }) => void;
20
+ onChange?: React.FormEventHandler<HTMLLabelElement>;
21
+ disabled?: boolean;
22
+ size?: 'sm' | 'md' | 'lg';
23
+ direction?: 'ltr' | 'rtl';
24
+ }
25
+ declare const Switch: React.ForwardRefExoticComponent<SwitchProps & React.RefAttributes<HTMLInputElement>>;
26
+
27
+ export { Switch, type SwitchProps };
@@ -0,0 +1,27 @@
1
+ import * as React from 'react';
2
+
3
+ interface SwitchProps extends Omit<React.ComponentPropsWithoutRef<'label'>, 'onChange' | 'dir'> {
4
+ inputProps?: React.InputHTMLAttributes<HTMLInputElement>;
5
+ labelProps?: React.ComponentPropsWithoutRef<'span'>;
6
+ rootRef?: React.Ref<HTMLLabelElement>;
7
+ trackLabel?: {
8
+ on: React.ReactNode;
9
+ off: React.ReactNode;
10
+ };
11
+ thumbLabel?: {
12
+ on: React.ReactNode;
13
+ off: React.ReactNode;
14
+ };
15
+ checked?: boolean;
16
+ defaultChecked?: boolean;
17
+ onCheckedChange?: (details: {
18
+ checked: boolean;
19
+ }) => void;
20
+ onChange?: React.FormEventHandler<HTMLLabelElement>;
21
+ disabled?: boolean;
22
+ size?: 'sm' | 'md' | 'lg';
23
+ direction?: 'ltr' | 'rtl';
24
+ }
25
+ declare const Switch: React.ForwardRefExoticComponent<SwitchProps & React.RefAttributes<HTMLInputElement>>;
26
+
27
+ export { Switch, type SwitchProps };