Spaces:
Running
Running
Commit
•
1ae14ee
1
Parent(s):
6108f36
updates
Browse files- package-lock.json +457 -0
- package.json +4 -0
- src/App.tsx +27 -34
- src/components/ui/badge.tsx +29 -0
- src/components/ui/command.tsx +153 -0
- src/components/ui/dialog.tsx +120 -0
- src/components/ui/multi-select.tsx +278 -0
- src/components/ui/popover.tsx +29 -0
- src/components/ui/select.tsx +0 -143
- src/components/ui/separator.tsx +29 -0
package-lock.json
CHANGED
@@ -10,10 +10,14 @@
|
|
10 |
"dependencies": {
|
11 |
"@radix-ui/react-checkbox": "^1.1.1",
|
12 |
"@radix-ui/react-collapsible": "^1.1.0",
|
|
|
|
|
13 |
"@radix-ui/react-select": "^2.1.1",
|
|
|
14 |
"@radix-ui/react-slot": "^1.1.0",
|
15 |
"class-variance-authority": "^0.7.0",
|
16 |
"clsx": "^2.1.1",
|
|
|
17 |
"lucide-react": "^0.399.0",
|
18 |
"react": "^18.3.1",
|
19 |
"react-dom": "^18.3.1",
|
@@ -1243,6 +1247,41 @@
|
|
1243 |
}
|
1244 |
}
|
1245 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1246 |
"node_modules/@radix-ui/react-direction": {
|
1247 |
"version": "1.1.0",
|
1248 |
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz",
|
@@ -1338,6 +1377,42 @@
|
|
1338 |
}
|
1339 |
}
|
1340 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1341 |
"node_modules/@radix-ui/react-popper": {
|
1342 |
"version": "1.2.0",
|
1343 |
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz",
|
@@ -1479,6 +1554,28 @@
|
|
1479 |
}
|
1480 |
}
|
1481 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1482 |
"node_modules/@radix-ui/react-slot": {
|
1483 |
"version": "1.1.0",
|
1484 |
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
@@ -2507,6 +2604,366 @@
|
|
2507 |
"node": ">=6"
|
2508 |
}
|
2509 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2510 |
"node_modules/color-convert": {
|
2511 |
"version": "1.9.3",
|
2512 |
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
|
|
10 |
"dependencies": {
|
11 |
"@radix-ui/react-checkbox": "^1.1.1",
|
12 |
"@radix-ui/react-collapsible": "^1.1.0",
|
13 |
+
"@radix-ui/react-dialog": "^1.1.1",
|
14 |
+
"@radix-ui/react-popover": "^1.1.1",
|
15 |
"@radix-ui/react-select": "^2.1.1",
|
16 |
+
"@radix-ui/react-separator": "^1.1.0",
|
17 |
"@radix-ui/react-slot": "^1.1.0",
|
18 |
"class-variance-authority": "^0.7.0",
|
19 |
"clsx": "^2.1.1",
|
20 |
+
"cmdk": "^1.0.0",
|
21 |
"lucide-react": "^0.399.0",
|
22 |
"react": "^18.3.1",
|
23 |
"react-dom": "^18.3.1",
|
|
|
1247 |
}
|
1248 |
}
|
1249 |
},
|
1250 |
+
"node_modules/@radix-ui/react-dialog": {
|
1251 |
+
"version": "1.1.1",
|
1252 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.1.tgz",
|
1253 |
+
"integrity": "sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg==",
|
1254 |
+
"dependencies": {
|
1255 |
+
"@radix-ui/primitive": "1.1.0",
|
1256 |
+
"@radix-ui/react-compose-refs": "1.1.0",
|
1257 |
+
"@radix-ui/react-context": "1.1.0",
|
1258 |
+
"@radix-ui/react-dismissable-layer": "1.1.0",
|
1259 |
+
"@radix-ui/react-focus-guards": "1.1.0",
|
1260 |
+
"@radix-ui/react-focus-scope": "1.1.0",
|
1261 |
+
"@radix-ui/react-id": "1.1.0",
|
1262 |
+
"@radix-ui/react-portal": "1.1.1",
|
1263 |
+
"@radix-ui/react-presence": "1.1.0",
|
1264 |
+
"@radix-ui/react-primitive": "2.0.0",
|
1265 |
+
"@radix-ui/react-slot": "1.1.0",
|
1266 |
+
"@radix-ui/react-use-controllable-state": "1.1.0",
|
1267 |
+
"aria-hidden": "^1.1.1",
|
1268 |
+
"react-remove-scroll": "2.5.7"
|
1269 |
+
},
|
1270 |
+
"peerDependencies": {
|
1271 |
+
"@types/react": "*",
|
1272 |
+
"@types/react-dom": "*",
|
1273 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
1274 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
1275 |
+
},
|
1276 |
+
"peerDependenciesMeta": {
|
1277 |
+
"@types/react": {
|
1278 |
+
"optional": true
|
1279 |
+
},
|
1280 |
+
"@types/react-dom": {
|
1281 |
+
"optional": true
|
1282 |
+
}
|
1283 |
+
}
|
1284 |
+
},
|
1285 |
"node_modules/@radix-ui/react-direction": {
|
1286 |
"version": "1.1.0",
|
1287 |
"resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz",
|
|
|
1377 |
}
|
1378 |
}
|
1379 |
},
|
1380 |
+
"node_modules/@radix-ui/react-popover": {
|
1381 |
+
"version": "1.1.1",
|
1382 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.1.tgz",
|
1383 |
+
"integrity": "sha512-3y1A3isulwnWhvTTwmIreiB8CF4L+qRjZnK1wYLO7pplddzXKby/GnZ2M7OZY3qgnl6p9AodUIHRYGXNah8Y7g==",
|
1384 |
+
"dependencies": {
|
1385 |
+
"@radix-ui/primitive": "1.1.0",
|
1386 |
+
"@radix-ui/react-compose-refs": "1.1.0",
|
1387 |
+
"@radix-ui/react-context": "1.1.0",
|
1388 |
+
"@radix-ui/react-dismissable-layer": "1.1.0",
|
1389 |
+
"@radix-ui/react-focus-guards": "1.1.0",
|
1390 |
+
"@radix-ui/react-focus-scope": "1.1.0",
|
1391 |
+
"@radix-ui/react-id": "1.1.0",
|
1392 |
+
"@radix-ui/react-popper": "1.2.0",
|
1393 |
+
"@radix-ui/react-portal": "1.1.1",
|
1394 |
+
"@radix-ui/react-presence": "1.1.0",
|
1395 |
+
"@radix-ui/react-primitive": "2.0.0",
|
1396 |
+
"@radix-ui/react-slot": "1.1.0",
|
1397 |
+
"@radix-ui/react-use-controllable-state": "1.1.0",
|
1398 |
+
"aria-hidden": "^1.1.1",
|
1399 |
+
"react-remove-scroll": "2.5.7"
|
1400 |
+
},
|
1401 |
+
"peerDependencies": {
|
1402 |
+
"@types/react": "*",
|
1403 |
+
"@types/react-dom": "*",
|
1404 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
1405 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
1406 |
+
},
|
1407 |
+
"peerDependenciesMeta": {
|
1408 |
+
"@types/react": {
|
1409 |
+
"optional": true
|
1410 |
+
},
|
1411 |
+
"@types/react-dom": {
|
1412 |
+
"optional": true
|
1413 |
+
}
|
1414 |
+
}
|
1415 |
+
},
|
1416 |
"node_modules/@radix-ui/react-popper": {
|
1417 |
"version": "1.2.0",
|
1418 |
"resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz",
|
|
|
1554 |
}
|
1555 |
}
|
1556 |
},
|
1557 |
+
"node_modules/@radix-ui/react-separator": {
|
1558 |
+
"version": "1.1.0",
|
1559 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.0.tgz",
|
1560 |
+
"integrity": "sha512-3uBAs+egzvJBDZAzvb/n4NxxOYpnspmWxO2u5NbZ8Y6FM/NdrGSF9bop3Cf6F6C71z1rTSn8KV0Fo2ZVd79lGA==",
|
1561 |
+
"dependencies": {
|
1562 |
+
"@radix-ui/react-primitive": "2.0.0"
|
1563 |
+
},
|
1564 |
+
"peerDependencies": {
|
1565 |
+
"@types/react": "*",
|
1566 |
+
"@types/react-dom": "*",
|
1567 |
+
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
1568 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
1569 |
+
},
|
1570 |
+
"peerDependenciesMeta": {
|
1571 |
+
"@types/react": {
|
1572 |
+
"optional": true
|
1573 |
+
},
|
1574 |
+
"@types/react-dom": {
|
1575 |
+
"optional": true
|
1576 |
+
}
|
1577 |
+
}
|
1578 |
+
},
|
1579 |
"node_modules/@radix-ui/react-slot": {
|
1580 |
"version": "1.1.0",
|
1581 |
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
|
|
2604 |
"node": ">=6"
|
2605 |
}
|
2606 |
},
|
2607 |
+
"node_modules/cmdk": {
|
2608 |
+
"version": "1.0.0",
|
2609 |
+
"resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.0.0.tgz",
|
2610 |
+
"integrity": "sha512-gDzVf0a09TvoJ5jnuPvygTB77+XdOSwEmJ88L6XPFPlv7T3RxbP9jgenfylrAMD0+Le1aO0nVjQUzl2g+vjz5Q==",
|
2611 |
+
"dependencies": {
|
2612 |
+
"@radix-ui/react-dialog": "1.0.5",
|
2613 |
+
"@radix-ui/react-primitive": "1.0.3"
|
2614 |
+
},
|
2615 |
+
"peerDependencies": {
|
2616 |
+
"react": "^18.0.0",
|
2617 |
+
"react-dom": "^18.0.0"
|
2618 |
+
}
|
2619 |
+
},
|
2620 |
+
"node_modules/cmdk/node_modules/@radix-ui/primitive": {
|
2621 |
+
"version": "1.0.1",
|
2622 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz",
|
2623 |
+
"integrity": "sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==",
|
2624 |
+
"dependencies": {
|
2625 |
+
"@babel/runtime": "^7.13.10"
|
2626 |
+
}
|
2627 |
+
},
|
2628 |
+
"node_modules/cmdk/node_modules/@radix-ui/react-compose-refs": {
|
2629 |
+
"version": "1.0.1",
|
2630 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz",
|
2631 |
+
"integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==",
|
2632 |
+
"dependencies": {
|
2633 |
+
"@babel/runtime": "^7.13.10"
|
2634 |
+
},
|
2635 |
+
"peerDependencies": {
|
2636 |
+
"@types/react": "*",
|
2637 |
+
"react": "^16.8 || ^17.0 || ^18.0"
|
2638 |
+
},
|
2639 |
+
"peerDependenciesMeta": {
|
2640 |
+
"@types/react": {
|
2641 |
+
"optional": true
|
2642 |
+
}
|
2643 |
+
}
|
2644 |
+
},
|
2645 |
+
"node_modules/cmdk/node_modules/@radix-ui/react-context": {
|
2646 |
+
"version": "1.0.1",
|
2647 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.0.1.tgz",
|
2648 |
+
"integrity": "sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==",
|
2649 |
+
"dependencies": {
|
2650 |
+
"@babel/runtime": "^7.13.10"
|
2651 |
+
},
|
2652 |
+
"peerDependencies": {
|
2653 |
+
"@types/react": "*",
|
2654 |
+
"react": "^16.8 || ^17.0 || ^18.0"
|
2655 |
+
},
|
2656 |
+
"peerDependenciesMeta": {
|
2657 |
+
"@types/react": {
|
2658 |
+
"optional": true
|
2659 |
+
}
|
2660 |
+
}
|
2661 |
+
},
|
2662 |
+
"node_modules/cmdk/node_modules/@radix-ui/react-dialog": {
|
2663 |
+
"version": "1.0.5",
|
2664 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz",
|
2665 |
+
"integrity": "sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==",
|
2666 |
+
"dependencies": {
|
2667 |
+
"@babel/runtime": "^7.13.10",
|
2668 |
+
"@radix-ui/primitive": "1.0.1",
|
2669 |
+
"@radix-ui/react-compose-refs": "1.0.1",
|
2670 |
+
"@radix-ui/react-context": "1.0.1",
|
2671 |
+
"@radix-ui/react-dismissable-layer": "1.0.5",
|
2672 |
+
"@radix-ui/react-focus-guards": "1.0.1",
|
2673 |
+
"@radix-ui/react-focus-scope": "1.0.4",
|
2674 |
+
"@radix-ui/react-id": "1.0.1",
|
2675 |
+
"@radix-ui/react-portal": "1.0.4",
|
2676 |
+
"@radix-ui/react-presence": "1.0.1",
|
2677 |
+
"@radix-ui/react-primitive": "1.0.3",
|
2678 |
+
"@radix-ui/react-slot": "1.0.2",
|
2679 |
+
"@radix-ui/react-use-controllable-state": "1.0.1",
|
2680 |
+
"aria-hidden": "^1.1.1",
|
2681 |
+
"react-remove-scroll": "2.5.5"
|
2682 |
+
},
|
2683 |
+
"peerDependencies": {
|
2684 |
+
"@types/react": "*",
|
2685 |
+
"@types/react-dom": "*",
|
2686 |
+
"react": "^16.8 || ^17.0 || ^18.0",
|
2687 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
2688 |
+
},
|
2689 |
+
"peerDependenciesMeta": {
|
2690 |
+
"@types/react": {
|
2691 |
+
"optional": true
|
2692 |
+
},
|
2693 |
+
"@types/react-dom": {
|
2694 |
+
"optional": true
|
2695 |
+
}
|
2696 |
+
}
|
2697 |
+
},
|
2698 |
+
"node_modules/cmdk/node_modules/@radix-ui/react-dismissable-layer": {
|
2699 |
+
"version": "1.0.5",
|
2700 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.5.tgz",
|
2701 |
+
"integrity": "sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==",
|
2702 |
+
"dependencies": {
|
2703 |
+
"@babel/runtime": "^7.13.10",
|
2704 |
+
"@radix-ui/primitive": "1.0.1",
|
2705 |
+
"@radix-ui/react-compose-refs": "1.0.1",
|
2706 |
+
"@radix-ui/react-primitive": "1.0.3",
|
2707 |
+
"@radix-ui/react-use-callback-ref": "1.0.1",
|
2708 |
+
"@radix-ui/react-use-escape-keydown": "1.0.3"
|
2709 |
+
},
|
2710 |
+
"peerDependencies": {
|
2711 |
+
"@types/react": "*",
|
2712 |
+
"@types/react-dom": "*",
|
2713 |
+
"react": "^16.8 || ^17.0 || ^18.0",
|
2714 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
2715 |
+
},
|
2716 |
+
"peerDependenciesMeta": {
|
2717 |
+
"@types/react": {
|
2718 |
+
"optional": true
|
2719 |
+
},
|
2720 |
+
"@types/react-dom": {
|
2721 |
+
"optional": true
|
2722 |
+
}
|
2723 |
+
}
|
2724 |
+
},
|
2725 |
+
"node_modules/cmdk/node_modules/@radix-ui/react-focus-guards": {
|
2726 |
+
"version": "1.0.1",
|
2727 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz",
|
2728 |
+
"integrity": "sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==",
|
2729 |
+
"dependencies": {
|
2730 |
+
"@babel/runtime": "^7.13.10"
|
2731 |
+
},
|
2732 |
+
"peerDependencies": {
|
2733 |
+
"@types/react": "*",
|
2734 |
+
"react": "^16.8 || ^17.0 || ^18.0"
|
2735 |
+
},
|
2736 |
+
"peerDependenciesMeta": {
|
2737 |
+
"@types/react": {
|
2738 |
+
"optional": true
|
2739 |
+
}
|
2740 |
+
}
|
2741 |
+
},
|
2742 |
+
"node_modules/cmdk/node_modules/@radix-ui/react-focus-scope": {
|
2743 |
+
"version": "1.0.4",
|
2744 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.4.tgz",
|
2745 |
+
"integrity": "sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==",
|
2746 |
+
"dependencies": {
|
2747 |
+
"@babel/runtime": "^7.13.10",
|
2748 |
+
"@radix-ui/react-compose-refs": "1.0.1",
|
2749 |
+
"@radix-ui/react-primitive": "1.0.3",
|
2750 |
+
"@radix-ui/react-use-callback-ref": "1.0.1"
|
2751 |
+
},
|
2752 |
+
"peerDependencies": {
|
2753 |
+
"@types/react": "*",
|
2754 |
+
"@types/react-dom": "*",
|
2755 |
+
"react": "^16.8 || ^17.0 || ^18.0",
|
2756 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
2757 |
+
},
|
2758 |
+
"peerDependenciesMeta": {
|
2759 |
+
"@types/react": {
|
2760 |
+
"optional": true
|
2761 |
+
},
|
2762 |
+
"@types/react-dom": {
|
2763 |
+
"optional": true
|
2764 |
+
}
|
2765 |
+
}
|
2766 |
+
},
|
2767 |
+
"node_modules/cmdk/node_modules/@radix-ui/react-id": {
|
2768 |
+
"version": "1.0.1",
|
2769 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.0.1.tgz",
|
2770 |
+
"integrity": "sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==",
|
2771 |
+
"dependencies": {
|
2772 |
+
"@babel/runtime": "^7.13.10",
|
2773 |
+
"@radix-ui/react-use-layout-effect": "1.0.1"
|
2774 |
+
},
|
2775 |
+
"peerDependencies": {
|
2776 |
+
"@types/react": "*",
|
2777 |
+
"react": "^16.8 || ^17.0 || ^18.0"
|
2778 |
+
},
|
2779 |
+
"peerDependenciesMeta": {
|
2780 |
+
"@types/react": {
|
2781 |
+
"optional": true
|
2782 |
+
}
|
2783 |
+
}
|
2784 |
+
},
|
2785 |
+
"node_modules/cmdk/node_modules/@radix-ui/react-portal": {
|
2786 |
+
"version": "1.0.4",
|
2787 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz",
|
2788 |
+
"integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==",
|
2789 |
+
"dependencies": {
|
2790 |
+
"@babel/runtime": "^7.13.10",
|
2791 |
+
"@radix-ui/react-primitive": "1.0.3"
|
2792 |
+
},
|
2793 |
+
"peerDependencies": {
|
2794 |
+
"@types/react": "*",
|
2795 |
+
"@types/react-dom": "*",
|
2796 |
+
"react": "^16.8 || ^17.0 || ^18.0",
|
2797 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
2798 |
+
},
|
2799 |
+
"peerDependenciesMeta": {
|
2800 |
+
"@types/react": {
|
2801 |
+
"optional": true
|
2802 |
+
},
|
2803 |
+
"@types/react-dom": {
|
2804 |
+
"optional": true
|
2805 |
+
}
|
2806 |
+
}
|
2807 |
+
},
|
2808 |
+
"node_modules/cmdk/node_modules/@radix-ui/react-presence": {
|
2809 |
+
"version": "1.0.1",
|
2810 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.0.1.tgz",
|
2811 |
+
"integrity": "sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==",
|
2812 |
+
"dependencies": {
|
2813 |
+
"@babel/runtime": "^7.13.10",
|
2814 |
+
"@radix-ui/react-compose-refs": "1.0.1",
|
2815 |
+
"@radix-ui/react-use-layout-effect": "1.0.1"
|
2816 |
+
},
|
2817 |
+
"peerDependencies": {
|
2818 |
+
"@types/react": "*",
|
2819 |
+
"@types/react-dom": "*",
|
2820 |
+
"react": "^16.8 || ^17.0 || ^18.0",
|
2821 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
2822 |
+
},
|
2823 |
+
"peerDependenciesMeta": {
|
2824 |
+
"@types/react": {
|
2825 |
+
"optional": true
|
2826 |
+
},
|
2827 |
+
"@types/react-dom": {
|
2828 |
+
"optional": true
|
2829 |
+
}
|
2830 |
+
}
|
2831 |
+
},
|
2832 |
+
"node_modules/cmdk/node_modules/@radix-ui/react-primitive": {
|
2833 |
+
"version": "1.0.3",
|
2834 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz",
|
2835 |
+
"integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==",
|
2836 |
+
"dependencies": {
|
2837 |
+
"@babel/runtime": "^7.13.10",
|
2838 |
+
"@radix-ui/react-slot": "1.0.2"
|
2839 |
+
},
|
2840 |
+
"peerDependencies": {
|
2841 |
+
"@types/react": "*",
|
2842 |
+
"@types/react-dom": "*",
|
2843 |
+
"react": "^16.8 || ^17.0 || ^18.0",
|
2844 |
+
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
2845 |
+
},
|
2846 |
+
"peerDependenciesMeta": {
|
2847 |
+
"@types/react": {
|
2848 |
+
"optional": true
|
2849 |
+
},
|
2850 |
+
"@types/react-dom": {
|
2851 |
+
"optional": true
|
2852 |
+
}
|
2853 |
+
}
|
2854 |
+
},
|
2855 |
+
"node_modules/cmdk/node_modules/@radix-ui/react-slot": {
|
2856 |
+
"version": "1.0.2",
|
2857 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
|
2858 |
+
"integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==",
|
2859 |
+
"dependencies": {
|
2860 |
+
"@babel/runtime": "^7.13.10",
|
2861 |
+
"@radix-ui/react-compose-refs": "1.0.1"
|
2862 |
+
},
|
2863 |
+
"peerDependencies": {
|
2864 |
+
"@types/react": "*",
|
2865 |
+
"react": "^16.8 || ^17.0 || ^18.0"
|
2866 |
+
},
|
2867 |
+
"peerDependenciesMeta": {
|
2868 |
+
"@types/react": {
|
2869 |
+
"optional": true
|
2870 |
+
}
|
2871 |
+
}
|
2872 |
+
},
|
2873 |
+
"node_modules/cmdk/node_modules/@radix-ui/react-use-callback-ref": {
|
2874 |
+
"version": "1.0.1",
|
2875 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz",
|
2876 |
+
"integrity": "sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==",
|
2877 |
+
"dependencies": {
|
2878 |
+
"@babel/runtime": "^7.13.10"
|
2879 |
+
},
|
2880 |
+
"peerDependencies": {
|
2881 |
+
"@types/react": "*",
|
2882 |
+
"react": "^16.8 || ^17.0 || ^18.0"
|
2883 |
+
},
|
2884 |
+
"peerDependenciesMeta": {
|
2885 |
+
"@types/react": {
|
2886 |
+
"optional": true
|
2887 |
+
}
|
2888 |
+
}
|
2889 |
+
},
|
2890 |
+
"node_modules/cmdk/node_modules/@radix-ui/react-use-controllable-state": {
|
2891 |
+
"version": "1.0.1",
|
2892 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz",
|
2893 |
+
"integrity": "sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==",
|
2894 |
+
"dependencies": {
|
2895 |
+
"@babel/runtime": "^7.13.10",
|
2896 |
+
"@radix-ui/react-use-callback-ref": "1.0.1"
|
2897 |
+
},
|
2898 |
+
"peerDependencies": {
|
2899 |
+
"@types/react": "*",
|
2900 |
+
"react": "^16.8 || ^17.0 || ^18.0"
|
2901 |
+
},
|
2902 |
+
"peerDependenciesMeta": {
|
2903 |
+
"@types/react": {
|
2904 |
+
"optional": true
|
2905 |
+
}
|
2906 |
+
}
|
2907 |
+
},
|
2908 |
+
"node_modules/cmdk/node_modules/@radix-ui/react-use-escape-keydown": {
|
2909 |
+
"version": "1.0.3",
|
2910 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz",
|
2911 |
+
"integrity": "sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==",
|
2912 |
+
"dependencies": {
|
2913 |
+
"@babel/runtime": "^7.13.10",
|
2914 |
+
"@radix-ui/react-use-callback-ref": "1.0.1"
|
2915 |
+
},
|
2916 |
+
"peerDependencies": {
|
2917 |
+
"@types/react": "*",
|
2918 |
+
"react": "^16.8 || ^17.0 || ^18.0"
|
2919 |
+
},
|
2920 |
+
"peerDependenciesMeta": {
|
2921 |
+
"@types/react": {
|
2922 |
+
"optional": true
|
2923 |
+
}
|
2924 |
+
}
|
2925 |
+
},
|
2926 |
+
"node_modules/cmdk/node_modules/@radix-ui/react-use-layout-effect": {
|
2927 |
+
"version": "1.0.1",
|
2928 |
+
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz",
|
2929 |
+
"integrity": "sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==",
|
2930 |
+
"dependencies": {
|
2931 |
+
"@babel/runtime": "^7.13.10"
|
2932 |
+
},
|
2933 |
+
"peerDependencies": {
|
2934 |
+
"@types/react": "*",
|
2935 |
+
"react": "^16.8 || ^17.0 || ^18.0"
|
2936 |
+
},
|
2937 |
+
"peerDependenciesMeta": {
|
2938 |
+
"@types/react": {
|
2939 |
+
"optional": true
|
2940 |
+
}
|
2941 |
+
}
|
2942 |
+
},
|
2943 |
+
"node_modules/cmdk/node_modules/react-remove-scroll": {
|
2944 |
+
"version": "2.5.5",
|
2945 |
+
"resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.5.tgz",
|
2946 |
+
"integrity": "sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==",
|
2947 |
+
"dependencies": {
|
2948 |
+
"react-remove-scroll-bar": "^2.3.3",
|
2949 |
+
"react-style-singleton": "^2.2.1",
|
2950 |
+
"tslib": "^2.1.0",
|
2951 |
+
"use-callback-ref": "^1.3.0",
|
2952 |
+
"use-sidecar": "^1.1.2"
|
2953 |
+
},
|
2954 |
+
"engines": {
|
2955 |
+
"node": ">=10"
|
2956 |
+
},
|
2957 |
+
"peerDependencies": {
|
2958 |
+
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
|
2959 |
+
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
2960 |
+
},
|
2961 |
+
"peerDependenciesMeta": {
|
2962 |
+
"@types/react": {
|
2963 |
+
"optional": true
|
2964 |
+
}
|
2965 |
+
}
|
2966 |
+
},
|
2967 |
"node_modules/color-convert": {
|
2968 |
"version": "1.9.3",
|
2969 |
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
package.json
CHANGED
@@ -12,10 +12,14 @@
|
|
12 |
"dependencies": {
|
13 |
"@radix-ui/react-checkbox": "^1.1.1",
|
14 |
"@radix-ui/react-collapsible": "^1.1.0",
|
|
|
|
|
15 |
"@radix-ui/react-select": "^2.1.1",
|
|
|
16 |
"@radix-ui/react-slot": "^1.1.0",
|
17 |
"class-variance-authority": "^0.7.0",
|
18 |
"clsx": "^2.1.1",
|
|
|
19 |
"lucide-react": "^0.399.0",
|
20 |
"react": "^18.3.1",
|
21 |
"react-dom": "^18.3.1",
|
|
|
12 |
"dependencies": {
|
13 |
"@radix-ui/react-checkbox": "^1.1.1",
|
14 |
"@radix-ui/react-collapsible": "^1.1.0",
|
15 |
+
"@radix-ui/react-dialog": "^1.1.1",
|
16 |
+
"@radix-ui/react-popover": "^1.1.1",
|
17 |
"@radix-ui/react-select": "^2.1.1",
|
18 |
+
"@radix-ui/react-separator": "^1.1.0",
|
19 |
"@radix-ui/react-slot": "^1.1.0",
|
20 |
"class-variance-authority": "^0.7.0",
|
21 |
"clsx": "^2.1.1",
|
22 |
+
"cmdk": "^1.0.0",
|
23 |
"lucide-react": "^0.399.0",
|
24 |
"react": "^18.3.1",
|
25 |
"react-dom": "^18.3.1",
|
src/App.tsx
CHANGED
@@ -3,7 +3,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
|
3 |
import { Checkbox } from '@/components/ui/checkbox'
|
4 |
import { Input } from '@/components/ui/input'
|
5 |
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
|
6 |
-
import {
|
7 |
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'
|
8 |
import { Button } from '@/components/ui/button'
|
9 |
import { ChevronDown, ChevronRight } from 'lucide-react'
|
@@ -145,36 +145,8 @@ const App: React.FC = () => {
|
|
145 |
<Table>
|
146 |
<TableHeader>
|
147 |
<TableRow>
|
148 |
-
<TableHead>
|
149 |
-
|
150 |
-
<SelectTrigger>
|
151 |
-
<SelectValue placeholder="Select Providers" />
|
152 |
-
</SelectTrigger>
|
153 |
-
<SelectContent>
|
154 |
-
{data.map((provider) => (
|
155 |
-
<SelectItem key={provider.provider} value={provider.provider}>
|
156 |
-
{provider.provider}
|
157 |
-
</SelectItem>
|
158 |
-
))}
|
159 |
-
</SelectContent>
|
160 |
-
</Select>
|
161 |
-
</TableHead>
|
162 |
-
<TableHead>
|
163 |
-
<Select onValueChange={(value) => setSelectedModels(value as string[])} defaultValue={[]} multiple>
|
164 |
-
<SelectTrigger>
|
165 |
-
<SelectValue placeholder="Select Models" />
|
166 |
-
</SelectTrigger>
|
167 |
-
<SelectContent>
|
168 |
-
{data
|
169 |
-
.flatMap((provider) => provider.models)
|
170 |
-
.map((model) => (
|
171 |
-
<SelectItem key={model.name} value={model.name}>
|
172 |
-
{model.name}
|
173 |
-
</SelectItem>
|
174 |
-
))}
|
175 |
-
</SelectContent>
|
176 |
-
</Select>
|
177 |
-
</TableHead>
|
178 |
<TableHead>Input Price (per 1M tokens)</TableHead>
|
179 |
<TableHead>Output Price (per 1M tokens)</TableHead>
|
180 |
<TableHead>Total Price</TableHead>
|
@@ -185,8 +157,24 @@ const App: React.FC = () => {
|
|
185 |
))}
|
186 |
</TableRow>
|
187 |
<TableRow>
|
188 |
-
<TableHead
|
189 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
190 |
<TableHead />
|
191 |
<TableHead />
|
192 |
<TableHead />
|
@@ -200,7 +188,12 @@ const App: React.FC = () => {
|
|
200 |
{filteredData.flatMap((provider) =>
|
201 |
provider.models.map((model) => (
|
202 |
<TableRow key={`${provider.provider}-${model.name}`}>
|
203 |
-
<TableCell>
|
|
|
|
|
|
|
|
|
|
|
204 |
<TableCell>{model.name}</TableCell>
|
205 |
<TableCell>${model.inputPrice.toFixed(2)}</TableCell>
|
206 |
<TableCell>${model.outputPrice.toFixed(2)}</TableCell>
|
|
|
3 |
import { Checkbox } from '@/components/ui/checkbox'
|
4 |
import { Input } from '@/components/ui/input'
|
5 |
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'
|
6 |
+
import { MultiSelect } from '@/components/ui/multi-select'
|
7 |
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'
|
8 |
import { Button } from '@/components/ui/button'
|
9 |
import { ChevronDown, ChevronRight } from 'lucide-react'
|
|
|
145 |
<Table>
|
146 |
<TableHeader>
|
147 |
<TableRow>
|
148 |
+
<TableHead>Provider</TableHead>
|
149 |
+
<TableHead>Model</TableHead>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
150 |
<TableHead>Input Price (per 1M tokens)</TableHead>
|
151 |
<TableHead>Output Price (per 1M tokens)</TableHead>
|
152 |
<TableHead>Total Price</TableHead>
|
|
|
157 |
))}
|
158 |
</TableRow>
|
159 |
<TableRow>
|
160 |
+
<TableHead>
|
161 |
+
<MultiSelect
|
162 |
+
options={data.map((provider) => ({ label: provider.provider, value: provider.provider })) || []}
|
163 |
+
onValueChange={setSelectedProviders}
|
164 |
+
defaultValue={selectedProviders}
|
165 |
+
/>
|
166 |
+
</TableHead>
|
167 |
+
<TableHead>
|
168 |
+
<MultiSelect
|
169 |
+
options={
|
170 |
+
data
|
171 |
+
.flatMap((provider) => provider.models)
|
172 |
+
.map((model) => ({ label: model.name, value: model.name })) || []
|
173 |
+
}
|
174 |
+
defaultValue={selectedModels}
|
175 |
+
onValueChange={setSelectedModels}
|
176 |
+
/>
|
177 |
+
</TableHead>
|
178 |
<TableHead />
|
179 |
<TableHead />
|
180 |
<TableHead />
|
|
|
188 |
{filteredData.flatMap((provider) =>
|
189 |
provider.models.map((model) => (
|
190 |
<TableRow key={`${provider.provider}-${model.name}`}>
|
191 |
+
<TableCell>
|
192 |
+
{' '}
|
193 |
+
<a href={provider.uri} className="underline">
|
194 |
+
{provider.provider}
|
195 |
+
</a>
|
196 |
+
</TableCell>
|
197 |
<TableCell>{model.name}</TableCell>
|
198 |
<TableCell>${model.inputPrice.toFixed(2)}</TableCell>
|
199 |
<TableCell>${model.outputPrice.toFixed(2)}</TableCell>
|
src/components/ui/badge.tsx
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import * as React from 'react'
|
2 |
+
import { cva, type VariantProps } from 'class-variance-authority'
|
3 |
+
|
4 |
+
import { cn } from '@/lib/utils'
|
5 |
+
|
6 |
+
const badgeVariants = cva(
|
7 |
+
'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
|
8 |
+
{
|
9 |
+
variants: {
|
10 |
+
variant: {
|
11 |
+
default: 'border-transparent bg-primary text-primary-foreground hover:bg-primary/80',
|
12 |
+
secondary: 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
13 |
+
destructive: 'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
|
14 |
+
outline: 'text-foreground',
|
15 |
+
},
|
16 |
+
},
|
17 |
+
defaultVariants: {
|
18 |
+
variant: 'default',
|
19 |
+
},
|
20 |
+
}
|
21 |
+
)
|
22 |
+
|
23 |
+
export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {}
|
24 |
+
|
25 |
+
function Badge({ className, variant, ...props }: BadgeProps) {
|
26 |
+
return <div className={cn(badgeVariants({ variant }), className)} {...props} />
|
27 |
+
}
|
28 |
+
|
29 |
+
export { Badge, badgeVariants }
|
src/components/ui/command.tsx
ADDED
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import * as React from "react"
|
2 |
+
import { type DialogProps } from "@radix-ui/react-dialog"
|
3 |
+
import { Command as CommandPrimitive } from "cmdk"
|
4 |
+
import { Search } from "lucide-react"
|
5 |
+
|
6 |
+
import { cn } from "@/lib/utils"
|
7 |
+
import { Dialog, DialogContent } from "@/components/ui/dialog"
|
8 |
+
|
9 |
+
const Command = React.forwardRef<
|
10 |
+
React.ElementRef<typeof CommandPrimitive>,
|
11 |
+
React.ComponentPropsWithoutRef<typeof CommandPrimitive>
|
12 |
+
>(({ className, ...props }, ref) => (
|
13 |
+
<CommandPrimitive
|
14 |
+
ref={ref}
|
15 |
+
className={cn(
|
16 |
+
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
|
17 |
+
className
|
18 |
+
)}
|
19 |
+
{...props}
|
20 |
+
/>
|
21 |
+
))
|
22 |
+
Command.displayName = CommandPrimitive.displayName
|
23 |
+
|
24 |
+
interface CommandDialogProps extends DialogProps {}
|
25 |
+
|
26 |
+
const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
|
27 |
+
return (
|
28 |
+
<Dialog {...props}>
|
29 |
+
<DialogContent className="overflow-hidden p-0 shadow-lg">
|
30 |
+
<Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
|
31 |
+
{children}
|
32 |
+
</Command>
|
33 |
+
</DialogContent>
|
34 |
+
</Dialog>
|
35 |
+
)
|
36 |
+
}
|
37 |
+
|
38 |
+
const CommandInput = React.forwardRef<
|
39 |
+
React.ElementRef<typeof CommandPrimitive.Input>,
|
40 |
+
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
|
41 |
+
>(({ className, ...props }, ref) => (
|
42 |
+
<div className="flex items-center border-b px-3" cmdk-input-wrapper="">
|
43 |
+
<Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
|
44 |
+
<CommandPrimitive.Input
|
45 |
+
ref={ref}
|
46 |
+
className={cn(
|
47 |
+
"flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
|
48 |
+
className
|
49 |
+
)}
|
50 |
+
{...props}
|
51 |
+
/>
|
52 |
+
</div>
|
53 |
+
))
|
54 |
+
|
55 |
+
CommandInput.displayName = CommandPrimitive.Input.displayName
|
56 |
+
|
57 |
+
const CommandList = React.forwardRef<
|
58 |
+
React.ElementRef<typeof CommandPrimitive.List>,
|
59 |
+
React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
|
60 |
+
>(({ className, ...props }, ref) => (
|
61 |
+
<CommandPrimitive.List
|
62 |
+
ref={ref}
|
63 |
+
className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
|
64 |
+
{...props}
|
65 |
+
/>
|
66 |
+
))
|
67 |
+
|
68 |
+
CommandList.displayName = CommandPrimitive.List.displayName
|
69 |
+
|
70 |
+
const CommandEmpty = React.forwardRef<
|
71 |
+
React.ElementRef<typeof CommandPrimitive.Empty>,
|
72 |
+
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
|
73 |
+
>((props, ref) => (
|
74 |
+
<CommandPrimitive.Empty
|
75 |
+
ref={ref}
|
76 |
+
className="py-6 text-center text-sm"
|
77 |
+
{...props}
|
78 |
+
/>
|
79 |
+
))
|
80 |
+
|
81 |
+
CommandEmpty.displayName = CommandPrimitive.Empty.displayName
|
82 |
+
|
83 |
+
const CommandGroup = React.forwardRef<
|
84 |
+
React.ElementRef<typeof CommandPrimitive.Group>,
|
85 |
+
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
|
86 |
+
>(({ className, ...props }, ref) => (
|
87 |
+
<CommandPrimitive.Group
|
88 |
+
ref={ref}
|
89 |
+
className={cn(
|
90 |
+
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
|
91 |
+
className
|
92 |
+
)}
|
93 |
+
{...props}
|
94 |
+
/>
|
95 |
+
))
|
96 |
+
|
97 |
+
CommandGroup.displayName = CommandPrimitive.Group.displayName
|
98 |
+
|
99 |
+
const CommandSeparator = React.forwardRef<
|
100 |
+
React.ElementRef<typeof CommandPrimitive.Separator>,
|
101 |
+
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
|
102 |
+
>(({ className, ...props }, ref) => (
|
103 |
+
<CommandPrimitive.Separator
|
104 |
+
ref={ref}
|
105 |
+
className={cn("-mx-1 h-px bg-border", className)}
|
106 |
+
{...props}
|
107 |
+
/>
|
108 |
+
))
|
109 |
+
CommandSeparator.displayName = CommandPrimitive.Separator.displayName
|
110 |
+
|
111 |
+
const CommandItem = React.forwardRef<
|
112 |
+
React.ElementRef<typeof CommandPrimitive.Item>,
|
113 |
+
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
|
114 |
+
>(({ className, ...props }, ref) => (
|
115 |
+
<CommandPrimitive.Item
|
116 |
+
ref={ref}
|
117 |
+
className={cn(
|
118 |
+
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50",
|
119 |
+
className
|
120 |
+
)}
|
121 |
+
{...props}
|
122 |
+
/>
|
123 |
+
))
|
124 |
+
|
125 |
+
CommandItem.displayName = CommandPrimitive.Item.displayName
|
126 |
+
|
127 |
+
const CommandShortcut = ({
|
128 |
+
className,
|
129 |
+
...props
|
130 |
+
}: React.HTMLAttributes<HTMLSpanElement>) => {
|
131 |
+
return (
|
132 |
+
<span
|
133 |
+
className={cn(
|
134 |
+
"ml-auto text-xs tracking-widest text-muted-foreground",
|
135 |
+
className
|
136 |
+
)}
|
137 |
+
{...props}
|
138 |
+
/>
|
139 |
+
)
|
140 |
+
}
|
141 |
+
CommandShortcut.displayName = "CommandShortcut"
|
142 |
+
|
143 |
+
export {
|
144 |
+
Command,
|
145 |
+
CommandDialog,
|
146 |
+
CommandInput,
|
147 |
+
CommandList,
|
148 |
+
CommandEmpty,
|
149 |
+
CommandGroup,
|
150 |
+
CommandItem,
|
151 |
+
CommandShortcut,
|
152 |
+
CommandSeparator,
|
153 |
+
}
|
src/components/ui/dialog.tsx
ADDED
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import * as React from "react"
|
2 |
+
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
3 |
+
import { X } from "lucide-react"
|
4 |
+
|
5 |
+
import { cn } from "@/lib/utils"
|
6 |
+
|
7 |
+
const Dialog = DialogPrimitive.Root
|
8 |
+
|
9 |
+
const DialogTrigger = DialogPrimitive.Trigger
|
10 |
+
|
11 |
+
const DialogPortal = DialogPrimitive.Portal
|
12 |
+
|
13 |
+
const DialogClose = DialogPrimitive.Close
|
14 |
+
|
15 |
+
const DialogOverlay = React.forwardRef<
|
16 |
+
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
17 |
+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
|
18 |
+
>(({ className, ...props }, ref) => (
|
19 |
+
<DialogPrimitive.Overlay
|
20 |
+
ref={ref}
|
21 |
+
className={cn(
|
22 |
+
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
23 |
+
className
|
24 |
+
)}
|
25 |
+
{...props}
|
26 |
+
/>
|
27 |
+
))
|
28 |
+
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
|
29 |
+
|
30 |
+
const DialogContent = React.forwardRef<
|
31 |
+
React.ElementRef<typeof DialogPrimitive.Content>,
|
32 |
+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
|
33 |
+
>(({ className, children, ...props }, ref) => (
|
34 |
+
<DialogPortal>
|
35 |
+
<DialogOverlay />
|
36 |
+
<DialogPrimitive.Content
|
37 |
+
ref={ref}
|
38 |
+
className={cn(
|
39 |
+
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
40 |
+
className
|
41 |
+
)}
|
42 |
+
{...props}
|
43 |
+
>
|
44 |
+
{children}
|
45 |
+
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
46 |
+
<X className="h-4 w-4" />
|
47 |
+
<span className="sr-only">Close</span>
|
48 |
+
</DialogPrimitive.Close>
|
49 |
+
</DialogPrimitive.Content>
|
50 |
+
</DialogPortal>
|
51 |
+
))
|
52 |
+
DialogContent.displayName = DialogPrimitive.Content.displayName
|
53 |
+
|
54 |
+
const DialogHeader = ({
|
55 |
+
className,
|
56 |
+
...props
|
57 |
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
58 |
+
<div
|
59 |
+
className={cn(
|
60 |
+
"flex flex-col space-y-1.5 text-center sm:text-left",
|
61 |
+
className
|
62 |
+
)}
|
63 |
+
{...props}
|
64 |
+
/>
|
65 |
+
)
|
66 |
+
DialogHeader.displayName = "DialogHeader"
|
67 |
+
|
68 |
+
const DialogFooter = ({
|
69 |
+
className,
|
70 |
+
...props
|
71 |
+
}: React.HTMLAttributes<HTMLDivElement>) => (
|
72 |
+
<div
|
73 |
+
className={cn(
|
74 |
+
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
75 |
+
className
|
76 |
+
)}
|
77 |
+
{...props}
|
78 |
+
/>
|
79 |
+
)
|
80 |
+
DialogFooter.displayName = "DialogFooter"
|
81 |
+
|
82 |
+
const DialogTitle = React.forwardRef<
|
83 |
+
React.ElementRef<typeof DialogPrimitive.Title>,
|
84 |
+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
|
85 |
+
>(({ className, ...props }, ref) => (
|
86 |
+
<DialogPrimitive.Title
|
87 |
+
ref={ref}
|
88 |
+
className={cn(
|
89 |
+
"text-lg font-semibold leading-none tracking-tight",
|
90 |
+
className
|
91 |
+
)}
|
92 |
+
{...props}
|
93 |
+
/>
|
94 |
+
))
|
95 |
+
DialogTitle.displayName = DialogPrimitive.Title.displayName
|
96 |
+
|
97 |
+
const DialogDescription = React.forwardRef<
|
98 |
+
React.ElementRef<typeof DialogPrimitive.Description>,
|
99 |
+
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
|
100 |
+
>(({ className, ...props }, ref) => (
|
101 |
+
<DialogPrimitive.Description
|
102 |
+
ref={ref}
|
103 |
+
className={cn("text-sm text-muted-foreground", className)}
|
104 |
+
{...props}
|
105 |
+
/>
|
106 |
+
))
|
107 |
+
DialogDescription.displayName = DialogPrimitive.Description.displayName
|
108 |
+
|
109 |
+
export {
|
110 |
+
Dialog,
|
111 |
+
DialogPortal,
|
112 |
+
DialogOverlay,
|
113 |
+
DialogClose,
|
114 |
+
DialogTrigger,
|
115 |
+
DialogContent,
|
116 |
+
DialogHeader,
|
117 |
+
DialogFooter,
|
118 |
+
DialogTitle,
|
119 |
+
DialogDescription,
|
120 |
+
}
|
src/components/ui/multi-select.tsx
ADDED
@@ -0,0 +1,278 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import * as React from 'react'
|
2 |
+
import { cva, type VariantProps } from 'class-variance-authority'
|
3 |
+
import { CheckIcon, XCircle, ChevronDown, XIcon, WandSparkles } from 'lucide-react'
|
4 |
+
|
5 |
+
import { cn } from '@/lib/utils'
|
6 |
+
import { Separator } from '@/components/ui/separator'
|
7 |
+
import { Button } from '@/components/ui/button'
|
8 |
+
import { Badge } from '@/components/ui/badge'
|
9 |
+
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
10 |
+
import {
|
11 |
+
Command,
|
12 |
+
CommandEmpty,
|
13 |
+
CommandGroup,
|
14 |
+
CommandInput,
|
15 |
+
CommandItem,
|
16 |
+
CommandList,
|
17 |
+
CommandSeparator,
|
18 |
+
} from '@/components/ui/command'
|
19 |
+
|
20 |
+
const multiSelectVariants = cva(
|
21 |
+
'm-1 transition ease-in-out delay-150 hover:-translate-y-1 hover:scale-110 duration-300',
|
22 |
+
{
|
23 |
+
variants: {
|
24 |
+
variant: {
|
25 |
+
default: 'border-foreground/10 text-foreground bg-card hover:bg-card/80',
|
26 |
+
secondary: 'border-foreground/10 bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
27 |
+
destructive: 'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80',
|
28 |
+
inverted: 'inverted',
|
29 |
+
},
|
30 |
+
},
|
31 |
+
defaultVariants: {
|
32 |
+
variant: 'default',
|
33 |
+
},
|
34 |
+
}
|
35 |
+
)
|
36 |
+
|
37 |
+
interface MultiSelectProps
|
38 |
+
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
39 |
+
VariantProps<typeof multiSelectVariants> {
|
40 |
+
options: {
|
41 |
+
label: string
|
42 |
+
value: string
|
43 |
+
icon?: React.ComponentType<{ className?: string }>
|
44 |
+
}[]
|
45 |
+
onValueChange: (value: string[]) => void
|
46 |
+
defaultValue: string[]
|
47 |
+
placeholder?: string
|
48 |
+
animation?: number
|
49 |
+
maxCount?: number
|
50 |
+
asChild?: boolean
|
51 |
+
className?: string
|
52 |
+
}
|
53 |
+
|
54 |
+
export const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>(
|
55 |
+
(
|
56 |
+
{
|
57 |
+
options,
|
58 |
+
onValueChange,
|
59 |
+
variant,
|
60 |
+
defaultValue = [],
|
61 |
+
placeholder = 'Select options',
|
62 |
+
animation = 0,
|
63 |
+
maxCount = 3,
|
64 |
+
asChild = false,
|
65 |
+
className,
|
66 |
+
...props
|
67 |
+
},
|
68 |
+
ref
|
69 |
+
) => {
|
70 |
+
const [selectedValues, setSelectedValues] = React.useState<string[]>(defaultValue)
|
71 |
+
const [isPopoverOpen, setIsPopoverOpen] = React.useState(false)
|
72 |
+
const [isAnimating, setIsAnimating] = React.useState(false)
|
73 |
+
|
74 |
+
React.useEffect(() => {
|
75 |
+
if (JSON.stringify(selectedValues) !== JSON.stringify(defaultValue)) {
|
76 |
+
setSelectedValues(defaultValue)
|
77 |
+
}
|
78 |
+
}, [defaultValue, selectedValues])
|
79 |
+
|
80 |
+
const handleInputKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
|
81 |
+
if (event.key === 'Enter') {
|
82 |
+
setIsPopoverOpen(true)
|
83 |
+
} else if (event.key === 'Backspace' && !event.currentTarget.value) {
|
84 |
+
const newSelectedValues = [...selectedValues]
|
85 |
+
newSelectedValues.pop()
|
86 |
+
setSelectedValues(newSelectedValues)
|
87 |
+
onValueChange(newSelectedValues)
|
88 |
+
}
|
89 |
+
}
|
90 |
+
|
91 |
+
const toggleOption = (value: string) => {
|
92 |
+
const newSelectedValues = selectedValues.includes(value)
|
93 |
+
? selectedValues.filter((v) => v !== value)
|
94 |
+
: [...selectedValues, value]
|
95 |
+
setSelectedValues(newSelectedValues)
|
96 |
+
onValueChange(newSelectedValues)
|
97 |
+
}
|
98 |
+
|
99 |
+
const handleClear = () => {
|
100 |
+
setSelectedValues([])
|
101 |
+
onValueChange([])
|
102 |
+
}
|
103 |
+
|
104 |
+
const handleTogglePopover = () => {
|
105 |
+
setIsPopoverOpen((prev) => !prev)
|
106 |
+
}
|
107 |
+
|
108 |
+
const clearExtraOptions = () => {
|
109 |
+
const newSelectedValues = selectedValues.slice(0, maxCount)
|
110 |
+
setSelectedValues(newSelectedValues)
|
111 |
+
onValueChange(newSelectedValues)
|
112 |
+
}
|
113 |
+
|
114 |
+
const toggleAll = () => {
|
115 |
+
if (selectedValues.length === options.length) {
|
116 |
+
handleClear()
|
117 |
+
} else {
|
118 |
+
const allValues = options.map((option) => option.value)
|
119 |
+
setSelectedValues(allValues)
|
120 |
+
onValueChange(allValues)
|
121 |
+
}
|
122 |
+
}
|
123 |
+
|
124 |
+
return (
|
125 |
+
<Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}>
|
126 |
+
<PopoverTrigger asChild>
|
127 |
+
<Button
|
128 |
+
ref={ref}
|
129 |
+
{...props}
|
130 |
+
onClick={handleTogglePopover}
|
131 |
+
className={cn(
|
132 |
+
'flex w-full p-1 rounded-md border min-h-10 h-auto items-center justify-between bg-inherit hover:bg-inherit',
|
133 |
+
className
|
134 |
+
)}
|
135 |
+
>
|
136 |
+
{selectedValues.length > 0 ? (
|
137 |
+
<div className="flex justify-between items-center w-full">
|
138 |
+
<div className="flex flex-wrap items-center">
|
139 |
+
{selectedValues.slice(0, maxCount).map((value) => {
|
140 |
+
const option = options.find((o) => o.value === value)
|
141 |
+
const IconComponent = option?.icon
|
142 |
+
return (
|
143 |
+
<Badge
|
144 |
+
key={value}
|
145 |
+
className={cn(isAnimating ? 'animate-bounce' : '', multiSelectVariants({ variant, className }))}
|
146 |
+
style={{ animationDuration: `${animation}s` }}
|
147 |
+
>
|
148 |
+
{IconComponent && <IconComponent className="h-4 w-4 mr-2" />}
|
149 |
+
{option?.label}
|
150 |
+
<XCircle
|
151 |
+
className="ml-2 h-4 w-4 cursor-pointer"
|
152 |
+
onClick={(event) => {
|
153 |
+
event.stopPropagation()
|
154 |
+
toggleOption(value)
|
155 |
+
}}
|
156 |
+
/>
|
157 |
+
</Badge>
|
158 |
+
)
|
159 |
+
})}
|
160 |
+
{selectedValues.length > maxCount && (
|
161 |
+
<Badge
|
162 |
+
className={cn(
|
163 |
+
'bg-transparent text-foreground border-foreground/1 hover:bg-transparent',
|
164 |
+
isAnimating ? 'animate-bounce' : '',
|
165 |
+
multiSelectVariants({ variant, className })
|
166 |
+
)}
|
167 |
+
style={{ animationDuration: `${animation}s` }}
|
168 |
+
>
|
169 |
+
{`+ ${selectedValues.length - maxCount} more`}
|
170 |
+
<XCircle
|
171 |
+
className="ml-2 h-4 w-4 cursor-pointer"
|
172 |
+
onClick={(event) => {
|
173 |
+
event.stopPropagation()
|
174 |
+
clearExtraOptions()
|
175 |
+
}}
|
176 |
+
/>
|
177 |
+
</Badge>
|
178 |
+
)}
|
179 |
+
</div>
|
180 |
+
<div className="flex items-center justify-between">
|
181 |
+
<XIcon
|
182 |
+
className="h-4 mx-2 cursor-pointer text-muted-foreground"
|
183 |
+
onClick={(event) => {
|
184 |
+
event.stopPropagation()
|
185 |
+
handleClear()
|
186 |
+
}}
|
187 |
+
/>
|
188 |
+
<Separator orientation="vertical" className="flex min-h-6 h-full" />
|
189 |
+
<ChevronDown className="h-4 mx-2 cursor-pointer text-muted-foreground" />
|
190 |
+
</div>
|
191 |
+
</div>
|
192 |
+
) : (
|
193 |
+
<div className="flex items-center justify-between w-full mx-auto">
|
194 |
+
<span className="text-sm text-muted-foreground mx-3">{placeholder}</span>
|
195 |
+
<ChevronDown className="h-4 cursor-pointer text-muted-foreground mx-2" />
|
196 |
+
</div>
|
197 |
+
)}
|
198 |
+
</Button>
|
199 |
+
</PopoverTrigger>
|
200 |
+
<PopoverContent className="w-auto p-0" align="start" onEscapeKeyDown={() => setIsPopoverOpen(false)}>
|
201 |
+
<Command>
|
202 |
+
<CommandInput placeholder="Search..." onKeyDown={handleInputKeyDown} />
|
203 |
+
<CommandList>
|
204 |
+
<CommandEmpty>No results found.</CommandEmpty>
|
205 |
+
<CommandGroup>
|
206 |
+
<CommandItem key="all" onSelect={toggleAll} className="cursor-pointer">
|
207 |
+
<div
|
208 |
+
className={cn(
|
209 |
+
'mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary',
|
210 |
+
selectedValues.length === options.length
|
211 |
+
? 'bg-primary text-primary-foreground'
|
212 |
+
: 'opacity-50 [&_svg]:invisible'
|
213 |
+
)}
|
214 |
+
>
|
215 |
+
<CheckIcon className="h-4 w-4" />
|
216 |
+
</div>
|
217 |
+
<span>(Select All)</span>
|
218 |
+
</CommandItem>
|
219 |
+
{options.map((option) => {
|
220 |
+
const isSelected = selectedValues.includes(option.value)
|
221 |
+
return (
|
222 |
+
<CommandItem
|
223 |
+
key={option.value}
|
224 |
+
onSelect={() => toggleOption(option.value)}
|
225 |
+
className="cursor-pointer"
|
226 |
+
>
|
227 |
+
<div
|
228 |
+
className={cn(
|
229 |
+
'mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary',
|
230 |
+
isSelected ? 'bg-primary text-primary-foreground' : 'opacity-50 [&_svg]:invisible'
|
231 |
+
)}
|
232 |
+
>
|
233 |
+
<CheckIcon className="h-4 w-4" />
|
234 |
+
</div>
|
235 |
+
{option.icon && <option.icon className="mr-2 h-4 w-4 text-muted-foreground" />}
|
236 |
+
<span>{option.label}</span>
|
237 |
+
</CommandItem>
|
238 |
+
)
|
239 |
+
})}
|
240 |
+
</CommandGroup>
|
241 |
+
<CommandSeparator />
|
242 |
+
<CommandGroup>
|
243 |
+
<div className="flex items-center justify-between">
|
244 |
+
{selectedValues.length > 0 && (
|
245 |
+
<>
|
246 |
+
<CommandItem onSelect={handleClear} className="flex-1 justify-center cursor-pointer">
|
247 |
+
Clear
|
248 |
+
</CommandItem>
|
249 |
+
<Separator orientation="vertical" className="flex min-h-6 h-full" />
|
250 |
+
</>
|
251 |
+
)}
|
252 |
+
<CommandSeparator />
|
253 |
+
<CommandItem
|
254 |
+
onSelect={() => setIsPopoverOpen(false)}
|
255 |
+
className="flex-1 justify-center cursor-pointer"
|
256 |
+
>
|
257 |
+
Close
|
258 |
+
</CommandItem>
|
259 |
+
</div>
|
260 |
+
</CommandGroup>
|
261 |
+
</CommandList>
|
262 |
+
</Command>
|
263 |
+
</PopoverContent>
|
264 |
+
{animation > 0 && selectedValues.length > 0 && (
|
265 |
+
<WandSparkles
|
266 |
+
className={cn(
|
267 |
+
'cursor-pointer my-2 text-foreground bg-background w-3 h-3',
|
268 |
+
isAnimating ? '' : 'text-muted-foreground'
|
269 |
+
)}
|
270 |
+
onClick={() => setIsAnimating(!isAnimating)}
|
271 |
+
/>
|
272 |
+
)}
|
273 |
+
</Popover>
|
274 |
+
)
|
275 |
+
}
|
276 |
+
)
|
277 |
+
|
278 |
+
MultiSelect.displayName = 'MultiSelect'
|
src/components/ui/popover.tsx
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import * as React from "react"
|
2 |
+
import * as PopoverPrimitive from "@radix-ui/react-popover"
|
3 |
+
|
4 |
+
import { cn } from "@/lib/utils"
|
5 |
+
|
6 |
+
const Popover = PopoverPrimitive.Root
|
7 |
+
|
8 |
+
const PopoverTrigger = PopoverPrimitive.Trigger
|
9 |
+
|
10 |
+
const PopoverContent = React.forwardRef<
|
11 |
+
React.ElementRef<typeof PopoverPrimitive.Content>,
|
12 |
+
React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
|
13 |
+
>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
|
14 |
+
<PopoverPrimitive.Portal>
|
15 |
+
<PopoverPrimitive.Content
|
16 |
+
ref={ref}
|
17 |
+
align={align}
|
18 |
+
sideOffset={sideOffset}
|
19 |
+
className={cn(
|
20 |
+
"z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
21 |
+
className
|
22 |
+
)}
|
23 |
+
{...props}
|
24 |
+
/>
|
25 |
+
</PopoverPrimitive.Portal>
|
26 |
+
))
|
27 |
+
PopoverContent.displayName = PopoverPrimitive.Content.displayName
|
28 |
+
|
29 |
+
export { Popover, PopoverTrigger, PopoverContent }
|
src/components/ui/select.tsx
DELETED
@@ -1,143 +0,0 @@
|
|
1 |
-
import * as React from 'react'
|
2 |
-
import * as SelectPrimitive from '@radix-ui/react-select'
|
3 |
-
import { Check, ChevronDown, ChevronUp } from 'lucide-react'
|
4 |
-
|
5 |
-
import { cn } from '@/lib/utils'
|
6 |
-
|
7 |
-
const Select = SelectPrimitive.Root
|
8 |
-
|
9 |
-
const SelectGroup = SelectPrimitive.Group
|
10 |
-
|
11 |
-
const SelectValue = SelectPrimitive.Value
|
12 |
-
|
13 |
-
const SelectTrigger = React.forwardRef<
|
14 |
-
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
15 |
-
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
16 |
-
>(({ className, children, ...props }, ref) => (
|
17 |
-
<SelectPrimitive.Trigger
|
18 |
-
ref={ref}
|
19 |
-
className={cn(
|
20 |
-
'flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
|
21 |
-
className
|
22 |
-
)}
|
23 |
-
{...props}
|
24 |
-
>
|
25 |
-
{children}
|
26 |
-
<SelectPrimitive.Icon asChild>
|
27 |
-
<ChevronDown className="h-4 w-4 opacity-50" />
|
28 |
-
</SelectPrimitive.Icon>
|
29 |
-
</SelectPrimitive.Trigger>
|
30 |
-
))
|
31 |
-
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
|
32 |
-
|
33 |
-
const SelectScrollUpButton = React.forwardRef<
|
34 |
-
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
35 |
-
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
|
36 |
-
>(({ className, ...props }, ref) => (
|
37 |
-
<SelectPrimitive.ScrollUpButton
|
38 |
-
ref={ref}
|
39 |
-
className={cn('flex cursor-default items-center justify-center py-1', className)}
|
40 |
-
{...props}
|
41 |
-
>
|
42 |
-
<ChevronUp className="h-4 w-4" />
|
43 |
-
</SelectPrimitive.ScrollUpButton>
|
44 |
-
))
|
45 |
-
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
|
46 |
-
|
47 |
-
const SelectScrollDownButton = React.forwardRef<
|
48 |
-
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
49 |
-
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
|
50 |
-
>(({ className, ...props }, ref) => (
|
51 |
-
<SelectPrimitive.ScrollDownButton
|
52 |
-
ref={ref}
|
53 |
-
className={cn('flex cursor-default items-center justify-center py-1', className)}
|
54 |
-
{...props}
|
55 |
-
>
|
56 |
-
<ChevronDown className="h-4 w-4" />
|
57 |
-
</SelectPrimitive.ScrollDownButton>
|
58 |
-
))
|
59 |
-
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName
|
60 |
-
|
61 |
-
const SelectContent = React.forwardRef<
|
62 |
-
React.ElementRef<typeof SelectPrimitive.Content>,
|
63 |
-
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
64 |
-
>(({ className, children, position = 'popper', ...props }, ref) => (
|
65 |
-
<SelectPrimitive.Portal>
|
66 |
-
<SelectPrimitive.Content
|
67 |
-
ref={ref}
|
68 |
-
className={cn(
|
69 |
-
'relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
70 |
-
position === 'popper' &&
|
71 |
-
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
72 |
-
className
|
73 |
-
)}
|
74 |
-
position={position}
|
75 |
-
{...props}
|
76 |
-
>
|
77 |
-
<SelectScrollUpButton />
|
78 |
-
<SelectPrimitive.Viewport
|
79 |
-
className={cn(
|
80 |
-
'p-1',
|
81 |
-
position === 'popper' &&
|
82 |
-
'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]'
|
83 |
-
)}
|
84 |
-
>
|
85 |
-
{children}
|
86 |
-
</SelectPrimitive.Viewport>
|
87 |
-
<SelectScrollDownButton />
|
88 |
-
</SelectPrimitive.Content>
|
89 |
-
</SelectPrimitive.Portal>
|
90 |
-
))
|
91 |
-
SelectContent.displayName = SelectPrimitive.Content.displayName
|
92 |
-
|
93 |
-
const SelectLabel = React.forwardRef<
|
94 |
-
React.ElementRef<typeof SelectPrimitive.Label>,
|
95 |
-
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
96 |
-
>(({ className, ...props }, ref) => (
|
97 |
-
<SelectPrimitive.Label ref={ref} className={cn('py-1.5 pl-8 pr-2 text-sm font-semibold', className)} {...props} />
|
98 |
-
))
|
99 |
-
SelectLabel.displayName = SelectPrimitive.Label.displayName
|
100 |
-
|
101 |
-
const SelectItem = React.forwardRef<
|
102 |
-
React.ElementRef<typeof SelectPrimitive.Item>,
|
103 |
-
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
104 |
-
>(({ className, children, ...props }, ref) => (
|
105 |
-
<SelectPrimitive.Item
|
106 |
-
ref={ref}
|
107 |
-
className={cn(
|
108 |
-
'relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
109 |
-
className
|
110 |
-
)}
|
111 |
-
{...props}
|
112 |
-
>
|
113 |
-
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
114 |
-
<SelectPrimitive.ItemIndicator>
|
115 |
-
<Check className="h-4 w-4" />
|
116 |
-
</SelectPrimitive.ItemIndicator>
|
117 |
-
</span>
|
118 |
-
|
119 |
-
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
120 |
-
</SelectPrimitive.Item>
|
121 |
-
))
|
122 |
-
SelectItem.displayName = SelectPrimitive.Item.displayName
|
123 |
-
|
124 |
-
const SelectSeparator = React.forwardRef<
|
125 |
-
React.ElementRef<typeof SelectPrimitive.Separator>,
|
126 |
-
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
127 |
-
>(({ className, ...props }, ref) => (
|
128 |
-
<SelectPrimitive.Separator ref={ref} className={cn('-mx-1 my-1 h-px bg-muted', className)} {...props} />
|
129 |
-
))
|
130 |
-
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
|
131 |
-
|
132 |
-
export {
|
133 |
-
Select,
|
134 |
-
SelectGroup,
|
135 |
-
SelectValue,
|
136 |
-
SelectTrigger,
|
137 |
-
SelectContent,
|
138 |
-
SelectLabel,
|
139 |
-
SelectItem,
|
140 |
-
SelectSeparator,
|
141 |
-
SelectScrollUpButton,
|
142 |
-
SelectScrollDownButton,
|
143 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/components/ui/separator.tsx
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import * as React from "react"
|
2 |
+
import * as SeparatorPrimitive from "@radix-ui/react-separator"
|
3 |
+
|
4 |
+
import { cn } from "@/lib/utils"
|
5 |
+
|
6 |
+
const Separator = React.forwardRef<
|
7 |
+
React.ElementRef<typeof SeparatorPrimitive.Root>,
|
8 |
+
React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
|
9 |
+
>(
|
10 |
+
(
|
11 |
+
{ className, orientation = "horizontal", decorative = true, ...props },
|
12 |
+
ref
|
13 |
+
) => (
|
14 |
+
<SeparatorPrimitive.Root
|
15 |
+
ref={ref}
|
16 |
+
decorative={decorative}
|
17 |
+
orientation={orientation}
|
18 |
+
className={cn(
|
19 |
+
"shrink-0 bg-border",
|
20 |
+
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
|
21 |
+
className
|
22 |
+
)}
|
23 |
+
{...props}
|
24 |
+
/>
|
25 |
+
)
|
26 |
+
)
|
27 |
+
Separator.displayName = SeparatorPrimitive.Root.displayName
|
28 |
+
|
29 |
+
export { Separator }
|