diff --git a/package.json b/package.json
index 0489116..9fabe12 100644
--- a/package.json
+++ b/package.json
@@ -16,8 +16,10 @@
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-scripts": "5.0.1",
+ "recharts": "^2.15.0",
"typescript": "^4.4.2",
- "web-vitals": "^2.1.0"
+ "web-vitals": "^2.1.0",
+ "zustand": "^5.0.2"
},
"scripts": {
"start": "react-scripts start",
diff --git a/src/App.tsx b/src/App.tsx
index ff61bdd..33a01ac 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,11 +1,13 @@
import React from "react";
import Header from "./components/Header";
+import PopulationGraphPage from "./pages/PopulationGraphPage";
import "./assets/styles/reset.css";
function App() {
return (
);
}
diff --git a/src/api/population.ts b/src/api/population.ts
index 5792d1f..009e89b 100644
--- a/src/api/population.ts
+++ b/src/api/population.ts
@@ -6,7 +6,7 @@ export const fetchPopulation = async (
): Promise => {
try {
const response = await axios.get(
- `${process.env.REACT_APP_YUMEMI_API_URL}/population/${prefCode}`,
+ `${process.env.REACT_APP_YUMEMI_API_URL}/api/v1/population/composition/perYear?prefCode=${prefCode}`,
{
headers: {
"X-API-KEY": process.env.REACT_APP_YUMEMI_API_KEY!,
diff --git a/src/api/prefectures.ts b/src/api/prefectures.ts
index 66061b6..0f5fae4 100644
--- a/src/api/prefectures.ts
+++ b/src/api/prefectures.ts
@@ -4,19 +4,13 @@ import { Prefecture } from "../types/prefecture";
export const fetchPrefectures = async (): Promise => {
try {
const response = await axios.get(
- `${process.env.REACT_APP_YUMEMI_API_URL}/prefectures`,
+ `${process.env.REACT_APP_YUMEMI_API_URL}/api/v1/prefectures`,
{
- headers: {
- "X-API-KEY": process.env.REACT_APP_YUMEMI_API_KEY!,
- },
+ headers: { "X-API-KEY": process.env.REACT_APP_YUMEMI_API_KEY },
}
);
- if (response.status !== 200) {
- throw new Error("Failed to fetch prefectures");
- }
-
- return response.data as Prefecture[];
+ return response.data.result as Prefecture[];
} catch (error) {
throw new Error("Error fetching prefectures: " + error);
}
diff --git a/src/components/PopulationChart.tsx b/src/components/PopulationChart.tsx
new file mode 100644
index 0000000..c231cb5
--- /dev/null
+++ b/src/components/PopulationChart.tsx
@@ -0,0 +1,129 @@
+import React, { useState } from "react";
+import {
+ LineChart,
+ Line,
+ XAxis,
+ YAxis,
+ CartesianGrid,
+ Tooltip,
+ Legend,
+ ResponsiveContainer,
+} from "recharts";
+
+interface PopulationData {
+ year: number;
+ value: number;
+ rate: number;
+}
+
+interface PrefectureCategoryData {
+ prefName: string;
+ data: PopulationData[];
+}
+
+interface AllCategoriesData {
+ [categoryName: string]: PrefectureCategoryData[];
+}
+
+interface PopulationChartProps {
+ allCategoriesData: AllCategoriesData;
+}
+
+const categories = ["総人口", "年少人口", "生産年齢人口", "老年人口"];
+
+const colorMap: { [prefName: string]: string } = {};
+
+const generateRandomColor = (): string => {
+ const letters = "0123456789ABCDEF";
+ let color = "#";
+ for (let i = 0; i < 6; i++) {
+ color += letters[Math.floor(Math.random() * 16)];
+ }
+ return color;
+};
+
+const getColorForPrefName = (pName: string) => {
+ if (!colorMap[pName]) {
+ colorMap[pName] = generateRandomColor();
+ }
+ return colorMap[pName];
+};
+
+const PopulationChart: React.FC = ({
+ allCategoriesData,
+}) => {
+ const [selectedCategory, setSelectedCategory] = useState(
+ categories[0]
+ );
+
+ const prefectureDataArray = allCategoriesData[selectedCategory] || [];
+
+ if (prefectureDataArray.length === 0) {
+ return 選択中のカテゴリのデータがありません
;
+ }
+
+ const allYears = prefectureDataArray.flatMap((p) =>
+ p.data.map((d) => d.year)
+ );
+ const uniqueYears = Array.from(new Set(allYears)).sort((a, b) => a - b);
+
+ const combinedData = uniqueYears.map((year) => {
+ const entry: { [key: string]: number | string } = { year };
+ for (const pData of prefectureDataArray) {
+ const yearData = pData.data.find((d) => d.year === year);
+ entry[pData.prefName] = yearData ? yearData.value : 0;
+ }
+ return entry;
+ });
+
+ const prefNames = prefectureDataArray.map((p) => p.prefName);
+
+ return (
+
+ {/* カテゴリ切り替えUI */}
+
+ {categories.map((cat) => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+ {prefNames.map((pName) => (
+
+ ))}
+
+
+
+ );
+};
+
+export default PopulationChart;
diff --git a/src/components/PopulationSelector.tsx b/src/components/PopulationSelector.tsx
index d0ec13c..a295cd3 100644
--- a/src/components/PopulationSelector.tsx
+++ b/src/components/PopulationSelector.tsx
@@ -1,7 +1,7 @@
import React, { useState } from "react";
-import { fetchPopulation } from "../api/population"; // 人口データを取得するAPI
-import { Prefecture } from "../types/prefecture"; // 都道府県の型
-import { PopulationCategory } from "../types/population"; // 人口データのカテゴリ型
+import { fetchPopulation } from "../api/population";
+import { Prefecture } from "../types/prefecture";
+import { PopulationCategory } from "../types/population";
interface PopulationSelectorProps {
prefectures: Prefecture[];
diff --git a/src/components/PrefectureCheckbox.tsx b/src/components/PrefectureCheckbox.tsx
index 039dbd3..5762480 100644
--- a/src/components/PrefectureCheckbox.tsx
+++ b/src/components/PrefectureCheckbox.tsx
@@ -1,43 +1,40 @@
import React from "react";
import { Prefecture } from "../types/prefecture";
+import usePrefecturesStore from "../store/usePrefecturesStore";
interface PrefectureCheckboxProps {
prefectures: Prefecture[];
- selectedPrefectures: string[];
- onSelect: (selected: string[]) => void;
+ onSelect: (prefCode: number) => void;
+ onSelectAll: (prefCodes: number[]) => void;
+ onClearSelection: () => void;
}
const PrefectureCheckbox: React.FC = ({
prefectures,
- selectedPrefectures,
onSelect,
+ onSelectAll,
+ onClearSelection,
}) => {
- const handleCheckboxChange = (prefCode: string) => {
- if (selectedPrefectures.includes(prefCode)) {
- onSelect(selectedPrefectures.filter((code) => code !== prefCode));
- } else {
- onSelect([...selectedPrefectures, prefCode]);
- }
- };
+ const { selectedPrefectures } = usePrefecturesStore();
return (
-
都道府県選択
- {prefectures.map((prefecture) => (
-
+
都道府県を選択
+
+
+ {prefectures.map((pref) => (
+
- handleCheckboxChange(prefecture.prefCode.toString())
- }
+ checked={selectedPrefectures.includes(pref.prefCode)}
+ onChange={() => onSelect(pref.prefCode)}
/>
-
+
))}
diff --git a/src/hooks/usePopulation.ts b/src/hooks/usePopulation.ts
new file mode 100644
index 0000000..2e654ad
--- /dev/null
+++ b/src/hooks/usePopulation.ts
@@ -0,0 +1,32 @@
+import { useState, useEffect } from "react";
+import { fetchPopulation } from "../api/population";
+import { PopulationCategory } from "../types/population";
+
+const usePopulation = (prefCode: number | null) => {
+ const [data, setData] = useState
([]);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ if (!prefCode) return;
+
+ const fetchData = async () => {
+ setLoading(true);
+ setError(null);
+ try {
+ const result = await fetchPopulation(prefCode.toString());
+ setData(result);
+ } catch (err) {
+ setError("人口データの取得に失敗しました. " + err);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchData();
+ }, [prefCode]);
+
+ return { data, loading, error };
+};
+
+export default usePopulation;
diff --git a/src/pages/PopulationGraphPage.tsx b/src/pages/PopulationGraphPage.tsx
new file mode 100644
index 0000000..00fe377
--- /dev/null
+++ b/src/pages/PopulationGraphPage.tsx
@@ -0,0 +1,111 @@
+import React, { useEffect, useState } from "react";
+import PrefectureCheckbox from "../components/PrefectureCheckbox";
+import PopulationChart from "../components/PopulationChart";
+import usePrefecturesStore from "../store/usePrefecturesStore";
+import { fetchPrefectures } from "../api/prefectures";
+import { fetchPopulation } from "../api/population";
+import { Prefecture } from "../types/prefecture";
+import { PopulationData } from "../types/population";
+
+interface PrefectureCategoryData {
+ prefName: string;
+ data: PopulationData[];
+}
+
+interface AllCategoriesData {
+ [categoryName: string]: PrefectureCategoryData[];
+}
+
+const categories = ["総人口", "年少人口", "生産年齢人口", "老年人口"];
+
+const PopulationGraphPage: React.FC = () => {
+ const { selectedPrefectures, prefectures, setPrefectures } =
+ usePrefecturesStore();
+ const [allCategoriesData, setAllCategoriesData] = useState(
+ {}
+ );
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ const initializePrefectures = async (): Promise => {
+ try {
+ const fetchedPrefectures: Prefecture[] = await fetchPrefectures();
+ setPrefectures(fetchedPrefectures);
+ } catch (err) {
+ setError("都道府県データの取得に失敗しました:" + err);
+ }
+ };
+ initializePrefectures();
+ }, [setPrefectures]);
+
+ useEffect(() => {
+ const fetchPopulationData = async () => {
+ setLoading(true);
+ setError(null);
+
+ const newAllCategoriesData: AllCategoriesData = {
+ 総人口: [],
+ 年少人口: [],
+ 生産年齢人口: [],
+ 老年人口: [],
+ };
+
+ try {
+ for (const prefCode of selectedPrefectures) {
+ const categoriesData = await fetchPopulation(prefCode.toString());
+ const prefName =
+ prefectures.find((p) => p.prefCode === Number(prefCode))
+ ?.prefName || prefCode.toString();
+
+ for (const catName of categories) {
+ const catData = categoriesData.find((c) => c.label === catName);
+ if (catData) {
+ newAllCategoriesData[catName].push({
+ prefName: prefName,
+ data: catData.data,
+ });
+ }
+ }
+ }
+ setAllCategoriesData(newAllCategoriesData);
+ } catch (err) {
+ setError("人口データの取得に失敗しました:" + err);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ if (selectedPrefectures.length > 0) {
+ fetchPopulationData();
+ } else {
+ setAllCategoriesData({
+ 総人口: [],
+ 年少人口: [],
+ 生産年齢人口: [],
+ 老年人口: [],
+ });
+ }
+ }, [selectedPrefectures, prefectures]);
+
+ const { togglePrefecture, selectAllPrefectures, clearSelection } =
+ usePrefecturesStore.getState();
+
+ return (
+
+
人口推移グラフ
+
togglePrefecture(prefCode)}
+ onSelectAll={(prefCodes: number[]) => selectAllPrefectures(prefCodes)}
+ onClearSelection={clearSelection}
+ />
+ {selectedPrefectures.length === 0 && 都道府県を選択してください。
}
+ {loading && データを取得中...
}
+ {error && {error}
}
+
+
+ );
+};
+
+export default PopulationGraphPage;
diff --git a/src/store/usePrefecturesStore.ts b/src/store/usePrefecturesStore.ts
new file mode 100644
index 0000000..ac557eb
--- /dev/null
+++ b/src/store/usePrefecturesStore.ts
@@ -0,0 +1,35 @@
+import { create } from "zustand";
+
+interface Prefecture {
+ prefCode: number;
+ prefName: string;
+}
+
+interface PrefecturesState {
+ prefectures: Prefecture[];
+ selectedPrefectures: number[];
+ setPrefectures: (prefectures: Prefecture[]) => void;
+ togglePrefecture: (prefCode: number) => void;
+ selectAllPrefectures: (prefCodes: number[]) => void;
+ clearSelection: () => void;
+ selectAll: (prefCodes: number[]) => void;
+}
+
+const usePrefecturesStore = create((set) => ({
+ prefectures: [],
+ selectedPrefectures: [],
+ setPrefectures: (prefectures) => set({ prefectures }),
+ togglePrefecture: (prefCode) =>
+ set((state) => ({
+ selectedPrefectures: state.selectedPrefectures.includes(prefCode)
+ ? state.selectedPrefectures.filter((code) => code !== prefCode)
+ : [...state.selectedPrefectures, prefCode],
+ })),
+ selectAllPrefectures: (prefCodes: number[]) =>
+ set({ selectedPrefectures: prefCodes }),
+ clearSelection: () => set({ selectedPrefectures: [] }),
+
+ selectAll: (prefCodes: number[]) => set({ selectedPrefectures: prefCodes }),
+}));
+
+export default usePrefecturesStore;
diff --git a/src/tests/PopulationChart.test.tsx b/src/tests/PopulationChart.test.tsx
new file mode 100644
index 0000000..c1e905c
--- /dev/null
+++ b/src/tests/PopulationChart.test.tsx
@@ -0,0 +1,80 @@
+import React from "react";
+import { render, screen, fireEvent } from "@testing-library/react";
+import PopulationChart from "../components/PopulationChart";
+
+jest.mock("recharts", () => {
+ const OriginalRecharts = jest.requireActual("recharts");
+ return {
+ ...OriginalRecharts,
+ ResponsiveContainer: ({ children }: { children: React.ReactNode }) => (
+ {children}
+ ),
+ };
+});
+
+const mockAllCategoriesData = {
+ 総人口: [
+ {
+ prefName: "北海道",
+ data: [
+ { year: 1960, value: 5000, rate: 0 },
+ { year: 1965, value: 6000, rate: 0 },
+ ],
+ },
+ {
+ prefName: "青森県",
+ data: [
+ { year: 1960, value: 4500, rate: 0 },
+ { year: 1965, value: 5500, rate: 0 },
+ ],
+ },
+ ],
+ 年少人口: [
+ {
+ prefName: "北海道",
+ data: [
+ { year: 1960, value: 2000, rate: 0 },
+ { year: 1965, value: 2500, rate: 0 },
+ ],
+ },
+ {
+ prefName: "青森県",
+ data: [
+ { year: 1960, value: 1800, rate: 0 },
+ { year: 1965, value: 2300, rate: 0 },
+ ],
+ },
+ ],
+ 生産年齢人口: [],
+ 老年人口: [],
+};
+
+describe("PopulationChartコンポーネントのテスト", () => {
+ it("グラフが正しくレンダリングされる", () => {
+ render();
+ expect(screen.getByText("総人口")).toBeInTheDocument();
+ expect(screen.getByText("年少人口")).toBeInTheDocument();
+ expect(screen.getByText("生産年齢人口")).toBeInTheDocument();
+ expect(screen.getByText("老年人口")).toBeInTheDocument();
+ });
+
+ it("カテゴリ切り替えボタンが動作する", () => {
+ render();
+ const button = screen.getByText("年少人口");
+ fireEvent.click(button);
+ expect(button).toHaveStyle("background-color: #007bff");
+ });
+
+ it("データがない場合にエラーメッセージが表示される", () => {
+ const emptyData = {
+ 総人口: [],
+ 年少人口: [],
+ 生産年齢人口: [],
+ 老年人口: [],
+ };
+ render();
+ expect(
+ screen.getByText("選択中のカテゴリのデータがありません")
+ ).toBeInTheDocument();
+ });
+});
diff --git a/src/tests/PrefectureCheckbox.test.tsx b/src/tests/PrefectureCheckbox.test.tsx
index 0f84690..223997e 100644
--- a/src/tests/PrefectureCheckbox.test.tsx
+++ b/src/tests/PrefectureCheckbox.test.tsx
@@ -1,54 +1,51 @@
import React from "react";
import { render, screen, fireEvent } from "@testing-library/react";
-import PrefectureCheckbox from "./../components/PrefectureCheckbox";
-import { Prefecture } from "../types/prefecture";
+import PrefectureCheckbox from "../components/PrefectureCheckbox";
-const mockPrefectures: Prefecture[] = [
+const mockPrefectures = [
{ prefCode: 1, prefName: "北海道" },
{ prefCode: 2, prefName: "青森県" },
+ { prefCode: 3, prefName: "岩手県" },
];
-describe("PrefectureCheckbox", () => {
- it("都道府県のチェックボックスを表示する", () => {
- render(
- {}}
- />
- );
-
- mockPrefectures.forEach((pref) => {
- expect(screen.getByLabelText(pref.prefName)).toBeInTheDocument();
- });
- });
-
- it("チェックボックスをクリックすると選択状態が変更される", () => {
+describe("PrefectureCheckboxコンポーネント", () => {
+ it("すべて選択ボタンが正しく動作する", () => {
const mockOnSelect = jest.fn();
+ const mockOnSelectAll = jest.fn();
+ const mockOnClearSelection = jest.fn();
+
render(
);
- const checkbox = screen.getByLabelText("北海道");
- fireEvent.click(checkbox);
+ const selectAllButton = screen.getByText("すべて選択");
+ fireEvent.click(selectAllButton);
- expect(mockOnSelect).toHaveBeenCalledWith(["1"]);
+ expect(mockOnSelectAll).toHaveBeenCalledWith([1, 2, 3]);
});
- it("選択済みの都道府県のチェックボックスはチェックされている", () => {
+ it("選択をクリアボタンが正しく動作する", () => {
+ const mockOnSelect = jest.fn();
+ const mockOnSelectAll = jest.fn();
+ const mockOnClearSelection = jest.fn();
+
render(
{}}
+ onSelect={mockOnSelect}
+ onSelectAll={mockOnSelectAll}
+ onClearSelection={mockOnClearSelection}
/>
);
- expect(screen.getByLabelText("北海道")).toBeChecked();
- expect(screen.getByLabelText("青森県")).not.toBeChecked();
+ const clearButton = screen.getByText("選択をクリア");
+ fireEvent.click(clearButton);
+
+ expect(mockOnClearSelection).toHaveBeenCalled();
});
});
diff --git a/src/tests/population.test.ts b/src/tests/population.test.ts
index ef58e3c..0a91fdb 100644
--- a/src/tests/population.test.ts
+++ b/src/tests/population.test.ts
@@ -30,7 +30,7 @@ describe("fetchPopulation", () => {
expect(result).toEqual(mockData);
expect(mockedAxios.get).toHaveBeenCalledWith(
- `${process.env.REACT_APP_YUMEMI_API_URL}/population/${prefCode}`,
+ `${process.env.REACT_APP_YUMEMI_API_URL}/api/v1/population/composition/perYear?prefCode=${prefCode}`,
{
headers: {
"X-API-KEY": process.env.REACT_APP_YUMEMI_API_KEY!,
@@ -48,7 +48,7 @@ describe("fetchPopulation", () => {
"Error fetching population data: Error: API Error"
);
expect(mockedAxios.get).toHaveBeenCalledWith(
- `${process.env.REACT_APP_YUMEMI_API_URL}/population/${prefCode}`,
+ `${process.env.REACT_APP_YUMEMI_API_URL}/api/v1/population/composition/perYear?prefCode=${prefCode}`,
{
headers: {
"X-API-KEY": process.env.REACT_APP_YUMEMI_API_KEY!,
diff --git a/src/tests/prefectures.test.ts b/src/tests/prefectures.test.ts
index f7fb20a..a0997f1 100644
--- a/src/tests/prefectures.test.ts
+++ b/src/tests/prefectures.test.ts
@@ -1,5 +1,5 @@
import axios from "axios";
-import { fetchPrefectures } from "./../api/prefectures";
+import { fetchPrefectures } from "../api/prefectures";
import { Prefecture } from "../types/prefecture";
jest.mock("axios");
@@ -10,20 +10,20 @@ describe("fetchPrefectures", () => {
const mockData: Prefecture[] = [
{ prefCode: 1, prefName: "北海道" },
{ prefCode: 2, prefName: "青森県" },
- { prefCode: 3, prefName: "宮城県" },
+ { prefCode: 3, prefName: "岩手県" },
];
- mockedAxios.get.mockResolvedValue({ status: 200, data: mockData });
+ mockedAxios.get.mockResolvedValue({
+ data: { message: null, result: mockData },
+ });
const result = await fetchPrefectures();
expect(result).toEqual(mockData);
expect(mockedAxios.get).toHaveBeenCalledWith(
- `${process.env.REACT_APP_YUMEMI_API_URL}/prefectures`,
+ `${process.env.REACT_APP_YUMEMI_API_URL}/api/v1/prefectures`,
{
- headers: {
- "X-API-KEY": process.env.REACT_APP_YUMEMI_API_KEY!,
- },
+ headers: { "X-API-KEY": process.env.REACT_APP_YUMEMI_API_KEY },
}
);
});
@@ -31,14 +31,8 @@ describe("fetchPrefectures", () => {
it("APIがエラーを返した場合、エラーをスローする", async () => {
mockedAxios.get.mockRejectedValue(new Error("API Error"));
- await expect(fetchPrefectures()).rejects.toThrow("API Error");
- expect(mockedAxios.get).toHaveBeenCalledWith(
- `${process.env.REACT_APP_YUMEMI_API_URL}/prefectures`,
- {
- headers: {
- "X-API-KEY": process.env.REACT_APP_YUMEMI_API_KEY!,
- },
- }
+ await expect(fetchPrefectures()).rejects.toThrow(
+ "Error fetching prefectures: Error: API Error"
);
});
});
diff --git a/src/tests/prefecturesStore.test.ts b/src/tests/prefecturesStore.test.ts
new file mode 100644
index 0000000..996ce22
--- /dev/null
+++ b/src/tests/prefecturesStore.test.ts
@@ -0,0 +1,55 @@
+import { act } from "react";
+import usePrefecturesStore from "../store/usePrefecturesStore";
+
+describe("都道府県ストアの動作", () => {
+ beforeEach(() => {
+ const { getState, setState } = usePrefecturesStore;
+ setState(() => getState());
+ });
+
+ it("都道府県の選択状態を切り替えられる", () => {
+ const { selectedPrefectures, togglePrefecture } =
+ usePrefecturesStore.getState();
+
+ expect(selectedPrefectures).toEqual([]);
+
+ act(() => {
+ togglePrefecture(1);
+ });
+ expect(usePrefecturesStore.getState().selectedPrefectures).toEqual([1]);
+
+ act(() => {
+ togglePrefecture(1);
+ });
+ expect(usePrefecturesStore.getState().selectedPrefectures).toEqual([]);
+ });
+
+ it("全ての都道府県を選択できる", () => {
+ const { selectedPrefectures, selectAll } = usePrefecturesStore.getState();
+
+ expect(selectedPrefectures).toEqual([]);
+
+ act(() => {
+ selectAll([1, 2, 3]);
+ });
+ expect(usePrefecturesStore.getState().selectedPrefectures).toEqual([
+ 1, 2, 3,
+ ]);
+ });
+
+ it("選択状態を全て解除できる", () => {
+ const { clearSelection, selectAll } = usePrefecturesStore.getState();
+
+ act(() => {
+ selectAll([1, 2, 3]);
+ });
+ expect(usePrefecturesStore.getState().selectedPrefectures).toEqual([
+ 1, 2, 3,
+ ]);
+
+ act(() => {
+ clearSelection();
+ });
+ expect(usePrefecturesStore.getState().selectedPrefectures).toEqual([]);
+ });
+});
diff --git a/src/tests/setupTests.ts b/src/tests/setupTests.ts
index 1dd407a..6c41415 100644
--- a/src/tests/setupTests.ts
+++ b/src/tests/setupTests.ts
@@ -3,3 +3,9 @@
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import "@testing-library/jest-dom";
+
+global.ResizeObserver = class {
+ observe() {}
+ unobserve() {}
+ disconnect() {}
+};
diff --git a/yarn.lock b/yarn.lock
index 0b6ec53..8af7cb4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1054,7 +1054,7 @@
"@babel/plugin-transform-modules-commonjs" "^7.25.9"
"@babel/plugin-transform-typescript" "^7.25.9"
-"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.8.4":
+"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
version "7.26.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1"
integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==
@@ -2170,6 +2170,57 @@
dependencies:
"@types/node" "*"
+"@types/d3-array@^3.0.3":
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-3.2.1.tgz#1f6658e3d2006c4fceac53fde464166859f8b8c5"
+ integrity sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==
+
+"@types/d3-color@*":
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-3.1.3.tgz#368c961a18de721da8200e80bf3943fb53136af2"
+ integrity sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==
+
+"@types/d3-ease@^3.0.0":
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/@types/d3-ease/-/d3-ease-3.0.2.tgz#e28db1bfbfa617076f7770dd1d9a48eaa3b6c51b"
+ integrity sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==
+
+"@types/d3-interpolate@^3.0.1":
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz#412b90e84870285f2ff8a846c6eb60344f12a41c"
+ integrity sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==
+ dependencies:
+ "@types/d3-color" "*"
+
+"@types/d3-path@*":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-3.1.0.tgz#2b907adce762a78e98828f0b438eaca339ae410a"
+ integrity sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==
+
+"@types/d3-scale@^4.0.2":
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-4.0.8.tgz#d409b5f9dcf63074464bf8ddfb8ee5a1f95945bb"
+ integrity sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==
+ dependencies:
+ "@types/d3-time" "*"
+
+"@types/d3-shape@^3.1.0":
+ version "3.1.6"
+ resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-3.1.6.tgz#65d40d5a548f0a023821773e39012805e6e31a72"
+ integrity sha512-5KKk5aKGu2I+O6SONMYSNflgiP0WfZIQvVUMan50wHsLG1G94JlxEVnCpQARfTtzytuY0p/9PXXZb3I7giofIA==
+ dependencies:
+ "@types/d3-path" "*"
+
+"@types/d3-time@*", "@types/d3-time@^3.0.0":
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-3.0.4.tgz#8472feecd639691450dd8000eb33edd444e1323f"
+ integrity sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==
+
+"@types/d3-timer@^3.0.0":
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/@types/d3-timer/-/d3-timer-3.0.2.tgz#70bbda77dc23aa727413e22e214afa3f0e852f70"
+ integrity sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==
+
"@types/eslint-scope@^3.7.7":
version "3.7.7"
resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5"
@@ -3633,6 +3684,11 @@ cliui@^8.0.1:
strip-ansi "^6.0.1"
wrap-ansi "^7.0.0"
+clsx@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999"
+ integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
+
co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
@@ -4061,6 +4117,77 @@ csstype@^3.0.2:
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
+"d3-array@2 - 3", "d3-array@2.10.0 - 3", d3-array@^3.1.6:
+ version "3.2.4"
+ resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.4.tgz#15fec33b237f97ac5d7c986dc77da273a8ed0bb5"
+ integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==
+ dependencies:
+ internmap "1 - 2"
+
+"d3-color@1 - 3":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2"
+ integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==
+
+d3-ease@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-3.0.1.tgz#9658ac38a2140d59d346160f1f6c30fda0bd12f4"
+ integrity sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==
+
+"d3-format@1 - 3":
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-3.1.0.tgz#9260e23a28ea5cb109e93b21a06e24e2ebd55641"
+ integrity sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==
+
+"d3-interpolate@1.2.0 - 3", d3-interpolate@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz#3c47aa5b32c5b3dfb56ef3fd4342078a632b400d"
+ integrity sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==
+ dependencies:
+ d3-color "1 - 3"
+
+d3-path@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-3.1.0.tgz#22df939032fb5a71ae8b1800d61ddb7851c42526"
+ integrity sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==
+
+d3-scale@^4.0.2:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-4.0.2.tgz#82b38e8e8ff7080764f8dcec77bd4be393689396"
+ integrity sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==
+ dependencies:
+ d3-array "2.10.0 - 3"
+ d3-format "1 - 3"
+ d3-interpolate "1.2.0 - 3"
+ d3-time "2.1.1 - 3"
+ d3-time-format "2 - 4"
+
+d3-shape@^3.1.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-3.2.0.tgz#a1a839cbd9ba45f28674c69d7f855bcf91dfc6a5"
+ integrity sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==
+ dependencies:
+ d3-path "^3.1.0"
+
+"d3-time-format@2 - 4":
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a"
+ integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==
+ dependencies:
+ d3-time "1 - 3"
+
+"d3-time@1 - 3", "d3-time@2.1.1 - 3", d3-time@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.1.0.tgz#9310db56e992e3c0175e1ef385e545e48a9bb5c7"
+ integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==
+ dependencies:
+ d3-array "2 - 3"
+
+d3-timer@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-3.0.1.tgz#6284d2a2708285b1abb7e201eda4380af35e63b0"
+ integrity sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==
+
damerau-levenshtein@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
@@ -4132,6 +4259,11 @@ debug@^3.2.7:
dependencies:
ms "^2.1.1"
+decimal.js-light@^2.4.1:
+ version "2.5.1"
+ resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934"
+ integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==
+
decimal.js@^10.2.1, decimal.js@^10.4.2:
version "10.4.3"
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
@@ -4295,6 +4427,14 @@ dom-converter@^0.2.0:
dependencies:
utila "~0.4"
+dom-helpers@^5.0.1:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
+ integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==
+ dependencies:
+ "@babel/runtime" "^7.8.7"
+ csstype "^3.0.2"
+
dom-serializer@0:
version "0.2.2"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
@@ -5008,7 +5148,7 @@ etag@~1.8.1:
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
-eventemitter3@^4.0.0:
+eventemitter3@^4.0.0, eventemitter3@^4.0.1:
version "4.0.7"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
@@ -5101,6 +5241,11 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+fast-equals@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-5.0.1.tgz#a4eefe3c5d1c0d021aeed0bc10ba5e0c12ee405d"
+ integrity sha512-WF1Wi8PwwSY7/6Kx0vKXtw8RwuSGoM1bvDaJbu7MxDlR1vovZjIAKrnzyrThgAjm6JDTu0fVgWXDlMGspodfoQ==
+
fast-glob@^3.2.9, fast-glob@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
@@ -5872,6 +6017,11 @@ internal-slot@^1.0.7:
hasown "^2.0.0"
side-channel "^1.0.4"
+"internmap@1 - 2":
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009"
+ integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==
+
ipaddr.js@1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
@@ -8705,7 +8855,7 @@ prompts@^2.0.1, prompts@^2.4.2:
kleur "^3.0.3"
sisteransi "^1.0.5"
-prop-types@^15.8.1:
+prop-types@^15.6.2, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -8859,7 +9009,7 @@ react-is@^17.0.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
-react-is@^18.0.0:
+react-is@^18.0.0, react-is@^18.3.1:
version "18.3.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e"
integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==
@@ -8924,6 +9074,25 @@ react-scripts@5.0.1:
optionalDependencies:
fsevents "^2.3.2"
+react-smooth@^4.0.0:
+ version "4.0.4"
+ resolved "https://registry.yarnpkg.com/react-smooth/-/react-smooth-4.0.4.tgz#a5875f8bb61963ca61b819cedc569dc2453894b4"
+ integrity sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==
+ dependencies:
+ fast-equals "^5.0.1"
+ prop-types "^15.8.1"
+ react-transition-group "^4.4.5"
+
+react-transition-group@^4.4.5:
+ version "4.4.5"
+ resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1"
+ integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==
+ dependencies:
+ "@babel/runtime" "^7.5.5"
+ dom-helpers "^5.0.1"
+ loose-envify "^1.4.0"
+ prop-types "^15.6.2"
+
react@^19.0.0:
version "19.0.0"
resolved "https://registry.yarnpkg.com/react/-/react-19.0.0.tgz#6e1969251b9f108870aa4bff37a0ce9ddfaaabdd"
@@ -8965,6 +9134,27 @@ readdirp@~3.6.0:
dependencies:
picomatch "^2.2.1"
+recharts-scale@^0.4.4:
+ version "0.4.5"
+ resolved "https://registry.yarnpkg.com/recharts-scale/-/recharts-scale-0.4.5.tgz#0969271f14e732e642fcc5bd4ab270d6e87dd1d9"
+ integrity sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==
+ dependencies:
+ decimal.js-light "^2.4.1"
+
+recharts@^2.15.0:
+ version "2.15.0"
+ resolved "https://registry.yarnpkg.com/recharts/-/recharts-2.15.0.tgz#0b77bff57a43885df9769ae649a14cb1a7fe19aa"
+ integrity sha512-cIvMxDfpAmqAmVgc4yb7pgm/O1tmmkl/CjrvXuW+62/+7jj/iF9Ykm+hb/UJt42TREHMyd3gb+pkgoa2MxgDIw==
+ dependencies:
+ clsx "^2.0.0"
+ eventemitter3 "^4.0.1"
+ lodash "^4.17.21"
+ react-is "^18.3.1"
+ react-smooth "^4.0.0"
+ recharts-scale "^0.4.4"
+ tiny-invariant "^1.3.1"
+ victory-vendor "^36.6.8"
+
recursive-readdir@^2.2.2:
version "2.2.3"
resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.3.tgz#e726f328c0d69153bcabd5c322d3195252379372"
@@ -10033,6 +10223,11 @@ thunky@^1.0.2:
resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d"
integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
+tiny-invariant@^1.3.1:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz#46680b7a873a0d5d10005995eb90a70d74d60127"
+ integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==
+
tmpl@1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc"
@@ -10397,6 +10592,26 @@ vary@~1.1.2:
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
+victory-vendor@^36.6.8:
+ version "36.9.2"
+ resolved "https://registry.yarnpkg.com/victory-vendor/-/victory-vendor-36.9.2.tgz#668b02a448fa4ea0f788dbf4228b7e64669ff801"
+ integrity sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==
+ dependencies:
+ "@types/d3-array" "^3.0.3"
+ "@types/d3-ease" "^3.0.0"
+ "@types/d3-interpolate" "^3.0.1"
+ "@types/d3-scale" "^4.0.2"
+ "@types/d3-shape" "^3.1.0"
+ "@types/d3-time" "^3.0.0"
+ "@types/d3-timer" "^3.0.0"
+ d3-array "^3.1.6"
+ d3-ease "^3.0.1"
+ d3-interpolate "^3.0.1"
+ d3-scale "^4.0.2"
+ d3-shape "^3.1.0"
+ d3-time "^3.0.0"
+ d3-timer "^3.0.1"
+
w3c-hr-time@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd"
@@ -11013,3 +11228,8 @@ yocto-queue@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
+
+zustand@^5.0.2:
+ version "5.0.2"
+ resolved "https://registry.yarnpkg.com/zustand/-/zustand-5.0.2.tgz#f7595ada55a565f1fd6464f002a91e701ee0cfca"
+ integrity sha512-8qNdnJVJlHlrKXi50LDqqUNmUbuBjoKLrYQBnoChIbVph7vni+sY+YpvdjXG9YLd/Bxr6scMcR+rm5H3aSqPaw==