add column sorting to market mover

This commit is contained in:
MuslemRahimi 2024-10-12 12:51:51 +02:00
parent 62d3f6a021
commit 1304059f11

View File

@ -5,13 +5,12 @@ import logo from '$lib/images/top_winner_logo.png';
import { abbreviateNumber } from '$lib/utils'; import { abbreviateNumber } from '$lib/utils';
import MiniPlot from '$lib/components/MiniPlot.svelte'; import MiniPlot from '$lib/components/MiniPlot.svelte';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import * as Tabs from "$lib/components/shadcn/tabs/index.js";
import ArrowLogo from "lucide-svelte/icons/move-up-right"; import ArrowLogo from "lucide-svelte/icons/move-up-right";
export let data; export let data;
let isLoaded = false; let isLoaded = false;
const rawData = data?.getMiniPlotsIndex; const rawData = data?.getMiniPlotsIndex;
let timePeriod = '1D'
let priceDataSP500; let priceDataSP500;
let priceDataNasdaq; let priceDataNasdaq;
let priceDataDowJones; let priceDataDowJones;
@ -81,109 +80,51 @@ rawData?.forEach(({ symbol, priceData, changesPercentage, previousClose }) => {
let outputList = data?.getDailyGainerLoserActive; let outputList = data?.getDailyGainerLoserActive;
let gainerLoserActive = outputList?.gainers['1D'] let gainerLoserActive = outputList?.gainers[timePeriod]
let displaySection = 'gainer'
let order = 'highToLow';
let sortBy = ''; // Default sorting by change percentage
let buttonText = 'Top Winners'; let buttonText = 'Top Winners';
function changeOrder(state:string) { const tabs = [
if (state === 'highToLow')
{ {
order = 'lowToHigh'; title: "Gainers",
} },
else {
order = 'highToLow';
}
}
const sortByChange = (tickerList) => {
return tickerList?.sort(function(a, b) {
if(order === 'highToLow')
{ {
return b?.changesPercentage - a?.changesPercentage; title: "Losers",
} },
else {
return a?.changesPercentage - b?.changesPercentage;
}
});
}
const sortByPrice = (tickerList) => {
return tickerList?.sort(function(a, b) {
if(order === 'highToLow')
{ {
return a?.price - b?.price; title: "Active",
} },
else { ];
return b?.price - a?.price;
}
}); let activeIdx = 0;
}
const sortByVolume = (tickerList) => { function changeSection(index) {
return tickerList?.sort(function(a, b) { activeIdx = index;
if(order === 'highToLow')
{
return b?.volume - a?.volume;
}
else {
return a?.volume - b?.volume;
}
}); if (index === 0)
}
const sortByMarketCap = (tickerList) => {
return tickerList?.sort(function(a, b) {
if(order === 'highToLow')
{
return b?.marketCap - a?.marketCap;
}
else {
return a?.marketCap - b?.marketCap;
}
});
}
function changeSection(state) {
displaySection = state;
const timePeriod = '1D';
sortBy = '';
order = '';
if (state === 'gainer')
{ {
gainerLoserActive = outputList?.gainers[timePeriod]; gainerLoserActive = outputList?.gainers[timePeriod];
buttonText = 'Top Winners' buttonText = 'Top Winners'
} }
else if (state === 'loser') else if (index === 1)
{ {
gainerLoserActive = outputList?.losers[timePeriod]; gainerLoserActive = outputList?.losers[timePeriod];
buttonText = 'Top Losers' buttonText = 'Top Losers'
} }
else if (state === 'active') else if (index === 2)
{ {
gainerLoserActive = outputList?.active[timePeriod]; gainerLoserActive = outputList?.active[timePeriod];
buttonText = 'Most Active' buttonText = 'Most Active'
} }
} }
function selectTimeInterval(event) { function selectTimeInterval(event) {
sortBy = '';
order = '';
const timePeriod = event.target.value === 'oneDay' ? '1D' : event.target.value === 'oneWeek' ? '1W' : event.target.value === 'oneMonth' ? '1M' : event.target.value === 'threeMonths' ? '3M' : '6M';
timePeriod = event.target.value === 'oneDay' ? '1D' : event.target.value === 'oneWeek' ? '1W' : event.target.value === 'oneMonth' ? '1M' : event.target.value === 'threeMonths' ? '3M' : '6M';
if (buttonText === 'Top Winners') if (buttonText === 'Top Winners')
@ -198,31 +139,95 @@ function selectTimeInterval(event) {
{ {
gainerLoserActive = outputList?.active[timePeriod]; gainerLoserActive = outputList?.active[timePeriod];
} }
} }
onMount( () => { onMount( () => {
isLoaded = true; isLoaded = true;
}) })
$: {
if(order)
{
// Add this condition for market cap sorting let sortOrders = {
if (sortBy === 'marketCap') { symbol: 'none',
gainerLoserActive = sortByMarketCap(gainerLoserActive); name: 'none',
} change: 'none',
else if (sortBy === 'change') { price: 'none',
gainerLoserActive = sortByChange(gainerLoserActive); marketCap: 'none',
} volume: 'none',
else if (sortBy === 'price') { };
gainerLoserActive = sortByPrice(gainerLoserActive);
} // Generalized sorting function
else if (sortBy === 'volume') { function sortData(key) {
gainerLoserActive = sortByVolume(gainerLoserActive); // 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'];
let originalData = [];
if (buttonText === 'Top Winners')
{
originalData = data?.getDailyGainerLoserActive?.gainers[timePeriod]
}
else if (buttonText === 'Top Losers')
{
originalData = data?.getDailyGainerLoserActive?.losers[timePeriod]
}
else if (buttonText === 'Most Active')
{
originalData = data?.getDailyGainerLoserActive?.active[timePeriod]
}
const currentOrderIndex = orderCycle.indexOf(sortOrders[key]);
sortOrders[key] = orderCycle[(currentOrderIndex + 1) % orderCycle.length];
const sortOrder = sortOrders[key];
// Reset to original data when 'none' and stop further sorting
if (sortOrder === 'none') {
gainerLoserActive = [...originalData]; // Reset to original data (spread to avoid mutation)
return;
}
// Define comparison functions for each key
const compareFunctions = {
symbol: (a, b) => {
const symbolA = a.symbol.toUpperCase();
const symbolB = b.symbol.toUpperCase();
return sortOrder === 'asc' ? symbolA.localeCompare(symbolB) : symbolB.localeCompare(symbolA);
},
name: (a, b) => {
const nameA = a.name.toUpperCase();
const nameB = b.name.toUpperCase();
return sortOrder === 'asc' ? nameA.localeCompare(nameB) : nameB.localeCompare(nameA);
},
change: (a, b) => {
const numA = parseFloat(a?.changesPercentage);
const numB = parseFloat(b?.changesPercentage);
return sortOrder === 'asc' ? numA - numB : numB - numA;
},
price: (a, b) => {
const numA = parseFloat(a?.price);
const numB = parseFloat(b?.price);
return sortOrder === 'asc' ? numA - numB : numB - numA;
},
marketCap: (a, b) => {
const numA = parseFloat(a.marketCap);
const numB = parseFloat(b.marketCap);
return sortOrder === 'asc' ? numA - numB : numB - numA;
},
volume: (a, b) => {
const numA = parseFloat(a.volume);
const numB = parseFloat(b.volume);
return sortOrder === 'asc' ? numA - numB : numB - numA;
},
};
// Sort using the appropriate comparison function
gainerLoserActive = [...originalData].sort(compareFunctions[key]);
} }
@ -319,7 +324,7 @@ $: charNumber = $screenWidth < 640 ? 20 : 30;
{#if isLoaded} {#if isLoaded}
<div class="text-white text-xs sm:text-sm pb-5 sm:pb-2 pl-3 sm:pl-0"> <div class="sm:hidden text-white text-xs sm:text-sm pb-5 sm:pb-2 pl-3 sm:pl-0">
Stock Indexes - {getCurrentDateFormatted()} Stock Indexes - {getCurrentDateFormatted()}
</div> </div>
@ -333,20 +338,31 @@ $: charNumber = $screenWidth < 640 ? 20 : 30;
</div> </div>
<div class="w-full m-auto mb-10 bg-[#09090B] pl-3 pr-3 sm:pl-0 sm:pr-0"> <div class="w-full m-auto mb-10 sm:mb-5 bg-[#09090B] pl-3 pr-3 sm:pl-0 sm:pr-0">
<div class="tabs flex flex-row justify-between sm:justify-start items-center w-full pl-3 pr-3 sm:pl-0 sm:pr-0"> <div class="bg-[#313131] w-fit relative m-auto sm:m-0 sm:mr-auto flex sm:flex-wrap items-center justify-center rounded-lg p-1 -mt-3">
<Tabs.Root value="gainers" class="w-[400px]"> {#each tabs as item, i}
<Tabs.List class="grid w-full grid-cols-3 bg-[#27272A]"> <label
<Tabs.Trigger on:click={() => (changeSection('gainer'))} value="gainers">Gainers</Tabs.Trigger> on:click={() => (changeSection(i))}
<Tabs.Trigger on:click={() => (changeSection('loser'))} value="losers">Losers</Tabs.Trigger> class="cursor-pointer group relative z-[1] rounded-full px-6 py-1 {activeIdx === i
<Tabs.Trigger on:click={() => (changeSection('active'))} value="active">Active</Tabs.Trigger> ? 'z-0'
</Tabs.List> : ''} "
</Tabs.Root> >
{#if activeIdx === i}
<div
class="absolute inset-0 rounded-lg bg-purple-600"
></div>
{/if}
<span class="relative text-[1rem] sm:text-lg block font-semibold duration-200 text-white">
{item.title}
</span>
</label>
{/each}
</div> </div>
</div> </div>
@ -377,29 +393,35 @@ $: charNumber = $screenWidth < 640 ? 20 : 30;
<table class="table table-sm table-compact rounded-none sm:rounded-md w-full bg-[#09090B] border-bg-[#09090B]"> <table class="table table-sm table-compact rounded-none sm:rounded-md w-full bg-[#09090B] border-bg-[#09090B]">
<thead> <thead>
<tr class="border-b border-[#27272A]"> <tr class="border-b border-[#27272A]">
<th class="text-white font-semibold text-[1rem] whitespace-nowrap">Symbol</th> <th on:click={() => sortData('symbol')} class="cursor-pointer select-none text-white font-semibold text-[1rem] whitespace-nowrap">
<th class="text-white font-semibold text-[1rem] whitespace-nowrap">Name</th> Symbol
<th on:click={() => { sortBy = 'change'; changeOrder(order); }} class="whitespace-nowrap cursor-pointer text-white font-semibold text-[1rem] text-end"> <svg class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['symbol'] === 'asc' ? 'rotate-180' : sortOrders['symbol'] === '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>
</th>
<th on:click={() => sortData('name')} class="cursor-pointer select-none text-white font-semibold text-[1rem] whitespace-nowrap">
Name
<svg class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['name'] === 'asc' ? 'rotate-180' : sortOrders['name'] === '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>
</th>
<th on:click={() => sortData('change')} class="cursor-pointer select-none text-end text-white font-semibold text-[1rem] whitespace-nowrap">
% Change % Change
<svg class="w-5 h-5 inline-block {order === 'highToLow' && sortBy === 'change' ? 'rotate-180' : ''}" viewBox="0 0 20 20" fill="currentColor" style="max-width:40px"><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> <svg class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['change'] === 'asc' ? 'rotate-180' : sortOrders['change'] === '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>
</th> </th>
<th on:click={() => { sortBy = 'price'; changeOrder(order); }} class="cursor-pointer text-white font-semibold text-end text-[1rem]"> <th on:click={() => sortData('price')} class="cursor-pointer select-none text-end text-white font-semibold text-[1rem] whitespace-nowrap">
Price Price
<svg class="w-5 h-5 inline-block {order === 'highToLow' && sortBy === 'price' ? 'rotate-180' : ''}" viewBox="0 0 20 20" fill="currentColor" style="max-width:40px"><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> <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>
</th> </th>
<th on:click={() => { sortBy = 'marketCap'; changeOrder(order); }} class="whitespace-nowrap cursor-pointer text-white font-semibold text-[1rem] text-end"> <th on:click={() => sortData('marketCap')} class="cursor-pointer select-none text-end text-white font-semibold text-[1rem] whitespace-nowrap">
Market Cap Market Cap
<svg class="w-5 h-5 inline-block {order === 'highToLow' && sortBy === 'marketCap' ? 'rotate-180' : ''}" viewBox="0 0 20 20" fill="currentColor" style="max-width:40px"><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> <svg class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['marketCap'] === 'asc' ? 'rotate-180' : sortOrders['marketCap'] === '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>
</th> </th>
<th on:click={() => { sortBy = 'volume'; changeOrder(order); }} class="cursor-pointer text-white font-semibold text-[1rem] text-end"> <th on:click={() => sortData('volume')} class="cursor-pointer select-none text-end text-white font-semibold text-[1rem] whitespace-nowrap">
Volume Volume
<svg class="w-5 h-5 inline-block {order === 'highToLow' && sortBy === 'volume' ? 'rotate-180' : ''}" viewBox="0 0 20 20" fill="currentColor" style="max-width:40px"><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> <svg class="flex-shrink-0 w-4 h-4 inline-block {sortOrders['volume'] === 'asc' ? 'rotate-180' : sortOrders['volume'] === '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>
</th> </th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{#each gainerLoserActive as item, index} {#each gainerLoserActive as item}
<tr on:click={() => goto("/stocks/"+item?.symbol)} class="border-b border-[#27272A] sm:hover:bg-[#245073] sm:hover:bg-opacity-[0.2] odd:bg-[#27272A] cursor-pointer"> <tr class="border-b border-[#27272A] sm:hover:bg-[#245073] sm:hover:bg-opacity-[0.2] odd:bg-[#27272A]">
<td class="border-b-[#09090B] text-sm sm:text-[1rem] whitespace-nowrap"> <td class="border-b-[#09090B] text-sm sm:text-[1rem] whitespace-nowrap">
<a href={"/stocks/"+item?.symbol} class="sm:hover:text-white text-blue-400"> <a href={"/stocks/"+item?.symbol} class="sm:hover:text-white text-blue-400">
@ -418,15 +440,15 @@ $: charNumber = $screenWidth < 640 ? 20 : 30;
{/if} {/if}
</td> </td>
<td class="text-white font-semibold text-sm sm:text-[1rem] text-end border-b-[#09090B]"> <td class="text-white text-sm sm:text-[1rem] text-end border-b-[#09090B]">
${item?.price?.toFixed(2)} {item?.price?.toFixed(2)}
</td> </td>
<td class="text-white text-sm sm:text-[1rem] font-semibold border-b-[#09090B] text-end"> <td class="text-white text-sm sm:text-[1rem] border-b-[#09090B] text-end">
{item?.marketCap !== null ? abbreviateNumber(item?.marketCap, true) : '-'} {item?.marketCap !== null ? abbreviateNumber(item?.marketCap) : '-'}
</td> </td>
<td class="text-white text-sm sm:text-[1rem] font-semibold border-b-[#09090B] text-end"> <td class="text-white text-sm sm:text-[1rem] border-b-[#09090B] text-end">
{item?.volume !== null ? abbreviateNumber(item?.volume) : '-'} {item?.volume !== null ? abbreviateNumber(item?.volume) : '-'}
</td> </td>
@ -482,7 +504,7 @@ $: charNumber = $screenWidth < 640 ? 20 : 30;
<div class="w-auto lg:w-full p-1 flex flex-col m-auto px-2 sm:px-0"> <div class="w-auto lg:w-full p-1 flex flex-col m-auto px-2 sm:px-0">
<div class="w-full flex justify-between items-center p-3 mt-3"> <div class="w-full flex justify-between items-center p-3 mt-3">
<h2 class="text-start text-xl font-semibold text-white ml-3"> <h2 class="text-start text-xl font-semibold text-white ml-3">
Wallstreet Analyst Top Analyst 📊
</h2> </h2>
<ArrowLogo class="w-8 h-8 mr-3 flex-shrink-0"/> <ArrowLogo class="w-8 h-8 mr-3 flex-shrink-0"/>
</div> </div>
@ -496,7 +518,7 @@ $: charNumber = $screenWidth < 640 ? 20 : 30;
<div class="w-auto lg:w-full p-1 flex flex-col m-auto px-2 sm:px-0"> <div class="w-auto lg:w-full p-1 flex flex-col m-auto px-2 sm:px-0">
<div class="w-full flex justify-between items-center p-3 mt-3"> <div class="w-full flex justify-between items-center p-3 mt-3">
<h2 class="text-start text-xl font-semibold text-white ml-3"> <h2 class="text-start text-xl font-semibold text-white ml-3">
Congress Trading Congress Trading 🇺🇸
</h2> </h2>
<ArrowLogo class="w-8 h-8 mr-3 flex-shrink-0"/> <ArrowLogo class="w-8 h-8 mr-3 flex-shrink-0"/>
</div> </div>