make column sortable

This commit is contained in:
MuslemRahimi 2024-12-07 13:38:05 +01:00
parent b062066048
commit 866484d416
2 changed files with 137 additions and 42 deletions

View File

@ -19,7 +19,7 @@
class="cursor-pointer select-none text-white font-semibold text-sm whitespace-nowrap {column.align === class="cursor-pointer select-none text-white font-semibold text-sm whitespace-nowrap {column.align ===
'right' 'right'
? 'text-end' ? 'text-end'
: ''}" : 'text-start'}"
> >
{column.label} {column.label}
{@html SortIcon({ sortOrder: sortOrders[column.key].order })} {@html SortIcon({ sortOrder: sortOrders[column.key].order })}

View File

@ -5,12 +5,15 @@
import ArrowLogo from "lucide-svelte/icons/move-up-right"; import ArrowLogo from "lucide-svelte/icons/move-up-right";
import * as DropdownMenu from "$lib/components/shadcn/dropdown-menu/index.js"; import * as DropdownMenu from "$lib/components/shadcn/dropdown-menu/index.js";
import { Button } from "$lib/components/shadcn/button/index.js"; import { Button } from "$lib/components/shadcn/button/index.js";
import TableHeader from "$lib/components/Table/TableHeader.svelte";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { page } from "$app/stores"; import { page } from "$app/stores";
export let data; export let data;
let rawData; let rawData;
let filterList = []; let filterList = [];
let weekdayFiltered = []; let weekdayFiltered = [];
let weekday; // Added declaration let weekday; // Added declaration
@ -23,14 +26,18 @@
let previousMax = false; let previousMax = false;
let nextMax = false; let nextMax = false;
let searchQuery = ""; let searchQuery = "";
let sortMode = false;
$: testList = []; $: testList = [];
$: economicCalendar = data?.getEconomicCalendar; $: economicCalendar = data?.getEconomicCalendar;
$: daysOfWeek = getDaysOfWeek(currentWeek); $: daysOfWeek = getDaysOfWeek(currentWeek);
$: formattedWeekday = daysOfWeek.map((day) => format(day.date, "EEE, MMM d")); $: formattedWeekday = daysOfWeek.map((day) => format(day.date, "EEE, MMM d"));
$: { $: {
weekday = getWeekdayData(economicCalendar, daysOfWeek); if (!sortMode) {
rawData = weekday; weekday = getWeekdayData(economicCalendar, daysOfWeek);
rawData = weekday;
}
// Reapply filters whenever weekday data changes // Reapply filters whenever weekday data changes
if (filterList.length > 0 && syncWorker) { if (filterList.length > 0 && syncWorker) {
loadWorker(); loadWorker();
@ -75,6 +82,7 @@
const handleMessage = (event) => { const handleMessage = (event) => {
weekdayFiltered = event.data?.finalData?.output ?? []; weekdayFiltered = event.data?.finalData?.output ?? [];
originalData = weekdayFiltered;
}; };
const loadWorker = async () => { const loadWorker = async () => {
@ -196,6 +204,104 @@
saveRules(); saveRules();
} }
let columns = [
{ key: "time", label: "Time", align: "left" },
{ key: "country", label: "Country", align: "left" },
{ key: "event", label: "Event", align: "left" },
{ key: "actual", label: "Actual", align: "right" },
{ key: "consensus", label: "Forecast", align: "right" },
{ key: "prior", label: "Previous", align: "right" },
{ key: "importance", label: "Importance", align: "right" },
];
let sortOrders = {
time: { order: "none", type: "string" },
country: { order: "none", type: "string" },
event: { order: "none", type: "string" },
actual: { order: "none", type: "number" },
consensus: { order: "none", type: "number" },
prior: { order: "none", type: "number" },
importance: { order: "none", type: "number" },
};
const sortData = (key) => {
sortMode = true;
for (const k in sortOrders) {
if (k !== key) {
sortOrders[k].order = "none";
}
}
// Cycle through 'none', 'asc', 'desc' for the clicked key
const orderCycle = ["none", "asc", "desc"];
const currentOrderIndex = orderCycle.indexOf(
sortOrders[key]?.order || "none",
);
sortOrders[key] = {
...(sortOrders[key] || {}),
order: orderCycle[(currentOrderIndex + 1) % orderCycle.length],
};
const sortOrder = sortOrders[key]?.order;
// Reset to original data when 'none' and stop further sorting
if (sortOrder === "none") {
sortMode = false;
return;
}
// Generic comparison function
const compareValues = (a, b) => {
const { type } = sortOrders[key];
let valueA, valueB;
switch (type) {
case "date":
valueA = new Date(a[key]);
valueB = new Date(b[key]);
break;
case "rating":
case "string":
// Retrieve values
valueA = a[key];
valueB = b[key];
// Handle null or undefined values, always placing them at the bottom
if (valueA == null && valueB == null) {
return 0; // Both are null/undefined, no need to change the order
} else if (valueA == null) {
return 1; // null goes to the bottom
} else if (valueB == null) {
return -1; // null goes to the bottom
}
// Convert the values to uppercase for case-insensitive comparison
valueA = valueA?.toUpperCase();
valueB = valueB?.toUpperCase();
// Perform the sorting based on ascending or descending order
return sortOrder === "asc"
? valueA?.localeCompare(valueB)
: valueB?.localeCompare(valueA);
case "number":
default:
valueA = parseFloat(a[key]);
valueB = parseFloat(b[key]);
break;
}
if (sortOrder === "asc") {
return valueA < valueB ? -1 : valueA > valueB ? 1 : 0;
} else {
return valueA > valueB ? -1 : valueA < valueB ? 1 : 0;
}
};
// Sort and update the originalData and stockList
weekday[selectedWeekday] = [...rawData[selectedWeekday]].sort(
compareValues,
);
};
</script> </script>
<svelte:head> <svelte:head>
@ -270,7 +376,7 @@
on:click={() => changeWeek("previous")} on:click={() => changeWeek("previous")}
class="{previousMax class="{previousMax
? 'opacity-80' ? 'opacity-80'
: ''} hidden sm:flex h-16 w-48 cursor-pointer border m-auto flex bg-[#27272A] border border-gray-600 mb-3" : ''} hidden sm:flex h-16 w-48 cursor-pointer border m-auto flex bg-primary border border-gray-600 mb-3"
> >
<svg <svg
class="w-6 h-6 m-auto rotate-180" class="w-6 h-6 m-auto rotate-180"
@ -342,7 +448,7 @@
on:click={() => changeWeek("next")} on:click={() => changeWeek("next")}
class="{nextMax class="{nextMax
? 'opacity-80' ? 'opacity-80'
: ''} hidden sm:flex h-16 w-48 cursor-pointer border m-auto flex bg-[#27272A] border border-gray-600 mb-3" : ''} hidden sm:flex h-16 w-48 cursor-pointer border m-auto flex bg-primary border border-gray-600 mb-3"
> >
<svg <svg
class="w-6 h-6 m-auto" class="w-6 h-6 m-auto"
@ -366,7 +472,7 @@
<DropdownMenu.Trigger asChild let:builder> <DropdownMenu.Trigger asChild let:builder>
<Button <Button
builders={[builder]} builders={[builder]}
class="border-gray-600 border bg-[#09090B] sm:hover:bg-[#27272A] ease-out flex flex-row justify-between items-center px-3 py-2 text-white rounded-md truncate" class="border-gray-600 border bg-[#09090B] sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-3 py-2 text-white rounded-md truncate"
> >
<span class="truncate text-white">Filter Country</span> <span class="truncate text-white">Filter Country</span>
<svg <svg
@ -405,7 +511,7 @@
</div> </div>
<DropdownMenu.Group> <DropdownMenu.Group>
{#each testList.length > 0 && searchQuery?.length > 0 ? testList : searchQuery?.length > 0 && testList?.length === 0 ? [] : listOfRelevantCountries as item} {#each testList.length > 0 && searchQuery?.length > 0 ? testList : searchQuery?.length > 0 && testList?.length === 0 ? [] : listOfRelevantCountries as item}
<DropdownMenu.Item class="sm:hover:bg-[#27272A]"> <DropdownMenu.Item class="sm:hover:bg-primary">
<div class="flex items-center"> <div class="flex items-center">
<label <label
on:click={() => { on:click={() => {
@ -431,7 +537,7 @@
<DropdownMenu.Trigger asChild let:builder> <DropdownMenu.Trigger asChild let:builder>
<Button <Button
builders={[builder]} builders={[builder]}
class="border-gray-600 border bg-[#09090B] sm:hover:bg-[#27272A] ease-out flex flex-row justify-between items-center px-3 py-2 text-white rounded-md truncate" class="border-gray-600 border bg-[#09090B] sm:hover:bg-primary ease-out flex flex-row justify-between items-center px-3 py-2 text-white rounded-md truncate"
> >
<span class="truncate text-white" <span class="truncate text-white"
>Filter Importance</span >Filter Importance</span
@ -462,7 +568,7 @@
></div> ></div>
<DropdownMenu.Group> <DropdownMenu.Group>
{#each [1, 2, 3] as i} {#each [1, 2, 3] as i}
<DropdownMenu.Item class="sm:hover:bg-[#27272A]"> <DropdownMenu.Item class="sm:hover:bg-primary">
<div class="flex items-center"> <div class="flex items-center">
<label <label
on:click={() => { on:click={() => {
@ -516,7 +622,7 @@
{#if filterList?.length !== 0} {#if filterList?.length !== 0}
<Button <Button
on:click={() => handleReset()} on:click={() => handleReset()}
class="w-fit border-gray-600 border bg-[#09090B] sm:hover:bg-[#27272A] ease-out flex flex-row justify-start items-center px-3 py-2 text-white rounded-md truncate" class="w-fit border-gray-600 border bg-[#09090B] sm:hover:bg-primary ease-out flex flex-row justify-start items-center px-3 py-2 text-white rounded-md truncate"
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -571,43 +677,13 @@
class="table-sm table-compact rounded-none sm:rounded-md w-full border-bg-[#09090B] m-auto mt-4" class="table-sm table-compact rounded-none sm:rounded-md w-full border-bg-[#09090B] m-auto mt-4"
> >
<thead> <thead>
<tr class="whitespace-nowrap"> <TableHeader {columns} {sortOrders} {sortData} />
<th
class="text-start text-white font-semibold text-sm"
>Time</th
>
<th
class="text-start text-white font-semibold text-sm sm:text-[1rem]"
>Country</th
>
<th
class="text-start text-white font-semibold text-sm sm:text-[1rem]"
>Event</th
>
<th
class="text-end text-white font-semibold text-sm sm:text-[1rem]"
>Actual</th
>
<th
class="text-end text-white font-semibold text-sm sm:text-[1rem]"
>Forecast</th
>
<th
class="text-end text-white font-semibold text-sm sm:text-[1rem]"
>Previous</th
>
<th
class="text-white font-semibold text-sm sm:text-[1rem] text-end"
>Importance</th
>
</tr>
</thead> </thead>
<tbody> <tbody>
{#each day as item} {#each day as item}
<!-- row --> <!-- row -->
<tr <tr
class="sm:hover:bg-[#245073] sm:hover:bg-opacity-[0.2] odd:bg-[#27272A]" class="sm:hover:bg-[#245073] sm:hover:bg-opacity-[0.2] odd:bg-primary"
> >
<td <td
class="text-white text-sm sm:text-[1rem] border-b-[#09090B]" class="text-white text-sm sm:text-[1rem] border-b-[#09090B]"
@ -771,6 +847,25 @@
{/if} {/if}
{/if} {/if}
{/each} {/each}
<div
class="w-full text-white text-start p-3 sm:p-5 mt-5 rounded-md sm:flex sm:flex-row sm:items-center border border-gray-600 text-sm sm:text-[1rem]"
>
<svg
class="w-6 h-6 flex-shrink-0 inline-block sm:mr-2"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 256 256"
><path
fill="#fff"
d="M128 24a104 104 0 1 0 104 104A104.11 104.11 0 0 0 128 24m-4 48a12 12 0 1 1-12 12a12 12 0 0 1 12-12m12 112a16 16 0 0 1-16-16v-40a8 8 0 0 1 0-16a16 16 0 0 1 16 16v40a8 8 0 0 1 0 16"
/></svg
>
Stocknear's Economic Calendar displays the latest and upcoming
economic events that may impact various assets, regions, and
global markets — including stocks, Forex, and bonds. Times are
shown in ET (Eastern Time).
</div>
</div> </div>
</div> </div>
</div> </div>