frame.select 1.0.8 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/App.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../src/App.tsx"],"names":[],"mappings":"AACA,OAAO,WAAW,CAAC;AAInB,OAAO,KAAK,MAAM,OAAO,CAAC;AAS1B,iBAAS,GAAG,sBA0FX;AAED,eAAe,GAAG,CAAC"}
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../src/App.tsx"],"names":[],"mappings":"AACA,OAAO,WAAW,CAAC;AAInB,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,iBAAS,GAAG,sBAuGX;AAED,eAAe,GAAG,CAAC"}
package/dist/App.js CHANGED
@@ -3,6 +3,7 @@ import './App.css';
3
3
  import ItemList from './components/ItemList';
4
4
  import { useState } from 'react';
5
5
  import React from 'react';
6
+ import { BrandList } from './components/BrandList';
6
7
  function App() {
7
8
  const brands = [
8
9
  {
@@ -10,36 +11,42 @@ function App() {
10
11
  id: 1,
11
12
  description: 'Description of Adidas',
12
13
  logo: 'https://via.placeholder.com/150',
14
+ item: ['Adidas'],
13
15
  },
14
16
  {
15
17
  name: 'Nike',
16
18
  id: 2,
17
19
  description: 'Description of Nike',
18
20
  logo: 'https://via.placeholder.com/150',
21
+ item: ['Nike'],
19
22
  },
20
23
  {
21
24
  name: 'Puma',
22
25
  id: 3,
23
26
  description: 'Description of Puma',
24
27
  logo: 'https://via.placeholder.com/150',
28
+ item: ['Puma'],
25
29
  },
26
30
  {
27
31
  name: 'Reebok',
28
32
  id: 4,
29
33
  description: 'Description of Reebok',
30
34
  logo: 'https://via.placeholder.com/150',
35
+ item: ['Reebok'],
31
36
  },
32
37
  {
33
38
  name: 'Under Armour',
34
39
  id: 5,
35
40
  description: 'Description of Under Armour',
36
41
  logo: 'https://via.placeholder.com/150',
42
+ item: ['Under Armour'],
37
43
  },
38
44
  {
39
45
  name: 'New Balance',
40
46
  id: 6,
41
47
  description: 'Description of New Balance',
42
48
  logo: 'https://via.placeholder.com/150',
49
+ item: ['New Balance'],
43
50
  },
44
51
  ];
45
52
  const columnHelper = createColumnHelper();
@@ -74,6 +81,7 @@ function App() {
74
81
  const renderItem = (item) => React.createElement("h4", null, item);
75
82
  return (React.createElement(React.Fragment, null,
76
83
  React.createElement("h1", null, "Brand List"),
77
- React.createElement(ItemList, { itemList: brands, renderItem: renderItem, clickFunc: onClickItem, itemFilter: tItemFilter, extraColumns: colWidth })));
84
+ React.createElement(ItemList, { itemList: brands, renderItem: renderItem, clickFunc: onClickItem, itemFilter: tItemFilter, extraColumns: colWidth }),
85
+ React.createElement(BrandList, { brandList: brands, renderItem: renderItem, clickFunc: onClickItem, extraColumns: colWidth })));
78
86
  }
79
87
  export default App;
