@xyd-js/source-react-runtime 0.0.0-build-23166ce-20260423151359
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/CHANGELOG.md +9 -0
- package/LICENSE +21 -0
- package/README.md +121 -0
- package/__fixtures__/-1.vite-lib.custom-property/input/package.json +8 -0
- package/__fixtures__/-1.vite-lib.custom-property/input/src/UserCard.tsx +27 -0
- package/__fixtures__/-1.vite-lib.custom-property/input/src/index.ts +1 -0
- package/__fixtures__/-1.vite-lib.custom-property/input/tsconfig.json +12 -0
- package/__fixtures__/-1.vite-lib.custom-property/input/vite.config.ts +22 -0
- package/__fixtures__/-1.vite-lib.custom-property/output.js +10 -0
- package/__fixtures__/1.vite-lib.user-card/input/package.json +8 -0
- package/__fixtures__/1.vite-lib.user-card/input/src/UserCard.tsx +27 -0
- package/__fixtures__/1.vite-lib.user-card/input/src/index.ts +1 -0
- package/__fixtures__/1.vite-lib.user-card/input/tsconfig.json +12 -0
- package/__fixtures__/1.vite-lib.user-card/input/vite.config.ts +22 -0
- package/__fixtures__/1.vite-lib.user-card/output.js +10 -0
- package/__fixtures__/2.vite-lib.sample-app/input/package.json +8 -0
- package/__fixtures__/2.vite-lib.sample-app/input/src/components/UserProfile.tsx +42 -0
- package/__fixtures__/2.vite-lib.sample-app/input/src/contexts/UserContext.tsx +27 -0
- package/__fixtures__/2.vite-lib.sample-app/input/src/index.ts +3 -0
- package/__fixtures__/2.vite-lib.sample-app/input/src/types/user.ts +18 -0
- package/__fixtures__/2.vite-lib.sample-app/input/tsconfig.json +12 -0
- package/__fixtures__/2.vite-lib.sample-app/input/vite.config.ts +22 -0
- package/__fixtures__/2.vite-lib.sample-app/output.js +27 -0
- package/__fixtures__/3.vite-lib.sample-real-app/input/package.json +8 -0
- package/__fixtures__/3.vite-lib.sample-real-app/input/src/components/AddTodoForm.tsx +51 -0
- package/__fixtures__/3.vite-lib.sample-real-app/input/src/components/FilterBar.tsx +56 -0
- package/__fixtures__/3.vite-lib.sample-real-app/input/src/components/StatsPanel.tsx +44 -0
- package/__fixtures__/3.vite-lib.sample-real-app/input/src/components/TodoItem.tsx +54 -0
- package/__fixtures__/3.vite-lib.sample-real-app/input/src/index.ts +6 -0
- package/__fixtures__/3.vite-lib.sample-real-app/input/src/types/todo.ts +23 -0
- package/__fixtures__/3.vite-lib.sample-real-app/input/tsconfig.json +12 -0
- package/__fixtures__/3.vite-lib.sample-real-app/input/vite.config.ts +22 -0
- package/__fixtures__/3.vite-lib.sample-real-app/output.js +63 -0
- package/__fixtures__/4.vite-app.user-card/input/package.json +8 -0
- package/__fixtures__/4.vite-app.user-card/input/src/UserCard.tsx +27 -0
- package/__fixtures__/4.vite-app.user-card/input/src/index.ts +1 -0
- package/__fixtures__/4.vite-app.user-card/input/tsconfig.json +12 -0
- package/__fixtures__/4.vite-app.user-card/input/vite.config.ts +23 -0
- package/__fixtures__/4.vite-app.user-card/output.js +10 -0
- package/__fixtures__/5.rollup.user-card/input/package.json +8 -0
- package/__fixtures__/5.rollup.user-card/input/rollup.config.mjs +20 -0
- package/__fixtures__/5.rollup.user-card/input/src/UserCard.tsx +27 -0
- package/__fixtures__/5.rollup.user-card/input/src/index.ts +1 -0
- package/__fixtures__/5.rollup.user-card/input/tsconfig.json +12 -0
- package/__fixtures__/5.rollup.user-card/output.js +11 -0
- package/__fixtures__/6.esbuild.user-card/input/esbuild.config.mjs +19 -0
- package/__fixtures__/6.esbuild.user-card/input/package.json +8 -0
- package/__fixtures__/6.esbuild.user-card/input/src/UserCard.tsx +27 -0
- package/__fixtures__/6.esbuild.user-card/input/src/index.ts +1 -0
- package/__fixtures__/6.esbuild.user-card/input/tsconfig.json +12 -0
- package/__fixtures__/6.esbuild.user-card/output.js +11 -0
- package/__fixtures__/7.react-router.app/input/app/components/ProductCard.tsx +26 -0
- package/__fixtures__/7.react-router.app/input/app/entry.server.tsx +36 -0
- package/__fixtures__/7.react-router.app/input/app/root.tsx +23 -0
- package/__fixtures__/7.react-router.app/input/app/routes/cart.tsx +16 -0
- package/__fixtures__/7.react-router.app/input/app/routes/home.tsx +29 -0
- package/__fixtures__/7.react-router.app/input/app/routes/product.tsx +6 -0
- package/__fixtures__/7.react-router.app/input/app/routes.ts +7 -0
- package/__fixtures__/7.react-router.app/input/app/types/product.ts +12 -0
- package/__fixtures__/7.react-router.app/input/package.json +8 -0
- package/__fixtures__/7.react-router.app/input/react-router.config.ts +2 -0
- package/__fixtures__/7.react-router.app/input/tsconfig.json +13 -0
- package/__fixtures__/7.react-router.app/input/vite.config.ts +14 -0
- package/__fixtures__/7.react-router.app/output.js +44 -0
- package/__fixtures__/8.tanstack-router.app/input/index.html +5 -0
- package/__fixtures__/8.tanstack-router.app/input/package.json +8 -0
- package/__fixtures__/8.tanstack-router.app/input/src/components/EmployeeTable.tsx +45 -0
- package/__fixtures__/8.tanstack-router.app/input/src/main.tsx +19 -0
- package/__fixtures__/8.tanstack-router.app/input/src/routeTree.gen.ts +77 -0
- package/__fixtures__/8.tanstack-router.app/input/src/routes/__root.tsx +13 -0
- package/__fixtures__/8.tanstack-router.app/input/src/routes/employees.tsx +23 -0
- package/__fixtures__/8.tanstack-router.app/input/src/routes/index.tsx +5 -0
- package/__fixtures__/8.tanstack-router.app/input/src/types/employee.ts +13 -0
- package/__fixtures__/8.tanstack-router.app/input/tsconfig.json +12 -0
- package/__fixtures__/8.tanstack-router.app/input/vite.config.ts +21 -0
- package/__fixtures__/8.tanstack-router.app/output.js +63 -0
- package/__tests__/source-react-runtime.test.ts +61 -0
- package/__tests__/utils.ts +100 -0
- package/dist/esbuild.d.ts +20 -0
- package/dist/esbuild.js +378 -0
- package/dist/esbuild.js.map +1 -0
- package/dist/index.d.ts +53 -0
- package/dist/index.js +348 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
- package/src/esbuild.ts +45 -0
- package/src/index.ts +437 -0
- package/src/json-schema-to-uniform.ts +108 -0
- package/tsconfig.json +16 -0
- package/tsconfig.tsup.json +6 -0
- package/tsup.config.ts +23 -0
- package/vitest.config.ts +7 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { TodoStats } from "../types/todo";
|
|
2
|
+
|
|
3
|
+
interface StatCardProps {
|
|
4
|
+
/** Stat label displayed above the value */
|
|
5
|
+
label: string;
|
|
6
|
+
/** Numeric value to display */
|
|
7
|
+
value: number;
|
|
8
|
+
/** Color accent for the value */
|
|
9
|
+
color?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface StatsPanelProps {
|
|
13
|
+
/** Todo statistics to display */
|
|
14
|
+
stats: TodoStats;
|
|
15
|
+
/** Render in compact horizontal mode */
|
|
16
|
+
compact?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function StatCard({ label, value, color }: StatCardProps) {
|
|
20
|
+
return (
|
|
21
|
+
<div className="stat-card">
|
|
22
|
+
<span className="stat-label">{label}</span>
|
|
23
|
+
<span className="stat-value" style={{ color }}>{value}</span>
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function StatsPanel({ stats, compact }: StatsPanelProps) {
|
|
29
|
+
const cards = [
|
|
30
|
+
{ label: "Total", value: stats.total },
|
|
31
|
+
{ label: "Active", value: stats.active, color: "#3b82f6" },
|
|
32
|
+
{ label: "Done", value: stats.completed, color: "#22c55e" },
|
|
33
|
+
{ label: "Overdue", value: stats.overdue, color: stats.overdue > 0 ? "#ef4444" : undefined },
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div className={`stats-panel ${compact ? "compact" : ""}`}>
|
|
38
|
+
{cards.map((c) => (
|
|
39
|
+
<StatCard key={c.label} label={c.label} value={c.value} color={c.color} />
|
|
40
|
+
))}
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import type { Todo, Priority } from "../types/todo";
|
|
3
|
+
|
|
4
|
+
interface TodoItemProps {
|
|
5
|
+
/** The todo data to render */
|
|
6
|
+
todo: Todo;
|
|
7
|
+
|
|
8
|
+
/** Called when the checkbox is toggled */
|
|
9
|
+
onToggle: (id: string) => void;
|
|
10
|
+
|
|
11
|
+
/** Called when the delete button is clicked */
|
|
12
|
+
onDelete: (id: string) => void;
|
|
13
|
+
|
|
14
|
+
/** Called when the todo is updated */
|
|
15
|
+
onUpdate: (id: string, updates: Partial<Pick<Todo, "title" | "description" | "priority" | "tags">>) => void;
|
|
16
|
+
|
|
17
|
+
/** Enable inline editing mode on mount */
|
|
18
|
+
autoEdit?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function TodoItem({ todo, onToggle, onDelete, onUpdate, autoEdit = false }: TodoItemProps) {
|
|
22
|
+
const [editing, setEditing] = useState(autoEdit);
|
|
23
|
+
const [editTitle, setEditTitle] = useState(todo.title);
|
|
24
|
+
const isOverdue = !todo.completed && todo.dueDate && new Date(todo.dueDate) < new Date();
|
|
25
|
+
|
|
26
|
+
const handleSave = () => {
|
|
27
|
+
if (editTitle.trim()) {
|
|
28
|
+
onUpdate(todo.id, { title: editTitle.trim() });
|
|
29
|
+
}
|
|
30
|
+
setEditing(false);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div className={`todo-item ${todo.completed ? "completed" : ""} ${isOverdue ? "overdue" : ""}`}>
|
|
35
|
+
<input type="checkbox" checked={todo.completed} onChange={() => onToggle(todo.id)} />
|
|
36
|
+
{editing ? (
|
|
37
|
+
<input value={editTitle} onChange={(e) => setEditTitle(e.target.value)} onBlur={handleSave} autoFocus />
|
|
38
|
+
) : (
|
|
39
|
+
<span onDoubleClick={() => setEditing(true)}>{todo.title}</span>
|
|
40
|
+
)}
|
|
41
|
+
{todo.description && <p className="description">{todo.description}</p>}
|
|
42
|
+
<div className="tags">
|
|
43
|
+
<span className={`priority priority-${todo.priority}`}>{todo.priority}</span>
|
|
44
|
+
{todo.tags.map((tag) => <span key={tag} className="tag">{tag}</span>)}
|
|
45
|
+
{isOverdue && <span className="overdue-badge">overdue</span>}
|
|
46
|
+
</div>
|
|
47
|
+
<div className="actions">
|
|
48
|
+
<button type="button" onClick={() => setEditing(true)}>Edit</button>
|
|
49
|
+
<button type="button" onClick={() => onDelete(todo.id)}>Delete</button>
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { TodoItem } from "./components/TodoItem";
|
|
2
|
+
export { AddTodoForm } from "./components/AddTodoForm";
|
|
3
|
+
export { FilterBar } from "./components/FilterBar";
|
|
4
|
+
export { StatsPanel } from "./components/StatsPanel";
|
|
5
|
+
|
|
6
|
+
export type { Todo, TodoStats, Priority, FilterStatus } from "./types/todo";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type Priority = "low" | "medium" | "high" | "urgent";
|
|
2
|
+
|
|
3
|
+
export type FilterStatus = "all" | "active" | "completed";
|
|
4
|
+
|
|
5
|
+
export interface Todo {
|
|
6
|
+
id: string;
|
|
7
|
+
title: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
completed: boolean;
|
|
10
|
+
priority: Priority;
|
|
11
|
+
tags: string[];
|
|
12
|
+
createdAt: string;
|
|
13
|
+
completedAt?: string;
|
|
14
|
+
dueDate?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface TodoStats {
|
|
18
|
+
total: number;
|
|
19
|
+
active: number;
|
|
20
|
+
completed: number;
|
|
21
|
+
overdue: number;
|
|
22
|
+
byPriority: Record<Priority, number>;
|
|
23
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {defineConfig} from 'vite';
|
|
2
|
+
import {resolve} from 'node:path';
|
|
3
|
+
import react from '@vitejs/plugin-react';
|
|
4
|
+
import {xydSourceReactRuntime} from '@xyd-js/source-react-runtime';
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
plugins: [
|
|
8
|
+
xydSourceReactRuntime(),
|
|
9
|
+
react(),
|
|
10
|
+
],
|
|
11
|
+
build: {
|
|
12
|
+
lib: {
|
|
13
|
+
entry: 'src/index.ts',
|
|
14
|
+
formats: ['es'],
|
|
15
|
+
fileName: 'index',
|
|
16
|
+
},
|
|
17
|
+
rollupOptions: {
|
|
18
|
+
external: ['react', 'react/jsx-runtime', 'react/jsx-dev-runtime', 'react-dom'],
|
|
19
|
+
},
|
|
20
|
+
minify: false,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// === index.js ===
|
|
2
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
function TodoItem({ todo, onToggle, onDelete, onUpdate, autoEdit = false }) {
|
|
5
|
+
const [editing, setEditing] = useState(autoEdit);
|
|
6
|
+
const [editTitle, setEditTitle] = useState(todo.title);
|
|
7
|
+
const isOverdue = !todo.completed && todo.dueDate && new Date(todo.dueDate) < /* @__PURE__ */ new Date();
|
|
8
|
+
const handleSave = () => {
|
|
9
|
+
if (editTitle.trim()) {
|
|
10
|
+
onUpdate(todo.id, { title: editTitle.trim() });
|
|
11
|
+
}
|
|
12
|
+
setEditing(false);
|
|
13
|
+
};
|
|
14
|
+
return jsxs("div", { className: `todo-item ${todo.completed ? "completed" : ""} ${isOverdue ? "overdue" : ""}`, children: [jsx("input", { type: "checkbox", checked: todo.completed, onChange: () => onToggle(todo.id) }), editing ? jsx("input", { value: editTitle, onChange: (e) => setEditTitle(e.target.value), onBlur: handleSave, autoFocus: true }) : jsx("span", { onDoubleClick: () => setEditing(true), children: todo.title }), todo.description && jsx("p", { className: "description", children: todo.description }), jsxs("div", { className: "tags", children: [jsx("span", { className: `priority priority-${todo.priority}`, children: todo.priority }), todo.tags.map((tag) => jsx("span", { className: "tag", children: tag }, tag)), isOverdue && jsx("span", { className: "overdue-badge", children: "overdue" })] }), jsxs("div", { className: "actions", children: [jsx("button", { type: "button", onClick: () => setEditing(true), children: "Edit" }), jsx("button", { type: "button", onClick: () => onDelete(todo.id), children: "Delete" })] })] });
|
|
15
|
+
}
|
|
16
|
+
TodoItem.__xydUniform = JSON.parse('{"title":"TodoItem","canonical":"","description":"","definitions":[{"title":"Props","properties":[{"name":"todo","type":"object","description":"","meta":[{"name":"required","value":"true"}],"properties":[{"name":"id","type":"string","description":"","meta":[{"name":"required","value":"true"}]},{"name":"title","type":"string","description":"","meta":[{"name":"required","value":"true"}]},{"name":"description","type":"string","description":"","meta":[]},{"name":"completed","type":"boolean","description":"","meta":[{"name":"required","value":"true"}]},{"name":"priority","type":"$xor","description":"","properties":[{"name":"priority","type":"object","description":"","meta":[{"name":"required","value":"true"}]},{"name":"priority","type":"object","description":"","meta":[{"name":"required","value":"true"}]},{"name":"priority","type":"object","description":"","meta":[{"name":"required","value":"true"}]},{"name":"priority","type":"object","description":"","meta":[{"name":"required","value":"true"}]}],"meta":[{"name":"required","value":"true"}]},{"name":"tags","type":"$array","description":"","meta":[{"name":"required","value":"true"}],"properties":[],"ofProperty":{"name":"","type":"string","properties":[],"description":"","meta":[{"name":"required","value":"true"}]}},{"name":"createdAt","type":"string","description":"","meta":[{"name":"required","value":"true"}]},{"name":"completedAt","type":"string","description":"","meta":[]},{"name":"dueDate","type":"string","description":"","meta":[]}]},{"name":"autoEdit","type":"boolean","description":"Enable inline editing mode on mount","meta":[]}],"meta":[{"name":"type","value":"parameters"}]}],"examples":{"groups":[]}}');
|
|
17
|
+
function AddTodoForm({ onAdd, defaultPriority = "medium", placeholder = "What needs to be done?" }) {
|
|
18
|
+
const [title, setTitle] = useState("");
|
|
19
|
+
const [priority, setPriority] = useState(defaultPriority);
|
|
20
|
+
const [description, setDescription] = useState("");
|
|
21
|
+
const [tags, setTags] = useState("");
|
|
22
|
+
const handleSubmit = (e) => {
|
|
23
|
+
e.preventDefault();
|
|
24
|
+
if (!title.trim())
|
|
25
|
+
return;
|
|
26
|
+
onAdd(title.trim(), priority, description.trim() || void 0, tags.split(",").map((t) => t.trim()).filter(Boolean));
|
|
27
|
+
setTitle("");
|
|
28
|
+
setDescription("");
|
|
29
|
+
setTags("");
|
|
30
|
+
setPriority(defaultPriority);
|
|
31
|
+
};
|
|
32
|
+
return jsxs("form", { onSubmit: handleSubmit, className: "add-todo-form", children: [jsx("input", { type: "text", value: title, onChange: (e) => setTitle(e.target.value), placeholder }), jsxs("select", { value: priority, onChange: (e) => setPriority(e.target.value), children: [jsx("option", { value: "low", children: "Low" }), jsx("option", { value: "medium", children: "Medium" }), jsx("option", { value: "high", children: "High" }), jsx("option", { value: "urgent", children: "Urgent" })] }), jsx("textarea", { value: description, onChange: (e) => setDescription(e.target.value), placeholder: "Description (optional)" }), jsx("input", { type: "text", value: tags, onChange: (e) => setTags(e.target.value), placeholder: "Tags (comma separated)" }), jsx("button", { type: "submit", disabled: !title.trim(), children: "Add" })] });
|
|
33
|
+
}
|
|
34
|
+
AddTodoForm.__xydUniform = JSON.parse('{"title":"AddTodoForm","canonical":"","description":"","definitions":[{"title":"Props","properties":[{"name":"defaultPriority","type":"$xor","description":"Default priority for new todos","properties":[{"name":"defaultPriority","type":"object","description":"","meta":[]},{"name":"defaultPriority","type":"object","description":"","meta":[]},{"name":"defaultPriority","type":"object","description":"","meta":[]},{"name":"defaultPriority","type":"object","description":"","meta":[]}],"meta":[]},{"name":"placeholder","type":"string","description":"Placeholder text for the title input","meta":[]}],"meta":[{"name":"type","value":"parameters"}]}],"examples":{"groups":[]}}');
|
|
35
|
+
function FilterBar({ filter, onFilterChange, searchQuery, onSearchChange, stats, onClearCompleted, className }) {
|
|
36
|
+
const tabs = [
|
|
37
|
+
{ value: "all", label: `All (${stats.total})` },
|
|
38
|
+
{ value: "active", label: `Active (${stats.active})` },
|
|
39
|
+
{ value: "completed", label: `Done (${stats.completed})` }
|
|
40
|
+
];
|
|
41
|
+
return jsxs("div", { className: `filter-bar ${className || ""}`, children: [jsx("div", { className: "tabs", children: tabs.map((tab) => jsx("button", { type: "button", className: filter === tab.value ? "active" : "", onClick: () => onFilterChange(tab.value), children: tab.label }, tab.value)) }), jsx("input", { type: "search", value: searchQuery, onChange: (e) => onSearchChange(e.target.value), placeholder: "Search..." }), stats.completed > 0 && jsx("button", { type: "button", onClick: onClearCompleted, className: "clear-btn", children: "Clear completed" })] });
|
|
42
|
+
}
|
|
43
|
+
FilterBar.__xydUniform = JSON.parse('{"title":"FilterBar","canonical":"","description":"","definitions":[{"title":"Props","properties":[{"name":"filter","type":"$xor","description":"","properties":[{"name":"filter","type":"object","description":"","meta":[{"name":"required","value":"true"}]},{"name":"filter","type":"object","description":"","meta":[{"name":"required","value":"true"}]},{"name":"filter","type":"object","description":"","meta":[{"name":"required","value":"true"}]}],"meta":[{"name":"required","value":"true"}]},{"name":"searchQuery","type":"string","description":"Current search query","meta":[{"name":"required","value":"true"}]},{"name":"stats","type":"object","description":"","meta":[{"name":"required","value":"true"}],"properties":[{"name":"total","type":"number","description":"","meta":[{"name":"required","value":"true"}]},{"name":"active","type":"number","description":"","meta":[{"name":"required","value":"true"}]},{"name":"completed","type":"number","description":"","meta":[{"name":"required","value":"true"}]},{"name":"overdue","type":"number","description":"","meta":[{"name":"required","value":"true"}]},{"name":"byPriority","type":"object","description":"Construct a type with a set of properties K of type T","meta":[{"name":"required","value":"true"}],"properties":[{"name":"low","type":"number","description":"","meta":[{"name":"required","value":"true"}]},{"name":"medium","type":"number","description":"","meta":[{"name":"required","value":"true"}]},{"name":"high","type":"number","description":"","meta":[{"name":"required","value":"true"}]},{"name":"urgent","type":"number","description":"","meta":[{"name":"required","value":"true"}]}]}]},{"name":"className","type":"string","description":"Additional CSS classes","meta":[]}],"meta":[{"name":"type","value":"parameters"}]}],"examples":{"groups":[]}}');
|
|
44
|
+
function StatCard({ label, value, color }) {
|
|
45
|
+
return jsxs("div", { className: "stat-card", children: [jsx("span", { className: "stat-label", children: label }), jsx("span", { className: "stat-value", style: { color }, children: value })] });
|
|
46
|
+
}
|
|
47
|
+
function StatsPanel({ stats, compact }) {
|
|
48
|
+
const cards = [
|
|
49
|
+
{ label: "Total", value: stats.total },
|
|
50
|
+
{ label: "Active", value: stats.active, color: "#3b82f6" },
|
|
51
|
+
{ label: "Done", value: stats.completed, color: "#22c55e" },
|
|
52
|
+
{ label: "Overdue", value: stats.overdue, color: stats.overdue > 0 ? "#ef4444" : void 0 }
|
|
53
|
+
];
|
|
54
|
+
return jsx("div", { className: `stats-panel ${compact ? "compact" : ""}`, children: cards.map((c) => jsx(StatCard, { label: c.label, value: c.value, color: c.color }, c.label)) });
|
|
55
|
+
}
|
|
56
|
+
StatsPanel.__xydUniform = JSON.parse('{"title":"StatsPanel","canonical":"","description":"","definitions":[{"title":"Props","properties":[{"name":"stats","type":"object","description":"","meta":[{"name":"required","value":"true"}],"properties":[{"name":"total","type":"number","description":"","meta":[{"name":"required","value":"true"}]},{"name":"active","type":"number","description":"","meta":[{"name":"required","value":"true"}]},{"name":"completed","type":"number","description":"","meta":[{"name":"required","value":"true"}]},{"name":"overdue","type":"number","description":"","meta":[{"name":"required","value":"true"}]},{"name":"byPriority","type":"object","description":"Construct a type with a set of properties K of type T","meta":[{"name":"required","value":"true"}],"properties":[{"name":"low","type":"number","description":"","meta":[{"name":"required","value":"true"}]},{"name":"medium","type":"number","description":"","meta":[{"name":"required","value":"true"}]},{"name":"high","type":"number","description":"","meta":[{"name":"required","value":"true"}]},{"name":"urgent","type":"number","description":"","meta":[{"name":"required","value":"true"}]}]}]},{"name":"compact","type":"boolean","description":"Render in compact horizontal mode","meta":[]}],"meta":[{"name":"type","value":"parameters"}]}],"examples":{"groups":[]}}');
|
|
57
|
+
export {
|
|
58
|
+
AddTodoForm,
|
|
59
|
+
FilterBar,
|
|
60
|
+
StatsPanel,
|
|
61
|
+
TodoItem
|
|
62
|
+
};
|
|
63
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
interface UserCardProps {
|
|
2
|
+
/** Full name of the user */
|
|
3
|
+
name: string;
|
|
4
|
+
|
|
5
|
+
/** Email address of the user */
|
|
6
|
+
email: string;
|
|
7
|
+
|
|
8
|
+
/** URL to the user's avatar image */
|
|
9
|
+
avatarUrl?: string;
|
|
10
|
+
|
|
11
|
+
/** Role or title of the user */
|
|
12
|
+
role?: "admin" | "editor" | "viewer";
|
|
13
|
+
|
|
14
|
+
/** Whether the card is in a loading state */
|
|
15
|
+
loading?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function UserCard(props: UserCardProps) {
|
|
19
|
+
return (
|
|
20
|
+
<div>
|
|
21
|
+
{props.avatarUrl && <img src={props.avatarUrl} alt={props.name} />}
|
|
22
|
+
<h3>{props.name}</h3>
|
|
23
|
+
<p>{props.email}</p>
|
|
24
|
+
{props.role && <span>{props.role}</span>}
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {UserCard} from './UserCard';
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {defineConfig} from 'vite';
|
|
2
|
+
import {resolve} from 'node:path';
|
|
3
|
+
import react from '@vitejs/plugin-react';
|
|
4
|
+
import {xydSourceReactRuntime} from '@xyd-js/source-react-runtime';
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
plugins: [
|
|
8
|
+
xydSourceReactRuntime(),
|
|
9
|
+
react({jsxRuntime: 'automatic'}),
|
|
10
|
+
],
|
|
11
|
+
build: {
|
|
12
|
+
lib: {
|
|
13
|
+
entry: 'src/index.ts',
|
|
14
|
+
formats: ['es'],
|
|
15
|
+
fileName: 'index',
|
|
16
|
+
},
|
|
17
|
+
rollupOptions: {
|
|
18
|
+
external: ['react', 'react/jsx-runtime', 'react/jsx-dev-runtime', 'react-dom'],
|
|
19
|
+
},
|
|
20
|
+
minify: false,
|
|
21
|
+
sourcemap: false,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// === index.js ===
|
|
2
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
3
|
+
function UserCard(props) {
|
|
4
|
+
return jsxs("div", { children: [props.avatarUrl && jsx("img", { src: props.avatarUrl, alt: props.name }), jsx("h3", { children: props.name }), jsx("p", { children: props.email }), props.role && jsx("span", { children: props.role })] });
|
|
5
|
+
}
|
|
6
|
+
UserCard.__xydUniform = JSON.parse(`{"title":"UserCard","canonical":"","description":"","definitions":[{"title":"Props","properties":[{"name":"name","type":"string","description":"Full name of the user","meta":[{"name":"required","value":"true"}]},{"name":"email","type":"string","description":"Email address of the user","meta":[{"name":"required","value":"true"}]},{"name":"avatarUrl","type":"string","description":"URL to the user's avatar image","meta":[]},{"name":"role","type":"$xor","description":"Role or title of the user","properties":[{"name":"role","type":"object","description":"","meta":[]},{"name":"role","type":"object","description":"","meta":[]},{"name":"role","type":"object","description":"","meta":[]}],"meta":[]},{"name":"loading","type":"boolean","description":"Whether the card is in a loading state","meta":[]}],"meta":[{"name":"type","value":"parameters"}]}],"examples":{"groups":[]}}`);
|
|
7
|
+
export {
|
|
8
|
+
UserCard
|
|
9
|
+
};
|
|
10
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {resolve, dirname} from 'node:path';
|
|
2
|
+
import {fileURLToPath} from 'node:url';
|
|
3
|
+
import typescript from '@rollup/plugin-typescript';
|
|
4
|
+
import {xydSourceReactRuntime} from '@xyd-js/source-react-runtime';
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
input: 'src/index.ts',
|
|
10
|
+
external: ['react', 'react/jsx-runtime', 'react/jsx-dev-runtime', 'react-dom'],
|
|
11
|
+
output: {
|
|
12
|
+
dir: 'dist',
|
|
13
|
+
format: 'es',
|
|
14
|
+
entryFileNames: 'index.js',
|
|
15
|
+
},
|
|
16
|
+
plugins: [
|
|
17
|
+
xydSourceReactRuntime(),
|
|
18
|
+
typescript({tsconfig: resolve(__dirname, 'tsconfig.json'), compilerOptions: {declaration: false}}),
|
|
19
|
+
],
|
|
20
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
interface UserCardProps {
|
|
2
|
+
/** Full name of the user */
|
|
3
|
+
name: string;
|
|
4
|
+
|
|
5
|
+
/** Email address of the user */
|
|
6
|
+
email: string;
|
|
7
|
+
|
|
8
|
+
/** URL to the user's avatar image */
|
|
9
|
+
avatarUrl?: string;
|
|
10
|
+
|
|
11
|
+
/** Role or title of the user */
|
|
12
|
+
role?: "admin" | "editor" | "viewer";
|
|
13
|
+
|
|
14
|
+
/** Whether the card is in a loading state */
|
|
15
|
+
loading?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function UserCard(props: UserCardProps) {
|
|
19
|
+
return (
|
|
20
|
+
<div>
|
|
21
|
+
{props.avatarUrl && <img src={props.avatarUrl} alt={props.name} />}
|
|
22
|
+
<h3>{props.name}</h3>
|
|
23
|
+
<p>{props.email}</p>
|
|
24
|
+
{props.role && <span>{props.role}</span>}
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {UserCard} from './UserCard';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// === index.js ===
|
|
2
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
3
|
+
import 'typia';
|
|
4
|
+
|
|
5
|
+
function UserCard(props) {
|
|
6
|
+
return (jsxs("div", { children: [props.avatarUrl && jsx("img", { src: props.avatarUrl, alt: props.name }), jsx("h3", { children: props.name }), jsx("p", { children: props.email }), props.role && jsx("span", { children: props.role })] }));
|
|
7
|
+
}
|
|
8
|
+
UserCard.__xydUniform = JSON.parse('{"title":"UserCard","canonical":"","description":"","definitions":[{"title":"Props","properties":[{"name":"name","type":"string","description":"Full name of the user","meta":[{"name":"required","value":"true"}]},{"name":"email","type":"string","description":"Email address of the user","meta":[{"name":"required","value":"true"}]},{"name":"avatarUrl","type":"string","description":"URL to the user\'s avatar image","meta":[]},{"name":"role","type":"$xor","description":"Role or title of the user","properties":[{"name":"role","type":"object","description":"","meta":[]},{"name":"role","type":"object","description":"","meta":[]},{"name":"role","type":"object","description":"","meta":[]}],"meta":[]},{"name":"loading","type":"boolean","description":"Whether the card is in a loading state","meta":[]}],"meta":[{"name":"type","value":"parameters"}]}],"examples":{"groups":[]}}');
|
|
9
|
+
|
|
10
|
+
export { UserCard };
|
|
11
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {resolve, dirname} from 'node:path';
|
|
2
|
+
import {fileURLToPath} from 'node:url';
|
|
3
|
+
import * as esbuild from 'esbuild';
|
|
4
|
+
import {xydSourceReactRuntimeEsbuild} from '@xyd-js/source-react-runtime/esbuild';
|
|
5
|
+
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
|
|
8
|
+
await esbuild.build({
|
|
9
|
+
entryPoints: [resolve(__dirname, 'src/index.ts')],
|
|
10
|
+
outfile: resolve(__dirname, 'dist/index.js'),
|
|
11
|
+
bundle: true,
|
|
12
|
+
format: 'esm',
|
|
13
|
+
platform: 'browser',
|
|
14
|
+
external: ['react', 'react/jsx-runtime', 'react/jsx-dev-runtime', 'react-dom'],
|
|
15
|
+
minify: false,
|
|
16
|
+
plugins: [
|
|
17
|
+
xydSourceReactRuntimeEsbuild(),
|
|
18
|
+
],
|
|
19
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
interface UserCardProps {
|
|
2
|
+
/** Full name of the user */
|
|
3
|
+
name: string;
|
|
4
|
+
|
|
5
|
+
/** Email address of the user */
|
|
6
|
+
email: string;
|
|
7
|
+
|
|
8
|
+
/** URL to the user's avatar image */
|
|
9
|
+
avatarUrl?: string;
|
|
10
|
+
|
|
11
|
+
/** Role or title of the user */
|
|
12
|
+
role?: "admin" | "editor" | "viewer";
|
|
13
|
+
|
|
14
|
+
/** Whether the card is in a loading state */
|
|
15
|
+
loading?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function UserCard(props: UserCardProps) {
|
|
19
|
+
return (
|
|
20
|
+
<div>
|
|
21
|
+
{props.avatarUrl && <img src={props.avatarUrl} alt={props.name} />}
|
|
22
|
+
<h3>{props.name}</h3>
|
|
23
|
+
<p>{props.email}</p>
|
|
24
|
+
{props.role && <span>{props.role}</span>}
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {UserCard} from './UserCard';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// === index.js ===
|
|
2
|
+
// src/UserCard.tsx
|
|
3
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
4
|
+
function UserCard(props) {
|
|
5
|
+
return _jsxs("div", { children: [props.avatarUrl && _jsx("img", { src: props.avatarUrl, alt: props.name }), _jsx("h3", { children: props.name }), _jsx("p", { children: props.email }), props.role && _jsx("span", { children: props.role })] });
|
|
6
|
+
}
|
|
7
|
+
UserCard.__xydUniform = JSON.parse(`{"title":"UserCard","canonical":"","description":"","definitions":[{"title":"Props","properties":[{"name":"name","type":"string","description":"Full name of the user","meta":[{"name":"required","value":"true"}]},{"name":"email","type":"string","description":"Email address of the user","meta":[{"name":"required","value":"true"}]},{"name":"avatarUrl","type":"string","description":"URL to the user's avatar image","meta":[]},{"name":"role","type":"$xor","description":"Role or title of the user","properties":[{"name":"role","type":"object","description":"","meta":[]},{"name":"role","type":"object","description":"","meta":[]},{"name":"role","type":"object","description":"","meta":[]}],"meta":[]},{"name":"loading","type":"boolean","description":"Whether the card is in a loading state","meta":[]}],"meta":[{"name":"type","value":"parameters"}]}],"examples":{"groups":[]}}`);
|
|
8
|
+
export {
|
|
9
|
+
UserCard
|
|
10
|
+
};
|
|
11
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Link } from "react-router";
|
|
2
|
+
import type { Product } from "../types/product";
|
|
3
|
+
|
|
4
|
+
interface ProductCardProps {
|
|
5
|
+
product: Product;
|
|
6
|
+
onAddToCart: (productId: string) => void;
|
|
7
|
+
layout?: "grid" | "list";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function ProductCard({ product, onAddToCart, layout = "grid" }: ProductCardProps) {
|
|
11
|
+
return (
|
|
12
|
+
<div className={`product-card product-card--${layout}`}>
|
|
13
|
+
<Link to={`/products/${product.id}`}>
|
|
14
|
+
<h3>{product.name}</h3>
|
|
15
|
+
</Link>
|
|
16
|
+
<span>${product.price}</span>
|
|
17
|
+
<button
|
|
18
|
+
type="button"
|
|
19
|
+
disabled={!product.inStock}
|
|
20
|
+
onClick={() => onAddToCart(product.id)}
|
|
21
|
+
>
|
|
22
|
+
{product.inStock ? "Add to Cart" : "Out of Stock"}
|
|
23
|
+
</button>
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { PassThrough } from "node:stream";
|
|
2
|
+
import { createReadableStreamFromReadable } from "@react-router/node";
|
|
3
|
+
import { ServerRouter } from "react-router";
|
|
4
|
+
import { renderToPipeableStream } from "react-dom/server";
|
|
5
|
+
import type { EntryContext } from "react-router";
|
|
6
|
+
|
|
7
|
+
export default function handleRequest(
|
|
8
|
+
request: Request,
|
|
9
|
+
responseStatusCode: number,
|
|
10
|
+
responseHeaders: Headers,
|
|
11
|
+
routerContext: EntryContext,
|
|
12
|
+
) {
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
const { pipe } = renderToPipeableStream(
|
|
15
|
+
<ServerRouter context={routerContext} url={request.url} />,
|
|
16
|
+
{
|
|
17
|
+
onShellReady() {
|
|
18
|
+
const body = new PassThrough();
|
|
19
|
+
const stream = createReadableStreamFromReadable(body);
|
|
20
|
+
|
|
21
|
+
responseHeaders.set("Content-Type", "text/html");
|
|
22
|
+
resolve(
|
|
23
|
+
new Response(stream, {
|
|
24
|
+
headers: responseHeaders,
|
|
25
|
+
status: responseStatusCode,
|
|
26
|
+
}),
|
|
27
|
+
);
|
|
28
|
+
pipe(body);
|
|
29
|
+
},
|
|
30
|
+
onShellError(error: unknown) {
|
|
31
|
+
reject(error);
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Links, Meta, Outlet, Scripts, ScrollRestoration } from "react-router";
|
|
2
|
+
|
|
3
|
+
export function Layout({ children }: { children: React.ReactNode }) {
|
|
4
|
+
return (
|
|
5
|
+
<html lang="en">
|
|
6
|
+
<head>
|
|
7
|
+
<meta charSet="utf-8" />
|
|
8
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
9
|
+
<Meta />
|
|
10
|
+
<Links />
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
{children}
|
|
14
|
+
<ScrollRestoration />
|
|
15
|
+
<Scripts />
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default function Root() {
|
|
22
|
+
return <Outlet />;
|
|
23
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Link } from "react-router";
|
|
2
|
+
import type { CartItem } from "../types/product";
|
|
3
|
+
|
|
4
|
+
interface CartRouteProps {
|
|
5
|
+
items: CartItem[];
|
|
6
|
+
onRemove: (productId: string) => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default function CartRoute() {
|
|
10
|
+
return (
|
|
11
|
+
<div>
|
|
12
|
+
<h1>Cart</h1>
|
|
13
|
+
<Link to="/">Continue Shopping</Link>
|
|
14
|
+
</div>
|
|
15
|
+
);
|
|
16
|
+
}
|