@loafmarkets/ui 0.0.1 → 0.0.3

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/dist/index.mjs CHANGED
@@ -212,6 +212,66 @@ var PortfolioSummary = React5.forwardRef(
212
212
  PortfolioSummary.displayName = "PortfolioSummary";
213
213
  var clamp = (v, min, max) => Math.min(max, Math.max(min, v));
214
214
  var fmt0 = (v) => Math.abs(v).toLocaleString(void 0, { maximumFractionDigits: 0 });
215
+ var normalizeLevels = (levels = []) => levels.filter((level) => Number.isFinite(level.price) && level.price > 0 && Number.isFinite(level.amount) && level.amount > 0);
216
+ var estimateMarketBuyFromUsd = (levels = [], usdAmount) => {
217
+ if (!Number.isFinite(usdAmount) || usdAmount <= 0) return { tokens: 0, value: 0, avgPrice: null };
218
+ const asks = normalizeLevels(levels).sort((a, b) => a.price - b.price);
219
+ let remainingUsd = usdAmount;
220
+ let tokensFilled = 0;
221
+ let spent = 0;
222
+ for (const level of asks) {
223
+ if (remainingUsd <= 0) break;
224
+ const levelValueCapacity = level.amount * level.price;
225
+ const usdToSpend = Math.min(remainingUsd, levelValueCapacity);
226
+ const tokensFromLevel = usdToSpend / level.price;
227
+ tokensFilled += tokensFromLevel;
228
+ spent += usdToSpend;
229
+ remainingUsd -= usdToSpend;
230
+ }
231
+ return {
232
+ tokens: tokensFilled,
233
+ value: spent,
234
+ avgPrice: tokensFilled > 0 ? spent / tokensFilled : null
235
+ };
236
+ };
237
+ var estimateMarketBuyFromTokens = (levels = [], tokenAmount) => {
238
+ if (!Number.isFinite(tokenAmount) || tokenAmount <= 0) return { tokens: 0, value: 0, avgPrice: null };
239
+ const asks = normalizeLevels(levels).sort((a, b) => a.price - b.price);
240
+ let remainingTokens = tokenAmount;
241
+ let tokensFilled = 0;
242
+ let spent = 0;
243
+ for (const level of asks) {
244
+ if (remainingTokens <= 0) break;
245
+ const tokensFromLevel = Math.min(remainingTokens, level.amount);
246
+ spent += tokensFromLevel * level.price;
247
+ tokensFilled += tokensFromLevel;
248
+ remainingTokens -= tokensFromLevel;
249
+ }
250
+ return {
251
+ tokens: tokensFilled,
252
+ value: spent,
253
+ avgPrice: tokensFilled > 0 ? spent / tokensFilled : null
254
+ };
255
+ };
256
+ var estimateMarketSellFromTokens = (levels = [], tokenAmount) => {
257
+ if (!Number.isFinite(tokenAmount) || tokenAmount <= 0) return { tokens: 0, value: 0, avgPrice: null };
258
+ const bids = normalizeLevels(levels).sort((a, b) => b.price - a.price);
259
+ let remainingTokens = tokenAmount;
260
+ let tokensFilled = 0;
261
+ let received = 0;
262
+ for (const level of bids) {
263
+ if (remainingTokens <= 0) break;
264
+ const tokensFromLevel = Math.min(remainingTokens, level.amount);
265
+ received += tokensFromLevel * level.price;
266
+ tokensFilled += tokensFromLevel;
267
+ remainingTokens -= tokensFromLevel;
268
+ }
269
+ return {
270
+ tokens: tokensFilled,
271
+ value: received,
272
+ avgPrice: tokensFilled > 0 ? received / tokensFilled : null
273
+ };
274
+ };
215
275
  function HousePositionSlider({
216
276
  tokenId,
217
277
  tokenSymbol,
@@ -221,6 +281,7 @@ function HousePositionSlider({
221
281
  tokensHeld,
222
282
  pendingOrders = [],
223
283
  defaultOrderType = "market",
284
+ orderbook,
224
285
  onConfirmOrder,
225
286
  className,
226
287
  ...props
@@ -234,45 +295,103 @@ function HousePositionSlider({
234
295
  const [orderType, setOrderType] = React5.useState(defaultOrderType);
235
296
  const [limitPrice, setLimitPrice] = React5.useState(currentPrice);
236
297
  const [limitPriceInput, setLimitPriceInput] = React5.useState(currentPrice.toFixed(2));
298
+ const [limitPriceDirty, setLimitPriceDirty] = React5.useState(false);
237
299
  const [ownershipInput, setOwnershipInput] = React5.useState("");
238
300
  const [tokenAmountInput, setTokenAmountInput] = React5.useState("");
239
301
  const houseRef = React5.useRef(null);
302
+ const asks = orderbook?.asks ?? [];
303
+ const bids = orderbook?.bids ?? [];
240
304
  React5.useEffect(() => {
305
+ if (orderType !== "limit") return;
306
+ if (limitPriceDirty) return;
241
307
  setLimitPrice(currentPrice);
242
308
  setLimitPriceInput(currentPrice.toFixed(2));
243
- }, [currentPrice]);
309
+ }, [currentPrice, limitPriceDirty, orderType]);
244
310
  const effectivePrice = orderType === "limit" ? limitPrice : currentPrice;
245
- const holdingsValue = tokensHeld * effectivePrice;
246
- const totalCapacity = holdingsValue + availableCash;
247
311
  const pendingBuyValue = pendingOrders.filter((o) => o.type === "buy").reduce((s, o) => s + o.value, 0);
248
312
  const pendingSellTokens = pendingOrders.filter((o) => o.type === "sell").reduce((s, o) => s + Math.abs(o.tokens), 0);
249
313
  const effectiveAvailableCash = Math.max(0, availableCash - pendingBuyValue);
250
314
  const effectiveTokensHeld = Math.max(0, tokensHeld - pendingSellTokens);
251
- const baselinePct = totalCapacity <= 0 ? 0 : holdingsValue / totalCapacity * 100;
315
+ const holdingsValue = tokensHeld * effectivePrice;
316
+ const sliderHoldingsValue = effectiveTokensHeld * effectivePrice;
317
+ const sliderTotalCapacity = sliderHoldingsValue + effectiveAvailableCash;
318
+ const baselinePct = sliderTotalCapacity <= 0 ? 0 : sliderHoldingsValue / sliderTotalCapacity * 100;
252
319
  let deltaTokens = 0;
253
320
  let deltaValue = 0;
321
+ let marketAvgPrice = null;
254
322
  let targetTokens = tokensHeld;
255
323
  let targetValue = holdingsValue;
324
+ const limitPriceSafe = limitPrice > 0 ? limitPrice : currentPrice || 1;
256
325
  if (orderMode === "buy") {
257
- if (buyTrackingMode === "tokens") {
258
- deltaTokens = deltaTokensBuy;
259
- deltaValue = deltaTokensBuy * effectivePrice;
326
+ if (orderType === "market") {
327
+ if (buyTrackingMode === "tokens") {
328
+ const desiredTokens = Math.max(0, deltaTokensBuy);
329
+ const result = estimateMarketBuyFromTokens(asks, desiredTokens);
330
+ deltaTokens = result.tokens;
331
+ deltaValue = result.value;
332
+ marketAvgPrice = result.avgPrice;
333
+ } else {
334
+ const notional = Math.min(Math.max(0, deltaDollars), effectiveAvailableCash);
335
+ const result = estimateMarketBuyFromUsd(asks, notional);
336
+ deltaTokens = result.tokens;
337
+ deltaValue = result.value;
338
+ marketAvgPrice = result.avgPrice;
339
+ }
260
340
  } else {
261
- deltaValue = deltaDollars;
262
- deltaTokens = deltaDollars / (effectivePrice || 1);
341
+ if (buyTrackingMode === "tokens") {
342
+ deltaTokens = deltaTokensBuy;
343
+ deltaValue = deltaTokensBuy * limitPriceSafe;
344
+ } else {
345
+ const notional = Math.min(Math.max(0, deltaDollars), effectiveAvailableCash);
346
+ deltaValue = notional;
347
+ deltaTokens = notional / limitPriceSafe;
348
+ }
263
349
  }
264
350
  } else if (orderMode === "sell") {
265
- deltaTokens = deltaTokensSell;
266
- deltaValue = deltaTokensSell * effectivePrice;
351
+ if (orderType === "market") {
352
+ const tokensToSell = Math.abs(deltaTokensSell);
353
+ const result = estimateMarketSellFromTokens(bids, tokensToSell);
354
+ deltaTokens = -result.tokens;
355
+ deltaValue = -result.value;
356
+ marketAvgPrice = result.avgPrice;
357
+ } else {
358
+ deltaTokens = deltaTokensSell;
359
+ deltaValue = deltaTokensSell * limitPriceSafe;
360
+ }
267
361
  }
268
362
  targetTokens = tokensHeld + deltaTokens;
269
363
  targetValue = targetTokens * effectivePrice;
270
- const targetPct = totalCapacity <= 0 ? 0 : targetValue / totalCapacity * 100;
364
+ const plannedDeltaValue = (() => {
365
+ if (orderMode === "buy") {
366
+ if (buyTrackingMode === "dollars") {
367
+ const notional = Math.min(Math.max(0, deltaDollars), effectiveAvailableCash);
368
+ return notional;
369
+ }
370
+ const tokensPlanned = Math.max(0, deltaTokensBuy);
371
+ const referencePrice = orderType === "market" ? currentPrice || limitPriceSafe : limitPriceSafe;
372
+ return Math.min(tokensPlanned * referencePrice, effectiveAvailableCash);
373
+ }
374
+ if (orderMode === "sell") {
375
+ const tokensToSell = Math.abs(Math.min(0, deltaTokensSell));
376
+ const sellValue = tokensToSell * effectivePrice;
377
+ return -Math.min(sellValue, sliderHoldingsValue);
378
+ }
379
+ return 0;
380
+ })();
381
+ const sliderTargetValue = clamp(sliderHoldingsValue + plannedDeltaValue, 0, sliderTotalCapacity);
382
+ const targetPct = sliderTotalCapacity <= 0 ? 0 : sliderTargetValue / sliderTotalCapacity * 100;
271
383
  const isIncrease = orderMode === "buy";
272
384
  const hasChange = orderMode !== "none" && (Math.abs(deltaTokens) > 1e-3 || Math.abs(deltaValue) > 0.01);
273
385
  const currentOwnership = totalTokens <= 0 ? 0 : tokensHeld / totalTokens * 100;
274
386
  const targetOwnership = totalTokens <= 0 ? 0 : targetTokens / totalTokens * 100;
275
387
  const estFeeTokens = Math.abs(deltaValue) * 5e-3 / (effectivePrice || 1);
388
+ const resetOrder = React5.useCallback(() => {
389
+ setOrderMode("none");
390
+ setBuyTrackingMode("dollars");
391
+ setDeltaDollars(0);
392
+ setDeltaTokensBuy(0);
393
+ setDeltaTokensSell(0);
394
+ }, []);
276
395
  const updateOrderFromTargetValue = React5.useCallback(
277
396
  (newTargetValue) => {
278
397
  const newDeltaValue = newTargetValue - holdingsValue;
@@ -293,13 +412,9 @@ function HousePositionSlider({
293
412
  setDeltaTokensBuy(0);
294
413
  return;
295
414
  }
296
- setOrderMode("none");
297
- setBuyTrackingMode("dollars");
298
- setDeltaDollars(0);
299
- setDeltaTokensBuy(0);
300
- setDeltaTokensSell(0);
415
+ resetOrder();
301
416
  },
302
- [effectiveAvailableCash, effectivePrice, effectiveTokensHeld, holdingsValue, tokensHeld]
417
+ [effectiveAvailableCash, effectivePrice, effectiveTokensHeld, holdingsValue, resetOrder, tokensHeld]
303
418
  );
304
419
  const updateOrderFromOwnership = React5.useCallback(
305
420
  (newOwnershipPercent) => {
@@ -328,13 +443,47 @@ function HousePositionSlider({
328
443
  setDeltaTokensBuy(0);
329
444
  return;
330
445
  }
331
- setOrderMode("none");
332
- setBuyTrackingMode("dollars");
333
- setDeltaDollars(0);
334
- setDeltaTokensBuy(0);
335
- setDeltaTokensSell(0);
446
+ resetOrder();
447
+ },
448
+ [effectiveAvailableCash, effectivePrice, effectiveTokensHeld, resetOrder]
449
+ );
450
+ const updateOrderFromSlider = React5.useCallback(
451
+ (pct) => {
452
+ const normalized = (pct - 50) / 50;
453
+ const magnitude = Math.min(Math.abs(normalized), 1);
454
+ if (magnitude < 0.02) {
455
+ resetOrder();
456
+ return;
457
+ }
458
+ if (normalized > 0) {
459
+ const notional = clamp(magnitude * effectiveAvailableCash, 0, effectiveAvailableCash);
460
+ if (notional <= 0) {
461
+ resetOrder();
462
+ return;
463
+ }
464
+ setOrderMode("buy");
465
+ setBuyTrackingMode("dollars");
466
+ setDeltaDollars(notional);
467
+ setDeltaTokensBuy(0);
468
+ setDeltaTokensSell(0);
469
+ return;
470
+ }
471
+ if (normalized < 0) {
472
+ const tokensToSell = clamp(magnitude * effectiveTokensHeld, 0, effectiveTokensHeld);
473
+ if (tokensToSell <= 0) {
474
+ resetOrder();
475
+ return;
476
+ }
477
+ setOrderMode("sell");
478
+ setBuyTrackingMode("dollars");
479
+ setDeltaTokensSell(-tokensToSell);
480
+ setDeltaDollars(0);
481
+ setDeltaTokensBuy(0);
482
+ return;
483
+ }
484
+ resetOrder();
336
485
  },
337
- [effectiveAvailableCash, effectivePrice, effectiveTokensHeld]
486
+ [effectiveAvailableCash, effectiveTokensHeld, resetOrder]
338
487
  );
339
488
  const handleDragAtClientY = React5.useCallback(
340
489
  (clientY) => {
@@ -342,9 +491,9 @@ function HousePositionSlider({
342
491
  const rect = houseRef.current.getBoundingClientRect();
343
492
  const y = clientY - rect.top;
344
493
  const pct = clamp(100 - y / rect.height * 100, 0, 100);
345
- updateOrderFromTargetValue(pct / 100 * totalCapacity);
494
+ updateOrderFromSlider(pct);
346
495
  },
347
- [totalCapacity, updateOrderFromTargetValue]
496
+ [updateOrderFromSlider]
348
497
  );
349
498
  const onMouseDown = (e) => {
350
499
  e.preventDefault();
@@ -374,15 +523,21 @@ function HousePositionSlider({
374
523
  document.addEventListener("touchend", onEnd);
375
524
  };
376
525
  const handleCancel = () => {
377
- setOrderMode("none");
378
- setBuyTrackingMode("dollars");
379
- setDeltaDollars(0);
380
- setDeltaTokensBuy(0);
381
- setDeltaTokensSell(0);
526
+ resetOrder();
527
+ };
528
+ const handleOrderTypeSelection = (next) => {
529
+ setOrderType(next);
530
+ if (next === "limit") {
531
+ setLimitPriceDirty(false);
532
+ setLimitPrice(currentPrice);
533
+ setLimitPriceInput(currentPrice.toFixed(2));
534
+ } else {
535
+ setLimitPriceDirty(false);
536
+ }
382
537
  };
383
538
  const handleConfirm = () => {
384
539
  if (!hasChange) return;
385
- const priceToUse = orderType === "market" ? currentPrice : limitPrice;
540
+ const priceToUse = orderType === "market" ? marketAvgPrice ?? currentPrice : limitPrice;
386
541
  onConfirmOrder?.({
387
542
  side: isIncrease ? "buy" : "sell",
388
543
  orderType,
@@ -407,6 +562,8 @@ function HousePositionSlider({
407
562
  const showIncrease = targetPct > baselinePct;
408
563
  const showDecrease = targetPct < baselinePct;
409
564
  const valueLabel = orderType === "limit" ? `${tokenSymbol} Owned at Limit` : `${tokenSymbol} Owned`;
565
+ const deltaSign = deltaValue > 0 ? "+" : deltaValue < 0 ? "-" : "";
566
+ const percentMarkers = [100, 50, 25, 0];
410
567
  return /* @__PURE__ */ jsxs("div", { className: cn("relative flex w-full flex-col items-center gap-6 rounded-[12px] bg-black/20 px-8 pb-6 pt-12", className), ...props, children: [
411
568
  /* @__PURE__ */ jsx("div", { className: "absolute left-4 top-4 text-[1.1rem] font-semibold tracking-[0.5px] text-white", children: "Place Order" }),
412
569
  hasChange ? /* @__PURE__ */ jsx(
@@ -420,46 +577,53 @@ function HousePositionSlider({
420
577
  ) : null,
421
578
  /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
422
579
  /* @__PURE__ */ jsx("div", { className: "mb-2 text-xs uppercase tracking-[1px] text-[#888]", children: valueLabel }),
423
- /* @__PURE__ */ jsx("div", { className: "inline-flex items-baseline gap-2 text-[2rem] font-bold text-white", children: isDragging ? /* @__PURE__ */ jsxs(
424
- "span",
425
- {
426
- className: cn("text-[2rem] font-semibold", deltaValue >= 0 ? "text-[#0ecb81]" : "text-[#f6465d]"),
427
- children: [
428
- deltaValue > 0 ? "+" : deltaValue < 0 ? "-" : "",
429
- "$",
430
- fmt0(deltaValue)
431
- ]
432
- }
433
- ) : /* @__PURE__ */ jsxs(Fragment, { children: [
434
- /* @__PURE__ */ jsxs("span", { children: [
435
- "$",
436
- fmt0(targetValue)
437
- ] }),
438
- hasChange ? /* @__PURE__ */ jsxs(
580
+ /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-center gap-3", children: [
581
+ /* @__PURE__ */ jsxs(
439
582
  "span",
440
583
  {
441
584
  className: cn(
442
- "relative top-[-0.5rem] text-[0.85rem] font-semibold",
585
+ "text-[2.2rem] font-semibold",
443
586
  deltaValue >= 0 ? "text-[#0ecb81]" : "text-[#f6465d]"
444
587
  ),
445
588
  children: [
446
- deltaValue > 0 ? "+" : deltaValue < 0 ? "-" : "",
589
+ deltaSign,
590
+ "$",
447
591
  fmt0(deltaValue)
448
592
  ]
449
593
  }
450
- ) : null
451
- ] }) })
452
- ] }),
453
- /* @__PURE__ */ jsx("div", { ref: houseRef, className: "h-[200px] w-[160px] select-none touch-none", style: { cursor: "ns-resize" }, onMouseDown, onTouchStart, children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 120 160", className: "h-full w-full overflow-visible", children: [
454
- /* @__PURE__ */ jsx("rect", { x: "10", y: "10", width: "100", height: "140", rx: "8", fill: "rgba(255,255,255,0.04)", stroke: "rgba(255,255,255,0.10)" }),
455
- /* @__PURE__ */ jsx("clipPath", { id: "loaf-clip", children: /* @__PURE__ */ jsx("rect", { x: "10", y: "10", width: "100", height: "140", rx: "8" }) }),
456
- /* @__PURE__ */ jsxs("g", { clipPath: "url(#loaf-clip)", children: [
457
- /* @__PURE__ */ jsx("rect", { x: "10", y: showDecrease ? targetFillY : baselineFillY, width: "100", height: showDecrease ? targetFillHeight : baselineFillHeight, fill: "rgba(220,175,120,0.7)" }),
458
- showIncrease ? /* @__PURE__ */ jsx("rect", { x: "10", y: targetFillY, width: "100", height: targetFillHeight - baselineFillHeight, fill: "rgba(14,203,129,0.35)" }) : null,
459
- showDecrease ? /* @__PURE__ */ jsx("rect", { x: "10", y: baselineFillY, width: "100", height: baselineFillHeight - targetFillHeight, fill: "rgba(246,70,93,0.35)" }) : null,
460
- /* @__PURE__ */ jsx("line", { x1: "12", y1: targetFillY, x2: "108", y2: targetFillY, stroke: showIncrease ? "#0ecb81" : showDecrease ? "#f6465d" : "rgba(234,217,162,1)", strokeWidth: "2", strokeLinecap: "round" })
594
+ ),
595
+ /* @__PURE__ */ jsxs("span", { className: "text-lg font-semibold text-white/60", children: [
596
+ "$",
597
+ fmt0(targetValue)
598
+ ] })
461
599
  ] })
462
- ] }) }),
600
+ ] }),
601
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
602
+ /* @__PURE__ */ jsx("div", { className: "flex h-[200px] flex-col justify-between text-xs text-white/40", children: percentMarkers.map((marker) => /* @__PURE__ */ jsxs("span", { children: [
603
+ marker,
604
+ "%"
605
+ ] }, marker)) }),
606
+ /* @__PURE__ */ jsx(
607
+ "div",
608
+ {
609
+ ref: houseRef,
610
+ className: "h-[200px] w-[160px] select-none touch-none",
611
+ style: { cursor: "ns-resize" },
612
+ onMouseDown,
613
+ onTouchStart,
614
+ children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 120 160", className: "h-full w-full overflow-visible", children: [
615
+ /* @__PURE__ */ jsx("rect", { x: "10", y: "10", width: "100", height: "140", rx: "8", fill: "rgba(255,255,255,0.04)", stroke: "rgba(255,255,255,0.10)" }),
616
+ /* @__PURE__ */ jsx("clipPath", { id: "loaf-clip", children: /* @__PURE__ */ jsx("rect", { x: "10", y: "10", width: "100", height: "140", rx: "8" }) }),
617
+ /* @__PURE__ */ jsxs("g", { clipPath: "url(#loaf-clip)", children: [
618
+ /* @__PURE__ */ jsx("rect", { x: "10", y: showDecrease ? targetFillY : baselineFillY, width: "100", height: showDecrease ? targetFillHeight : baselineFillHeight, fill: "rgba(220,175,120,0.7)" }),
619
+ showIncrease ? /* @__PURE__ */ jsx("rect", { x: "10", y: targetFillY, width: "100", height: targetFillHeight - baselineFillHeight, fill: "rgba(14,203,129,0.35)" }) : null,
620
+ showDecrease ? /* @__PURE__ */ jsx("rect", { x: "10", y: baselineFillY, width: "100", height: baselineFillHeight - targetFillHeight, fill: "rgba(246,70,93,0.35)" }) : null,
621
+ /* @__PURE__ */ jsx("line", { x1: "12", y1: targetFillY, x2: "108", y2: targetFillY, stroke: showIncrease ? "#0ecb81" : showDecrease ? "#f6465d" : "rgba(234,217,162,1)", strokeWidth: "2", strokeLinecap: "round" })
622
+ ] })
623
+ ] })
624
+ }
625
+ )
626
+ ] }),
463
627
  /* @__PURE__ */ jsx(
464
628
  "button",
465
629
  {
@@ -509,7 +673,7 @@ function HousePositionSlider({
509
673
  )
510
674
  ] })
511
675
  ] }),
512
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between py-2", children: [
676
+ orderType === "market" ? null : /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between py-2", children: [
513
677
  /* @__PURE__ */ jsx("span", { className: "text-white/50", children: isIncrease ? "Buying" : "Selling" }),
514
678
  /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
515
679
  orderMode === "buy" && buyTrackingMode === "dollars" ? /* @__PURE__ */ jsx("span", { className: "mr-1 text-[#0ecb81]", children: "~" }) : null,
@@ -558,7 +722,7 @@ function HousePositionSlider({
558
722
  "button",
559
723
  {
560
724
  type: "button",
561
- onClick: () => setOrderType("market"),
725
+ onClick: () => handleOrderTypeSelection("market"),
562
726
  className: cn(
563
727
  "flex-1 rounded-[6px] px-3 py-2 text-[0.8rem] font-medium transition",
564
728
  orderType === "market" ? "bg-[rgba(201,162,39,0.2)] text-[#C9A227]" : "text-white/50 hover:bg-white/5"
@@ -570,7 +734,7 @@ function HousePositionSlider({
570
734
  "button",
571
735
  {
572
736
  type: "button",
573
- onClick: () => setOrderType("limit"),
737
+ onClick: () => handleOrderTypeSelection("limit"),
574
738
  className: cn(
575
739
  "flex-1 rounded-[6px] px-3 py-2 text-[0.8rem] font-medium transition",
576
740
  orderType === "limit" ? "bg-[rgba(201,162,39,0.2)] text-[#C9A227]" : "text-white/50 hover:bg-white/5"
@@ -588,6 +752,7 @@ function HousePositionSlider({
588
752
  onChange: (e) => {
589
753
  const input = e.target.value;
590
754
  if (input === "" || /^[0-9]*\.?[0-9]*$/.test(input)) {
755
+ setLimitPriceDirty(true);
591
756
  setLimitPriceInput(input);
592
757
  const num = Number.parseFloat(input);
593
758
  if (Number.isFinite(num) && num > 0) setLimitPrice(num);