@@ -0,0 +1,6 @@
1
+ import { Column } from '@tanstack/react-table';
2
+ import React from 'react';
3
+ export declare function BrandFilter({ column }: {
4
+ column: Column<any, unknown>;
5
+ }): React.JSX.Element;
6
+ //# sourceMappingURL=BrandFilter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BrandFilter.d.ts","sourceRoot":"","sources":["../../src/components/BrandFilter.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAI/C,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;CAAE,qBAgDvE"}
@@ -0,0 +1,32 @@
1
+ import { useMemo } from 'react';
2
+ import { DebouncedInput } from './DebounceInput';
3
+ // import { Message } from '../../../../common/commands';
4
+ import React from 'react';
5
+ export function BrandFilter({ column }) {
6
+ const columnFilterValue = column.getFilterValue();
7
+ const [value, setFilterValue] = React.useState('');
8
+ // const { filterVariant } = column.columnDef.meta ?? {};
9
+ React.useEffect(() => {
10
+ console.log(`has changed`);
11
+ setFilterValue(columnFilterValue);
12
+ }, [columnFilterValue]);
13
+ React.useEffect(() => {
14
+ console.log(`BrandFilter.CommandDBBrandFilter`);
15
+ // window.electron.ipcRenderer.on(
16
+ // Message.CommandDBBrandFilter,
17
+ // (_event: unknown, data: string) => {
18
+ // setVal(data);
19
+ // setFilterValue(data);
20
+ // }
21
+ // );
22
+ }, []);
23
+ // setFilterValue((columnFilterValue ?? '') as string);
24
+ const setVal = React.useCallback((str) => {
25
+ column.setFilterValue(str);
26
+ }, [column]);
27
+ return useMemo(() => (React.createElement(DebouncedInput, { className: "w-36 border shadow rounded", title: "brand", onChange: (value) => {
28
+ document.body.style.cursor = 'wait';
29
+ setVal(value);
30
+ document.body.style.cursor = 'default';
31
+ }, placeholder: `Search...`, type: "text", value: value })), [setVal, value]);
32
+ }
@@ -0,0 +1,13 @@
1
+ import { Cell } from '@tanstack/react-table';
2
+ import { TColumn } from './itemTypes';
3
+ import { JSX } from 'react';
4
+ import React from 'react';
5
+ import { TBrand } from './types';
6
+ export declare function BrandList(props: {
7
+ brandList: TBrand[];
8
+ renderItem?: (item: string) => React.ReactNode;
9
+ clickFunc?: (item: Cell<TBrand, unknown>) => void;
10
+ extraColumns?: TColumn<TBrand>[];
11
+ style?: React.CSSProperties;
12
+ }): JSX.Element;
13
+ //# sourceMappingURL=BrandList.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BrandList.d.ts","sourceRoot":"","sources":["../../src/components/BrandList.tsx"],"names":[],"mappings":"AASA,OAAO,EAIL,IAAI,EAGL,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAe,OAAO,EAAS,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAE,GAAG,EAA6C,MAAM,OAAO,CAAC;AACvE,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEjC,wBAAgB,SAAS,CAAC,KAAK,EAAE;IAC/B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK,CAAC,SAAS,CAAC;IAC/C,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IAClD,YAAY,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B,GAAG,GAAG,CAAC,OAAO,CAgLd"}
@@ -0,0 +1,111 @@
1
+ import { TableContainer, Table, TableHead, TableRow, TableCell, TableBody, Button, } from '@mui/material';
2
+ import { useReactTable, getCoreRowModel, getFilteredRowModel, createColumnHelper, } from '@tanstack/react-table';
3
+ import ItemFilter from './ItemFilter';
4
+ import { useMemo, useState, useCallback } from 'react';
5
+ import React from 'react';
6
+ export function BrandList(props) {
7
+ 'use no memo'; // For React Compiler compatibility with TanStack Table
8
+ const [filter, setFilter] = useState('');
9
+ const filterMatches = useCallback((item, fltr) => {
10
+ const match = item.name
11
+ .toLowerCase()
12
+ .includes(fltr ? fltr.toLowerCase() : '');
13
+ return match;
14
+ }, []);
15
+ const tBrandFilter = useMemo(() => ({
16
+ property: 'name',
17
+ filter,
18
+ setFilter,
19
+ filterMatches,
20
+ placeholder: 'Brands',
21
+ }), [filter, filterMatches]);
22
+ const serializedExtraColumns = useMemo(() => JSON.stringify(props.extraColumns), [props.extraColumns]);
23
+ const extraCols = useMemo(() => {
24
+ if (props.extraColumns) {
25
+ return props.extraColumns.filter((col) => col !== undefined);
26
+ }
27
+ else {
28
+ return [];
29
+ }
30
+ // eslint-disable-next-line react-hooks/exhaustive-deps
31
+ }, [serializedExtraColumns]); // Use the serialized version as a dependency
32
+ const filteredItems = useMemo(() => {
33
+ if (!tBrandFilter) {
34
+ return props.brandList;
35
+ }
36
+ let filteredList = props.brandList;
37
+ if (tBrandFilter.filterMatches) {
38
+ filteredList = props.brandList.filter((item) => tBrandFilter.filterMatches(item, tBrandFilter.filter));
39
+ }
40
+ extraCols.forEach((col) => {
41
+ if (col?.filter) {
42
+ filteredList = filteredList.filter((item) => col.filter?.filterMatches(item, col.filter.filter));
43
+ }
44
+ });
45
+ // tBrandFilter.filterEvent?.(filteredList)
46
+ return filteredList;
47
+ }, [tBrandFilter, props.brandList, extraCols]);
48
+ const defaultHeader = 'Name';
49
+ const columnHelper = createColumnHelper();
50
+ const defaultColumns = [
51
+ columnHelper.accessor((row) => row.name, {
52
+ header: defaultHeader,
53
+ cell: (info) => (React.createElement("div", { style: { fontWeight: 'bold' } }, info.getValue())),
54
+ footer: (info) => info.column.id,
55
+ }),
56
+ ];
57
+ const columns = [...defaultColumns];
58
+ extraCols.forEach((col) => {
59
+ if (col) {
60
+ columns.push(col.column);
61
+ }
62
+ });
63
+ const onClick = (cell) => () => {
64
+ if (props.clickFunc) {
65
+ props.clickFunc?.(cell);
66
+ }
67
+ };
68
+ function displayHeader(header) {
69
+ let filter = React.createElement("div", null);
70
+ if (header.id === defaultHeader) {
71
+ if (tBrandFilter) {
72
+ filter = (React.createElement("div", { style: { width: '100%' } },
73
+ React.createElement(ItemFilter, { itemFilter: tBrandFilter })));
74
+ }
75
+ }
76
+ else {
77
+ extraCols.forEach((col) => {
78
+ if (col && header.id === col.column.header) {
79
+ filter = col.filter ? (React.createElement("div", { style: { width: '100%' } },
80
+ React.createElement(ItemFilter, { itemFilter: col.filter }))) : (React.createElement("div", null));
81
+ }
82
+ });
83
+ }
84
+ return filter;
85
+ }
86
+ function displayBody(cell) {
87
+ let cellContent = React.createElement("div", null);
88
+ const cll = cell.getValue();
89
+ if (cell.column.columnDef.header === defaultHeader) {
90
+ cellContent = (React.createElement(TableCell, { style: { backgroundColor: 'white', padding: 2 }, key: cell.id },
91
+ React.createElement(Button, { onClick: onClick(cell) }, props.renderItem ? props.renderItem(cll) : React.createElement("div", null, cll))));
92
+ }
93
+ else {
94
+ cellContent = (React.createElement(TableCell, { style: { backgroundColor: 'white', padding: 2 }, key: cell.id }, props.renderItem ? props.renderItem(cll) : React.createElement("div", null, cll)));
95
+ }
96
+ return cellContent;
97
+ }
98
+ const table = useReactTable({
99
+ data: filteredItems,
100
+ columns,
101
+ getCoreRowModel: getCoreRowModel(),
102
+ getFilteredRowModel: getFilteredRowModel(),
103
+ });
104
+ return (React.createElement(TableContainer, { "data-testid": "frame.select-TableContainer", style: props.style },
105
+ React.createElement(Table, { stickyHeader: true, "aria-label": "sticky table" },
106
+ React.createElement(TableHead, null, table.getHeaderGroups().map((headerGroup) => (React.createElement(TableRow, { key: headerGroup.id, "data-testid": headerGroup.id }, headerGroup.headers.map((header) => (React.createElement(TableCell, { key: header.id }, displayHeader(header)))))))),
107
+ React.createElement(TableBody, null, table.getRowModel().rows.map((row) => (React.createElement(TableRow, { key: row.id }, row.getVisibleCells().map((cell) => {
108
+ // console.log(`cell: ${JSON.stringify(cell, null)}`)
109
+ return displayBody(cell);
110
+ }))))))));
111
+ }
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ export declare function DebouncedInput({ value: initialValue, title, onChange, debounce, }: {
3
+ value: string | number;
4
+ title: string;
5
+ onChange: (value: string | number) => void;
6
+ debounce?: number;
7
+ } & Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange'>): React.JSX.Element;
8
+ //# sourceMappingURL=DebounceInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DebounceInput.d.ts","sourceRoot":"","sources":["../../src/components/DebounceInput.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EAAE,YAAY,EACnB,KAAK,EACL,QAAQ,EACR,QAAY,GACb,EAAE;IACD,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,CAAC;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,UAAU,CAAC,qBAkEhE"}
@@ -0,0 +1,41 @@
1
+ import { Box, Button, TextField } from '@mui/material';
2
+ import CancelPresentationRounded from '@mui/icons-material/CancelPresentationRounded';
3
+ import React from 'react';
4
+ // import { Message } from '../../../../common/commands';
5
+ export function DebouncedInput({ value: initialValue, title, onChange, debounce = 0, }) {
6
+ const [value, setValue] = React.useState(initialValue);
7
+ React.useEffect(() => {
8
+ console.log(`DebounceInput.clearTextField`);
9
+ // window.electron.ipcRenderer.on(
10
+ // Message.CommandDBBrandClearFilter,
11
+ // (_event: unknown, _data: string) => {
12
+ // clearTextField();
13
+ // }
14
+ // );
15
+ }, []);
16
+ React.useEffect(() => {
17
+ setValue(initialValue);
18
+ }, [initialValue]);
19
+ React.useEffect(() => {
20
+ const timeout = setTimeout(() => {
21
+ onChange(value);
22
+ }, debounce);
23
+ return () => clearTimeout(timeout);
24
+ }, [value]);
25
+ function clearTextField() {
26
+ setValue('');
27
+ }
28
+ return (React.createElement("div", null,
29
+ React.createElement(Box, { sx: { display: 'flex', alignItems: 'flex-end' } },
30
+ React.createElement(Button, { "data-testid": `button.clear-${title}`, style: {
31
+ height: '100%',
32
+ display: 'flex',
33
+ alignItems: 'center',
34
+ marginLeft: '8px',
35
+ }, onClick: clearTextField },
36
+ React.createElement(CancelPresentationRounded, null)),
37
+ React.createElement(TextField, { id: "input-with-sx", label: title, variant: "standard", value: value, onChange: (e) => {
38
+ console.log(e.target.value);
39
+ setValue(e.target.value);
40
+ }, sx: { width: '100px' } }))));
41
+ }
@@ -32,7 +32,7 @@ const ItemFilter = (props) => {
32
32
  props.itemFilter.setFilter('');
33
33
  }
34
34
  return (React.createElement("div", { style: { display: 'flex', alignItems: 'center' } },
35
- React.createElement("input", { type: "text", placeholder: props.itemFilter.placeholder ?? 'Items', value: props.itemFilter.filter, onChange: handleInputChange, style: { width: '80%' } }),
35
+ React.createElement("input", { type: "text", placeholder: props.itemFilter.placeholder ?? 'Items', value: props.itemFilter.filter || '', onChange: handleInputChange, style: { width: '80%' } }),
36
36
  props.itemFilter.filter !== '' ? (React.createElement(Button, { style: { minWidth: 'unset', padding: '1px' }, onClick: clearFilter },
37
37
  React.createElement(CancelPresentationIcon, null))) : (React.createElement("div", null))));
38
38
  };
