react-scanner-ui 0.0.7 → 0.0.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-scanner-ui",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
6
  "react-scanner-ui": "dist/index.js"
@@ -0,0 +1,28 @@
1
+ .container {
2
+ max-width: 1200px;
3
+ margin: 0 auto;
4
+ padding: 24px;
5
+ }
6
+
7
+ h1 {
8
+ color: var(--color-text);
9
+ margin: 0 0 24px 0;
10
+ font-size: 28px;
11
+ font-weight: 600;
12
+ }
13
+
14
+ .loading {
15
+ color: var(--color-text-secondary);
16
+ font-style: italic;
17
+ padding: 20px;
18
+ text-align: center;
19
+ }
20
+
21
+ .error {
22
+ background-color: var(--color-error-bg);
23
+ border: 1px solid var(--color-error-border);
24
+ color: var(--color-error-text);
25
+ padding: 16px;
26
+ border-radius: var(--radius-sm);
27
+ margin-bottom: 20px;
28
+ }
@@ -0,0 +1,52 @@
1
+ import { useState, useEffect } from "react";
2
+ import { ComponentTable } from "./ComponentTable";
3
+ import "./App.css";
4
+
5
+ export interface ScanData {
6
+ [componentName: string]: {
7
+ instances: number;
8
+ props?: Record<string, number>;
9
+ };
10
+ }
11
+
12
+ interface ApiResponse {
13
+ data: ScanData | null;
14
+ error: string | null;
15
+ }
16
+
17
+ function App() {
18
+ const [data, setData] = useState<ScanData | null>(null);
19
+ const [error, setError] = useState<string | null>(null);
20
+ const [loading, setLoading] = useState(true);
21
+
22
+ useEffect(() => {
23
+ fetch("/api/scan-data")
24
+ .then((res) => res.json())
25
+ .then((result: ApiResponse) => {
26
+ if (result.error) {
27
+ setError(result.error);
28
+ } else {
29
+ setData(result.data);
30
+ }
31
+ setLoading(false);
32
+ })
33
+ .catch((err) => {
34
+ setError("Failed to fetch scan data: " + err.message);
35
+ setLoading(false);
36
+ });
37
+ }, []);
38
+
39
+ return (
40
+ <div className="container">
41
+ <h1>React Scanner UI</h1>
42
+ {loading && <div className="loading">Loading scan data...</div>}
43
+ {error && <div className="error">{error}</div>}
44
+ {!loading && !error && data && <ComponentTable data={data} />}
45
+ {!loading && !error && !data && (
46
+ <div className="error">No component data found.</div>
47
+ )}
48
+ </div>
49
+ );
50
+ }
51
+
52
+ export default App;
@@ -0,0 +1,61 @@
1
+ .component-table {
2
+ width: 100%;
3
+ border-collapse: collapse;
4
+ background-color: var(--color-bg-card);
5
+ box-shadow: var(--shadow-sm);
6
+ border-radius: var(--radius-md);
7
+ overflow: hidden;
8
+ }
9
+
10
+ .component-table th,
11
+ .component-table td {
12
+ padding: 12px 16px;
13
+ text-align: left;
14
+ border-bottom: 1px solid var(--color-border);
15
+ }
16
+
17
+ .component-table th {
18
+ background-color: var(--color-bg-header);
19
+ font-weight: 600;
20
+ color: var(--color-text);
21
+ cursor: pointer;
22
+ user-select: none;
23
+ transition: background-color 0.15s ease;
24
+ }
25
+
26
+ .component-table th:hover {
27
+ background-color: #e9ecef;
28
+ }
29
+
30
+ .component-table th .sort-indicator {
31
+ margin-left: 5px;
32
+ color: var(--color-text-muted);
33
+ font-size: 0.75em;
34
+ }
35
+
36
+ .component-table tbody tr:last-child td {
37
+ border-bottom: none;
38
+ }
39
+
40
+ .component-table tbody tr:hover td {
41
+ background-color: var(--color-bg-hover);
42
+ }
43
+
44
+ .component-table .count {
45
+ font-family: var(--font-mono);
46
+ font-weight: 500;
47
+ }
48
+
49
+ .component-table .total-row {
50
+ font-weight: 600;
51
+ background-color: var(--color-bg-total);
52
+ }
53
+
54
+ .component-table .total-row td {
55
+ border-top: 2px solid var(--color-border-strong);
56
+ border-bottom: none;
57
+ }
58
+
59
+ .component-table .total-row:hover td {
60
+ background-color: var(--color-bg-total);
61
+ }
@@ -0,0 +1,88 @@
1
+ import { useState } from "react";
2
+ import type { ScanData } from "./App";
3
+ import "./ComponentTable.css";
4
+
5
+ interface ComponentTableProps {
6
+ data: ScanData;
7
+ }
8
+
9
+ type SortKey = "name" | "count";
10
+ type SortDirection = "asc" | "desc";
11
+
12
+ interface SortConfig {
13
+ key: SortKey;
14
+ direction: SortDirection;
15
+ }
16
+
17
+ export function ComponentTable({ data }: ComponentTableProps) {
18
+ const [sortConfig, setSortConfig] = useState<SortConfig>({
19
+ key: "count",
20
+ direction: "desc",
21
+ });
22
+
23
+ const handleSort = (key: SortKey) => {
24
+ setSortConfig((prev) => ({
25
+ key,
26
+ direction: prev.key === key && prev.direction === "asc" ? "desc" : "asc",
27
+ }));
28
+ };
29
+
30
+ const getSortIndicator = (key: SortKey) => {
31
+ if (sortConfig.key !== key) return "";
32
+ return sortConfig.direction === "asc" ? "▲" : "▼";
33
+ };
34
+
35
+ // Transform data into array and sort
36
+ const components = Object.entries(data).map(([name, info]) => ({
37
+ name,
38
+ count: info.instances || 0,
39
+ }));
40
+
41
+ components.sort((a, b) => {
42
+ const aValue = sortConfig.key === "name" ? a.name.toLowerCase() : a.count;
43
+ const bValue = sortConfig.key === "name" ? b.name.toLowerCase() : b.count;
44
+
45
+ if (aValue < bValue) return sortConfig.direction === "asc" ? -1 : 1;
46
+ if (aValue > bValue) return sortConfig.direction === "asc" ? 1 : -1;
47
+ return 0;
48
+ });
49
+
50
+ const totalCount = components.reduce((sum, c) => sum + c.count, 0);
51
+
52
+ if (components.length === 0) {
53
+ return <div className="empty-state">No components found in scan data.</div>;
54
+ }
55
+
56
+ return (
57
+ <div className="table-container">
58
+ <table className="component-table">
59
+ <thead>
60
+ <tr>
61
+ <th onClick={() => handleSort("name")}>
62
+ Component
63
+ <span className="sort-indicator">{getSortIndicator("name")}</span>
64
+ </th>
65
+ <th onClick={() => handleSort("count")}>
66
+ Count
67
+ <span className="sort-indicator">
68
+ {getSortIndicator("count")}
69
+ </span>
70
+ </th>
71
+ </tr>
72
+ </thead>
73
+ <tbody>
74
+ {components.map((component) => (
75
+ <tr key={component.name}>
76
+ <td className="component-name">{component.name}</td>
77
+ <td className="count">{component.count}</td>
78
+ </tr>
79
+ ))}
80
+ <tr className="total-row">
81
+ <td>Total</td>
82
+ <td className="count">{totalCount}</td>
83
+ </tr>
84
+ </tbody>
85
+ </table>
86
+ </div>
87
+ );
88
+ }
package/ui/index.css ADDED
@@ -0,0 +1,59 @@
1
+ * {
2
+ box-sizing: border-box;
3
+ }
4
+
5
+ :root {
6
+ --color-bg: #f5f5f5;
7
+ --color-bg-card: #ffffff;
8
+ --color-bg-header: #f8f9fa;
9
+ --color-bg-hover: #f8f9fa;
10
+ --color-bg-total: #f0f0f0;
11
+ --color-text: #333333;
12
+ --color-text-secondary: #666666;
13
+ --color-text-muted: #999999;
14
+ --color-border: #eeeeee;
15
+ --color-border-strong: #dddddd;
16
+ --color-error-bg: #fee2e2;
17
+ --color-error-border: #fecaca;
18
+ --color-error-text: #dc2626;
19
+ --color-primary: #3b82f6;
20
+ --color-primary-hover: #2563eb;
21
+ --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
22
+ --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
23
+ --radius-sm: 4px;
24
+ --radius-md: 8px;
25
+ --font-mono: 'SF Mono', Monaco, 'Courier New', monospace;
26
+ --font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
27
+ }
28
+
29
+ body {
30
+ font-family: var(--font-sans);
31
+ margin: 0;
32
+ padding: 0;
33
+ background-color: var(--color-bg);
34
+ color: var(--color-text);
35
+ line-height: 1.5;
36
+ }
37
+
38
+ #root {
39
+ min-height: 100vh;
40
+ }
41
+
42
+ a {
43
+ color: var(--color-primary);
44
+ text-decoration: none;
45
+ }
46
+
47
+ a:hover {
48
+ color: var(--color-primary-hover);
49
+ text-decoration: underline;
50
+ }
51
+
52
+ button {
53
+ font-family: inherit;
54
+ cursor: pointer;
55
+ }
56
+
57
+ input, select, textarea {
58
+ font-family: inherit;
59
+ }
package/ui/index.html CHANGED
@@ -1,12 +1,12 @@
1
- <!DOCTYPE html>
1
+ <!doctype html>
2
2
  <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <title>React Scanner UI</title>
7
- </head>
8
- <body>
9
- <div id="root"></div>
10
- <script type="module" src="/src/main.tsx"></script>
11
- </body>
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>React Scanner UI</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="./main.tsx"></script>
11
+ </body>
12
12
  </html>
package/ui/main.tsx ADDED
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import ReactDOM from "react-dom/client";
3
+ import App from "./components/App";
4
+ import "./index.css";
5
+
6
+ ReactDOM.createRoot(document.getElementById("root")!).render(
7
+ <React.StrictMode>
8
+ <App />
9
+ </React.StrictMode>,
10
+ );