@loafmarkets/ui 0.1.415 → 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 +1818 -578
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1591 -355
- 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
|
|
3036
3582
|
);
|
|
3037
|
-
|
|
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]
|
|
3616
|
+
);
|
|
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
|
}, []);
|
|
@@ -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");
|
|
@@ -8728,8 +9639,8 @@ var TransakIframe = styled10__default.default.iframe`
|
|
|
8728
9639
|
border-radius: var(--border-radius, 12px);
|
|
8729
9640
|
`;
|
|
8730
9641
|
var MiniLiveFeed = () => {
|
|
8731
|
-
const [purchases, setPurchases] =
|
|
8732
|
-
|
|
9642
|
+
const [purchases, setPurchases] = React9.useState([]);
|
|
9643
|
+
React9.useEffect(() => {
|
|
8733
9644
|
const handles = [
|
|
8734
9645
|
"Landlord",
|
|
8735
9646
|
"PropertyKing",
|
|
@@ -8985,8 +9896,8 @@ var FactText = styled10__default.default.p`
|
|
|
8985
9896
|
}
|
|
8986
9897
|
`;
|
|
8987
9898
|
var PreparingFacts = () => {
|
|
8988
|
-
const [index, setIndex] =
|
|
8989
|
-
|
|
9899
|
+
const [index, setIndex] = React9.useState(() => Math.floor(Math.random() * PREPARING_FACTS.length));
|
|
9900
|
+
React9.useEffect(() => {
|
|
8990
9901
|
const id = setInterval(() => {
|
|
8991
9902
|
setIndex((i) => (i + 1) % PREPARING_FACTS.length);
|
|
8992
9903
|
}, 4e3);
|
|
@@ -9249,11 +10160,11 @@ function OnboardingGuide({
|
|
|
9249
10160
|
onClaimHandle,
|
|
9250
10161
|
logoSrc = "/Loaf-logo-Banner.png"
|
|
9251
10162
|
}) {
|
|
9252
|
-
const [step, setStep] =
|
|
9253
|
-
const [handle, setHandle] =
|
|
9254
|
-
const [claiming, setClaiming] =
|
|
9255
|
-
const [handleError, setHandleError] =
|
|
9256
|
-
|
|
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(() => {
|
|
9257
10168
|
if (open) {
|
|
9258
10169
|
setStep(onClaimHandle ? "handle" : "funds");
|
|
9259
10170
|
setHandle("");
|
|
@@ -9261,7 +10172,7 @@ function OnboardingGuide({
|
|
|
9261
10172
|
setHandleError("");
|
|
9262
10173
|
}
|
|
9263
10174
|
}, [open, onClaimHandle]);
|
|
9264
|
-
|
|
10175
|
+
React9.useEffect(() => {
|
|
9265
10176
|
if (!open) return;
|
|
9266
10177
|
const onKey = (e) => {
|
|
9267
10178
|
if (e.key === "Escape") onClose();
|
|
@@ -9361,7 +10272,7 @@ function OnboardingGuide({
|
|
|
9361
10272
|
] })
|
|
9362
10273
|
] }) });
|
|
9363
10274
|
}
|
|
9364
|
-
var PropertyCompareBar =
|
|
10275
|
+
var PropertyCompareBar = React9__namespace.forwardRef(
|
|
9365
10276
|
({
|
|
9366
10277
|
className,
|
|
9367
10278
|
addresses,
|
|
@@ -9372,7 +10283,7 @@ var PropertyCompareBar = React5__namespace.forwardRef(
|
|
|
9372
10283
|
price,
|
|
9373
10284
|
...props
|
|
9374
10285
|
}, ref) => {
|
|
9375
|
-
const normalizedAddresses =
|
|
10286
|
+
const normalizedAddresses = React9__namespace.useMemo(() => {
|
|
9376
10287
|
return addresses.map(
|
|
9377
10288
|
(option) => typeof option === "string" ? { id: option, label: option } : option
|
|
9378
10289
|
);
|
|
@@ -9380,11 +10291,11 @@ var PropertyCompareBar = React5__namespace.forwardRef(
|
|
|
9380
10291
|
const hasAddresses = normalizedAddresses.length > 0;
|
|
9381
10292
|
const firstAddressId = normalizedAddresses[0]?.id;
|
|
9382
10293
|
const isControlled = selectedAddressId !== void 0;
|
|
9383
|
-
const [internalSelectedId, setInternalSelectedId] =
|
|
10294
|
+
const [internalSelectedId, setInternalSelectedId] = React9__namespace.useState(
|
|
9384
10295
|
() => isControlled ? void 0 : firstAddressId
|
|
9385
10296
|
);
|
|
9386
10297
|
const resolvedSelectedId = isControlled ? selectedAddressId : internalSelectedId;
|
|
9387
|
-
|
|
10298
|
+
React9__namespace.useEffect(() => {
|
|
9388
10299
|
if (!isControlled) {
|
|
9389
10300
|
setInternalSelectedId((current) => {
|
|
9390
10301
|
if (current != null && normalizedAddresses.some((option) => option.id === current)) {
|
|
@@ -9395,9 +10306,9 @@ var PropertyCompareBar = React5__namespace.forwardRef(
|
|
|
9395
10306
|
}
|
|
9396
10307
|
}, [firstAddressId, isControlled, normalizedAddresses]);
|
|
9397
10308
|
const selectedOption = normalizedAddresses.find((option) => option.id === resolvedSelectedId) ?? normalizedAddresses[0];
|
|
9398
|
-
const [isDropdownOpen, setIsDropdownOpen] =
|
|
9399
|
-
const dropdownRef =
|
|
9400
|
-
|
|
10309
|
+
const [isDropdownOpen, setIsDropdownOpen] = React9__namespace.useState(false);
|
|
10310
|
+
const dropdownRef = React9__namespace.useRef(null);
|
|
10311
|
+
React9__namespace.useEffect(() => {
|
|
9401
10312
|
if (!isDropdownOpen) return;
|
|
9402
10313
|
const handleClick = (event) => {
|
|
9403
10314
|
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
|
@@ -9790,8 +10701,8 @@ var PriceChange = styled10__default.default.span`
|
|
|
9790
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)"};
|
|
9791
10702
|
`;
|
|
9792
10703
|
function useMaxWidth(maxWidth) {
|
|
9793
|
-
const [match, setMatch] =
|
|
9794
|
-
|
|
10704
|
+
const [match, setMatch] = React9.useState(() => typeof window !== "undefined" && window.matchMedia(`(max-width: ${maxWidth}px)`).matches);
|
|
10705
|
+
React9.useEffect(() => {
|
|
9795
10706
|
if (typeof window === "undefined") return;
|
|
9796
10707
|
const mq = window.matchMedia(`(max-width: ${maxWidth}px)`);
|
|
9797
10708
|
const onChange = () => setMatch(mq.matches);
|
|
@@ -9816,14 +10727,14 @@ function GalleryMapSection({
|
|
|
9816
10727
|
hideSuburb = false,
|
|
9817
10728
|
featuresSlot
|
|
9818
10729
|
}) {
|
|
9819
|
-
const [carouselIndex, setCarouselIndex] =
|
|
9820
|
-
const [showVideo, setShowVideo] =
|
|
9821
|
-
const [autoPlaying, setAutoPlaying] =
|
|
9822
|
-
const [showFullMap, setShowFullMap] =
|
|
9823
|
-
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);
|
|
9824
10735
|
const isMobile = useMaxWidth(768);
|
|
9825
10736
|
const resolvedMapUrl = mapUrl ?? (tokenName ? `/map/${tokenName}?embed=true&zoom=14&hideOthers=true` : "about:blank");
|
|
9826
|
-
|
|
10737
|
+
React9.useEffect(() => {
|
|
9827
10738
|
if (!autoPlaying || images.length <= 1) return;
|
|
9828
10739
|
const interval = setInterval(() => {
|
|
9829
10740
|
setCarouselIndex((p) => (p + 1) % images.length);
|
|
@@ -10010,10 +10921,10 @@ function GalleryMapSection({
|
|
|
10010
10921
|
] });
|
|
10011
10922
|
}
|
|
10012
10923
|
function StackedGallery({ images, onPhotoClick }) {
|
|
10013
|
-
const [expanded, setExpanded] =
|
|
10014
|
-
const [cardH, setCardH] =
|
|
10015
|
-
const wrapRef =
|
|
10016
|
-
|
|
10924
|
+
const [expanded, setExpanded] = React9.useState(false);
|
|
10925
|
+
const [cardH, setCardH] = React9.useState(200);
|
|
10926
|
+
const wrapRef = React9.useRef(null);
|
|
10927
|
+
React9.useEffect(() => {
|
|
10017
10928
|
const el = wrapRef.current;
|
|
10018
10929
|
if (!el) return;
|
|
10019
10930
|
const measure = () => {
|
|
@@ -10025,7 +10936,7 @@ function StackedGallery({ images, onPhotoClick }) {
|
|
|
10025
10936
|
ro.observe(el);
|
|
10026
10937
|
return () => ro.disconnect();
|
|
10027
10938
|
}, []);
|
|
10028
|
-
|
|
10939
|
+
React9.useEffect(() => {
|
|
10029
10940
|
const el = wrapRef.current;
|
|
10030
10941
|
if (!el) return;
|
|
10031
10942
|
const update = () => {
|
|
@@ -10117,21 +11028,21 @@ function StackedGallery({ images, onPhotoClick }) {
|
|
|
10117
11028
|
] });
|
|
10118
11029
|
}
|
|
10119
11030
|
function FullMapPopup({ onClose, mapUrl }) {
|
|
10120
|
-
|
|
11031
|
+
React9.useEffect(() => {
|
|
10121
11032
|
const handleEsc = (e) => {
|
|
10122
11033
|
if (e.key === "Escape") onClose();
|
|
10123
11034
|
};
|
|
10124
11035
|
window.addEventListener("keydown", handleEsc);
|
|
10125
11036
|
return () => window.removeEventListener("keydown", handleEsc);
|
|
10126
11037
|
}, [onClose]);
|
|
10127
|
-
|
|
11038
|
+
React9.useEffect(() => {
|
|
10128
11039
|
const handleMessage = (e) => {
|
|
10129
11040
|
if (e.data === "loaf:close-embed") onClose();
|
|
10130
11041
|
};
|
|
10131
11042
|
window.addEventListener("message", handleMessage);
|
|
10132
11043
|
return () => window.removeEventListener("message", handleMessage);
|
|
10133
11044
|
}, [onClose]);
|
|
10134
|
-
const iframeRef =
|
|
11045
|
+
const iframeRef = React9.useCallback((node) => {
|
|
10135
11046
|
if (!node) return;
|
|
10136
11047
|
const handleLoad = () => {
|
|
10137
11048
|
try {
|
|
@@ -10608,10 +11519,10 @@ function PropertyOverview({
|
|
|
10608
11519
|
beta = false,
|
|
10609
11520
|
marketHours
|
|
10610
11521
|
}) {
|
|
10611
|
-
const [isDescExpanded, setDescExpanded] =
|
|
10612
|
-
const [showDividendHistory, setShowDividendHistory] =
|
|
10613
|
-
const [copiedAddress, setCopiedAddress] =
|
|
10614
|
-
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");
|
|
10615
11526
|
const description = descriptionProp ?? overviewData?.description ?? DEFAULT_DESCRIPTION;
|
|
10616
11527
|
const landSize = landProp ?? overviewData?.landSizeSqm ?? null;
|
|
10617
11528
|
const buildingSize = buildingProp ?? overviewData?.buildingSizeSqm ?? null;
|
|
@@ -10920,8 +11831,8 @@ function DividendHistoryPopup({
|
|
|
10920
11831
|
onClose
|
|
10921
11832
|
}) {
|
|
10922
11833
|
const { records, priceHistory } = generateDividendHistory(valuation ?? 0, totalTokens ?? 0, tokenPrice ?? 0, realPriceHistory);
|
|
10923
|
-
const [highlightedIdx, setHighlightedIdx] =
|
|
10924
|
-
|
|
11834
|
+
const [highlightedIdx, setHighlightedIdx] = React9.useState(null);
|
|
11835
|
+
React9.useEffect(() => {
|
|
10925
11836
|
const handleEsc = (e) => {
|
|
10926
11837
|
if (e.key === "Escape") onClose();
|
|
10927
11838
|
};
|
|
@@ -11768,8 +12679,8 @@ var OfferPricePulse = styled10__default.default.span`
|
|
|
11768
12679
|
`;
|
|
11769
12680
|
var eventTypes = ["Leased", "Renovated", "Extended", "Rebuilt", "Rezoned", "DA Approval"];
|
|
11770
12681
|
function PropertyHistory() {
|
|
11771
|
-
const [historyFilter, setHistoryFilter] =
|
|
11772
|
-
const pastSales =
|
|
12682
|
+
const [historyFilter, setHistoryFilter] = React9.useState("all");
|
|
12683
|
+
const pastSales = React9.useMemo(() => generateMockPastSales(), []);
|
|
11773
12684
|
const loafListing = pastSales.find((sale) => sale.type === "Listed");
|
|
11774
12685
|
const loafEvents = pastSales.filter(
|
|
11775
12686
|
(sale) => sale.ownershipPeriod === "current" && sale.type !== "Listed" && (sale.id === "leased-current" || sale.id === "da-approval")
|
|
@@ -11906,7 +12817,7 @@ function PropertyHistory() {
|
|
|
11906
12817
|
] });
|
|
11907
12818
|
}
|
|
11908
12819
|
function EventDetails({ event }) {
|
|
11909
|
-
const [expanded, setExpanded] =
|
|
12820
|
+
const [expanded, setExpanded] = React9.useState(false);
|
|
11910
12821
|
const toggleExpand = () => {
|
|
11911
12822
|
setExpanded((prev) => !prev);
|
|
11912
12823
|
};
|
|
@@ -12403,9 +13314,9 @@ var COMING_SOON_TITLES = [
|
|
|
12403
13314
|
"Risk Disclosure Statement"
|
|
12404
13315
|
];
|
|
12405
13316
|
function PropertyDocuments({ documentsData, highlightDocument, onClearHighlight }) {
|
|
12406
|
-
const highlightRef =
|
|
12407
|
-
const [previewUrl, setPreviewUrl] =
|
|
12408
|
-
const [previewTitle, setPreviewTitle] =
|
|
13317
|
+
const highlightRef = React9.useRef(null);
|
|
13318
|
+
const [previewUrl, setPreviewUrl] = React9.useState(null);
|
|
13319
|
+
const [previewTitle, setPreviewTitle] = React9.useState(null);
|
|
12409
13320
|
const backendDocuments = Array.isArray(documentsData?.documents) ? documentsData.documents : [];
|
|
12410
13321
|
const liveDocs = backendDocuments.filter(
|
|
12411
13322
|
(d) => Boolean(d.documentUrl) && isSafeUrl(d.documentUrl) && LIVE_TITLE_PATTERNS.some((rx) => rx.test(d.title))
|
|
@@ -12420,7 +13331,7 @@ function PropertyDocuments({ documentsData, highlightDocument, onClearHighlight
|
|
|
12420
13331
|
setPreviewUrl(doc.documentUrl);
|
|
12421
13332
|
setPreviewTitle(doc.title);
|
|
12422
13333
|
};
|
|
12423
|
-
|
|
13334
|
+
React9.useEffect(() => {
|
|
12424
13335
|
if (highlightDocument && highlightRef.current) {
|
|
12425
13336
|
highlightRef.current.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
12426
13337
|
}
|
|
@@ -12429,7 +13340,7 @@ function PropertyDocuments({ documentsData, highlightDocument, onClearHighlight
|
|
|
12429
13340
|
if (hit) selectPreview(hit);
|
|
12430
13341
|
}
|
|
12431
13342
|
}, [highlightDocument]);
|
|
12432
|
-
|
|
13343
|
+
React9.useEffect(() => {
|
|
12433
13344
|
if (!highlightDocument || !onClearHighlight) return;
|
|
12434
13345
|
const handler = (e) => {
|
|
12435
13346
|
if (highlightRef.current && !highlightRef.current.contains(e.target)) {
|
|
@@ -12825,6 +13736,32 @@ var SignInButton = styled10__default.default.button`
|
|
|
12825
13736
|
background: rgba(240, 185, 11, 0.1);
|
|
12826
13737
|
}
|
|
12827
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
|
+
}
|
|
12828
13765
|
function displayName(item) {
|
|
12829
13766
|
if (item.name) return item.name;
|
|
12830
13767
|
return item.tokenName.charAt(0).toUpperCase() + item.tokenName.slice(1);
|
|
@@ -12838,24 +13775,79 @@ function AssetSelectorBar({
|
|
|
12838
13775
|
selectorItems,
|
|
12839
13776
|
onSelect,
|
|
12840
13777
|
trailing,
|
|
13778
|
+
afterName,
|
|
12841
13779
|
imageUrl,
|
|
12842
13780
|
badgeLabel,
|
|
12843
|
-
tickerPrimary
|
|
13781
|
+
tickerPrimary,
|
|
13782
|
+
compactName
|
|
12844
13783
|
}) {
|
|
12845
|
-
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");
|
|
12846
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
|
+
};
|
|
12847
13827
|
const currentTicker = currentTokenName ? selectorItems?.find((i) => i.tokenName.toLowerCase() === currentTokenName.toLowerCase())?.ticker : void 0;
|
|
12848
13828
|
const metrics = metricsProp ?? [
|
|
12849
13829
|
...tokenPrice != null ? [{ label: "Unit Price", value: `$${tokenPrice.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`, accent: true }] : [],
|
|
12850
13830
|
...offeringValuation != null ? [{ label: "Offering Valuation", value: `$${offeringValuation.toLocaleString()}` }] : []
|
|
12851
13831
|
];
|
|
12852
|
-
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: [
|
|
12853
13833
|
imageUrl && /* @__PURE__ */ jsxRuntime.jsx(AssetThumbnail, { src: imageUrl, alt: propertyName }),
|
|
12854
|
-
/* @__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: [
|
|
12855
13835
|
tickerPrimary && currentTicker ? /* @__PURE__ */ jsxRuntime.jsxs(AssetIdentity, { children: [
|
|
12856
13836
|
/* @__PURE__ */ jsxRuntime.jsx(AssetTicker, { children: currentTicker.toUpperCase() }),
|
|
12857
13837
|
/* @__PURE__ */ jsxRuntime.jsx(AssetSubName, { children: propertyName })
|
|
12858
|
-
] }) : /* @__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: [
|
|
12859
13851
|
propertyName,
|
|
12860
13852
|
currentTicker ? ` (${currentTicker.toUpperCase()})` : ""
|
|
12861
13853
|
] }),
|
|
@@ -12873,6 +13865,7 @@ function AssetSelectorBar({
|
|
|
12873
13865
|
}
|
|
12874
13866
|
)
|
|
12875
13867
|
] }) }),
|
|
13868
|
+
afterName != null && /* @__PURE__ */ jsxRuntime.jsx(AfterNameSlot, { className: "asset-bar-after-name", children: afterName }),
|
|
12876
13869
|
metrics.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(SelectorMetrics, { children: metrics.map((m, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "contents" }, children: [
|
|
12877
13870
|
i > 0 && /* @__PURE__ */ jsxRuntime.jsx(Separator, { children: "|" }),
|
|
12878
13871
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
|
|
@@ -12888,40 +13881,128 @@ function AssetSelectorBar({
|
|
|
12888
13881
|
] })
|
|
12889
13882
|
] }, m.label)) }),
|
|
12890
13883
|
trailing != null && /* @__PURE__ */ jsxRuntime.jsx(TrailingWrapper, { className: "asset-bar-trailing", children: /* @__PURE__ */ jsxRuntime.jsx(TrailingInner, { children: trailing }) }),
|
|
12891
|
-
|
|
12892
|
-
|
|
12893
|
-
|
|
12894
|
-
const statusLabel = status === "PENDING" ? "Preparing" : status === "CLOSED" ? "Closed" : status === "CANCELLED" ? "Cancelled" : null;
|
|
12895
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
12896
|
-
IPOOption,
|
|
13884
|
+
nameTip != null && typeof document !== "undefined" && ReactDOM.createPortal(
|
|
13885
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
13886
|
+
NameTip,
|
|
12897
13887
|
{
|
|
12898
|
-
|
|
12899
|
-
|
|
12900
|
-
|
|
12901
|
-
|
|
12902
|
-
|
|
12903
|
-
|
|
12904
|
-
|
|
12905
|
-
|
|
12906
|
-
|
|
12907
|
-
|
|
12908
|
-
|
|
12909
|
-
|
|
12910
|
-
|
|
12911
|
-
|
|
12912
|
-
|
|
12913
|
-
|
|
12914
|
-
/* @__PURE__ */ jsxRuntime.
|
|
12915
|
-
|
|
12916
|
-
|
|
12917
|
-
|
|
12918
|
-
|
|
12919
|
-
|
|
12920
|
-
|
|
12921
|
-
|
|
12922
|
-
|
|
12923
|
-
|
|
12924
|
-
|
|
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
|
+
)
|
|
12925
14006
|
] }) });
|
|
12926
14007
|
}
|
|
12927
14008
|
var AssetSelectorWrapper = styled10__default.default.div`
|
|
@@ -12983,7 +14064,9 @@ var AssetSelectorDropdown = styled10__default.default.div`
|
|
|
12983
14064
|
padding: ${(p) => p.$flush ? "0.25rem 0" : "0.5rem"};
|
|
12984
14065
|
border-radius: 8px;
|
|
12985
14066
|
transition: ${(p) => p.$flush ? "none" : "all 0.2s ease"};
|
|
12986
|
-
|
|
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"};
|
|
12987
14070
|
min-width: 0;
|
|
12988
14071
|
overflow: hidden;
|
|
12989
14072
|
&:hover { background-color: ${(p) => p.$flush ? "transparent" : "rgba(255,255,255,0.05)"}; }
|
|
@@ -13040,6 +14123,35 @@ var AssetName = styled10__default.default.div`
|
|
|
13040
14123
|
svg { margin-left: 0.4rem; width: 16px; height: 16px; }
|
|
13041
14124
|
}
|
|
13042
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
|
+
`;
|
|
13043
14155
|
var SelectorMetrics = styled10__default.default.div`
|
|
13044
14156
|
display: flex;
|
|
13045
14157
|
align-items: center;
|
|
@@ -13060,6 +14172,12 @@ var SelectorMetrics = styled10__default.default.div`
|
|
|
13060
14172
|
padding: 0.4rem 0.5rem;
|
|
13061
14173
|
}
|
|
13062
14174
|
`;
|
|
14175
|
+
var AfterNameSlot = styled10__default.default.div`
|
|
14176
|
+
display: flex;
|
|
14177
|
+
align-items: center;
|
|
14178
|
+
flex-shrink: 0;
|
|
14179
|
+
min-width: 0;
|
|
14180
|
+
`;
|
|
13063
14181
|
var TrailingWrapper = styled10__default.default.div`
|
|
13064
14182
|
display: flex;
|
|
13065
14183
|
align-items: center;
|
|
@@ -13131,14 +14249,16 @@ var IPODropdown = styled10__default.default.div`
|
|
|
13131
14249
|
position: absolute;
|
|
13132
14250
|
top: calc(100% + 6px);
|
|
13133
14251
|
left: 0;
|
|
13134
|
-
width: 320px;
|
|
14252
|
+
width: ${(p) => p.$wide ? "min(660px, calc(100vw - 24px))" : "320px"};
|
|
13135
14253
|
background: #0d0d0f;
|
|
13136
14254
|
border-radius: 10px;
|
|
13137
14255
|
box-shadow: 0 12px 32px rgba(0,0,0,0.7), 0 0 0 1px rgba(255,255,255,0.06);
|
|
13138
14256
|
z-index: 1000;
|
|
13139
|
-
max-height:
|
|
14257
|
+
max-height: 360px;
|
|
13140
14258
|
overflow-y: auto;
|
|
13141
|
-
|
|
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"};
|
|
13142
14262
|
|
|
13143
14263
|
scrollbar-width: thin;
|
|
13144
14264
|
scrollbar-color: rgba(255,255,255,0.12) transparent;
|
|
@@ -13146,8 +14266,8 @@ var IPODropdown = styled10__default.default.div`
|
|
|
13146
14266
|
&::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.12); border-radius: 2px; }
|
|
13147
14267
|
|
|
13148
14268
|
@media (max-width: 480px) {
|
|
13149
|
-
width: 280px;
|
|
13150
|
-
max-height:
|
|
14269
|
+
width: ${(p) => p.$wide ? "min(360px, calc(100vw - 16px))" : "280px"};
|
|
14270
|
+
max-height: 300px;
|
|
13151
14271
|
border-radius: 8px;
|
|
13152
14272
|
}
|
|
13153
14273
|
animation: slideDown 0.2s cubic-bezier(0.16,1,0.3,1);
|
|
@@ -13156,6 +14276,99 @@ var IPODropdown = styled10__default.default.div`
|
|
|
13156
14276
|
to { opacity: 1; transform: translateY(0); }
|
|
13157
14277
|
}
|
|
13158
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
|
+
`;
|
|
13159
14372
|
var IPOOption = styled10__default.default.div`
|
|
13160
14373
|
padding: 0.6rem 0.75rem;
|
|
13161
14374
|
transition: background 0.15s ease;
|
|
@@ -13181,6 +14394,25 @@ var IPOOptionName = styled10__default.default.div`
|
|
|
13181
14394
|
font-size: 0.82rem;
|
|
13182
14395
|
color: ${"#ebebeb"};
|
|
13183
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;
|
|
13184
14416
|
`;
|
|
13185
14417
|
var CompetitionBadge = styled10__default.default.span`
|
|
13186
14418
|
margin-left: 0.4rem;
|
|
@@ -13199,6 +14431,11 @@ var IPOOptionLocation = styled10__default.default.div`
|
|
|
13199
14431
|
font-size: 0.72rem;
|
|
13200
14432
|
color: rgba(255,255,255,0.38);
|
|
13201
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;
|
|
13202
14439
|
span {
|
|
13203
14440
|
margin-left: 0.4rem;
|
|
13204
14441
|
color: var(--color-accent, #E6C87E);
|
|
@@ -13232,10 +14469,10 @@ var SlideOld = styled10__default.default.span`
|
|
|
13232
14469
|
animation: ${slideOut} 0.35s cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
|
13233
14470
|
`;
|
|
13234
14471
|
function SlideDigit({ value }) {
|
|
13235
|
-
const prevRef =
|
|
13236
|
-
const [prev, setPrev] =
|
|
13237
|
-
const [animKey, setAnimKey] =
|
|
13238
|
-
|
|
14472
|
+
const prevRef = React9.useRef(value);
|
|
14473
|
+
const [prev, setPrev] = React9.useState(null);
|
|
14474
|
+
const [animKey, setAnimKey] = React9.useState(0);
|
|
14475
|
+
React9.useEffect(() => {
|
|
13239
14476
|
if (value !== prevRef.current) {
|
|
13240
14477
|
setPrev(prevRef.current);
|
|
13241
14478
|
prevRef.current = value;
|
|
@@ -13275,15 +14512,15 @@ function OfferingProgressCard({
|
|
|
13275
14512
|
style,
|
|
13276
14513
|
className
|
|
13277
14514
|
}) {
|
|
13278
|
-
const [currentTime, setCurrentTime] =
|
|
13279
|
-
const [countdown, setCountdown] =
|
|
14515
|
+
const [currentTime, setCurrentTime] = React9.useState(/* @__PURE__ */ new Date());
|
|
14516
|
+
const [countdown, setCountdown] = React9.useState(null);
|
|
13280
14517
|
const computedRaised = raisedAmount ?? totalSold * tokenPrice;
|
|
13281
14518
|
const computedTarget = targetAmount ?? supplyToSell * tokenPrice;
|
|
13282
|
-
|
|
14519
|
+
React9.useEffect(() => {
|
|
13283
14520
|
const timer = setInterval(() => setCurrentTime(/* @__PURE__ */ new Date()), 1e3);
|
|
13284
14521
|
return () => clearInterval(timer);
|
|
13285
14522
|
}, []);
|
|
13286
|
-
const [tradingCountdown, setTradingCountdown] =
|
|
14523
|
+
const [tradingCountdown, setTradingCountdown] = React9.useState(null);
|
|
13287
14524
|
const calcCountdownFrom = (target) => {
|
|
13288
14525
|
const now = Math.floor(Date.now() / 1e3);
|
|
13289
14526
|
const diff = target - now;
|
|
@@ -13295,7 +14532,7 @@ function OfferingProgressCard({
|
|
|
13295
14532
|
seconds: diff % 60
|
|
13296
14533
|
};
|
|
13297
14534
|
};
|
|
13298
|
-
|
|
14535
|
+
React9.useEffect(() => {
|
|
13299
14536
|
if (!opensAt) {
|
|
13300
14537
|
setCountdown(null);
|
|
13301
14538
|
return;
|
|
@@ -13304,7 +14541,7 @@ function OfferingProgressCard({
|
|
|
13304
14541
|
const interval = setInterval(() => setCountdown(calcCountdownFrom(opensAt)), 1e3);
|
|
13305
14542
|
return () => clearInterval(interval);
|
|
13306
14543
|
}, [opensAt]);
|
|
13307
|
-
|
|
14544
|
+
React9.useEffect(() => {
|
|
13308
14545
|
if (!tradingStartsAt) {
|
|
13309
14546
|
setTradingCountdown(null);
|
|
13310
14547
|
return;
|
|
@@ -13798,8 +15035,8 @@ var HomeSpinner = styled10__default.default.span`
|
|
|
13798
15035
|
}
|
|
13799
15036
|
`;
|
|
13800
15037
|
function useCountdown(opensAt) {
|
|
13801
|
-
const [now, setNow] =
|
|
13802
|
-
|
|
15038
|
+
const [now, setNow] = React9.useState(() => Math.floor(Date.now() / 1e3));
|
|
15039
|
+
React9.useEffect(() => {
|
|
13803
15040
|
if (opensAt == null) return;
|
|
13804
15041
|
const id = setInterval(() => setNow(Math.floor(Date.now() / 1e3)), 1e3);
|
|
13805
15042
|
return () => clearInterval(id);
|
|
@@ -13849,10 +15086,10 @@ function OrderPanel({
|
|
|
13849
15086
|
toast
|
|
13850
15087
|
}) {
|
|
13851
15088
|
const countdown = useCountdown(opensAt);
|
|
13852
|
-
const [payInputValue, setPayInputValue] =
|
|
13853
|
-
const [receiveInputValue, setReceiveInputValue] =
|
|
13854
|
-
const [isPayInputFocused, setIsPayInputFocused] =
|
|
13855
|
-
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);
|
|
13856
15093
|
const handlePayBlur = () => {
|
|
13857
15094
|
setIsPayInputFocused(false);
|
|
13858
15095
|
const parsed = parseInt(payInputValue.replace(/,/g, "").replace(/[^0-9]/g, ""), 10) || 0;
|
|
@@ -14878,7 +16115,7 @@ function OrderConfirmationModal({
|
|
|
14878
16115
|
isPlacing = false,
|
|
14879
16116
|
placingLabel = "Placing order\u2026"
|
|
14880
16117
|
}) {
|
|
14881
|
-
const [dontShowAgain, setDontShowAgain] =
|
|
16118
|
+
const [dontShowAgain, setDontShowAgain] = React9.useState(false);
|
|
14882
16119
|
if (!open) return null;
|
|
14883
16120
|
const handleOverlayClose = () => {
|
|
14884
16121
|
if (isPlacing) return;
|
|
@@ -15330,14 +16567,14 @@ function PortfolioActivityPanel({
|
|
|
15330
16567
|
style
|
|
15331
16568
|
}) {
|
|
15332
16569
|
const resolvedDefaultTab = defaultTab ?? (showPositionsTab ? "positions" : showSubscriptionsTab ? "subscriptions" : "orders");
|
|
15333
|
-
const [activeTab, setActiveTab] =
|
|
15334
|
-
const [currentPage, setCurrentPage] =
|
|
15335
|
-
const [pendingCancelOrderId, setPendingCancelOrderId] =
|
|
15336
|
-
const [cancelAllConfirmOpen, setCancelAllConfirmOpen] =
|
|
15337
|
-
const [cancellingAll, setCancellingAll] =
|
|
15338
|
-
const [closeAllOpen, setCloseAllOpen] =
|
|
15339
|
-
const [closeAllType, setCloseAllType] =
|
|
15340
|
-
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);
|
|
15341
16578
|
const settlingPositionIds = positions.filter((p) => p.settling).map((p) => p.propertyId);
|
|
15342
16579
|
const settlingKey = settlingPositionIds.join(",");
|
|
15343
16580
|
const settlingTradeStartByProperty = /* @__PURE__ */ new Map();
|
|
@@ -15350,9 +16587,9 @@ function PortfolioActivityPanel({
|
|
|
15350
16587
|
if (earliest === void 0 || startedAt < earliest) settlingTradeStartByProperty.set(trade.propertyId, startedAt);
|
|
15351
16588
|
}
|
|
15352
16589
|
const tradeDeadlineKey = settlingTradeDeadlines.join(",");
|
|
15353
|
-
const settlingSinceRef =
|
|
15354
|
-
const [now, setNow] =
|
|
15355
|
-
|
|
16590
|
+
const settlingSinceRef = React9.useRef(/* @__PURE__ */ new Map());
|
|
16591
|
+
const [now, setNow] = React9.useState(() => Date.now());
|
|
16592
|
+
React9.useEffect(() => {
|
|
15356
16593
|
const since = settlingSinceRef.current;
|
|
15357
16594
|
const seenAt = Date.now();
|
|
15358
16595
|
for (const id of settlingPositionIds) if (!since.has(id)) since.set(id, seenAt);
|
|
@@ -15360,7 +16597,7 @@ function PortfolioActivityPanel({
|
|
|
15360
16597
|
setNow(seenAt);
|
|
15361
16598
|
}, [settlingKey]);
|
|
15362
16599
|
const positionSettlingStart = (propertyId) => settlingTradeStartByProperty.get(propertyId) ?? settlingSinceRef.current.get(propertyId) ?? now;
|
|
15363
|
-
|
|
16600
|
+
React9.useEffect(() => {
|
|
15364
16601
|
const pending = [
|
|
15365
16602
|
...settlingPositionIds.map((id) => positionSettlingStart(id) + STUCK_SETTLING_MS),
|
|
15366
16603
|
...settlingTradeDeadlines
|
|
@@ -15373,8 +16610,8 @@ function PortfolioActivityPanel({
|
|
|
15373
16610
|
const isPositionStuck = (pos) => !!pos.settling && now - positionSettlingStart(pos.propertyId) >= STUCK_SETTLING_MS;
|
|
15374
16611
|
const isTradeStuck = (trade) => now - toMillis(trade.executedAt) >= STUCK_SETTLING_MS;
|
|
15375
16612
|
const hasPositionActions = !!(onClosePosition || onSharePosition || onCloseAllPositions);
|
|
15376
|
-
const [posSortKey, setPosSortKey] =
|
|
15377
|
-
const [posSortAsc, setPosSortAsc] =
|
|
16613
|
+
const [posSortKey, setPosSortKey] = React9.useState("asset");
|
|
16614
|
+
const [posSortAsc, setPosSortAsc] = React9.useState(true);
|
|
15378
16615
|
const handlePosSort = (key) => {
|
|
15379
16616
|
if (posSortKey === key) {
|
|
15380
16617
|
setPosSortAsc((v) => !v);
|
|
@@ -15383,9 +16620,9 @@ function PortfolioActivityPanel({
|
|
|
15383
16620
|
setPosSortAsc(key === "asset");
|
|
15384
16621
|
}
|
|
15385
16622
|
};
|
|
15386
|
-
const [expandedPositions, setExpandedPositions] =
|
|
15387
|
-
const [allExpanded, setAllExpanded] =
|
|
15388
|
-
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);
|
|
15389
16626
|
const togglePosition = (tokenName) => {
|
|
15390
16627
|
setExpandedPositions((prev) => {
|
|
15391
16628
|
const next = new Set(prev);
|
|
@@ -15394,7 +16631,7 @@ function PortfolioActivityPanel({
|
|
|
15394
16631
|
return next;
|
|
15395
16632
|
});
|
|
15396
16633
|
};
|
|
15397
|
-
const [expandedOpenOrders, setExpandedOpenOrders] =
|
|
16634
|
+
const [expandedOpenOrders, setExpandedOpenOrders] = React9.useState(/* @__PURE__ */ new Set());
|
|
15398
16635
|
const toggleOpenOrder = (id) => {
|
|
15399
16636
|
setExpandedOpenOrders((prev) => {
|
|
15400
16637
|
const next = new Set(prev);
|
|
@@ -15403,7 +16640,7 @@ function PortfolioActivityPanel({
|
|
|
15403
16640
|
return next;
|
|
15404
16641
|
});
|
|
15405
16642
|
};
|
|
15406
|
-
const [expandedRows, setExpandedRows] =
|
|
16643
|
+
const [expandedRows, setExpandedRows] = React9.useState(/* @__PURE__ */ new Set());
|
|
15407
16644
|
const toggleRow = (key) => {
|
|
15408
16645
|
setExpandedRows((prev) => {
|
|
15409
16646
|
const next = new Set(prev);
|
|
@@ -15431,7 +16668,7 @@ function PortfolioActivityPanel({
|
|
|
15431
16668
|
}
|
|
15432
16669
|
};
|
|
15433
16670
|
const sortKeys = ["asset", "units", "value", "avgPrice", "currentPrice", "pnl"];
|
|
15434
|
-
const sortedPositions =
|
|
16671
|
+
const sortedPositions = React9.useMemo(() => {
|
|
15435
16672
|
const arr = [...positions];
|
|
15436
16673
|
const dir = posSortAsc ? 1 : -1;
|
|
15437
16674
|
arr.sort((a, b) => {
|
|
@@ -15466,7 +16703,7 @@ function PortfolioActivityPanel({
|
|
|
15466
16703
|
setAllExpanded(true);
|
|
15467
16704
|
}
|
|
15468
16705
|
};
|
|
15469
|
-
const mergedOrderHistory =
|
|
16706
|
+
const mergedOrderHistory = React9.useMemo(() => {
|
|
15470
16707
|
if (showSubscriptionsTab || offeringOrders.length === 0) {
|
|
15471
16708
|
return [...orderHistory].sort((a, b) => b.createdAt - a.createdAt);
|
|
15472
16709
|
}
|
|
@@ -15491,32 +16728,32 @@ function PortfolioActivityPanel({
|
|
|
15491
16728
|
}));
|
|
15492
16729
|
return [...orderHistory, ...mapped].sort((a, b) => b.createdAt - a.createdAt);
|
|
15493
16730
|
}, [showSubscriptionsTab, offeringOrders, orderHistory]);
|
|
15494
|
-
const sortedOpenOrders =
|
|
16731
|
+
const sortedOpenOrders = React9.useMemo(
|
|
15495
16732
|
() => [...openOrders].sort((a, b) => b.createdAt - a.createdAt),
|
|
15496
16733
|
[openOrders]
|
|
15497
16734
|
);
|
|
15498
|
-
const sortedOfferingOrders =
|
|
16735
|
+
const sortedOfferingOrders = React9.useMemo(
|
|
15499
16736
|
() => [...offeringOrders].sort((a, b) => b.createdAt - a.createdAt),
|
|
15500
16737
|
[offeringOrders]
|
|
15501
16738
|
);
|
|
15502
|
-
const sortedTradeHistory =
|
|
16739
|
+
const sortedTradeHistory = React9.useMemo(
|
|
15503
16740
|
() => [...tradeHistory].sort((a, b) => b.executedAt - a.executedAt),
|
|
15504
16741
|
[tradeHistory]
|
|
15505
16742
|
);
|
|
15506
|
-
const sortedTransfers =
|
|
16743
|
+
const sortedTransfers = React9.useMemo(
|
|
15507
16744
|
() => [...transfers].sort((a, b) => b.createdAt - a.createdAt),
|
|
15508
16745
|
[transfers]
|
|
15509
16746
|
);
|
|
15510
16747
|
const positionsCount = positions.length;
|
|
15511
|
-
const openOrdersCount =
|
|
16748
|
+
const openOrdersCount = React9.useMemo(
|
|
15512
16749
|
() => openOrders.filter((o) => o.status === "OPEN" || o.status === "PARTIALLY_FILLED").length,
|
|
15513
16750
|
[openOrders]
|
|
15514
16751
|
);
|
|
15515
|
-
const pendingHistoryCount =
|
|
16752
|
+
const pendingHistoryCount = React9.useMemo(
|
|
15516
16753
|
() => mergedOrderHistory.filter((o) => o.status === "OPEN" || o.status === "PARTIALLY_FILLED").length,
|
|
15517
16754
|
[mergedOrderHistory]
|
|
15518
16755
|
);
|
|
15519
|
-
const pendingOfferingsCount =
|
|
16756
|
+
const pendingOfferingsCount = React9.useMemo(
|
|
15520
16757
|
() => offeringOrders.filter((o) => {
|
|
15521
16758
|
const s = o.status.toUpperCase();
|
|
15522
16759
|
return s !== "FILLED" && s !== "ALLOCATED" && s !== "CONFIRMED" && s !== "CANCELLED" && s !== "CANCELED" && s !== "REJECTED";
|
|
@@ -16363,7 +17600,7 @@ var ContentScroll = styled10__default.default.div`
|
|
|
16363
17600
|
`;
|
|
16364
17601
|
var TabContainer = styled10__default.default.div`
|
|
16365
17602
|
display: flex;
|
|
16366
|
-
gap:
|
|
17603
|
+
gap: 1rem;
|
|
16367
17604
|
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
|
16368
17605
|
margin-bottom: 0.75rem;
|
|
16369
17606
|
overflow-x: auto;
|
|
@@ -16375,12 +17612,12 @@ var TabContainer = styled10__default.default.div`
|
|
|
16375
17612
|
var Tab = styled10__default.default.button`
|
|
16376
17613
|
background: transparent;
|
|
16377
17614
|
border: none;
|
|
16378
|
-
padding: 0.
|
|
16379
|
-
font-size: 0.
|
|
17615
|
+
padding: 0 0 0.5rem;
|
|
17616
|
+
font-size: 0.8rem;
|
|
16380
17617
|
font-weight: 500;
|
|
16381
17618
|
white-space: nowrap;
|
|
16382
17619
|
flex-shrink: 0;
|
|
16383
|
-
color: ${({ $active }) => $active ? "#
|
|
17620
|
+
color: ${({ $active }) => $active ? "#fff" : "rgba(255, 255, 255, 0.6)"};
|
|
16384
17621
|
cursor: pointer;
|
|
16385
17622
|
transition: color 0.15s;
|
|
16386
17623
|
position: relative;
|
|
@@ -16393,12 +17630,11 @@ var Tab = styled10__default.default.button`
|
|
|
16393
17630
|
right: 0;
|
|
16394
17631
|
height: 2px;
|
|
16395
17632
|
background: ${({ $active }) => $active ? "#E6C87E" : "transparent"};
|
|
16396
|
-
border-radius: 1px;
|
|
16397
17633
|
transition: background 0.15s;
|
|
16398
17634
|
}
|
|
16399
17635
|
|
|
16400
17636
|
&:hover {
|
|
16401
|
-
color: ${({ $active }) => $active ? "#fff" : "rgba(255, 255, 255, 0.
|
|
17637
|
+
color: ${({ $active }) => $active ? "#fff" : "rgba(255, 255, 255, 0.85)"};
|
|
16402
17638
|
}
|
|
16403
17639
|
`;
|
|
16404
17640
|
var TabCount = styled10__default.default.span`
|
|
@@ -17117,17 +18353,17 @@ function PropertyBuy({
|
|
|
17117
18353
|
videoUrl,
|
|
17118
18354
|
toast
|
|
17119
18355
|
}) {
|
|
17120
|
-
const [sliderValue, setSliderValue] =
|
|
17121
|
-
const [availableBalance, setAvailableBalance] =
|
|
17122
|
-
const [manualOrderAmount, setManualOrderAmount] =
|
|
17123
|
-
const [ownedTokens, setOwnedTokens] =
|
|
17124
|
-
const [displayedOwnedTokens, setDisplayedOwnedTokens] =
|
|
17125
|
-
const [ownedTokensJustUpdated, setOwnedTokensJustUpdated] =
|
|
17126
|
-
const [lastOrderQuantity, setLastOrderQuantity] =
|
|
17127
|
-
const [orderPendingAllocation, setOrderPendingAllocation] =
|
|
17128
|
-
const [orderPlacedSuccess, setOrderPlacedSuccess] =
|
|
17129
|
-
const [lastOrderDetails, setLastOrderDetails] =
|
|
17130
|
-
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);
|
|
17131
18367
|
const tokenPrice = saleData?.tokenPrice ?? 250;
|
|
17132
18368
|
const feeRate = (saleData?.feePercent ?? 0.5) / 100;
|
|
17133
18369
|
const totalSold = saleData?.totalSold ?? 0;
|
|
@@ -17147,12 +18383,12 @@ function PropertyBuy({
|
|
|
17147
18383
|
const orderTotal = tokenQuantity * tokenPrice + feeInUsd;
|
|
17148
18384
|
const estExposure = (tokenQuantity / supplyToSell * 100).toFixed(4);
|
|
17149
18385
|
const hasInsufficientFunds = orderTotal > availableBalance;
|
|
17150
|
-
|
|
18386
|
+
React9.useEffect(() => {
|
|
17151
18387
|
if (walletUsdcBalance != null) {
|
|
17152
18388
|
setAvailableBalance(walletUsdcBalance);
|
|
17153
18389
|
}
|
|
17154
18390
|
}, [walletUsdcBalance]);
|
|
17155
|
-
|
|
18391
|
+
React9.useEffect(() => {
|
|
17156
18392
|
if (walletPropertyTokenBalance != null) {
|
|
17157
18393
|
setOwnedTokens(walletPropertyTokenBalance);
|
|
17158
18394
|
setDisplayedOwnedTokens(walletPropertyTokenBalance);
|
|
@@ -17168,17 +18404,17 @@ function PropertyBuy({
|
|
|
17168
18404
|
}
|
|
17169
18405
|
setShowOrderConfirmModal(true);
|
|
17170
18406
|
};
|
|
17171
|
-
|
|
18407
|
+
React9.useEffect(() => {
|
|
17172
18408
|
if (purchaseStatus === "success" && orderPendingAllocation) {
|
|
17173
18409
|
setOrderPendingAllocation(false);
|
|
17174
18410
|
setOrderPlacedSuccess(true);
|
|
17175
18411
|
}
|
|
17176
18412
|
}, [purchaseStatus, orderPendingAllocation]);
|
|
17177
|
-
const handlePlaceAnotherOrder =
|
|
18413
|
+
const handlePlaceAnotherOrder = React9.useCallback(() => {
|
|
17178
18414
|
setOrderPlacedSuccess(false);
|
|
17179
18415
|
setOrderPendingAllocation(false);
|
|
17180
18416
|
}, []);
|
|
17181
|
-
const sortedOrders =
|
|
18417
|
+
const sortedOrders = React9.useMemo(
|
|
17182
18418
|
() => [...recentOrders].sort((a, b) => b.timestamp - a.timestamp).slice(0, 7),
|
|
17183
18419
|
[recentOrders]
|
|
17184
18420
|
);
|
|
@@ -17748,7 +18984,7 @@ function PaymentPopup({
|
|
|
17748
18984
|
userTokenHoldings,
|
|
17749
18985
|
propertyName
|
|
17750
18986
|
}) {
|
|
17751
|
-
const [selectedPaymentMethod, setSelectedPaymentMethod] =
|
|
18987
|
+
const [selectedPaymentMethod, setSelectedPaymentMethod] = React9.useState(
|
|
17752
18988
|
"tokens"
|
|
17753
18989
|
);
|
|
17754
18990
|
if (!isOpen) return null;
|
|
@@ -17927,20 +19163,20 @@ function PaymentPopup({
|
|
|
17927
19163
|
var payment_popup_default = PaymentPopup;
|
|
17928
19164
|
var SUMMER_MONTHS = /* @__PURE__ */ new Set([11, 0, 1]);
|
|
17929
19165
|
var OwnerBooking = ({ propertyName, token }) => {
|
|
17930
|
-
const today =
|
|
19166
|
+
const today = React9.useMemo(() => {
|
|
17931
19167
|
const base = /* @__PURE__ */ new Date();
|
|
17932
19168
|
base.setHours(0, 0, 0, 0);
|
|
17933
19169
|
return base;
|
|
17934
19170
|
}, []);
|
|
17935
|
-
const [currentYear, setCurrentYear] =
|
|
17936
|
-
const [currentMonth, setCurrentMonth] =
|
|
17937
|
-
const [dateRanges, setDateRanges] =
|
|
17938
|
-
const [selectedDates, setSelectedDates] =
|
|
17939
|
-
const [hoverDate, setHoverDate] =
|
|
17940
|
-
const [isDateSelectorOpen, setIsDateSelectorOpen] =
|
|
17941
|
-
const [selectorYear, setSelectorYear] =
|
|
17942
|
-
const [isPaymentPopupOpen, setIsPaymentPopupOpen] =
|
|
17943
|
-
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);
|
|
17944
19180
|
const tokenPrice = token.price ?? 700;
|
|
17945
19181
|
const userTokenHoldings = 2.2;
|
|
17946
19182
|
const MIN_NIGHTS = 3;
|
|
@@ -17950,25 +19186,25 @@ var OwnerBooking = ({ propertyName, token }) => {
|
|
|
17950
19186
|
{ bg: "rgba(230, 126, 34, 0.25)", border: "rgba(230, 126, 34, 0.6)" },
|
|
17951
19187
|
{ bg: "rgba(46, 204, 113, 0.25)", border: "rgba(46, 204, 113, 0.6)" }
|
|
17952
19188
|
];
|
|
17953
|
-
const calculateTokensPerDayOwner =
|
|
19189
|
+
const calculateTokensPerDayOwner = React9.useCallback(() => {
|
|
17954
19190
|
if (tokenPrice <= 0) return 0;
|
|
17955
19191
|
const target = 650;
|
|
17956
19192
|
const raw = target / tokenPrice;
|
|
17957
19193
|
return Math.ceil(raw * 1e4) / 1e4;
|
|
17958
19194
|
}, [tokenPrice]);
|
|
17959
|
-
const calculateTokensPerDayMarket =
|
|
19195
|
+
const calculateTokensPerDayMarket = React9.useCallback(() => {
|
|
17960
19196
|
if (tokenPrice <= 0) return 0;
|
|
17961
19197
|
const target = 900;
|
|
17962
19198
|
const raw = target / tokenPrice;
|
|
17963
19199
|
return Math.ceil(raw * 1e4) / 1e4;
|
|
17964
19200
|
}, [tokenPrice]);
|
|
17965
19201
|
const qualifiesForOwnerRate = userTokenHoldings >= 0.1;
|
|
17966
|
-
const getTokensPerDay =
|
|
19202
|
+
const getTokensPerDay = React9.useCallback(
|
|
17967
19203
|
() => calculateTokensPerDayOwner() ,
|
|
17968
19204
|
[qualifiesForOwnerRate, calculateTokensPerDayOwner, calculateTokensPerDayMarket]
|
|
17969
19205
|
);
|
|
17970
19206
|
const getRateLabel = () => "Owner's Rate" ;
|
|
17971
|
-
const selectionStats =
|
|
19207
|
+
const selectionStats = React9.useMemo(() => {
|
|
17972
19208
|
if (selectedDates.length !== 2) return null;
|
|
17973
19209
|
const [a, b] = selectedDates;
|
|
17974
19210
|
const start = a < b ? a : b;
|
|
@@ -17979,10 +19215,10 @@ var OwnerBooking = ({ propertyName, token }) => {
|
|
|
17979
19215
|
const usd = tokens * tokenPrice;
|
|
17980
19216
|
return { nights, tokensPerDay, tokens, usd, start, end };
|
|
17981
19217
|
}, [selectedDates, getTokensPerDay, tokenPrice]);
|
|
17982
|
-
const totalNights =
|
|
17983
|
-
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]);
|
|
17984
19220
|
const totalUSD = totalTokens * tokenPrice;
|
|
17985
|
-
const generateCalendarDays =
|
|
19221
|
+
const generateCalendarDays = React9.useCallback(
|
|
17986
19222
|
(year, month) => {
|
|
17987
19223
|
const first = new Date(year, month, 1);
|
|
17988
19224
|
const last = new Date(year, month + 1, 0);
|
|
@@ -18011,11 +19247,11 @@ var OwnerBooking = ({ propertyName, token }) => {
|
|
|
18011
19247
|
},
|
|
18012
19248
|
[today]
|
|
18013
19249
|
);
|
|
18014
|
-
const calendarDays =
|
|
19250
|
+
const calendarDays = React9.useMemo(
|
|
18015
19251
|
() => generateCalendarDays(currentYear, currentMonth),
|
|
18016
19252
|
[currentYear, currentMonth, generateCalendarDays]
|
|
18017
19253
|
);
|
|
18018
|
-
const isDateReserved =
|
|
19254
|
+
const isDateReserved = React9.useCallback(
|
|
18019
19255
|
(date) => dateRanges.some((r) => date >= r.arrival && date <= r.departure),
|
|
18020
19256
|
[dateRanges]
|
|
18021
19257
|
);
|
|
@@ -18109,14 +19345,14 @@ var OwnerBooking = ({ propertyName, token }) => {
|
|
|
18109
19345
|
"December"
|
|
18110
19346
|
];
|
|
18111
19347
|
const dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
18112
|
-
const setMonthYear =
|
|
19348
|
+
const setMonthYear = React9.useCallback(
|
|
18113
19349
|
(month, year) => {
|
|
18114
19350
|
setCurrentMonth(month);
|
|
18115
19351
|
setCurrentYear(year);
|
|
18116
19352
|
},
|
|
18117
19353
|
[setCurrentMonth, setCurrentYear]
|
|
18118
19354
|
);
|
|
18119
|
-
const shiftMonth =
|
|
19355
|
+
const shiftMonth = React9.useCallback(
|
|
18120
19356
|
(direction) => {
|
|
18121
19357
|
const delta = direction === "prev" ? -1 : 1;
|
|
18122
19358
|
let newMonth = currentMonth + delta;
|
|
@@ -18133,7 +19369,7 @@ var OwnerBooking = ({ propertyName, token }) => {
|
|
|
18133
19369
|
},
|
|
18134
19370
|
[currentMonth, currentYear, setMonthYear]
|
|
18135
19371
|
);
|
|
18136
|
-
const jumpToNextAvailable =
|
|
19372
|
+
const jumpToNextAvailable = React9.useCallback(() => {
|
|
18137
19373
|
for (let offset = 1; offset <= 12; offset++) {
|
|
18138
19374
|
const candidateMonth = (currentMonth + offset) % 12;
|
|
18139
19375
|
if (!SUMMER_MONTHS.has(candidateMonth)) continue;
|
|
@@ -18150,7 +19386,7 @@ var OwnerBooking = ({ propertyName, token }) => {
|
|
|
18150
19386
|
setMonthYear(month, selectorYear);
|
|
18151
19387
|
setIsDateSelectorOpen(false);
|
|
18152
19388
|
};
|
|
18153
|
-
|
|
19389
|
+
React9.useEffect(() => {
|
|
18154
19390
|
const handleClickOutside = (event) => {
|
|
18155
19391
|
if (dateSelectorRef.current && !dateSelectorRef.current.contains(event.target)) {
|
|
18156
19392
|
setIsDateSelectorOpen(false);
|
|
@@ -18583,8 +19819,8 @@ function PropertyValuation({
|
|
|
18583
19819
|
offeringValuationValue,
|
|
18584
19820
|
totalUnits
|
|
18585
19821
|
}) {
|
|
18586
|
-
const [hoveredSaleYear, setHoveredSaleYear] =
|
|
18587
|
-
const gaugePosition =
|
|
19822
|
+
const [hoveredSaleYear, setHoveredSaleYear] = React9.useState(null);
|
|
19823
|
+
const gaugePosition = React9.useMemo(() => {
|
|
18588
19824
|
const under = valSummary?.undervaluedThreshold;
|
|
18589
19825
|
const over = valSummary?.overvaluedThreshold;
|
|
18590
19826
|
const price = tokenPriceValue;
|
|
@@ -18594,7 +19830,7 @@ function PropertyValuation({
|
|
|
18594
19830
|
}
|
|
18595
19831
|
return 50;
|
|
18596
19832
|
}, [valSummary?.undervaluedThreshold, valSummary?.overvaluedThreshold, tokenPriceValue]);
|
|
18597
|
-
const chartOptions =
|
|
19833
|
+
const chartOptions = React9.useMemo(() => ({
|
|
18598
19834
|
responsive: true,
|
|
18599
19835
|
maintainAspectRatio: false,
|
|
18600
19836
|
onHover: (_event, elements) => {
|
|
@@ -18637,7 +19873,7 @@ function PropertyValuation({
|
|
|
18637
19873
|
}
|
|
18638
19874
|
}
|
|
18639
19875
|
}), []);
|
|
18640
|
-
const chartData =
|
|
19876
|
+
const chartData = React9.useMemo(() => ({
|
|
18641
19877
|
labels: musgraveComparisonData.labels,
|
|
18642
19878
|
datasets: musgraveComparisonData.datasets.map((dataset, index) => ({
|
|
18643
19879
|
label: dataset.name,
|
|
@@ -18678,8 +19914,8 @@ function PropertyValuation({
|
|
|
18678
19914
|
fill: false
|
|
18679
19915
|
}))
|
|
18680
19916
|
}), [hoveredSaleYear]);
|
|
18681
|
-
const handleMarkerEnter =
|
|
18682
|
-
const handleMarkerLeave =
|
|
19917
|
+
const handleMarkerEnter = React9.useCallback((year) => setHoveredSaleYear(year), []);
|
|
19918
|
+
const handleMarkerLeave = React9.useCallback(() => setHoveredSaleYear(null), []);
|
|
18683
19919
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
18684
19920
|
/* @__PURE__ */ jsxRuntime.jsx(ValuationHeading, { children: "Valuation Model" }),
|
|
18685
19921
|
/* @__PURE__ */ jsxRuntime.jsxs(LoafPricingSection, { children: [
|
|
@@ -19604,20 +20840,20 @@ function PropertyPhotoGallery({
|
|
|
19604
20840
|
);
|
|
19605
20841
|
}
|
|
19606
20842
|
function GalleryContent({ galleryImages, startIndex, startWithVideo, title, subtitle, onClose, videoUrl }) {
|
|
19607
|
-
const [currentIndex, setCurrentIndex] =
|
|
19608
|
-
const [activeHotspotId, setActiveHotspotId] =
|
|
19609
|
-
const [showVideo, setShowVideo] =
|
|
19610
|
-
const sliderRef =
|
|
19611
|
-
const [isDragging, setIsDragging] =
|
|
19612
|
-
const wasDraggingRef =
|
|
19613
|
-
const touchStartX =
|
|
19614
|
-
const touchStartY =
|
|
19615
|
-
const isSwiping =
|
|
19616
|
-
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(() => {
|
|
19617
20853
|
if (galleryImages.length <= 1) return 0;
|
|
19618
20854
|
return currentIndex / (galleryImages.length - 1) * 100;
|
|
19619
20855
|
}, [currentIndex, galleryImages.length]);
|
|
19620
|
-
|
|
20856
|
+
React9.useEffect(() => {
|
|
19621
20857
|
document.body.style.overflow = "hidden";
|
|
19622
20858
|
document.body.style.touchAction = "none";
|
|
19623
20859
|
return () => {
|
|
@@ -19625,7 +20861,7 @@ function GalleryContent({ galleryImages, startIndex, startWithVideo, title, subt
|
|
|
19625
20861
|
document.body.style.touchAction = "";
|
|
19626
20862
|
};
|
|
19627
20863
|
}, []);
|
|
19628
|
-
|
|
20864
|
+
React9.useEffect(() => {
|
|
19629
20865
|
const handleKey = (event) => {
|
|
19630
20866
|
if (event.key === "Escape") onClose();
|
|
19631
20867
|
if (event.key === "ArrowLeft") setCurrentIndex((i) => i === 0 ? galleryImages.length - 1 : i - 1);
|
|
@@ -20029,23 +21265,23 @@ function truncateHash(hash) {
|
|
|
20029
21265
|
}
|
|
20030
21266
|
var SWIPE_DISMISS_PX = 100;
|
|
20031
21267
|
function ToastItem({ toast, onDismiss }) {
|
|
20032
|
-
const [exiting, setExiting] =
|
|
20033
|
-
const [dragX, setDragX] =
|
|
20034
|
-
const [dragging, setDragging] =
|
|
20035
|
-
const [touched, setTouched] =
|
|
20036
|
-
const timerRef =
|
|
20037
|
-
const dragStartRef =
|
|
20038
|
-
const dragXRef =
|
|
20039
|
-
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(() => {
|
|
20040
21276
|
setExiting(true);
|
|
20041
21277
|
setTimeout(() => onDismiss(toast.id), 280);
|
|
20042
21278
|
}, [onDismiss, toast.id]);
|
|
20043
|
-
const armAutoDismiss =
|
|
21279
|
+
const armAutoDismiss = React9.useCallback(() => {
|
|
20044
21280
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
20045
21281
|
const duration2 = toast.duration ?? 6e3;
|
|
20046
21282
|
if (duration2 > 0) timerRef.current = setTimeout(dismiss, duration2);
|
|
20047
21283
|
}, [dismiss, toast.duration]);
|
|
20048
|
-
|
|
21284
|
+
React9.useEffect(() => {
|
|
20049
21285
|
armAutoDismiss();
|
|
20050
21286
|
return () => {
|
|
20051
21287
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
@@ -20125,10 +21361,10 @@ function ToastItem({ toast, onDismiss }) {
|
|
|
20125
21361
|
}
|
|
20126
21362
|
);
|
|
20127
21363
|
}
|
|
20128
|
-
var ToastContext =
|
|
21364
|
+
var ToastContext = React9.createContext(null);
|
|
20129
21365
|
function ToastProvider({ children, maxVisible }) {
|
|
20130
|
-
const [toasts, setToasts] =
|
|
20131
|
-
const addToast =
|
|
21366
|
+
const [toasts, setToasts] = React9.useState([]);
|
|
21367
|
+
const addToast = React9.useCallback((data) => {
|
|
20132
21368
|
const id = `toast-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
|
|
20133
21369
|
setToasts((prev) => {
|
|
20134
21370
|
const next = [...prev, { ...data, id }];
|
|
@@ -20136,10 +21372,10 @@ function ToastProvider({ children, maxVisible }) {
|
|
|
20136
21372
|
});
|
|
20137
21373
|
return id;
|
|
20138
21374
|
}, [maxVisible]);
|
|
20139
|
-
const update =
|
|
21375
|
+
const update = React9.useCallback((id, patch) => {
|
|
20140
21376
|
setToasts((prev) => prev.map((t) => t.id === id ? { ...t, ...patch } : t));
|
|
20141
21377
|
}, []);
|
|
20142
|
-
const dismiss =
|
|
21378
|
+
const dismiss = React9.useCallback((id) => {
|
|
20143
21379
|
setToasts((prev) => prev.filter((t) => t.id !== id));
|
|
20144
21380
|
}, []);
|
|
20145
21381
|
return /* @__PURE__ */ jsxRuntime.jsxs(ToastContext.Provider, { value: { toast: addToast, update, dismiss }, children: [
|
|
@@ -20148,15 +21384,15 @@ function ToastProvider({ children, maxVisible }) {
|
|
|
20148
21384
|
] });
|
|
20149
21385
|
}
|
|
20150
21386
|
function useToast() {
|
|
20151
|
-
const ctx =
|
|
21387
|
+
const ctx = React9.useContext(ToastContext);
|
|
20152
21388
|
if (!ctx) throw new Error("useToast must be used within a ToastProvider");
|
|
20153
21389
|
return ctx;
|
|
20154
21390
|
}
|
|
20155
21391
|
var INTERVALS = [1e3, 2e3, 4e3, 5e3, 7e3, 1e4, 12e3];
|
|
20156
21392
|
function useAdaptivePolling({ enabled, onPoll }) {
|
|
20157
|
-
const onPollRef =
|
|
21393
|
+
const onPollRef = React9.useRef(onPoll);
|
|
20158
21394
|
onPollRef.current = onPoll;
|
|
20159
|
-
|
|
21395
|
+
React9.useEffect(() => {
|
|
20160
21396
|
if (!enabled) return;
|
|
20161
21397
|
let cancelled = false;
|
|
20162
21398
|
let timeoutId = null;
|
|
@@ -20329,7 +21565,7 @@ var OpenMailButton = styled10__default.default.a`
|
|
|
20329
21565
|
}
|
|
20330
21566
|
`;
|
|
20331
21567
|
var ContactPopup = ({ onClose }) => {
|
|
20332
|
-
const [copied, setCopied] =
|
|
21568
|
+
const [copied, setCopied] = React9.useState(false);
|
|
20333
21569
|
const email = "admin@loafmarkets.com";
|
|
20334
21570
|
const handleCopy = async () => {
|
|
20335
21571
|
try {
|
|
@@ -20538,7 +21774,7 @@ var DisclaimerText = styled10__default.default.p`
|
|
|
20538
21774
|
max-width: 100%;
|
|
20539
21775
|
`;
|
|
20540
21776
|
var SiteFooter = () => {
|
|
20541
|
-
const [showContactPopup, setShowContactPopup] =
|
|
21777
|
+
const [showContactPopup, setShowContactPopup] = React9.useState(false);
|
|
20542
21778
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
20543
21779
|
/* @__PURE__ */ jsxRuntime.jsx(FooterContainer, { children: /* @__PURE__ */ jsxRuntime.jsxs(FooterContent, { children: [
|
|
20544
21780
|
/* @__PURE__ */ jsxRuntime.jsxs(FooterTop, { children: [
|
|
@@ -20627,10 +21863,10 @@ function extractSparkline(candles, maxPoints = 20) {
|
|
|
20627
21863
|
}
|
|
20628
21864
|
return points;
|
|
20629
21865
|
}
|
|
20630
|
-
var SparklineChart =
|
|
20631
|
-
const [hoverX, setHoverX] =
|
|
20632
|
-
const svgRef =
|
|
20633
|
-
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(() => {
|
|
20634
21870
|
const mn = Math.min(...data);
|
|
20635
21871
|
const mx = Math.max(...data);
|
|
20636
21872
|
const range = mx - mn || 1;
|
|
@@ -20643,7 +21879,7 @@ var SparklineChart = React5__namespace.default.memo(function SparklineChart2({ d
|
|
|
20643
21879
|
const fill = `${line} L${width},${height} L0,${height} Z`;
|
|
20644
21880
|
return { linePath: line, fillPath: fill, points: pts };
|
|
20645
21881
|
}, [data, width, height]);
|
|
20646
|
-
const interpolated =
|
|
21882
|
+
const interpolated = React9.useMemo(() => {
|
|
20647
21883
|
if (hoverX == null || points.length < 2) return null;
|
|
20648
21884
|
const stepX = width / (data.length - 1);
|
|
20649
21885
|
const rawIdx = hoverX / stepX;
|
|
@@ -20656,14 +21892,14 @@ var SparklineChart = React5__namespace.default.memo(function SparklineChart2({ d
|
|
|
20656
21892
|
price: lerp(data[i0], data[i1], t)
|
|
20657
21893
|
};
|
|
20658
21894
|
}, [hoverX, points, data, width]);
|
|
20659
|
-
const handleMouseMove =
|
|
21895
|
+
const handleMouseMove = React9.useCallback((e) => {
|
|
20660
21896
|
const svg = svgRef.current;
|
|
20661
21897
|
if (!svg) return;
|
|
20662
21898
|
const rect = svg.getBoundingClientRect();
|
|
20663
21899
|
const x = Math.max(0, Math.min(width, e.clientX - rect.left));
|
|
20664
21900
|
setHoverX(x);
|
|
20665
21901
|
}, [width]);
|
|
20666
|
-
const handleMouseLeave =
|
|
21902
|
+
const handleMouseLeave = React9.useCallback(() => setHoverX(null), []);
|
|
20667
21903
|
if (data.length < 2) return null;
|
|
20668
21904
|
const color = positive ? "#00C076" : "#FF5757";
|
|
20669
21905
|
const gradId = `spark-grad-${positive ? "up" : "dn"}`;
|
|
@@ -20781,6 +22017,7 @@ exports.PropertyPhotoGallery = PropertyPhotoGallery;
|
|
|
20781
22017
|
exports.PropertySubheader = PropertySubheader;
|
|
20782
22018
|
exports.PropertyTour = PropertyTour;
|
|
20783
22019
|
exports.PropertyValuation = PropertyValuation;
|
|
22020
|
+
exports.SentimentBar = SentimentBar;
|
|
20784
22021
|
exports.SiteFooter = SiteFooter;
|
|
20785
22022
|
exports.Skeleton = Skeleton;
|
|
20786
22023
|
exports.SlideDigit = SlideDigit;
|
|
@@ -20789,10 +22026,13 @@ exports.ToastProvider = ToastProvider;
|
|
|
20789
22026
|
exports.TradeConfirmationModal = TradeConfirmationModal;
|
|
20790
22027
|
exports.TradingSlider = TradingSlider;
|
|
20791
22028
|
exports.YourOrders = YourOrders;
|
|
22029
|
+
exports.__resetDismissedBreaking = __resetDismissedBreaking;
|
|
20792
22030
|
exports.badgeVariants = badgeVariants;
|
|
20793
22031
|
exports.buttonVariants = buttonVariants;
|
|
20794
22032
|
exports.extractSparkline = extractSparkline;
|
|
22033
|
+
exports.formatAge = formatAge;
|
|
20795
22034
|
exports.hasPendingActivity = hasPendingActivity;
|
|
22035
|
+
exports.rowAgeLabel = rowAgeLabel;
|
|
20796
22036
|
exports.useAdaptivePolling = useAdaptivePolling;
|
|
20797
22037
|
exports.useToast = useToast;
|
|
20798
22038
|
//# sourceMappingURL=index.js.map
|