@@ -0,0 +1,7 @@
1
+ export type TBrand = {
2
+ name: string;
3
+ id: number;
4
+ description: string;
5
+ logo: string;
6
+ };
7
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/components/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,MAAM,GAAG;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
1
  export { default as ItemList } from './components/ItemList';
2
2
  export type { TColumn, TItem, TItemFilter } from './components/itemTypes';
3
+ export { BrandList } from './components/BrandList';
4
+ export type { TBrand } from './components/types';
3
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAC5D,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAC5D,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,YAAY,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -1 +1,2 @@
1
1
  export { default as ItemList } from './components/ItemList';
2
+ export { BrandList } from './components/BrandList';
@@ -1 +1 @@
1
- {"root":["../src/app.tsx","../src/index.ts","../src/main.tsx","../src/vite-env.d.ts","../src/components/itemfilter.test.tsx","../src/components/itemfilter.tsx","../src/components/itemlist.test.tsx","../src/components/itemlist.tsx","../src/components/itemtypes.ts"],"version":"5.9.2"}
1
+ {"root":["../src/app.tsx","../src/index.ts","../src/main.tsx","../src/vite-env.d.ts","../src/components/brandfilter.tsx","../src/components/brandlist.tsx","../src/components/debounceinput.tsx","../src/components/itemfilter.test.tsx","../src/components/itemfilter.tsx","../src/components/itemlist.test.tsx","../src/components/itemlist.tsx","../src/components/itemtypes.ts","../src/components/types.ts"],"version":"5.9.3"}
package/package.json CHANGED
@@ -1,48 +1,52 @@
1
- {
2
- "name": "frame.select",
3
- "version": "1.0.8",
4
- "description": "A React component for selecting items from a list with a search bar.",
5
- "license": "MIT",
6
- "type": "module",
7
- "main": "dist/index.js",
8
- "types": "dist/index.d.ts",
9
- "files": [
10
- "dist/",
11
- "README.md"
12
- ],
13
- "scripts": {
14
- "dev": "vite",
15
- "build": "tsc -b",
16
- "build-and-vite": "tsc -b && vite build",
17
- "lint": "eslint .",
18
- "test": "vitest --config vitest.config.ts",
19
- "preview": "vite preview"
20
- },
21
- "dependencies": {
22
- "@emotion/react": "^11.14.0",
23
- "@emotion/styled": "^11.14.1",
24
- "@mui/icons-material": "^7.3.1",
25
- "@mui/material": "^7.3.1",
26
- "@tanstack/react-table": "^8.21.3",
27
- "react": "^18.3.1",
28
- "react-dom": "^18.3.1"
29
- },
30
- "devDependencies": {
31
- "@eslint/js": "^9.34.0",
32
- "@testing-library/dom": "^10.4.1",
33
- "@testing-library/jest-dom": "^6.8.0",
34
- "@testing-library/react": "^16.2.0",
35
- "@types/react": "^18.3.3",
36
- "@types/react-dom": "^18.3.0",
37
- "@vitejs/plugin-react": "^5.0.1",
38
- "eslint": "^9.34.0",
39
- "eslint-plugin-react-hooks": "^5.1.0",
40
- "eslint-plugin-react-refresh": "^0.4.20",
41
- "globals": "^16.3.0",
42
- "jsdom": "^26.1.0",
43
- "typescript": "~5.9.2",
44
- "typescript-eslint": "^8.40.0",
45
- "vite": "^7.1.3",
46
- "vitest": "^3.2.4"
47
- }
48
- }
1
+ {
2
+ "name": "frame.select",
3
+ "version": "1.1.0",
4
+ "description": "A React component for selecting items from a list with a search bar.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "files": [
10
+ "dist/",
11
+ "README.md"
12
+ ],
13
+ "scripts": {
14
+ "dev": "vite",
15
+ "build": "tsc -b",
16
+ "build-and-vite": "tsc -b && vite build",
17
+ "lint": "eslint .",
18
+ "test": "vitest --config vitest.config.ts",
19
+ "preview": "vite preview"
20
+ },
21
+ "dependencies": {
22
+ "@emotion/react": "^11.14.0",
23
+ "@emotion/styled": "^11.14.1",
24
+ "@mui/icons-material": "^7.3.6",
25
+ "@mui/material": "^7.3.6",
26
+ "@tanstack/react-table": "^8.21.3"
27
+ },
28
+ "peerDependencies": {
29
+ "react": "^19.2.3",
30
+ "react-dom": "^19.2.3"
31
+ },
32
+ "devDependencies": {
33
+ "react": "^19.2.3",
34
+ "react-dom": "^19.2.3",
35
+ "@eslint/js": "^9.39.1",
36
+ "@testing-library/dom": "^10.4.1",
37
+ "@testing-library/jest-dom": "^6.9.1",
38
+ "@testing-library/react": "^16.2.0",
39
+ "@types/react": "^19.2.7",
40
+ "@types/react-dom": "^19.2.3",
41
+ "@vitejs/plugin-react": "^5.1.2",
42
+ "eslint": "^9.39.1",
43
+ "eslint-plugin-react-hooks": "^7.0.1",
44
+ "eslint-plugin-react-refresh": "^0.4.24",
45
+ "globals": "^16.5.0",
46
+ "jsdom": "^27.3.0",
47
+ "typescript": "~5.9.3",
48
+ "typescript-eslint": "^8.49.0",
49
+ "vite": "^7.2.7",
50
+ "vitest": "^4.0.15"
51
+ }
52
+ }