@loafmarkets/ui 0.0.5 → 0.0.7
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.d.mts +22 -17
- package/dist/index.d.ts +22 -17
- package/dist/index.js +544 -319
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +545 -320
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { cva } from 'class-variance-authority';
|
|
|
4
4
|
import { clsx } from 'clsx';
|
|
5
5
|
import { twMerge } from 'tailwind-merge';
|
|
6
6
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
|
-
import {
|
|
7
|
+
import { Newspaper, BedDouble, Bath, CarFront } from 'lucide-react';
|
|
8
8
|
import { createChart } from 'lightweight-charts';
|
|
9
9
|
|
|
10
10
|
// src/components/button.tsx
|
|
@@ -146,8 +146,8 @@ var PortfolioSummary = React5.forwardRef(
|
|
|
146
146
|
onResetAccount,
|
|
147
147
|
positionsHeading = "Current Positions",
|
|
148
148
|
emptyPositionsText = "No positions yet. Start trading to build your portfolio!",
|
|
149
|
-
formatCurrency = defaultFormatCurrency,
|
|
150
|
-
formatPercent = defaultFormatPercent,
|
|
149
|
+
formatCurrency: formatCurrency2 = defaultFormatCurrency,
|
|
150
|
+
formatPercent: formatPercent2 = defaultFormatPercent,
|
|
151
151
|
formatSignedCurrency = defaultFormatSignedCurrency,
|
|
152
152
|
className,
|
|
153
153
|
children,
|
|
@@ -184,15 +184,15 @@ var PortfolioSummary = React5.forwardRef(
|
|
|
184
184
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
185
185
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
186
186
|
/* @__PURE__ */ jsx("p", { className: "text-xs uppercase tracking-[0.5px] text-white/50", children: "Available Cash" }),
|
|
187
|
-
/* @__PURE__ */ jsx("p", { className: "mt-1 text-[1.25rem] font-semibold text-white", children:
|
|
187
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-[1.25rem] font-semibold text-white", children: formatCurrency2(availableCash) })
|
|
188
188
|
] }),
|
|
189
189
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
190
190
|
/* @__PURE__ */ jsx("p", { className: "text-xs uppercase tracking-[0.5px] text-white/50", children: "Portfolio Value" }),
|
|
191
|
-
/* @__PURE__ */ jsx("p", { className: "mt-1 text-[1.25rem] font-semibold text-white", children:
|
|
191
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-[1.25rem] font-semibold text-white", children: formatCurrency2(portfolioValue) })
|
|
192
192
|
] }),
|
|
193
193
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
194
194
|
/* @__PURE__ */ jsx("p", { className: "text-xs uppercase tracking-[0.5px] text-white/50", children: "Total Return" }),
|
|
195
|
-
/* @__PURE__ */ jsx("p", { className: cn("mt-1 text-[1.25rem] font-semibold", totalReturnClassName), children:
|
|
195
|
+
/* @__PURE__ */ jsx("p", { className: cn("mt-1 text-[1.25rem] font-semibold", totalReturnClassName), children: formatPercent2(totalReturnPercent) })
|
|
196
196
|
] }),
|
|
197
197
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
198
198
|
/* @__PURE__ */ jsx("p", { className: "text-xs uppercase tracking-[0.5px] text-white/50", children: "Unrealized P&L" }),
|
|
@@ -282,6 +282,7 @@ function HousePositionSlider({
|
|
|
282
282
|
pendingOrders = [],
|
|
283
283
|
defaultOrderType = "market",
|
|
284
284
|
orderbook,
|
|
285
|
+
ownershipPercentOverride,
|
|
285
286
|
onConfirmOrder,
|
|
286
287
|
className,
|
|
287
288
|
...props
|
|
@@ -292,6 +293,7 @@ function HousePositionSlider({
|
|
|
292
293
|
const [deltaTokensBuy, setDeltaTokensBuy] = React5.useState(0);
|
|
293
294
|
const [deltaTokensSell, setDeltaTokensSell] = React5.useState(0);
|
|
294
295
|
const [isDragging, setIsDragging] = React5.useState(false);
|
|
296
|
+
const [visualTargetPct, setVisualTargetPct] = React5.useState(null);
|
|
295
297
|
const [orderType, setOrderType] = React5.useState(defaultOrderType);
|
|
296
298
|
const [limitPrice, setLimitPrice] = React5.useState(currentPrice);
|
|
297
299
|
const [limitPriceInput, setLimitPriceInput] = React5.useState(currentPrice.toFixed(2));
|
|
@@ -313,9 +315,8 @@ function HousePositionSlider({
|
|
|
313
315
|
const effectiveAvailableCash = Math.max(0, availableCash - pendingBuyValue);
|
|
314
316
|
const effectiveTokensHeld = Math.max(0, tokensHeld - pendingSellTokens);
|
|
315
317
|
const holdingsValue = tokensHeld * effectivePrice;
|
|
316
|
-
const
|
|
317
|
-
const
|
|
318
|
-
const baselinePct = sliderTotalCapacity <= 0 ? 0 : sliderHoldingsValue / sliderTotalCapacity * 100;
|
|
318
|
+
const safeTotalTokens = totalTokens > 0 ? totalTokens : 1;
|
|
319
|
+
const baselineOwnershipActual = clamp(effectiveTokensHeld / safeTotalTokens * 100, 0, 100);
|
|
319
320
|
let deltaTokens = 0;
|
|
320
321
|
let deltaValue = 0;
|
|
321
322
|
let marketAvgPrice = null;
|
|
@@ -361,29 +362,21 @@ function HousePositionSlider({
|
|
|
361
362
|
}
|
|
362
363
|
targetTokens = tokensHeld + deltaTokens;
|
|
363
364
|
targetValue = targetTokens * effectivePrice;
|
|
364
|
-
const
|
|
365
|
-
|
|
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;
|
|
365
|
+
const sliderTargetTokens = clamp(effectiveTokensHeld + deltaTokens, 0, safeTotalTokens);
|
|
366
|
+
const normalizedTargetPct = sliderTargetTokens / safeTotalTokens * 100;
|
|
383
367
|
const isIncrease = orderMode === "buy";
|
|
384
368
|
const hasChange = orderMode !== "none" && (Math.abs(deltaTokens) > 1e-3 || Math.abs(deltaValue) > 0.01);
|
|
385
|
-
const currentOwnership = totalTokens <= 0 ? 0 : tokensHeld / totalTokens * 100;
|
|
386
|
-
const targetOwnership = totalTokens <= 0 ? 0 : targetTokens / totalTokens * 100;
|
|
369
|
+
const currentOwnership = totalTokens <= 0 ? 0 : clamp(tokensHeld / totalTokens * 100, 0, 100);
|
|
370
|
+
const targetOwnership = totalTokens <= 0 ? 0 : clamp(targetTokens / totalTokens * 100, 0, 100);
|
|
371
|
+
const ownershipOverrideValue = typeof ownershipPercentOverride === "number" && Number.isFinite(ownershipPercentOverride) ? clamp(ownershipPercentOverride, 0, 100) : null;
|
|
372
|
+
const ownershipShift = ownershipOverrideValue != null ? ownershipOverrideValue - baselineOwnershipActual : 0;
|
|
373
|
+
const baselinePct = clamp(ownershipOverrideValue ?? baselineOwnershipActual, 0, 100);
|
|
374
|
+
const impliedTargetPct = clamp(normalizedTargetPct + ownershipShift, 0, 100);
|
|
375
|
+
const displayTargetPct = visualTargetPct ?? impliedTargetPct;
|
|
376
|
+
const targetPct = displayTargetPct;
|
|
377
|
+
const displayCurrentOwnership = clamp(ownershipOverrideValue ?? currentOwnership, 0, 100);
|
|
378
|
+
const impliedDisplayTargetOwnership = clamp(targetOwnership + ownershipShift, 0, 100);
|
|
379
|
+
const displayTargetOwnership = visualTargetPct ?? impliedDisplayTargetOwnership;
|
|
387
380
|
const estFeeTokens = Math.abs(deltaValue) * 5e-3 / (effectivePrice || 1);
|
|
388
381
|
const resetOrder = React5.useCallback(() => {
|
|
389
382
|
setOrderMode("none");
|
|
@@ -391,6 +384,7 @@ function HousePositionSlider({
|
|
|
391
384
|
setDeltaDollars(0);
|
|
392
385
|
setDeltaTokensBuy(0);
|
|
393
386
|
setDeltaTokensSell(0);
|
|
387
|
+
setVisualTargetPct(null);
|
|
394
388
|
}, []);
|
|
395
389
|
const updateOrderFromTargetValue = React5.useCallback(
|
|
396
390
|
(newTargetValue) => {
|
|
@@ -421,6 +415,7 @@ function HousePositionSlider({
|
|
|
421
415
|
const nextOwnership = clamp(newOwnershipPercent, 0, 100);
|
|
422
416
|
const newTargetTokens = nextOwnership / 100 * totalTokens;
|
|
423
417
|
updateOrderFromTargetValue(newTargetTokens * effectivePrice);
|
|
418
|
+
setVisualTargetPct(nextOwnership);
|
|
424
419
|
},
|
|
425
420
|
[effectivePrice, totalTokens, updateOrderFromTargetValue]
|
|
426
421
|
);
|
|
@@ -453,12 +448,14 @@ function HousePositionSlider({
|
|
|
453
448
|
const magnitude = Math.min(Math.abs(normalized), 1);
|
|
454
449
|
if (magnitude < 0.02) {
|
|
455
450
|
resetOrder();
|
|
451
|
+
setVisualTargetPct(null);
|
|
456
452
|
return;
|
|
457
453
|
}
|
|
458
454
|
if (normalized > 0) {
|
|
459
455
|
const notional = clamp(magnitude * effectiveAvailableCash, 0, effectiveAvailableCash);
|
|
460
456
|
if (notional <= 0) {
|
|
461
457
|
resetOrder();
|
|
458
|
+
setVisualTargetPct(null);
|
|
462
459
|
return;
|
|
463
460
|
}
|
|
464
461
|
setOrderMode("buy");
|
|
@@ -466,12 +463,14 @@ function HousePositionSlider({
|
|
|
466
463
|
setDeltaDollars(notional);
|
|
467
464
|
setDeltaTokensBuy(0);
|
|
468
465
|
setDeltaTokensSell(0);
|
|
466
|
+
setVisualTargetPct(clamp(pct, 0, 100));
|
|
469
467
|
return;
|
|
470
468
|
}
|
|
471
469
|
if (normalized < 0) {
|
|
472
470
|
const tokensToSell = clamp(magnitude * effectiveTokensHeld, 0, effectiveTokensHeld);
|
|
473
471
|
if (tokensToSell <= 0) {
|
|
474
472
|
resetOrder();
|
|
473
|
+
setVisualTargetPct(null);
|
|
475
474
|
return;
|
|
476
475
|
}
|
|
477
476
|
setOrderMode("sell");
|
|
@@ -479,9 +478,11 @@ function HousePositionSlider({
|
|
|
479
478
|
setDeltaTokensSell(-tokensToSell);
|
|
480
479
|
setDeltaDollars(0);
|
|
481
480
|
setDeltaTokensBuy(0);
|
|
481
|
+
setVisualTargetPct(clamp(pct, 0, 100));
|
|
482
482
|
return;
|
|
483
483
|
}
|
|
484
484
|
resetOrder();
|
|
485
|
+
setVisualTargetPct(null);
|
|
485
486
|
},
|
|
486
487
|
[effectiveAvailableCash, effectiveTokensHeld, resetOrder]
|
|
487
488
|
);
|
|
@@ -644,19 +645,19 @@ function HousePositionSlider({
|
|
|
644
645
|
" Ownership"
|
|
645
646
|
] }),
|
|
646
647
|
/* @__PURE__ */ jsxs("span", { className: "text-white", children: [
|
|
647
|
-
|
|
648
|
+
displayCurrentOwnership.toFixed(2),
|
|
648
649
|
"%",
|
|
649
650
|
/* @__PURE__ */ jsx("span", { className: "mx-1.5 text-white/50", children: "\u2192" }),
|
|
650
651
|
/* @__PURE__ */ jsx(
|
|
651
652
|
"input",
|
|
652
653
|
{
|
|
653
654
|
type: "text",
|
|
654
|
-
value: ownershipInput ||
|
|
655
|
+
value: ownershipInput || displayTargetOwnership.toFixed(2),
|
|
655
656
|
onChange: (e) => {
|
|
656
657
|
const val = e.target.value;
|
|
657
658
|
if (val === "" || /^[0-9]*\.?[0-9]*$/.test(val)) setOwnershipInput(val);
|
|
658
659
|
},
|
|
659
|
-
onFocus: () => setOwnershipInput(
|
|
660
|
+
onFocus: () => setOwnershipInput(displayTargetOwnership.toFixed(2)),
|
|
660
661
|
onBlur: () => {
|
|
661
662
|
const num = Number.parseFloat(ownershipInput);
|
|
662
663
|
if (Number.isFinite(num)) updateOrderFromOwnership(num);
|
|
@@ -667,7 +668,7 @@ function HousePositionSlider({
|
|
|
667
668
|
},
|
|
668
669
|
className: cn(
|
|
669
670
|
"w-[70px] rounded-[4px] border bg-white/10 px-2 py-1 text-right font-semibold outline-none",
|
|
670
|
-
|
|
671
|
+
displayTargetOwnership >= displayCurrentOwnership ? "border-[rgba(14,203,129,0.3)] text-[#0ecb81] focus:border-[#0ecb81]" : "border-[rgba(246,70,93,0.3)] text-[#f6465d] focus:border-[#f6465d]"
|
|
671
672
|
)
|
|
672
673
|
}
|
|
673
674
|
)
|
|
@@ -818,6 +819,9 @@ function DepthRow({
|
|
|
818
819
|
] });
|
|
819
820
|
}
|
|
820
821
|
var clamp2 = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
822
|
+
var LEVEL_ROWS_VISIBLE = 6;
|
|
823
|
+
var DEPTH_ROW_HEIGHT_PX = 34;
|
|
824
|
+
var SECTION_HEIGHT = LEVEL_ROWS_VISIBLE * DEPTH_ROW_HEIGHT_PX;
|
|
821
825
|
var Orderbook = React5.forwardRef(
|
|
822
826
|
({
|
|
823
827
|
asks,
|
|
@@ -958,18 +962,25 @@ var Orderbook = React5.forwardRef(
|
|
|
958
962
|
] }, `${trade.type}-${trade.price}-${trade.amount}-${trade.time ?? i}`)) })
|
|
959
963
|
}
|
|
960
964
|
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
961
|
-
/* @__PURE__ */ jsx(
|
|
962
|
-
|
|
965
|
+
/* @__PURE__ */ jsx(
|
|
966
|
+
"div",
|
|
963
967
|
{
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
968
|
+
className: "divide-y divide-white/5 overflow-y-auto",
|
|
969
|
+
style: { height: `${SECTION_HEIGHT}px`, scrollbarGutter: "stable" },
|
|
970
|
+
children: asks.map((l, idx) => /* @__PURE__ */ jsx(
|
|
971
|
+
DepthRow,
|
|
972
|
+
{
|
|
973
|
+
side: "ask",
|
|
974
|
+
price: l.price,
|
|
975
|
+
amount: l.amount,
|
|
976
|
+
depthPct: (l.depth ?? l.amount) / maxAskDepth * 100,
|
|
977
|
+
precision,
|
|
978
|
+
amountPrecision
|
|
979
|
+
},
|
|
980
|
+
`ask-${idx}-${l.price}-${l.amount}`
|
|
981
|
+
))
|
|
982
|
+
}
|
|
983
|
+
),
|
|
973
984
|
/* @__PURE__ */ jsxs("div", { className: "mt-2 grid grid-cols-2 items-center gap-3 bg-[#0b1a24] px-3 py-2", children: [
|
|
974
985
|
/* @__PURE__ */ jsxs("div", { className: cn("text-lg font-semibold tabular-nums", midClass), children: [
|
|
975
986
|
"$",
|
|
@@ -982,18 +993,25 @@ var Orderbook = React5.forwardRef(
|
|
|
982
993
|
] }),
|
|
983
994
|
/* @__PURE__ */ jsx("div", {})
|
|
984
995
|
] }),
|
|
985
|
-
/* @__PURE__ */ jsx(
|
|
986
|
-
|
|
996
|
+
/* @__PURE__ */ jsx(
|
|
997
|
+
"div",
|
|
987
998
|
{
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
999
|
+
className: "divide-y divide-white/5 overflow-y-auto",
|
|
1000
|
+
style: { height: `${SECTION_HEIGHT}px`, scrollbarGutter: "stable" },
|
|
1001
|
+
children: bids.map((l, idx) => /* @__PURE__ */ jsx(
|
|
1002
|
+
DepthRow,
|
|
1003
|
+
{
|
|
1004
|
+
side: "bid",
|
|
1005
|
+
price: l.price,
|
|
1006
|
+
amount: l.amount,
|
|
1007
|
+
depthPct: (l.depth ?? l.amount) / maxBidDepth * 100,
|
|
1008
|
+
precision,
|
|
1009
|
+
amountPrecision
|
|
1010
|
+
},
|
|
1011
|
+
`bid-${idx}-${l.price}-${l.amount}`
|
|
1012
|
+
))
|
|
1013
|
+
}
|
|
1014
|
+
)
|
|
997
1015
|
] })
|
|
998
1016
|
] })
|
|
999
1017
|
]
|
|
@@ -1095,132 +1113,208 @@ var PropertyTour = React5.forwardRef(
|
|
|
1095
1113
|
}
|
|
1096
1114
|
);
|
|
1097
1115
|
PropertyTour.displayName = "PropertyTour";
|
|
1098
|
-
var
|
|
1116
|
+
var ITEMS_PER_PAGE = 4;
|
|
1117
|
+
var ROW_HEIGHT_PX = 86;
|
|
1118
|
+
var ensureAnimationsInjected = () => {
|
|
1119
|
+
if (typeof document === "undefined") return;
|
|
1120
|
+
if (document.getElementById("property-news-updates-animations")) return;
|
|
1121
|
+
const style = document.createElement("style");
|
|
1122
|
+
style.id = "property-news-updates-animations";
|
|
1123
|
+
style.textContent = `
|
|
1124
|
+
@keyframes propertyNewsPulse {
|
|
1125
|
+
0% { opacity: 0.6; transform: scale(0.9); }
|
|
1126
|
+
50% { opacity: 1; transform: scale(1); }
|
|
1127
|
+
100% { opacity: 0.6; transform: scale(0.9); }
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
@keyframes propertyNewsSlideIn {
|
|
1131
|
+
from { transform: translateY(-6px); opacity: 0; }
|
|
1132
|
+
to { transform: translateY(0); opacity: 1; }
|
|
1133
|
+
}
|
|
1134
|
+
`;
|
|
1135
|
+
document.head.appendChild(style);
|
|
1136
|
+
};
|
|
1137
|
+
var categoryStyles = {
|
|
1099
1138
|
property: {
|
|
1100
1139
|
label: "Property Update",
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
pillColor: "#f8d12f",
|
|
1105
|
-
icon: Building2
|
|
1140
|
+
backgroundColor: "rgba(14, 203, 129, 0.15)",
|
|
1141
|
+
borderColor: "rgba(14, 203, 129, 0.45)",
|
|
1142
|
+
color: "#0ecb81"
|
|
1106
1143
|
},
|
|
1107
1144
|
market: {
|
|
1108
|
-
label: "Market
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
pillColor: "#0ecb81",
|
|
1113
|
-
icon: LineChart
|
|
1114
|
-
},
|
|
1115
|
-
portfolio: {
|
|
1116
|
-
label: "Portfolio Signal",
|
|
1117
|
-
accent: "#6c8cff",
|
|
1118
|
-
pillBg: "rgba(108, 140, 255, 0.12)",
|
|
1119
|
-
pillBorder: "rgba(108, 140, 255, 0.45)",
|
|
1120
|
-
pillColor: "#c9d5ff",
|
|
1121
|
-
icon: Sparkles
|
|
1145
|
+
label: "Market News",
|
|
1146
|
+
backgroundColor: "rgba(248, 209, 47, 0.15)",
|
|
1147
|
+
borderColor: "rgba(248, 209, 47, 0.45)",
|
|
1148
|
+
color: "#f8d12f"
|
|
1122
1149
|
}
|
|
1123
1150
|
};
|
|
1124
|
-
var getTypeMeta = (type) => {
|
|
1125
|
-
if (!type) return TYPE_META.property;
|
|
1126
|
-
const key = type;
|
|
1127
|
-
if (key in TYPE_META) return TYPE_META[key];
|
|
1128
|
-
return {
|
|
1129
|
-
label: type.toString(),
|
|
1130
|
-
accent: "#f5f5f5",
|
|
1131
|
-
pillBg: "rgba(255,255,255,0.08)",
|
|
1132
|
-
pillBorder: "rgba(255,255,255,0.25)",
|
|
1133
|
-
pillColor: "#ffffff",
|
|
1134
|
-
icon: Newspaper
|
|
1135
|
-
};
|
|
1136
|
-
};
|
|
1137
1151
|
var formatDate = (value) => {
|
|
1138
|
-
|
|
1139
|
-
if (Number.isNaN(
|
|
1140
|
-
return
|
|
1152
|
+
if (typeof value === "string") return value;
|
|
1153
|
+
if (!(value instanceof Date) || Number.isNaN(value.getTime())) return "";
|
|
1154
|
+
return value.toLocaleDateString(void 0, {
|
|
1141
1155
|
month: "short",
|
|
1142
1156
|
day: "numeric",
|
|
1143
1157
|
year: "numeric"
|
|
1144
1158
|
});
|
|
1145
1159
|
};
|
|
1146
1160
|
var PropertyNewsUpdates = React5.forwardRef(
|
|
1147
|
-
({
|
|
1161
|
+
({
|
|
1162
|
+
className,
|
|
1163
|
+
heading = "Property News & Headlines",
|
|
1164
|
+
subheading,
|
|
1165
|
+
items,
|
|
1166
|
+
emptyState,
|
|
1167
|
+
highlightFirst = true,
|
|
1168
|
+
...props
|
|
1169
|
+
}, ref) => {
|
|
1170
|
+
const [page, setPage] = React5.useState(0);
|
|
1171
|
+
React5.useEffect(() => {
|
|
1172
|
+
ensureAnimationsInjected();
|
|
1173
|
+
}, []);
|
|
1148
1174
|
const hasItems = Array.isArray(items) && items.length > 0;
|
|
1175
|
+
const totalPages = React5.useMemo(() => hasItems ? Math.max(1, Math.ceil(items.length / ITEMS_PER_PAGE)) : 1, [hasItems, items.length]);
|
|
1176
|
+
React5.useEffect(() => {
|
|
1177
|
+
setPage((prev) => Math.min(prev, totalPages - 1));
|
|
1178
|
+
}, [totalPages]);
|
|
1179
|
+
const paginatedItems = hasItems ? items.slice(page * ITEMS_PER_PAGE, page * ITEMS_PER_PAGE + ITEMS_PER_PAGE) : [];
|
|
1149
1180
|
return /* @__PURE__ */ jsxs(
|
|
1150
1181
|
"div",
|
|
1151
1182
|
{
|
|
1152
1183
|
ref,
|
|
1153
1184
|
className: cn(
|
|
1154
|
-
"w-full max-w-[
|
|
1185
|
+
"w-full max-w-[840px] rounded-2xl border border-white/10 bg-[#0b0e10] p-5 text-white shadow-[0_20px_40px_rgba(0,0,0,0.35)]",
|
|
1155
1186
|
className
|
|
1156
1187
|
),
|
|
1157
1188
|
...props,
|
|
1158
1189
|
children: [
|
|
1159
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
1160
|
-
/* @__PURE__ */
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
const Icon = meta.icon;
|
|
1166
|
-
const key = item.id ?? `${item.title}-${index}`;
|
|
1167
|
-
const content = /* @__PURE__ */ jsxs(
|
|
1190
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1191
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1192
|
+
/* @__PURE__ */ jsx("p", { className: "text-lg font-semibold text-white", children: heading }),
|
|
1193
|
+
subheading ? /* @__PURE__ */ jsx("p", { className: "text-sm text-white/60", children: subheading }) : null
|
|
1194
|
+
] }),
|
|
1195
|
+
/* @__PURE__ */ jsxs(
|
|
1168
1196
|
"div",
|
|
1169
1197
|
{
|
|
1170
|
-
className: "
|
|
1198
|
+
className: "inline-flex items-center font-semibold uppercase text-emerald-300",
|
|
1199
|
+
style: { gap: "0.35rem", fontSize: "0.8rem", letterSpacing: "0.15em" },
|
|
1171
1200
|
children: [
|
|
1172
|
-
/* @__PURE__ */
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
{
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
children: /* @__PURE__ */ jsx(Icon, { className: "h-5 w-5" })
|
|
1183
|
-
}
|
|
1184
|
-
),
|
|
1185
|
-
index !== items.length - 1 ? /* @__PURE__ */ jsx("div", { className: "mt-2 h-full w-px flex-1 bg-white/10" }) : null
|
|
1186
|
-
] }),
|
|
1187
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col", children: [
|
|
1188
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
|
|
1189
|
-
/* @__PURE__ */ jsx("span", { className: "text-[0.7rem] font-semibold uppercase tracking-[0.3em]", style: { color: meta.accent }, children: meta.label }),
|
|
1190
|
-
/* @__PURE__ */ jsxs("span", { className: "flex items-center text-xs text-white/60", children: [
|
|
1191
|
-
/* @__PURE__ */ jsx(CalendarDays, { className: "mr-1 h-3.5 w-3.5" }),
|
|
1192
|
-
formatDate(item.date)
|
|
1193
|
-
] })
|
|
1194
|
-
] }),
|
|
1195
|
-
/* @__PURE__ */ jsx("p", { className: "mt-2 text-base font-semibold leading-snug", children: item.title }),
|
|
1196
|
-
item.description ? /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-white/65", children: item.description }) : null,
|
|
1197
|
-
(item.href || item.onAction) && item.actionLabel ? /* @__PURE__ */ jsxs(
|
|
1198
|
-
"button",
|
|
1199
|
-
{
|
|
1200
|
-
type: "button",
|
|
1201
|
-
className: "group mt-3 inline-flex items-center gap-1 text-sm font-semibold text-white transition hover:text-white/80",
|
|
1202
|
-
onClick: () => {
|
|
1203
|
-
if (item.href) {
|
|
1204
|
-
window?.open?.(item.href, "_blank", "noopener,noreferrer");
|
|
1205
|
-
}
|
|
1206
|
-
item.onAction?.(item);
|
|
1207
|
-
},
|
|
1208
|
-
children: [
|
|
1209
|
-
item.actionLabel,
|
|
1210
|
-
/* @__PURE__ */ jsx(ArrowUpRight, { className: "h-4 w-4 transition group-hover:translate-x-0.5 group-hover:-translate-y-0.5" })
|
|
1211
|
-
]
|
|
1201
|
+
/* @__PURE__ */ jsx(
|
|
1202
|
+
"span",
|
|
1203
|
+
{
|
|
1204
|
+
style: {
|
|
1205
|
+
display: "inline-block",
|
|
1206
|
+
width: "6px",
|
|
1207
|
+
height: "6px",
|
|
1208
|
+
borderRadius: "50%",
|
|
1209
|
+
backgroundColor: "#34d399",
|
|
1210
|
+
animation: "propertyNewsPulse 2s infinite"
|
|
1212
1211
|
}
|
|
1213
|
-
|
|
1214
|
-
|
|
1212
|
+
}
|
|
1213
|
+
),
|
|
1214
|
+
"LIVE"
|
|
1215
1215
|
]
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1216
|
+
}
|
|
1217
|
+
)
|
|
1218
|
+
] }),
|
|
1219
|
+
/* @__PURE__ */ jsx(
|
|
1220
|
+
"div",
|
|
1221
|
+
{
|
|
1222
|
+
className: "mt-4 flex flex-col gap-3",
|
|
1223
|
+
style: { minHeight: `${ITEMS_PER_PAGE * ROW_HEIGHT_PX}px` },
|
|
1224
|
+
children: hasItems ? paginatedItems.map((item, index) => {
|
|
1225
|
+
const absoluteIndex = page * ITEMS_PER_PAGE + index;
|
|
1226
|
+
const key = item.displayId ?? item.id ?? `${item.title}-${absoluteIndex}`;
|
|
1227
|
+
const styles = categoryStyles[item.type] ?? categoryStyles.market;
|
|
1228
|
+
const dateLabel = item.isNew ?? (highlightFirst && absoluteIndex === 0) ? "Just now" : typeof item.date === "string" && item.date.trim().length > 0 ? item.date : formatDate(item.date);
|
|
1229
|
+
const isHighlighted = item.isNew ?? (highlightFirst && absoluteIndex === 0);
|
|
1230
|
+
return /* @__PURE__ */ jsxs(
|
|
1231
|
+
"div",
|
|
1232
|
+
{
|
|
1233
|
+
style: {
|
|
1234
|
+
padding: "0.75rem",
|
|
1235
|
+
borderRadius: "6px",
|
|
1236
|
+
backgroundColor: isHighlighted ? "rgba(14, 203, 129, 0.1)" : "transparent",
|
|
1237
|
+
border: "1px solid transparent",
|
|
1238
|
+
transition: "background-color 0.2s",
|
|
1239
|
+
animation: item.isNew ? "propertyNewsSlideIn 0.5s ease-out" : void 0
|
|
1240
|
+
},
|
|
1241
|
+
children: [
|
|
1242
|
+
/* @__PURE__ */ jsx("p", { style: { fontSize: "0.9375rem", fontWeight: 500, marginBottom: "0.35rem" }, children: item.title }),
|
|
1243
|
+
/* @__PURE__ */ jsxs(
|
|
1244
|
+
"div",
|
|
1245
|
+
{
|
|
1246
|
+
style: {
|
|
1247
|
+
display: "flex",
|
|
1248
|
+
justifyContent: "space-between",
|
|
1249
|
+
alignItems: "center",
|
|
1250
|
+
fontSize: "0.75rem",
|
|
1251
|
+
color: "#b5b8c5"
|
|
1252
|
+
},
|
|
1253
|
+
children: [
|
|
1254
|
+
/* @__PURE__ */ jsx("span", { style: { color: isHighlighted ? "#0ecb81" : "inherit" }, children: dateLabel }),
|
|
1255
|
+
/* @__PURE__ */ jsx(
|
|
1256
|
+
"span",
|
|
1257
|
+
{
|
|
1258
|
+
style: {
|
|
1259
|
+
padding: "0.25rem 0.6rem",
|
|
1260
|
+
borderRadius: "4px",
|
|
1261
|
+
border: `1px solid ${styles.borderColor}`,
|
|
1262
|
+
backgroundColor: styles.backgroundColor,
|
|
1263
|
+
color: styles.color,
|
|
1264
|
+
fontSize: "0.68rem",
|
|
1265
|
+
fontWeight: 600,
|
|
1266
|
+
textTransform: "uppercase"
|
|
1267
|
+
},
|
|
1268
|
+
children: item.type === "property" ? styles.label : "Market News"
|
|
1269
|
+
}
|
|
1270
|
+
)
|
|
1271
|
+
]
|
|
1272
|
+
}
|
|
1273
|
+
)
|
|
1274
|
+
]
|
|
1275
|
+
},
|
|
1276
|
+
key
|
|
1277
|
+
);
|
|
1278
|
+
}) : emptyState ?? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center rounded-2xl border border-dashed border-white/20 px-6 py-10 text-center text-sm text-white/60", children: [
|
|
1279
|
+
/* @__PURE__ */ jsx(Newspaper, { className: "mb-3 h-8 w-8 text-white/40" }),
|
|
1280
|
+
"No property news yet. Updates will land here as soon as we receive new intelligence."
|
|
1281
|
+
] })
|
|
1282
|
+
}
|
|
1283
|
+
),
|
|
1284
|
+
hasItems && totalPages > 1 ? /* @__PURE__ */ jsxs("div", { className: "mt-4 flex items-center justify-between text-xs text-white/60", children: [
|
|
1285
|
+
/* @__PURE__ */ jsx(
|
|
1286
|
+
"button",
|
|
1287
|
+
{
|
|
1288
|
+
type: "button",
|
|
1289
|
+
onClick: () => setPage((prev) => Math.max(0, prev - 1)),
|
|
1290
|
+
disabled: page === 0,
|
|
1291
|
+
className: cn(
|
|
1292
|
+
"rounded-full border border-white/15 px-3 py-1 uppercase tracking-[0.2em]",
|
|
1293
|
+
page === 0 ? "opacity-40 cursor-not-allowed" : "hover:border-white/40"
|
|
1294
|
+
),
|
|
1295
|
+
children: "Prev"
|
|
1296
|
+
}
|
|
1297
|
+
),
|
|
1298
|
+
/* @__PURE__ */ jsxs("span", { className: "font-medium text-white/70", children: [
|
|
1299
|
+
"Page ",
|
|
1300
|
+
page + 1,
|
|
1301
|
+
" / ",
|
|
1302
|
+
totalPages
|
|
1303
|
+
] }),
|
|
1304
|
+
/* @__PURE__ */ jsx(
|
|
1305
|
+
"button",
|
|
1306
|
+
{
|
|
1307
|
+
type: "button",
|
|
1308
|
+
onClick: () => setPage((prev) => Math.min(totalPages - 1, prev + 1)),
|
|
1309
|
+
disabled: page >= totalPages - 1,
|
|
1310
|
+
className: cn(
|
|
1311
|
+
"rounded-full border border-white/15 px-3 py-1 uppercase tracking-[0.2em]",
|
|
1312
|
+
page >= totalPages - 1 ? "opacity-40 cursor-not-allowed" : "hover:border-white/40"
|
|
1313
|
+
),
|
|
1314
|
+
children: "Next"
|
|
1315
|
+
}
|
|
1316
|
+
)
|
|
1317
|
+
] }) : null
|
|
1224
1318
|
]
|
|
1225
1319
|
}
|
|
1226
1320
|
);
|
|
@@ -1295,12 +1389,13 @@ var PropertyCompareBar = React5.forwardRef(
|
|
|
1295
1389
|
{
|
|
1296
1390
|
ref,
|
|
1297
1391
|
className: cn(
|
|
1298
|
-
"flex w-full flex-col gap-3
|
|
1392
|
+
"flex w-full flex-col gap-3 border border-white/10 px-4 py-3 text-white shadow-[0_18px_40px_rgba(0,0,0,0.55)] md:flex-row md:items-center md:justify-between md:gap-4",
|
|
1299
1393
|
className
|
|
1300
1394
|
),
|
|
1395
|
+
style: { borderRadius: "16px" },
|
|
1301
1396
|
...props,
|
|
1302
1397
|
children: [
|
|
1303
|
-
/* @__PURE__ */ jsxs("div", { className: "relative
|
|
1398
|
+
/* @__PURE__ */ jsxs("div", { className: "relative w-auto", ref: dropdownRef, children: [
|
|
1304
1399
|
/* @__PURE__ */ jsxs(
|
|
1305
1400
|
"button",
|
|
1306
1401
|
{
|
|
@@ -1308,7 +1403,7 @@ var PropertyCompareBar = React5.forwardRef(
|
|
|
1308
1403
|
disabled: !hasAddresses,
|
|
1309
1404
|
onClick: () => setIsDropdownOpen((prev) => !prev),
|
|
1310
1405
|
className: cn(
|
|
1311
|
-
"flex h-[42px] w-
|
|
1406
|
+
"flex h-[42px] w-auto items-center gap-2 rounded-[12px] border border-transparent bg-transparent px-0 text-left text-[15px] font-semibold text-white transition hover:text-white/80 focus-visible:outline-none",
|
|
1312
1407
|
!hasAddresses && "text-white/40"
|
|
1313
1408
|
),
|
|
1314
1409
|
children: [
|
|
@@ -1327,7 +1422,7 @@ var PropertyCompareBar = React5.forwardRef(
|
|
|
1327
1422
|
]
|
|
1328
1423
|
}
|
|
1329
1424
|
),
|
|
1330
|
-
isDropdownOpen && hasAddresses ? /* @__PURE__ */ jsx("div", { className: "absolute left-0 top-[calc(100%+8px)] z-20 w-full rounded-[12px] border border-
|
|
1425
|
+
isDropdownOpen && hasAddresses ? /* @__PURE__ */ jsx("div", { className: "absolute left-0 top-[calc(100%+8px)] z-20 w-full rounded-[12px] border border-white/10 py-1 shadow-[0_25px_55px_rgba(0,0,0,0.6)] bg-black", children: normalizedAddresses.map((option) => {
|
|
1331
1426
|
const active = option.id === resolvedSelectedId;
|
|
1332
1427
|
return /* @__PURE__ */ jsx(
|
|
1333
1428
|
"button",
|
|
@@ -1349,7 +1444,7 @@ var PropertyCompareBar = React5.forwardRef(
|
|
|
1349
1444
|
{
|
|
1350
1445
|
variant: "accentOutline",
|
|
1351
1446
|
size: "sm",
|
|
1352
|
-
className: "flex
|
|
1447
|
+
className: "flex items-center justify-center gap-2 rounded-[10px] border-[var(--color-accent,#e6c87e)] bg-transparent px-4 py-2 text-[14px] font-semibold text-[var(--color-accent,#e6c87e)] transition hover:bg-[rgba(230,200,126,0.08)] md:ml-auto",
|
|
1353
1448
|
onClick: onCompareClick,
|
|
1354
1449
|
disabled: !hasAddresses,
|
|
1355
1450
|
children: [
|
|
@@ -1364,152 +1459,263 @@ var PropertyCompareBar = React5.forwardRef(
|
|
|
1364
1459
|
}
|
|
1365
1460
|
);
|
|
1366
1461
|
PropertyCompareBar.displayName = "PropertyCompareBar";
|
|
1367
|
-
var
|
|
1368
|
-
|
|
1369
|
-
"
|
|
1370
|
-
{
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1462
|
+
var TABS = [
|
|
1463
|
+
{ id: "portfolio", label: "Portfolio" },
|
|
1464
|
+
{ id: "openOrders", label: "Open Orders" },
|
|
1465
|
+
{ id: "tradeHistory", label: "Trade History" },
|
|
1466
|
+
{ id: "orderHistory", label: "Order History" }
|
|
1467
|
+
];
|
|
1468
|
+
var panelStyle = {
|
|
1469
|
+
width: "100%",
|
|
1470
|
+
borderRadius: "16px",
|
|
1471
|
+
backgroundColor: "#111111",
|
|
1472
|
+
border: "1px solid rgba(255,255,255,0.08)",
|
|
1473
|
+
color: "#ffffff",
|
|
1474
|
+
display: "flex",
|
|
1475
|
+
flexDirection: "column"
|
|
1476
|
+
};
|
|
1477
|
+
var headerStyle = {
|
|
1478
|
+
padding: "1.25rem 1.5rem 0.5rem"
|
|
1479
|
+
};
|
|
1480
|
+
var titleStyle = {
|
|
1481
|
+
margin: 0,
|
|
1482
|
+
fontSize: "1.1rem",
|
|
1483
|
+
fontWeight: 600
|
|
1484
|
+
};
|
|
1485
|
+
var tabsRowStyle = {
|
|
1486
|
+
display: "flex",
|
|
1487
|
+
gap: "0.5rem",
|
|
1488
|
+
padding: "0 1.5rem",
|
|
1489
|
+
borderBottom: "1px solid rgba(255,255,255,0.08)"
|
|
1490
|
+
};
|
|
1491
|
+
var tabButtonBase = {
|
|
1492
|
+
background: "none",
|
|
1493
|
+
border: "none",
|
|
1494
|
+
color: "rgba(255,255,255,0.5)",
|
|
1495
|
+
fontSize: "0.9rem",
|
|
1496
|
+
fontWeight: 400,
|
|
1497
|
+
padding: "0.75rem 1rem",
|
|
1498
|
+
cursor: "pointer",
|
|
1499
|
+
position: "relative",
|
|
1500
|
+
transition: "color 0.2s ease"
|
|
1501
|
+
};
|
|
1502
|
+
var tabContentWrapper = {
|
|
1503
|
+
padding: "1.25rem 1.5rem 1.5rem",
|
|
1504
|
+
display: "flex",
|
|
1505
|
+
flexDirection: "column",
|
|
1506
|
+
gap: "0.75rem",
|
|
1507
|
+
flex: 1
|
|
1508
|
+
};
|
|
1509
|
+
var tableHeaderStyle = {
|
|
1510
|
+
display: "grid",
|
|
1511
|
+
gridTemplateColumns: "1.8fr 0.9fr 0.7fr 0.8fr 0.8fr 1fr",
|
|
1512
|
+
gap: "0.5rem",
|
|
1513
|
+
paddingBottom: "0.75rem",
|
|
1514
|
+
borderBottom: "1px solid rgba(255,255,255,0.1)",
|
|
1515
|
+
minWidth: "700px"
|
|
1516
|
+
};
|
|
1517
|
+
var tableHeaderCell = {
|
|
1518
|
+
fontSize: "0.7rem",
|
|
1519
|
+
fontWeight: 700,
|
|
1520
|
+
color: "#ffffff",
|
|
1521
|
+
textTransform: "uppercase",
|
|
1522
|
+
letterSpacing: "0.3px"
|
|
1523
|
+
};
|
|
1524
|
+
var emptyStateStyle = {
|
|
1525
|
+
flex: 1,
|
|
1526
|
+
display: "flex",
|
|
1527
|
+
alignItems: "center",
|
|
1528
|
+
justifyContent: "center",
|
|
1529
|
+
textAlign: "center",
|
|
1530
|
+
color: "rgba(255,255,255,0.6)",
|
|
1531
|
+
fontSize: "0.95rem",
|
|
1532
|
+
border: "1px dashed rgba(255,255,255,0.12)",
|
|
1533
|
+
borderRadius: "8px",
|
|
1534
|
+
minHeight: "220px"
|
|
1535
|
+
};
|
|
1536
|
+
var rowStyle = {
|
|
1537
|
+
display: "grid",
|
|
1538
|
+
gridTemplateColumns: "1.8fr 0.9fr 0.7fr 0.8fr 0.8fr 1fr",
|
|
1539
|
+
gap: "0.5rem",
|
|
1540
|
+
alignItems: "center",
|
|
1541
|
+
minWidth: "700px",
|
|
1542
|
+
padding: "0.75rem 0",
|
|
1543
|
+
borderBottom: "1px solid rgba(255,255,255,0.08)"
|
|
1544
|
+
};
|
|
1545
|
+
var formatCurrency = (value) => {
|
|
1546
|
+
if (value == null || Number.isNaN(value)) return "\u2014";
|
|
1547
|
+
return `$${value.toLocaleString(void 0, {
|
|
1548
|
+
minimumFractionDigits: 2,
|
|
1549
|
+
maximumFractionDigits: 2
|
|
1550
|
+
})}`;
|
|
1551
|
+
};
|
|
1552
|
+
var formatPercent = (value, fractionDigits = 2) => {
|
|
1553
|
+
if (value == null || Number.isNaN(value)) return "\u2014";
|
|
1554
|
+
return `${value.toFixed(fractionDigits)}%`;
|
|
1555
|
+
};
|
|
1399
1556
|
var YourOrders = React5.forwardRef(
|
|
1400
|
-
({
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
)
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
"button",
|
|
1453
|
-
{
|
|
1454
|
-
type: "button",
|
|
1455
|
-
onClick: canEditPrice ? () => onEditPrice?.(order) : void 0,
|
|
1456
|
-
className: cn(
|
|
1457
|
-
"ml-1 inline-flex items-center justify-center text-white/60 transition-colors",
|
|
1458
|
-
canEditPrice ? "cursor-pointer hover:text-[#C9A227] active:translate-y-px" : "cursor-default"
|
|
1459
|
-
),
|
|
1460
|
-
"aria-label": "Edit price",
|
|
1461
|
-
children: /* @__PURE__ */ jsx(EditIcon, { className: "h-[14px] w-[14px]" })
|
|
1462
|
-
}
|
|
1463
|
-
)
|
|
1464
|
-
] }),
|
|
1465
|
-
/* @__PURE__ */ jsxs("div", { className: "px-2 font-medium", children: [
|
|
1466
|
-
/* @__PURE__ */ jsx("span", { className: "tabular-nums", children: order.amount.toLocaleString() }),
|
|
1467
|
-
/* @__PURE__ */ jsx(
|
|
1468
|
-
"button",
|
|
1557
|
+
({ className, title = "Portfolio Holdings (Demo)", orders, ...props }, ref) => {
|
|
1558
|
+
const [activeTab, setActiveTab] = React5.useState("portfolio");
|
|
1559
|
+
const hasOrders = Array.isArray(orders) && orders.length > 0;
|
|
1560
|
+
const renderPortfolio = () => {
|
|
1561
|
+
if (!hasOrders) {
|
|
1562
|
+
return /* @__PURE__ */ jsx("div", { style: emptyStateStyle, children: "No holdings yet. Start trading to build your portfolio." });
|
|
1563
|
+
}
|
|
1564
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1565
|
+
/* @__PURE__ */ jsxs("div", { style: tableHeaderStyle, children: [
|
|
1566
|
+
/* @__PURE__ */ jsx("div", { style: tableHeaderCell, children: "Property" }),
|
|
1567
|
+
/* @__PURE__ */ jsx("div", { style: tableHeaderCell, children: "Value" }),
|
|
1568
|
+
/* @__PURE__ */ jsx("div", { style: tableHeaderCell, children: "Holding" }),
|
|
1569
|
+
/* @__PURE__ */ jsx("div", { style: tableHeaderCell, children: "% of Property" }),
|
|
1570
|
+
/* @__PURE__ */ jsx("div", { style: tableHeaderCell, children: "Avg Price" }),
|
|
1571
|
+
/* @__PURE__ */ jsx("div", { style: tableHeaderCell, children: "P&L" })
|
|
1572
|
+
] }),
|
|
1573
|
+
/* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: "0.65rem" }, children: orders.map((order) => {
|
|
1574
|
+
const propertyName = order.asset;
|
|
1575
|
+
const holding = order.holdingLabel ?? `${order.amount.toLocaleString(void 0, {
|
|
1576
|
+
minimumFractionDigits: 0,
|
|
1577
|
+
maximumFractionDigits: 2
|
|
1578
|
+
})} ${propertyName}`;
|
|
1579
|
+
const value = order.value ?? order.total;
|
|
1580
|
+
const portfolioShare = order.portfolioSharePercent != null ? `${order.portfolioSharePercent.toFixed(1)}% of portfolio` : void 0;
|
|
1581
|
+
const propertyPercent = order.propertyPercent;
|
|
1582
|
+
const avgPrice = order.avgEntryPrice ?? order.price;
|
|
1583
|
+
const pnlValue = order.pnlValue;
|
|
1584
|
+
const pnlPercent = order.pnlPercent;
|
|
1585
|
+
const pnlPositive = pnlValue != null ? pnlValue >= 0 : (pnlPercent ?? 0) >= 0;
|
|
1586
|
+
const currentPrice = order.currentPrice ?? order.price;
|
|
1587
|
+
const priceChangePercent = order.priceChangePercent ?? order.pnlPercent;
|
|
1588
|
+
const priceChangePositive = order.priceChangePositive ?? (priceChangePercent != null ? priceChangePercent >= 0 : void 0);
|
|
1589
|
+
return /* @__PURE__ */ jsxs("div", { style: rowStyle, children: [
|
|
1590
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "1rem", alignItems: "center" }, children: [
|
|
1591
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.15rem" }, children: [
|
|
1592
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.9rem", fontWeight: 500 }, children: propertyName }),
|
|
1593
|
+
portfolioShare ? /* @__PURE__ */ jsx("div", { style: { fontSize: "0.72rem", color: "rgba(255,255,255,0.5)" }, children: portfolioShare }) : null
|
|
1594
|
+
] }),
|
|
1595
|
+
currentPrice != null ? /* @__PURE__ */ jsxs("div", { children: [
|
|
1596
|
+
/* @__PURE__ */ jsxs(
|
|
1597
|
+
"div",
|
|
1598
|
+
{
|
|
1599
|
+
style: {
|
|
1600
|
+
fontSize: "0.9rem",
|
|
1601
|
+
color: "var(--color-text, #ffffff)",
|
|
1602
|
+
display: "flex",
|
|
1603
|
+
alignItems: "center",
|
|
1604
|
+
gap: "0.25rem"
|
|
1605
|
+
},
|
|
1606
|
+
children: [
|
|
1607
|
+
priceChangePositive != null ? /* @__PURE__ */ jsx(
|
|
1608
|
+
"span",
|
|
1469
1609
|
{
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
),
|
|
1476
|
-
"aria-label": "Edit amount",
|
|
1477
|
-
children: /* @__PURE__ */ jsx(EditIcon, { className: "h-[14px] w-[14px]" })
|
|
1610
|
+
style: {
|
|
1611
|
+
color: priceChangePositive ? "#0ecb81" : "#f6465d",
|
|
1612
|
+
fontSize: "0.7em"
|
|
1613
|
+
},
|
|
1614
|
+
children: priceChangePositive ? "\u25B2" : "\u25BC"
|
|
1478
1615
|
}
|
|
1479
|
-
)
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
"
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1616
|
+
) : null,
|
|
1617
|
+
formatCurrency(currentPrice)
|
|
1618
|
+
]
|
|
1619
|
+
}
|
|
1620
|
+
),
|
|
1621
|
+
priceChangePercent != null ? /* @__PURE__ */ jsxs(
|
|
1622
|
+
"div",
|
|
1623
|
+
{
|
|
1624
|
+
style: {
|
|
1625
|
+
fontSize: "0.7rem",
|
|
1626
|
+
color: priceChangePositive ? "#0ecb81" : "#f6465d"
|
|
1627
|
+
},
|
|
1628
|
+
children: [
|
|
1629
|
+
"(",
|
|
1630
|
+
priceChangePositive ? "+" : "",
|
|
1631
|
+
priceChangePercent.toFixed(2),
|
|
1632
|
+
"%)"
|
|
1633
|
+
]
|
|
1634
|
+
}
|
|
1635
|
+
) : null
|
|
1636
|
+
] }) : null
|
|
1637
|
+
] }),
|
|
1638
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.9rem", fontWeight: 500, color: "#D4AF37" }, children: formatCurrency(value) }),
|
|
1639
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.9rem", color: "rgba(255,255,255,0.92)" }, children: holding }),
|
|
1640
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.9rem", color: "rgba(255,255,255,0.8)" }, children: formatPercent(propertyPercent, 3) }),
|
|
1641
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.9rem", color: "rgba(255,255,255,0.9)" }, children: formatCurrency(avgPrice) }),
|
|
1642
|
+
/* @__PURE__ */ jsxs(
|
|
1643
|
+
"div",
|
|
1644
|
+
{
|
|
1645
|
+
style: {
|
|
1646
|
+
fontSize: "0.9rem",
|
|
1647
|
+
fontWeight: 500,
|
|
1648
|
+
color: pnlPositive ? "#0ecb81" : "#f6465d"
|
|
1505
1649
|
},
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1650
|
+
children: [
|
|
1651
|
+
pnlValue != null ? `${pnlPositive ? "+" : "-"}${formatCurrency(Math.abs(pnlValue))}` : formatCurrency(pnlValue),
|
|
1652
|
+
pnlPercent != null ? /* @__PURE__ */ jsxs("span", { style: { marginLeft: "0.35rem", fontSize: "0.75rem", color: "rgba(255,255,255,0.6)" }, children: [
|
|
1653
|
+
"(",
|
|
1654
|
+
pnlPositive ? "+" : "",
|
|
1655
|
+
pnlPercent.toFixed(1),
|
|
1656
|
+
"%)"
|
|
1657
|
+
] }) : null
|
|
1658
|
+
]
|
|
1659
|
+
}
|
|
1660
|
+
)
|
|
1661
|
+
] }, order.id);
|
|
1662
|
+
}) })
|
|
1663
|
+
] });
|
|
1664
|
+
};
|
|
1665
|
+
const renderOtherTab = (label) => /* @__PURE__ */ jsx("div", { style: emptyStateStyle, children: `No ${label.toLowerCase()} data available.` });
|
|
1666
|
+
let tabContent = null;
|
|
1667
|
+
switch (activeTab) {
|
|
1668
|
+
case "portfolio":
|
|
1669
|
+
tabContent = renderPortfolio();
|
|
1670
|
+
break;
|
|
1671
|
+
case "openOrders":
|
|
1672
|
+
tabContent = renderOtherTab("Open Orders");
|
|
1673
|
+
break;
|
|
1674
|
+
case "tradeHistory":
|
|
1675
|
+
tabContent = renderOtherTab("Trade History");
|
|
1676
|
+
break;
|
|
1677
|
+
case "orderHistory":
|
|
1678
|
+
tabContent = renderOtherTab("Order History");
|
|
1679
|
+
break;
|
|
1680
|
+
default:
|
|
1681
|
+
tabContent = null;
|
|
1682
|
+
}
|
|
1683
|
+
return /* @__PURE__ */ jsxs("div", { ref, style: panelStyle, className, ...props, children: [
|
|
1684
|
+
/* @__PURE__ */ jsx("div", { style: headerStyle, children: /* @__PURE__ */ jsx("h3", { style: titleStyle, children: title }) }),
|
|
1685
|
+
/* @__PURE__ */ jsx("div", { style: tabsRowStyle, children: TABS.map((tab) => {
|
|
1686
|
+
const isActive = activeTab === tab.id;
|
|
1687
|
+
return /* @__PURE__ */ jsxs(
|
|
1688
|
+
"button",
|
|
1689
|
+
{
|
|
1690
|
+
type: "button",
|
|
1691
|
+
onClick: () => setActiveTab(tab.id),
|
|
1692
|
+
style: {
|
|
1693
|
+
...tabButtonBase,
|
|
1694
|
+
color: isActive ? "#ffffff" : "rgba(255,255,255,0.55)",
|
|
1695
|
+
fontWeight: isActive ? 600 : 400
|
|
1696
|
+
},
|
|
1697
|
+
children: [
|
|
1698
|
+
tab.label,
|
|
1699
|
+
isActive ? /* @__PURE__ */ jsx(
|
|
1700
|
+
"div",
|
|
1701
|
+
{
|
|
1702
|
+
style: {
|
|
1703
|
+
position: "absolute",
|
|
1704
|
+
bottom: "-1px",
|
|
1705
|
+
left: 0,
|
|
1706
|
+
right: 0,
|
|
1707
|
+
height: "2px",
|
|
1708
|
+
background: "#f0b90b"
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
) : null
|
|
1712
|
+
]
|
|
1713
|
+
},
|
|
1714
|
+
tab.id
|
|
1715
|
+
);
|
|
1716
|
+
}) }),
|
|
1717
|
+
/* @__PURE__ */ jsx("div", { style: tabContentWrapper, children: tabContent })
|
|
1718
|
+
] });
|
|
1513
1719
|
}
|
|
1514
1720
|
);
|
|
1515
1721
|
YourOrders.displayName = "YourOrders";
|
|
@@ -1719,7 +1925,10 @@ var PropertyHeroHeader = React5.forwardRef(
|
|
|
1719
1925
|
...props
|
|
1720
1926
|
}, ref) => {
|
|
1721
1927
|
const isPositive = changePercent == null ? void 0 : changePercent >= 0;
|
|
1722
|
-
const
|
|
1928
|
+
const accentColor = "#e6c87e";
|
|
1929
|
+
const tradeHoverColor = "#f5dd9a";
|
|
1930
|
+
const [isTradeInteracting, setIsTradeInteracting] = React5.useState(false);
|
|
1931
|
+
const [isOfferInteracting, setIsOfferInteracting] = React5.useState(false);
|
|
1723
1932
|
return /* @__PURE__ */ jsxs(
|
|
1724
1933
|
"div",
|
|
1725
1934
|
{
|
|
@@ -1805,20 +2014,30 @@ var PropertyHeroHeader = React5.forwardRef(
|
|
|
1805
2014
|
propertyTypeLabel == null ? null : /* @__PURE__ */ jsx("div", { children: propertyTypeLabel })
|
|
1806
2015
|
] })
|
|
1807
2016
|
] }),
|
|
1808
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-
|
|
2017
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3 max-[768px]:w-full max-[768px]:justify-between max-[768px]:gap-2 max-[480px]:gap-2", children: [
|
|
1809
2018
|
/* @__PURE__ */ jsx(
|
|
1810
2019
|
"button",
|
|
1811
2020
|
{
|
|
1812
2021
|
type: "button",
|
|
1813
2022
|
onClick: onTrade,
|
|
1814
|
-
className: "
|
|
1815
|
-
style: {
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
2023
|
+
className: "flex items-center justify-center rounded border font-semibold transition-all duration-200 hover:-translate-y-0.5 hover:shadow-[0_4px_8px_rgba(0,0,0,0.2)] active:translate-y-0 active:shadow-[0_2px_4px_rgba(0,0,0,0.1)] text-[0.95rem] max-[480px]:text-[0.9rem]",
|
|
2024
|
+
style: {
|
|
2025
|
+
backgroundColor: isTradeInteracting ? tradeHoverColor : accentColor,
|
|
2026
|
+
color: "black",
|
|
2027
|
+
width: "88.06px",
|
|
2028
|
+
height: "43px",
|
|
2029
|
+
minWidth: "88.06px",
|
|
2030
|
+
borderColor: isTradeInteracting ? accentColor : "transparent",
|
|
2031
|
+
boxShadow: isTradeInteracting ? `0 0 0 2px rgba(0,0,0,0.4), 0 0 0 4px ${accentColor}` : "none"
|
|
1821
2032
|
},
|
|
2033
|
+
onMouseEnter: () => setIsTradeInteracting(true),
|
|
2034
|
+
onMouseLeave: () => setIsTradeInteracting(false),
|
|
2035
|
+
onMouseDown: () => setIsTradeInteracting(true),
|
|
2036
|
+
onMouseUp: () => setIsTradeInteracting(false),
|
|
2037
|
+
onFocus: () => setIsTradeInteracting(true),
|
|
2038
|
+
onBlur: () => setIsTradeInteracting(false),
|
|
2039
|
+
onTouchStart: () => setIsTradeInteracting(true),
|
|
2040
|
+
onTouchEnd: () => setIsTradeInteracting(false),
|
|
1822
2041
|
children: "Trade"
|
|
1823
2042
|
}
|
|
1824
2043
|
),
|
|
@@ -1827,18 +2046,24 @@ var PropertyHeroHeader = React5.forwardRef(
|
|
|
1827
2046
|
{
|
|
1828
2047
|
type: "button",
|
|
1829
2048
|
onClick: onMakeOffer,
|
|
1830
|
-
className: "
|
|
2049
|
+
className: "flex items-center justify-center rounded border font-semibold transition-all duration-200 hover:-translate-y-0.5 hover:shadow-[0_4px_8px_rgba(0,0,0,0.2)] active:translate-y-0 active:shadow-[0_2px_4px_rgba(0,0,0,0.1)] text-[0.95rem] max-[480px]:text-[0.9rem]",
|
|
1831
2050
|
style: {
|
|
1832
|
-
backgroundColor: "transparent",
|
|
1833
|
-
borderColor:
|
|
1834
|
-
color: "
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
onMouseLeave: (e) => {
|
|
1840
|
-
e.currentTarget.style.backgroundColor = "transparent";
|
|
2051
|
+
backgroundColor: isOfferInteracting ? accentColor : "transparent",
|
|
2052
|
+
borderColor: accentColor,
|
|
2053
|
+
color: isOfferInteracting ? "black" : accentColor,
|
|
2054
|
+
width: "127.14px",
|
|
2055
|
+
height: "43px",
|
|
2056
|
+
minWidth: "127.14px",
|
|
2057
|
+
boxShadow: isOfferInteracting ? `0 0 0 2px rgba(0,0,0,0.4), 0 0 0 4px ${accentColor}` : "none"
|
|
1841
2058
|
},
|
|
2059
|
+
onMouseEnter: () => setIsOfferInteracting(true),
|
|
2060
|
+
onMouseLeave: () => setIsOfferInteracting(false),
|
|
2061
|
+
onMouseDown: () => setIsOfferInteracting(true),
|
|
2062
|
+
onMouseUp: () => setIsOfferInteracting(false),
|
|
2063
|
+
onFocus: () => setIsOfferInteracting(true),
|
|
2064
|
+
onBlur: () => setIsOfferInteracting(false),
|
|
2065
|
+
onTouchStart: () => setIsOfferInteracting(true),
|
|
2066
|
+
onTouchEnd: () => setIsOfferInteracting(false),
|
|
1842
2067
|
children: "Make Offer"
|
|
1843
2068
|
}
|
|
1844
2069
|
)
|