refactor code

This commit is contained in:
MuslemRahimi 2024-12-09 17:44:37 +01:00
parent 249c4f0077
commit 282acde244
2 changed files with 850 additions and 886 deletions

View File

@ -0,0 +1,840 @@
<script lang="ts">
import { screenWidth } from "$lib/store";
import { abbreviateNumber } from "$lib/utils";
import VirtualList from "svelte-tiny-virtual-list";
import HoverStockChart from "$lib/components/HoverStockChart.svelte";
export let data;
export let optionsWatchlist;
export let displayedData = [];
export let filteredData = [];
export let rawData = [];
let animationClass = "";
let animationId = "";
function formatTime(timeString) {
// Split the time string into components
const [hours, minutes, seconds] = timeString?.split(":").map(Number);
// Determine AM or PM
const period = hours >= 12 ? "PM" : "AM";
// Convert hours from 24-hour to 12-hour format
const formattedHours = hours % 12 || 12; // Converts 0 to 12 for midnight
// Format the time string
const formattedTimeString = `${formattedHours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")} ${period}`;
return formattedTimeString;
}
function reformatDate(dateString) {
return (
dateString.substring(5, 7) +
"/" +
dateString.substring(8) +
"/" +
dateString.substring(2, 4)
);
}
async function addToWatchlist(itemId) {
if (data?.user?.tier === "Pro") {
try {
const postData = {
itemIdList: [itemId],
id: optionsWatchlist?.id,
};
if (optionsWatchlist?.optionsId?.includes(itemId)) {
// Remove ticker from the watchlist.
optionsWatchlist.optionsId = optionsWatchlist?.optionsId.filter(
(item) => item !== itemId,
);
} else {
// Add ticker to the watchlist.
animationId = itemId;
animationClass = "heartbeat";
const removeAnimation = () => {
animationId = "";
animationClass = "";
};
optionsWatchlist.optionsId = [...optionsWatchlist?.optionsId, itemId];
const heartbeatElement = document.getElementById(itemId);
if (heartbeatElement) {
// Only add listener if it's not already present
if (!heartbeatElement.classList.contains("animation-added")) {
heartbeatElement.addEventListener(
"animationend",
removeAnimation,
);
heartbeatElement.classList.add("animation-added"); // Prevent re-adding listener
}
}
}
const response = await fetch("/api/update-options-watchlist", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(postData),
});
optionsWatchlist.id = await response.json();
if (!response.ok) {
throw new Error("Network response was not ok");
}
} catch (error) {
console.error("An error occurred:", error);
// Handle the error appropriately (e.g., show an error message to the user)
}
} else {
toast.error("Only for Pro Members", {
style: "border-radius: 200px; background: #333; color: #fff;",
});
}
}
let sortOrders = {
time: "none",
ticker: "none",
expiry: "none",
dte: "none",
strike: "none",
callPut: "none",
sentiment: "none",
spot: "none",
price: "none",
premium: "none",
type: "none",
exec: "none",
vol: "none",
oi: "none",
};
// Generalized sorting function
function sortData(key) {
// Reset all other keys to 'none' except the current key
for (const k in sortOrders) {
if (k !== key) {
sortOrders[k] = "none";
}
}
// Cycle through 'none', 'asc', 'desc' for the clicked key
const orderCycle = ["none", "asc", "desc"];
const currentOrderIndex = orderCycle.indexOf(sortOrders[key]);
sortOrders[key] = orderCycle[(currentOrderIndex + 1) % orderCycle.length];
const sortOrder = sortOrders[key];
const originalData =
filteredData?.length !== 0 ? [...filteredData] : [...rawData];
// Reset to original data when 'none'
if (sortOrder === "none") {
displayedData = originalData;
return;
}
const compareFunctions = {
time: (a, b) => {
const timeA = new Date("1970-01-01T" + a.time).getTime();
const timeB = new Date("1970-01-01T" + b.time).getTime();
return sortOrder === "asc" ? timeA - timeB : timeB - timeA;
},
ticker: (a, b) => {
const tickerA = a.ticker.toUpperCase();
const tickerB = b.ticker.toUpperCase();
return sortOrder === "asc"
? tickerA.localeCompare(tickerB)
: tickerB.localeCompare(tickerA);
},
expiry: (a, b) => {
const timeA = new Date(a.date_expiration);
const timeB = new Date(b.date_expiration);
return sortOrder === "asc" ? timeA - timeB : timeB - timeA;
},
dte: (a, b) => {
const timeA = new Date(a.date_expiration);
const timeB = new Date(b.date_expiration);
return sortOrder === "asc" ? timeA - timeB : timeB - timeA;
},
strike: (a, b) => {
const strikeA = parseFloat(a.strike_price);
const strikeB = parseFloat(b.strike_price);
return sortOrder === "asc" ? strikeA - strikeB : strikeB - strikeA;
},
spot: (a, b) => {
const spotA = parseFloat(a.underlying_price);
const spotB = parseFloat(b.underlying_price);
return sortOrder === "asc" ? spotA - spotB : spotB - spotA;
},
price: (a, b) => {
const priceA = parseFloat(a.price);
const priceB = parseFloat(b.price);
return sortOrder === "asc" ? priceA - priceB : priceB - priceA;
},
premium: (a, b) => {
const premiumA = parseFloat(a.cost_basis);
const premiumB = parseFloat(b.cost_basis);
return sortOrder === "asc" ? premiumA - premiumB : premiumB - premiumA;
},
size: (a, b) => {
const volA = parseFloat(a?.size);
const volB = parseFloat(b?.size);
return sortOrder === "asc" ? volA - volB : volB - volA;
},
vol: (a, b) => {
const volA = parseFloat(a.volume);
const volB = parseFloat(b.volume);
return sortOrder === "asc" ? volA - volB : volB - volA;
},
oi: (a, b) => {
const oiA = parseFloat(a.open_interest);
const oiB = parseFloat(b.open_interest);
return sortOrder === "asc" ? oiA - oiB : oiB - oiA;
},
callPut: (a, b) => {
const callPutA = a.put_call?.toUpperCase();
const callPutB = b.put_call?.toUpperCase();
return sortOrder === "asc"
? callPutA.localeCompare(callPutB)
: callPutB.localeCompare(callPutA);
},
sentiment: (a, b) => {
const sentimentOrder = { BULLISH: 1, NEUTRAL: 2, BEARISH: 3 };
const sentimentA = sentimentOrder[a?.sentiment?.toUpperCase()] || 4;
const sentimentB = sentimentOrder[b?.sentiment?.toUpperCase()] || 4;
return sortOrder === "asc"
? sentimentA - sentimentB
: sentimentB - sentimentA;
},
type: (a, b) => {
const typeOrder = { SWEEP: 1, TRADE: 2 };
const typeA = typeOrder[a.option_activity_type?.toUpperCase()] || 3;
const typeB = typeOrder[b.option_activity_type?.toUpperCase()] || 3;
return sortOrder === "asc" ? typeA - typeB : typeB - typeA;
},
exec: (a, b) => {
const tickerA = a?.execution_estimate?.toUpperCase();
const tickerB = b?.execution_estimate?.toUpperCase();
return sortOrder === "asc"
? tickerA.localeCompare(tickerB)
: tickerB.localeCompare(tickerA);
},
};
// Sort using the appropriate comparison function
displayedData = originalData.sort(compareFunctions[key]);
}
</script>
<div class="table-container">
<div class="table">
<VirtualList
width="100%"
height={$screenWidth < 640 ? 550 : 850}
itemCount={displayedData.length}
itemSize={40}
>
<div slot="header" class="tr th sticky z-40 top-0">
<!-- Table headers -->
<div
on:click={() => sortData("time")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Time
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['time'] ===
'asc'
? 'rotate-180'
: ''} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("ticker")}
class="td cursor-pointer select-none bg-[#1E222D] font-bold text-slate-300 text-xs text-start uppercase"
>
Symbol
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['ticker'] ===
'asc'
? 'rotate-180'
: sortOrders['ticker'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
class="td select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Save
</div>
<div
on:click={() => sortData("expiry")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Expiry
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['expiry'] ===
'asc'
? 'rotate-180'
: sortOrders['expiry'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("dte")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
DTE
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['dte'] ===
'asc'
? 'rotate-180'
: sortOrders['dte'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("strike")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Strike
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['strike'] ===
'asc'
? 'rotate-180'
: sortOrders['strike'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("callPut")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
C/P
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['callPut'] ===
'asc'
? 'rotate-180'
: sortOrders['callPut'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("sentiment")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Sent.
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders[
'sentiment'
] === 'asc'
? 'rotate-180'
: sortOrders['sentiment'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("spot")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Spot
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['spot'] ===
'asc'
? 'rotate-180'
: sortOrders['spot'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("price")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Price
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['price'] ===
'asc'
? 'rotate-180'
: sortOrders['price'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("premium")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Prem
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['premium'] ===
'asc'
? 'rotate-180'
: sortOrders['premium'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("type")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Type
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['type'] ===
'asc'
? 'rotate-180'
: sortOrders['type'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("exec")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Exec
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['exec'] ===
'asc'
? 'rotate-180'
: sortOrders['exec'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("size")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Size
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['size'] ===
'asc'
? 'rotate-180'
: sortOrders['size'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("vol")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Vol
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['vol'] ===
'asc'
? 'rotate-180'
: sortOrders['vol'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("oi")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
OI
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['oi'] ===
'asc'
? 'rotate-180'
: sortOrders['oi'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
</div>
<div
slot="item"
let:index
let:style
{style}
class="tr {index % 2 === 0 ? 'bg-secondary' : 'bg-[#09090B]'}"
>
<!-- Row data -->
<div
style="justify-content: center;"
class="td text-white text-xs sm:text-sm text-start m-auto whitespace-nowrap"
>
{formatTime(displayedData[index]?.time)}
</div>
<div
on:click|stopPropagation
style="justify-content: center;"
class="td text-sm sm:hover:text-white sm:text-[1rem] text-blue-400 font-normal"
>
<HoverStockChart
symbol={displayedData[index]?.ticker}
assetType={displayedData[index]?.underlying_type}
/>
</div>
<div
id={displayedData[index]?.id}
on:click|stopPropagation={() =>
addToWatchlist(displayedData[index]?.id)}
style="justify-content: center;"
class="td {optionsWatchlist.optionsId?.includes(
displayedData[index]?.id,
)
? 'text-[#FBCE3C]'
: 'text-white'}"
>
<svg
class="{displayedData[index]?.id === animationId
? animationClass
: ''} w-4 sm:w-5 sm:h-5 inline-block cursor-pointer flex-shrink-0"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
><path
fill="currentColor"
d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327l4.898.696c.441.062.612.636.282.95l-3.522 3.356l.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"
/></svg
>
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] text-white text-start"
>
{reformatDate(displayedData[index]?.date_expiration)}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] text-white text-start"
>
{displayedData[index]?.dte < 0
? "expired"
: displayedData[index]?.dte + "d"}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] text-white text-start"
>
{displayedData[index]?.strike_price}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] {displayedData[index]?.put_call ===
'Calls'
? 'text-[#00FC50]'
: 'text-[#FF2F1F]'} text-start"
>
{displayedData[index]?.put_call}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] {displayedData[index]?.sentiment ===
'Bullish'
? 'text-[#00FC50]'
: displayedData[index]?.sentiment === 'Bearish'
? 'text-[#FF2F1F]'
: 'text-[#C6A755]'} text-start"
>
{displayedData[index]?.sentiment}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] text-start text-white"
>
{displayedData[index]?.underlying_price}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] text-start text-white"
>
{displayedData[index]?.price}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] text-start font-semibold {displayedData[
index
]?.put_call === 'Puts'
? 'text-[#CB281C]'
: 'text-[#0FB307]'}"
>
{abbreviateNumber(displayedData[index]?.cost_basis)}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] text-start {displayedData[index]
?.option_activity_type === 'Sweep'
? 'text-[#C6A755]'
: 'text-[#976DB7]'}"
>
{displayedData[index]?.option_activity_type}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] text-start text-[#C6A755]"
>
{displayedData[index]?.execution_estimate?.replace(
"At Midpoint",
"Midpoint",
)}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] text-white text-end"
>
{new Intl.NumberFormat("en", {
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(displayedData[index]?.size)}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] text-white text-end"
>
{new Intl.NumberFormat("en", {
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(displayedData[index]?.volume)}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] text-white text-end"
>
{new Intl.NumberFormat("en", {
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(displayedData[index]?.open_interest)}
</div>
</div>
</VirtualList>
</div>
</div>
<style>
.table-container {
width: 100%;
overflow-x: auto;
}
.table :global(.virtual-list-inner) {
width: 100%;
display: flex;
flex-direction: column;
}
@media (max-width: 768px) {
.table {
width: 1000px;
}
}
.table .virtual-list-inner {
flex-flow: column nowrap;
font-size: 0.8rem;
line-height: 1.5;
flex: 1 1 auto;
}
.th {
display: none;
font-weight: 700;
background-color: #09090b;
}
.th > .td {
white-space: nowrap;
justify-content: center;
}
.tr {
width: 100%;
display: flex;
flex-flow: row nowrap;
}
.tr.even {
background-color: #2a2e39;
}
.tr.odd {
background-color: #09090b;
}
.td {
display: flex;
flex-flow: row nowrap;
flex-grow: 1;
flex-basis: 0;
padding: 0.5em;
word-break: break-word;
overflow: hidden;
text-overflow: ellipsis;
min-width: 0px;
white-space: nowrap;
}
.heartbeat {
animation: heartbeat-animation 0.3s;
animation-timing-function: ease-in-out;
}
@keyframes heartbeat-animation {
0% {
transform: rotate(0deg) scale(0.95);
}
25% {
transform: rotate(10deg) scale(1.05);
}
50% {
transform: rotate(0deg) scale(1.2);
}
75% {
transform: rotate(-10deg) scale(1.05);
}
100% {
transform: rotate(0deg) scale(0.95);
}
}
</style>

View File

@ -11,8 +11,8 @@
import { Button } from "$lib/components/shadcn/button/index.js";
import { Calendar } from "$lib/components/shadcn/calendar/index.js";
import CalendarIcon from "lucide-svelte/icons/calendar";
import HoverStockChart from "$lib/components/HoverStockChart.svelte";
import VirtualList from "svelte-tiny-virtual-list";
import OptionsFlowTable from "$lib/components/Table/OptionsFlowTable.svelte";
import { writable } from "svelte/store";
export let data;
@ -24,8 +24,7 @@
let displayRules = [];
let filteredData = [];
let filterQuery = "";
let animationClass = "";
let animationId = "";
let socket: WebSocket | null = null; // Initialize socket as null
let syncWorker: Worker | undefined;
@ -504,52 +503,6 @@
}
}
function formatTime(timeString) {
// Split the time string into components
const [hours, minutes, seconds] = timeString.split(":").map(Number);
// Determine AM or PM
const period = hours >= 12 ? "PM" : "AM";
// Convert hours from 24-hour to 12-hour format
const formattedHours = hours % 12 || 12; // Converts 0 to 12 for midnight
// Format the time string
const formattedTimeString = `${formattedHours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")} ${period}`;
return formattedTimeString;
}
function handleViewData(optionData) {
//optionStart = optionData['Start Date'] === null ? 'n/a' : new Date(optionData['Start Date'])?.toLocaleString('en-US', { month: 'short', day: 'numeric', year: 'numeric', daySuffix: '2-digit' });
optionSymbol = optionData?.option_symbol;
optionDescription = optionData?.description;
optionPremium = abbreviateNumber(optionData?.cost_basis, true);
optionExpiry = reformatDate(optionData?.date_expiration);
optionContract = optionData?.put_call;
optionType = optionData?.option_activity_type;
optionStrike = optionData?.strike_price;
optionVolume = new Intl.NumberFormat("en", {
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(optionData?.volume);
optionSpot = optionData?.underlying_price;
optionOpenInterest = new Intl.NumberFormat("en", {
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(optionData?.open_interest);
optionSentiment = optionData?.sentiment;
optionPrice = optionData?.price;
//optionTradeCount = optionData?.tradeCount;
optionexecution_estimate = optionData?.execution_estimate;
//optionExchange = optionData?.exchange;
const openPopup =
$screenWidth < 640
? document.getElementById("optionDetailsMobileModal")
: document.getElementById("optionDetailsDesktopModal");
openPopup?.dispatchEvent(new MouseEvent("click"));
}
/*
function sendMessage(message) {
if (socket && socket.readyState === WebSocket.OPEN) {
@ -925,139 +878,6 @@ function sendMessage(message) {
}
}
}
let sortOrders = {
time: "none",
ticker: "none",
expiry: "none",
dte: "none",
strike: "none",
callPut: "none",
sentiment: "none",
spot: "none",
price: "none",
premium: "none",
type: "none",
exec: "none",
vol: "none",
oi: "none",
};
// Generalized sorting function
function sortData(key) {
// Reset all other keys to 'none' except the current key
for (const k in sortOrders) {
if (k !== key) {
sortOrders[k] = "none";
}
}
// Cycle through 'none', 'asc', 'desc' for the clicked key
const orderCycle = ["none", "asc", "desc"];
const currentOrderIndex = orderCycle.indexOf(sortOrders[key]);
sortOrders[key] = orderCycle[(currentOrderIndex + 1) % orderCycle.length];
const sortOrder = sortOrders[key];
const originalData =
filteredData?.length !== 0 ? [...filteredData] : [...rawData];
// Reset to original data when 'none'
if (sortOrder === "none") {
displayedData = originalData;
return;
}
const compareFunctions = {
time: (a, b) => {
const timeA = new Date("1970-01-01T" + a.time).getTime();
const timeB = new Date("1970-01-01T" + b.time).getTime();
return sortOrder === "asc" ? timeA - timeB : timeB - timeA;
},
ticker: (a, b) => {
const tickerA = a.ticker.toUpperCase();
const tickerB = b.ticker.toUpperCase();
return sortOrder === "asc"
? tickerA.localeCompare(tickerB)
: tickerB.localeCompare(tickerA);
},
expiry: (a, b) => {
const timeA = new Date(a.date_expiration);
const timeB = new Date(b.date_expiration);
return sortOrder === "asc" ? timeA - timeB : timeB - timeA;
},
dte: (a, b) => {
const timeA = new Date(a.date_expiration);
const timeB = new Date(b.date_expiration);
return sortOrder === "asc" ? timeA - timeB : timeB - timeA;
},
strike: (a, b) => {
const strikeA = parseFloat(a.strike_price);
const strikeB = parseFloat(b.strike_price);
return sortOrder === "asc" ? strikeA - strikeB : strikeB - strikeA;
},
spot: (a, b) => {
const spotA = parseFloat(a.underlying_price);
const spotB = parseFloat(b.underlying_price);
return sortOrder === "asc" ? spotA - spotB : spotB - spotA;
},
price: (a, b) => {
const priceA = parseFloat(a.price);
const priceB = parseFloat(b.price);
return sortOrder === "asc" ? priceA - priceB : priceB - priceA;
},
premium: (a, b) => {
const premiumA = parseFloat(a.cost_basis);
const premiumB = parseFloat(b.cost_basis);
return sortOrder === "asc" ? premiumA - premiumB : premiumB - premiumA;
},
size: (a, b) => {
const volA = parseFloat(a?.size);
const volB = parseFloat(b?.size);
return sortOrder === "asc" ? volA - volB : volB - volA;
},
vol: (a, b) => {
const volA = parseFloat(a.volume);
const volB = parseFloat(b.volume);
return sortOrder === "asc" ? volA - volB : volB - volA;
},
oi: (a, b) => {
const oiA = parseFloat(a.open_interest);
const oiB = parseFloat(b.open_interest);
return sortOrder === "asc" ? oiA - oiB : oiB - oiA;
},
callPut: (a, b) => {
const callPutA = a.put_call?.toUpperCase();
const callPutB = b.put_call?.toUpperCase();
return sortOrder === "asc"
? callPutA.localeCompare(callPutB)
: callPutB.localeCompare(callPutA);
},
sentiment: (a, b) => {
const sentimentOrder = { BULLISH: 1, NEUTRAL: 2, BEARISH: 3 };
const sentimentA = sentimentOrder[a?.sentiment?.toUpperCase()] || 4;
const sentimentB = sentimentOrder[b?.sentiment?.toUpperCase()] || 4;
return sortOrder === "asc"
? sentimentA - sentimentB
: sentimentB - sentimentA;
},
type: (a, b) => {
const typeOrder = { SWEEP: 1, TRADE: 2 };
const typeA = typeOrder[a.option_activity_type?.toUpperCase()] || 3;
const typeB = typeOrder[b.option_activity_type?.toUpperCase()] || 3;
return sortOrder === "asc" ? typeA - typeB : typeB - typeA;
},
exec: (a, b) => {
const tickerA = a?.execution_estimate?.toUpperCase();
const tickerB = b?.execution_estimate?.toUpperCase();
return sortOrder === "asc"
? tickerA.localeCompare(tickerB)
: tickerB.localeCompare(tickerA);
},
};
// Sort using the appropriate comparison function
displayedData = originalData.sort(compareFunctions[key]);
}
</script>
<svelte:head>
@ -1843,543 +1663,13 @@ function sendMessage(message) {
<div class="flex w-full m-auto h-full overflow-hidden">
{#if displayedData?.length !== 0}
<div class="mt-8 w-full overflow-x-auto h-[850px] overflow-hidden">
<div class="table-container">
<div class="table">
<VirtualList
width="100%"
height={$screenWidth < 640 ? 550 : 850}
itemCount={displayedData.length}
itemSize={40}
>
<div slot="header" class="tr th sticky z-40 top-0">
<!-- Table headers -->
<div
on:click={() => sortData("time")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Time
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders[
'time'
] === 'asc'
? 'rotate-180'
: ''} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("ticker")}
class="td cursor-pointer select-none bg-[#1E222D] font-bold text-slate-300 text-xs text-start uppercase"
>
Symbol
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders[
'ticker'
] === 'asc'
? 'rotate-180'
: sortOrders['ticker'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
class="td select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Save
</div>
<div
on:click={() => sortData("expiry")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Expiry
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders[
'expiry'
] === 'asc'
? 'rotate-180'
: sortOrders['expiry'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("dte")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
DTE
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders[
'dte'
] === 'asc'
? 'rotate-180'
: sortOrders['dte'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("strike")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Strike
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders[
'strike'
] === 'asc'
? 'rotate-180'
: sortOrders['strike'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("callPut")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
C/P
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders[
'callPut'
] === 'asc'
? 'rotate-180'
: sortOrders['callPut'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("sentiment")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Sent.
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders[
'sentiment'
] === 'asc'
? 'rotate-180'
: sortOrders['sentiment'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("spot")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Spot
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders[
'spot'
] === 'asc'
? 'rotate-180'
: sortOrders['spot'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("price")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Price
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders[
'price'
] === 'asc'
? 'rotate-180'
: sortOrders['price'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("premium")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Prem
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders[
'premium'
] === 'asc'
? 'rotate-180'
: sortOrders['premium'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("type")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Type
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders[
'type'
] === 'asc'
? 'rotate-180'
: sortOrders['type'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("exec")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Exec
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders[
'exec'
] === 'asc'
? 'rotate-180'
: sortOrders['exec'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("size")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Size
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders[
'size'
] === 'asc'
? 'rotate-180'
: sortOrders['size'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("vol")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
Vol
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders[
'vol'
] === 'asc'
? 'rotate-180'
: sortOrders['vol'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
<div
on:click={() => sortData("oi")}
class="td cursor-pointer select-none bg-[#1E222D] text-slate-300 font-bold text-xs text-start uppercase"
>
OI
<svg
class="flex-shrink-0 w-4 h-4 inline-block {sortOrders[
'oi'
] === 'asc'
? 'rotate-180'
: sortOrders['oi'] === 'desc'
? ''
: 'hidden'} "
viewBox="0 0 20 20"
fill="currentColor"
style="max-width:50px"
><path
fill-rule="evenodd"
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
clip-rule="evenodd"
></path></svg
>
</div>
</div>
<div
on:click={() => handleViewData(displayedData[index])}
slot="item"
let:index
let:style
{style}
class="tr cursor-pointer {index % 2 === 0
? 'bg-[#2A2E39]'
: 'bg-[#09090B]'}"
>
<!-- Row data -->
<div
style="justify-content: center;"
class="td text-white text-xs sm:text-sm text-start m-auto whitespace-nowrap"
>
{formatTime(displayedData[index]?.time)}
</div>
<div
on:click|stopPropagation
style="justify-content: center;"
class="td text-sm sm:hover:text-white sm:text-[1rem] text-blue-400 font-normal"
>
<HoverStockChart
symbol={displayedData[index]?.ticker}
assetType={displayedData[index]?.underlying_type}
/>
</div>
<div
id={displayedData[index]?.id}
on:click|stopPropagation={() =>
addToWatchlist(displayedData[index]?.id)}
style="justify-content: center;"
class="td {optionsWatchlist.optionsId?.includes(
displayedData[index]?.id,
)
? 'text-[#FBCE3C]'
: 'text-white'}"
>
<svg
class="{displayedData[index]?.id === animationId
? animationClass
: ''} w-4 sm:w-5 sm:h-5 inline-block cursor-pointer flex-shrink-0"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 16 16"
><path
fill="currentColor"
d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327l4.898.696c.441.062.612.636.282.95l-3.522 3.356l.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"
/></svg
>
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] text-white text-start"
>
{reformatDate(displayedData[index]?.date_expiration)}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] text-white text-start"
>
{displayedData[index]?.dte < 0
? "expired"
: displayedData[index]?.dte + "d"}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] text-white text-start"
>
{displayedData[index]?.strike_price}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] {displayedData[index]
?.put_call === 'Calls'
? 'text-[#00FC50]'
: 'text-[#FF2F1F]'} text-start"
>
{displayedData[index]?.put_call}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] {displayedData[index]
?.sentiment === 'Bullish'
? 'text-[#00FC50]'
: displayedData[index]?.sentiment === 'Bearish'
? 'text-[#FF2F1F]'
: 'text-[#C6A755]'} text-start"
>
{displayedData[index]?.sentiment}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] text-start text-white"
>
{displayedData[index]?.underlying_price}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] text-start text-white"
>
{displayedData[index]?.price}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] text-start font-semibold {displayedData[
index
]?.put_call === 'Puts'
? 'text-[#CB281C]'
: 'text-[#0FB307]'}"
>
{abbreviateNumber(displayedData[index]?.cost_basis)}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] text-start {displayedData[
index
]?.option_activity_type === 'Sweep'
? 'text-[#C6A755]'
: 'text-[#976DB7]'}"
>
{displayedData[index]?.option_activity_type}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] text-start text-[#C6A755]"
>
{displayedData[index]?.execution_estimate?.replace(
"At Midpoint",
"Midpoint",
)}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] text-white text-end"
>
{new Intl.NumberFormat("en", {
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(displayedData[index]?.size)}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] text-white text-end"
>
{new Intl.NumberFormat("en", {
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(displayedData[index]?.volume)}
</div>
<div
style="justify-content: center;"
class="td text-sm sm:text-[1rem] text-white text-end"
>
{new Intl.NumberFormat("en", {
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(displayedData[index]?.open_interest)}
</div>
</div>
</VirtualList>
</div>
</div>
<OptionsFlowTable
{data}
{optionsWatchlist}
{displayedData}
{filteredData}
{rawData}
/>
</div>
{:else}
<div
@ -2577,169 +1867,3 @@ function sendMessage(message) {
<!--End Choose Rule Modal-->
<!--Start Options Detail Desktop Modal-->
<!-- Put this part before </body> tag -->
<input type="checkbox" id="optionDetailsDesktopModal" class="modal-toggle" />
<label for="optionDetailsDesktopModal" class="modal p-2 sm:p-0">
<label
for="optionDetailsDesktopModal"
class="cursor-pointer modal-backdrop bg-[#000] bg-opacity-[0.8]"
></label>
<!-- svelte-ignore a11y-label-has-associated-control -->
<label
class="modal-box rounded w-full relative bg-primary h-auto max-h-[900px] border border-gray-800 overflow-y-scroll"
>
<label
for="optionDetailsDesktopModal"
class="cursor-pointer absolute right-5 top-2 bg-primary text-2xl text-white"
>
</label>
<p class="text-gray-200 mt-10">
<span class="text-white text-xl font-semibold">Order Details:</span>
<br />
{optionSymbol}
</p>
<p class="py-4 text-gray-200 bg-primary w-full">
<span class="font-semibold text-white">Description:</span>
<br />
{optionDescription}
</p>
<table
class="table table-sm table-compact bg-primary w-full mt-5 mb-10 text-white"
>
<tbody>
<!-- row 1 -->
<tr class="border-b border-slate-700 odd:bg-secondary">
<td class="font-semibold">Premium</td>
<td class="">{optionPremium}</td>
<td class="font-semibold">C/P</td>
<td class="">{optionContract}</td>
</tr>
<tr class="border-b border-slate-700 odd:bg-secondary">
<td class="font-semibold">Expiry</td>
<td class="">{optionExpiry}</td>
<td class="font-semibold">Type</td>
<td class="">{optionType}</td>
</tr>
<tr class="border-b border-slate-700 odd:bg-secondary">
<td class="font-semibold">Strike</td>
<td class="">${optionStrike}</td>
<td class="font-semibold">Volume</td>
<td class="">{optionVolume}</td>
</tr>
<tr class="border-b border-slate-700 odd:bg-secondary">
<td class="font-semibold">Spot</td>
<td class="">${optionSpot}</td>
<td class="font-semibold">Open Interest</td>
<td class="">{optionOpenInterest}</td>
</tr>
<tr class="border-b border-slate-700 odd:bg-secondary">
<td class="font-semibold">Price</td>
<td class="">${optionPrice}</td>
<td class="font-semibold">Sentiment</td>
<td class="">{optionSentiment}</td>
</tr>
<tr class="border-b border-slate-700 odd:bg-secondary">
<td class="font-semibold">Execution Estimate</td>
<td class="">{optionexecution_estimate}</td>
<td class="font-semibold"></td>
<td class=""></td>
</tr>
</tbody>
</table>
</label>
</label>
<!--End Options Detial Desktop Modal-->
<style>
.table-container {
width: 100%;
overflow-x: auto;
}
.table :global(.virtual-list-inner) {
width: 100%;
display: flex;
flex-direction: column;
}
@media (max-width: 768px) {
.table {
width: 1000px;
}
}
.table .virtual-list-inner {
flex-flow: column nowrap;
font-size: 0.8rem;
line-height: 1.5;
flex: 1 1 auto;
}
.th {
display: none;
font-weight: 700;
background-color: #09090b;
}
.th > .td {
white-space: nowrap;
justify-content: center;
}
.tr {
width: 100%;
display: flex;
flex-flow: row nowrap;
}
.tr.even {
background-color: #2a2e39;
}
.tr.odd {
background-color: #09090b;
}
.td {
display: flex;
flex-flow: row nowrap;
flex-grow: 1;
flex-basis: 0;
padding: 0.5em;
word-break: break-word;
overflow: hidden;
text-overflow: ellipsis;
min-width: 0px;
white-space: nowrap;
}
.heartbeat {
animation: heartbeat-animation 0.3s;
animation-timing-function: ease-in-out;
}
@keyframes heartbeat-animation {
0% {
transform: rotate(0deg) scale(0.95);
}
25% {
transform: rotate(10deg) scale(1.05);
}
50% {
transform: rotate(0deg) scale(1.2);
}
75% {
transform: rotate(-10deg) scale(1.05);
}
100% {
transform: rotate(0deg) scale(0.95);
}
}
</style>