From 39cffa420bdf5b03c03e8568158fb4e7b10e2e11 Mon Sep 17 00:00:00 2001 From: Emilio Hurtado Date: Tue, 15 Apr 2025 22:47:18 -0300 Subject: [PATCH 1/3] feat(leaderboard): implement sorting functionality for driver data Added a sorting dropdown to the leaderboard component, allowing users to sort drivers by various criteria such as position, best lap, last lap, and more. Updated the sorting logic in the sorting utility to accommodate new criteria and integrated a Zustand store for managing the selected sorting criteria. --- dash/src/components/LeaderBoard.tsx | 87 +++++++++++++++++++++-------- dash/src/lib/sorting.ts | 52 +++++++++++++++++ dash/src/stores/useSortingStore.ts | 22 ++++++++ 3 files changed, 138 insertions(+), 23 deletions(-) create mode 100644 dash/src/stores/useSortingStore.ts diff --git a/dash/src/components/LeaderBoard.tsx b/dash/src/components/LeaderBoard.tsx index dc090dea..894f07a9 100644 --- a/dash/src/components/LeaderBoard.tsx +++ b/dash/src/components/LeaderBoard.tsx @@ -3,41 +3,82 @@ import clsx from "clsx"; import { useSettingsStore } from "@/stores/useSettingsStore"; import { useDataStore } from "@/stores/useDataStore"; +import { useSortingStore, type SortingCriteria } from "@/stores/useSortingStore"; -import { sortPos } from "@/lib/sorting"; +import { sortDrivers } from "@/lib/sorting"; import { objectEntries } from "@/lib/driverHelper"; import Driver from "@/components/driver/Driver"; +const sortOptions: { label: string; value: SortingCriteria }[] = [ + { label: "Position", value: "position" }, + { label: "Best Lap", value: "bestLap" }, + { label: "Last Lap", value: "lastLap" }, + { label: "Pit Status", value: "pitStatus" }, + { label: "Position Change", value: "positionChange" }, + { label: "Sector 1", value: "sector1" }, + { label: "Sector 2", value: "sector2" }, + { label: "Sector 3", value: "sector3" }, + { label: "Tyre Age", value: "tyreAge" }, +]; + export default function LeaderBoard() { const drivers = useDataStore((state) => state?.driverList); const driversTiming = useDataStore((state) => state?.timingData); + const driversAppTiming = useDataStore((state) => state?.timingAppData); const showTableHeader = useSettingsStore((state) => state.tableHeaders); + const sortCriteria = useSortingStore((state) => state.criteria); + const setSortCriteria = useSortingStore((state) => state.setCriteria); return ( -
- {showTableHeader && } - - {(!drivers || !driversTiming) && - new Array(20).fill("").map((_, index) => )} - - - {drivers && driversTiming && ( - - {objectEntries(driversTiming.lines) - .sort(sortPos) - .map((timingDriver, index) => ( - - ))} - - )} - +
+
+ + +
+ +
+ {showTableHeader && } + + {(!drivers || !driversTiming) && + new Array(20).fill("").map((_, index) => )} + + + {drivers && driversTiming && ( + + {objectEntries(driversTiming.lines) + .sort((a, b) => + sortDrivers( + sortCriteria, + a, + b, + driversAppTiming?.lines[a.racingNumber], + driversAppTiming?.lines[b.racingNumber] + ) + ) + .map((timingDriver, index) => ( + + ))} + + )} + +
); } diff --git a/dash/src/lib/sorting.ts b/dash/src/lib/sorting.ts index 59e8be33..9ab683d2 100644 --- a/dash/src/lib/sorting.ts +++ b/dash/src/lib/sorting.ts @@ -1,4 +1,6 @@ import { utc } from "moment"; +import type { TimingDataDriver, TimingAppDataDriver } from "@/types/state.type"; +import type { SortingCriteria } from "@/stores/useSortingStore"; type PosObject = { position: string }; export const sortPos = (a: PosObject, b: PosObject) => { @@ -21,3 +23,53 @@ type UtcObject = { utc: string }; export const sortUtc = (a: UtcObject, b: UtcObject) => { return utc(b.utc).diff(utc(a.utc)); }; + +// Convert lap time string (e.g. "1:23.456") to milliseconds for comparison +const lapTimeToMs = (time: string): number => { + if (!time) return Infinity; + const [mins, secs] = time.split(":"); + if (!secs) return parseFloat(mins) * 1000; + return (parseInt(mins) * 60 + parseFloat(secs)) * 1000; +}; + +export const sortDrivers = ( + criteria: SortingCriteria, + a: TimingDataDriver, + b: TimingDataDriver, + appDataA?: TimingAppDataDriver, + appDataB?: TimingAppDataDriver +): number => { + switch (criteria) { + case "position": + return parseInt(a.position) - parseInt(b.position); + + case "bestLap": + return lapTimeToMs(a.bestLapTime.value) - lapTimeToMs(b.bestLapTime.value); + + case "lastLap": + return lapTimeToMs(a.lastLapTime.value) - lapTimeToMs(b.lastLapTime.value); + + case "pitStatus": + const getPitPriority = (d: TimingDataDriver) => (d.pitOut ? 0 : d.inPit ? 1 : 2); + return getPitPriority(a) - getPitPriority(b); + + case "positionChange": + const aChange = appDataA ? parseInt(appDataA.gridPos) - parseInt(a.position) : 0; + const bChange = appDataB ? parseInt(appDataB.gridPos) - parseInt(b.position) : 0; + return bChange - aChange; // Sort by most positions gained + + case "sector1": + case "sector2": + case "sector3": + const sectorIndex = parseInt(criteria.slice(-1)) - 1; + return lapTimeToMs(a.sectors[sectorIndex]?.value || "") - lapTimeToMs(b.sectors[sectorIndex]?.value || ""); + + case "tyreAge": + const aAge = appDataA?.stints?.length ? appDataA.stints[appDataA.stints.length - 1].totalLaps || 0 : 0; + const bAge = appDataB?.stints?.length ? appDataB.stints[appDataB.stints.length - 1].totalLaps || 0 : 0; + return bAge - aAge; // Sort by oldest tyres first + + default: + return 0; + } +}; diff --git a/dash/src/stores/useSortingStore.ts b/dash/src/stores/useSortingStore.ts new file mode 100644 index 00000000..c9d3c336 --- /dev/null +++ b/dash/src/stores/useSortingStore.ts @@ -0,0 +1,22 @@ +import { create } from "zustand"; + +export type SortingCriteria = + | "position" // Default race position + | "bestLap" // Best lap time + | "lastLap" // Last lap time + | "pitStatus" // In pit/pit out status + | "positionChange" // Positions gained/lost + | "sector1" // Best sector 1 time + | "sector2" // Best sector 2 time + | "sector3" // Best sector 3 time + | "tyreAge"; // Tyre age + +interface SortingState { + criteria: SortingCriteria; + setCriteria: (criteria: SortingCriteria) => void; +} + +export const useSortingStore = create((set) => ({ + criteria: "position", + setCriteria: (criteria) => set({ criteria }), +})); \ No newline at end of file From 56f081e0e81ece5e4bbb204b975d3b0bacc83276 Mon Sep 17 00:00:00 2001 From: Emilio Hurtado Date: Wed, 23 Apr 2025 19:03:17 -0300 Subject: [PATCH 2/3] feat(leaderboard): enhance sorting functionality and add react-icons - Added `react-icons` dependency for improved UI elements. - Updated the LeaderBoard component to include sorting icons for better user experience. - Enhanced sorting logic to support direction toggling and integrated it with the Zustand store. - Refactored Select component for better styling and functionality. --- dash/package.json | 1 + dash/src/components/LeaderBoard.tsx | 142 +++++++++++++++++++++------- dash/src/components/Select.tsx | 72 +++++++------- dash/src/lib/sorting.ts | 35 ++++--- dash/src/stores/useSortingStore.ts | 23 +++++ dash/yarn.lock | 10 ++ 6 files changed, 201 insertions(+), 82 deletions(-) diff --git a/dash/package.json b/dash/package.json index b7fc8b56..cef62077 100644 --- a/dash/package.json +++ b/dash/package.json @@ -20,6 +20,7 @@ "pako": "2.1.0", "react": "18.3.1", "react-dom": "18.3.1", + "react-icons": "^5.5.0", "sharp": "0.33.5", "zod": "3.23.8", "zustand": "5.0.1" diff --git a/dash/src/components/LeaderBoard.tsx b/dash/src/components/LeaderBoard.tsx index 894f07a9..3b4a0ae9 100644 --- a/dash/src/components/LeaderBoard.tsx +++ b/dash/src/components/LeaderBoard.tsx @@ -1,5 +1,6 @@ import { AnimatePresence, LayoutGroup } from "framer-motion"; import clsx from "clsx"; +import { BiSortAlt2, BiSortDown, BiSortUp } from "react-icons/bi"; import { useSettingsStore } from "@/stores/useSettingsStore"; import { useDataStore } from "@/stores/useDataStore"; @@ -9,19 +10,29 @@ import { sortDrivers } from "@/lib/sorting"; import { objectEntries } from "@/lib/driverHelper"; import Driver from "@/components/driver/Driver"; - -const sortOptions: { label: string; value: SortingCriteria }[] = [ - { label: "Position", value: "position" }, - { label: "Best Lap", value: "bestLap" }, - { label: "Last Lap", value: "lastLap" }, - { label: "Pit Status", value: "pitStatus" }, - { label: "Position Change", value: "positionChange" }, - { label: "Sector 1", value: "sector1" }, - { label: "Sector 2", value: "sector2" }, - { label: "Sector 3", value: "sector3" }, - { label: "Tyre Age", value: "tyreAge" }, +import Select from "@/components/Select"; + +const sortOptions = [ + { label: "Position", value: "position" as SortingCriteria }, + { label: "Best Lap", value: "bestLap" as SortingCriteria }, + { label: "Last Lap", value: "lastLap" as SortingCriteria }, + { label: "Pit Status", value: "pitStatus" as SortingCriteria }, + { label: "Position Change", value: "positionChange" as SortingCriteria }, + { label: "Sector 1", value: "sector1" as SortingCriteria }, + { label: "Sector 2", value: "sector2" as SortingCriteria }, + { label: "Sector 3", value: "sector3" as SortingCriteria }, + { label: "Tyre Age", value: "tyreAge" as SortingCriteria }, ]; +const columnSortMapping: Record = { + "Position": "position", + "Tire": "tyreAge", + "Info": "positionChange", + "Gap": "position", + "LapTime": "bestLap", + "Sectors": "sector1", +}; + export default function LeaderBoard() { const drivers = useDataStore((state) => state?.driverList); const driversTiming = useDataStore((state) => state?.timingData); @@ -29,27 +40,48 @@ export default function LeaderBoard() { const showTableHeader = useSettingsStore((state) => state.tableHeaders); const sortCriteria = useSortingStore((state) => state.criteria); + const sortDirection = useSortingStore((state) => state.direction); + const showSortOptions = useSortingStore((state) => state.showSortOptions); const setSortCriteria = useSortingStore((state) => state.setCriteria); + const toggleDirection = useSortingStore((state) => state.toggleDirection); + const toggleSortOptions = useSortingStore((state) => state.toggleSortOptions); + const setSort = useSortingStore((state) => state.setSort); return ( -
-
- - + + Sort + + + {sortDirection === "asc" ? + : + + } + + {showSortOptions && ( +
+ + placeholder="Sort by" + options={sortOptions} + selected={sortCriteria} + setSelected={(value) => value && setSortCriteria(value)} + /> +
+ )}
- {showTableHeader && } + {showTableHeader && } {(!drivers || !driversTiming) && new Array(20).fill("").map((_, index) => )} @@ -61,6 +93,7 @@ export default function LeaderBoard() { .sort((a, b) => sortDrivers( sortCriteria, + sortDirection, a, b, driversAppTiming?.lines[a.racingNumber], @@ -83,7 +116,34 @@ export default function LeaderBoard() { ); } -const TableHeaders = () => { +type TableHeadersProps = { + currentSort: SortingCriteria; + direction: "asc" | "desc"; + onSortChange: (criteria: SortingCriteria) => void; +}; + +const TableHeaders = ({ currentSort, direction, onSortChange }: TableHeadersProps) => { + const renderSortIcon = (column: string) => { + const criteria = columnSortMapping[column]; + if (!criteria || currentSort !== criteria) return null; + + return direction === "asc" + ? + : ; + }; + + const createClickHandler = (column: string) => { + const criteria = columnSortMapping[column]; + if (!criteria) return undefined; + + return () => onSortChange(criteria); + }; + + const headerClass = (column: string) => clsx( + "cursor-pointer hover:text-zinc-300 transition-colors duration-150 flex items-center", + columnSortMapping[column] && currentSort === columnSortMapping[column] ? "text-sky-400" : "" + ); + return (
{ gridTemplateColumns: "5.5rem 4rem 5.5rem 4rem 5rem 5.5rem auto auto", }} > -

Position

+
+ Position + {renderSortIcon("Position")} +

DRS

-

Tire

-

Info

-

Gap

-

LapTime

-

Sectors

+
+ Tire + {renderSortIcon("Tire")} +
+
+ Info + {renderSortIcon("Info")} +
+
+ Gap + {renderSortIcon("Gap")} +
+
+ LapTime + {renderSortIcon("LapTime")} +
+
+ Sectors + {renderSortIcon("Sectors")} +
); }; diff --git a/dash/src/components/Select.tsx b/dash/src/components/Select.tsx index aeee8dd8..0333f84f 100644 --- a/dash/src/components/Select.tsx +++ b/dash/src/components/Select.tsx @@ -1,8 +1,9 @@ "use client"; +import { Fragment, useState } from "react"; import { Combobox, ComboboxButton, ComboboxInput, ComboboxOption, ComboboxOptions } from "@headlessui/react"; -import { useState } from "react"; import clsx from "clsx"; +import { BiChevronDown } from "react-icons/bi"; type Option = { value: T; @@ -11,54 +12,47 @@ type Option = { type Props = { placeholder?: string; - options: Option[]; - selected: T | null; setSelected: (value: T | null) => void; }; export default function Select({ placeholder, options, selected, setSelected }: Props) { const [query, setQuery] = useState(""); - - const filteredOptions = - query === "" ? options : options.filter((option) => option.label.toLowerCase().includes(query.toLowerCase())); - + const selectedOption = options.find(option => option.value === selected); + return ( - setSelected(value)} onClose={() => setQuery("")}> +
- | null) => option?.label ?? ""} - onChange={(event) => setQuery(event.target.value)} - /> - - {/* */} - +
+ selectedOption?.label || ""} + placeholder={placeholder || "Select option"} + onChange={(event) => setQuery(event.target.value)} + /> + + +
+ + {options.map((option, index) => ( + + clsx( + "relative cursor-default select-none py-2 pl-3 pr-9", + active ? "bg-zinc-700 text-white" : "text-zinc-300", + selected && "bg-zinc-700" + ) + } + > + {option.label} + + ))} +
- - - {filteredOptions.map((option, idx) => ( - - {/* */} -
{option.label}
-
- ))} -
); } diff --git a/dash/src/lib/sorting.ts b/dash/src/lib/sorting.ts index 9ab683d2..ae31d78c 100644 --- a/dash/src/lib/sorting.ts +++ b/dash/src/lib/sorting.ts @@ -1,6 +1,6 @@ import { utc } from "moment"; import type { TimingDataDriver, TimingAppDataDriver } from "@/types/state.type"; -import type { SortingCriteria } from "@/stores/useSortingStore"; +import type { SortingCriteria, SortDirection } from "@/stores/useSortingStore"; type PosObject = { position: string }; export const sortPos = (a: PosObject, b: PosObject) => { @@ -34,42 +34,55 @@ const lapTimeToMs = (time: string): number => { export const sortDrivers = ( criteria: SortingCriteria, + direction: SortDirection, a: TimingDataDriver, b: TimingDataDriver, appDataA?: TimingAppDataDriver, appDataB?: TimingAppDataDriver ): number => { + let result = 0; + switch (criteria) { case "position": - return parseInt(a.position) - parseInt(b.position); + result = parseInt(a.position) - parseInt(b.position); + break; case "bestLap": - return lapTimeToMs(a.bestLapTime.value) - lapTimeToMs(b.bestLapTime.value); + result = lapTimeToMs(a.bestLapTime.value) - lapTimeToMs(b.bestLapTime.value); + break; case "lastLap": - return lapTimeToMs(a.lastLapTime.value) - lapTimeToMs(b.lastLapTime.value); + result = lapTimeToMs(a.lastLapTime.value) - lapTimeToMs(b.lastLapTime.value); + break; case "pitStatus": + // Sort pit status: pit out first, then in pit, then on track const getPitPriority = (d: TimingDataDriver) => (d.pitOut ? 0 : d.inPit ? 1 : 2); - return getPitPriority(a) - getPitPriority(b); + result = getPitPriority(a) - getPitPriority(b); + break; case "positionChange": + // Calculate position changes if grid position data is available const aChange = appDataA ? parseInt(appDataA.gridPos) - parseInt(a.position) : 0; const bChange = appDataB ? parseInt(appDataB.gridPos) - parseInt(b.position) : 0; - return bChange - aChange; // Sort by most positions gained + result = bChange - aChange; // Sort by most positions gained + break; case "sector1": case "sector2": case "sector3": const sectorIndex = parseInt(criteria.slice(-1)) - 1; - return lapTimeToMs(a.sectors[sectorIndex]?.value || "") - lapTimeToMs(b.sectors[sectorIndex]?.value || ""); + result = lapTimeToMs(a.sectors[sectorIndex]?.value || "") - lapTimeToMs(b.sectors[sectorIndex]?.value || ""); + break; case "tyreAge": + // Sort by tyre age (number of laps on current stint) const aAge = appDataA?.stints?.length ? appDataA.stints[appDataA.stints.length - 1].totalLaps || 0 : 0; const bAge = appDataB?.stints?.length ? appDataB.stints[appDataB.stints.length - 1].totalLaps || 0 : 0; - return bAge - aAge; // Sort by oldest tyres first - - default: - return 0; + result = bAge - aAge; // Sort by oldest tyres first + break; } + + // Apply direction + return direction === "asc" ? result : -result; }; diff --git a/dash/src/stores/useSortingStore.ts b/dash/src/stores/useSortingStore.ts index c9d3c336..8d101a0e 100644 --- a/dash/src/stores/useSortingStore.ts +++ b/dash/src/stores/useSortingStore.ts @@ -11,12 +11,35 @@ export type SortingCriteria = | "sector3" // Best sector 3 time | "tyreAge"; // Tyre age +export type SortDirection = "asc" | "desc"; + interface SortingState { criteria: SortingCriteria; + direction: SortDirection; + showSortOptions: boolean; setCriteria: (criteria: SortingCriteria) => void; + toggleDirection: () => void; + toggleSortOptions: () => void; + setSort: (criteria: SortingCriteria) => void; // Set criteria and toggle direction if same criteria } export const useSortingStore = create((set) => ({ criteria: "position", + direction: "asc", + showSortOptions: false, setCriteria: (criteria) => set({ criteria }), + toggleDirection: () => set((state) => ({ + direction: state.direction === "asc" ? "desc" : "asc" + })), + toggleSortOptions: () => set((state) => ({ + showSortOptions: !state.showSortOptions + })), + setSort: (criteria) => set((state) => { + // If clicking the same criteria, toggle direction + if (state.criteria === criteria) { + return { direction: state.direction === "asc" ? "desc" : "asc" }; + } + // Otherwise, change criteria and set direction to ascending + return { criteria, direction: "asc" }; + }), })); \ No newline at end of file diff --git a/dash/yarn.lock b/dash/yarn.lock index 1cfef17e..96c262af 100644 --- a/dash/yarn.lock +++ b/dash/yarn.lock @@ -1034,6 +1034,7 @@ __metadata: prettier-plugin-tailwindcss: "npm:0.6.8" react: "npm:18.3.1" react-dom: "npm:18.3.1" + react-icons: "npm:^5.5.0" sharp: "npm:0.33.5" tailwindcss: "npm:3.4.15" typescript: "npm:5.6.3" @@ -2008,6 +2009,15 @@ __metadata: languageName: node linkType: hard +"react-icons@npm:^5.5.0": + version: 5.5.0 + resolution: "react-icons@npm:5.5.0" + peerDependencies: + react: "*" + checksum: 10c0/a24309bfc993c19cbcbfc928157e53a137851822779977b9588f6dd41ffc4d11ebc98b447f4039b0d309a858f0a42980f6bfb4477fb19f9f2d1bc2e190fcf79c + languageName: node + linkType: hard + "react@npm:18.3.1": version: 18.3.1 resolution: "react@npm:18.3.1" From f8b6807fbde5dd9064dd048184c2ef49a10b80e1 Mon Sep 17 00:00:00 2001 From: Slowlydev Date: Thu, 29 May 2025 21:43:07 +0200 Subject: [PATCH 3/3] chore: deps --- dash/package.json | 2 +- dash/yarn.lock | 141 ++++++++++++++++++++++++++++++---------------- 2 files changed, 93 insertions(+), 50 deletions(-) diff --git a/dash/package.json b/dash/package.json index ee5b6929..7e2faddc 100644 --- a/dash/package.json +++ b/dash/package.json @@ -18,9 +18,9 @@ "motion": "12.9.2", "next": "15.3.1", "pako": "2.1.0", - "react-icons": "^5.5.0", "react": "19.1.0", "react-dom": "19.1.0", + "react-icons": "^5.5.0", "sharp": "0.34.1", "zod": "3.23.8", "zustand": "5.0.3" diff --git a/dash/yarn.lock b/dash/yarn.lock index d882435d..4bdcf182 100644 --- a/dash/yarn.lock +++ b/dash/yarn.lock @@ -127,21 +127,21 @@ __metadata: linkType: hard "@floating-ui/core@npm:^1.6.0": - version: 1.6.9 - resolution: "@floating-ui/core@npm:1.6.9" + version: 1.6.8 + resolution: "@floating-ui/core@npm:1.6.8" dependencies: - "@floating-ui/utils": "npm:^0.2.9" - checksum: 10c0/77debdfc26bc36c6f5ae1f26ab3c15468215738b3f5682af4e1915602fa21ba33ad210273f31c9d2da1c531409929e1afb1138b1608c6b54a0f5853ee84c340d + "@floating-ui/utils": "npm:^0.2.8" + checksum: 10c0/d6985462aeccae7b55a2d3f40571551c8c42bf820ae0a477fc40ef462e33edc4f3f5b7f11b100de77c9b58ecb581670c5c3f46d0af82b5e30aa185c735257eb9 languageName: node linkType: hard "@floating-ui/dom@npm:^1.0.0": - version: 1.6.13 - resolution: "@floating-ui/dom@npm:1.6.13" + version: 1.6.12 + resolution: "@floating-ui/dom@npm:1.6.12" dependencies: "@floating-ui/core": "npm:^1.6.0" - "@floating-ui/utils": "npm:^0.2.9" - checksum: 10c0/272242d2eb6238ffcee0cb1f3c66e0eafae804d5d7b449db5ecf904bc37d31ad96cf575a9e650b93c1190f64f49a684b1559d10e05ed3ec210628b19116991a9 + "@floating-ui/utils": "npm:^0.2.8" + checksum: 10c0/c67b39862175b175c6ac299ea970f17a22c7482cfdf3b1bc79313407bf0880188b022b878953fa69d3ce166ff2bd9ae57c86043e5dd800c262b470d877591b7d languageName: node linkType: hard @@ -171,7 +171,7 @@ __metadata: languageName: node linkType: hard -"@floating-ui/utils@npm:^0.2.8, @floating-ui/utils@npm:^0.2.9": +"@floating-ui/utils@npm:^0.2.8": version: 0.2.9 resolution: "@floating-ui/utils@npm:0.2.9" checksum: 10c0/48bbed10f91cb7863a796cc0d0e917c78d11aeb89f98d03fc38d79e7eb792224a79f538ed8a2d5d5584511d4ca6354ef35f1712659fd569868e342df4398ad6f @@ -607,22 +607,21 @@ __metadata: linkType: hard "@react-aria/focus@npm:^3.17.1": - version: 3.20.2 - resolution: "@react-aria/focus@npm:3.20.2" + version: 3.18.4 + resolution: "@react-aria/focus@npm:3.18.4" dependencies: - "@react-aria/interactions": "npm:^3.25.0" - "@react-aria/utils": "npm:^3.28.2" - "@react-types/shared": "npm:^3.29.0" + "@react-aria/interactions": "npm:^3.22.4" + "@react-aria/utils": "npm:^3.25.3" + "@react-types/shared": "npm:^3.25.0" "@swc/helpers": "npm:^0.5.0" clsx: "npm:^2.0.0" peerDependencies: - react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10c0/83c7ce227affed990833664b75c99601390ea9c879a44032541447268da22508712c512f5a943f702aef07bfe1e0ea51f554f49db132f17d80b2da9cb71ec687 + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + checksum: 10c0/141f8ef80060c5b58384af4af9446c0792618671e9f963942c3edc29bb15b7eb0ebb62cbe118135c7379c2732e86071aa7d7c890903a0ae411be07f2ec854e6a languageName: node linkType: hard -"@react-aria/interactions@npm:^3.21.3, @react-aria/interactions@npm:^3.25.0": +"@react-aria/interactions@npm:^3.21.3": version: 3.25.0 resolution: "@react-aria/interactions@npm:3.25.0" dependencies: @@ -638,6 +637,31 @@ __metadata: languageName: node linkType: hard +"@react-aria/interactions@npm:^3.22.4": + version: 3.22.4 + resolution: "@react-aria/interactions@npm:3.22.4" + dependencies: + "@react-aria/ssr": "npm:^3.9.6" + "@react-aria/utils": "npm:^3.25.3" + "@react-types/shared": "npm:^3.25.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + checksum: 10c0/8455a68540a4085b71ed034cad5c349a7e756e44cd30d69d340d7f7a66ce1886882021fbcc8049a5d8aeba54b47cd2ca49a7bc4e6910aab2d13b41703d55c7a5 + languageName: node + linkType: hard + +"@react-aria/ssr@npm:^3.9.6": + version: 3.9.6 + resolution: "@react-aria/ssr@npm:3.9.6" + dependencies: + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + checksum: 10c0/be52f2909035e093d3f72cccde15b66b4eef2dc30c71dac46a1ea43d3847dace1a709114640bfa3e9aa72ba716749635fb72116f4da16f7d80248ca348146456 + languageName: node + linkType: hard + "@react-aria/ssr@npm:^3.9.8": version: 3.9.8 resolution: "@react-aria/ssr@npm:3.9.8" @@ -649,6 +673,21 @@ __metadata: languageName: node linkType: hard +"@react-aria/utils@npm:^3.25.3": + version: 3.25.3 + resolution: "@react-aria/utils@npm:3.25.3" + dependencies: + "@react-aria/ssr": "npm:^3.9.6" + "@react-stately/utils": "npm:^3.10.4" + "@react-types/shared": "npm:^3.25.0" + "@swc/helpers": "npm:^0.5.0" + clsx: "npm:^2.0.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + checksum: 10c0/dc86ea48c24232f5c51d0b5317d947c4ccf01a8afb3bdc89cb880a7b0a695a04c8a7c615fb190664f4f3c7da8669ab2bd2f7cdfb2861339f5816cbd600249a84 + languageName: node + linkType: hard + "@react-aria/utils@npm:^3.28.2": version: 3.28.2 resolution: "@react-aria/utils@npm:3.28.2" @@ -675,6 +714,17 @@ __metadata: languageName: node linkType: hard +"@react-stately/utils@npm:^3.10.4": + version: 3.10.4 + resolution: "@react-stately/utils@npm:3.10.4" + dependencies: + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + checksum: 10c0/875c11424fadf4419caceeee13e5bfdee2b0c330fe0220c0ea9d68d570cc9a34525f2f124d977e519b397a738cd2f8e36b7b03a046e3e7da99460e99282977a4 + languageName: node + linkType: hard + "@react-stately/utils@npm:^3.10.6": version: 3.10.6 resolution: "@react-stately/utils@npm:3.10.6" @@ -686,6 +736,15 @@ __metadata: languageName: node linkType: hard +"@react-types/shared@npm:^3.25.0": + version: 3.25.0 + resolution: "@react-types/shared@npm:3.25.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0 + checksum: 10c0/d168f6b404c345928ef8ead94f0cecd3831d8f6df708dbe897ac62d566949a0931c3b0d95ef6dd02bc5af05b183781b531e6f041ffd1d320bc2cab7697fd27d0 + languageName: node + linkType: hard + "@react-types/shared@npm:^3.29.0": version: 3.29.0 resolution: "@react-types/shared@npm:3.29.0" @@ -1523,9 +1582,9 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.30001579": - version: 1.0.30001713 - resolution: "caniuse-lite@npm:1.0.30001713" - checksum: 10c0/f5468abfe73ce30e29cc8bde2ea67df2aab69032bdd93345e0640efefb76b7901c84fe1d28d591a797e65fe52fc24cae97060bb5552f9f9740322aff95ce2f9d + version: 1.0.30001680 + resolution: "caniuse-lite@npm:1.0.30001680" + checksum: 10c0/11a4e7f6f5d5f965cfd4b7dc4aef34e12a26e99647f02b5ac9fd7f7670845473b95ada416a785473237e4b1b67281f7b043c8736c85b77097f6b697e8950b15f languageName: node linkType: hard @@ -2210,23 +2269,13 @@ __metadata: motion: "npm:12.9.2" next: "npm:15.3.1" pako: "npm:2.1.0" -<<<<<<< HEAD - postcss: "npm:8.4.49" - prettier: "npm:3.3.3" - prettier-plugin-tailwindcss: "npm:0.6.8" - react: "npm:18.3.1" - react-dom: "npm:18.3.1" - react-icons: "npm:^5.5.0" - sharp: "npm:0.33.5" - tailwindcss: "npm:3.4.15" -======= prettier: "npm:3.5.3" prettier-plugin-tailwindcss: "npm:0.6.11" react: "npm:19.1.0" react-dom: "npm:19.1.0" + react-icons: "npm:^5.5.0" sharp: "npm:0.34.1" tailwindcss: "npm:4.0.8" ->>>>>>> 0352dc70248d5c7ab09719af4ab3cb2f71eea622 typescript: "npm:5.6.3" zod: "npm:3.23.8" zustand: "npm:5.0.3" @@ -2254,15 +2303,15 @@ __metadata: linkType: hard "fast-glob@npm:^3.3.2": - version: 3.3.3 - resolution: "fast-glob@npm:3.3.3" + version: 3.3.2 + resolution: "fast-glob@npm:3.3.2" dependencies: "@nodelib/fs.stat": "npm:^2.0.2" "@nodelib/fs.walk": "npm:^1.2.3" glob-parent: "npm:^5.1.2" merge2: "npm:^1.3.0" - micromatch: "npm:^4.0.8" - checksum: 10c0/f6aaa141d0d3384cf73cbcdfc52f475ed293f6d5b65bfc5def368b09163a9f7e5ec2b3014d80f733c405f58e470ee0cc451c2937685045cddcdeaa24199c43fe + micromatch: "npm:^4.0.4" + checksum: 10c0/42baad7b9cd40b63e42039132bde27ca2cb3a4950d0a0f9abe4639ea1aa9d3e3b40f98b1fe31cbc0cc17b664c9ea7447d911a152fa34ec5b72977b125a6fc845 languageName: node linkType: hard @@ -2281,11 +2330,11 @@ __metadata: linkType: hard "fastq@npm:^1.6.0": - version: 1.19.1 - resolution: "fastq@npm:1.19.1" + version: 1.17.1 + resolution: "fastq@npm:1.17.1" dependencies: reusify: "npm:^1.0.4" - checksum: 10c0/ebc6e50ac7048daaeb8e64522a1ea7a26e92b3cee5cd1c7f2316cdca81ba543aa40a136b53891446ea5c3a67ec215fbaca87ad405f102dd97012f62916905630 + checksum: 10c0/1095f16cea45fb3beff558bb3afa74ca7a9250f5a670b65db7ed585f92b4b48381445cd328b3d87323da81e43232b5d5978a8201bde84e0cd514310f1ea6da34 languageName: node linkType: hard @@ -3244,7 +3293,7 @@ __metadata: languageName: node linkType: hard -"micromatch@npm:^4.0.4, micromatch@npm:^4.0.8": +"micromatch@npm:^4.0.4": version: 4.0.8 resolution: "micromatch@npm:4.0.8" dependencies: @@ -3778,7 +3827,6 @@ __metadata: languageName: node linkType: hard -<<<<<<< HEAD "react-icons@npm:^5.5.0": version: 5.5.0 resolution: "react-icons@npm:5.5.0" @@ -3788,10 +3836,6 @@ __metadata: languageName: node linkType: hard -"react@npm:18.3.1": - version: 18.3.1 - resolution: "react@npm:18.3.1" -======= "react-is@npm:^16.13.1": version: 16.13.1 resolution: "react-is@npm:16.13.1" @@ -3809,7 +3853,6 @@ __metadata: "reflect.getprototypeof@npm:^1.0.6, reflect.getprototypeof@npm:^1.0.9": version: 1.0.10 resolution: "reflect.getprototypeof@npm:1.0.10" ->>>>>>> 0352dc70248d5c7ab09719af4ab3cb2f71eea622 dependencies: call-bind: "npm:^1.0.8" define-properties: "npm:^1.2.1" @@ -3913,9 +3956,9 @@ __metadata: linkType: hard "reusify@npm:^1.0.4": - version: 1.1.0 - resolution: "reusify@npm:1.1.0" - checksum: 10c0/4eff0d4a5f9383566c7d7ec437b671cc51b25963bd61bf127c3f3d3f68e44a026d99b8d2f1ad344afff8d278a8fe70a8ea092650a716d22287e8bef7126bb2fa + version: 1.0.4 + resolution: "reusify@npm:1.0.4" + checksum: 10c0/c19ef26e4e188f408922c46f7ff480d38e8dfc55d448310dfb518736b23ed2c4f547fb64a6ed5bdba92cd7e7ddc889d36ff78f794816d5e71498d645ef476107 languageName: node linkType: hard