@pands/ui 0.0.0-stage → 0.1.1

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/README.md CHANGED
@@ -1,3 +1,70 @@
1
- # Temporary Holding Version
1
+ # @pands/ui
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ > Accessible React component library with Tailwind CSS v4 for the **Pand Application Platform**.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@pands/ui.svg)](https://www.npmjs.com/package/@pands/ui)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ `@pands/ui` provides a unified set of modern, accessible UI components styled with Tailwind CSS v4, including forms, data tables, dialogs, cards, navigation bars, and application shells.
9
+
10
+ ---
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ pnpm add @pands/ui
16
+ # or with npm:
17
+ npm install @pands/ui
18
+ ```
19
+
20
+ ---
21
+
22
+ ## Setup with Tailwind CSS v4
23
+
24
+ In your application's `styles.css`:
25
+
26
+ ```css
27
+ @import "tailwindcss";
28
+ @source "../node_modules/@pands/ui";
29
+ ```
30
+
31
+ And import styles in your entry point:
32
+ ```typescript
33
+ import "@pands/ui/styles.css";
34
+ ```
35
+
36
+ ---
37
+
38
+ ## Usage
39
+
40
+ ```tsx
41
+ import {
42
+ Button,
43
+ Input,
44
+ Card,
45
+ Badge,
46
+ DataTable,
47
+ Dialog,
48
+ AppShell
49
+ } from "@pands/ui";
50
+
51
+ export function Dashboard() {
52
+ return (
53
+ <Card className="p-6 space-y-4">
54
+ <div className="flex items-center justify-between">
55
+ <h2 className="text-xl font-semibold">Active Invoices</h2>
56
+ <Badge variant="success">Live</Badge>
57
+ </div>
58
+
59
+ <Input placeholder="Filter by invoice ID..." />
60
+ <Button variant="primary">Create Invoice</Button>
61
+ </Card>
62
+ );
63
+ }
64
+ ```
65
+
66
+ ---
67
+
68
+ ## License
69
+
70
+ MIT © [Mehdi Nosrati](https://github.com/MehdiNosrati)
@@ -0,0 +1,65 @@
1
+ import { ClassValue } from 'clsx';
2
+ import React from 'react';
3
+
4
+ declare function cn(...inputs: ClassValue[]): string;
5
+ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
6
+ variant?: "primary" | "secondary" | "danger" | "ghost";
7
+ size?: "sm" | "md" | "lg";
8
+ }
9
+ declare function Button({ variant, size, className, children, ...props }: ButtonProps): React.JSX.Element;
10
+ interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
11
+ label?: string;
12
+ }
13
+ declare function Input({ label, className, id, ...props }: InputProps): React.JSX.Element;
14
+ interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
15
+ label?: string;
16
+ }
17
+ declare function Textarea({ label, className, id, ...props }: TextareaProps): React.JSX.Element;
18
+ interface SelectOption {
19
+ label: string;
20
+ value: string;
21
+ }
22
+ interface SelectProps extends React.SelectHTMLAttributes<HTMLSelectElement> {
23
+ label?: string;
24
+ options: SelectOption[];
25
+ }
26
+ declare function Select({ label, options, className, id, ...props }: SelectProps): React.JSX.Element;
27
+ interface CheckboxProps extends React.InputHTMLAttributes<HTMLInputElement> {
28
+ label?: string;
29
+ }
30
+ declare function Checkbox({ label, className, id, ...props }: CheckboxProps): React.JSX.Element;
31
+ interface BadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
32
+ variant?: "default" | "success" | "warning" | "danger";
33
+ }
34
+ declare function Badge({ variant, className, children, ...props }: BadgeProps): React.JSX.Element;
35
+ declare function Card({ className, children, ...props }: React.HTMLAttributes<HTMLDivElement>): React.JSX.Element;
36
+ interface DialogProps {
37
+ open: boolean;
38
+ title: string;
39
+ onClose: () => void;
40
+ children: React.ReactNode;
41
+ }
42
+ declare function Dialog({ open, title, onClose, children }: DialogProps): React.JSX.Element | null;
43
+ declare function Table({ className, children, ...props }: React.TableHTMLAttributes<HTMLTableElement>): React.JSX.Element;
44
+ interface NavItem {
45
+ label: string;
46
+ to: string;
47
+ }
48
+ interface AppShellProps {
49
+ title: string;
50
+ navItems: NavItem[];
51
+ currentPath?: string;
52
+ onNavigate?: (to: string) => void;
53
+ userEmail?: string | null;
54
+ onSignOut?: () => void;
55
+ children: React.ReactNode;
56
+ }
57
+ declare function AppShell({ title, navItems, currentPath, onNavigate, userEmail, onSignOut, children, }: AppShellProps): React.JSX.Element;
58
+ interface ToastProps {
59
+ message: string | null;
60
+ variant?: "info" | "success" | "error";
61
+ onDismiss?: () => void;
62
+ }
63
+ declare function Toast({ message, variant, onDismiss }: ToastProps): React.JSX.Element | null;
64
+
65
+ export { AppShell, type AppShellProps, Badge, type BadgeProps, Button, type ButtonProps, Card, Checkbox, type CheckboxProps, Dialog, type DialogProps, Input, type InputProps, type NavItem, Select, type SelectOption, type SelectProps, Table, Textarea, type TextareaProps, Toast, type ToastProps, cn };
package/dist/index.js ADDED
@@ -0,0 +1,242 @@
1
+ // src/index.tsx
2
+ import { clsx } from "clsx";
3
+ import { twMerge } from "tailwind-merge";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ function cn(...inputs) {
6
+ return twMerge(clsx(inputs));
7
+ }
8
+ function Button({
9
+ variant = "primary",
10
+ size = "md",
11
+ className,
12
+ children,
13
+ ...props
14
+ }) {
15
+ const variantStyles = {
16
+ primary: "bg-blue-600 text-white hover:bg-blue-700 border-transparent",
17
+ secondary: "bg-white text-slate-900 border-slate-300 hover:bg-slate-50",
18
+ danger: "bg-red-600 text-white hover:bg-red-700 border-transparent",
19
+ ghost: "bg-transparent text-slate-700 hover:bg-slate-100 border-transparent"
20
+ };
21
+ const sizeStyles = {
22
+ sm: "px-2.5 py-1.5 text-xs",
23
+ md: "px-4 py-2 text-sm",
24
+ lg: "px-5 py-2.5 text-base"
25
+ };
26
+ return /* @__PURE__ */ jsx(
27
+ "button",
28
+ {
29
+ type: "button",
30
+ className: cn(
31
+ "inline-flex items-center justify-center rounded-md border font-medium transition-colors disabled:opacity-50 disabled:pointer-events-none cursor-pointer",
32
+ variantStyles[variant],
33
+ sizeStyles[size],
34
+ className
35
+ ),
36
+ ...props,
37
+ children
38
+ }
39
+ );
40
+ }
41
+ function Input({ label, className, id, ...props }) {
42
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
43
+ label ? /* @__PURE__ */ jsx("label", { htmlFor: id, className: "text-xs font-medium text-slate-700", children: label }) : null,
44
+ /* @__PURE__ */ jsx(
45
+ "input",
46
+ {
47
+ id,
48
+ className: cn(
49
+ "w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 focus:border-blue-500 focus:outline-none",
50
+ className
51
+ ),
52
+ ...props
53
+ }
54
+ )
55
+ ] });
56
+ }
57
+ function Textarea({ label, className, id, ...props }) {
58
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
59
+ label ? /* @__PURE__ */ jsx("label", { htmlFor: id, className: "text-xs font-medium text-slate-700", children: label }) : null,
60
+ /* @__PURE__ */ jsx(
61
+ "textarea",
62
+ {
63
+ id,
64
+ className: cn(
65
+ "w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 focus:border-blue-500 focus:outline-none",
66
+ className
67
+ ),
68
+ ...props
69
+ }
70
+ )
71
+ ] });
72
+ }
73
+ function Select({ label, options, className, id, ...props }) {
74
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
75
+ label ? /* @__PURE__ */ jsx("label", { htmlFor: id, className: "text-xs font-medium text-slate-700", children: label }) : null,
76
+ /* @__PURE__ */ jsx(
77
+ "select",
78
+ {
79
+ id,
80
+ className: cn(
81
+ "w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 focus:border-blue-500 focus:outline-none",
82
+ className
83
+ ),
84
+ ...props,
85
+ children: options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
86
+ }
87
+ )
88
+ ] });
89
+ }
90
+ function Checkbox({ label, className, id, ...props }) {
91
+ return /* @__PURE__ */ jsxs("label", { htmlFor: id, className: cn("inline-flex items-center gap-2 text-sm", className), children: [
92
+ /* @__PURE__ */ jsx(
93
+ "input",
94
+ {
95
+ id,
96
+ type: "checkbox",
97
+ className: "h-4 w-4 rounded border-slate-300 text-blue-600 focus:ring-blue-500",
98
+ ...props
99
+ }
100
+ ),
101
+ label ? /* @__PURE__ */ jsx("span", { className: "text-slate-700", children: label }) : null
102
+ ] });
103
+ }
104
+ function Badge({ variant = "default", className, children, ...props }) {
105
+ const styles = {
106
+ default: "bg-slate-100 text-slate-800",
107
+ success: "bg-emerald-100 text-emerald-800",
108
+ warning: "bg-amber-100 text-amber-800",
109
+ danger: "bg-red-100 text-red-800"
110
+ };
111
+ return /* @__PURE__ */ jsx(
112
+ "span",
113
+ {
114
+ className: cn(
115
+ "inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium",
116
+ styles[variant],
117
+ className
118
+ ),
119
+ ...props,
120
+ children
121
+ }
122
+ );
123
+ }
124
+ function Card({ className, children, ...props }) {
125
+ return /* @__PURE__ */ jsx(
126
+ "div",
127
+ {
128
+ className: cn("rounded-lg border border-slate-200 bg-white p-5 shadow-xs", className),
129
+ ...props,
130
+ children
131
+ }
132
+ );
133
+ }
134
+ function Dialog({ open, title, onClose, children }) {
135
+ if (!open) return null;
136
+ return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4", children: /* @__PURE__ */ jsxs("div", { className: "w-full max-w-lg rounded-lg bg-white p-6 shadow-xl", children: [
137
+ /* @__PURE__ */ jsxs("div", { className: "mb-4 flex items-center justify-between border-b border-slate-100 pb-3", children: [
138
+ /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold text-slate-900", children: title }),
139
+ /* @__PURE__ */ jsx(
140
+ "button",
141
+ {
142
+ type: "button",
143
+ onClick: onClose,
144
+ className: "text-slate-400 hover:text-slate-600 cursor-pointer",
145
+ children: "\u2715"
146
+ }
147
+ )
148
+ ] }),
149
+ /* @__PURE__ */ jsx("div", { children })
150
+ ] }) });
151
+ }
152
+ function Table({
153
+ className,
154
+ children,
155
+ ...props
156
+ }) {
157
+ return /* @__PURE__ */ jsx("div", { className: "w-full overflow-x-auto rounded-lg border border-slate-200 bg-white", children: /* @__PURE__ */ jsx("table", { className: cn("w-full border-collapse text-left text-sm", className), ...props, children }) });
158
+ }
159
+ function AppShell({
160
+ title,
161
+ navItems,
162
+ currentPath,
163
+ onNavigate,
164
+ userEmail,
165
+ onSignOut,
166
+ children
167
+ }) {
168
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-h-screen bg-slate-50 text-slate-900", children: [
169
+ /* @__PURE__ */ jsxs("aside", { className: "flex w-64 flex-col border-r border-slate-200 bg-white", children: [
170
+ /* @__PURE__ */ jsx("div", { className: "border-b border-slate-200 px-6 py-4", children: /* @__PURE__ */ jsx("span", { className: "text-lg font-bold tracking-tight text-slate-900", children: title }) }),
171
+ /* @__PURE__ */ jsx("nav", { className: "flex-1 space-y-1 p-3", children: navItems.map((item) => {
172
+ const active = currentPath === item.to;
173
+ return /* @__PURE__ */ jsx(
174
+ "a",
175
+ {
176
+ href: item.to,
177
+ onClick: (e) => {
178
+ if (onNavigate) {
179
+ e.preventDefault();
180
+ onNavigate(item.to);
181
+ }
182
+ },
183
+ className: cn(
184
+ "block rounded-md px-3 py-2 text-sm font-medium transition-colors",
185
+ active ? "bg-blue-50 text-blue-700" : "text-slate-600 hover:bg-slate-100 hover:text-slate-900"
186
+ ),
187
+ children: item.label
188
+ },
189
+ item.to
190
+ );
191
+ }) }),
192
+ userEmail ? /* @__PURE__ */ jsxs("div", { className: "border-t border-slate-200 p-4 text-xs text-slate-600", children: [
193
+ /* @__PURE__ */ jsx("div", { className: "truncate font-medium text-slate-900", children: userEmail }),
194
+ onSignOut ? /* @__PURE__ */ jsx(
195
+ "button",
196
+ {
197
+ type: "button",
198
+ onClick: onSignOut,
199
+ className: "mt-2 text-blue-600 hover:underline cursor-pointer",
200
+ children: "Sign out"
201
+ }
202
+ ) : null
203
+ ] }) : null
204
+ ] }),
205
+ /* @__PURE__ */ jsx("main", { className: "flex-1 p-8", children })
206
+ ] });
207
+ }
208
+ function Toast({ message, variant = "info", onDismiss }) {
209
+ if (!message) return null;
210
+ const styles = {
211
+ info: "bg-slate-900 text-white",
212
+ success: "bg-emerald-600 text-white",
213
+ error: "bg-red-600 text-white"
214
+ };
215
+ return /* @__PURE__ */ jsxs(
216
+ "div",
217
+ {
218
+ className: cn(
219
+ "fixed right-4 bottom-4 z-50 flex items-center gap-3 rounded-lg px-4 py-3 text-sm shadow-lg",
220
+ styles[variant]
221
+ ),
222
+ children: [
223
+ /* @__PURE__ */ jsx("span", { children: message }),
224
+ onDismiss ? /* @__PURE__ */ jsx("button", { type: "button", onClick: onDismiss, className: "opacity-80 hover:opacity-100", children: "\u2715" }) : null
225
+ ]
226
+ }
227
+ );
228
+ }
229
+ export {
230
+ AppShell,
231
+ Badge,
232
+ Button,
233
+ Card,
234
+ Checkbox,
235
+ Dialog,
236
+ Input,
237
+ Select,
238
+ Table,
239
+ Textarea,
240
+ Toast,
241
+ cn
242
+ };
package/package.json CHANGED
@@ -1,6 +1,53 @@
1
1
  {
2
2
  "name": "@pands/ui",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.1.1",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "./styles.css": "./src/styles.css"
14
+ },
15
+ "dependencies": {
16
+ "clsx": "^2.1.1",
17
+ "lucide-react": "^0.475.0",
18
+ "tailwind-merge": "^3.0.2"
19
+ },
20
+ "peerDependencies": {
21
+ "react": "^19.0.0",
22
+ "react-dom": "^19.0.0"
23
+ },
24
+ "devDependencies": {
25
+ "@types/react": "^19.0.10",
26
+ "@types/react-dom": "^19.0.4",
27
+ "react": "^19.0.0",
28
+ "react-dom": "^19.0.0"
29
+ },
30
+ "description": "Tailwind CSS and React component library for Pand applications",
31
+ "license": "MIT",
32
+ "author": "Mehdi Nosrati",
33
+ "repository": {
34
+ "type": "git",
35
+ "url": "git+https://github.com/MehdiNosrati/pand.git"
36
+ },
37
+ "homepage": "https://github.com/MehdiNosrati/pand#readme",
38
+ "bugs": {
39
+ "url": "https://github.com/MehdiNosrati/pand/issues"
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "files": [
45
+ "dist",
46
+ "src/styles.css"
47
+ ],
48
+ "scripts": {
49
+ "build": "tsup src/index.tsx --format esm --dts --clean --external react --external react-dom",
50
+ "typecheck": "tsc --noEmit",
51
+ "test": "node -e \"console.log('@pands/ui ok')\""
52
+ }
6
53
  }
package/src/styles.css ADDED
@@ -0,0 +1,31 @@
1
+ @import "tailwindcss";
2
+ @source "./";
3
+ @source "../dist";
4
+ @source "../../admin-ui";
5
+ @source "../node_modules/@pands/ui";
6
+ @source "../node_modules/@pands/admin-ui";
7
+
8
+ :root {
9
+ color-scheme: light;
10
+ --pand-bg: #f8fafc;
11
+ --pand-surface: #ffffff;
12
+ --pand-border: #e2e8f0;
13
+ --pand-text: #0f172a;
14
+ --pand-muted: #64748b;
15
+ --pand-primary: #2563eb;
16
+ --pand-primary-hover: #1d4ed8;
17
+ --pand-danger: #dc2626;
18
+ }
19
+
20
+ *,
21
+ *::before,
22
+ *::after {
23
+ box-sizing: border-box;
24
+ }
25
+
26
+ body {
27
+ background-color: var(--pand-bg);
28
+ color: var(--pand-text);
29
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
30
+ margin: 0;
31
+ }