@loafmarkets/ui 0.1.414 → 0.1.416
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 +87 -2
- package/dist/index.d.ts +87 -2
- package/dist/index.js +1836 -583
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1609 -360
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var React9 = require('react');
|
|
4
4
|
var reactSlot = require('@radix-ui/react-slot');
|
|
5
5
|
var classVarianceAuthority = require('class-variance-authority');
|
|
6
6
|
var clsx = require('clsx');
|
|
7
7
|
var tailwindMerge = require('tailwind-merge');
|
|
8
8
|
var jsxRuntime = require('react/jsx-runtime');
|
|
9
|
-
var styled10 = require('styled-components');
|
|
10
9
|
var ReactDOM = require('react-dom');
|
|
10
|
+
var styled10 = require('styled-components');
|
|
11
11
|
var lucideReact = require('lucide-react');
|
|
12
12
|
var LightweightCharts = require('lightweight-charts');
|
|
13
13
|
var md = require('react-icons/md');
|
|
@@ -36,9 +36,9 @@ function _interopNamespace(e) {
|
|
|
36
36
|
return Object.freeze(n);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
var
|
|
40
|
-
var styled10__default = /*#__PURE__*/_interopDefault(styled10);
|
|
39
|
+
var React9__namespace = /*#__PURE__*/_interopNamespace(React9);
|
|
41
40
|
var ReactDOM__namespace = /*#__PURE__*/_interopNamespace(ReactDOM);
|
|
41
|
+
var styled10__default = /*#__PURE__*/_interopDefault(styled10);
|
|
42
42
|
var LightweightCharts__namespace = /*#__PURE__*/_interopNamespace(LightweightCharts);
|
|
43
43
|
|
|
44
44
|
// src/components/button.tsx
|
|
@@ -79,7 +79,7 @@ var buttonVariants = classVarianceAuthority.cva(
|
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
);
|
|
82
|
-
var Button =
|
|
82
|
+
var Button = React9__namespace.forwardRef(
|
|
83
83
|
({ className, variant, size, radius, asChild = false, ...props }, ref) => {
|
|
84
84
|
const Comp = asChild ? reactSlot.Slot : "button";
|
|
85
85
|
const coercedRadius = radius ?? (variant === "onboarding" || variant === "onboardingOutline" ? "square" : void 0);
|
|
@@ -118,11 +118,11 @@ var badgeVariants = classVarianceAuthority.cva(
|
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
);
|
|
121
|
-
var Badge =
|
|
121
|
+
var Badge = React9__namespace.forwardRef(
|
|
122
122
|
({ className, variant, size, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("span", { ref, className: cn(badgeVariants({ variant, size }), className), ...props })
|
|
123
123
|
);
|
|
124
124
|
Badge.displayName = "Badge";
|
|
125
|
-
var Card =
|
|
125
|
+
var Card = React9__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
126
126
|
"div",
|
|
127
127
|
{
|
|
128
128
|
ref,
|
|
@@ -134,11 +134,11 @@ var Card = React5__namespace.forwardRef(({ className, ...props }, ref) => /* @__
|
|
|
134
134
|
}
|
|
135
135
|
));
|
|
136
136
|
Card.displayName = "Card";
|
|
137
|
-
var CardHeader =
|
|
137
|
+
var CardHeader = React9__namespace.forwardRef(
|
|
138
138
|
({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("flex flex-col space-y-1.5 p-6", className), ...props })
|
|
139
139
|
);
|
|
140
140
|
CardHeader.displayName = "CardHeader";
|
|
141
|
-
var CardTitle =
|
|
141
|
+
var CardTitle = React9__namespace.forwardRef(
|
|
142
142
|
({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
143
143
|
"h3",
|
|
144
144
|
{
|
|
@@ -149,15 +149,15 @@ var CardTitle = React5__namespace.forwardRef(
|
|
|
149
149
|
)
|
|
150
150
|
);
|
|
151
151
|
CardTitle.displayName = "CardTitle";
|
|
152
|
-
var CardDescription =
|
|
152
|
+
var CardDescription = React9__namespace.forwardRef(
|
|
153
153
|
({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("p", { ref, className: cn("text-sm text-slate-500", className), ...props })
|
|
154
154
|
);
|
|
155
155
|
CardDescription.displayName = "CardDescription";
|
|
156
|
-
var CardContent =
|
|
156
|
+
var CardContent = React9__namespace.forwardRef(
|
|
157
157
|
({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("px-6 pb-6 pt-2 text-sm text-slate-600", className), ...props })
|
|
158
158
|
);
|
|
159
159
|
CardContent.displayName = "CardContent";
|
|
160
|
-
var CardFooter =
|
|
160
|
+
var CardFooter = React9__namespace.forwardRef(
|
|
161
161
|
({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsx("div", { ref, className: cn("flex items-center border-t border-slate-100 px-6 py-4", className), ...props })
|
|
162
162
|
);
|
|
163
163
|
CardFooter.displayName = "CardFooter";
|
|
@@ -175,7 +175,7 @@ var defaultFormatSignedCurrency = (value) => {
|
|
|
175
175
|
const sign = value >= 0 ? "+" : "-";
|
|
176
176
|
return `${sign}${defaultFormatCurrency(Math.abs(value))}`;
|
|
177
177
|
};
|
|
178
|
-
var PortfolioSummary =
|
|
178
|
+
var PortfolioSummary = React9__namespace.forwardRef(
|
|
179
179
|
({
|
|
180
180
|
availableCash,
|
|
181
181
|
portfolioValue,
|
|
@@ -325,23 +325,23 @@ function HousePositionSlider({
|
|
|
325
325
|
className,
|
|
326
326
|
...props
|
|
327
327
|
}) {
|
|
328
|
-
const [orderMode, setOrderMode] =
|
|
329
|
-
const [buyTrackingMode, setBuyTrackingMode] =
|
|
330
|
-
const [deltaDollars, setDeltaDollars] =
|
|
331
|
-
const [deltaTokensBuy, setDeltaTokensBuy] =
|
|
332
|
-
const [deltaTokensSell, setDeltaTokensSell] =
|
|
333
|
-
const [_isDragging, setIsDragging] =
|
|
334
|
-
const [visualTargetPct, setVisualTargetPct] =
|
|
335
|
-
const [orderType, setOrderType] =
|
|
336
|
-
const [limitPrice, setLimitPrice] =
|
|
337
|
-
const [limitPriceInput, setLimitPriceInput] =
|
|
338
|
-
const [limitPriceDirty, setLimitPriceDirty] =
|
|
339
|
-
const [ownershipInput, setOwnershipInput] =
|
|
340
|
-
const [tokenAmountInput, setTokenAmountInput] =
|
|
341
|
-
const houseRef =
|
|
328
|
+
const [orderMode, setOrderMode] = React9__namespace.useState("none");
|
|
329
|
+
const [buyTrackingMode, setBuyTrackingMode] = React9__namespace.useState("dollars");
|
|
330
|
+
const [deltaDollars, setDeltaDollars] = React9__namespace.useState(0);
|
|
331
|
+
const [deltaTokensBuy, setDeltaTokensBuy] = React9__namespace.useState(0);
|
|
332
|
+
const [deltaTokensSell, setDeltaTokensSell] = React9__namespace.useState(0);
|
|
333
|
+
const [_isDragging, setIsDragging] = React9__namespace.useState(false);
|
|
334
|
+
const [visualTargetPct, setVisualTargetPct] = React9__namespace.useState(null);
|
|
335
|
+
const [orderType, setOrderType] = React9__namespace.useState(defaultOrderType);
|
|
336
|
+
const [limitPrice, setLimitPrice] = React9__namespace.useState(currentPrice);
|
|
337
|
+
const [limitPriceInput, setLimitPriceInput] = React9__namespace.useState(currentPrice.toFixed(2));
|
|
338
|
+
const [limitPriceDirty, setLimitPriceDirty] = React9__namespace.useState(false);
|
|
339
|
+
const [ownershipInput, setOwnershipInput] = React9__namespace.useState("");
|
|
340
|
+
const [tokenAmountInput, setTokenAmountInput] = React9__namespace.useState("");
|
|
341
|
+
const houseRef = React9__namespace.useRef(null);
|
|
342
342
|
const asks = orderbook?.asks ?? [];
|
|
343
343
|
const bids = orderbook?.bids ?? [];
|
|
344
|
-
|
|
344
|
+
React9__namespace.useEffect(() => {
|
|
345
345
|
if (orderType !== "limit") return;
|
|
346
346
|
if (limitPriceDirty) return;
|
|
347
347
|
setLimitPrice(currentPrice);
|
|
@@ -416,7 +416,7 @@ function HousePositionSlider({
|
|
|
416
416
|
const impliedDisplayTargetOwnership = clamp(targetOwnership + ownershipShift, 0, 100);
|
|
417
417
|
const displayTargetOwnership = visualTargetPct ?? impliedDisplayTargetOwnership;
|
|
418
418
|
const estFeeTokens = Math.abs(deltaValue) * 5e-3 / (effectivePrice || 1);
|
|
419
|
-
const resetOrder =
|
|
419
|
+
const resetOrder = React9__namespace.useCallback(() => {
|
|
420
420
|
setOrderMode("none");
|
|
421
421
|
setBuyTrackingMode("dollars");
|
|
422
422
|
setDeltaDollars(0);
|
|
@@ -424,7 +424,7 @@ function HousePositionSlider({
|
|
|
424
424
|
setDeltaTokensSell(0);
|
|
425
425
|
setVisualTargetPct(null);
|
|
426
426
|
}, []);
|
|
427
|
-
const updateOrderFromTargetValue =
|
|
427
|
+
const updateOrderFromTargetValue = React9__namespace.useCallback(
|
|
428
428
|
(newTargetValue) => {
|
|
429
429
|
const newDeltaValue = newTargetValue - holdingsValue;
|
|
430
430
|
if (newDeltaValue > 0) {
|
|
@@ -448,7 +448,7 @@ function HousePositionSlider({
|
|
|
448
448
|
},
|
|
449
449
|
[effectiveAvailableCash, effectivePrice, effectiveTokensHeld, holdingsValue, resetOrder, tokensHeld]
|
|
450
450
|
);
|
|
451
|
-
const updateOrderFromOwnership =
|
|
451
|
+
const updateOrderFromOwnership = React9__namespace.useCallback(
|
|
452
452
|
(newOwnershipPercent) => {
|
|
453
453
|
const nextOwnership = clamp(newOwnershipPercent, 0, 100);
|
|
454
454
|
const newTargetTokens = nextOwnership / 100 * totalTokens;
|
|
@@ -457,7 +457,7 @@ function HousePositionSlider({
|
|
|
457
457
|
},
|
|
458
458
|
[effectivePrice, totalTokens, updateOrderFromTargetValue]
|
|
459
459
|
);
|
|
460
|
-
const updateOrderFromTokenAmount =
|
|
460
|
+
const updateOrderFromTokenAmount = React9__namespace.useCallback(
|
|
461
461
|
(tokenAmountSigned) => {
|
|
462
462
|
if (tokenAmountSigned > 0) {
|
|
463
463
|
const maxTokens = effectiveAvailableCash / (effectivePrice || 1);
|
|
@@ -480,7 +480,7 @@ function HousePositionSlider({
|
|
|
480
480
|
},
|
|
481
481
|
[effectiveAvailableCash, effectivePrice, effectiveTokensHeld, resetOrder]
|
|
482
482
|
);
|
|
483
|
-
const updateOrderFromSlider =
|
|
483
|
+
const updateOrderFromSlider = React9__namespace.useCallback(
|
|
484
484
|
(pct) => {
|
|
485
485
|
const normalized = (pct - 50) / 50;
|
|
486
486
|
const magnitude = Math.min(Math.abs(normalized), 1);
|
|
@@ -524,7 +524,7 @@ function HousePositionSlider({
|
|
|
524
524
|
},
|
|
525
525
|
[effectiveAvailableCash, effectiveTokensHeld, resetOrder]
|
|
526
526
|
);
|
|
527
|
-
const handleDragAtClientY =
|
|
527
|
+
const handleDragAtClientY = React9__namespace.useCallback(
|
|
528
528
|
(clientY) => {
|
|
529
529
|
if (!houseRef.current) return;
|
|
530
530
|
const rect = houseRef.current.getBoundingClientRect();
|
|
@@ -1040,18 +1040,18 @@ function HousePositionSliderMobile({
|
|
|
1040
1040
|
className,
|
|
1041
1041
|
...props
|
|
1042
1042
|
}) {
|
|
1043
|
-
const [orderMode, setOrderMode] =
|
|
1044
|
-
const [deltaDollars, setDeltaDollars] =
|
|
1045
|
-
const [deltaTokensSell, setDeltaTokensSell] =
|
|
1046
|
-
const [deltaTokensBuy, setDeltaTokensBuy] =
|
|
1047
|
-
const [buyTrackingMode, setBuyTrackingMode] =
|
|
1048
|
-
const [orderType, setOrderType] =
|
|
1049
|
-
const [limitPrice, setLimitPrice] =
|
|
1050
|
-
const [limitPriceInput, setLimitPriceInput] =
|
|
1051
|
-
const [limitPriceDirty, setLimitPriceDirty] =
|
|
1052
|
-
const [tokenAmountInput, setTokenAmountInput] =
|
|
1053
|
-
const houseRef =
|
|
1054
|
-
|
|
1043
|
+
const [orderMode, setOrderMode] = React9__namespace.useState("none");
|
|
1044
|
+
const [deltaDollars, setDeltaDollars] = React9__namespace.useState(0);
|
|
1045
|
+
const [deltaTokensSell, setDeltaTokensSell] = React9__namespace.useState(0);
|
|
1046
|
+
const [deltaTokensBuy, setDeltaTokensBuy] = React9__namespace.useState(0);
|
|
1047
|
+
const [buyTrackingMode, setBuyTrackingMode] = React9__namespace.useState("dollars");
|
|
1048
|
+
const [orderType, setOrderType] = React9__namespace.useState(defaultOrderType);
|
|
1049
|
+
const [limitPrice, setLimitPrice] = React9__namespace.useState(currentPrice);
|
|
1050
|
+
const [limitPriceInput, setLimitPriceInput] = React9__namespace.useState(currentPrice.toFixed(2));
|
|
1051
|
+
const [limitPriceDirty, setLimitPriceDirty] = React9__namespace.useState(false);
|
|
1052
|
+
const [tokenAmountInput, setTokenAmountInput] = React9__namespace.useState("");
|
|
1053
|
+
const houseRef = React9__namespace.useRef(null);
|
|
1054
|
+
React9__namespace.useEffect(() => {
|
|
1055
1055
|
if (orderType !== "limit") return;
|
|
1056
1056
|
if (limitPriceDirty) return;
|
|
1057
1057
|
setLimitPrice(currentPrice);
|
|
@@ -1093,7 +1093,7 @@ function HousePositionSliderMobile({
|
|
|
1093
1093
|
const hasChange = orderMode !== "none" && (Math.abs(deltaTokens) > 1e-3 || Math.abs(deltaValue) > 0.01);
|
|
1094
1094
|
const targetOwnership = totalTokens > 0 ? targetTokens / totalTokens * 100 : 0;
|
|
1095
1095
|
const estFeeTokens = effectivePrice > 0 ? Math.abs(deltaValue) * 5e-3 / effectivePrice : 0;
|
|
1096
|
-
const updateOrderFromTargetValue =
|
|
1096
|
+
const updateOrderFromTargetValue = React9__namespace.useCallback(
|
|
1097
1097
|
(newTargetValue) => {
|
|
1098
1098
|
const newDeltaValue = newTargetValue - holdingsValue;
|
|
1099
1099
|
if (newDeltaValue > 0.01) {
|
|
@@ -1121,7 +1121,7 @@ function HousePositionSliderMobile({
|
|
|
1121
1121
|
},
|
|
1122
1122
|
[holdingsValue, availableCash, effectivePrice, tokensHeld]
|
|
1123
1123
|
);
|
|
1124
|
-
const updateOrderFromTokenAmount =
|
|
1124
|
+
const updateOrderFromTokenAmount = React9__namespace.useCallback(
|
|
1125
1125
|
(tokenAmount) => {
|
|
1126
1126
|
if (tokenAmount > 0) {
|
|
1127
1127
|
const maxTokens = effectivePrice > 0 ? availableCash / effectivePrice : 0;
|
|
@@ -1148,14 +1148,14 @@ function HousePositionSliderMobile({
|
|
|
1148
1148
|
},
|
|
1149
1149
|
[effectivePrice, availableCash, tokensHeld]
|
|
1150
1150
|
);
|
|
1151
|
-
const handleMarkerClick =
|
|
1151
|
+
const handleMarkerClick = React9__namespace.useCallback(
|
|
1152
1152
|
(pct) => {
|
|
1153
1153
|
const newTargetValue = pct / 100 * totalCapacity;
|
|
1154
1154
|
updateOrderFromTargetValue(newTargetValue);
|
|
1155
1155
|
},
|
|
1156
1156
|
[totalCapacity, updateOrderFromTargetValue]
|
|
1157
1157
|
);
|
|
1158
|
-
const onMouseDown =
|
|
1158
|
+
const onMouseDown = React9__namespace.useCallback(
|
|
1159
1159
|
(e) => {
|
|
1160
1160
|
e.preventDefault();
|
|
1161
1161
|
const move = (ev) => {
|
|
@@ -1175,7 +1175,7 @@ function HousePositionSliderMobile({
|
|
|
1175
1175
|
},
|
|
1176
1176
|
[totalCapacity, updateOrderFromTargetValue]
|
|
1177
1177
|
);
|
|
1178
|
-
const onTouchStart =
|
|
1178
|
+
const onTouchStart = React9__namespace.useCallback(
|
|
1179
1179
|
(e) => {
|
|
1180
1180
|
e.preventDefault();
|
|
1181
1181
|
e.stopPropagation();
|
|
@@ -1655,7 +1655,7 @@ var StatusDot = styled10__default.default.span`
|
|
|
1655
1655
|
background: rgba(255, 255, 255, 0.3);
|
|
1656
1656
|
`}
|
|
1657
1657
|
`;
|
|
1658
|
-
var LoafLiquidityBadge =
|
|
1658
|
+
var LoafLiquidityBadge = React9__namespace.forwardRef(
|
|
1659
1659
|
({ className, isGlowing, status = "unknown", onClick, children, ...props }, ref) => {
|
|
1660
1660
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1661
1661
|
LogoContainer,
|
|
@@ -1679,7 +1679,7 @@ var LoafLiquidityBadge = React5__namespace.forwardRef(
|
|
|
1679
1679
|
);
|
|
1680
1680
|
LoafLiquidityBadge.displayName = "LoafLiquidityBadge";
|
|
1681
1681
|
var toSize = (v, fallback) => v == null ? fallback : typeof v === "number" ? `${v}px` : v;
|
|
1682
|
-
var Skeleton =
|
|
1682
|
+
var Skeleton = React9__namespace.forwardRef(
|
|
1683
1683
|
({ width, height, radius, className, style, ...props }, ref) => {
|
|
1684
1684
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1685
1685
|
"div",
|
|
@@ -1703,9 +1703,17 @@ var Skeleton = React5__namespace.forwardRef(
|
|
|
1703
1703
|
}
|
|
1704
1704
|
);
|
|
1705
1705
|
Skeleton.displayName = "Skeleton";
|
|
1706
|
+
var DEFAULT_PRICE_LABEL = "Price (USD)";
|
|
1707
|
+
var PRICE_UNIT_MIN_WIDTH_PX = 320;
|
|
1708
|
+
function priceHeaderLabel(label, panelWidth) {
|
|
1709
|
+
if (label === DEFAULT_PRICE_LABEL && panelWidth != null && panelWidth > 0 && panelWidth < PRICE_UNIT_MIN_WIDTH_PX) {
|
|
1710
|
+
return "Price";
|
|
1711
|
+
}
|
|
1712
|
+
return label;
|
|
1713
|
+
}
|
|
1706
1714
|
function useViewportCompact(breakpoint) {
|
|
1707
|
-
const [isCompact, setIsCompact] =
|
|
1708
|
-
|
|
1715
|
+
const [isCompact, setIsCompact] = React9__namespace.useState(false);
|
|
1716
|
+
React9__namespace.useEffect(() => {
|
|
1709
1717
|
if (typeof window === "undefined") return;
|
|
1710
1718
|
const check = () => setIsCompact(window.innerWidth <= breakpoint);
|
|
1711
1719
|
check();
|
|
@@ -1720,9 +1728,14 @@ var formatTradeTime = (time) => {
|
|
|
1720
1728
|
if (Number.isNaN(d.getTime())) return time;
|
|
1721
1729
|
return d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: false });
|
|
1722
1730
|
};
|
|
1723
|
-
var FLASH_DURATION_MS =
|
|
1724
|
-
var FLASH_UP_COLOR = "rgba(14, 203, 129, 0.
|
|
1725
|
-
var FLASH_DOWN_COLOR = "rgba(246, 70, 93, 0.
|
|
1731
|
+
var FLASH_DURATION_MS = 900;
|
|
1732
|
+
var FLASH_UP_COLOR = "rgba(14, 203, 129, 0.22)";
|
|
1733
|
+
var FLASH_DOWN_COLOR = "rgba(246, 70, 93, 0.22)";
|
|
1734
|
+
var FLASH_KEYFRAMES = (color) => [
|
|
1735
|
+
{ backgroundColor: "rgba(0,0,0,0)" },
|
|
1736
|
+
{ backgroundColor: color, offset: 0.3 },
|
|
1737
|
+
{ backgroundColor: "rgba(0,0,0,0)" }
|
|
1738
|
+
];
|
|
1726
1739
|
function getTradeKey(trade, fallbackIndex) {
|
|
1727
1740
|
if (trade.tradeId != null) return `id-${trade.tradeId}`;
|
|
1728
1741
|
if (trade.time != null) return `t-${trade.time}-${trade.type}-${trade.price}-${trade.amount}`;
|
|
@@ -1731,14 +1744,11 @@ function getTradeKey(trade, fallbackIndex) {
|
|
|
1731
1744
|
function flashElement(node, side) {
|
|
1732
1745
|
if (typeof node.animate !== "function") return;
|
|
1733
1746
|
const color = side === "bid" ? FLASH_UP_COLOR : FLASH_DOWN_COLOR;
|
|
1734
|
-
node.animate(
|
|
1735
|
-
[{ backgroundColor: color }, { backgroundColor: "transparent" }],
|
|
1736
|
-
{ duration: FLASH_DURATION_MS, easing: "ease-out" }
|
|
1737
|
-
);
|
|
1747
|
+
node.animate(FLASH_KEYFRAMES(color), { duration: FLASH_DURATION_MS, easing: "ease-in-out" });
|
|
1738
1748
|
}
|
|
1739
1749
|
function useAmountChangeFlash(ref, amount, side, flashOnMount) {
|
|
1740
|
-
const prevAmountRef =
|
|
1741
|
-
|
|
1750
|
+
const prevAmountRef = React9__namespace.useRef(flashOnMount ? null : amount);
|
|
1751
|
+
React9__namespace.useEffect(() => {
|
|
1742
1752
|
const prev = prevAmountRef.current;
|
|
1743
1753
|
const node = ref.current;
|
|
1744
1754
|
if (node && prev !== amount) {
|
|
@@ -1759,18 +1769,15 @@ function TradeRow({
|
|
|
1759
1769
|
compact,
|
|
1760
1770
|
explorerUrl
|
|
1761
1771
|
}) {
|
|
1762
|
-
const rowRef =
|
|
1772
|
+
const rowRef = React9__namespace.useRef(null);
|
|
1763
1773
|
const { type } = trade;
|
|
1764
|
-
|
|
1774
|
+
React9__namespace.useEffect(() => {
|
|
1765
1775
|
if (seenTradeKeysRef.current.has(tradeKey)) return;
|
|
1766
1776
|
seenTradeKeysRef.current.add(tradeKey);
|
|
1767
1777
|
const node = rowRef.current;
|
|
1768
1778
|
if (!node || typeof node.animate !== "function") return;
|
|
1769
1779
|
const color = type === "buy" ? FLASH_UP_COLOR : FLASH_DOWN_COLOR;
|
|
1770
|
-
node.animate(
|
|
1771
|
-
[{ backgroundColor: color }, { backgroundColor: "transparent" }],
|
|
1772
|
-
{ duration: FLASH_DURATION_MS, easing: "ease-out" }
|
|
1773
|
-
);
|
|
1780
|
+
node.animate(FLASH_KEYFRAMES(color), { duration: FLASH_DURATION_MS, easing: "ease-in-out" });
|
|
1774
1781
|
}, [tradeKey, seenTradeKeysRef, type]);
|
|
1775
1782
|
if (compact) {
|
|
1776
1783
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -1837,6 +1844,27 @@ function TradeRow({
|
|
|
1837
1844
|
}
|
|
1838
1845
|
);
|
|
1839
1846
|
}
|
|
1847
|
+
function BreadSliceGlyph() {
|
|
1848
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1849
|
+
"svg",
|
|
1850
|
+
{
|
|
1851
|
+
width: "10",
|
|
1852
|
+
height: "10",
|
|
1853
|
+
viewBox: "0 0 10 10",
|
|
1854
|
+
"aria-hidden": "true",
|
|
1855
|
+
style: { display: "block", filter: "drop-shadow(0 0 4px rgba(201,162,39,0.6))" },
|
|
1856
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1857
|
+
"path",
|
|
1858
|
+
{
|
|
1859
|
+
d: "M1 4.2 C1 1.9 2.7 1 5 1 C7.3 1 9 1.9 9 4.2 L9 8.2 C9 8.6 8.6 9 8.2 9 L1.8 9 C1.4 9 1 8.6 1 8.2 Z",
|
|
1860
|
+
fill: "none",
|
|
1861
|
+
stroke: "#C9A227",
|
|
1862
|
+
strokeWidth: "1.3"
|
|
1863
|
+
}
|
|
1864
|
+
)
|
|
1865
|
+
}
|
|
1866
|
+
);
|
|
1867
|
+
}
|
|
1840
1868
|
function DepthRow({
|
|
1841
1869
|
side,
|
|
1842
1870
|
price,
|
|
@@ -1851,12 +1879,14 @@ function DepthRow({
|
|
|
1851
1879
|
fillHeight
|
|
1852
1880
|
}) {
|
|
1853
1881
|
const isAsk = side === "ask";
|
|
1854
|
-
const rowRef =
|
|
1882
|
+
const rowRef = React9__namespace.useRef(null);
|
|
1855
1883
|
useAmountChangeFlash(rowRef, amount, side, flashOnMount);
|
|
1856
1884
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1857
1885
|
"div",
|
|
1858
1886
|
{
|
|
1859
1887
|
ref: rowRef,
|
|
1888
|
+
"data-depth-row": true,
|
|
1889
|
+
"data-price": `$${formatNumber(price, precision)}`,
|
|
1860
1890
|
className: "relative grid grid-cols-3 items-center gap-3 px-3 text-[0.8rem]",
|
|
1861
1891
|
style: fillHeight ? { flex: "1 1 0", minHeight: 0, maxHeight: `${DEPTH_ROW_HEIGHT_PX}px`, cursor: onPriceClick ? "pointer" : void 0 } : { height: `${DEPTH_ROW_HEIGHT_PX}px`, flexShrink: 0, cursor: onPriceClick ? "pointer" : void 0 },
|
|
1862
1892
|
onClick: onPriceClick ? () => onPriceClick(price) : void 0,
|
|
@@ -1864,14 +1894,20 @@ function DepthRow({
|
|
|
1864
1894
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1865
1895
|
"div",
|
|
1866
1896
|
{
|
|
1897
|
+
"data-depth-bar": true,
|
|
1867
1898
|
className: cn(isAsk ? "bg-rose-900/30" : "bg-emerald-900/30"),
|
|
1868
1899
|
style: {
|
|
1869
1900
|
position: "absolute",
|
|
1870
|
-
top:
|
|
1871
|
-
bottom:
|
|
1901
|
+
top: 1,
|
|
1902
|
+
bottom: 1,
|
|
1872
1903
|
left: 0,
|
|
1873
1904
|
width: `${clamp3(depthPct, 0, 100)}%`,
|
|
1874
|
-
transition: "width 300ms ease-out"
|
|
1905
|
+
transition: "width 300ms ease-out",
|
|
1906
|
+
borderRadius: "0 4px 4px 0",
|
|
1907
|
+
// backgroundImage LONGHAND, not the `background` shorthand — jsdom's
|
|
1908
|
+
// cssstyle drops gradients from the shorthand (tests assert this).
|
|
1909
|
+
backgroundColor: "transparent",
|
|
1910
|
+
backgroundImage: isAsk ? "linear-gradient(90deg, rgba(246,70,93,0.11), rgba(246,70,93,0.15))" : "linear-gradient(90deg, rgba(14,203,129,0.09), rgba(14,203,129,0.13))"
|
|
1875
1911
|
}
|
|
1876
1912
|
}
|
|
1877
1913
|
),
|
|
@@ -1879,33 +1915,35 @@ function DepthRow({
|
|
|
1879
1915
|
"span",
|
|
1880
1916
|
{
|
|
1881
1917
|
"aria-label": "Your order at this price",
|
|
1882
|
-
className: "absolute left-0 top-1/2 -translate-y-1/2 z-[2]
|
|
1918
|
+
className: "absolute left-0 top-1/2 -translate-y-1/2 z-[2]",
|
|
1919
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(BreadSliceGlyph, {})
|
|
1883
1920
|
}
|
|
1884
1921
|
) : null,
|
|
1885
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative z-[1] tabular-nums", isAsk ? "text-[#f6465d]" : "text-[#0ecb81]"), children: [
|
|
1922
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { "data-cell": "price", className: cn("relative z-[1] tabular-nums", isAsk ? "text-[#f6465d]" : "text-[#0ecb81]"), children: [
|
|
1886
1923
|
"$",
|
|
1887
1924
|
formatNumber(price, precision)
|
|
1888
1925
|
] }),
|
|
1889
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative z-[1] text-right tabular-nums text-white/90", children: formatNumber(amount, amountPrecision) }),
|
|
1890
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative z-[1] text-right tabular-nums text-white/50", children: formatNumber(cumDepth, amountPrecision) })
|
|
1926
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { "data-cell": "amt", className: "relative z-[1] text-right tabular-nums text-white/90", children: formatNumber(amount, amountPrecision) }),
|
|
1927
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { "data-cell": "tot", className: "relative z-[1] text-right tabular-nums text-white/50", children: formatNumber(cumDepth, amountPrecision) })
|
|
1891
1928
|
]
|
|
1892
1929
|
}
|
|
1893
1930
|
);
|
|
1894
1931
|
}
|
|
1895
1932
|
var clamp3 = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
1933
|
+
var fitLevelCount = (availableHeightPx, max) => clamp3(Math.floor(availableHeightPx / DEPTH_ROW_HEIGHT_PX), 1, max);
|
|
1896
1934
|
var LEVEL_ROWS_VISIBLE = 8;
|
|
1897
1935
|
var DEPTH_ROW_HEIGHT_PX = 28;
|
|
1898
1936
|
var COMPACT_ROWS_VISIBLE = 5;
|
|
1899
1937
|
var COMPACT_ROW_HEIGHT_PX = 30;
|
|
1900
1938
|
var COMPACT_BREAKPOINT_PX = 1024;
|
|
1901
|
-
var Orderbook =
|
|
1939
|
+
var Orderbook = React9__namespace.forwardRef(
|
|
1902
1940
|
({
|
|
1903
1941
|
asks,
|
|
1904
1942
|
bids,
|
|
1905
1943
|
midPrice,
|
|
1906
1944
|
midChangePercent,
|
|
1907
1945
|
trades = [],
|
|
1908
|
-
priceLabel =
|
|
1946
|
+
priceLabel = DEFAULT_PRICE_LABEL,
|
|
1909
1947
|
amountLabel = "Amount",
|
|
1910
1948
|
precision = 2,
|
|
1911
1949
|
amountPrecision = 2,
|
|
@@ -1933,12 +1971,12 @@ var Orderbook = React5__namespace.forwardRef(
|
|
|
1933
1971
|
status: liquidityStatus
|
|
1934
1972
|
}
|
|
1935
1973
|
);
|
|
1936
|
-
const [tab, setTabState] =
|
|
1974
|
+
const [tab, setTabState] = React9__namespace.useState(defaultTab);
|
|
1937
1975
|
const setTab = setTabState;
|
|
1938
1976
|
const effectiveTab = showTradesTab ? tab : "orderbook";
|
|
1939
|
-
const [tradeFilter, setTradeFilter] =
|
|
1977
|
+
const [tradeFilter, setTradeFilter] = React9__namespace.useState("all");
|
|
1940
1978
|
const viewportCompact = useViewportCompact(COMPACT_BREAKPOINT_PX);
|
|
1941
|
-
const { userAskPrices, userBidPrices } =
|
|
1979
|
+
const { userAskPrices, userBidPrices } = React9__namespace.useMemo(() => {
|
|
1942
1980
|
const ask = /* @__PURE__ */ new Set();
|
|
1943
1981
|
const bid = /* @__PURE__ */ new Set();
|
|
1944
1982
|
if (!userOrderPrices) return { userAskPrices: ask, userBidPrices: bid };
|
|
@@ -1948,15 +1986,15 @@ var Orderbook = React5__namespace.forwardRef(
|
|
|
1948
1986
|
}
|
|
1949
1987
|
return { userAskPrices: ask, userBidPrices: bid };
|
|
1950
1988
|
}, [userOrderPrices]);
|
|
1951
|
-
const seenTradeKeysRef =
|
|
1952
|
-
const initializedRef =
|
|
1989
|
+
const seenTradeKeysRef = React9__namespace.useRef(/* @__PURE__ */ new Set());
|
|
1990
|
+
const initializedRef = React9__namespace.useRef(false);
|
|
1953
1991
|
if (!initializedRef.current && trades.length > 0) {
|
|
1954
1992
|
for (let i = 0; i < trades.length; i++) {
|
|
1955
1993
|
seenTradeKeysRef.current.add(getTradeKey(trades[i], i));
|
|
1956
1994
|
}
|
|
1957
1995
|
initializedRef.current = true;
|
|
1958
1996
|
}
|
|
1959
|
-
|
|
1997
|
+
React9__namespace.useEffect(() => {
|
|
1960
1998
|
if (!initializedRef.current) return;
|
|
1961
1999
|
const live = /* @__PURE__ */ new Set();
|
|
1962
2000
|
for (let i = 0; i < trades.length; i++) {
|
|
@@ -1967,7 +2005,7 @@ var Orderbook = React5__namespace.forwardRef(
|
|
|
1967
2005
|
if (!live.has(key)) seen.delete(key);
|
|
1968
2006
|
}
|
|
1969
2007
|
}, [trades]);
|
|
1970
|
-
|
|
2008
|
+
React9__namespace.useEffect(() => {
|
|
1971
2009
|
setTab(defaultTab);
|
|
1972
2010
|
}, [defaultTab]);
|
|
1973
2011
|
const handleTab = (next) => {
|
|
@@ -1975,10 +2013,49 @@ var Orderbook = React5__namespace.forwardRef(
|
|
|
1975
2013
|
onTabChange?.(next);
|
|
1976
2014
|
};
|
|
1977
2015
|
const isCompact = variant === "compact" || variant === "auto" && viewportCompact;
|
|
2016
|
+
const rootRef = React9__namespace.useRef(null);
|
|
2017
|
+
const setRootRef = React9__namespace.useCallback(
|
|
2018
|
+
(node) => {
|
|
2019
|
+
rootRef.current = node;
|
|
2020
|
+
if (typeof ref === "function") ref(node);
|
|
2021
|
+
else if (ref) ref.current = node;
|
|
2022
|
+
},
|
|
2023
|
+
[ref]
|
|
2024
|
+
);
|
|
2025
|
+
const [panelWidth, setPanelWidth] = React9__namespace.useState(null);
|
|
2026
|
+
React9__namespace.useEffect(() => {
|
|
2027
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
2028
|
+
const el = rootRef.current;
|
|
2029
|
+
if (!el) return;
|
|
2030
|
+
const ro = new ResizeObserver((entries) => {
|
|
2031
|
+
const w = entries[0]?.contentRect?.width;
|
|
2032
|
+
if (typeof w === "number") setPanelWidth((prev) => prev === w ? prev : w);
|
|
2033
|
+
});
|
|
2034
|
+
ro.observe(el);
|
|
2035
|
+
return () => ro.disconnect();
|
|
2036
|
+
}, []);
|
|
2037
|
+
const effectivePriceLabel = priceHeaderLabel(priceLabel, panelWidth);
|
|
1978
2038
|
const effectiveLevels = maxLevels ?? LEVEL_ROWS_VISIBLE;
|
|
1979
2039
|
const effectiveCompactLevels = maxLevels ?? COMPACT_ROWS_VISIBLE;
|
|
1980
2040
|
const sectionHeight = isCompact ? effectiveCompactLevels * COMPACT_ROW_HEIGHT_PX : effectiveLevels * DEPTH_ROW_HEIGHT_PX;
|
|
1981
|
-
const
|
|
2041
|
+
const asksSectionRef = React9__namespace.useRef(null);
|
|
2042
|
+
const [depthSectionH, setDepthSectionH] = React9__namespace.useState(null);
|
|
2043
|
+
React9__namespace.useEffect(() => {
|
|
2044
|
+
if (!fillHeight || isCompact || effectiveTab !== "orderbook") return;
|
|
2045
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
2046
|
+
const el = asksSectionRef.current;
|
|
2047
|
+
if (!el) return;
|
|
2048
|
+
const ro = new ResizeObserver((entries) => {
|
|
2049
|
+
const h = entries[0]?.contentRect?.height;
|
|
2050
|
+
if (typeof h === "number") {
|
|
2051
|
+
setDepthSectionH((prev) => prev === h ? prev : h);
|
|
2052
|
+
}
|
|
2053
|
+
});
|
|
2054
|
+
ro.observe(el);
|
|
2055
|
+
return () => ro.disconnect();
|
|
2056
|
+
}, [fillHeight, isCompact, effectiveTab]);
|
|
2057
|
+
const fitRows = fillHeight && !isCompact && depthSectionH != null ? fitLevelCount(depthSectionH, effectiveLevels) : null;
|
|
2058
|
+
const rowCount = isCompact ? effectiveCompactLevels : fitRows ?? effectiveLevels;
|
|
1982
2059
|
const askVisibleLevels = asks.slice(-rowCount);
|
|
1983
2060
|
const bidVisibleLevels = bids.slice(0, rowCount);
|
|
1984
2061
|
const askCumDepths = new Array(askVisibleLevels.length);
|
|
@@ -1999,9 +2076,9 @@ var Orderbook = React5__namespace.forwardRef(
|
|
|
1999
2076
|
}
|
|
2000
2077
|
const combinedMaxCumDepth = Math.max(1, ...askCumDepths, ...bidCumDepths);
|
|
2001
2078
|
const askMaxCumDepth = combinedMaxCumDepth;
|
|
2002
|
-
const prevAskPricesRef =
|
|
2003
|
-
const prevBidPricesRef =
|
|
2004
|
-
const newAskPrices =
|
|
2079
|
+
const prevAskPricesRef = React9__namespace.useRef(null);
|
|
2080
|
+
const prevBidPricesRef = React9__namespace.useRef(null);
|
|
2081
|
+
const newAskPrices = React9__namespace.useMemo(() => {
|
|
2005
2082
|
const current = new Set(askVisibleLevels.map((l) => l.price));
|
|
2006
2083
|
const prev = prevAskPricesRef.current;
|
|
2007
2084
|
if (!prev) return /* @__PURE__ */ new Set();
|
|
@@ -2011,7 +2088,7 @@ var Orderbook = React5__namespace.forwardRef(
|
|
|
2011
2088
|
}
|
|
2012
2089
|
return added;
|
|
2013
2090
|
}, [askVisibleLevels]);
|
|
2014
|
-
const newBidPrices =
|
|
2091
|
+
const newBidPrices = React9__namespace.useMemo(() => {
|
|
2015
2092
|
const current = new Set(bidVisibleLevels.map((l) => l.price));
|
|
2016
2093
|
const prev = prevBidPricesRef.current;
|
|
2017
2094
|
if (!prev) return /* @__PURE__ */ new Set();
|
|
@@ -2021,12 +2098,16 @@ var Orderbook = React5__namespace.forwardRef(
|
|
|
2021
2098
|
}
|
|
2022
2099
|
return added;
|
|
2023
2100
|
}, [bidVisibleLevels]);
|
|
2024
|
-
|
|
2101
|
+
React9__namespace.useEffect(() => {
|
|
2025
2102
|
prevAskPricesRef.current = new Set(askVisibleLevels.map((l) => l.price));
|
|
2026
2103
|
prevBidPricesRef.current = new Set(bidVisibleLevels.map((l) => l.price));
|
|
2027
2104
|
}, [askVisibleLevels, bidVisibleLevels]);
|
|
2028
2105
|
const bidMaxCumDepth = combinedMaxCumDepth;
|
|
2029
2106
|
const midClass = midChangePercent == null ? "text-white" : midChangePercent >= 0 ? "text-[#0ecb81]" : "text-[#f6465d]";
|
|
2107
|
+
const bestAskPrice = asks.length > 0 ? asks[asks.length - 1].price : null;
|
|
2108
|
+
const bestBidPrice = bids.length > 0 ? bids[0].price : null;
|
|
2109
|
+
const spread = bestAskPrice != null && bestBidPrice != null ? bestAskPrice - bestBidPrice : null;
|
|
2110
|
+
const spreadPct = spread != null && midPrice > 0 ? spread / midPrice * 100 : null;
|
|
2030
2111
|
const tradeFiltered = trades.filter((t) => tradeFilter === "all" || t.type === tradeFilter);
|
|
2031
2112
|
const layoutProps = {
|
|
2032
2113
|
tab: effectiveTab,
|
|
@@ -2035,7 +2116,7 @@ var Orderbook = React5__namespace.forwardRef(
|
|
|
2035
2116
|
tradeFilter,
|
|
2036
2117
|
setTradeFilter,
|
|
2037
2118
|
rightHeader: resolvedRightHeader,
|
|
2038
|
-
priceLabel,
|
|
2119
|
+
priceLabel: effectivePriceLabel,
|
|
2039
2120
|
amountLabel,
|
|
2040
2121
|
tradeFiltered,
|
|
2041
2122
|
precision,
|
|
@@ -2062,12 +2143,15 @@ var Orderbook = React5__namespace.forwardRef(
|
|
|
2062
2143
|
compactLevelCount: effectiveCompactLevels,
|
|
2063
2144
|
fillHeight,
|
|
2064
2145
|
newAskPrices,
|
|
2065
|
-
newBidPrices
|
|
2146
|
+
newBidPrices,
|
|
2147
|
+
spread,
|
|
2148
|
+
spreadPct,
|
|
2149
|
+
asksSectionRef
|
|
2066
2150
|
};
|
|
2067
2151
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2068
2152
|
Card,
|
|
2069
2153
|
{
|
|
2070
|
-
ref,
|
|
2154
|
+
ref: setRootRef,
|
|
2071
2155
|
className: cn(
|
|
2072
2156
|
"w-full max-w-[520px] overflow-hidden rounded-[12px] border border-white/10 bg-[#111111] text-white shadow-md flex flex-col",
|
|
2073
2157
|
isCompact && "max-w-none",
|
|
@@ -2081,6 +2165,170 @@ var Orderbook = React5__namespace.forwardRef(
|
|
|
2081
2165
|
}
|
|
2082
2166
|
);
|
|
2083
2167
|
Orderbook.displayName = "Orderbook";
|
|
2168
|
+
var MID_ROW_GOLD_STYLE = {
|
|
2169
|
+
backgroundImage: "linear-gradient(90deg, rgba(230,200,126,0.07), rgba(230,200,126,0.015))",
|
|
2170
|
+
borderTop: "1px solid rgba(230,200,126,0.22)",
|
|
2171
|
+
borderBottom: "1px solid rgba(230,200,126,0.22)"
|
|
2172
|
+
};
|
|
2173
|
+
function SpreadReadout({
|
|
2174
|
+
spread,
|
|
2175
|
+
spreadPct,
|
|
2176
|
+
precision: _precision,
|
|
2177
|
+
compact
|
|
2178
|
+
}) {
|
|
2179
|
+
if (spread == null || spreadPct == null) return null;
|
|
2180
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2181
|
+
"div",
|
|
2182
|
+
{
|
|
2183
|
+
style: {
|
|
2184
|
+
display: "flex",
|
|
2185
|
+
alignItems: "baseline",
|
|
2186
|
+
gap: 5,
|
|
2187
|
+
whiteSpace: "nowrap",
|
|
2188
|
+
lineHeight: 1
|
|
2189
|
+
},
|
|
2190
|
+
children: [
|
|
2191
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2192
|
+
"span",
|
|
2193
|
+
{
|
|
2194
|
+
style: {
|
|
2195
|
+
fontSize: compact ? "0.48rem" : "0.52rem",
|
|
2196
|
+
letterSpacing: "0.14em",
|
|
2197
|
+
textTransform: "uppercase",
|
|
2198
|
+
color: "rgba(255,255,255,0.45)",
|
|
2199
|
+
fontWeight: 600
|
|
2200
|
+
},
|
|
2201
|
+
children: "Spread"
|
|
2202
|
+
}
|
|
2203
|
+
),
|
|
2204
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2205
|
+
"span",
|
|
2206
|
+
{
|
|
2207
|
+
className: "tabular-nums",
|
|
2208
|
+
style: { fontSize: compact ? "0.56rem" : "0.62rem", color: "rgba(255,255,255,0.45)" },
|
|
2209
|
+
children: [
|
|
2210
|
+
spreadPct.toFixed(2),
|
|
2211
|
+
"%"
|
|
2212
|
+
]
|
|
2213
|
+
}
|
|
2214
|
+
)
|
|
2215
|
+
]
|
|
2216
|
+
}
|
|
2217
|
+
);
|
|
2218
|
+
}
|
|
2219
|
+
function useGlidePill() {
|
|
2220
|
+
const sectionRef = React9__namespace.useRef(null);
|
|
2221
|
+
const pillRef = React9__namespace.useRef(null);
|
|
2222
|
+
const priceSpanRef = React9__namespace.useRef(null);
|
|
2223
|
+
const boldedCellRef = React9__namespace.useRef(null);
|
|
2224
|
+
const [pillPrice, setPillPrice] = React9__namespace.useState(null);
|
|
2225
|
+
const motion = React9__namespace.useRef({ x: 0, targetX: 0, y: 0, targetY: 0, amp: 0, on: false, raf: 0 });
|
|
2226
|
+
const reducedMotion = typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
2227
|
+
const unbold = () => {
|
|
2228
|
+
if (boldedCellRef.current) {
|
|
2229
|
+
boldedCellRef.current.style.fontWeight = "";
|
|
2230
|
+
boldedCellRef.current = null;
|
|
2231
|
+
}
|
|
2232
|
+
};
|
|
2233
|
+
const frame = React9__namespace.useCallback(() => {
|
|
2234
|
+
const m = motion.current;
|
|
2235
|
+
const section = sectionRef.current;
|
|
2236
|
+
const pill = pillRef.current;
|
|
2237
|
+
m.raf = 0;
|
|
2238
|
+
if (!section || !pill) return;
|
|
2239
|
+
const k = reducedMotion ? 1 : 0.12;
|
|
2240
|
+
m.x += (m.targetX - m.x) * k;
|
|
2241
|
+
m.y += (m.targetY - m.y) * k;
|
|
2242
|
+
pill.style.transform = `translate(${m.x.toFixed(1)}px, ${m.y.toFixed(1)}px)`;
|
|
2243
|
+
const ampTarget = m.on && !reducedMotion ? 1 : 0;
|
|
2244
|
+
m.amp += (ampTarget - m.amp) * 0.14;
|
|
2245
|
+
const crest = m.y + pill.offsetHeight / 2;
|
|
2246
|
+
const rows = section.querySelectorAll("[data-depth-row]");
|
|
2247
|
+
for (const row of rows) {
|
|
2248
|
+
const rect = row.getBoundingClientRect();
|
|
2249
|
+
const center = rect.top + rect.height / 2;
|
|
2250
|
+
const d = center - crest;
|
|
2251
|
+
const inf = Math.exp(-(d * d) / (2 * 42 * 42)) * m.amp;
|
|
2252
|
+
const bar = row.querySelector("[data-depth-bar]");
|
|
2253
|
+
const priceCell = row.querySelector('[data-cell="price"]');
|
|
2254
|
+
if (inf < 0.01) {
|
|
2255
|
+
if (row.dataset.waved) {
|
|
2256
|
+
delete row.dataset.waved;
|
|
2257
|
+
row.style.zIndex = "";
|
|
2258
|
+
row.style.background = "";
|
|
2259
|
+
row.style.boxShadow = "";
|
|
2260
|
+
if (bar) bar.style.filter = "";
|
|
2261
|
+
if (priceCell) priceCell.style.transform = "";
|
|
2262
|
+
}
|
|
2263
|
+
continue;
|
|
2264
|
+
}
|
|
2265
|
+
row.dataset.waved = "1";
|
|
2266
|
+
row.style.zIndex = String(3 + Math.round(inf * 10));
|
|
2267
|
+
row.style.background = `rgba(19,20,24,${(inf * 0.92).toFixed(2)})`;
|
|
2268
|
+
row.style.boxShadow = `0 ${(inf * 3).toFixed(1)}px ${(inf * 12).toFixed(1)}px rgba(0,0,0,${(inf * 0.45).toFixed(2)})`;
|
|
2269
|
+
if (bar) bar.style.filter = `brightness(${(1 + inf * 0.45).toFixed(2)})`;
|
|
2270
|
+
if (priceCell) {
|
|
2271
|
+
priceCell.style.transformOrigin = "left center";
|
|
2272
|
+
priceCell.style.transform = `scale(${(1 + inf * 0.09).toFixed(3)})`;
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
if (m.on || Math.abs(m.targetX - m.x) + Math.abs(m.targetY - m.y) > 0.5 || m.amp > 5e-3) {
|
|
2276
|
+
m.raf = requestAnimationFrame(frame);
|
|
2277
|
+
}
|
|
2278
|
+
}, [reducedMotion]);
|
|
2279
|
+
const onMouseMove = React9__namespace.useCallback(
|
|
2280
|
+
(e) => {
|
|
2281
|
+
const target = e.target;
|
|
2282
|
+
const row = target?.closest?.("[data-depth-row]");
|
|
2283
|
+
const m = motion.current;
|
|
2284
|
+
if (!row || !row.dataset.price) {
|
|
2285
|
+
m.on = false;
|
|
2286
|
+
unbold();
|
|
2287
|
+
setPillPrice(null);
|
|
2288
|
+
return;
|
|
2289
|
+
}
|
|
2290
|
+
setPillPrice(row.dataset.price);
|
|
2291
|
+
const priceCell = row.querySelector('[data-cell="price"]');
|
|
2292
|
+
if (priceCell !== boldedCellRef.current) {
|
|
2293
|
+
unbold();
|
|
2294
|
+
if (priceCell) {
|
|
2295
|
+
priceCell.style.fontWeight = "600";
|
|
2296
|
+
boldedCellRef.current = priceCell;
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
const pill = pillRef.current;
|
|
2300
|
+
if (pill && priceCell) {
|
|
2301
|
+
const rowRect = row.getBoundingClientRect();
|
|
2302
|
+
const cellRect = priceCell.getBoundingClientRect();
|
|
2303
|
+
const priceOffset = priceSpanRef.current?.offsetLeft || 48;
|
|
2304
|
+
const nextX = cellRect.left - priceOffset;
|
|
2305
|
+
const nextY = rowRect.top + (rowRect.height || 28) / 2 - pill.offsetHeight / 2;
|
|
2306
|
+
if (!m.on) {
|
|
2307
|
+
m.x = nextX;
|
|
2308
|
+
m.y = nextY;
|
|
2309
|
+
}
|
|
2310
|
+
m.targetX = nextX;
|
|
2311
|
+
m.targetY = nextY;
|
|
2312
|
+
}
|
|
2313
|
+
m.on = true;
|
|
2314
|
+
if (!m.raf && typeof requestAnimationFrame === "function") {
|
|
2315
|
+
m.raf = requestAnimationFrame(frame);
|
|
2316
|
+
}
|
|
2317
|
+
},
|
|
2318
|
+
[frame]
|
|
2319
|
+
);
|
|
2320
|
+
const onMouseLeave = React9__namespace.useCallback(() => {
|
|
2321
|
+
motion.current.on = false;
|
|
2322
|
+
unbold();
|
|
2323
|
+
setPillPrice(null);
|
|
2324
|
+
}, []);
|
|
2325
|
+
React9__namespace.useEffect(() => {
|
|
2326
|
+
return () => {
|
|
2327
|
+
if (motion.current.raf) cancelAnimationFrame(motion.current.raf);
|
|
2328
|
+
};
|
|
2329
|
+
}, []);
|
|
2330
|
+
return { sectionRef, pillRef, priceSpanRef, pillPrice, onMouseMove, onMouseLeave };
|
|
2331
|
+
}
|
|
2084
2332
|
function SkeletonRow({ compact }) {
|
|
2085
2333
|
if (compact) {
|
|
2086
2334
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -2137,8 +2385,12 @@ function DesktopOrderbookLayout({
|
|
|
2137
2385
|
compactLevelCount: _compactLevelCount,
|
|
2138
2386
|
fillHeight,
|
|
2139
2387
|
newAskPrices,
|
|
2140
|
-
newBidPrices
|
|
2388
|
+
newBidPrices,
|
|
2389
|
+
spread,
|
|
2390
|
+
spreadPct,
|
|
2391
|
+
asksSectionRef
|
|
2141
2392
|
}) {
|
|
2393
|
+
const glide = useGlidePill();
|
|
2142
2394
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2143
2395
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-4 pt-4", children: [
|
|
2144
2396
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [
|
|
@@ -2148,8 +2400,8 @@ function DesktopOrderbookLayout({
|
|
|
2148
2400
|
type: "button",
|
|
2149
2401
|
onClick: () => handleTab("orderbook"),
|
|
2150
2402
|
className: cn(
|
|
2151
|
-
"relative pb-2 text-
|
|
2152
|
-
tab === "orderbook" ? "text-white" : "text-white/60"
|
|
2403
|
+
"relative pb-2 text-[0.8rem] font-medium transition-colors",
|
|
2404
|
+
tab === "orderbook" ? "text-white" : "text-white/60 hover:text-white/85"
|
|
2153
2405
|
),
|
|
2154
2406
|
children: [
|
|
2155
2407
|
"Orderbook",
|
|
@@ -2163,8 +2415,8 @@ function DesktopOrderbookLayout({
|
|
|
2163
2415
|
type: "button",
|
|
2164
2416
|
onClick: () => handleTab("trades"),
|
|
2165
2417
|
className: cn(
|
|
2166
|
-
"relative pb-2 text-
|
|
2167
|
-
tab === "trades" ? "text-white" : "text-white/60"
|
|
2418
|
+
"relative pb-2 text-[0.8rem] font-medium transition-colors",
|
|
2419
|
+
tab === "trades" ? "text-white" : "text-white/60 hover:text-white/85"
|
|
2168
2420
|
),
|
|
2169
2421
|
children: [
|
|
2170
2422
|
"Trades",
|
|
@@ -2212,100 +2464,170 @@ function DesktopOrderbookLayout({
|
|
|
2212
2464
|
)
|
|
2213
2465
|
] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-3", children: rightHeader })
|
|
2214
2466
|
] }),
|
|
2215
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
const tradeKey = getTradeKey(trade, i);
|
|
2228
|
-
const explorerUrl = trade.txHash && tradeExplorerUrl ? tradeExplorerUrl(trade.txHash) : trade.txHash ? `https://sepolia.etherscan.io/tx/${trade.txHash}` : void 0;
|
|
2229
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2230
|
-
TradeRow,
|
|
2467
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2468
|
+
"div",
|
|
2469
|
+
{
|
|
2470
|
+
ref: glide.sectionRef,
|
|
2471
|
+
"data-glide-section": true,
|
|
2472
|
+
className: "relative flex flex-1 flex-col min-h-0 px-4 pt-2",
|
|
2473
|
+
onMouseMove: glide.onMouseMove,
|
|
2474
|
+
onMouseLeave: glide.onMouseLeave,
|
|
2475
|
+
children: [
|
|
2476
|
+
typeof document !== "undefined" ? ReactDOM.createPortal(
|
|
2477
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2478
|
+
"div",
|
|
2231
2479
|
{
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2480
|
+
ref: glide.pillRef,
|
|
2481
|
+
"data-glide-pill": true,
|
|
2482
|
+
"data-visible": glide.pillPrice != null ? "true" : "false",
|
|
2483
|
+
style: {
|
|
2484
|
+
position: "fixed",
|
|
2485
|
+
left: 0,
|
|
2486
|
+
top: 0,
|
|
2487
|
+
zIndex: 60,
|
|
2488
|
+
pointerEvents: "none",
|
|
2489
|
+
display: "flex",
|
|
2490
|
+
alignItems: "center",
|
|
2491
|
+
gap: 8,
|
|
2492
|
+
whiteSpace: "nowrap",
|
|
2493
|
+
background: "#1b1d22",
|
|
2494
|
+
border: "1px solid rgba(255,255,255,0.14)",
|
|
2495
|
+
borderRadius: 999,
|
|
2496
|
+
padding: "4px 13px",
|
|
2497
|
+
boxShadow: "0 6px 20px rgba(0,0,0,0.55)",
|
|
2498
|
+
fontVariantNumeric: "tabular-nums",
|
|
2499
|
+
opacity: glide.pillPrice != null ? 1 : 0,
|
|
2500
|
+
transition: "opacity 160ms ease"
|
|
2501
|
+
},
|
|
2502
|
+
children: [
|
|
2503
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2504
|
+
"span",
|
|
2505
|
+
{
|
|
2506
|
+
style: {
|
|
2507
|
+
fontSize: "0.6rem",
|
|
2508
|
+
fontWeight: 600,
|
|
2509
|
+
letterSpacing: "0.08em",
|
|
2510
|
+
textTransform: "uppercase",
|
|
2511
|
+
color: "#E6C87E",
|
|
2512
|
+
lineHeight: 1
|
|
2513
|
+
},
|
|
2514
|
+
children: "Limit"
|
|
2515
|
+
}
|
|
2516
|
+
),
|
|
2517
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2518
|
+
"span",
|
|
2519
|
+
{
|
|
2520
|
+
ref: glide.priceSpanRef,
|
|
2521
|
+
className: "tabular-nums",
|
|
2522
|
+
style: { fontSize: "0.8rem", fontWeight: 600, color: "#fff" },
|
|
2523
|
+
children: glide.pillPrice ?? ""
|
|
2524
|
+
}
|
|
2525
|
+
)
|
|
2526
|
+
]
|
|
2527
|
+
}
|
|
2528
|
+
),
|
|
2529
|
+
document.body
|
|
2530
|
+
) : null,
|
|
2531
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-3 gap-3 px-3 pb-1.5 pt-1 text-xs text-white/60", children: [
|
|
2532
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: priceLabel }),
|
|
2533
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-right", children: amountLabel }),
|
|
2534
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-right", children: tab === "orderbook" ? "Total" : "Time" })
|
|
2535
|
+
] }),
|
|
2536
|
+
tab === "trades" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2537
|
+
"div",
|
|
2538
|
+
{
|
|
2539
|
+
className: "max-h-[380px] overflow-y-auto overflow-x-hidden",
|
|
2540
|
+
style: { scrollbarGutter: "stable" },
|
|
2541
|
+
children: isLoading && tradeFiltered.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { children: Array.from({ length: effectiveLevels }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(SkeletonRow, {}, `trade-skel-${i}`)) }) : tradeFiltered.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-10 text-center text-sm text-white/50", children: "No trades" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { children: tradeFiltered.map((trade, i) => {
|
|
2542
|
+
const tradeKey = getTradeKey(trade, i);
|
|
2543
|
+
const explorerUrl = trade.txHash && tradeExplorerUrl ? tradeExplorerUrl(trade.txHash) : trade.txHash ? `https://sepolia.etherscan.io/tx/${trade.txHash}` : void 0;
|
|
2544
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2545
|
+
TradeRow,
|
|
2546
|
+
{
|
|
2547
|
+
trade,
|
|
2548
|
+
tradeKey,
|
|
2549
|
+
precision,
|
|
2550
|
+
amountPrecision,
|
|
2551
|
+
seenTradeKeysRef,
|
|
2552
|
+
explorerUrl
|
|
2553
|
+
},
|
|
2554
|
+
tradeKey
|
|
2555
|
+
);
|
|
2556
|
+
}) })
|
|
2557
|
+
}
|
|
2558
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("flex flex-col min-h-0 overflow-hidden", fillHeight && "flex-1"), children: [
|
|
2559
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2560
|
+
"div",
|
|
2251
2561
|
{
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
midChangePercent >= 0 ? "+" : "",
|
|
2277
|
-
midChangePercent.toFixed(2),
|
|
2278
|
-
"%"
|
|
2279
|
-
] })
|
|
2280
|
-
] }) })
|
|
2281
|
-
}
|
|
2282
|
-
),
|
|
2283
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2284
|
-
"div",
|
|
2285
|
-
{
|
|
2286
|
-
className: cn("flex flex-col overflow-hidden", !fillHeight && "flex-shrink-0"),
|
|
2287
|
-
style: fillHeight ? { flex: 1, minHeight: 0 } : { height: `${sectionHeight}px` },
|
|
2288
|
-
children: isLoading ? Array.from({ length: effectiveLevels }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(SkeletonRow, {}, `bid-skel-${i}`)) : bidVisibleLevels.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center px-3 text-xs text-white/40", children: "No bids" }) : bidVisibleLevels.map((l, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2289
|
-
DepthRow,
|
|
2562
|
+
ref: asksSectionRef,
|
|
2563
|
+
className: cn("flex flex-col justify-end overflow-hidden", !fillHeight && "flex-shrink-0"),
|
|
2564
|
+
style: fillHeight ? { flex: 1, minHeight: 0 } : { height: `${sectionHeight}px` },
|
|
2565
|
+
children: isLoading ? Array.from({ length: effectiveLevels }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(SkeletonRow, {}, `ask-skel-${i}`)) : askVisibleLevels.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center px-3 text-xs text-white/40", children: "No asks" }) : askVisibleLevels.map((l, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2566
|
+
DepthRow,
|
|
2567
|
+
{
|
|
2568
|
+
side: "ask",
|
|
2569
|
+
price: l.price,
|
|
2570
|
+
amount: l.amount,
|
|
2571
|
+
cumDepth: askCumDepths[i],
|
|
2572
|
+
depthPct: askCumDepths[i] / askMaxCumDepth * 100,
|
|
2573
|
+
precision,
|
|
2574
|
+
amountPrecision,
|
|
2575
|
+
hasUserOrder: userAskPrices.has(l.price),
|
|
2576
|
+
onPriceClick,
|
|
2577
|
+
flashOnMount: newAskPrices.has(l.price),
|
|
2578
|
+
fillHeight
|
|
2579
|
+
},
|
|
2580
|
+
`ask-${l.price}`
|
|
2581
|
+
))
|
|
2582
|
+
}
|
|
2583
|
+
),
|
|
2584
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2585
|
+
"div",
|
|
2290
2586
|
{
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2587
|
+
className: "flex items-center justify-between gap-2 px-3 py-2 flex-shrink-0 overflow-hidden orderbook-mid-row",
|
|
2588
|
+
style: { ...MID_ROW_GOLD_STYLE, alignItems: "baseline" },
|
|
2589
|
+
children: [
|
|
2590
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("text-lg font-semibold tabular-nums whitespace-nowrap orderbook-mid-price", midClass), children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { width: 110, height: 20 }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2591
|
+
"$",
|
|
2592
|
+
formatNumber(midPrice, precision),
|
|
2593
|
+
midChangePercent == null ? null : /* @__PURE__ */ jsxRuntime.jsxs("span", { className: cn("ml-2 text-sm font-semibold tabular-nums", midClass), children: [
|
|
2594
|
+
midChangePercent >= 0 ? "+" : "",
|
|
2595
|
+
midChangePercent.toFixed(2),
|
|
2596
|
+
"%"
|
|
2597
|
+
] })
|
|
2598
|
+
] }) }),
|
|
2599
|
+
!isLoading && /* @__PURE__ */ jsxRuntime.jsx(SpreadReadout, { spread, spreadPct, precision })
|
|
2600
|
+
]
|
|
2601
|
+
}
|
|
2602
|
+
),
|
|
2603
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2604
|
+
"div",
|
|
2605
|
+
{
|
|
2606
|
+
className: cn("flex flex-col overflow-hidden", !fillHeight && "flex-shrink-0"),
|
|
2607
|
+
style: fillHeight ? { flex: 1, minHeight: 0 } : { height: `${sectionHeight}px` },
|
|
2608
|
+
children: isLoading ? Array.from({ length: effectiveLevels }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(SkeletonRow, {}, `bid-skel-${i}`)) : bidVisibleLevels.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center px-3 text-xs text-white/40", children: "No bids" }) : bidVisibleLevels.map((l, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2609
|
+
DepthRow,
|
|
2610
|
+
{
|
|
2611
|
+
side: "bid",
|
|
2612
|
+
price: l.price,
|
|
2613
|
+
amount: l.amount,
|
|
2614
|
+
cumDepth: bidCumDepths[i],
|
|
2615
|
+
depthPct: bidCumDepths[i] / bidMaxCumDepth * 100,
|
|
2616
|
+
precision,
|
|
2617
|
+
amountPrecision,
|
|
2618
|
+
hasUserOrder: userBidPrices.has(l.price),
|
|
2619
|
+
onPriceClick,
|
|
2620
|
+
flashOnMount: newBidPrices.has(l.price),
|
|
2621
|
+
fillHeight
|
|
2622
|
+
},
|
|
2623
|
+
`bid-${l.price}`
|
|
2624
|
+
))
|
|
2625
|
+
}
|
|
2626
|
+
)
|
|
2627
|
+
] })
|
|
2628
|
+
]
|
|
2629
|
+
}
|
|
2630
|
+
)
|
|
2309
2631
|
] });
|
|
2310
2632
|
}
|
|
2311
2633
|
function MobileOrderbookLayout({
|
|
@@ -2432,8 +2754,7 @@ function MobileOrderbookLayout({
|
|
|
2432
2754
|
gridTemplateColumns: "1.2fr 0.8fr",
|
|
2433
2755
|
padding: "0.3rem 0",
|
|
2434
2756
|
fontSize: "0.8rem",
|
|
2435
|
-
|
|
2436
|
-
borderBottom: "1px solid rgba(255,255,255,0.1)"
|
|
2757
|
+
...MID_ROW_GOLD_STYLE
|
|
2437
2758
|
},
|
|
2438
2759
|
children: [
|
|
2439
2760
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2515,7 +2836,7 @@ function MobileDepthRow({
|
|
|
2515
2836
|
flashOnMount
|
|
2516
2837
|
}) {
|
|
2517
2838
|
const isAsk = side === "ask";
|
|
2518
|
-
const rowRef =
|
|
2839
|
+
const rowRef = React9__namespace.useRef(null);
|
|
2519
2840
|
useAmountChangeFlash(rowRef, amount, side, flashOnMount);
|
|
2520
2841
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2521
2842
|
"div",
|
|
@@ -2541,16 +2862,12 @@ function MobileDepthRow({
|
|
|
2541
2862
|
left: "2px",
|
|
2542
2863
|
top: "50%",
|
|
2543
2864
|
transform: "translateY(-50%)",
|
|
2544
|
-
width: "6px",
|
|
2545
|
-
height: "6px",
|
|
2546
|
-
borderRadius: "50%",
|
|
2547
|
-
backgroundColor: "#C9A227",
|
|
2548
|
-
boxShadow: "0 0 6px rgba(201,162,39,0.7)",
|
|
2549
2865
|
zIndex: 2
|
|
2550
|
-
}
|
|
2866
|
+
},
|
|
2867
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(BreadSliceGlyph, {})
|
|
2551
2868
|
}
|
|
2552
2869
|
) : null,
|
|
2553
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", zIndex: 1, color: isAsk ? "#f6465d" : "#0ecb81", paddingLeft: hasUserOrder ? "
|
|
2870
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", zIndex: 1, color: isAsk ? "#f6465d" : "#0ecb81", paddingLeft: hasUserOrder ? "14px" : void 0 }, children: [
|
|
2554
2871
|
"$",
|
|
2555
2872
|
formatNumber(price, precision)
|
|
2556
2873
|
] }),
|
|
@@ -2560,12 +2877,12 @@ function MobileDepthRow({
|
|
|
2560
2877
|
{
|
|
2561
2878
|
style: {
|
|
2562
2879
|
position: "absolute",
|
|
2563
|
-
top:
|
|
2880
|
+
top: 1,
|
|
2564
2881
|
left: 0,
|
|
2565
|
-
bottom:
|
|
2882
|
+
bottom: 1,
|
|
2566
2883
|
width: `${clamp3(depthPct, 0, 100)}%`,
|
|
2567
|
-
|
|
2568
|
-
|
|
2884
|
+
backgroundImage: isAsk ? "linear-gradient(90deg, rgba(246,70,93,0.11), rgba(246,70,93,0.15))" : "linear-gradient(90deg, rgba(14,203,129,0.09), rgba(14,203,129,0.13))",
|
|
2885
|
+
borderRadius: "0 4px 4px 0",
|
|
2569
2886
|
zIndex: 0,
|
|
2570
2887
|
transition: "width 300ms ease-out"
|
|
2571
2888
|
}
|
|
@@ -2578,20 +2895,20 @@ function MobileDepthRow({
|
|
|
2578
2895
|
var isHlsManifest = (url) => /\.m3u8(\?|$)/i.test(url);
|
|
2579
2896
|
var HLS_MIME = "application/vnd.apple.mpegurl";
|
|
2580
2897
|
var MANIFEST_FAILURES = /* @__PURE__ */ new Set(["manifestLoadError", "manifestLoadTimeOut", "manifestParsingError"]);
|
|
2581
|
-
var HlsVideo =
|
|
2898
|
+
var HlsVideo = React9__namespace.forwardRef(
|
|
2582
2899
|
({ src, capLevelToPlayerSize = true, onError, ...videoProps }, ref) => {
|
|
2583
|
-
const videoRef =
|
|
2584
|
-
|
|
2900
|
+
const videoRef = React9__namespace.useRef(null);
|
|
2901
|
+
React9__namespace.useImperativeHandle(ref, () => videoRef.current, []);
|
|
2585
2902
|
const primary = typeof src === "string" ? src : src.src;
|
|
2586
2903
|
const fallbackSrc = typeof src === "string" ? void 0 : src.fallbackSrc;
|
|
2587
|
-
const [failedSrc, setFailedSrc] =
|
|
2904
|
+
const [failedSrc, setFailedSrc] = React9__namespace.useState(null);
|
|
2588
2905
|
const usingFallback = fallbackSrc != null && failedSrc === primary;
|
|
2589
2906
|
const activeSrc = usingFallback ? fallbackSrc : primary;
|
|
2590
|
-
const failPrimary =
|
|
2907
|
+
const failPrimary = React9__namespace.useCallback(() => {
|
|
2591
2908
|
if (fallbackSrc == null) return;
|
|
2592
2909
|
setFailedSrc(primary);
|
|
2593
2910
|
}, [fallbackSrc, primary]);
|
|
2594
|
-
|
|
2911
|
+
React9__namespace.useEffect(() => {
|
|
2595
2912
|
const video = videoRef.current;
|
|
2596
2913
|
if (!video || !activeSrc) return;
|
|
2597
2914
|
if (!isHlsManifest(activeSrc) || video.canPlayType(HLS_MIME)) {
|
|
@@ -2624,7 +2941,7 @@ var HlsVideo = React5__namespace.forwardRef(
|
|
|
2624
2941
|
hls?.destroy();
|
|
2625
2942
|
};
|
|
2626
2943
|
}, [activeSrc, capLevelToPlayerSize, failPrimary]);
|
|
2627
|
-
const handleError =
|
|
2944
|
+
const handleError = React9__namespace.useCallback(
|
|
2628
2945
|
(e) => {
|
|
2629
2946
|
if (!usingFallback) failPrimary();
|
|
2630
2947
|
onError?.(e);
|
|
@@ -2635,7 +2952,7 @@ var HlsVideo = React5__namespace.forwardRef(
|
|
|
2635
2952
|
}
|
|
2636
2953
|
);
|
|
2637
2954
|
HlsVideo.displayName = "HlsVideo";
|
|
2638
|
-
var PropertyTour =
|
|
2955
|
+
var PropertyTour = React9__namespace.forwardRef(
|
|
2639
2956
|
({
|
|
2640
2957
|
className,
|
|
2641
2958
|
title,
|
|
@@ -2648,8 +2965,8 @@ var PropertyTour = React5__namespace.forwardRef(
|
|
|
2648
2965
|
playsInline = true,
|
|
2649
2966
|
...props
|
|
2650
2967
|
}, ref) => {
|
|
2651
|
-
const videoRef =
|
|
2652
|
-
|
|
2968
|
+
const videoRef = React9__namespace.useRef(null);
|
|
2969
|
+
React9__namespace.useEffect(() => {
|
|
2653
2970
|
const video = videoRef.current;
|
|
2654
2971
|
if (!video) return;
|
|
2655
2972
|
const handleFullscreenChange = () => {
|
|
@@ -2757,8 +3074,50 @@ function isSafeUrl(url) {
|
|
|
2757
3074
|
return false;
|
|
2758
3075
|
}
|
|
2759
3076
|
}
|
|
3077
|
+
var BREAKING_WINDOW_HOURS = 6;
|
|
3078
|
+
var MAX_PINNED_BREAKING = 2;
|
|
3079
|
+
var BREAKING_IMPACT_FLOOR = 8;
|
|
3080
|
+
var breakingImpact = (it) => typeof it.impactScore === "number" && Number.isFinite(it.impactScore) ? it.impactScore : BREAKING_IMPACT_FLOOR;
|
|
3081
|
+
var dismissedBreakingKeys = /* @__PURE__ */ new Set();
|
|
3082
|
+
var __resetDismissedBreaking = () => dismissedBreakingKeys.clear();
|
|
3083
|
+
var breakingKey = (it) => String(it.id ?? it.title);
|
|
3084
|
+
var BREAKING_SWIPE_MIN_PX = 80;
|
|
2760
3085
|
var ITEMS_PER_PAGE = 5;
|
|
2761
3086
|
var ITEMS_PER_PAGE_MOBILE = 5;
|
|
3087
|
+
function formatAge(publishedAt, now) {
|
|
3088
|
+
const elapsed = Math.max(0, now - new Date(publishedAt).getTime());
|
|
3089
|
+
const secs = Math.floor(elapsed / 1e3);
|
|
3090
|
+
if (secs < 60) return `${secs}s ago`;
|
|
3091
|
+
const mins = Math.floor(secs / 60);
|
|
3092
|
+
if (mins < 60) return `${mins}m ago`;
|
|
3093
|
+
return `${Math.floor(mins / 60)}h ago`;
|
|
3094
|
+
}
|
|
3095
|
+
function rowAgeLabel(publishedAt, now) {
|
|
3096
|
+
if (!publishedAt) return null;
|
|
3097
|
+
const t = new Date(publishedAt).getTime();
|
|
3098
|
+
if (!Number.isFinite(t)) return null;
|
|
3099
|
+
const ageMs = Math.max(0, now - t);
|
|
3100
|
+
if (ageMs >= 24 * 36e5) return null;
|
|
3101
|
+
return { label: formatAge(publishedAt, now), fresh: ageMs < 36e5 };
|
|
3102
|
+
}
|
|
3103
|
+
function useTickingNow(active, youngestPublishedAt) {
|
|
3104
|
+
const [now, setNow] = React9__namespace.useState(() => Date.now());
|
|
3105
|
+
React9__namespace.useEffect(() => {
|
|
3106
|
+
if (!active || !youngestPublishedAt) return;
|
|
3107
|
+
let timer;
|
|
3108
|
+
const schedule = () => {
|
|
3109
|
+
const ageSecs = (Date.now() - new Date(youngestPublishedAt).getTime()) / 1e3;
|
|
3110
|
+
const delay = ageSecs < 60 ? 1e3 : ageSecs < 3600 ? 15e3 : 6e4;
|
|
3111
|
+
timer = setTimeout(() => {
|
|
3112
|
+
setNow(Date.now());
|
|
3113
|
+
schedule();
|
|
3114
|
+
}, delay);
|
|
3115
|
+
};
|
|
3116
|
+
schedule();
|
|
3117
|
+
return () => clearTimeout(timer);
|
|
3118
|
+
}, [active, youngestPublishedAt]);
|
|
3119
|
+
return now;
|
|
3120
|
+
}
|
|
2762
3121
|
var ensureAnimationsInjected = () => {
|
|
2763
3122
|
if (typeof document === "undefined") return;
|
|
2764
3123
|
if (document.getElementById("property-news-updates-animations")) return;
|
|
@@ -2781,6 +3140,110 @@ var ensureAnimationsInjected = () => {
|
|
|
2781
3140
|
from { opacity: 0; transform: translateY(12px) scale(0.98); }
|
|
2782
3141
|
to { opacity: 1; transform: translateY(0) scale(1); }
|
|
2783
3142
|
}
|
|
3143
|
+
/* The list scrolls but never shows a scrollbar (user call 2026-08-22: the
|
|
3144
|
+
grey track crowded the rows). Same treatment as the rest of the app. */
|
|
3145
|
+
.news-list-scroll { scrollbar-width: none; -ms-overflow-style: none; }
|
|
3146
|
+
.news-list-scroll::-webkit-scrollbar { display: none; width: 0; height: 0; }
|
|
3147
|
+
.property-news-breaking-dismiss {
|
|
3148
|
+
opacity: 0;
|
|
3149
|
+
pointer-events: none;
|
|
3150
|
+
transform: translateY(3px);
|
|
3151
|
+
transition: opacity 0.16s ease, transform 0.2s cubic-bezier(0.22, 1, 0.36, 1), color 0.15s ease;
|
|
3152
|
+
}
|
|
3153
|
+
.property-news-breaking-item:hover .property-news-breaking-dismiss,
|
|
3154
|
+
.property-news-breaking-item:focus-within .property-news-breaking-dismiss {
|
|
3155
|
+
opacity: 1;
|
|
3156
|
+
pointer-events: auto;
|
|
3157
|
+
transform: translateY(0);
|
|
3158
|
+
}
|
|
3159
|
+
.property-news-breaking-dismiss:hover,
|
|
3160
|
+
.property-news-breaking-dismiss:focus-visible {
|
|
3161
|
+
color: #fff !important;
|
|
3162
|
+
text-decoration: underline;
|
|
3163
|
+
text-underline-offset: 2px;
|
|
3164
|
+
outline: none;
|
|
3165
|
+
}
|
|
3166
|
+
.property-news-breaking-item[data-dismissing="true"] {
|
|
3167
|
+
pointer-events: none;
|
|
3168
|
+
animation: propertyNewsBreakingDismiss 0.22s cubic-bezier(0.4, 0, 1, 1) forwards;
|
|
3169
|
+
}
|
|
3170
|
+
.property-news-breaking-item[data-new-breaking="true"] {
|
|
3171
|
+
position: relative;
|
|
3172
|
+
overflow: hidden;
|
|
3173
|
+
animation: propertyNewsBreakingArrive 1.18s cubic-bezier(0.16, 1, 0.3, 1) both;
|
|
3174
|
+
}
|
|
3175
|
+
.property-news-breaking-item[data-new-breaking="true"]::after {
|
|
3176
|
+
content: "";
|
|
3177
|
+
position: absolute;
|
|
3178
|
+
inset: 0;
|
|
3179
|
+
z-index: 0;
|
|
3180
|
+
pointer-events: none;
|
|
3181
|
+
background: linear-gradient(100deg, transparent 8%, rgba(var(--color-accent-rgb, 230, 200, 126), 0.42) 48%, transparent 82%);
|
|
3182
|
+
transform: translateX(-120%);
|
|
3183
|
+
animation: propertyNewsBreakingSheen 1.18s 0.2s cubic-bezier(0.22, 1, 0.36, 1) both;
|
|
3184
|
+
}
|
|
3185
|
+
.property-news-breaking-item[data-new-breaking="true"] > span {
|
|
3186
|
+
position: relative;
|
|
3187
|
+
z-index: 1;
|
|
3188
|
+
}
|
|
3189
|
+
.property-news-breaking-item[data-new-breaking="true"][data-dismissing="true"] {
|
|
3190
|
+
animation: propertyNewsBreakingDismiss 0.22s cubic-bezier(0.4, 0, 1, 1) forwards;
|
|
3191
|
+
}
|
|
3192
|
+
@keyframes propertyNewsBreakingArrive {
|
|
3193
|
+
0% { opacity: 0; transform: translateY(-14px) scale(0.985); box-shadow: 0 0 0 rgba(var(--color-accent-rgb, 230, 200, 126), 0); filter: brightness(1); }
|
|
3194
|
+
24% { opacity: 1; transform: translateY(0) scale(1); box-shadow: 0 0 26px rgba(var(--color-accent-rgb, 230, 200, 126), 0.38); filter: brightness(1.16); }
|
|
3195
|
+
58% { box-shadow: 0 0 16px rgba(var(--color-accent-rgb, 230, 200, 126), 0.22); filter: brightness(1.06); }
|
|
3196
|
+
100% { opacity: 1; transform: translateY(0) scale(1); box-shadow: 0 0 0 rgba(var(--color-accent-rgb, 230, 200, 126), 0); }
|
|
3197
|
+
}
|
|
3198
|
+
@keyframes propertyNewsBreakingSheen {
|
|
3199
|
+
from { transform: translateX(-120%); }
|
|
3200
|
+
to { transform: translateX(120%); }
|
|
3201
|
+
}
|
|
3202
|
+
@keyframes propertyNewsBreakingDismiss {
|
|
3203
|
+
from { opacity: 1; transform: translateX(0); max-height: 96px; }
|
|
3204
|
+
to { opacity: 0; transform: translateX(24px); max-height: 0; padding-top: 0; padding-bottom: 0; }
|
|
3205
|
+
}
|
|
3206
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3207
|
+
.property-news-breaking-dismiss { transition: none; }
|
|
3208
|
+
.property-news-breaking-item[data-new-breaking="true"],
|
|
3209
|
+
.property-news-breaking-item[data-new-breaking="true"]::after { animation-duration: 1ms; animation-delay: 0ms; }
|
|
3210
|
+
.property-news-breaking-item[data-dismissing="true"] { animation-duration: 1ms; }
|
|
3211
|
+
}
|
|
3212
|
+
/* Asset badge: the wrapper hosts the INSTANT CSS tooltip (native title is
|
|
3213
|
+
too slow) and the hover affordance that says "the icon itself is the
|
|
3214
|
+
target" \u2014 the box scales up with a gold ring while the tooltip shows
|
|
3215
|
+
"Name (TICKER)" immediately. */
|
|
3216
|
+
.news-badge-wrap { position: relative; display: inline-flex; flex-shrink: 0; }
|
|
3217
|
+
.news-badge-wrap > .news-badge-box { transition: transform 0.15s ease, box-shadow 0.15s ease; }
|
|
3218
|
+
.news-badge-wrap:hover > .news-badge-box {
|
|
3219
|
+
transform: scale(1.08);
|
|
3220
|
+
box-shadow: 0 0 0 1px rgba(var(--color-accent-rgb, 230, 200, 126), 0.7);
|
|
3221
|
+
}
|
|
3222
|
+
/* Instant element tooltip (an ::after can't carry a coloured % change).
|
|
3223
|
+
PORTALED to document.body with position:fixed \u2014 as an absolute child it
|
|
3224
|
+
was clipped by the scrolling news list at the top/right edges. Anchored
|
|
3225
|
+
above the badge by default (translateY(-100%)), flipped below via
|
|
3226
|
+
data-tip-below when the badge sits too close to the viewport top. */
|
|
3227
|
+
.news-badge-tipbox {
|
|
3228
|
+
position: fixed;
|
|
3229
|
+
display: flex;
|
|
3230
|
+
transform: translate(-50%, -100%);
|
|
3231
|
+
flex-direction: column;
|
|
3232
|
+
align-items: center;
|
|
3233
|
+
gap: 2px;
|
|
3234
|
+
background: #1a1d24;
|
|
3235
|
+
color: #fff;
|
|
3236
|
+
font-size: 0.62rem;
|
|
3237
|
+
font-weight: 600;
|
|
3238
|
+
line-height: 1.2;
|
|
3239
|
+
padding: 0.32rem 0.55rem;
|
|
3240
|
+
border-radius: 5px;
|
|
3241
|
+
border: 1px solid rgba(255, 255, 255, 0.12);
|
|
3242
|
+
white-space: nowrap;
|
|
3243
|
+
z-index: 10060;
|
|
3244
|
+
pointer-events: none;
|
|
3245
|
+
}
|
|
3246
|
+
.news-badge-tipbox[data-tip-below="true"] { transform: translate(-50%, 0); }
|
|
2784
3247
|
`;
|
|
2785
3248
|
document.head.appendChild(style);
|
|
2786
3249
|
};
|
|
@@ -2817,7 +3280,8 @@ var formatDateShort = (value) => {
|
|
|
2817
3280
|
if (diffS < 3600) return `${Math.floor(diffS / 60)}m ago`;
|
|
2818
3281
|
if (diffS < 86400) return `${Math.floor(diffS / 3600)}h ago`;
|
|
2819
3282
|
const time = d.toLocaleTimeString(void 0, { hour: "numeric", minute: "2-digit", hour12: true });
|
|
2820
|
-
|
|
3283
|
+
const date = d.toLocaleDateString(void 0, { month: "short", day: "numeric" });
|
|
3284
|
+
return `${date} \xB7 ${time}`;
|
|
2821
3285
|
};
|
|
2822
3286
|
var formatTimeAgo = (timestamp) => {
|
|
2823
3287
|
const diff = (Date.now() - new Date(timestamp).getTime()) / 1e3;
|
|
@@ -2831,10 +3295,66 @@ function getSentimentInfo(score) {
|
|
|
2831
3295
|
if (score < -0.15) return { arrow: "\u25BC", label: "Bearish", color: "#f6465d" };
|
|
2832
3296
|
return { arrow: "", label: "Neutral", color: "#848e9c" };
|
|
2833
3297
|
}
|
|
3298
|
+
function SentimentBar({ score, color }) {
|
|
3299
|
+
const clamped = Math.max(-1, Math.min(1, score));
|
|
3300
|
+
const widthPct = Math.abs(clamped) * 50;
|
|
3301
|
+
const direction = clamped > 0 ? "positive" : clamped < 0 ? "negative" : "neutral";
|
|
3302
|
+
const word = clamped > 0.15 ? "bullish" : clamped < -0.15 ? "bearish" : "neutral";
|
|
3303
|
+
const signed = `${clamped >= 0 ? "+" : "\u2212"}${Math.abs(clamped).toFixed(2)}`;
|
|
3304
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3305
|
+
"span",
|
|
3306
|
+
{
|
|
3307
|
+
"data-testid": "sentiment-bar",
|
|
3308
|
+
"data-score": String(score),
|
|
3309
|
+
role: "img",
|
|
3310
|
+
"aria-label": `Sentiment: ${word}, ${signed} of +1`,
|
|
3311
|
+
style: {
|
|
3312
|
+
position: "relative",
|
|
3313
|
+
display: "inline-block",
|
|
3314
|
+
width: "36px",
|
|
3315
|
+
height: "3px",
|
|
3316
|
+
borderRadius: "2px",
|
|
3317
|
+
background: "rgba(255,255,255,0.10)",
|
|
3318
|
+
flexShrink: 0
|
|
3319
|
+
},
|
|
3320
|
+
children: [
|
|
3321
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3322
|
+
"span",
|
|
3323
|
+
{
|
|
3324
|
+
style: {
|
|
3325
|
+
position: "absolute",
|
|
3326
|
+
left: "50%",
|
|
3327
|
+
top: "-1px",
|
|
3328
|
+
width: "1px",
|
|
3329
|
+
height: "5px",
|
|
3330
|
+
background: "rgba(255,255,255,0.28)"
|
|
3331
|
+
}
|
|
3332
|
+
}
|
|
3333
|
+
),
|
|
3334
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3335
|
+
"span",
|
|
3336
|
+
{
|
|
3337
|
+
"data-testid": "sentiment-fill",
|
|
3338
|
+
"data-direction": direction,
|
|
3339
|
+
style: {
|
|
3340
|
+
position: "absolute",
|
|
3341
|
+
top: 0,
|
|
3342
|
+
height: "100%",
|
|
3343
|
+
borderRadius: "2px",
|
|
3344
|
+
background: color,
|
|
3345
|
+
width: `${widthPct}%`,
|
|
3346
|
+
left: `${clamped >= 0 ? 50 : 50 - widthPct}%`
|
|
3347
|
+
}
|
|
3348
|
+
}
|
|
3349
|
+
)
|
|
3350
|
+
]
|
|
3351
|
+
}
|
|
3352
|
+
);
|
|
3353
|
+
}
|
|
2834
3354
|
function NewsArticleModal({ item, onClose }) {
|
|
2835
3355
|
const sentimentInfo = getSentimentInfo(item.sentimentScore);
|
|
2836
3356
|
const catStyle = categoryStyles[item.type] ?? categoryStyles.market;
|
|
2837
|
-
|
|
3357
|
+
React9__namespace.useEffect(() => {
|
|
2838
3358
|
const handler = (e) => {
|
|
2839
3359
|
if (e.key === "Escape") onClose();
|
|
2840
3360
|
};
|
|
@@ -2895,7 +3415,7 @@ function NewsArticleModal({ item, onClose }) {
|
|
|
2895
3415
|
children: /* @__PURE__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "16", height: "16", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" }) })
|
|
2896
3416
|
}
|
|
2897
3417
|
) }),
|
|
2898
|
-
sentimentInfo && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginBottom: "0.6rem" }, children: /* @__PURE__ */ jsxRuntime.
|
|
3418
|
+
sentimentInfo && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginBottom: "0.6rem" }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2899
3419
|
"span",
|
|
2900
3420
|
{
|
|
2901
3421
|
style: {
|
|
@@ -2910,10 +3430,11 @@ function NewsArticleModal({ item, onClose }) {
|
|
|
2910
3430
|
background: `${sentimentInfo.color}1a`,
|
|
2911
3431
|
border: `1px solid ${sentimentInfo.color}40`
|
|
2912
3432
|
},
|
|
2913
|
-
children: [
|
|
3433
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: "0.3rem" }, children: [
|
|
2914
3434
|
sentimentInfo.arrow ? `${sentimentInfo.arrow} ` : "",
|
|
2915
|
-
sentimentInfo.label
|
|
2916
|
-
|
|
3435
|
+
sentimentInfo.label,
|
|
3436
|
+
item.sentimentScore != null && /* @__PURE__ */ jsxRuntime.jsx(SentimentBar, { score: item.sentimentScore, color: sentimentInfo.color })
|
|
3437
|
+
] })
|
|
2917
3438
|
}
|
|
2918
3439
|
) }),
|
|
2919
3440
|
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: { fontSize: "1rem", fontWeight: 600, color: "#fff", lineHeight: 1.5, margin: "0 0 1rem" }, children: item.title }),
|
|
@@ -2928,7 +3449,13 @@ function NewsArticleModal({ item, onClose }) {
|
|
|
2928
3449
|
fontSize: "0.65rem",
|
|
2929
3450
|
fontWeight: 500,
|
|
2930
3451
|
background: "rgba(255,255,255,0.07)",
|
|
2931
|
-
color: "#848e9c"
|
|
3452
|
+
color: "#848e9c",
|
|
3453
|
+
// Truncation guard moved here with the source itself — a long
|
|
3454
|
+
// publication name would otherwise stretch the meta row.
|
|
3455
|
+
maxWidth: "120px",
|
|
3456
|
+
overflow: "hidden",
|
|
3457
|
+
textOverflow: "ellipsis",
|
|
3458
|
+
whiteSpace: "nowrap"
|
|
2932
3459
|
},
|
|
2933
3460
|
children: item.source
|
|
2934
3461
|
}
|
|
@@ -2990,7 +3517,7 @@ function NewsArticleModal({ item, onClose }) {
|
|
|
2990
3517
|
}
|
|
2991
3518
|
return modal;
|
|
2992
3519
|
}
|
|
2993
|
-
var PropertyNewsUpdates =
|
|
3520
|
+
var PropertyNewsUpdates = React9__namespace.forwardRef(
|
|
2994
3521
|
({
|
|
2995
3522
|
className,
|
|
2996
3523
|
heading,
|
|
@@ -3004,49 +3531,120 @@ var PropertyNewsUpdates = React5__namespace.forwardRef(
|
|
|
3004
3531
|
viewAllLabel = "View All News",
|
|
3005
3532
|
connectionStatus = "live",
|
|
3006
3533
|
disablePagination = false,
|
|
3534
|
+
hideHeading = false,
|
|
3535
|
+
hideCategoryTabs = false,
|
|
3536
|
+
category,
|
|
3537
|
+
onCategoryChange,
|
|
3538
|
+
pinBreaking = false,
|
|
3539
|
+
onAssetOpen,
|
|
3540
|
+
showAssetBadges = true,
|
|
3007
3541
|
...props
|
|
3008
3542
|
}, ref) => {
|
|
3009
3543
|
const isPurchaseVariant = variant === "purchases";
|
|
3010
3544
|
const isHomeVariant = variant === "home";
|
|
3011
3545
|
const resolvedHeading = heading ?? (isPurchaseVariant ? "Live Purchases" : "Property News & Headlines");
|
|
3012
|
-
const
|
|
3013
|
-
const [
|
|
3546
|
+
const headerTrailing = Boolean(isHomeVariant ? viewAllHref : !isPurchaseVariant);
|
|
3547
|
+
const [homeTabInternal, setHomeTabInternal] = React9__namespace.useState("all");
|
|
3548
|
+
const homeTab = category ?? homeTabInternal;
|
|
3549
|
+
const setHomeTab = React9__namespace.useCallback(
|
|
3550
|
+
(next) => {
|
|
3551
|
+
setHomeTabInternal(next);
|
|
3552
|
+
onCategoryChange?.(next);
|
|
3553
|
+
},
|
|
3554
|
+
[onCategoryChange]
|
|
3555
|
+
);
|
|
3556
|
+
const [homePage, setHomePage] = React9__namespace.useState(0);
|
|
3014
3557
|
const purchaseItems = purchasesProp ?? [];
|
|
3015
|
-
const [page, setPage] =
|
|
3016
|
-
const [selectedItem, setSelectedItem] =
|
|
3017
|
-
const [isMobile, setIsMobile] =
|
|
3558
|
+
const [page, setPage] = React9__namespace.useState(0);
|
|
3559
|
+
const [selectedItem, setSelectedItem] = React9__namespace.useState(null);
|
|
3560
|
+
const [isMobile, setIsMobile] = React9__namespace.useState(
|
|
3018
3561
|
() => typeof window !== "undefined" && window.innerWidth <= 768
|
|
3019
3562
|
);
|
|
3020
|
-
|
|
3563
|
+
React9__namespace.useEffect(() => {
|
|
3021
3564
|
const check = () => setIsMobile(window.innerWidth <= 768);
|
|
3022
3565
|
window.addEventListener("resize", check);
|
|
3023
3566
|
return () => window.removeEventListener("resize", check);
|
|
3024
3567
|
}, []);
|
|
3025
3568
|
const homePageSize = isMobile ? ITEMS_PER_PAGE_MOBILE : ITEMS_PER_PAGE;
|
|
3026
|
-
|
|
3569
|
+
React9__namespace.useEffect(() => {
|
|
3027
3570
|
ensureAnimationsInjected();
|
|
3028
3571
|
}, []);
|
|
3029
|
-
|
|
3572
|
+
React9__namespace.useEffect(() => {
|
|
3030
3573
|
setHomePage(0);
|
|
3031
3574
|
}, [homeTab]);
|
|
3032
|
-
const
|
|
3033
|
-
|
|
3034
|
-
() =>
|
|
3035
|
-
|
|
3575
|
+
const breakingCandidates = React9__namespace.useMemo(() => {
|
|
3576
|
+
if (!pinBreaking || !Array.isArray(items)) return [];
|
|
3577
|
+
return items.filter((it) => it.breaking && it.publishedAt && Number.isFinite(new Date(it.publishedAt).getTime())).sort((a, b) => new Date(b.publishedAt).getTime() - new Date(a.publishedAt).getTime());
|
|
3578
|
+
}, [pinBreaking, items]);
|
|
3579
|
+
const breakingNow = useTickingNow(
|
|
3580
|
+
breakingCandidates.length > 0,
|
|
3581
|
+
breakingCandidates[0]?.publishedAt ?? null
|
|
3582
|
+
);
|
|
3583
|
+
const breakingEligible = React9__namespace.useMemo(() => {
|
|
3584
|
+
const cutoff = breakingNow - BREAKING_WINDOW_HOURS * 36e5;
|
|
3585
|
+
return breakingCandidates.filter((it) => {
|
|
3586
|
+
if (new Date(it.publishedAt).getTime() < cutoff) return false;
|
|
3587
|
+
return homeTab === "all" || it.type === homeTab;
|
|
3588
|
+
});
|
|
3589
|
+
}, [breakingCandidates, breakingNow, homeTab]);
|
|
3590
|
+
const [dismissedVersion, setDismissedVersion] = React9__namespace.useState(0);
|
|
3591
|
+
const [dismissingBreakingKey, setDismissingBreakingKey] = React9__namespace.useState(null);
|
|
3592
|
+
const [breakingSwipe, setBreakingSwipe] = React9__namespace.useState(null);
|
|
3593
|
+
const breakingSwipeStartXRef = React9__namespace.useRef(0);
|
|
3594
|
+
const suppressBreakingClickRef = React9__namespace.useRef(false);
|
|
3595
|
+
const breakingSwipeActiveKeyRef = React9__namespace.useRef(null);
|
|
3596
|
+
const breakingSwipeMaxRef = React9__namespace.useRef(0);
|
|
3597
|
+
const [badgeTip, setBadgeTip] = React9__namespace.useState(null);
|
|
3598
|
+
const BADGE_TIP_GAP = 7;
|
|
3599
|
+
const BADGE_TIP_FLIP_PX = 56;
|
|
3600
|
+
const showBadgeTip = (el, asset) => {
|
|
3601
|
+
const rect = el.getBoundingClientRect();
|
|
3602
|
+
const below = rect.top < BADGE_TIP_FLIP_PX;
|
|
3603
|
+
setBadgeTip({
|
|
3604
|
+
asset,
|
|
3605
|
+
left: rect.left + rect.width / 2,
|
|
3606
|
+
top: below ? rect.bottom + BADGE_TIP_GAP : rect.top - BADGE_TIP_GAP,
|
|
3607
|
+
below
|
|
3608
|
+
});
|
|
3609
|
+
};
|
|
3610
|
+
const pinnedBreaking = React9__namespace.useMemo(() => {
|
|
3611
|
+
return breakingEligible.filter((it) => !dismissedBreakingKeys.has(breakingKey(it))).sort((a, b) => breakingImpact(b) - breakingImpact(a)).slice(0, MAX_PINNED_BREAKING);
|
|
3612
|
+
}, [breakingEligible, dismissedVersion]);
|
|
3613
|
+
const breakingRowKeys = React9__namespace.useMemo(
|
|
3614
|
+
() => new Set(breakingEligible.map(breakingKey)),
|
|
3615
|
+
[breakingEligible]
|
|
3036
3616
|
);
|
|
3037
|
-
|
|
3617
|
+
const listItems = React9__namespace.useMemo(() => {
|
|
3618
|
+
if (pinnedBreaking.length === 0) return items;
|
|
3619
|
+
const pinned = new Set(pinnedBreaking);
|
|
3620
|
+
return items.filter((it) => !pinned.has(it));
|
|
3621
|
+
}, [items, pinnedBreaking]);
|
|
3622
|
+
const hasItems = Array.isArray(listItems) && listItems.length > 0;
|
|
3623
|
+
const totalPages = React9__namespace.useMemo(
|
|
3624
|
+
() => hasItems ? Math.max(1, Math.ceil(listItems.length / ITEMS_PER_PAGE)) : 1,
|
|
3625
|
+
[hasItems, listItems.length]
|
|
3626
|
+
);
|
|
3627
|
+
React9__namespace.useEffect(() => {
|
|
3038
3628
|
setPage((prev) => Math.min(prev, totalPages - 1));
|
|
3039
3629
|
}, [totalPages]);
|
|
3040
|
-
const paginatedItems = hasItems ?
|
|
3041
|
-
const homeFilteredItems =
|
|
3042
|
-
() => hasItems ?
|
|
3043
|
-
[hasItems,
|
|
3630
|
+
const paginatedItems = hasItems ? listItems.slice(page * ITEMS_PER_PAGE, page * ITEMS_PER_PAGE + ITEMS_PER_PAGE) : [];
|
|
3631
|
+
const homeFilteredItems = React9__namespace.useMemo(
|
|
3632
|
+
() => hasItems ? listItems.filter((it) => homeTab === "all" || it.type === homeTab) : [],
|
|
3633
|
+
[hasItems, listItems, homeTab]
|
|
3044
3634
|
);
|
|
3045
|
-
const homeTotalPages =
|
|
3635
|
+
const homeTotalPages = React9__namespace.useMemo(
|
|
3046
3636
|
() => Math.max(1, Math.ceil(homeFilteredItems.length / homePageSize)),
|
|
3047
3637
|
[homeFilteredItems.length, homePageSize]
|
|
3048
3638
|
);
|
|
3049
3639
|
const homePaginatedItems = disablePagination ? homeFilteredItems : homeFilteredItems.slice(homePage * homePageSize, (homePage + 1) * homePageSize);
|
|
3640
|
+
const youngestRowPublishedAt = React9__namespace.useMemo(() => {
|
|
3641
|
+
let youngest = null;
|
|
3642
|
+
for (const it of homePaginatedItems) {
|
|
3643
|
+
if (it.publishedAt && (!youngest || it.publishedAt > youngest)) youngest = it.publishedAt;
|
|
3644
|
+
}
|
|
3645
|
+
return youngest;
|
|
3646
|
+
}, [homePaginatedItems]);
|
|
3647
|
+
const rowNow = useTickingNow(isHomeVariant && youngestRowPublishedAt != null, youngestRowPublishedAt);
|
|
3050
3648
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3051
3649
|
"div",
|
|
3052
3650
|
{
|
|
@@ -3057,8 +3655,8 @@ var PropertyNewsUpdates = React5__namespace.forwardRef(
|
|
|
3057
3655
|
),
|
|
3058
3656
|
...props,
|
|
3059
3657
|
children: [
|
|
3060
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", style: isHomeVariant ? { marginBottom: "0.5rem", minHeight: isMobile ? "32px" : "24px", gap: isMobile ? "0.5rem" : "1rem" } : void 0, children: [
|
|
3061
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
3658
|
+
(!hideHeading || headerTrailing) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", style: isHomeVariant ? { marginBottom: "0.5rem", minHeight: isMobile ? "32px" : "24px", gap: isMobile ? "0.5rem" : "1rem", justifyContent: hideHeading ? "flex-end" : void 0 } : hideHeading ? { justifyContent: "flex-end" } : void 0, children: [
|
|
3659
|
+
!hideHeading && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
3062
3660
|
isHomeVariant && /* @__PURE__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: isMobile ? 14 : 16, height: isMobile ? 14 : 16, viewBox: "0 0 24 24", fill: "currentColor", style: { color: "var(--color-text, #fff)", flexShrink: 0, display: "block" }, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-5 14H4v-4h11v4zm0-5H4V9h11v4zm5 5h-4V9h4v9z" }) }),
|
|
3063
3661
|
isPurchaseVariant && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { display: "inline-block", width: "8px", height: "8px", borderRadius: "50%", backgroundColor: "#0ecb81", boxShadow: "0 0 8px rgba(14,203,129,0.8)", animation: "propertyNewsPulse 1.5s infinite" } }),
|
|
3064
3662
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
@@ -3077,7 +3675,7 @@ var PropertyNewsUpdates = React5__namespace.forwardRef(
|
|
|
3077
3675
|
"LIVE"
|
|
3078
3676
|
] }) : null
|
|
3079
3677
|
] }),
|
|
3080
|
-
isHomeVariant && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", gap: isMobile ? "1rem" : "1.5rem", marginTop: "0.25rem", marginBottom: "1rem", borderBottom: "1px solid rgba(255, 255, 255, 0.08)", paddingBottom: "0.5rem" }, children: ["all", "property", "market"].map((tab) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3678
|
+
isHomeVariant && !hideCategoryTabs && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", gap: isMobile ? "1rem" : "1.5rem", marginTop: "0.25rem", marginBottom: "1rem", borderBottom: "1px solid rgba(255, 255, 255, 0.08)", paddingBottom: "0.5rem" }, children: ["all", "property", "market"].map((tab) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3081
3679
|
"button",
|
|
3082
3680
|
{
|
|
3083
3681
|
type: "button",
|
|
@@ -3098,10 +3696,229 @@ var PropertyNewsUpdates = React5__namespace.forwardRef(
|
|
|
3098
3696
|
},
|
|
3099
3697
|
tab
|
|
3100
3698
|
)) }),
|
|
3699
|
+
pinnedBreaking.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3700
|
+
"div",
|
|
3701
|
+
{
|
|
3702
|
+
"data-testid": "news-breaking",
|
|
3703
|
+
style: {
|
|
3704
|
+
display: "flex",
|
|
3705
|
+
flexDirection: "column",
|
|
3706
|
+
marginBottom: "0.5rem",
|
|
3707
|
+
// Clips the row while it's being swiped out to the right. The gold
|
|
3708
|
+
// band styling lives on each ITEM below, not here — the whole band
|
|
3709
|
+
// (rule, wash, hairlines) must travel with a swipe, not just the
|
|
3710
|
+
// text inside it (user bug report, 2026-08-20).
|
|
3711
|
+
overflow: "hidden"
|
|
3712
|
+
},
|
|
3713
|
+
children: pinnedBreaking.map((item, i) => {
|
|
3714
|
+
const sentiment = getSentimentInfo(item.sentimentScore);
|
|
3715
|
+
const k = breakingKey(item);
|
|
3716
|
+
const dx = breakingSwipe?.key === k ? breakingSwipe.dx : 0;
|
|
3717
|
+
const finishDismiss = () => {
|
|
3718
|
+
dismissedBreakingKeys.add(k);
|
|
3719
|
+
setDismissingBreakingKey(null);
|
|
3720
|
+
setDismissedVersion((v) => v + 1);
|
|
3721
|
+
};
|
|
3722
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3723
|
+
"button",
|
|
3724
|
+
{
|
|
3725
|
+
type: "button",
|
|
3726
|
+
className: "property-news-breaking-item",
|
|
3727
|
+
"data-new-breaking": item.isNew ? "true" : void 0,
|
|
3728
|
+
"data-dismissing": dismissingBreakingKey === k ? "true" : void 0,
|
|
3729
|
+
"data-testid": "news-breaking-item",
|
|
3730
|
+
onAnimationEnd: (e) => {
|
|
3731
|
+
if (e.animationName === "propertyNewsBreakingDismiss" && dismissingBreakingKey === k) {
|
|
3732
|
+
finishDismiss();
|
|
3733
|
+
}
|
|
3734
|
+
},
|
|
3735
|
+
onClick: () => {
|
|
3736
|
+
if (suppressBreakingClickRef.current) {
|
|
3737
|
+
suppressBreakingClickRef.current = false;
|
|
3738
|
+
return;
|
|
3739
|
+
}
|
|
3740
|
+
setSelectedItem(item);
|
|
3741
|
+
},
|
|
3742
|
+
onPointerDown: (e) => {
|
|
3743
|
+
breakingSwipeStartXRef.current = e.clientX;
|
|
3744
|
+
breakingSwipeActiveKeyRef.current = k;
|
|
3745
|
+
breakingSwipeMaxRef.current = 0;
|
|
3746
|
+
setBreakingSwipe({ key: k, dx: 0 });
|
|
3747
|
+
try {
|
|
3748
|
+
e.currentTarget.setPointerCapture?.(e.pointerId);
|
|
3749
|
+
} catch {
|
|
3750
|
+
}
|
|
3751
|
+
},
|
|
3752
|
+
onPointerMove: (e) => {
|
|
3753
|
+
if (breakingSwipeActiveKeyRef.current !== k) return;
|
|
3754
|
+
const delta = e.clientX - breakingSwipeStartXRef.current;
|
|
3755
|
+
breakingSwipeMaxRef.current = Math.max(breakingSwipeMaxRef.current, Math.abs(delta));
|
|
3756
|
+
setBreakingSwipe({ key: k, dx: Math.max(0, delta) });
|
|
3757
|
+
},
|
|
3758
|
+
onPointerUp: (e) => {
|
|
3759
|
+
if (breakingSwipeActiveKeyRef.current !== k) return;
|
|
3760
|
+
breakingSwipeActiveKeyRef.current = null;
|
|
3761
|
+
if (breakingSwipeMaxRef.current > 6) suppressBreakingClickRef.current = true;
|
|
3762
|
+
setBreakingSwipe(null);
|
|
3763
|
+
const drag = Math.max(0, e.clientX - breakingSwipeStartXRef.current);
|
|
3764
|
+
const width = e.currentTarget.offsetWidth || 0;
|
|
3765
|
+
if (drag >= Math.max(BREAKING_SWIPE_MIN_PX, width * 0.3)) {
|
|
3766
|
+
dismissedBreakingKeys.add(k);
|
|
3767
|
+
setDismissedVersion((v) => v + 1);
|
|
3768
|
+
}
|
|
3769
|
+
},
|
|
3770
|
+
onPointerCancel: () => {
|
|
3771
|
+
breakingSwipeActiveKeyRef.current = null;
|
|
3772
|
+
setBreakingSwipe(null);
|
|
3773
|
+
},
|
|
3774
|
+
style: {
|
|
3775
|
+
display: "block",
|
|
3776
|
+
width: "100%",
|
|
3777
|
+
padding: "0.4rem 0 0.5rem 0.6rem",
|
|
3778
|
+
textAlign: "left",
|
|
3779
|
+
background: "none",
|
|
3780
|
+
border: "none",
|
|
3781
|
+
cursor: "pointer",
|
|
3782
|
+
minWidth: 0,
|
|
3783
|
+
// Highlighted on purpose (user call, 2026-08-20): BREAKING is
|
|
3784
|
+
// the one thing on this page allowed to shout. The gold rule,
|
|
3785
|
+
// hairlines and wash live HERE on the item — the whole band
|
|
3786
|
+
// travels when the item is swiped away.
|
|
3787
|
+
borderLeft: "3px solid var(--color-accent, #E6C87E)",
|
|
3788
|
+
borderTop: "1px solid rgba(var(--color-accent-rgb, 230, 200, 126), 0.28)",
|
|
3789
|
+
borderBottom: "1px solid rgba(var(--color-accent-rgb, 230, 200, 126), 0.28)",
|
|
3790
|
+
backgroundImage: "linear-gradient(90deg, rgba(var(--color-accent-rgb, 230, 200, 126), 0.16) 0%, rgba(var(--color-accent-rgb, 230, 200, 126), 0.06) 45%, rgba(var(--color-accent-rgb, 230, 200, 126), 0.02) 100%)",
|
|
3791
|
+
// Horizontal drags are ours; vertical stays with the scroll.
|
|
3792
|
+
touchAction: "pan-y",
|
|
3793
|
+
transform: dx > 0 ? `translateX(${dx}px)` : void 0,
|
|
3794
|
+
opacity: dx > 0 ? Math.max(0.3, 1 - dx / 320) : 1,
|
|
3795
|
+
transition: dx > 0 ? "none" : "transform 0.15s ease, opacity 0.15s ease"
|
|
3796
|
+
},
|
|
3797
|
+
children: [
|
|
3798
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3799
|
+
"span",
|
|
3800
|
+
{
|
|
3801
|
+
"data-testid": "news-breaking-title",
|
|
3802
|
+
style: {
|
|
3803
|
+
display: "-webkit-box",
|
|
3804
|
+
WebkitBoxOrient: "vertical",
|
|
3805
|
+
WebkitLineClamp: 2,
|
|
3806
|
+
fontSize: "0.8rem",
|
|
3807
|
+
fontWeight: 600,
|
|
3808
|
+
color: "#fff",
|
|
3809
|
+
lineHeight: 1.35,
|
|
3810
|
+
overflow: "hidden",
|
|
3811
|
+
overflowWrap: "anywhere"
|
|
3812
|
+
},
|
|
3813
|
+
children: item.title
|
|
3814
|
+
}
|
|
3815
|
+
),
|
|
3816
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3817
|
+
"span",
|
|
3818
|
+
{
|
|
3819
|
+
style: {
|
|
3820
|
+
display: "flex",
|
|
3821
|
+
alignItems: "center",
|
|
3822
|
+
gap: "0.45rem",
|
|
3823
|
+
marginTop: "0.2rem"
|
|
3824
|
+
},
|
|
3825
|
+
children: [
|
|
3826
|
+
(() => {
|
|
3827
|
+
const age = rowAgeLabel(item.publishedAt, breakingNow);
|
|
3828
|
+
return age ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3829
|
+
"span",
|
|
3830
|
+
{
|
|
3831
|
+
"data-age-fresh": age.fresh ? "true" : void 0,
|
|
3832
|
+
style: {
|
|
3833
|
+
fontSize: "0.64rem",
|
|
3834
|
+
fontWeight: 600,
|
|
3835
|
+
fontVariantNumeric: "tabular-nums",
|
|
3836
|
+
color: age.fresh ? "var(--color-accent, #E6C87E)" : "rgba(255,255,255,0.65)"
|
|
3837
|
+
},
|
|
3838
|
+
children: age.label
|
|
3839
|
+
}
|
|
3840
|
+
) : null;
|
|
3841
|
+
})(),
|
|
3842
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3843
|
+
"span",
|
|
3844
|
+
{
|
|
3845
|
+
"data-testid": "breaking-tag",
|
|
3846
|
+
style: {
|
|
3847
|
+
fontSize: "0.55rem",
|
|
3848
|
+
fontWeight: 700,
|
|
3849
|
+
letterSpacing: "0.09em",
|
|
3850
|
+
color: "#0a0b0e",
|
|
3851
|
+
background: "var(--color-accent, #E6C87E)",
|
|
3852
|
+
padding: "0.16rem 0.34rem 0.13rem",
|
|
3853
|
+
borderRadius: "3px",
|
|
3854
|
+
lineHeight: 1
|
|
3855
|
+
},
|
|
3856
|
+
children: "BREAKING"
|
|
3857
|
+
}
|
|
3858
|
+
),
|
|
3859
|
+
sentiment ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3860
|
+
"span",
|
|
3861
|
+
{
|
|
3862
|
+
style: {
|
|
3863
|
+
display: "inline-flex",
|
|
3864
|
+
alignItems: "center",
|
|
3865
|
+
gap: "0.3rem",
|
|
3866
|
+
fontSize: "0.58rem",
|
|
3867
|
+
fontWeight: 600,
|
|
3868
|
+
color: sentiment.color
|
|
3869
|
+
},
|
|
3870
|
+
children: [
|
|
3871
|
+
sentiment.arrow ? `${sentiment.arrow} ` : "",
|
|
3872
|
+
sentiment.label,
|
|
3873
|
+
item.sentimentScore != null && /* @__PURE__ */ jsxRuntime.jsx(SentimentBar, { score: item.sentimentScore, color: sentiment.color })
|
|
3874
|
+
]
|
|
3875
|
+
}
|
|
3876
|
+
) : null,
|
|
3877
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3878
|
+
"span",
|
|
3879
|
+
{
|
|
3880
|
+
className: "property-news-breaking-dismiss",
|
|
3881
|
+
"data-testid": "breaking-dismiss",
|
|
3882
|
+
role: "button",
|
|
3883
|
+
tabIndex: 0,
|
|
3884
|
+
"aria-label": `Dismiss breaking story: ${item.title}`,
|
|
3885
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
3886
|
+
onClick: (e) => {
|
|
3887
|
+
e.stopPropagation();
|
|
3888
|
+
setDismissingBreakingKey(k);
|
|
3889
|
+
},
|
|
3890
|
+
onKeyDown: (e) => {
|
|
3891
|
+
if (e.key !== "Enter" && e.key !== " ") return;
|
|
3892
|
+
e.preventDefault();
|
|
3893
|
+
e.stopPropagation();
|
|
3894
|
+
setDismissingBreakingKey(k);
|
|
3895
|
+
},
|
|
3896
|
+
style: {
|
|
3897
|
+
marginLeft: "auto",
|
|
3898
|
+
padding: "0.2rem 0.35rem 0.2rem 0.5rem",
|
|
3899
|
+
fontSize: "0.58rem",
|
|
3900
|
+
fontWeight: 600,
|
|
3901
|
+
color: "rgba(255,255,255,0.58)",
|
|
3902
|
+
cursor: "pointer",
|
|
3903
|
+
whiteSpace: "nowrap"
|
|
3904
|
+
},
|
|
3905
|
+
children: "Dismiss"
|
|
3906
|
+
}
|
|
3907
|
+
)
|
|
3908
|
+
]
|
|
3909
|
+
}
|
|
3910
|
+
)
|
|
3911
|
+
]
|
|
3912
|
+
},
|
|
3913
|
+
item.id ?? `breaking-${i}`
|
|
3914
|
+
);
|
|
3915
|
+
})
|
|
3916
|
+
}
|
|
3917
|
+
),
|
|
3101
3918
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3102
3919
|
"div",
|
|
3103
3920
|
{
|
|
3104
|
-
className: cn("flex flex-1 flex-col overflow-y-auto overflow-x-hidden", isHomeVariant ? "gap-0" : "mt-4 gap-3"),
|
|
3921
|
+
className: cn("news-list-scroll flex flex-1 flex-col overflow-y-auto overflow-x-hidden", isHomeVariant ? "gap-0" : "mt-4 gap-3"),
|
|
3105
3922
|
style: !isPurchaseVariant && !isHomeVariant ? { minHeight: `${ITEMS_PER_PAGE * 86}px` } : void 0,
|
|
3106
3923
|
children: isPurchaseVariant ? purchaseItems.length > 0 ? purchaseItems.slice(0, 7).map((purchase, index) => {
|
|
3107
3924
|
const maxAmount = 6e4;
|
|
@@ -3132,10 +3949,12 @@ var PropertyNewsUpdates = React5__namespace.forwardRef(
|
|
|
3132
3949
|
const key = item.displayId ?? item.id ?? `${item.title}-${index}`;
|
|
3133
3950
|
const isProperty = item.type === "property";
|
|
3134
3951
|
const sentimentInfo = getSentimentInfo(item.sentimentScore);
|
|
3952
|
+
const isBreakingRow = pinBreaking && Boolean(item.breaking) && breakingRowKeys.has(breakingKey(item));
|
|
3135
3953
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3136
3954
|
"button",
|
|
3137
3955
|
{
|
|
3138
3956
|
type: "button",
|
|
3957
|
+
"data-breaking-row": isBreakingRow ? "true" : void 0,
|
|
3139
3958
|
onClick: () => setSelectedItem(item),
|
|
3140
3959
|
style: {
|
|
3141
3960
|
width: "100%",
|
|
@@ -3151,6 +3970,10 @@ var PropertyNewsUpdates = React5__namespace.forwardRef(
|
|
|
3151
3970
|
gap: isMobile ? "0.25rem" : "0.5rem",
|
|
3152
3971
|
color: "inherit",
|
|
3153
3972
|
transition: "background-color 0.15s"
|
|
3973
|
+
// No gold wash or left edge on a dismissed row: once swiped
|
|
3974
|
+
// out of the pin it is back in the ordinary feed, and the
|
|
3975
|
+
// BREAKING badge below is enough to find it again. Only the
|
|
3976
|
+
// PINNED strip keeps the gold treatment.
|
|
3154
3977
|
},
|
|
3155
3978
|
onMouseEnter: (e) => {
|
|
3156
3979
|
e.currentTarget.style.backgroundColor = "rgba(255,255,255,0.04)";
|
|
@@ -3164,15 +3987,91 @@ var PropertyNewsUpdates = React5__namespace.forwardRef(
|
|
|
3164
3987
|
},
|
|
3165
3988
|
children: [
|
|
3166
3989
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { flex: 1 }, children: [
|
|
3167
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { style: { fontSize:
|
|
3990
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { style: { fontSize: "0.8rem", fontWeight: 500, marginBottom: isMobile ? "0.2rem" : "0.3rem", color: "#f8f9fa", lineHeight: 1.35, transition: "color 0.15s" }, children: item.title }),
|
|
3168
3991
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: isMobile ? "0.35rem" : "0.5rem", alignItems: "center", flexWrap: "wrap" }, children: [
|
|
3169
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3170
|
-
|
|
3992
|
+
isBreakingRow && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3993
|
+
"span",
|
|
3994
|
+
{
|
|
3995
|
+
"data-testid": "breaking-tag",
|
|
3996
|
+
style: {
|
|
3997
|
+
fontSize: "0.55rem",
|
|
3998
|
+
fontWeight: 700,
|
|
3999
|
+
letterSpacing: "0.09em",
|
|
4000
|
+
color: "#0a0b0e",
|
|
4001
|
+
background: "var(--color-accent, #E6C87E)",
|
|
4002
|
+
padding: "0.16rem 0.34rem 0.13rem",
|
|
4003
|
+
borderRadius: "3px",
|
|
4004
|
+
lineHeight: 1
|
|
4005
|
+
},
|
|
4006
|
+
children: "BREAKING"
|
|
4007
|
+
}
|
|
4008
|
+
),
|
|
4009
|
+
(() => {
|
|
4010
|
+
const age = rowAgeLabel(item.publishedAt, rowNow);
|
|
4011
|
+
return age ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4012
|
+
"span",
|
|
4013
|
+
{
|
|
4014
|
+
"data-testid": "news-age",
|
|
4015
|
+
"data-age-fresh": age.fresh ? "true" : void 0,
|
|
4016
|
+
style: {
|
|
4017
|
+
// Sized against the 0.8rem row title (matches the
|
|
4018
|
+
// breaking strip's meta scale).
|
|
4019
|
+
fontSize: isMobile ? "0.58rem" : "0.64rem",
|
|
4020
|
+
fontWeight: 600,
|
|
4021
|
+
fontVariantNumeric: "tabular-nums",
|
|
4022
|
+
color: age.fresh ? "var(--color-accent, #E6C87E)" : "rgba(255,255,255,0.65)"
|
|
4023
|
+
},
|
|
4024
|
+
children: age.label
|
|
4025
|
+
}
|
|
4026
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#848e9c", fontSize: isMobile ? "0.55rem" : "0.7rem" }, children: formatDateShort(item.date) });
|
|
4027
|
+
})(),
|
|
4028
|
+
showAssetBadges && isProperty && (item.assets ?? (item.asset ? [item.asset] : [])).map((badgeAsset) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4029
|
+
"span",
|
|
4030
|
+
{
|
|
4031
|
+
className: "news-badge-wrap",
|
|
4032
|
+
onClick: onAssetOpen ? (e) => {
|
|
4033
|
+
e.stopPropagation();
|
|
4034
|
+
e.preventDefault();
|
|
4035
|
+
onAssetOpen(badgeAsset);
|
|
4036
|
+
} : void 0,
|
|
4037
|
+
onMouseEnter: (e) => showBadgeTip(e.currentTarget, badgeAsset),
|
|
4038
|
+
onMouseLeave: () => setBadgeTip(null),
|
|
4039
|
+
style: { cursor: onAssetOpen ? "pointer" : void 0 },
|
|
4040
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4041
|
+
"span",
|
|
4042
|
+
{
|
|
4043
|
+
className: "news-badge-box",
|
|
4044
|
+
"data-testid": "news-asset-badge",
|
|
4045
|
+
style: {
|
|
4046
|
+
display: "inline-flex",
|
|
4047
|
+
alignItems: "center",
|
|
4048
|
+
justifyContent: "center",
|
|
4049
|
+
width: "20px",
|
|
4050
|
+
height: "20px",
|
|
4051
|
+
borderRadius: "5px",
|
|
4052
|
+
overflow: "hidden",
|
|
4053
|
+
background: "rgba(255,255,255,0.08)",
|
|
4054
|
+
flexShrink: 0
|
|
4055
|
+
},
|
|
4056
|
+
children: badgeAsset.imageUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4057
|
+
"img",
|
|
4058
|
+
{
|
|
4059
|
+
src: badgeAsset.imageUrl,
|
|
4060
|
+
alt: badgeAsset.name ?? badgeAsset.ticker,
|
|
4061
|
+
style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
|
|
4062
|
+
}
|
|
4063
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.45rem", fontWeight: 700, letterSpacing: "0.02em", color: "rgba(255,255,255,0.75)" }, children: badgeAsset.ticker.slice(0, 4) })
|
|
4064
|
+
}
|
|
4065
|
+
)
|
|
4066
|
+
},
|
|
4067
|
+
badgeAsset.ticker
|
|
4068
|
+
)),
|
|
3171
4069
|
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { padding: isMobile ? "0.1rem 0.35rem" : "0.15rem 0.4rem", borderRadius: "2px", fontSize: isMobile ? "0.52rem" : "0.65rem", fontWeight: 500, backgroundColor: isProperty ? "rgba(14,203,129,0.1)" : "rgba(240,185,11,0.1)", color: isProperty ? "#0ecb81" : "#E6C87E" }, children: isProperty ? "Property Update" : "Market News" }),
|
|
3172
|
-
sentimentInfo && /* @__PURE__ */ jsxRuntime.
|
|
4070
|
+
sentimentInfo && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: isMobile ? "0.52rem" : "0.65rem", fontWeight: 600, color: sentimentInfo.color }, children: /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: "0.3rem" }, children: [
|
|
3173
4071
|
sentimentInfo.arrow ? `${sentimentInfo.arrow} ` : "",
|
|
3174
|
-
sentimentInfo.label
|
|
3175
|
-
|
|
4072
|
+
sentimentInfo.label,
|
|
4073
|
+
item.sentimentScore != null && /* @__PURE__ */ jsxRuntime.jsx(SentimentBar, { score: item.sentimentScore, color: sentimentInfo.color })
|
|
4074
|
+
] }) })
|
|
3176
4075
|
] })
|
|
3177
4076
|
] }),
|
|
3178
4077
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#848e9c", paddingTop: "2px", flexShrink: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z" }) }) })
|
|
@@ -3221,15 +4120,13 @@ var PropertyNewsUpdates = React5__namespace.forwardRef(
|
|
|
3221
4120
|
children: [
|
|
3222
4121
|
/* @__PURE__ */ jsxRuntime.jsx("p", { style: { fontSize: "0.9375rem", fontWeight: 500, marginBottom: "0.4rem", color: "#fff", lineHeight: 1.4 }, children: item.title }),
|
|
3223
4122
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-end", gap: "0.5rem" }, children: [
|
|
3224
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3225
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.75rem", color: isHighlighted ? "#0ecb81" : "#b5b8c5", whiteSpace: "nowrap" }, children: dateLabel }),
|
|
3226
|
-
item.source && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { padding: "0.15rem 0.45rem", borderRadius: "3px", fontSize: "0.65rem", fontWeight: 500, backgroundColor: "rgba(255,255,255,0.07)", color: "#848e9c", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", maxWidth: "120px" }, children: item.source })
|
|
3227
|
-
] }),
|
|
4123
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem", minWidth: 0, flexWrap: "wrap" }, children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.75rem", color: isHighlighted ? "#0ecb81" : "#b5b8c5", whiteSpace: "nowrap" }, children: dateLabel }) }),
|
|
3228
4124
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "flex-end", gap: "0.3rem", flexShrink: 0 }, children: [
|
|
3229
|
-
sentimentInfo && /* @__PURE__ */ jsxRuntime.
|
|
4125
|
+
sentimentInfo && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "0.65rem", fontWeight: 700, color: sentimentInfo.color, whiteSpace: "nowrap" }, children: /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: "0.3rem" }, children: [
|
|
3230
4126
|
sentimentInfo.arrow ? `${sentimentInfo.arrow} ` : "",
|
|
3231
|
-
sentimentInfo.label
|
|
3232
|
-
|
|
4127
|
+
sentimentInfo.label,
|
|
4128
|
+
item.sentimentScore != null && /* @__PURE__ */ jsxRuntime.jsx(SentimentBar, { score: item.sentimentScore, color: sentimentInfo.color })
|
|
4129
|
+
] }) }),
|
|
3233
4130
|
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { padding: "0.25rem 0.6rem", borderRadius: "4px", border: `1px solid ${catStyle.borderColor}`, backgroundColor: catStyle.backgroundColor, color: catStyle.color, fontSize: "0.68rem", fontWeight: 600, textTransform: "uppercase", whiteSpace: "nowrap" }, children: item.type === "property" ? catStyle.label : "Market News" })
|
|
3234
4131
|
] })
|
|
3235
4132
|
] })
|
|
@@ -3302,7 +4199,21 @@ var PropertyNewsUpdates = React5__namespace.forwardRef(
|
|
|
3302
4199
|
}
|
|
3303
4200
|
)
|
|
3304
4201
|
] }),
|
|
3305
|
-
selectedItem && !isPurchaseVariant && /* @__PURE__ */ jsxRuntime.jsx(NewsArticleModal, { item: selectedItem, onClose: () => setSelectedItem(null) })
|
|
4202
|
+
selectedItem && !isPurchaseVariant && /* @__PURE__ */ jsxRuntime.jsx(NewsArticleModal, { item: selectedItem, onClose: () => setSelectedItem(null) }),
|
|
4203
|
+
badgeTip && ReactDOM__namespace.createPortal(
|
|
4204
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4205
|
+
"span",
|
|
4206
|
+
{
|
|
4207
|
+
className: "news-badge-tipbox",
|
|
4208
|
+
"data-testid": "news-asset-tip",
|
|
4209
|
+
"data-tip-below": badgeTip.below ? "true" : void 0,
|
|
4210
|
+
"aria-hidden": true,
|
|
4211
|
+
style: { position: "fixed", left: `${badgeTip.left}px`, top: `${badgeTip.top}px` },
|
|
4212
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { children: badgeTip.asset.name ? `${badgeTip.asset.name} (${badgeTip.asset.ticker})` : badgeTip.asset.ticker })
|
|
4213
|
+
}
|
|
4214
|
+
),
|
|
4215
|
+
document.body
|
|
4216
|
+
)
|
|
3306
4217
|
]
|
|
3307
4218
|
}
|
|
3308
4219
|
);
|
|
@@ -3355,7 +4266,7 @@ var defaultFormat = (value) => new Intl.NumberFormat("en-US", {
|
|
|
3355
4266
|
notation: value >= 1e5 ? "compact" : "standard",
|
|
3356
4267
|
maximumFractionDigits: value >= 1e3 ? 0 : 2
|
|
3357
4268
|
}).format(value);
|
|
3358
|
-
var TradingSlider =
|
|
4269
|
+
var TradingSlider = React9__namespace.forwardRef(
|
|
3359
4270
|
({
|
|
3360
4271
|
label = "Trade size",
|
|
3361
4272
|
helperText = "Drag to pick the desired notional.",
|
|
@@ -3373,7 +4284,7 @@ var TradingSlider = React5__namespace.forwardRef(
|
|
|
3373
4284
|
...rest
|
|
3374
4285
|
}, ref) => {
|
|
3375
4286
|
const isControlled = value !== void 0;
|
|
3376
|
-
const [internalValue, setInternalValue] =
|
|
4287
|
+
const [internalValue, setInternalValue] = React9__namespace.useState(
|
|
3377
4288
|
defaultValue ?? (typeof min === "number" ? min : 0)
|
|
3378
4289
|
);
|
|
3379
4290
|
const currentValue = isControlled ? Number(value) : internalValue;
|
|
@@ -3476,7 +4387,7 @@ var MobileToggleButton = styled10__default.default.button`
|
|
|
3476
4387
|
padding: 0.6rem 0.5rem;
|
|
3477
4388
|
}
|
|
3478
4389
|
`;
|
|
3479
|
-
var MobileTradeNav =
|
|
4390
|
+
var MobileTradeNav = React9__namespace.forwardRef(
|
|
3480
4391
|
({ className, items, activeId, onChange, ...props }, ref) => {
|
|
3481
4392
|
return /* @__PURE__ */ jsxRuntime.jsx(MobileToggleContainer, { ref, className: cn(className), ...props, children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3482
4393
|
MobileToggleButton,
|
|
@@ -4002,7 +4913,7 @@ var formatPercent = (value, fractionDigits = 2) => {
|
|
|
4002
4913
|
if (value == null || Number.isNaN(value)) return "\u2014";
|
|
4003
4914
|
return `${value.toFixed(fractionDigits)}%`;
|
|
4004
4915
|
};
|
|
4005
|
-
var YourOrders =
|
|
4916
|
+
var YourOrders = React9__namespace.forwardRef(
|
|
4006
4917
|
({
|
|
4007
4918
|
className,
|
|
4008
4919
|
title,
|
|
@@ -4015,10 +4926,10 @@ var YourOrders = React5__namespace.forwardRef(
|
|
|
4015
4926
|
pageSize: pageSizeOverride,
|
|
4016
4927
|
...props
|
|
4017
4928
|
}, ref) => {
|
|
4018
|
-
const [internalActiveTab, setInternalActiveTab] =
|
|
4019
|
-
const [page, setPage] =
|
|
4929
|
+
const [internalActiveTab, setInternalActiveTab] = React9__namespace.useState(tabs?.[0]?.id ?? "portfolio");
|
|
4930
|
+
const [page, setPage] = React9__namespace.useState(0);
|
|
4020
4931
|
const effectiveActiveTabId = activeTabId ?? internalActiveTab;
|
|
4021
|
-
|
|
4932
|
+
React9__namespace.useEffect(() => {
|
|
4022
4933
|
setPage(0);
|
|
4023
4934
|
}, [effectiveActiveTabId]);
|
|
4024
4935
|
const handleTabChange = (tabId) => {
|
|
@@ -4400,7 +5311,7 @@ function createCandlestickSeries(chart, options) {
|
|
|
4400
5311
|
}
|
|
4401
5312
|
throw new Error("Candlestick series API is not available in the current lightweight-charts version.");
|
|
4402
5313
|
}
|
|
4403
|
-
var PriceChart =
|
|
5314
|
+
var PriceChart = React9__namespace.forwardRef(
|
|
4404
5315
|
({
|
|
4405
5316
|
className,
|
|
4406
5317
|
title = "Price Chart",
|
|
@@ -4414,18 +5325,18 @@ var PriceChart = React5__namespace.forwardRef(
|
|
|
4414
5325
|
height = 301.52,
|
|
4415
5326
|
...props
|
|
4416
5327
|
}, ref) => {
|
|
4417
|
-
const containerRef =
|
|
4418
|
-
const chartRef =
|
|
4419
|
-
const seriesRef =
|
|
4420
|
-
const priceLineRef =
|
|
4421
|
-
const [hoveredRange, setHoveredRange] =
|
|
4422
|
-
const [dropdownOpen, setDropdownOpen] =
|
|
4423
|
-
const dropdownRef =
|
|
4424
|
-
const isAutoScrollRef =
|
|
5328
|
+
const containerRef = React9__namespace.useRef(null);
|
|
5329
|
+
const chartRef = React9__namespace.useRef(null);
|
|
5330
|
+
const seriesRef = React9__namespace.useRef(null);
|
|
5331
|
+
const priceLineRef = React9__namespace.useRef(null);
|
|
5332
|
+
const [hoveredRange, setHoveredRange] = React9__namespace.useState(null);
|
|
5333
|
+
const [dropdownOpen, setDropdownOpen] = React9__namespace.useState(false);
|
|
5334
|
+
const dropdownRef = React9__namespace.useRef(null);
|
|
5335
|
+
const isAutoScrollRef = React9__namespace.useRef(true);
|
|
4425
5336
|
const visibleRanges = ranges.slice(0, VISIBLE_RANGE_COUNT);
|
|
4426
5337
|
const dropdownRanges = ranges.slice(VISIBLE_RANGE_COUNT);
|
|
4427
5338
|
const selectedInDropdown = dropdownRanges.includes(selectedRange);
|
|
4428
|
-
|
|
5339
|
+
React9__namespace.useEffect(() => {
|
|
4429
5340
|
const handleClickOutside = (e) => {
|
|
4430
5341
|
if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
|
|
4431
5342
|
setDropdownOpen(false);
|
|
@@ -4434,25 +5345,25 @@ var PriceChart = React5__namespace.forwardRef(
|
|
|
4434
5345
|
document.addEventListener("mousedown", handleClickOutside);
|
|
4435
5346
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
4436
5347
|
}, []);
|
|
4437
|
-
const resolvedPrice =
|
|
5348
|
+
const resolvedPrice = React9__namespace.useMemo(() => {
|
|
4438
5349
|
if (price != null) return price;
|
|
4439
5350
|
const last = data.at(-1);
|
|
4440
5351
|
return last?.close;
|
|
4441
5352
|
}, [data, price]);
|
|
4442
|
-
const inferredChangePercent =
|
|
5353
|
+
const inferredChangePercent = React9__namespace.useMemo(() => {
|
|
4443
5354
|
if (changePercent != null) return changePercent;
|
|
4444
5355
|
const first = data[0]?.open;
|
|
4445
5356
|
const last = data.at(-1)?.close;
|
|
4446
5357
|
if (first == null || last == null || first === 0) return void 0;
|
|
4447
5358
|
return (last - first) / first * 100;
|
|
4448
5359
|
}, [changePercent, data]);
|
|
4449
|
-
const dollarChange =
|
|
5360
|
+
const dollarChange = React9__namespace.useMemo(() => {
|
|
4450
5361
|
const first = data[0]?.open;
|
|
4451
5362
|
const last = data.at(-1)?.close;
|
|
4452
5363
|
if (first == null || last == null) return void 0;
|
|
4453
5364
|
return last - first;
|
|
4454
5365
|
}, [data]);
|
|
4455
|
-
|
|
5366
|
+
React9__namespace.useEffect(() => {
|
|
4456
5367
|
const el = containerRef.current;
|
|
4457
5368
|
if (!el) return;
|
|
4458
5369
|
const chart = LightweightCharts__namespace.createChart(el, {
|
|
@@ -4497,7 +5408,7 @@ var PriceChart = React5__namespace.forwardRef(
|
|
|
4497
5408
|
chart.remove();
|
|
4498
5409
|
};
|
|
4499
5410
|
}, []);
|
|
4500
|
-
|
|
5411
|
+
React9__namespace.useEffect(() => {
|
|
4501
5412
|
const chart = chartRef.current;
|
|
4502
5413
|
if (!chart) return;
|
|
4503
5414
|
const effectiveRange = selectedRange ?? ranges?.[0] ?? "1D";
|
|
@@ -4505,7 +5416,7 @@ var PriceChart = React5__namespace.forwardRef(
|
|
|
4505
5416
|
timeScale: getTimeScaleOptions(effectiveRange)
|
|
4506
5417
|
});
|
|
4507
5418
|
}, [selectedRange, ranges]);
|
|
4508
|
-
|
|
5419
|
+
React9__namespace.useEffect(() => {
|
|
4509
5420
|
const chart = chartRef.current;
|
|
4510
5421
|
const series = seriesRef.current;
|
|
4511
5422
|
if (!chart || !series) return;
|
|
@@ -4720,7 +5631,7 @@ var formatPrice3 = (value, currencySymbol, compact) => {
|
|
|
4720
5631
|
maximumFractionDigits: 2
|
|
4721
5632
|
})}`;
|
|
4722
5633
|
};
|
|
4723
|
-
var PropertyHeroHeader =
|
|
5634
|
+
var PropertyHeroHeader = React9__namespace.forwardRef(
|
|
4724
5635
|
({
|
|
4725
5636
|
className,
|
|
4726
5637
|
imageUrl,
|
|
@@ -4754,12 +5665,12 @@ var PropertyHeroHeader = React5__namespace.forwardRef(
|
|
|
4754
5665
|
const isPositive = changePercent == null ? void 0 : changePercent >= 0;
|
|
4755
5666
|
const accentColor = "#e6c87e";
|
|
4756
5667
|
const tradeHoverColor = "#f5dd9a";
|
|
4757
|
-
const [isTradeInteracting, setIsTradeInteracting] =
|
|
4758
|
-
const [isOfferInteracting, setIsOfferInteracting] =
|
|
4759
|
-
const [isMobile, setIsMobile] =
|
|
5668
|
+
const [isTradeInteracting, setIsTradeInteracting] = React9__namespace.useState(false);
|
|
5669
|
+
const [isOfferInteracting, setIsOfferInteracting] = React9__namespace.useState(false);
|
|
5670
|
+
const [isMobile, setIsMobile] = React9__namespace.useState(
|
|
4760
5671
|
() => typeof window !== "undefined" && window.innerWidth <= 768
|
|
4761
5672
|
);
|
|
4762
|
-
|
|
5673
|
+
React9__namespace.useEffect(() => {
|
|
4763
5674
|
const onResize = () => setIsMobile(window.innerWidth <= 768);
|
|
4764
5675
|
window.addEventListener("resize", onResize);
|
|
4765
5676
|
return () => window.removeEventListener("resize", onResize);
|
|
@@ -5150,10 +6061,10 @@ function fmtCompactUsd(n) {
|
|
|
5150
6061
|
}
|
|
5151
6062
|
function useIsNarrow(maxWidthPx = 768) {
|
|
5152
6063
|
const query = `(max-width:${maxWidthPx}px)`;
|
|
5153
|
-
const [narrow, setNarrow] =
|
|
6064
|
+
const [narrow, setNarrow] = React9.useState(
|
|
5154
6065
|
typeof window !== "undefined" ? window.matchMedia(query).matches : false
|
|
5155
6066
|
);
|
|
5156
|
-
|
|
6067
|
+
React9.useEffect(() => {
|
|
5157
6068
|
if (typeof window === "undefined") return;
|
|
5158
6069
|
const mq = window.matchMedia(query);
|
|
5159
6070
|
const onChange = () => setNarrow(mq.matches);
|
|
@@ -5197,20 +6108,20 @@ var Header = ({
|
|
|
5197
6108
|
headerCta = "deposit",
|
|
5198
6109
|
showRefer = false
|
|
5199
6110
|
}) => {
|
|
5200
|
-
const [headerBgOpacity, setHeaderBgOpacity] =
|
|
5201
|
-
const [isUserMenuOpen, setIsUserMenuOpen] =
|
|
5202
|
-
const [isMobileMenuOpen, setIsMobileMenuOpen] =
|
|
6111
|
+
const [headerBgOpacity, setHeaderBgOpacity] = React9.useState(transparentOnTop ? 0 : 1);
|
|
6112
|
+
const [isUserMenuOpen, setIsUserMenuOpen] = React9.useState(false);
|
|
6113
|
+
const [isMobileMenuOpen, setIsMobileMenuOpen] = React9.useState(false);
|
|
5203
6114
|
const isNarrow = useIsNarrow();
|
|
5204
|
-
const [isMoreMenuOpen, setIsMoreMenuOpen] =
|
|
5205
|
-
const [showLoginPopup, setShowLoginPopup] =
|
|
5206
|
-
const [loginPopupInitialView, setLoginPopupInitialView] =
|
|
5207
|
-
const [isPortfolioBarVisible, setIsPortfolioBarVisible] =
|
|
6115
|
+
const [isMoreMenuOpen, setIsMoreMenuOpen] = React9.useState(false);
|
|
6116
|
+
const [showLoginPopup, setShowLoginPopup] = React9.useState(false);
|
|
6117
|
+
const [loginPopupInitialView, setLoginPopupInitialView] = React9.useState(void 0);
|
|
6118
|
+
const [isPortfolioBarVisible, setIsPortfolioBarVisible] = React9.useState(() => {
|
|
5208
6119
|
if (typeof localStorage === "undefined") return false;
|
|
5209
6120
|
return localStorage.getItem("portfolioBarVisible") !== "false";
|
|
5210
6121
|
});
|
|
5211
|
-
const portfolioPillRef =
|
|
5212
|
-
const [portfolioArrowLeft, setPortfolioArrowLeft] =
|
|
5213
|
-
const togglePortfolioBar =
|
|
6122
|
+
const portfolioPillRef = React9.useRef(null);
|
|
6123
|
+
const [portfolioArrowLeft, setPortfolioArrowLeft] = React9.useState(null);
|
|
6124
|
+
const togglePortfolioBar = React9.useCallback(() => {
|
|
5214
6125
|
setIsPortfolioBarVisible((prev) => {
|
|
5215
6126
|
const next = !prev;
|
|
5216
6127
|
try {
|
|
@@ -5221,13 +6132,13 @@ var Header = ({
|
|
|
5221
6132
|
});
|
|
5222
6133
|
}, []);
|
|
5223
6134
|
const SIMULATE_FUNDS = false;
|
|
5224
|
-
const [simFundsPending, setSimFundsPending] =
|
|
5225
|
-
|
|
6135
|
+
const [simFundsPending, setSimFundsPending] = React9.useState(true);
|
|
6136
|
+
React9.useEffect(() => {
|
|
5226
6137
|
return;
|
|
5227
6138
|
}, [simFundsPending]);
|
|
5228
6139
|
const showFundsPending = (fundsPending || SIMULATE_FUNDS) && isAuthenticated && isPortfolioBarVisible;
|
|
5229
|
-
const [fundsCountdown, setFundsCountdown] =
|
|
5230
|
-
|
|
6140
|
+
const [fundsCountdown, setFundsCountdown] = React9.useState(10);
|
|
6141
|
+
React9.useEffect(() => {
|
|
5231
6142
|
if (!showFundsPending) return;
|
|
5232
6143
|
setFundsCountdown(10);
|
|
5233
6144
|
const id = setInterval(() => {
|
|
@@ -5241,7 +6152,7 @@ var Header = ({
|
|
|
5241
6152
|
}, 1e3);
|
|
5242
6153
|
return () => clearInterval(id);
|
|
5243
6154
|
}, [showFundsPending]);
|
|
5244
|
-
|
|
6155
|
+
React9.useEffect(() => {
|
|
5245
6156
|
const handler = () => {
|
|
5246
6157
|
if (LoginPopupComponent) {
|
|
5247
6158
|
setLoginPopupInitialView("funding");
|
|
@@ -5251,7 +6162,7 @@ var Header = ({
|
|
|
5251
6162
|
window.addEventListener("loaf:open-funding", handler);
|
|
5252
6163
|
return () => window.removeEventListener("loaf:open-funding", handler);
|
|
5253
6164
|
}, [LoginPopupComponent]);
|
|
5254
|
-
const updatePortfolioArrow =
|
|
6165
|
+
const updatePortfolioArrow = React9.useCallback(() => {
|
|
5255
6166
|
const eye = document.getElementById("portfolio-eye-toggle");
|
|
5256
6167
|
const pill = portfolioPillRef.current;
|
|
5257
6168
|
if (!eye || !pill) return;
|
|
@@ -5260,13 +6171,13 @@ var Header = ({
|
|
|
5260
6171
|
const offset = eyeRect.left + eyeRect.width / 2 - pillRect.left - 7;
|
|
5261
6172
|
setPortfolioArrowLeft(offset);
|
|
5262
6173
|
}, []);
|
|
5263
|
-
|
|
6174
|
+
React9.useEffect(() => {
|
|
5264
6175
|
if (!isPortfolioBarVisible || !portfolioSummary) return;
|
|
5265
6176
|
updatePortfolioArrow();
|
|
5266
6177
|
window.addEventListener("resize", updatePortfolioArrow);
|
|
5267
6178
|
return () => window.removeEventListener("resize", updatePortfolioArrow);
|
|
5268
6179
|
}, [isPortfolioBarVisible, portfolioSummary, updatePortfolioArrow]);
|
|
5269
|
-
|
|
6180
|
+
React9.useEffect(() => {
|
|
5270
6181
|
if (typeof window === "undefined") return;
|
|
5271
6182
|
const ua = navigator.userAgent;
|
|
5272
6183
|
const isTelegram = /Telegram/i.test(ua);
|
|
@@ -5275,7 +6186,7 @@ var Header = ({
|
|
|
5275
6186
|
document.documentElement.style.setProperty("--telegram-safe-top", "59px");
|
|
5276
6187
|
}
|
|
5277
6188
|
}, []);
|
|
5278
|
-
|
|
6189
|
+
React9.useEffect(() => {
|
|
5279
6190
|
if (!transparentOnTop) {
|
|
5280
6191
|
setHeaderBgOpacity(1);
|
|
5281
6192
|
return;
|
|
@@ -5288,7 +6199,7 @@ var Header = ({
|
|
|
5288
6199
|
window.addEventListener("scroll", onScroll, { passive: true });
|
|
5289
6200
|
return () => window.removeEventListener("scroll", onScroll);
|
|
5290
6201
|
}, [transparentOnTop]);
|
|
5291
|
-
|
|
6202
|
+
React9.useEffect(() => {
|
|
5292
6203
|
const handleClickOutside = (event) => {
|
|
5293
6204
|
const target = event.target;
|
|
5294
6205
|
if (!target) return;
|
|
@@ -5308,7 +6219,7 @@ var Header = ({
|
|
|
5308
6219
|
document.removeEventListener("mousedown", handleClickOutside);
|
|
5309
6220
|
};
|
|
5310
6221
|
}, [isUserMenuOpen, isMobileMenuOpen, isMoreMenuOpen]);
|
|
5311
|
-
|
|
6222
|
+
React9.useEffect(() => {
|
|
5312
6223
|
if (typeof window === "undefined") return;
|
|
5313
6224
|
const handleExternalLoginPopup = (event) => {
|
|
5314
6225
|
const customEvent = event;
|
|
@@ -5439,7 +6350,7 @@ var Header = ({
|
|
|
5439
6350
|
setShowLoginPopup(true);
|
|
5440
6351
|
}
|
|
5441
6352
|
};
|
|
5442
|
-
const handleLoginPopupClose =
|
|
6353
|
+
const handleLoginPopupClose = React9__namespace.default.useCallback(() => {
|
|
5443
6354
|
setShowLoginPopup(false);
|
|
5444
6355
|
setLoginPopupInitialView(void 0);
|
|
5445
6356
|
}, []);
|
|
@@ -5717,7 +6628,7 @@ var Header = ({
|
|
|
5717
6628
|
}
|
|
5718
6629
|
)
|
|
5719
6630
|
] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center" }, children: [
|
|
5720
|
-
/* @__PURE__ */ jsxRuntime.jsx(Button2, { className: "signup", onClick: handleSignIn, children: "
|
|
6631
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button2, { className: "signup", onClick: handleSignIn, children: "Access Beta" }),
|
|
5721
6632
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5722
6633
|
MobileOnlyButton,
|
|
5723
6634
|
{
|
|
@@ -6603,10 +7514,10 @@ var PBOperator = styled10__default.default.span`
|
|
|
6603
7514
|
font-size: 0.55rem;
|
|
6604
7515
|
}
|
|
6605
7516
|
`;
|
|
6606
|
-
var PropertySubheader =
|
|
7517
|
+
var PropertySubheader = React9__namespace.forwardRef(
|
|
6607
7518
|
({ className, tabs, activeTabId, onTabChange, actions, ...props }, ref) => {
|
|
6608
|
-
const tabsContainerRef =
|
|
6609
|
-
|
|
7519
|
+
const tabsContainerRef = React9__namespace.useRef(null);
|
|
7520
|
+
React9__namespace.useEffect(() => {
|
|
6610
7521
|
const container = tabsContainerRef.current;
|
|
6611
7522
|
if (!container) return;
|
|
6612
7523
|
const isMobile = window.innerWidth <= 768;
|
|
@@ -6778,34 +7689,34 @@ var LoginPopup = ({
|
|
|
6778
7689
|
onCheckAccess,
|
|
6779
7690
|
gate
|
|
6780
7691
|
}) => {
|
|
6781
|
-
const [view, setView] =
|
|
6782
|
-
const [email, setEmail] =
|
|
6783
|
-
const [handle, setHandle] =
|
|
6784
|
-
const [otp, setOtp] =
|
|
6785
|
-
const [error, setError] =
|
|
6786
|
-
const [copied, setCopied] =
|
|
6787
|
-
const [referralCode, setReferralCode] =
|
|
7692
|
+
const [view, setView] = React9.useState(() => initialView ?? "main");
|
|
7693
|
+
const [email, setEmail] = React9.useState("");
|
|
7694
|
+
const [handle, setHandle] = React9.useState("");
|
|
7695
|
+
const [otp, setOtp] = React9.useState(Array(OTP_INPUT_LENGTH).fill(""));
|
|
7696
|
+
const [error, setError] = React9.useState("");
|
|
7697
|
+
const [copied, setCopied] = React9.useState(false);
|
|
7698
|
+
const [referralCode, setReferralCode] = React9.useState(
|
|
6788
7699
|
() => gate && initialCode ? normalizeAccessCode(initialCode) : ""
|
|
6789
7700
|
);
|
|
6790
|
-
const [referralLoading, setReferralLoading] =
|
|
6791
|
-
const [referralError, setReferralError] =
|
|
6792
|
-
const [codeStatus, setCodeStatus] =
|
|
6793
|
-
const [signInRevealed, setSignInRevealed] =
|
|
6794
|
-
const [otpStatus, setOtpStatus] =
|
|
6795
|
-
const [signInMode, setSignInMode] =
|
|
6796
|
-
const [loading, setLoading] =
|
|
6797
|
-
const [isSignUp, setIsSignUp] =
|
|
6798
|
-
const [fundingAmount] =
|
|
6799
|
-
const [kycLoading, setKycLoading] =
|
|
6800
|
-
const [showKycWidget, setShowKycWidget] =
|
|
6801
|
-
const [showKycFooter, setShowKycFooter] =
|
|
6802
|
-
const [cryptoFundingLoading, setCryptoFundingLoading] =
|
|
6803
|
-
const [fiatFundingLoading, setFiatFundingLoading] =
|
|
6804
|
-
const [fundingError, setFundingError] =
|
|
6805
|
-
const [transakWidgetUrl, setTransakWidgetUrl] =
|
|
6806
|
-
const suppressAutoCloseRef =
|
|
6807
|
-
const validatedCodeRef =
|
|
6808
|
-
const finishGate =
|
|
7701
|
+
const [referralLoading, setReferralLoading] = React9.useState(false);
|
|
7702
|
+
const [referralError, setReferralError] = React9.useState("");
|
|
7703
|
+
const [codeStatus, setCodeStatus] = React9.useState("idle");
|
|
7704
|
+
const [signInRevealed, setSignInRevealed] = React9.useState(false);
|
|
7705
|
+
const [otpStatus, setOtpStatus] = React9.useState("idle");
|
|
7706
|
+
const [signInMode, setSignInMode] = React9.useState(false);
|
|
7707
|
+
const [loading, setLoading] = React9.useState(false);
|
|
7708
|
+
const [isSignUp, setIsSignUp] = React9.useState(false);
|
|
7709
|
+
const [fundingAmount] = React9.useState("");
|
|
7710
|
+
const [kycLoading, setKycLoading] = React9.useState(false);
|
|
7711
|
+
const [showKycWidget, setShowKycWidget] = React9.useState(false);
|
|
7712
|
+
const [showKycFooter, setShowKycFooter] = React9.useState(false);
|
|
7713
|
+
const [cryptoFundingLoading, setCryptoFundingLoading] = React9.useState(false);
|
|
7714
|
+
const [fiatFundingLoading, setFiatFundingLoading] = React9.useState(false);
|
|
7715
|
+
const [fundingError, setFundingError] = React9.useState("");
|
|
7716
|
+
const [transakWidgetUrl, setTransakWidgetUrl] = React9.useState(null);
|
|
7717
|
+
const suppressAutoCloseRef = React9__namespace.default.useRef(false);
|
|
7718
|
+
const validatedCodeRef = React9__namespace.default.useRef(null);
|
|
7719
|
+
const finishGate = React9__namespace.default.useCallback(async () => {
|
|
6809
7720
|
if (!gate) return true;
|
|
6810
7721
|
const code = validatedCodeRef.current;
|
|
6811
7722
|
if (code) {
|
|
@@ -6831,7 +7742,7 @@ var LoginPopup = ({
|
|
|
6831
7742
|
);
|
|
6832
7743
|
return false;
|
|
6833
7744
|
}, [gate, onSubmitReferralCode, onCheckAccess]);
|
|
6834
|
-
const runCodeValidation =
|
|
7745
|
+
const runCodeValidation = React9__namespace.default.useCallback(async (fullCode) => {
|
|
6835
7746
|
setCodeStatus("checking");
|
|
6836
7747
|
setError("");
|
|
6837
7748
|
try {
|
|
@@ -6854,20 +7765,20 @@ var LoginPopup = ({
|
|
|
6854
7765
|
setCodeStatus("invalid");
|
|
6855
7766
|
}
|
|
6856
7767
|
}, [onValidateCode, isAuthenticated, finishGate, onClose]);
|
|
6857
|
-
const autoCodeTriedRef =
|
|
6858
|
-
|
|
7768
|
+
const autoCodeTriedRef = React9__namespace.default.useRef(false);
|
|
7769
|
+
React9.useEffect(() => {
|
|
6859
7770
|
if (autoCodeTriedRef.current || !gate || !initialCode) return;
|
|
6860
7771
|
const full = normalizeAccessCode(initialCode);
|
|
6861
7772
|
if (full.length !== CODE_PREFIX.length + CODE_SUFFIX_LEN) return;
|
|
6862
7773
|
autoCodeTriedRef.current = true;
|
|
6863
7774
|
void runCodeValidation(full);
|
|
6864
7775
|
}, [gate, initialCode, runCodeValidation]);
|
|
6865
|
-
|
|
7776
|
+
React9.useEffect(() => {
|
|
6866
7777
|
if (typeof initialView === "string") {
|
|
6867
7778
|
setView(initialView);
|
|
6868
7779
|
}
|
|
6869
7780
|
}, [initialView]);
|
|
6870
|
-
|
|
7781
|
+
React9.useEffect(() => {
|
|
6871
7782
|
if (!showKycWidget) {
|
|
6872
7783
|
setShowKycFooter(false);
|
|
6873
7784
|
return;
|
|
@@ -6875,7 +7786,7 @@ var LoginPopup = ({
|
|
|
6875
7786
|
const timer = setTimeout(() => setShowKycFooter(true), 5e3);
|
|
6876
7787
|
return () => clearTimeout(timer);
|
|
6877
7788
|
}, [showKycWidget]);
|
|
6878
|
-
|
|
7789
|
+
React9.useEffect(() => {
|
|
6879
7790
|
if (!transakWidgetUrl) return;
|
|
6880
7791
|
const handleTransakMessage = (event) => {
|
|
6881
7792
|
if (!isTrustedTransakOrigin(event.origin, transakWidgetUrl)) {
|
|
@@ -6900,7 +7811,7 @@ var LoginPopup = ({
|
|
|
6900
7811
|
window.addEventListener("message", handleTransakMessage);
|
|
6901
7812
|
return () => window.removeEventListener("message", handleTransakMessage);
|
|
6902
7813
|
}, [transakWidgetUrl, onClose]);
|
|
6903
|
-
|
|
7814
|
+
React9.useEffect(() => {
|
|
6904
7815
|
if (!autoCloseOnAuth) {
|
|
6905
7816
|
return;
|
|
6906
7817
|
}
|
|
@@ -6914,7 +7825,7 @@ var LoginPopup = ({
|
|
|
6914
7825
|
onClose();
|
|
6915
7826
|
}
|
|
6916
7827
|
}, [autoCloseOnAuth, currentUser, isAuthenticated, onClose, view]);
|
|
6917
|
-
|
|
7828
|
+
React9.useEffect(() => {
|
|
6918
7829
|
const handleEsc = (e) => {
|
|
6919
7830
|
if (e.key === "Escape" && !gate) onClose();
|
|
6920
7831
|
};
|
|
@@ -7137,7 +8048,7 @@ var LoginPopup = ({
|
|
|
7137
8048
|
setLoading(false);
|
|
7138
8049
|
}
|
|
7139
8050
|
};
|
|
7140
|
-
const runGateOtpVerify =
|
|
8051
|
+
const runGateOtpVerify = React9__namespace.default.useCallback(async (code) => {
|
|
7141
8052
|
if (!onVerifyEmailCode) return;
|
|
7142
8053
|
setOtpStatus("checking");
|
|
7143
8054
|
setError("");
|
|
@@ -7153,7 +8064,7 @@ var LoginPopup = ({
|
|
|
7153
8064
|
setOtpStatus("invalid");
|
|
7154
8065
|
}
|
|
7155
8066
|
}, [onVerifyEmailCode, email, finishGate, onClose]);
|
|
7156
|
-
|
|
8067
|
+
React9.useEffect(() => {
|
|
7157
8068
|
if (!gate || view !== "otp") return;
|
|
7158
8069
|
if (otp.join("").length < OTP_INPUT_LENGTH) {
|
|
7159
8070
|
if (otpStatus === "invalid") setOtpStatus("idle");
|
|
@@ -7262,7 +8173,7 @@ var LoginPopup = ({
|
|
|
7262
8173
|
/* @__PURE__ */ jsxRuntime.jsxs(Title, { children: [
|
|
7263
8174
|
/* @__PURE__ */ jsxRuntime.jsxs(LogoContainer3, { children: [
|
|
7264
8175
|
/* @__PURE__ */ jsxRuntime.jsx(LogoImage, { src: logoSrc, alt: logoAlt }),
|
|
7265
|
-
/* @__PURE__ */ jsxRuntime.jsx(LogoBeta, { children: "
|
|
8176
|
+
/* @__PURE__ */ jsxRuntime.jsx(LogoBeta, { children: "Beta" })
|
|
7266
8177
|
] }),
|
|
7267
8178
|
/* @__PURE__ */ jsxRuntime.jsx(TitleText, { children: "Welcome to Loaf" })
|
|
7268
8179
|
] }),
|
|
@@ -7357,7 +8268,7 @@ var LoginPopup = ({
|
|
|
7357
8268
|
/* @__PURE__ */ jsxRuntime.jsxs(GateBrand, { children: [
|
|
7358
8269
|
/* @__PURE__ */ jsxRuntime.jsx(GateLogoLink, { href: "https://loafmarkets.com", "aria-label": "Go to loafmarkets.com", children: /* @__PURE__ */ jsxRuntime.jsx(GateLogo, { src: logoSrc, alt: logoAlt }) }),
|
|
7359
8270
|
/* @__PURE__ */ jsxRuntime.jsx(GateBrandDivider, {}),
|
|
7360
|
-
/* @__PURE__ */ jsxRuntime.jsx(GateBetaTag, { children: "
|
|
8271
|
+
/* @__PURE__ */ jsxRuntime.jsx(GateBetaTag, { children: "Beta" })
|
|
7361
8272
|
] }),
|
|
7362
8273
|
/* @__PURE__ */ jsxRuntime.jsxs(GateOtpText, { children: [
|
|
7363
8274
|
"We sent a code to",
|
|
@@ -7696,6 +8607,16 @@ var LoginPopup = ({
|
|
|
7696
8607
|
),
|
|
7697
8608
|
/* @__PURE__ */ jsxRuntime.jsx(GateSubmit, { type: "submit", "aria-label": "Continue", disabled: loading || !email, children: loading ? /* @__PURE__ */ jsxRuntime.jsx(GateSpinner, {}) : /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 12h14M13 6l6 6-6 6" }) }) })
|
|
7698
8609
|
] }),
|
|
8610
|
+
!signInMode && /* @__PURE__ */ jsxRuntime.jsx(GateHandleRow, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
8611
|
+
GateEmailInput,
|
|
8612
|
+
{
|
|
8613
|
+
type: "text",
|
|
8614
|
+
autoComplete: "username",
|
|
8615
|
+
placeholder: "Claim your username (Optional)",
|
|
8616
|
+
value: handle,
|
|
8617
|
+
onChange: (event) => setHandle(event.target.value)
|
|
8618
|
+
}
|
|
8619
|
+
) }),
|
|
7699
8620
|
error && /* @__PURE__ */ jsxRuntime.jsx(StatusMessage, { $error: true, children: error })
|
|
7700
8621
|
] }),
|
|
7701
8622
|
/* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
@@ -7714,7 +8635,7 @@ var LoginPopup = ({
|
|
|
7714
8635
|
/* @__PURE__ */ jsxRuntime.jsxs(GateBrand, { children: [
|
|
7715
8636
|
/* @__PURE__ */ jsxRuntime.jsx(GateLogoLink, { href: "https://loafmarkets.com", "aria-label": "Go to loafmarkets.com", children: /* @__PURE__ */ jsxRuntime.jsx(GateLogo, { src: logoSrc, alt: logoAlt }) }),
|
|
7716
8637
|
/* @__PURE__ */ jsxRuntime.jsx(GateBrandDivider, {}),
|
|
7717
|
-
/* @__PURE__ */ jsxRuntime.jsx(GateBetaTag, { children: "
|
|
8638
|
+
/* @__PURE__ */ jsxRuntime.jsx(GateBetaTag, { children: "Beta" })
|
|
7718
8639
|
] }),
|
|
7719
8640
|
signInMode ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
7720
8641
|
signInStep,
|
|
@@ -7766,7 +8687,7 @@ var LoginPopup = ({
|
|
|
7766
8687
|
/* @__PURE__ */ jsxRuntime.jsxs(Title, { children: [
|
|
7767
8688
|
/* @__PURE__ */ jsxRuntime.jsxs(LogoContainer3, { children: [
|
|
7768
8689
|
/* @__PURE__ */ jsxRuntime.jsx(LogoImage, { src: logoSrc, alt: logoAlt }),
|
|
7769
|
-
/* @__PURE__ */ jsxRuntime.jsx(LogoBeta, { children: "
|
|
8690
|
+
/* @__PURE__ */ jsxRuntime.jsx(LogoBeta, { children: "Beta" })
|
|
7770
8691
|
] }),
|
|
7771
8692
|
/* @__PURE__ */ jsxRuntime.jsx(TitleText, { children: "Enter your access code" })
|
|
7772
8693
|
] }),
|
|
@@ -8430,6 +9351,9 @@ var GateSubmit = styled10__default.default.button`
|
|
|
8430
9351
|
cursor: default;
|
|
8431
9352
|
}
|
|
8432
9353
|
`;
|
|
9354
|
+
var GateHandleRow = styled10__default.default(CodeInputWrapper)`
|
|
9355
|
+
margin-top: 0.6rem;
|
|
9356
|
+
`;
|
|
8433
9357
|
var GateEmailInput = styled10__default.default.input`
|
|
8434
9358
|
flex: 1;
|
|
8435
9359
|
min-width: 0;
|
|
@@ -8715,8 +9639,8 @@ var TransakIframe = styled10__default.default.iframe`
|
|
|
8715
9639
|
border-radius: var(--border-radius, 12px);
|
|
8716
9640
|
`;
|
|
8717
9641
|
var MiniLiveFeed = () => {
|
|
8718
|
-
const [purchases, setPurchases] =
|
|
8719
|
-
|
|
9642
|
+
const [purchases, setPurchases] = React9.useState([]);
|
|
9643
|
+
React9.useEffect(() => {
|
|
8720
9644
|
const handles = [
|
|
8721
9645
|
"Landlord",
|
|
8722
9646
|
"PropertyKing",
|
|
@@ -8972,8 +9896,8 @@ var FactText = styled10__default.default.p`
|
|
|
8972
9896
|
}
|
|
8973
9897
|
`;
|
|
8974
9898
|
var PreparingFacts = () => {
|
|
8975
|
-
const [index, setIndex] =
|
|
8976
|
-
|
|
9899
|
+
const [index, setIndex] = React9.useState(() => Math.floor(Math.random() * PREPARING_FACTS.length));
|
|
9900
|
+
React9.useEffect(() => {
|
|
8977
9901
|
const id = setInterval(() => {
|
|
8978
9902
|
setIndex((i) => (i + 1) % PREPARING_FACTS.length);
|
|
8979
9903
|
}, 4e3);
|
|
@@ -9236,11 +10160,11 @@ function OnboardingGuide({
|
|
|
9236
10160
|
onClaimHandle,
|
|
9237
10161
|
logoSrc = "/Loaf-logo-Banner.png"
|
|
9238
10162
|
}) {
|
|
9239
|
-
const [step, setStep] =
|
|
9240
|
-
const [handle, setHandle] =
|
|
9241
|
-
const [claiming, setClaiming] =
|
|
9242
|
-
const [handleError, setHandleError] =
|
|
9243
|
-
|
|
10163
|
+
const [step, setStep] = React9.useState(onClaimHandle ? "handle" : "funds");
|
|
10164
|
+
const [handle, setHandle] = React9.useState("");
|
|
10165
|
+
const [claiming, setClaiming] = React9.useState(false);
|
|
10166
|
+
const [handleError, setHandleError] = React9.useState("");
|
|
10167
|
+
React9.useEffect(() => {
|
|
9244
10168
|
if (open) {
|
|
9245
10169
|
setStep(onClaimHandle ? "handle" : "funds");
|
|
9246
10170
|
setHandle("");
|
|
@@ -9248,7 +10172,7 @@ function OnboardingGuide({
|
|
|
9248
10172
|
setHandleError("");
|
|
9249
10173
|
}
|
|
9250
10174
|
}, [open, onClaimHandle]);
|
|
9251
|
-
|
|
10175
|
+
React9.useEffect(() => {
|
|
9252
10176
|
if (!open) return;
|
|
9253
10177
|
const onKey = (e) => {
|
|
9254
10178
|
if (e.key === "Escape") onClose();
|
|
@@ -9348,7 +10272,7 @@ function OnboardingGuide({
|
|
|
9348
10272
|
] })
|
|
9349
10273
|
] }) });
|
|
9350
10274
|
}
|
|
9351
|
-
var PropertyCompareBar =
|
|
10275
|
+
var PropertyCompareBar = React9__namespace.forwardRef(
|
|
9352
10276
|
({
|
|
9353
10277
|
className,
|
|
9354
10278
|
addresses,
|
|
@@ -9359,7 +10283,7 @@ var PropertyCompareBar = React5__namespace.forwardRef(
|
|
|
9359
10283
|
price,
|
|
9360
10284
|
...props
|
|
9361
10285
|
}, ref) => {
|
|
9362
|
-
const normalizedAddresses =
|
|
10286
|
+
const normalizedAddresses = React9__namespace.useMemo(() => {
|
|
9363
10287
|
return addresses.map(
|
|
9364
10288
|
(option) => typeof option === "string" ? { id: option, label: option } : option
|
|
9365
10289
|
);
|
|
@@ -9367,11 +10291,11 @@ var PropertyCompareBar = React5__namespace.forwardRef(
|
|
|
9367
10291
|
const hasAddresses = normalizedAddresses.length > 0;
|
|
9368
10292
|
const firstAddressId = normalizedAddresses[0]?.id;
|
|
9369
10293
|
const isControlled = selectedAddressId !== void 0;
|
|
9370
|
-
const [internalSelectedId, setInternalSelectedId] =
|
|
10294
|
+
const [internalSelectedId, setInternalSelectedId] = React9__namespace.useState(
|
|
9371
10295
|
() => isControlled ? void 0 : firstAddressId
|
|
9372
10296
|
);
|
|
9373
10297
|
const resolvedSelectedId = isControlled ? selectedAddressId : internalSelectedId;
|
|
9374
|
-
|
|
10298
|
+
React9__namespace.useEffect(() => {
|
|
9375
10299
|
if (!isControlled) {
|
|
9376
10300
|
setInternalSelectedId((current) => {
|
|
9377
10301
|
if (current != null && normalizedAddresses.some((option) => option.id === current)) {
|
|
@@ -9382,9 +10306,9 @@ var PropertyCompareBar = React5__namespace.forwardRef(
|
|
|
9382
10306
|
}
|
|
9383
10307
|
}, [firstAddressId, isControlled, normalizedAddresses]);
|
|
9384
10308
|
const selectedOption = normalizedAddresses.find((option) => option.id === resolvedSelectedId) ?? normalizedAddresses[0];
|
|
9385
|
-
const [isDropdownOpen, setIsDropdownOpen] =
|
|
9386
|
-
const dropdownRef =
|
|
9387
|
-
|
|
10309
|
+
const [isDropdownOpen, setIsDropdownOpen] = React9__namespace.useState(false);
|
|
10310
|
+
const dropdownRef = React9__namespace.useRef(null);
|
|
10311
|
+
React9__namespace.useEffect(() => {
|
|
9388
10312
|
if (!isDropdownOpen) return;
|
|
9389
10313
|
const handleClick = (event) => {
|
|
9390
10314
|
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
|
@@ -9777,8 +10701,8 @@ var PriceChange = styled10__default.default.span`
|
|
|
9777
10701
|
color: ${(props) => props.$isPositive == null ? "var(--color-text-secondary, rgba(255, 255, 255, 0.6))" : props.$isPositive ? "var(--color-positive, #0ecb81)" : "var(--color-negative, #f6465d)"};
|
|
9778
10702
|
`;
|
|
9779
10703
|
function useMaxWidth(maxWidth) {
|
|
9780
|
-
const [match, setMatch] =
|
|
9781
|
-
|
|
10704
|
+
const [match, setMatch] = React9.useState(() => typeof window !== "undefined" && window.matchMedia(`(max-width: ${maxWidth}px)`).matches);
|
|
10705
|
+
React9.useEffect(() => {
|
|
9782
10706
|
if (typeof window === "undefined") return;
|
|
9783
10707
|
const mq = window.matchMedia(`(max-width: ${maxWidth}px)`);
|
|
9784
10708
|
const onChange = () => setMatch(mq.matches);
|
|
@@ -9803,14 +10727,14 @@ function GalleryMapSection({
|
|
|
9803
10727
|
hideSuburb = false,
|
|
9804
10728
|
featuresSlot
|
|
9805
10729
|
}) {
|
|
9806
|
-
const [carouselIndex, setCarouselIndex] =
|
|
9807
|
-
const [showVideo, setShowVideo] =
|
|
9808
|
-
const [autoPlaying, setAutoPlaying] =
|
|
9809
|
-
const [showFullMap, setShowFullMap] =
|
|
9810
|
-
const [showFullVideo, setShowFullVideo] =
|
|
10730
|
+
const [carouselIndex, setCarouselIndex] = React9.useState(0);
|
|
10731
|
+
const [showVideo, setShowVideo] = React9.useState(false);
|
|
10732
|
+
const [autoPlaying, setAutoPlaying] = React9.useState(autoPlay);
|
|
10733
|
+
const [showFullMap, setShowFullMap] = React9.useState(false);
|
|
10734
|
+
const [showFullVideo, setShowFullVideo] = React9.useState(false);
|
|
9811
10735
|
const isMobile = useMaxWidth(768);
|
|
9812
10736
|
const resolvedMapUrl = mapUrl ?? (tokenName ? `/map/${tokenName}?embed=true&zoom=14&hideOthers=true` : "about:blank");
|
|
9813
|
-
|
|
10737
|
+
React9.useEffect(() => {
|
|
9814
10738
|
if (!autoPlaying || images.length <= 1) return;
|
|
9815
10739
|
const interval = setInterval(() => {
|
|
9816
10740
|
setCarouselIndex((p) => (p + 1) % images.length);
|
|
@@ -9997,10 +10921,10 @@ function GalleryMapSection({
|
|
|
9997
10921
|
] });
|
|
9998
10922
|
}
|
|
9999
10923
|
function StackedGallery({ images, onPhotoClick }) {
|
|
10000
|
-
const [expanded, setExpanded] =
|
|
10001
|
-
const [cardH, setCardH] =
|
|
10002
|
-
const wrapRef =
|
|
10003
|
-
|
|
10924
|
+
const [expanded, setExpanded] = React9.useState(false);
|
|
10925
|
+
const [cardH, setCardH] = React9.useState(200);
|
|
10926
|
+
const wrapRef = React9.useRef(null);
|
|
10927
|
+
React9.useEffect(() => {
|
|
10004
10928
|
const el = wrapRef.current;
|
|
10005
10929
|
if (!el) return;
|
|
10006
10930
|
const measure = () => {
|
|
@@ -10012,7 +10936,7 @@ function StackedGallery({ images, onPhotoClick }) {
|
|
|
10012
10936
|
ro.observe(el);
|
|
10013
10937
|
return () => ro.disconnect();
|
|
10014
10938
|
}, []);
|
|
10015
|
-
|
|
10939
|
+
React9.useEffect(() => {
|
|
10016
10940
|
const el = wrapRef.current;
|
|
10017
10941
|
if (!el) return;
|
|
10018
10942
|
const update = () => {
|
|
@@ -10104,21 +11028,21 @@ function StackedGallery({ images, onPhotoClick }) {
|
|
|
10104
11028
|
] });
|
|
10105
11029
|
}
|
|
10106
11030
|
function FullMapPopup({ onClose, mapUrl }) {
|
|
10107
|
-
|
|
11031
|
+
React9.useEffect(() => {
|
|
10108
11032
|
const handleEsc = (e) => {
|
|
10109
11033
|
if (e.key === "Escape") onClose();
|
|
10110
11034
|
};
|
|
10111
11035
|
window.addEventListener("keydown", handleEsc);
|
|
10112
11036
|
return () => window.removeEventListener("keydown", handleEsc);
|
|
10113
11037
|
}, [onClose]);
|
|
10114
|
-
|
|
11038
|
+
React9.useEffect(() => {
|
|
10115
11039
|
const handleMessage = (e) => {
|
|
10116
11040
|
if (e.data === "loaf:close-embed") onClose();
|
|
10117
11041
|
};
|
|
10118
11042
|
window.addEventListener("message", handleMessage);
|
|
10119
11043
|
return () => window.removeEventListener("message", handleMessage);
|
|
10120
11044
|
}, [onClose]);
|
|
10121
|
-
const iframeRef =
|
|
11045
|
+
const iframeRef = React9.useCallback((node) => {
|
|
10122
11046
|
if (!node) return;
|
|
10123
11047
|
const handleLoad = () => {
|
|
10124
11048
|
try {
|
|
@@ -10595,10 +11519,10 @@ function PropertyOverview({
|
|
|
10595
11519
|
beta = false,
|
|
10596
11520
|
marketHours
|
|
10597
11521
|
}) {
|
|
10598
|
-
const [isDescExpanded, setDescExpanded] =
|
|
10599
|
-
const [showDividendHistory, setShowDividendHistory] =
|
|
10600
|
-
const [copiedAddress, setCopiedAddress] =
|
|
10601
|
-
const [statsTab, setStatsTab] =
|
|
11522
|
+
const [isDescExpanded, setDescExpanded] = React9.useState(false);
|
|
11523
|
+
const [showDividendHistory, setShowDividendHistory] = React9.useState(false);
|
|
11524
|
+
const [copiedAddress, setCopiedAddress] = React9.useState(false);
|
|
11525
|
+
const [statsTab, setStatsTab] = React9.useState("token");
|
|
10602
11526
|
const description = descriptionProp ?? overviewData?.description ?? DEFAULT_DESCRIPTION;
|
|
10603
11527
|
const landSize = landProp ?? overviewData?.landSizeSqm ?? null;
|
|
10604
11528
|
const buildingSize = buildingProp ?? overviewData?.buildingSizeSqm ?? null;
|
|
@@ -10907,8 +11831,8 @@ function DividendHistoryPopup({
|
|
|
10907
11831
|
onClose
|
|
10908
11832
|
}) {
|
|
10909
11833
|
const { records, priceHistory } = generateDividendHistory(valuation ?? 0, totalTokens ?? 0, tokenPrice ?? 0, realPriceHistory);
|
|
10910
|
-
const [highlightedIdx, setHighlightedIdx] =
|
|
10911
|
-
|
|
11834
|
+
const [highlightedIdx, setHighlightedIdx] = React9.useState(null);
|
|
11835
|
+
React9.useEffect(() => {
|
|
10912
11836
|
const handleEsc = (e) => {
|
|
10913
11837
|
if (e.key === "Escape") onClose();
|
|
10914
11838
|
};
|
|
@@ -11755,8 +12679,8 @@ var OfferPricePulse = styled10__default.default.span`
|
|
|
11755
12679
|
`;
|
|
11756
12680
|
var eventTypes = ["Leased", "Renovated", "Extended", "Rebuilt", "Rezoned", "DA Approval"];
|
|
11757
12681
|
function PropertyHistory() {
|
|
11758
|
-
const [historyFilter, setHistoryFilter] =
|
|
11759
|
-
const pastSales =
|
|
12682
|
+
const [historyFilter, setHistoryFilter] = React9.useState("all");
|
|
12683
|
+
const pastSales = React9.useMemo(() => generateMockPastSales(), []);
|
|
11760
12684
|
const loafListing = pastSales.find((sale) => sale.type === "Listed");
|
|
11761
12685
|
const loafEvents = pastSales.filter(
|
|
11762
12686
|
(sale) => sale.ownershipPeriod === "current" && sale.type !== "Listed" && (sale.id === "leased-current" || sale.id === "da-approval")
|
|
@@ -11893,7 +12817,7 @@ function PropertyHistory() {
|
|
|
11893
12817
|
] });
|
|
11894
12818
|
}
|
|
11895
12819
|
function EventDetails({ event }) {
|
|
11896
|
-
const [expanded, setExpanded] =
|
|
12820
|
+
const [expanded, setExpanded] = React9.useState(false);
|
|
11897
12821
|
const toggleExpand = () => {
|
|
11898
12822
|
setExpanded((prev) => !prev);
|
|
11899
12823
|
};
|
|
@@ -12390,9 +13314,9 @@ var COMING_SOON_TITLES = [
|
|
|
12390
13314
|
"Risk Disclosure Statement"
|
|
12391
13315
|
];
|
|
12392
13316
|
function PropertyDocuments({ documentsData, highlightDocument, onClearHighlight }) {
|
|
12393
|
-
const highlightRef =
|
|
12394
|
-
const [previewUrl, setPreviewUrl] =
|
|
12395
|
-
const [previewTitle, setPreviewTitle] =
|
|
13317
|
+
const highlightRef = React9.useRef(null);
|
|
13318
|
+
const [previewUrl, setPreviewUrl] = React9.useState(null);
|
|
13319
|
+
const [previewTitle, setPreviewTitle] = React9.useState(null);
|
|
12396
13320
|
const backendDocuments = Array.isArray(documentsData?.documents) ? documentsData.documents : [];
|
|
12397
13321
|
const liveDocs = backendDocuments.filter(
|
|
12398
13322
|
(d) => Boolean(d.documentUrl) && isSafeUrl(d.documentUrl) && LIVE_TITLE_PATTERNS.some((rx) => rx.test(d.title))
|
|
@@ -12407,7 +13331,7 @@ function PropertyDocuments({ documentsData, highlightDocument, onClearHighlight
|
|
|
12407
13331
|
setPreviewUrl(doc.documentUrl);
|
|
12408
13332
|
setPreviewTitle(doc.title);
|
|
12409
13333
|
};
|
|
12410
|
-
|
|
13334
|
+
React9.useEffect(() => {
|
|
12411
13335
|
if (highlightDocument && highlightRef.current) {
|
|
12412
13336
|
highlightRef.current.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
12413
13337
|
}
|
|
@@ -12416,7 +13340,7 @@ function PropertyDocuments({ documentsData, highlightDocument, onClearHighlight
|
|
|
12416
13340
|
if (hit) selectPreview(hit);
|
|
12417
13341
|
}
|
|
12418
13342
|
}, [highlightDocument]);
|
|
12419
|
-
|
|
13343
|
+
React9.useEffect(() => {
|
|
12420
13344
|
if (!highlightDocument || !onClearHighlight) return;
|
|
12421
13345
|
const handler = (e) => {
|
|
12422
13346
|
if (highlightRef.current && !highlightRef.current.contains(e.target)) {
|
|
@@ -12812,6 +13736,32 @@ var SignInButton = styled10__default.default.button`
|
|
|
12812
13736
|
background: rgba(240, 185, 11, 0.1);
|
|
12813
13737
|
}
|
|
12814
13738
|
`;
|
|
13739
|
+
var SORT_COLUMNS = [
|
|
13740
|
+
{ field: "price", label: "Price" },
|
|
13741
|
+
{ field: "change", label: "24h Change" },
|
|
13742
|
+
{ field: "volume", label: "24h Volume", hideOnPhone: true },
|
|
13743
|
+
{ field: "value", label: "Asset Value", hideOnPhone: true }
|
|
13744
|
+
];
|
|
13745
|
+
function sortValue(item, field) {
|
|
13746
|
+
switch (field) {
|
|
13747
|
+
case "volume":
|
|
13748
|
+
return item.volume24h ?? null;
|
|
13749
|
+
case "price":
|
|
13750
|
+
return item.price ?? null;
|
|
13751
|
+
case "change":
|
|
13752
|
+
return item.changePercent ?? null;
|
|
13753
|
+
case "value":
|
|
13754
|
+
return item.propertyValue ?? null;
|
|
13755
|
+
}
|
|
13756
|
+
}
|
|
13757
|
+
function fmtWholeUsd(value) {
|
|
13758
|
+
if (value == null) return null;
|
|
13759
|
+
return `$${value.toLocaleString(void 0, { maximumFractionDigits: 0 })}`;
|
|
13760
|
+
}
|
|
13761
|
+
function fmtPrice(value) {
|
|
13762
|
+
if (value == null) return null;
|
|
13763
|
+
return `$${value.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
13764
|
+
}
|
|
12815
13765
|
function displayName(item) {
|
|
12816
13766
|
if (item.name) return item.name;
|
|
12817
13767
|
return item.tokenName.charAt(0).toUpperCase() + item.tokenName.slice(1);
|
|
@@ -12825,24 +13775,79 @@ function AssetSelectorBar({
|
|
|
12825
13775
|
selectorItems,
|
|
12826
13776
|
onSelect,
|
|
12827
13777
|
trailing,
|
|
13778
|
+
afterName,
|
|
12828
13779
|
imageUrl,
|
|
12829
13780
|
badgeLabel,
|
|
12830
|
-
tickerPrimary
|
|
13781
|
+
tickerPrimary,
|
|
13782
|
+
compactName
|
|
12831
13783
|
}) {
|
|
12832
|
-
const [isDropdownOpen, setIsDropdownOpen] =
|
|
13784
|
+
const [isDropdownOpen, setIsDropdownOpen] = React9.useState(false);
|
|
13785
|
+
const [nameTip, setNameTip] = React9.useState(null);
|
|
13786
|
+
const anchorRef = React9.useRef(null);
|
|
13787
|
+
const [dropdownPos, setDropdownPos] = React9.useState(null);
|
|
13788
|
+
React9.useLayoutEffect(() => {
|
|
13789
|
+
if (!isDropdownOpen) return;
|
|
13790
|
+
const measure = () => {
|
|
13791
|
+
const rect = anchorRef.current?.getBoundingClientRect();
|
|
13792
|
+
if (rect) setDropdownPos({ top: rect.bottom + 6, left: rect.left });
|
|
13793
|
+
};
|
|
13794
|
+
measure();
|
|
13795
|
+
window.addEventListener("resize", measure);
|
|
13796
|
+
window.addEventListener("scroll", measure, true);
|
|
13797
|
+
return () => {
|
|
13798
|
+
window.removeEventListener("resize", measure);
|
|
13799
|
+
window.removeEventListener("scroll", measure, true);
|
|
13800
|
+
};
|
|
13801
|
+
}, [isDropdownOpen]);
|
|
13802
|
+
const [sortField, setSortField] = React9.useState("volume");
|
|
13803
|
+
const [sortDir, setSortDir] = React9.useState("desc");
|
|
12833
13804
|
const hasItems = selectorItems && selectorItems.length > 0;
|
|
13805
|
+
const hasMarketData = !!selectorItems?.some(
|
|
13806
|
+
(i) => i.price != null || i.changePercent != null || i.volume24h != null || i.propertyValue != null
|
|
13807
|
+
);
|
|
13808
|
+
const sortedItems = React9.useMemo(() => {
|
|
13809
|
+
if (!selectorItems) return [];
|
|
13810
|
+
if (!hasMarketData) return [...selectorItems];
|
|
13811
|
+
const dir = sortDir === "desc" ? -1 : 1;
|
|
13812
|
+
const missing = sortDir === "desc" ? -Infinity : Infinity;
|
|
13813
|
+
return [...selectorItems].sort((a, b) => {
|
|
13814
|
+
const av = sortValue(a, sortField) ?? missing;
|
|
13815
|
+
const bv = sortValue(b, sortField) ?? missing;
|
|
13816
|
+
return (av - bv) * dir || 0;
|
|
13817
|
+
});
|
|
13818
|
+
}, [selectorItems, hasMarketData, sortField, sortDir]);
|
|
13819
|
+
const handleSortClick = (field) => {
|
|
13820
|
+
if (field === sortField) {
|
|
13821
|
+
setSortDir((d) => d === "desc" ? "asc" : "desc");
|
|
13822
|
+
} else {
|
|
13823
|
+
setSortField(field);
|
|
13824
|
+
setSortDir("desc");
|
|
13825
|
+
}
|
|
13826
|
+
};
|
|
12834
13827
|
const currentTicker = currentTokenName ? selectorItems?.find((i) => i.tokenName.toLowerCase() === currentTokenName.toLowerCase())?.ticker : void 0;
|
|
12835
13828
|
const metrics = metricsProp ?? [
|
|
12836
13829
|
...tokenPrice != null ? [{ label: "Unit Price", value: `$${tokenPrice.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`, accent: true }] : [],
|
|
12837
13830
|
...offeringValuation != null ? [{ label: "Offering Valuation", value: `$${offeringValuation.toLocaleString()}` }] : []
|
|
12838
13831
|
];
|
|
12839
|
-
return /* @__PURE__ */ jsxRuntime.jsx(AssetSelectorWrapper, { children: /* @__PURE__ */ jsxRuntime.jsxs(IPOAssetSelector, { children: [
|
|
13832
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AssetSelectorWrapper, { children: /* @__PURE__ */ jsxRuntime.jsxs(IPOAssetSelector, { ref: anchorRef, children: [
|
|
12840
13833
|
imageUrl && /* @__PURE__ */ jsxRuntime.jsx(AssetThumbnail, { src: imageUrl, alt: propertyName }),
|
|
12841
|
-
/* @__PURE__ */ jsxRuntime.jsx(AssetSelectorDropdown, { $flush: tickerPrimary, onClick: () => hasItems && setIsDropdownOpen((prev) => !prev), children: /* @__PURE__ */ jsxRuntime.jsxs(AssetName, { children: [
|
|
13834
|
+
/* @__PURE__ */ jsxRuntime.jsx(AssetSelectorDropdown, { $flush: tickerPrimary, $noShrink: !!compactName && !!currentTicker && !tickerPrimary, onClick: () => hasItems && setIsDropdownOpen((prev) => !prev), children: /* @__PURE__ */ jsxRuntime.jsxs(AssetName, { children: [
|
|
12842
13835
|
tickerPrimary && currentTicker ? /* @__PURE__ */ jsxRuntime.jsxs(AssetIdentity, { children: [
|
|
12843
13836
|
/* @__PURE__ */ jsxRuntime.jsx(AssetTicker, { children: currentTicker.toUpperCase() }),
|
|
12844
13837
|
/* @__PURE__ */ jsxRuntime.jsx(AssetSubName, { children: propertyName })
|
|
12845
|
-
] }) : /* @__PURE__ */ jsxRuntime.
|
|
13838
|
+
] }) : compactName && currentTicker ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
13839
|
+
CompactName,
|
|
13840
|
+
{
|
|
13841
|
+
className: "asset-bar-compact-name",
|
|
13842
|
+
"aria-label": propertyName,
|
|
13843
|
+
onMouseEnter: (e) => {
|
|
13844
|
+
const r = e.currentTarget.getBoundingClientRect();
|
|
13845
|
+
setNameTip({ left: r.left + r.width / 2, top: r.bottom + 10 });
|
|
13846
|
+
},
|
|
13847
|
+
onMouseLeave: () => setNameTip(null),
|
|
13848
|
+
children: currentTicker.toUpperCase()
|
|
13849
|
+
}
|
|
13850
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
12846
13851
|
propertyName,
|
|
12847
13852
|
currentTicker ? ` (${currentTicker.toUpperCase()})` : ""
|
|
12848
13853
|
] }),
|
|
@@ -12860,6 +13865,7 @@ function AssetSelectorBar({
|
|
|
12860
13865
|
}
|
|
12861
13866
|
)
|
|
12862
13867
|
] }) }),
|
|
13868
|
+
afterName != null && /* @__PURE__ */ jsxRuntime.jsx(AfterNameSlot, { className: "asset-bar-after-name", children: afterName }),
|
|
12863
13869
|
metrics.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(SelectorMetrics, { children: metrics.map((m, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "contents" }, children: [
|
|
12864
13870
|
i > 0 && /* @__PURE__ */ jsxRuntime.jsx(Separator, { children: "|" }),
|
|
12865
13871
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
@@ -12875,40 +13881,128 @@ function AssetSelectorBar({
|
|
|
12875
13881
|
] })
|
|
12876
13882
|
] }, m.label)) }),
|
|
12877
13883
|
trailing != null && /* @__PURE__ */ jsxRuntime.jsx(TrailingWrapper, { className: "asset-bar-trailing", children: /* @__PURE__ */ jsxRuntime.jsx(TrailingInner, { children: trailing }) }),
|
|
12878
|
-
|
|
12879
|
-
|
|
12880
|
-
|
|
12881
|
-
const statusLabel = status === "PENDING" ? "Preparing" : status === "CLOSED" ? "Closed" : status === "CANCELLED" ? "Cancelled" : null;
|
|
12882
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
12883
|
-
IPOOption,
|
|
13884
|
+
nameTip != null && typeof document !== "undefined" && ReactDOM.createPortal(
|
|
13885
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13886
|
+
NameTip,
|
|
12884
13887
|
{
|
|
12885
|
-
|
|
12886
|
-
|
|
12887
|
-
|
|
12888
|
-
|
|
12889
|
-
|
|
12890
|
-
|
|
12891
|
-
|
|
12892
|
-
|
|
12893
|
-
|
|
12894
|
-
|
|
12895
|
-
|
|
12896
|
-
|
|
12897
|
-
|
|
12898
|
-
|
|
12899
|
-
|
|
12900
|
-
|
|
12901
|
-
/* @__PURE__ */ jsxRuntime.
|
|
12902
|
-
|
|
12903
|
-
|
|
12904
|
-
|
|
12905
|
-
|
|
12906
|
-
|
|
12907
|
-
|
|
12908
|
-
|
|
12909
|
-
|
|
12910
|
-
|
|
12911
|
-
|
|
13888
|
+
role: "tooltip",
|
|
13889
|
+
"data-testid": "asset-name-tip",
|
|
13890
|
+
style: { position: "fixed", left: nameTip.left, top: nameTip.top, transform: "translateX(-50%)", zIndex: 10050 },
|
|
13891
|
+
children: propertyName
|
|
13892
|
+
}
|
|
13893
|
+
),
|
|
13894
|
+
document.body
|
|
13895
|
+
),
|
|
13896
|
+
isDropdownOpen && hasItems && dropdownPos != null && typeof document !== "undefined" && ReactDOM.createPortal(
|
|
13897
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
13898
|
+
IPODropdown,
|
|
13899
|
+
{
|
|
13900
|
+
$wide: hasMarketData,
|
|
13901
|
+
style: { position: "fixed", top: dropdownPos.top, left: dropdownPos.left, zIndex: 10050 },
|
|
13902
|
+
children: [
|
|
13903
|
+
hasMarketData && /* @__PURE__ */ jsxRuntime.jsxs(HeaderRow, { children: [
|
|
13904
|
+
/* @__PURE__ */ jsxRuntime.jsx(HeaderLabel, { children: "Asset" }),
|
|
13905
|
+
SORT_COLUMNS.map(({ field, label, hideOnPhone }) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
13906
|
+
SortButton,
|
|
13907
|
+
{
|
|
13908
|
+
type: "button",
|
|
13909
|
+
$active: sortField === field,
|
|
13910
|
+
$hideOnPhone: hideOnPhone,
|
|
13911
|
+
onClick: (e) => {
|
|
13912
|
+
e.stopPropagation();
|
|
13913
|
+
handleSortClick(field);
|
|
13914
|
+
},
|
|
13915
|
+
children: [
|
|
13916
|
+
label,
|
|
13917
|
+
sortField === field ? sortDir === "desc" ? " \u25BE" : " \u25B4" : ""
|
|
13918
|
+
]
|
|
13919
|
+
},
|
|
13920
|
+
field
|
|
13921
|
+
))
|
|
13922
|
+
] }),
|
|
13923
|
+
sortedItems.map((item) => {
|
|
13924
|
+
const isCurrent = item.tokenName === currentTokenName;
|
|
13925
|
+
const status = item.status?.toUpperCase();
|
|
13926
|
+
const statusLabel = status === "PENDING" ? "Preparing" : status === "CLOSED" ? "Closed" : status === "CANCELLED" ? "Cancelled" : null;
|
|
13927
|
+
const price = fmtPrice(item.price);
|
|
13928
|
+
const change = item.changePercent;
|
|
13929
|
+
const vol = fmtWholeUsd(item.volume24h);
|
|
13930
|
+
const val = fmtWholeUsd(item.propertyValue);
|
|
13931
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
13932
|
+
IPOOption,
|
|
13933
|
+
{
|
|
13934
|
+
"data-selector-row": item.tokenName,
|
|
13935
|
+
onClick: () => {
|
|
13936
|
+
if (!isCurrent && onSelect) {
|
|
13937
|
+
onSelect(item.tokenName);
|
|
13938
|
+
}
|
|
13939
|
+
setIsDropdownOpen(false);
|
|
13940
|
+
},
|
|
13941
|
+
$selected: isCurrent,
|
|
13942
|
+
style: {
|
|
13943
|
+
cursor: isCurrent ? "default" : "pointer",
|
|
13944
|
+
// Arena gold for competition assets — the same restrained cue as
|
|
13945
|
+
// the home Overview row (gold left crust + pooled spotlight, no
|
|
13946
|
+
// badge). backgroundImage LONGHAND: jsdom drops gradients from
|
|
13947
|
+
// the shorthand and UI6-AS-508 asserts it.
|
|
13948
|
+
...item.isCompetition ? {
|
|
13949
|
+
// Straight crust bar — an inset box-shadow hugged the
|
|
13950
|
+
// row's rounded corners and read as a curved rim.
|
|
13951
|
+
// Left corners squared so the bar runs dead straight
|
|
13952
|
+
// like the home Overview arena row; right side keeps
|
|
13953
|
+
// the row rounding.
|
|
13954
|
+
borderLeft: "3px solid #E0B377",
|
|
13955
|
+
borderRadius: "0 7px 7px 0",
|
|
13956
|
+
backgroundImage: "radial-gradient(140% 90% at 0% 50%, rgba(224, 179, 119, 0.18) 0%, rgba(224, 179, 119, 0.06) 40%, transparent 68%)"
|
|
13957
|
+
} : {}
|
|
13958
|
+
},
|
|
13959
|
+
children: hasMarketData ? /* @__PURE__ */ jsxRuntime.jsxs(MarketRowInner, { children: [
|
|
13960
|
+
/* @__PURE__ */ jsxRuntime.jsxs(IdentityCell, { children: [
|
|
13961
|
+
item.imageUrl && /* @__PURE__ */ jsxRuntime.jsx(IPOOptionThumb, { src: item.imageUrl, alt: displayName(item) }),
|
|
13962
|
+
/* @__PURE__ */ jsxRuntime.jsxs(IPOOptionIdentity, { children: [
|
|
13963
|
+
/* @__PURE__ */ jsxRuntime.jsxs(IPOOptionName, { children: [
|
|
13964
|
+
/* @__PURE__ */ jsxRuntime.jsx(MarketTicker, { children: (item.ticker || displayName(item)).toUpperCase() }),
|
|
13965
|
+
item.typeBadge && /* @__PURE__ */ jsxRuntime.jsx(TypePill, { style: { color: item.typeBadge.color, background: item.typeBadge.background }, children: item.typeBadge.label }),
|
|
13966
|
+
item.badge && /* @__PURE__ */ jsxRuntime.jsx(CompetitionBadge, { children: item.badge })
|
|
13967
|
+
] }),
|
|
13968
|
+
/* @__PURE__ */ jsxRuntime.jsxs(IPOOptionLocation, { children: [
|
|
13969
|
+
item.streetAddress,
|
|
13970
|
+
statusLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { children: statusLabel })
|
|
13971
|
+
] })
|
|
13972
|
+
] })
|
|
13973
|
+
] }),
|
|
13974
|
+
/* @__PURE__ */ jsxRuntime.jsx(PriceCell, { children: price ?? "\u2014" }),
|
|
13975
|
+
change != null ? /* @__PURE__ */ jsxRuntime.jsxs(ChangeCell, { $positive: change >= 0, children: [
|
|
13976
|
+
change >= 0 ? "+" : "",
|
|
13977
|
+
change.toFixed(2),
|
|
13978
|
+
"%"
|
|
13979
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx(MutedCell, { children: "\u2014" }),
|
|
13980
|
+
/* @__PURE__ */ jsxRuntime.jsx(MutedCell, { $hideOnPhone: true, children: vol ?? "\u2014" }),
|
|
13981
|
+
/* @__PURE__ */ jsxRuntime.jsx(MutedCell, { $hideOnPhone: true, children: val ?? "\u2014" })
|
|
13982
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(IPOOptionInner, { children: [
|
|
13983
|
+
item.imageUrl && /* @__PURE__ */ jsxRuntime.jsx(IPOOptionThumb, { src: item.imageUrl, alt: displayName(item) }),
|
|
13984
|
+
/* @__PURE__ */ jsxRuntime.jsxs(IPOOptionIdentity, { children: [
|
|
13985
|
+
/* @__PURE__ */ jsxRuntime.jsxs(IPOOptionName, { children: [
|
|
13986
|
+
displayName(item),
|
|
13987
|
+
item.ticker ? ` (${item.ticker})` : "",
|
|
13988
|
+
item.badge && /* @__PURE__ */ jsxRuntime.jsx(CompetitionBadge, { children: item.badge })
|
|
13989
|
+
] }),
|
|
13990
|
+
/* @__PURE__ */ jsxRuntime.jsxs(IPOOptionLocation, { children: [
|
|
13991
|
+
item.streetAddress,
|
|
13992
|
+
item.suburb ? `, ${item.suburb}` : "",
|
|
13993
|
+
statusLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { children: statusLabel })
|
|
13994
|
+
] })
|
|
13995
|
+
] })
|
|
13996
|
+
] })
|
|
13997
|
+
},
|
|
13998
|
+
item.tokenName
|
|
13999
|
+
);
|
|
14000
|
+
})
|
|
14001
|
+
]
|
|
14002
|
+
}
|
|
14003
|
+
),
|
|
14004
|
+
document.body
|
|
14005
|
+
)
|
|
12912
14006
|
] }) });
|
|
12913
14007
|
}
|
|
12914
14008
|
var AssetSelectorWrapper = styled10__default.default.div`
|
|
@@ -12970,7 +14064,9 @@ var AssetSelectorDropdown = styled10__default.default.div`
|
|
|
12970
14064
|
padding: ${(p) => p.$flush ? "0.25rem 0" : "0.5rem"};
|
|
12971
14065
|
border-radius: 8px;
|
|
12972
14066
|
transition: ${(p) => p.$flush ? "none" : "all 0.2s ease"};
|
|
12973
|
-
|
|
14067
|
+
/* $noShrink (compactName): a four-letter ticker is already the floor — it must
|
|
14068
|
+
never clip to "Y" when the bar is starved; the trailing scrolls instead. */
|
|
14069
|
+
flex: ${(p) => p.$noShrink ? "0 0 auto" : "0 1 auto"};
|
|
12974
14070
|
min-width: 0;
|
|
12975
14071
|
overflow: hidden;
|
|
12976
14072
|
&:hover { background-color: ${(p) => p.$flush ? "transparent" : "rgba(255,255,255,0.05)"}; }
|
|
@@ -13027,6 +14123,35 @@ var AssetName = styled10__default.default.div`
|
|
|
13027
14123
|
svg { margin-left: 0.4rem; width: 16px; height: 16px; }
|
|
13028
14124
|
}
|
|
13029
14125
|
`;
|
|
14126
|
+
var CompactName = styled10__default.default.span`
|
|
14127
|
+
white-space: nowrap;
|
|
14128
|
+
letter-spacing: 0.01em;
|
|
14129
|
+
`;
|
|
14130
|
+
var NameTip = styled10__default.default.div`
|
|
14131
|
+
background: #1a1d24;
|
|
14132
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
14133
|
+
border-radius: 8px;
|
|
14134
|
+
padding: 0.45rem 0.7rem;
|
|
14135
|
+
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
|
|
14136
|
+
pointer-events: none;
|
|
14137
|
+
white-space: nowrap;
|
|
14138
|
+
color: #fff;
|
|
14139
|
+
font-size: 0.78rem;
|
|
14140
|
+
font-weight: 600;
|
|
14141
|
+
letter-spacing: -0.01em;
|
|
14142
|
+
&::before {
|
|
14143
|
+
content: '';
|
|
14144
|
+
position: absolute;
|
|
14145
|
+
top: -5px;
|
|
14146
|
+
left: 50%;
|
|
14147
|
+
transform: translateX(-50%) rotate(45deg);
|
|
14148
|
+
width: 8px;
|
|
14149
|
+
height: 8px;
|
|
14150
|
+
background: #1a1d24;
|
|
14151
|
+
border-left: 1px solid rgba(255, 255, 255, 0.1);
|
|
14152
|
+
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
14153
|
+
}
|
|
14154
|
+
`;
|
|
13030
14155
|
var SelectorMetrics = styled10__default.default.div`
|
|
13031
14156
|
display: flex;
|
|
13032
14157
|
align-items: center;
|
|
@@ -13047,6 +14172,12 @@ var SelectorMetrics = styled10__default.default.div`
|
|
|
13047
14172
|
padding: 0.4rem 0.5rem;
|
|
13048
14173
|
}
|
|
13049
14174
|
`;
|
|
14175
|
+
var AfterNameSlot = styled10__default.default.div`
|
|
14176
|
+
display: flex;
|
|
14177
|
+
align-items: center;
|
|
14178
|
+
flex-shrink: 0;
|
|
14179
|
+
min-width: 0;
|
|
14180
|
+
`;
|
|
13050
14181
|
var TrailingWrapper = styled10__default.default.div`
|
|
13051
14182
|
display: flex;
|
|
13052
14183
|
align-items: center;
|
|
@@ -13118,14 +14249,16 @@ var IPODropdown = styled10__default.default.div`
|
|
|
13118
14249
|
position: absolute;
|
|
13119
14250
|
top: calc(100% + 6px);
|
|
13120
14251
|
left: 0;
|
|
13121
|
-
width: 320px;
|
|
14252
|
+
width: ${(p) => p.$wide ? "min(660px, calc(100vw - 24px))" : "320px"};
|
|
13122
14253
|
background: #0d0d0f;
|
|
13123
14254
|
border-radius: 10px;
|
|
13124
14255
|
box-shadow: 0 12px 32px rgba(0,0,0,0.7), 0 0 0 1px rgba(255,255,255,0.06);
|
|
13125
14256
|
z-index: 1000;
|
|
13126
|
-
max-height:
|
|
14257
|
+
max-height: 360px;
|
|
13127
14258
|
overflow-y: auto;
|
|
13128
|
-
|
|
14259
|
+
/* Market mode: NO top padding — the sticky header must sit flush against the
|
|
14260
|
+
container's top edge so scrolling rows can never peek above/behind it. */
|
|
14261
|
+
padding: ${(p) => p.$wide ? "0 0 4px" : "4px"};
|
|
13129
14262
|
|
|
13130
14263
|
scrollbar-width: thin;
|
|
13131
14264
|
scrollbar-color: rgba(255,255,255,0.12) transparent;
|
|
@@ -13133,8 +14266,8 @@ var IPODropdown = styled10__default.default.div`
|
|
|
13133
14266
|
&::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.12); border-radius: 2px; }
|
|
13134
14267
|
|
|
13135
14268
|
@media (max-width: 480px) {
|
|
13136
|
-
width: 280px;
|
|
13137
|
-
max-height:
|
|
14269
|
+
width: ${(p) => p.$wide ? "min(360px, calc(100vw - 16px))" : "280px"};
|
|
14270
|
+
max-height: 300px;
|
|
13138
14271
|
border-radius: 8px;
|
|
13139
14272
|
}
|
|
13140
14273
|
animation: slideDown 0.2s cubic-bezier(0.16,1,0.3,1);
|
|
@@ -13143,6 +14276,99 @@ var IPODropdown = styled10__default.default.div`
|
|
|
13143
14276
|
to { opacity: 1; transform: translateY(0); }
|
|
13144
14277
|
}
|
|
13145
14278
|
`;
|
|
14279
|
+
var MARKET_GRID = "minmax(0, 1fr) 76px 92px 108px 118px";
|
|
14280
|
+
var MARKET_GRID_PHONE = "minmax(0, 1fr) 76px 92px";
|
|
14281
|
+
var HeaderRow = styled10__default.default.div`
|
|
14282
|
+
position: sticky;
|
|
14283
|
+
top: 0;
|
|
14284
|
+
z-index: 2;
|
|
14285
|
+
display: grid;
|
|
14286
|
+
grid-template-columns: ${MARKET_GRID};
|
|
14287
|
+
gap: 10px;
|
|
14288
|
+
align-items: center;
|
|
14289
|
+
padding: 0.5rem 0.85rem 0.45rem;
|
|
14290
|
+
background: #0d0d0f;
|
|
14291
|
+
border-bottom: 1px solid rgba(255,255,255,0.06);
|
|
14292
|
+
|
|
14293
|
+
@media (max-width: 480px) { grid-template-columns: ${MARKET_GRID_PHONE}; }
|
|
14294
|
+
`;
|
|
14295
|
+
var HeaderLabel = styled10__default.default.span`
|
|
14296
|
+
font-size: 0.6rem;
|
|
14297
|
+
font-weight: 600;
|
|
14298
|
+
letter-spacing: 0.08em;
|
|
14299
|
+
text-transform: uppercase;
|
|
14300
|
+
color: rgba(255,255,255,0.45);
|
|
14301
|
+
white-space: nowrap;
|
|
14302
|
+
`;
|
|
14303
|
+
var SortButton = styled10__default.default.button`
|
|
14304
|
+
background: none;
|
|
14305
|
+
border: none;
|
|
14306
|
+
padding: 0;
|
|
14307
|
+
cursor: pointer;
|
|
14308
|
+
font-family: inherit;
|
|
14309
|
+
font-size: 0.6rem;
|
|
14310
|
+
font-weight: 600;
|
|
14311
|
+
letter-spacing: 0.08em;
|
|
14312
|
+
text-transform: uppercase;
|
|
14313
|
+
white-space: nowrap;
|
|
14314
|
+
/* Left-aligned like every other column (user call, 2026-08-20). */
|
|
14315
|
+
text-align: left;
|
|
14316
|
+
justify-self: start;
|
|
14317
|
+
color: ${(p) => p.$active ? "var(--color-accent, #E6C87E)" : "rgba(255,255,255,0.45)"};
|
|
14318
|
+
transition: color 0.15s ease;
|
|
14319
|
+
&:hover { color: ${(p) => p.$active ? "var(--color-accent, #E6C87E)" : "rgba(255,255,255,0.75)"}; }
|
|
14320
|
+
|
|
14321
|
+
@media (max-width: 480px) {
|
|
14322
|
+
display: ${(p) => p.$hideOnPhone ? "none" : "inline"};
|
|
14323
|
+
}
|
|
14324
|
+
`;
|
|
14325
|
+
var IPOOptionIdentity = styled10__default.default.div`
|
|
14326
|
+
flex: 1 1 0;
|
|
14327
|
+
min-width: 0;
|
|
14328
|
+
overflow: hidden;
|
|
14329
|
+
`;
|
|
14330
|
+
var MarketRowInner = styled10__default.default.div`
|
|
14331
|
+
display: grid;
|
|
14332
|
+
grid-template-columns: ${MARKET_GRID};
|
|
14333
|
+
gap: 10px;
|
|
14334
|
+
align-items: center;
|
|
14335
|
+
|
|
14336
|
+
@media (max-width: 480px) { grid-template-columns: ${MARKET_GRID_PHONE}; }
|
|
14337
|
+
`;
|
|
14338
|
+
var IdentityCell = styled10__default.default.div`
|
|
14339
|
+
display: flex;
|
|
14340
|
+
align-items: center;
|
|
14341
|
+
gap: 0.65rem;
|
|
14342
|
+
min-width: 0;
|
|
14343
|
+
`;
|
|
14344
|
+
var PriceCell = styled10__default.default.span`
|
|
14345
|
+
font-size: 0.8rem;
|
|
14346
|
+
font-weight: 600;
|
|
14347
|
+
color: #fff;
|
|
14348
|
+
font-variant-numeric: tabular-nums;
|
|
14349
|
+
white-space: nowrap;
|
|
14350
|
+
text-align: left;
|
|
14351
|
+
`;
|
|
14352
|
+
var ChangeCell = styled10__default.default.span`
|
|
14353
|
+
font-size: 0.72rem;
|
|
14354
|
+
font-weight: 600;
|
|
14355
|
+
color: ${(p) => p.$positive ? "#0ecb81" : "#f6465d"};
|
|
14356
|
+
font-variant-numeric: tabular-nums;
|
|
14357
|
+
white-space: nowrap;
|
|
14358
|
+
text-align: left;
|
|
14359
|
+
`;
|
|
14360
|
+
var MutedCell = styled10__default.default.span`
|
|
14361
|
+
font-size: 0.72rem;
|
|
14362
|
+
/* Same ink as the price column (user call, 2026-08-20). */
|
|
14363
|
+
color: #fff;
|
|
14364
|
+
font-variant-numeric: tabular-nums;
|
|
14365
|
+
white-space: nowrap;
|
|
14366
|
+
text-align: left;
|
|
14367
|
+
|
|
14368
|
+
@media (max-width: 480px) {
|
|
14369
|
+
display: ${(p) => p.$hideOnPhone ? "none" : "inline"};
|
|
14370
|
+
}
|
|
14371
|
+
`;
|
|
13146
14372
|
var IPOOption = styled10__default.default.div`
|
|
13147
14373
|
padding: 0.6rem 0.75rem;
|
|
13148
14374
|
transition: background 0.15s ease;
|
|
@@ -13168,6 +14394,25 @@ var IPOOptionName = styled10__default.default.div`
|
|
|
13168
14394
|
font-size: 0.82rem;
|
|
13169
14395
|
color: ${"#ebebeb"};
|
|
13170
14396
|
line-height: 1.3;
|
|
14397
|
+
/* One line like the location — wrapped names made market rows uneven. */
|
|
14398
|
+
white-space: nowrap;
|
|
14399
|
+
overflow: hidden;
|
|
14400
|
+
text-overflow: ellipsis;
|
|
14401
|
+
`;
|
|
14402
|
+
var MarketTicker = styled10__default.default.span`
|
|
14403
|
+
font-weight: 700;
|
|
14404
|
+
letter-spacing: 0.01em;
|
|
14405
|
+
`;
|
|
14406
|
+
var TypePill = styled10__default.default.span`
|
|
14407
|
+
margin-left: 0.45rem;
|
|
14408
|
+
font-size: 0.55rem;
|
|
14409
|
+
font-weight: 700;
|
|
14410
|
+
letter-spacing: 0.08em;
|
|
14411
|
+
text-transform: uppercase;
|
|
14412
|
+
padding: 1px 5px;
|
|
14413
|
+
border-radius: 3px;
|
|
14414
|
+
vertical-align: middle;
|
|
14415
|
+
white-space: nowrap;
|
|
13171
14416
|
`;
|
|
13172
14417
|
var CompetitionBadge = styled10__default.default.span`
|
|
13173
14418
|
margin-left: 0.4rem;
|
|
@@ -13186,6 +14431,11 @@ var IPOOptionLocation = styled10__default.default.div`
|
|
|
13186
14431
|
font-size: 0.72rem;
|
|
13187
14432
|
color: rgba(255,255,255,0.38);
|
|
13188
14433
|
margin-top: 1px;
|
|
14434
|
+
/* One line, ellipsized — a long address must never wrap the row taller
|
|
14435
|
+
(matches the deployed dropdown's compact rows). */
|
|
14436
|
+
white-space: nowrap;
|
|
14437
|
+
overflow: hidden;
|
|
14438
|
+
text-overflow: ellipsis;
|
|
13189
14439
|
span {
|
|
13190
14440
|
margin-left: 0.4rem;
|
|
13191
14441
|
color: var(--color-accent, #E6C87E);
|
|
@@ -13219,10 +14469,10 @@ var SlideOld = styled10__default.default.span`
|
|
|
13219
14469
|
animation: ${slideOut} 0.35s cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
|
13220
14470
|
`;
|
|
13221
14471
|
function SlideDigit({ value }) {
|
|
13222
|
-
const prevRef =
|
|
13223
|
-
const [prev, setPrev] =
|
|
13224
|
-
const [animKey, setAnimKey] =
|
|
13225
|
-
|
|
14472
|
+
const prevRef = React9.useRef(value);
|
|
14473
|
+
const [prev, setPrev] = React9.useState(null);
|
|
14474
|
+
const [animKey, setAnimKey] = React9.useState(0);
|
|
14475
|
+
React9.useEffect(() => {
|
|
13226
14476
|
if (value !== prevRef.current) {
|
|
13227
14477
|
setPrev(prevRef.current);
|
|
13228
14478
|
prevRef.current = value;
|
|
@@ -13262,15 +14512,15 @@ function OfferingProgressCard({
|
|
|
13262
14512
|
style,
|
|
13263
14513
|
className
|
|
13264
14514
|
}) {
|
|
13265
|
-
const [currentTime, setCurrentTime] =
|
|
13266
|
-
const [countdown, setCountdown] =
|
|
14515
|
+
const [currentTime, setCurrentTime] = React9.useState(/* @__PURE__ */ new Date());
|
|
14516
|
+
const [countdown, setCountdown] = React9.useState(null);
|
|
13267
14517
|
const computedRaised = raisedAmount ?? totalSold * tokenPrice;
|
|
13268
14518
|
const computedTarget = targetAmount ?? supplyToSell * tokenPrice;
|
|
13269
|
-
|
|
14519
|
+
React9.useEffect(() => {
|
|
13270
14520
|
const timer = setInterval(() => setCurrentTime(/* @__PURE__ */ new Date()), 1e3);
|
|
13271
14521
|
return () => clearInterval(timer);
|
|
13272
14522
|
}, []);
|
|
13273
|
-
const [tradingCountdown, setTradingCountdown] =
|
|
14523
|
+
const [tradingCountdown, setTradingCountdown] = React9.useState(null);
|
|
13274
14524
|
const calcCountdownFrom = (target) => {
|
|
13275
14525
|
const now = Math.floor(Date.now() / 1e3);
|
|
13276
14526
|
const diff = target - now;
|
|
@@ -13282,7 +14532,7 @@ function OfferingProgressCard({
|
|
|
13282
14532
|
seconds: diff % 60
|
|
13283
14533
|
};
|
|
13284
14534
|
};
|
|
13285
|
-
|
|
14535
|
+
React9.useEffect(() => {
|
|
13286
14536
|
if (!opensAt) {
|
|
13287
14537
|
setCountdown(null);
|
|
13288
14538
|
return;
|
|
@@ -13291,7 +14541,7 @@ function OfferingProgressCard({
|
|
|
13291
14541
|
const interval = setInterval(() => setCountdown(calcCountdownFrom(opensAt)), 1e3);
|
|
13292
14542
|
return () => clearInterval(interval);
|
|
13293
14543
|
}, [opensAt]);
|
|
13294
|
-
|
|
14544
|
+
React9.useEffect(() => {
|
|
13295
14545
|
if (!tradingStartsAt) {
|
|
13296
14546
|
setTradingCountdown(null);
|
|
13297
14547
|
return;
|
|
@@ -13785,8 +15035,8 @@ var HomeSpinner = styled10__default.default.span`
|
|
|
13785
15035
|
}
|
|
13786
15036
|
`;
|
|
13787
15037
|
function useCountdown(opensAt) {
|
|
13788
|
-
const [now, setNow] =
|
|
13789
|
-
|
|
15038
|
+
const [now, setNow] = React9.useState(() => Math.floor(Date.now() / 1e3));
|
|
15039
|
+
React9.useEffect(() => {
|
|
13790
15040
|
if (opensAt == null) return;
|
|
13791
15041
|
const id = setInterval(() => setNow(Math.floor(Date.now() / 1e3)), 1e3);
|
|
13792
15042
|
return () => clearInterval(id);
|
|
@@ -13836,10 +15086,10 @@ function OrderPanel({
|
|
|
13836
15086
|
toast
|
|
13837
15087
|
}) {
|
|
13838
15088
|
const countdown = useCountdown(opensAt);
|
|
13839
|
-
const [payInputValue, setPayInputValue] =
|
|
13840
|
-
const [receiveInputValue, setReceiveInputValue] =
|
|
13841
|
-
const [isPayInputFocused, setIsPayInputFocused] =
|
|
13842
|
-
const [isReceiveInputFocused, setIsReceiveInputFocused] =
|
|
15089
|
+
const [payInputValue, setPayInputValue] = React9.useState("");
|
|
15090
|
+
const [receiveInputValue, setReceiveInputValue] = React9.useState("");
|
|
15091
|
+
const [isPayInputFocused, setIsPayInputFocused] = React9.useState(false);
|
|
15092
|
+
const [isReceiveInputFocused, setIsReceiveInputFocused] = React9.useState(false);
|
|
13843
15093
|
const handlePayBlur = () => {
|
|
13844
15094
|
setIsPayInputFocused(false);
|
|
13845
15095
|
const parsed = parseInt(payInputValue.replace(/,/g, "").replace(/[^0-9]/g, ""), 10) || 0;
|
|
@@ -14865,7 +16115,7 @@ function OrderConfirmationModal({
|
|
|
14865
16115
|
isPlacing = false,
|
|
14866
16116
|
placingLabel = "Placing order\u2026"
|
|
14867
16117
|
}) {
|
|
14868
|
-
const [dontShowAgain, setDontShowAgain] =
|
|
16118
|
+
const [dontShowAgain, setDontShowAgain] = React9.useState(false);
|
|
14869
16119
|
if (!open) return null;
|
|
14870
16120
|
const handleOverlayClose = () => {
|
|
14871
16121
|
if (isPlacing) return;
|
|
@@ -15317,14 +16567,14 @@ function PortfolioActivityPanel({
|
|
|
15317
16567
|
style
|
|
15318
16568
|
}) {
|
|
15319
16569
|
const resolvedDefaultTab = defaultTab ?? (showPositionsTab ? "positions" : showSubscriptionsTab ? "subscriptions" : "orders");
|
|
15320
|
-
const [activeTab, setActiveTab] =
|
|
15321
|
-
const [currentPage, setCurrentPage] =
|
|
15322
|
-
const [pendingCancelOrderId, setPendingCancelOrderId] =
|
|
15323
|
-
const [cancelAllConfirmOpen, setCancelAllConfirmOpen] =
|
|
15324
|
-
const [cancellingAll, setCancellingAll] =
|
|
15325
|
-
const [closeAllOpen, setCloseAllOpen] =
|
|
15326
|
-
const [closeAllType, setCloseAllType] =
|
|
15327
|
-
const [closingAll, setClosingAll] =
|
|
16570
|
+
const [activeTab, setActiveTab] = React9.useState(resolvedDefaultTab);
|
|
16571
|
+
const [currentPage, setCurrentPage] = React9.useState(0);
|
|
16572
|
+
const [pendingCancelOrderId, setPendingCancelOrderId] = React9.useState(null);
|
|
16573
|
+
const [cancelAllConfirmOpen, setCancelAllConfirmOpen] = React9.useState(false);
|
|
16574
|
+
const [cancellingAll, setCancellingAll] = React9.useState(false);
|
|
16575
|
+
const [closeAllOpen, setCloseAllOpen] = React9.useState(false);
|
|
16576
|
+
const [closeAllType, setCloseAllType] = React9.useState("market");
|
|
16577
|
+
const [closingAll, setClosingAll] = React9.useState(false);
|
|
15328
16578
|
const settlingPositionIds = positions.filter((p) => p.settling).map((p) => p.propertyId);
|
|
15329
16579
|
const settlingKey = settlingPositionIds.join(",");
|
|
15330
16580
|
const settlingTradeStartByProperty = /* @__PURE__ */ new Map();
|
|
@@ -15337,9 +16587,9 @@ function PortfolioActivityPanel({
|
|
|
15337
16587
|
if (earliest === void 0 || startedAt < earliest) settlingTradeStartByProperty.set(trade.propertyId, startedAt);
|
|
15338
16588
|
}
|
|
15339
16589
|
const tradeDeadlineKey = settlingTradeDeadlines.join(",");
|
|
15340
|
-
const settlingSinceRef =
|
|
15341
|
-
const [now, setNow] =
|
|
15342
|
-
|
|
16590
|
+
const settlingSinceRef = React9.useRef(/* @__PURE__ */ new Map());
|
|
16591
|
+
const [now, setNow] = React9.useState(() => Date.now());
|
|
16592
|
+
React9.useEffect(() => {
|
|
15343
16593
|
const since = settlingSinceRef.current;
|
|
15344
16594
|
const seenAt = Date.now();
|
|
15345
16595
|
for (const id of settlingPositionIds) if (!since.has(id)) since.set(id, seenAt);
|
|
@@ -15347,7 +16597,7 @@ function PortfolioActivityPanel({
|
|
|
15347
16597
|
setNow(seenAt);
|
|
15348
16598
|
}, [settlingKey]);
|
|
15349
16599
|
const positionSettlingStart = (propertyId) => settlingTradeStartByProperty.get(propertyId) ?? settlingSinceRef.current.get(propertyId) ?? now;
|
|
15350
|
-
|
|
16600
|
+
React9.useEffect(() => {
|
|
15351
16601
|
const pending = [
|
|
15352
16602
|
...settlingPositionIds.map((id) => positionSettlingStart(id) + STUCK_SETTLING_MS),
|
|
15353
16603
|
...settlingTradeDeadlines
|
|
@@ -15360,8 +16610,8 @@ function PortfolioActivityPanel({
|
|
|
15360
16610
|
const isPositionStuck = (pos) => !!pos.settling && now - positionSettlingStart(pos.propertyId) >= STUCK_SETTLING_MS;
|
|
15361
16611
|
const isTradeStuck = (trade) => now - toMillis(trade.executedAt) >= STUCK_SETTLING_MS;
|
|
15362
16612
|
const hasPositionActions = !!(onClosePosition || onSharePosition || onCloseAllPositions);
|
|
15363
|
-
const [posSortKey, setPosSortKey] =
|
|
15364
|
-
const [posSortAsc, setPosSortAsc] =
|
|
16613
|
+
const [posSortKey, setPosSortKey] = React9.useState("asset");
|
|
16614
|
+
const [posSortAsc, setPosSortAsc] = React9.useState(true);
|
|
15365
16615
|
const handlePosSort = (key) => {
|
|
15366
16616
|
if (posSortKey === key) {
|
|
15367
16617
|
setPosSortAsc((v) => !v);
|
|
@@ -15370,9 +16620,9 @@ function PortfolioActivityPanel({
|
|
|
15370
16620
|
setPosSortAsc(key === "asset");
|
|
15371
16621
|
}
|
|
15372
16622
|
};
|
|
15373
|
-
const [expandedPositions, setExpandedPositions] =
|
|
15374
|
-
const [allExpanded, setAllExpanded] =
|
|
15375
|
-
const [sortDropdownOpen, setSortDropdownOpen] =
|
|
16623
|
+
const [expandedPositions, setExpandedPositions] = React9.useState(/* @__PURE__ */ new Set());
|
|
16624
|
+
const [allExpanded, setAllExpanded] = React9.useState(false);
|
|
16625
|
+
const [sortDropdownOpen, setSortDropdownOpen] = React9.useState(false);
|
|
15376
16626
|
const togglePosition = (tokenName) => {
|
|
15377
16627
|
setExpandedPositions((prev) => {
|
|
15378
16628
|
const next = new Set(prev);
|
|
@@ -15381,7 +16631,7 @@ function PortfolioActivityPanel({
|
|
|
15381
16631
|
return next;
|
|
15382
16632
|
});
|
|
15383
16633
|
};
|
|
15384
|
-
const [expandedOpenOrders, setExpandedOpenOrders] =
|
|
16634
|
+
const [expandedOpenOrders, setExpandedOpenOrders] = React9.useState(/* @__PURE__ */ new Set());
|
|
15385
16635
|
const toggleOpenOrder = (id) => {
|
|
15386
16636
|
setExpandedOpenOrders((prev) => {
|
|
15387
16637
|
const next = new Set(prev);
|
|
@@ -15390,7 +16640,7 @@ function PortfolioActivityPanel({
|
|
|
15390
16640
|
return next;
|
|
15391
16641
|
});
|
|
15392
16642
|
};
|
|
15393
|
-
const [expandedRows, setExpandedRows] =
|
|
16643
|
+
const [expandedRows, setExpandedRows] = React9.useState(/* @__PURE__ */ new Set());
|
|
15394
16644
|
const toggleRow = (key) => {
|
|
15395
16645
|
setExpandedRows((prev) => {
|
|
15396
16646
|
const next = new Set(prev);
|
|
@@ -15418,7 +16668,7 @@ function PortfolioActivityPanel({
|
|
|
15418
16668
|
}
|
|
15419
16669
|
};
|
|
15420
16670
|
const sortKeys = ["asset", "units", "value", "avgPrice", "currentPrice", "pnl"];
|
|
15421
|
-
const sortedPositions =
|
|
16671
|
+
const sortedPositions = React9.useMemo(() => {
|
|
15422
16672
|
const arr = [...positions];
|
|
15423
16673
|
const dir = posSortAsc ? 1 : -1;
|
|
15424
16674
|
arr.sort((a, b) => {
|
|
@@ -15453,7 +16703,7 @@ function PortfolioActivityPanel({
|
|
|
15453
16703
|
setAllExpanded(true);
|
|
15454
16704
|
}
|
|
15455
16705
|
};
|
|
15456
|
-
const mergedOrderHistory =
|
|
16706
|
+
const mergedOrderHistory = React9.useMemo(() => {
|
|
15457
16707
|
if (showSubscriptionsTab || offeringOrders.length === 0) {
|
|
15458
16708
|
return [...orderHistory].sort((a, b) => b.createdAt - a.createdAt);
|
|
15459
16709
|
}
|
|
@@ -15478,32 +16728,32 @@ function PortfolioActivityPanel({
|
|
|
15478
16728
|
}));
|
|
15479
16729
|
return [...orderHistory, ...mapped].sort((a, b) => b.createdAt - a.createdAt);
|
|
15480
16730
|
}, [showSubscriptionsTab, offeringOrders, orderHistory]);
|
|
15481
|
-
const sortedOpenOrders =
|
|
16731
|
+
const sortedOpenOrders = React9.useMemo(
|
|
15482
16732
|
() => [...openOrders].sort((a, b) => b.createdAt - a.createdAt),
|
|
15483
16733
|
[openOrders]
|
|
15484
16734
|
);
|
|
15485
|
-
const sortedOfferingOrders =
|
|
16735
|
+
const sortedOfferingOrders = React9.useMemo(
|
|
15486
16736
|
() => [...offeringOrders].sort((a, b) => b.createdAt - a.createdAt),
|
|
15487
16737
|
[offeringOrders]
|
|
15488
16738
|
);
|
|
15489
|
-
const sortedTradeHistory =
|
|
16739
|
+
const sortedTradeHistory = React9.useMemo(
|
|
15490
16740
|
() => [...tradeHistory].sort((a, b) => b.executedAt - a.executedAt),
|
|
15491
16741
|
[tradeHistory]
|
|
15492
16742
|
);
|
|
15493
|
-
const sortedTransfers =
|
|
16743
|
+
const sortedTransfers = React9.useMemo(
|
|
15494
16744
|
() => [...transfers].sort((a, b) => b.createdAt - a.createdAt),
|
|
15495
16745
|
[transfers]
|
|
15496
16746
|
);
|
|
15497
16747
|
const positionsCount = positions.length;
|
|
15498
|
-
const openOrdersCount =
|
|
16748
|
+
const openOrdersCount = React9.useMemo(
|
|
15499
16749
|
() => openOrders.filter((o) => o.status === "OPEN" || o.status === "PARTIALLY_FILLED").length,
|
|
15500
16750
|
[openOrders]
|
|
15501
16751
|
);
|
|
15502
|
-
const pendingHistoryCount =
|
|
16752
|
+
const pendingHistoryCount = React9.useMemo(
|
|
15503
16753
|
() => mergedOrderHistory.filter((o) => o.status === "OPEN" || o.status === "PARTIALLY_FILLED").length,
|
|
15504
16754
|
[mergedOrderHistory]
|
|
15505
16755
|
);
|
|
15506
|
-
const pendingOfferingsCount =
|
|
16756
|
+
const pendingOfferingsCount = React9.useMemo(
|
|
15507
16757
|
() => offeringOrders.filter((o) => {
|
|
15508
16758
|
const s = o.status.toUpperCase();
|
|
15509
16759
|
return s !== "FILLED" && s !== "ALLOCATED" && s !== "CONFIRMED" && s !== "CANCELLED" && s !== "CANCELED" && s !== "REJECTED";
|
|
@@ -16350,7 +17600,7 @@ var ContentScroll = styled10__default.default.div`
|
|
|
16350
17600
|
`;
|
|
16351
17601
|
var TabContainer = styled10__default.default.div`
|
|
16352
17602
|
display: flex;
|
|
16353
|
-
gap:
|
|
17603
|
+
gap: 1rem;
|
|
16354
17604
|
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
|
16355
17605
|
margin-bottom: 0.75rem;
|
|
16356
17606
|
overflow-x: auto;
|
|
@@ -16362,12 +17612,12 @@ var TabContainer = styled10__default.default.div`
|
|
|
16362
17612
|
var Tab = styled10__default.default.button`
|
|
16363
17613
|
background: transparent;
|
|
16364
17614
|
border: none;
|
|
16365
|
-
padding: 0.
|
|
16366
|
-
font-size: 0.
|
|
17615
|
+
padding: 0 0 0.5rem;
|
|
17616
|
+
font-size: 0.8rem;
|
|
16367
17617
|
font-weight: 500;
|
|
16368
17618
|
white-space: nowrap;
|
|
16369
17619
|
flex-shrink: 0;
|
|
16370
|
-
color: ${({ $active }) => $active ? "#
|
|
17620
|
+
color: ${({ $active }) => $active ? "#fff" : "rgba(255, 255, 255, 0.6)"};
|
|
16371
17621
|
cursor: pointer;
|
|
16372
17622
|
transition: color 0.15s;
|
|
16373
17623
|
position: relative;
|
|
@@ -16380,12 +17630,11 @@ var Tab = styled10__default.default.button`
|
|
|
16380
17630
|
right: 0;
|
|
16381
17631
|
height: 2px;
|
|
16382
17632
|
background: ${({ $active }) => $active ? "#E6C87E" : "transparent"};
|
|
16383
|
-
border-radius: 1px;
|
|
16384
17633
|
transition: background 0.15s;
|
|
16385
17634
|
}
|
|
16386
17635
|
|
|
16387
17636
|
&:hover {
|
|
16388
|
-
color: ${({ $active }) => $active ? "#fff" : "rgba(255, 255, 255, 0.
|
|
17637
|
+
color: ${({ $active }) => $active ? "#fff" : "rgba(255, 255, 255, 0.85)"};
|
|
16389
17638
|
}
|
|
16390
17639
|
`;
|
|
16391
17640
|
var TabCount = styled10__default.default.span`
|
|
@@ -17104,17 +18353,17 @@ function PropertyBuy({
|
|
|
17104
18353
|
videoUrl,
|
|
17105
18354
|
toast
|
|
17106
18355
|
}) {
|
|
17107
|
-
const [sliderValue, setSliderValue] =
|
|
17108
|
-
const [availableBalance, setAvailableBalance] =
|
|
17109
|
-
const [manualOrderAmount, setManualOrderAmount] =
|
|
17110
|
-
const [ownedTokens, setOwnedTokens] =
|
|
17111
|
-
const [displayedOwnedTokens, setDisplayedOwnedTokens] =
|
|
17112
|
-
const [ownedTokensJustUpdated, setOwnedTokensJustUpdated] =
|
|
17113
|
-
const [lastOrderQuantity, setLastOrderQuantity] =
|
|
17114
|
-
const [orderPendingAllocation, setOrderPendingAllocation] =
|
|
17115
|
-
const [orderPlacedSuccess, setOrderPlacedSuccess] =
|
|
17116
|
-
const [lastOrderDetails, setLastOrderDetails] =
|
|
17117
|
-
const [showOrderConfirmModal, setShowOrderConfirmModal] =
|
|
18356
|
+
const [sliderValue, setSliderValue] = React9.useState(0);
|
|
18357
|
+
const [availableBalance, setAvailableBalance] = React9.useState(walletUsdcBalance ?? 0);
|
|
18358
|
+
const [manualOrderAmount, setManualOrderAmount] = React9.useState(null);
|
|
18359
|
+
const [ownedTokens, setOwnedTokens] = React9.useState(0);
|
|
18360
|
+
const [displayedOwnedTokens, setDisplayedOwnedTokens] = React9.useState(0);
|
|
18361
|
+
const [ownedTokensJustUpdated, setOwnedTokensJustUpdated] = React9.useState(false);
|
|
18362
|
+
const [lastOrderQuantity, setLastOrderQuantity] = React9.useState(0);
|
|
18363
|
+
const [orderPendingAllocation, setOrderPendingAllocation] = React9.useState(false);
|
|
18364
|
+
const [orderPlacedSuccess, setOrderPlacedSuccess] = React9.useState(false);
|
|
18365
|
+
const [lastOrderDetails, setLastOrderDetails] = React9.useState(null);
|
|
18366
|
+
const [showOrderConfirmModal, setShowOrderConfirmModal] = React9.useState(false);
|
|
17118
18367
|
const tokenPrice = saleData?.tokenPrice ?? 250;
|
|
17119
18368
|
const feeRate = (saleData?.feePercent ?? 0.5) / 100;
|
|
17120
18369
|
const totalSold = saleData?.totalSold ?? 0;
|
|
@@ -17134,12 +18383,12 @@ function PropertyBuy({
|
|
|
17134
18383
|
const orderTotal = tokenQuantity * tokenPrice + feeInUsd;
|
|
17135
18384
|
const estExposure = (tokenQuantity / supplyToSell * 100).toFixed(4);
|
|
17136
18385
|
const hasInsufficientFunds = orderTotal > availableBalance;
|
|
17137
|
-
|
|
18386
|
+
React9.useEffect(() => {
|
|
17138
18387
|
if (walletUsdcBalance != null) {
|
|
17139
18388
|
setAvailableBalance(walletUsdcBalance);
|
|
17140
18389
|
}
|
|
17141
18390
|
}, [walletUsdcBalance]);
|
|
17142
|
-
|
|
18391
|
+
React9.useEffect(() => {
|
|
17143
18392
|
if (walletPropertyTokenBalance != null) {
|
|
17144
18393
|
setOwnedTokens(walletPropertyTokenBalance);
|
|
17145
18394
|
setDisplayedOwnedTokens(walletPropertyTokenBalance);
|
|
@@ -17155,17 +18404,17 @@ function PropertyBuy({
|
|
|
17155
18404
|
}
|
|
17156
18405
|
setShowOrderConfirmModal(true);
|
|
17157
18406
|
};
|
|
17158
|
-
|
|
18407
|
+
React9.useEffect(() => {
|
|
17159
18408
|
if (purchaseStatus === "success" && orderPendingAllocation) {
|
|
17160
18409
|
setOrderPendingAllocation(false);
|
|
17161
18410
|
setOrderPlacedSuccess(true);
|
|
17162
18411
|
}
|
|
17163
18412
|
}, [purchaseStatus, orderPendingAllocation]);
|
|
17164
|
-
const handlePlaceAnotherOrder =
|
|
18413
|
+
const handlePlaceAnotherOrder = React9.useCallback(() => {
|
|
17165
18414
|
setOrderPlacedSuccess(false);
|
|
17166
18415
|
setOrderPendingAllocation(false);
|
|
17167
18416
|
}, []);
|
|
17168
|
-
const sortedOrders =
|
|
18417
|
+
const sortedOrders = React9.useMemo(
|
|
17169
18418
|
() => [...recentOrders].sort((a, b) => b.timestamp - a.timestamp).slice(0, 7),
|
|
17170
18419
|
[recentOrders]
|
|
17171
18420
|
);
|
|
@@ -17735,7 +18984,7 @@ function PaymentPopup({
|
|
|
17735
18984
|
userTokenHoldings,
|
|
17736
18985
|
propertyName
|
|
17737
18986
|
}) {
|
|
17738
|
-
const [selectedPaymentMethod, setSelectedPaymentMethod] =
|
|
18987
|
+
const [selectedPaymentMethod, setSelectedPaymentMethod] = React9.useState(
|
|
17739
18988
|
"tokens"
|
|
17740
18989
|
);
|
|
17741
18990
|
if (!isOpen) return null;
|
|
@@ -17914,20 +19163,20 @@ function PaymentPopup({
|
|
|
17914
19163
|
var payment_popup_default = PaymentPopup;
|
|
17915
19164
|
var SUMMER_MONTHS = /* @__PURE__ */ new Set([11, 0, 1]);
|
|
17916
19165
|
var OwnerBooking = ({ propertyName, token }) => {
|
|
17917
|
-
const today =
|
|
19166
|
+
const today = React9.useMemo(() => {
|
|
17918
19167
|
const base = /* @__PURE__ */ new Date();
|
|
17919
19168
|
base.setHours(0, 0, 0, 0);
|
|
17920
19169
|
return base;
|
|
17921
19170
|
}, []);
|
|
17922
|
-
const [currentYear, setCurrentYear] =
|
|
17923
|
-
const [currentMonth, setCurrentMonth] =
|
|
17924
|
-
const [dateRanges, setDateRanges] =
|
|
17925
|
-
const [selectedDates, setSelectedDates] =
|
|
17926
|
-
const [hoverDate, setHoverDate] =
|
|
17927
|
-
const [isDateSelectorOpen, setIsDateSelectorOpen] =
|
|
17928
|
-
const [selectorYear, setSelectorYear] =
|
|
17929
|
-
const [isPaymentPopupOpen, setIsPaymentPopupOpen] =
|
|
17930
|
-
const dateSelectorRef =
|
|
19171
|
+
const [currentYear, setCurrentYear] = React9.useState(today.getFullYear());
|
|
19172
|
+
const [currentMonth, setCurrentMonth] = React9.useState(today.getMonth());
|
|
19173
|
+
const [dateRanges, setDateRanges] = React9.useState([]);
|
|
19174
|
+
const [selectedDates, setSelectedDates] = React9.useState([]);
|
|
19175
|
+
const [hoverDate, setHoverDate] = React9.useState(null);
|
|
19176
|
+
const [isDateSelectorOpen, setIsDateSelectorOpen] = React9.useState(false);
|
|
19177
|
+
const [selectorYear, setSelectorYear] = React9.useState(today.getFullYear());
|
|
19178
|
+
const [isPaymentPopupOpen, setIsPaymentPopupOpen] = React9.useState(false);
|
|
19179
|
+
const dateSelectorRef = React9.useRef(null);
|
|
17931
19180
|
const tokenPrice = token.price ?? 700;
|
|
17932
19181
|
const userTokenHoldings = 2.2;
|
|
17933
19182
|
const MIN_NIGHTS = 3;
|
|
@@ -17937,25 +19186,25 @@ var OwnerBooking = ({ propertyName, token }) => {
|
|
|
17937
19186
|
{ bg: "rgba(230, 126, 34, 0.25)", border: "rgba(230, 126, 34, 0.6)" },
|
|
17938
19187
|
{ bg: "rgba(46, 204, 113, 0.25)", border: "rgba(46, 204, 113, 0.6)" }
|
|
17939
19188
|
];
|
|
17940
|
-
const calculateTokensPerDayOwner =
|
|
19189
|
+
const calculateTokensPerDayOwner = React9.useCallback(() => {
|
|
17941
19190
|
if (tokenPrice <= 0) return 0;
|
|
17942
19191
|
const target = 650;
|
|
17943
19192
|
const raw = target / tokenPrice;
|
|
17944
19193
|
return Math.ceil(raw * 1e4) / 1e4;
|
|
17945
19194
|
}, [tokenPrice]);
|
|
17946
|
-
const calculateTokensPerDayMarket =
|
|
19195
|
+
const calculateTokensPerDayMarket = React9.useCallback(() => {
|
|
17947
19196
|
if (tokenPrice <= 0) return 0;
|
|
17948
19197
|
const target = 900;
|
|
17949
19198
|
const raw = target / tokenPrice;
|
|
17950
19199
|
return Math.ceil(raw * 1e4) / 1e4;
|
|
17951
19200
|
}, [tokenPrice]);
|
|
17952
19201
|
const qualifiesForOwnerRate = userTokenHoldings >= 0.1;
|
|
17953
|
-
const getTokensPerDay =
|
|
19202
|
+
const getTokensPerDay = React9.useCallback(
|
|
17954
19203
|
() => calculateTokensPerDayOwner() ,
|
|
17955
19204
|
[qualifiesForOwnerRate, calculateTokensPerDayOwner, calculateTokensPerDayMarket]
|
|
17956
19205
|
);
|
|
17957
19206
|
const getRateLabel = () => "Owner's Rate" ;
|
|
17958
|
-
const selectionStats =
|
|
19207
|
+
const selectionStats = React9.useMemo(() => {
|
|
17959
19208
|
if (selectedDates.length !== 2) return null;
|
|
17960
19209
|
const [a, b] = selectedDates;
|
|
17961
19210
|
const start = a < b ? a : b;
|
|
@@ -17966,10 +19215,10 @@ var OwnerBooking = ({ propertyName, token }) => {
|
|
|
17966
19215
|
const usd = tokens * tokenPrice;
|
|
17967
19216
|
return { nights, tokensPerDay, tokens, usd, start, end };
|
|
17968
19217
|
}, [selectedDates, getTokensPerDay, tokenPrice]);
|
|
17969
|
-
const totalNights =
|
|
17970
|
-
const totalTokens =
|
|
19218
|
+
const totalNights = React9.useMemo(() => dateRanges.reduce((sum, r) => sum + r.nights, 0), [dateRanges]);
|
|
19219
|
+
const totalTokens = React9.useMemo(() => dateRanges.reduce((sum, r) => sum + r.tokens, 0), [dateRanges]);
|
|
17971
19220
|
const totalUSD = totalTokens * tokenPrice;
|
|
17972
|
-
const generateCalendarDays =
|
|
19221
|
+
const generateCalendarDays = React9.useCallback(
|
|
17973
19222
|
(year, month) => {
|
|
17974
19223
|
const first = new Date(year, month, 1);
|
|
17975
19224
|
const last = new Date(year, month + 1, 0);
|
|
@@ -17998,11 +19247,11 @@ var OwnerBooking = ({ propertyName, token }) => {
|
|
|
17998
19247
|
},
|
|
17999
19248
|
[today]
|
|
18000
19249
|
);
|
|
18001
|
-
const calendarDays =
|
|
19250
|
+
const calendarDays = React9.useMemo(
|
|
18002
19251
|
() => generateCalendarDays(currentYear, currentMonth),
|
|
18003
19252
|
[currentYear, currentMonth, generateCalendarDays]
|
|
18004
19253
|
);
|
|
18005
|
-
const isDateReserved =
|
|
19254
|
+
const isDateReserved = React9.useCallback(
|
|
18006
19255
|
(date) => dateRanges.some((r) => date >= r.arrival && date <= r.departure),
|
|
18007
19256
|
[dateRanges]
|
|
18008
19257
|
);
|
|
@@ -18096,14 +19345,14 @@ var OwnerBooking = ({ propertyName, token }) => {
|
|
|
18096
19345
|
"December"
|
|
18097
19346
|
];
|
|
18098
19347
|
const dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
18099
|
-
const setMonthYear =
|
|
19348
|
+
const setMonthYear = React9.useCallback(
|
|
18100
19349
|
(month, year) => {
|
|
18101
19350
|
setCurrentMonth(month);
|
|
18102
19351
|
setCurrentYear(year);
|
|
18103
19352
|
},
|
|
18104
19353
|
[setCurrentMonth, setCurrentYear]
|
|
18105
19354
|
);
|
|
18106
|
-
const shiftMonth =
|
|
19355
|
+
const shiftMonth = React9.useCallback(
|
|
18107
19356
|
(direction) => {
|
|
18108
19357
|
const delta = direction === "prev" ? -1 : 1;
|
|
18109
19358
|
let newMonth = currentMonth + delta;
|
|
@@ -18120,7 +19369,7 @@ var OwnerBooking = ({ propertyName, token }) => {
|
|
|
18120
19369
|
},
|
|
18121
19370
|
[currentMonth, currentYear, setMonthYear]
|
|
18122
19371
|
);
|
|
18123
|
-
const jumpToNextAvailable =
|
|
19372
|
+
const jumpToNextAvailable = React9.useCallback(() => {
|
|
18124
19373
|
for (let offset = 1; offset <= 12; offset++) {
|
|
18125
19374
|
const candidateMonth = (currentMonth + offset) % 12;
|
|
18126
19375
|
if (!SUMMER_MONTHS.has(candidateMonth)) continue;
|
|
@@ -18137,7 +19386,7 @@ var OwnerBooking = ({ propertyName, token }) => {
|
|
|
18137
19386
|
setMonthYear(month, selectorYear);
|
|
18138
19387
|
setIsDateSelectorOpen(false);
|
|
18139
19388
|
};
|
|
18140
|
-
|
|
19389
|
+
React9.useEffect(() => {
|
|
18141
19390
|
const handleClickOutside = (event) => {
|
|
18142
19391
|
if (dateSelectorRef.current && !dateSelectorRef.current.contains(event.target)) {
|
|
18143
19392
|
setIsDateSelectorOpen(false);
|
|
@@ -18570,8 +19819,8 @@ function PropertyValuation({
|
|
|
18570
19819
|
offeringValuationValue,
|
|
18571
19820
|
totalUnits
|
|
18572
19821
|
}) {
|
|
18573
|
-
const [hoveredSaleYear, setHoveredSaleYear] =
|
|
18574
|
-
const gaugePosition =
|
|
19822
|
+
const [hoveredSaleYear, setHoveredSaleYear] = React9.useState(null);
|
|
19823
|
+
const gaugePosition = React9.useMemo(() => {
|
|
18575
19824
|
const under = valSummary?.undervaluedThreshold;
|
|
18576
19825
|
const over = valSummary?.overvaluedThreshold;
|
|
18577
19826
|
const price = tokenPriceValue;
|
|
@@ -18581,7 +19830,7 @@ function PropertyValuation({
|
|
|
18581
19830
|
}
|
|
18582
19831
|
return 50;
|
|
18583
19832
|
}, [valSummary?.undervaluedThreshold, valSummary?.overvaluedThreshold, tokenPriceValue]);
|
|
18584
|
-
const chartOptions =
|
|
19833
|
+
const chartOptions = React9.useMemo(() => ({
|
|
18585
19834
|
responsive: true,
|
|
18586
19835
|
maintainAspectRatio: false,
|
|
18587
19836
|
onHover: (_event, elements) => {
|
|
@@ -18624,7 +19873,7 @@ function PropertyValuation({
|
|
|
18624
19873
|
}
|
|
18625
19874
|
}
|
|
18626
19875
|
}), []);
|
|
18627
|
-
const chartData =
|
|
19876
|
+
const chartData = React9.useMemo(() => ({
|
|
18628
19877
|
labels: musgraveComparisonData.labels,
|
|
18629
19878
|
datasets: musgraveComparisonData.datasets.map((dataset, index) => ({
|
|
18630
19879
|
label: dataset.name,
|
|
@@ -18665,8 +19914,8 @@ function PropertyValuation({
|
|
|
18665
19914
|
fill: false
|
|
18666
19915
|
}))
|
|
18667
19916
|
}), [hoveredSaleYear]);
|
|
18668
|
-
const handleMarkerEnter =
|
|
18669
|
-
const handleMarkerLeave =
|
|
19917
|
+
const handleMarkerEnter = React9.useCallback((year) => setHoveredSaleYear(year), []);
|
|
19918
|
+
const handleMarkerLeave = React9.useCallback(() => setHoveredSaleYear(null), []);
|
|
18670
19919
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
18671
19920
|
/* @__PURE__ */ jsxRuntime.jsx(ValuationHeading, { children: "Valuation Model" }),
|
|
18672
19921
|
/* @__PURE__ */ jsxRuntime.jsxs(LoafPricingSection, { children: [
|
|
@@ -19591,20 +20840,20 @@ function PropertyPhotoGallery({
|
|
|
19591
20840
|
);
|
|
19592
20841
|
}
|
|
19593
20842
|
function GalleryContent({ galleryImages, startIndex, startWithVideo, title, subtitle, onClose, videoUrl }) {
|
|
19594
|
-
const [currentIndex, setCurrentIndex] =
|
|
19595
|
-
const [activeHotspotId, setActiveHotspotId] =
|
|
19596
|
-
const [showVideo, setShowVideo] =
|
|
19597
|
-
const sliderRef =
|
|
19598
|
-
const [isDragging, setIsDragging] =
|
|
19599
|
-
const wasDraggingRef =
|
|
19600
|
-
const touchStartX =
|
|
19601
|
-
const touchStartY =
|
|
19602
|
-
const isSwiping =
|
|
19603
|
-
const progress =
|
|
20843
|
+
const [currentIndex, setCurrentIndex] = React9.useState(() => clampIndex(startIndex, galleryImages.length));
|
|
20844
|
+
const [activeHotspotId, setActiveHotspotId] = React9.useState(null);
|
|
20845
|
+
const [showVideo, setShowVideo] = React9.useState(startWithVideo ?? false);
|
|
20846
|
+
const sliderRef = React9.useRef(null);
|
|
20847
|
+
const [isDragging, setIsDragging] = React9.useState(false);
|
|
20848
|
+
const wasDraggingRef = React9.useRef(false);
|
|
20849
|
+
const touchStartX = React9.useRef(null);
|
|
20850
|
+
const touchStartY = React9.useRef(null);
|
|
20851
|
+
const isSwiping = React9.useRef(false);
|
|
20852
|
+
const progress = React9.useMemo(() => {
|
|
19604
20853
|
if (galleryImages.length <= 1) return 0;
|
|
19605
20854
|
return currentIndex / (galleryImages.length - 1) * 100;
|
|
19606
20855
|
}, [currentIndex, galleryImages.length]);
|
|
19607
|
-
|
|
20856
|
+
React9.useEffect(() => {
|
|
19608
20857
|
document.body.style.overflow = "hidden";
|
|
19609
20858
|
document.body.style.touchAction = "none";
|
|
19610
20859
|
return () => {
|
|
@@ -19612,7 +20861,7 @@ function GalleryContent({ galleryImages, startIndex, startWithVideo, title, subt
|
|
|
19612
20861
|
document.body.style.touchAction = "";
|
|
19613
20862
|
};
|
|
19614
20863
|
}, []);
|
|
19615
|
-
|
|
20864
|
+
React9.useEffect(() => {
|
|
19616
20865
|
const handleKey = (event) => {
|
|
19617
20866
|
if (event.key === "Escape") onClose();
|
|
19618
20867
|
if (event.key === "ArrowLeft") setCurrentIndex((i) => i === 0 ? galleryImages.length - 1 : i - 1);
|
|
@@ -20016,23 +21265,23 @@ function truncateHash(hash) {
|
|
|
20016
21265
|
}
|
|
20017
21266
|
var SWIPE_DISMISS_PX = 100;
|
|
20018
21267
|
function ToastItem({ toast, onDismiss }) {
|
|
20019
|
-
const [exiting, setExiting] =
|
|
20020
|
-
const [dragX, setDragX] =
|
|
20021
|
-
const [dragging, setDragging] =
|
|
20022
|
-
const [touched, setTouched] =
|
|
20023
|
-
const timerRef =
|
|
20024
|
-
const dragStartRef =
|
|
20025
|
-
const dragXRef =
|
|
20026
|
-
const dismiss =
|
|
21268
|
+
const [exiting, setExiting] = React9.useState(false);
|
|
21269
|
+
const [dragX, setDragX] = React9.useState(0);
|
|
21270
|
+
const [dragging, setDragging] = React9.useState(false);
|
|
21271
|
+
const [touched, setTouched] = React9.useState(false);
|
|
21272
|
+
const timerRef = React9.useRef(null);
|
|
21273
|
+
const dragStartRef = React9.useRef(null);
|
|
21274
|
+
const dragXRef = React9.useRef(0);
|
|
21275
|
+
const dismiss = React9.useCallback(() => {
|
|
20027
21276
|
setExiting(true);
|
|
20028
21277
|
setTimeout(() => onDismiss(toast.id), 280);
|
|
20029
21278
|
}, [onDismiss, toast.id]);
|
|
20030
|
-
const armAutoDismiss =
|
|
21279
|
+
const armAutoDismiss = React9.useCallback(() => {
|
|
20031
21280
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
20032
21281
|
const duration2 = toast.duration ?? 6e3;
|
|
20033
21282
|
if (duration2 > 0) timerRef.current = setTimeout(dismiss, duration2);
|
|
20034
21283
|
}, [dismiss, toast.duration]);
|
|
20035
|
-
|
|
21284
|
+
React9.useEffect(() => {
|
|
20036
21285
|
armAutoDismiss();
|
|
20037
21286
|
return () => {
|
|
20038
21287
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
@@ -20112,10 +21361,10 @@ function ToastItem({ toast, onDismiss }) {
|
|
|
20112
21361
|
}
|
|
20113
21362
|
);
|
|
20114
21363
|
}
|
|
20115
|
-
var ToastContext =
|
|
21364
|
+
var ToastContext = React9.createContext(null);
|
|
20116
21365
|
function ToastProvider({ children, maxVisible }) {
|
|
20117
|
-
const [toasts, setToasts] =
|
|
20118
|
-
const addToast =
|
|
21366
|
+
const [toasts, setToasts] = React9.useState([]);
|
|
21367
|
+
const addToast = React9.useCallback((data) => {
|
|
20119
21368
|
const id = `toast-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
|
|
20120
21369
|
setToasts((prev) => {
|
|
20121
21370
|
const next = [...prev, { ...data, id }];
|
|
@@ -20123,10 +21372,10 @@ function ToastProvider({ children, maxVisible }) {
|
|
|
20123
21372
|
});
|
|
20124
21373
|
return id;
|
|
20125
21374
|
}, [maxVisible]);
|
|
20126
|
-
const update =
|
|
21375
|
+
const update = React9.useCallback((id, patch) => {
|
|
20127
21376
|
setToasts((prev) => prev.map((t) => t.id === id ? { ...t, ...patch } : t));
|
|
20128
21377
|
}, []);
|
|
20129
|
-
const dismiss =
|
|
21378
|
+
const dismiss = React9.useCallback((id) => {
|
|
20130
21379
|
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
20131
21380
|
}, []);
|
|
20132
21381
|
return /* @__PURE__ */ jsxRuntime.jsxs(ToastContext.Provider, { value: { toast: addToast, update, dismiss }, children: [
|
|
@@ -20135,15 +21384,15 @@ function ToastProvider({ children, maxVisible }) {
|
|
|
20135
21384
|
] });
|
|
20136
21385
|
}
|
|
20137
21386
|
function useToast() {
|
|
20138
|
-
const ctx =
|
|
21387
|
+
const ctx = React9.useContext(ToastContext);
|
|
20139
21388
|
if (!ctx) throw new Error("useToast must be used within a ToastProvider");
|
|
20140
21389
|
return ctx;
|
|
20141
21390
|
}
|
|
20142
21391
|
var INTERVALS = [1e3, 2e3, 4e3, 5e3, 7e3, 1e4, 12e3];
|
|
20143
21392
|
function useAdaptivePolling({ enabled, onPoll }) {
|
|
20144
|
-
const onPollRef =
|
|
21393
|
+
const onPollRef = React9.useRef(onPoll);
|
|
20145
21394
|
onPollRef.current = onPoll;
|
|
20146
|
-
|
|
21395
|
+
React9.useEffect(() => {
|
|
20147
21396
|
if (!enabled) return;
|
|
20148
21397
|
let cancelled = false;
|
|
20149
21398
|
let timeoutId = null;
|
|
@@ -20316,7 +21565,7 @@ var OpenMailButton = styled10__default.default.a`
|
|
|
20316
21565
|
}
|
|
20317
21566
|
`;
|
|
20318
21567
|
var ContactPopup = ({ onClose }) => {
|
|
20319
|
-
const [copied, setCopied] =
|
|
21568
|
+
const [copied, setCopied] = React9.useState(false);
|
|
20320
21569
|
const email = "admin@loafmarkets.com";
|
|
20321
21570
|
const handleCopy = async () => {
|
|
20322
21571
|
try {
|
|
@@ -20525,7 +21774,7 @@ var DisclaimerText = styled10__default.default.p`
|
|
|
20525
21774
|
max-width: 100%;
|
|
20526
21775
|
`;
|
|
20527
21776
|
var SiteFooter = () => {
|
|
20528
|
-
const [showContactPopup, setShowContactPopup] =
|
|
21777
|
+
const [showContactPopup, setShowContactPopup] = React9.useState(false);
|
|
20529
21778
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
20530
21779
|
/* @__PURE__ */ jsxRuntime.jsx(FooterContainer, { children: /* @__PURE__ */ jsxRuntime.jsxs(FooterContent, { children: [
|
|
20531
21780
|
/* @__PURE__ */ jsxRuntime.jsxs(FooterTop, { children: [
|
|
@@ -20614,10 +21863,10 @@ function extractSparkline(candles, maxPoints = 20) {
|
|
|
20614
21863
|
}
|
|
20615
21864
|
return points;
|
|
20616
21865
|
}
|
|
20617
|
-
var SparklineChart =
|
|
20618
|
-
const [hoverX, setHoverX] =
|
|
20619
|
-
const svgRef =
|
|
20620
|
-
const { linePath, fillPath, points } =
|
|
21866
|
+
var SparklineChart = React9__namespace.default.memo(function SparklineChart2({ data, width, height, positive }) {
|
|
21867
|
+
const [hoverX, setHoverX] = React9.useState(null);
|
|
21868
|
+
const svgRef = React9.useRef(null);
|
|
21869
|
+
const { linePath, fillPath, points } = React9.useMemo(() => {
|
|
20621
21870
|
const mn = Math.min(...data);
|
|
20622
21871
|
const mx = Math.max(...data);
|
|
20623
21872
|
const range = mx - mn || 1;
|
|
@@ -20630,7 +21879,7 @@ var SparklineChart = React5__namespace.default.memo(function SparklineChart2({ d
|
|
|
20630
21879
|
const fill = `${line} L${width},${height} L0,${height} Z`;
|
|
20631
21880
|
return { linePath: line, fillPath: fill, points: pts };
|
|
20632
21881
|
}, [data, width, height]);
|
|
20633
|
-
const interpolated =
|
|
21882
|
+
const interpolated = React9.useMemo(() => {
|
|
20634
21883
|
if (hoverX == null || points.length < 2) return null;
|
|
20635
21884
|
const stepX = width / (data.length - 1);
|
|
20636
21885
|
const rawIdx = hoverX / stepX;
|
|
@@ -20643,14 +21892,14 @@ var SparklineChart = React5__namespace.default.memo(function SparklineChart2({ d
|
|
|
20643
21892
|
price: lerp(data[i0], data[i1], t)
|
|
20644
21893
|
};
|
|
20645
21894
|
}, [hoverX, points, data, width]);
|
|
20646
|
-
const handleMouseMove =
|
|
21895
|
+
const handleMouseMove = React9.useCallback((e) => {
|
|
20647
21896
|
const svg = svgRef.current;
|
|
20648
21897
|
if (!svg) return;
|
|
20649
21898
|
const rect = svg.getBoundingClientRect();
|
|
20650
21899
|
const x = Math.max(0, Math.min(width, e.clientX - rect.left));
|
|
20651
21900
|
setHoverX(x);
|
|
20652
21901
|
}, [width]);
|
|
20653
|
-
const handleMouseLeave =
|
|
21902
|
+
const handleMouseLeave = React9.useCallback(() => setHoverX(null), []);
|
|
20654
21903
|
if (data.length < 2) return null;
|
|
20655
21904
|
const color = positive ? "#00C076" : "#FF5757";
|
|
20656
21905
|
const gradId = `spark-grad-${positive ? "up" : "dn"}`;
|
|
@@ -20768,6 +22017,7 @@ exports.PropertyPhotoGallery = PropertyPhotoGallery;
|
|
|
20768
22017
|
exports.PropertySubheader = PropertySubheader;
|
|
20769
22018
|
exports.PropertyTour = PropertyTour;
|
|
20770
22019
|
exports.PropertyValuation = PropertyValuation;
|
|
22020
|
+
exports.SentimentBar = SentimentBar;
|
|
20771
22021
|
exports.SiteFooter = SiteFooter;
|
|
20772
22022
|
exports.Skeleton = Skeleton;
|
|
20773
22023
|
exports.SlideDigit = SlideDigit;
|
|
@@ -20776,10 +22026,13 @@ exports.ToastProvider = ToastProvider;
|
|
|
20776
22026
|
exports.TradeConfirmationModal = TradeConfirmationModal;
|
|
20777
22027
|
exports.TradingSlider = TradingSlider;
|
|
20778
22028
|
exports.YourOrders = YourOrders;
|
|
22029
|
+
exports.__resetDismissedBreaking = __resetDismissedBreaking;
|
|
20779
22030
|
exports.badgeVariants = badgeVariants;
|
|
20780
22031
|
exports.buttonVariants = buttonVariants;
|
|
20781
22032
|
exports.extractSparkline = extractSparkline;
|
|
22033
|
+
exports.formatAge = formatAge;
|
|
20782
22034
|
exports.hasPendingActivity = hasPendingActivity;
|
|
22035
|
+
exports.rowAgeLabel = rowAgeLabel;
|
|
20783
22036
|
exports.useAdaptivePolling = useAdaptivePolling;
|
|
20784
22037
|
exports.useToast = useToast;
|
|
20785
22038
|
//# sourceMappingURL=index.js.map
|