@launch77-shared/plugin-ui 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +242 -0
- package/dist/generator.js +135 -0
- package/package.json +47 -0
- package/plugin.json +9 -0
- package/templates/src/app/ui-examples/page.tsx +241 -0
- package/templates/src/modules/ui/README.md +229 -0
- package/templates/src/modules/ui/index.ts +14 -0
package/README.md
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# @launch77-shared/plugin-ui
|
|
2
|
+
|
|
3
|
+
Launch77 UI library plugin - Setup Radix UI component library with design system integration.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This plugin installs and configures the `@launch77-shared/lib-ui` component library in your Launch77 application. It automatically:
|
|
8
|
+
|
|
9
|
+
- ✅ Installs `@launch77-shared/lib-ui` package
|
|
10
|
+
- ✅ Configures Tailwind to include UI library components
|
|
11
|
+
- ✅ Verifies design system integration
|
|
12
|
+
- ✅ Provides interactive component showcase
|
|
13
|
+
- ✅ Includes comprehensive documentation
|
|
14
|
+
|
|
15
|
+
## Requirements
|
|
16
|
+
|
|
17
|
+
- **Design System Plugin**: This plugin requires the `design-system` plugin to be installed first
|
|
18
|
+
- **Tailwind CSS**: Your application must have Tailwind CSS configured
|
|
19
|
+
- **Next.js**: Compatible with Next.js App Router applications
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
From your Launch77 application directory:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
launch77 plugin:install ui
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The plugin will automatically:
|
|
30
|
+
|
|
31
|
+
1. Check for design-system plugin dependency
|
|
32
|
+
2. Install `@launch77-shared/lib-ui` package
|
|
33
|
+
3. Update `tailwind.config.ts` to include UI library in content paths
|
|
34
|
+
4. Copy example pages and documentation to your app
|
|
35
|
+
|
|
36
|
+
## What Gets Installed
|
|
37
|
+
|
|
38
|
+
### Package Dependencies
|
|
39
|
+
|
|
40
|
+
- `@launch77-shared/lib-ui` - UI component library
|
|
41
|
+
|
|
42
|
+
### Configuration Changes
|
|
43
|
+
|
|
44
|
+
**tailwind.config.ts**
|
|
45
|
+
|
|
46
|
+
- Adds `node_modules/@launch77-shared/lib-ui/dist/**/*.js` to content array
|
|
47
|
+
- Ensures Tailwind processes UI component styles
|
|
48
|
+
|
|
49
|
+
### Template Files
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
src/
|
|
53
|
+
├── app/
|
|
54
|
+
│ └── ui-examples/
|
|
55
|
+
│ └── page.tsx # Interactive component showcase
|
|
56
|
+
└── modules/
|
|
57
|
+
└── ui/
|
|
58
|
+
├── README.md # Component documentation
|
|
59
|
+
└── index.ts # Convenience re-exports
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Usage
|
|
63
|
+
|
|
64
|
+
### Importing Components
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
// Direct import from library
|
|
68
|
+
import { Button, Input, Card } from '@launch77-shared/lib-ui'
|
|
69
|
+
|
|
70
|
+
// Or from module re-exports
|
|
71
|
+
import { Button, Input, Card } from '@/modules/ui'
|
|
72
|
+
|
|
73
|
+
export default function MyComponent() {
|
|
74
|
+
return (
|
|
75
|
+
<Card className="p-6">
|
|
76
|
+
<h2>Hello World</h2>
|
|
77
|
+
<Input placeholder="Enter text..." />
|
|
78
|
+
<Button>Submit</Button>
|
|
79
|
+
</Card>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Available Components
|
|
85
|
+
|
|
86
|
+
- **Form Controls**: Button, Input, Textarea, Select, Checkbox, Switch
|
|
87
|
+
- **Layout**: Card, Sidebar
|
|
88
|
+
- **Feedback**: Alert, Badge, Dialog
|
|
89
|
+
- **Display**: Avatar
|
|
90
|
+
- **Form Utilities**: FormField, FormError, FormHelperText
|
|
91
|
+
|
|
92
|
+
### Component Showcase
|
|
93
|
+
|
|
94
|
+
Visit the interactive showcase to explore all components:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
npm run dev
|
|
98
|
+
# Navigate to http://localhost:3000/ui-examples
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Documentation
|
|
102
|
+
|
|
103
|
+
After installation, view detailed component documentation at:
|
|
104
|
+
|
|
105
|
+
- `src/modules/ui/README.md` - Full API reference and examples
|
|
106
|
+
- `/ui-examples` - Live interactive examples
|
|
107
|
+
|
|
108
|
+
## Architecture
|
|
109
|
+
|
|
110
|
+
### How It Works
|
|
111
|
+
|
|
112
|
+
1. **Plugin Dependencies**: Declares `design-system` as a required plugin
|
|
113
|
+
2. **Package Installation**: Adds `@launch77-shared/lib-ui` to your `package.json`
|
|
114
|
+
3. **Tailwind Integration**: Updates content paths so Tailwind processes component styles
|
|
115
|
+
4. **Template Copy**: Copies example pages and documentation to your app
|
|
116
|
+
|
|
117
|
+
### Generator Logic
|
|
118
|
+
|
|
119
|
+
The plugin's generator ([src/generator.ts](./src/generator.ts)) extends `StandardGenerator` and:
|
|
120
|
+
|
|
121
|
+
1. **`updateDependencies()`** - Merges library dependencies into your package.json
|
|
122
|
+
2. **`installDependencies()`** - Runs `npm install` to fetch packages
|
|
123
|
+
3. **`copyTemplates()`** - Copies example and documentation files
|
|
124
|
+
4. **`injectCode()`** - Updates Tailwind config with UI library content path
|
|
125
|
+
5. **`showNextSteps()`** - Displays usage instructions
|
|
126
|
+
|
|
127
|
+
## Customization
|
|
128
|
+
|
|
129
|
+
### Styling Components
|
|
130
|
+
|
|
131
|
+
All components support Tailwind utility classes via `className` prop:
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
<Button className="w-full bg-purple-600 hover:bg-purple-700">Custom Button</Button>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Brand Customization
|
|
138
|
+
|
|
139
|
+
Customize the design system tokens that affect all UI components:
|
|
140
|
+
|
|
141
|
+
```css
|
|
142
|
+
/* src/modules/design-system/config/brand.css */
|
|
143
|
+
:root {
|
|
144
|
+
--primary: 220 90% 56%;
|
|
145
|
+
--secondary: 240 5% 26%;
|
|
146
|
+
/* ... more tokens */
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Extending Components
|
|
151
|
+
|
|
152
|
+
Create custom variants by wrapping base components:
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
// src/components/PrimaryButton.tsx
|
|
156
|
+
import { Button } from '@launch77-shared/lib-ui'
|
|
157
|
+
|
|
158
|
+
export function PrimaryButton(props) {
|
|
159
|
+
return <Button className="bg-gradient-to-r from-blue-600 to-purple-600" {...props} />
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Troubleshooting
|
|
164
|
+
|
|
165
|
+
### Styles Not Appearing
|
|
166
|
+
|
|
167
|
+
If component styles aren't applying:
|
|
168
|
+
|
|
169
|
+
1. **Check Tailwind Config**: Verify `tailwind.config.ts` includes:
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
content: [
|
|
173
|
+
// ... other paths
|
|
174
|
+
'node_modules/@launch77-shared/lib-ui/dist/**/*.js',
|
|
175
|
+
]
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
2. **Rebuild**: Restart your dev server:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
npm run dev
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
3. **Design System**: Ensure design-system plugin is installed:
|
|
185
|
+
```bash
|
|
186
|
+
launch77 plugin:install design-system
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### TypeScript Errors
|
|
190
|
+
|
|
191
|
+
If you see TypeScript errors importing components:
|
|
192
|
+
|
|
193
|
+
1. **Install Dependencies**:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
npm install
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
2. **Check Package**: Verify `@launch77-shared/lib-ui` is in your `package.json`
|
|
200
|
+
|
|
201
|
+
### Missing Components
|
|
202
|
+
|
|
203
|
+
If example page shows missing components:
|
|
204
|
+
|
|
205
|
+
1. **Verify Installation**: Check `node_modules/@launch77-shared/lib-ui` exists
|
|
206
|
+
2. **Reinstall Plugin**:
|
|
207
|
+
```bash
|
|
208
|
+
launch77 plugin:install ui
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Development
|
|
212
|
+
|
|
213
|
+
### Building the Plugin
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
cd plugins/ui
|
|
217
|
+
npm run build
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Testing Locally
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
# In your app directory
|
|
224
|
+
launch77 plugin:install ui
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Related Plugins
|
|
228
|
+
|
|
229
|
+
- **design-system** (required) - Provides design tokens and Tailwind preset
|
|
230
|
+
- **analytics-web** - Track user interactions with UI components
|
|
231
|
+
|
|
232
|
+
## License
|
|
233
|
+
|
|
234
|
+
UNLICENSED - Internal use only
|
|
235
|
+
|
|
236
|
+
## Support
|
|
237
|
+
|
|
238
|
+
For issues or questions:
|
|
239
|
+
|
|
240
|
+
1. Check `src/modules/ui/README.md` for component documentation
|
|
241
|
+
2. Review design system at `/design-system-test`
|
|
242
|
+
3. Open an issue in the Launch77 CLI repository
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/generator.ts
|
|
4
|
+
import * as path from "path";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import { StandardGenerator } from "@launch77/plugin-runtime";
|
|
7
|
+
|
|
8
|
+
// src/utils/config-modifier.ts
|
|
9
|
+
import fs from "fs/promises";
|
|
10
|
+
async function addTailwindContent(filePath, contentPath) {
|
|
11
|
+
try {
|
|
12
|
+
const fileExists = await fs.access(filePath).then(() => true).catch(() => false);
|
|
13
|
+
if (!fileExists) {
|
|
14
|
+
return {
|
|
15
|
+
success: false,
|
|
16
|
+
error: `File not found: ${filePath}`
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
20
|
+
if (content.includes(contentPath)) {
|
|
21
|
+
return {
|
|
22
|
+
success: true,
|
|
23
|
+
alreadyExists: true
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const lines = content.split("\n");
|
|
27
|
+
let contentArrayStartIndex = -1;
|
|
28
|
+
let contentArrayEndIndex = -1;
|
|
29
|
+
for (let i = 0; i < lines.length; i++) {
|
|
30
|
+
const line = lines[i];
|
|
31
|
+
if (line.includes("content:") && line.includes("[")) {
|
|
32
|
+
contentArrayStartIndex = i;
|
|
33
|
+
if (line.includes("]")) {
|
|
34
|
+
contentArrayEndIndex = i;
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
38
|
+
if (lines[j].includes("]")) {
|
|
39
|
+
contentArrayEndIndex = j;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (contentArrayStartIndex === -1) {
|
|
47
|
+
return {
|
|
48
|
+
success: false,
|
|
49
|
+
error: "Could not find content array in Tailwind config"
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
let indentation = " ";
|
|
53
|
+
for (let i = contentArrayStartIndex + 1; i < contentArrayEndIndex; i++) {
|
|
54
|
+
const match = lines[i].match(/^(\s+)['"]/);
|
|
55
|
+
if (match) {
|
|
56
|
+
indentation = match[1];
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const newLine = `${indentation}'${contentPath}',`;
|
|
61
|
+
lines.splice(contentArrayEndIndex, 0, newLine);
|
|
62
|
+
const newContent = lines.join("\n");
|
|
63
|
+
await fs.writeFile(filePath, newContent, "utf-8");
|
|
64
|
+
return {
|
|
65
|
+
success: true
|
|
66
|
+
};
|
|
67
|
+
} catch (error) {
|
|
68
|
+
return {
|
|
69
|
+
success: false,
|
|
70
|
+
error: error instanceof Error ? error.message : String(error)
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// src/generator.ts
|
|
76
|
+
var UiGenerator = class extends StandardGenerator {
|
|
77
|
+
constructor(context) {
|
|
78
|
+
super(context);
|
|
79
|
+
}
|
|
80
|
+
async injectCode() {
|
|
81
|
+
console.log(chalk.cyan("\u{1F527} Setting up UI library...\n"));
|
|
82
|
+
await this.updateTailwindConfig();
|
|
83
|
+
}
|
|
84
|
+
async updateTailwindConfig() {
|
|
85
|
+
const tailwindConfigPath = path.join(this.context.appPath, "tailwind.config.ts");
|
|
86
|
+
const result1 = await addTailwindContent(tailwindConfigPath, "node_modules/@launch77-shared/lib-ui/dist/**/*.js");
|
|
87
|
+
const result2 = await addTailwindContent(tailwindConfigPath, "../../node_modules/@launch77-shared/lib-ui/dist/**/*.js");
|
|
88
|
+
if (result1.success || result2.success) {
|
|
89
|
+
if (result1.alreadyExists && result2.alreadyExists) {
|
|
90
|
+
console.log(chalk.gray(" \u2713 UI library already configured in tailwind.config.ts"));
|
|
91
|
+
} else {
|
|
92
|
+
console.log(chalk.green(" \u2713 Added UI library to Tailwind content paths"));
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
console.log(chalk.yellow(` \u26A0\uFE0F Could not auto-configure tailwind.config.ts: ${result1.error}`));
|
|
96
|
+
console.log(chalk.gray(" You will need to add the content paths manually:"));
|
|
97
|
+
console.log(chalk.gray(" 'node_modules/@launch77-shared/lib-ui/dist/**/*.js'"));
|
|
98
|
+
console.log(chalk.gray(" '../../node_modules/@launch77-shared/lib-ui/dist/**/*.js'"));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
showNextSteps() {
|
|
102
|
+
console.log(chalk.bold.green("\n\u2705 Plugin installed successfully!\n"));
|
|
103
|
+
console.log(chalk.bold("Next Steps:\n"));
|
|
104
|
+
console.log("1. Explore components:");
|
|
105
|
+
console.log(chalk.cyan(" Visit http://localhost:3000/ui-examples\n"));
|
|
106
|
+
console.log("2. Import components in your code:");
|
|
107
|
+
console.log(chalk.cyan(" import { Button, Input, Card } from '@launch77-shared/lib-ui'\n"));
|
|
108
|
+
console.log("3. View documentation:");
|
|
109
|
+
console.log(chalk.cyan(" See src/modules/ui/README.md\n"));
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
async function main() {
|
|
113
|
+
const args = process.argv.slice(2);
|
|
114
|
+
const appPath = args.find((arg) => arg.startsWith("--appPath="))?.split("=")[1];
|
|
115
|
+
const appName = args.find((arg) => arg.startsWith("--appName="))?.split("=")[1];
|
|
116
|
+
const workspaceName = args.find((arg) => arg.startsWith("--workspaceName="))?.split("=")[1];
|
|
117
|
+
const pluginPath = args.find((arg) => arg.startsWith("--pluginPath="))?.split("=")[1];
|
|
118
|
+
if (!appPath || !appName || !workspaceName || !pluginPath) {
|
|
119
|
+
console.error(chalk.red("Error: Missing required arguments"));
|
|
120
|
+
console.error("Usage: generate --appPath=<path> --appName=<name> --workspaceName=<workspace> --pluginPath=<path>");
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
const generator = new UiGenerator({ appPath, appName, workspaceName, pluginPath });
|
|
124
|
+
await generator.run();
|
|
125
|
+
}
|
|
126
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
127
|
+
main().catch((error) => {
|
|
128
|
+
console.error(chalk.red("\n\u274C Error during UI library setup:"));
|
|
129
|
+
console.error(error);
|
|
130
|
+
process.exit(1);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
export {
|
|
134
|
+
UiGenerator
|
|
135
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@launch77-shared/plugin-ui",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Launch77 UI library plugin - Setup Radix UI component library with design system integration",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/generator.js",
|
|
8
|
+
"bin": {
|
|
9
|
+
"generate": "./dist/generator.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist/",
|
|
13
|
+
"templates/",
|
|
14
|
+
"plugin.json"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsup",
|
|
18
|
+
"dev": "tsup --watch",
|
|
19
|
+
"typecheck": "tsc --noEmit",
|
|
20
|
+
"lint": "eslint src/**/*.ts",
|
|
21
|
+
"release:connect": "launch77-release-connect",
|
|
22
|
+
"release:verify": "launch77-release-verify"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@launch77/plugin-runtime": "^0.3.0",
|
|
26
|
+
"@launch77-shared/lib-ui": "^0.1.0",
|
|
27
|
+
"chalk": "^5.3.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^20.10.0",
|
|
31
|
+
"tsup": "^8.0.0",
|
|
32
|
+
"typescript": "^5.3.0"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"launch77": {
|
|
38
|
+
"installedPlugins": {
|
|
39
|
+
"release": {
|
|
40
|
+
"package": "release",
|
|
41
|
+
"version": "1.0.1",
|
|
42
|
+
"installedAt": "2026-01-26T00:47:19.303Z",
|
|
43
|
+
"source": "local"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
package/plugin.json
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
import { Alert, Avatar, Badge, Button, Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter, Checkbox, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, FormField, FormHelperText, FormError, Input, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Switch, Textarea } from '@launch77-shared/lib-ui'
|
|
5
|
+
|
|
6
|
+
export default function UiExamplesPage() {
|
|
7
|
+
const [checked, setChecked] = useState(false)
|
|
8
|
+
const [termsAccepted, setTermsAccepted] = useState(false)
|
|
9
|
+
const [termsError, setTermsError] = useState(false)
|
|
10
|
+
const [switchEnabled, setSwitchEnabled] = useState(false)
|
|
11
|
+
const [selectedValue, setSelectedValue] = useState('')
|
|
12
|
+
const [textareaValue, setTextareaValue] = useState('')
|
|
13
|
+
const [textareaError, setTextareaError] = useState(false)
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<div className="container mx-auto max-w-6xl py-12 px-4">
|
|
17
|
+
<div className="mb-12">
|
|
18
|
+
<h1 className="text-4xl font-bold mb-4">UI Component Library</h1>
|
|
19
|
+
<p className="text-muted-foreground text-lg">Explore the Launch77 UI component library built with Radix UI primitives</p>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<div className="space-y-12">
|
|
23
|
+
{/* Buttons */}
|
|
24
|
+
<section>
|
|
25
|
+
<h2 className="text-2xl font-semibold mb-6">Buttons</h2>
|
|
26
|
+
<div className="flex flex-wrap gap-4">
|
|
27
|
+
<Button>Default Button</Button>
|
|
28
|
+
<Button variant="secondary">Secondary</Button>
|
|
29
|
+
<Button variant="outline">Outline</Button>
|
|
30
|
+
<Button variant="ghost">Ghost</Button>
|
|
31
|
+
<Button variant="destructive">Destructive</Button>
|
|
32
|
+
<Button size="sm">Small</Button>
|
|
33
|
+
<Button size="lg">Large</Button>
|
|
34
|
+
<Button disabled>Disabled</Button>
|
|
35
|
+
</div>
|
|
36
|
+
</section>
|
|
37
|
+
|
|
38
|
+
{/* Inputs */}
|
|
39
|
+
<section>
|
|
40
|
+
<h2 className="text-2xl font-semibold mb-6">Form Controls</h2>
|
|
41
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 max-w-4xl">
|
|
42
|
+
<FormField label="Email Address" helperText="We'll never share your email" htmlFor="input-1">
|
|
43
|
+
<Input id="input-1" type="email" placeholder="Enter your email..." />
|
|
44
|
+
</FormField>
|
|
45
|
+
|
|
46
|
+
<FormField label="Password" error="Password must be at least 8 characters" required htmlFor="input-2">
|
|
47
|
+
<Input id="input-2" type="password" placeholder="Enter password..." />
|
|
48
|
+
</FormField>
|
|
49
|
+
|
|
50
|
+
<FormField label="Message" helperText="Enter a brief message (max 200 characters)" htmlFor="textarea">
|
|
51
|
+
<Textarea
|
|
52
|
+
id="textarea"
|
|
53
|
+
placeholder="Enter your message..."
|
|
54
|
+
rows={4}
|
|
55
|
+
value={textareaValue}
|
|
56
|
+
onChange={(e) => {
|
|
57
|
+
setTextareaValue(e.target.value)
|
|
58
|
+
setTextareaError(e.target.value.length > 200)
|
|
59
|
+
}}
|
|
60
|
+
error={textareaError}
|
|
61
|
+
/>
|
|
62
|
+
</FormField>
|
|
63
|
+
|
|
64
|
+
<FormField label="Country" htmlFor="select" required>
|
|
65
|
+
<Select value={selectedValue} onValueChange={setSelectedValue}>
|
|
66
|
+
<SelectTrigger id="select">
|
|
67
|
+
<SelectValue placeholder="Choose a country" />
|
|
68
|
+
</SelectTrigger>
|
|
69
|
+
<SelectContent>
|
|
70
|
+
<SelectItem value="us">United States</SelectItem>
|
|
71
|
+
<SelectItem value="ca">Canada</SelectItem>
|
|
72
|
+
<SelectItem value="uk">United Kingdom</SelectItem>
|
|
73
|
+
</SelectContent>
|
|
74
|
+
</Select>
|
|
75
|
+
</FormField>
|
|
76
|
+
</div>
|
|
77
|
+
</section>
|
|
78
|
+
|
|
79
|
+
{/* Checkbox & Switch */}
|
|
80
|
+
<section>
|
|
81
|
+
<h2 className="text-2xl font-semibold mb-6">Toggle Controls</h2>
|
|
82
|
+
<div className="space-y-4">
|
|
83
|
+
<div className="flex items-center gap-3">
|
|
84
|
+
<Checkbox id="checkbox-example" checked={checked} onCheckedChange={(value) => setChecked(value as boolean)} />
|
|
85
|
+
<label htmlFor="checkbox-example" className="cursor-pointer">
|
|
86
|
+
Checkbox example
|
|
87
|
+
</label>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<div className="space-y-2">
|
|
91
|
+
<div className="flex items-center gap-3">
|
|
92
|
+
<Checkbox
|
|
93
|
+
id="terms-checkbox"
|
|
94
|
+
checked={termsAccepted}
|
|
95
|
+
onCheckedChange={(value) => {
|
|
96
|
+
setTermsAccepted(value as boolean)
|
|
97
|
+
setTermsError(false)
|
|
98
|
+
}}
|
|
99
|
+
error={termsError}
|
|
100
|
+
/>
|
|
101
|
+
<label htmlFor="terms-checkbox" className="cursor-pointer">
|
|
102
|
+
I accept the terms and conditions (required)
|
|
103
|
+
</label>
|
|
104
|
+
</div>
|
|
105
|
+
{termsError && <FormError>You must accept the terms to continue</FormError>}
|
|
106
|
+
<Button
|
|
107
|
+
size="sm"
|
|
108
|
+
onClick={() => {
|
|
109
|
+
if (!termsAccepted) {
|
|
110
|
+
setTermsError(true)
|
|
111
|
+
}
|
|
112
|
+
}}
|
|
113
|
+
>
|
|
114
|
+
Test Validation
|
|
115
|
+
</Button>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<div className="flex items-center gap-3">
|
|
119
|
+
<Switch id="switch-example" checked={switchEnabled} onCheckedChange={setSwitchEnabled} />
|
|
120
|
+
<label htmlFor="switch-example" className="cursor-pointer">
|
|
121
|
+
Enable notifications
|
|
122
|
+
</label>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
</section>
|
|
126
|
+
|
|
127
|
+
{/* Cards */}
|
|
128
|
+
<section>
|
|
129
|
+
<h2 className="text-2xl font-semibold mb-6">Cards</h2>
|
|
130
|
+
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
131
|
+
<Card>
|
|
132
|
+
<CardHeader>
|
|
133
|
+
<CardTitle>Simple Card</CardTitle>
|
|
134
|
+
<CardDescription>A basic card with header and content</CardDescription>
|
|
135
|
+
</CardHeader>
|
|
136
|
+
<CardContent>
|
|
137
|
+
<p className="text-sm text-muted-foreground">This is the main content area of the card.</p>
|
|
138
|
+
</CardContent>
|
|
139
|
+
</Card>
|
|
140
|
+
|
|
141
|
+
<Card>
|
|
142
|
+
<CardHeader>
|
|
143
|
+
<CardTitle>Card with Action</CardTitle>
|
|
144
|
+
<CardDescription>Cards can have footers with actions</CardDescription>
|
|
145
|
+
</CardHeader>
|
|
146
|
+
<CardContent>
|
|
147
|
+
<p className="text-sm text-muted-foreground">Use CardFooter for action buttons.</p>
|
|
148
|
+
</CardContent>
|
|
149
|
+
<CardFooter>
|
|
150
|
+
<Button size="sm">View Details</Button>
|
|
151
|
+
</CardFooter>
|
|
152
|
+
</Card>
|
|
153
|
+
|
|
154
|
+
<Card>
|
|
155
|
+
<CardHeader>
|
|
156
|
+
<CardTitle>Interactive Card</CardTitle>
|
|
157
|
+
<CardDescription>Multiple actions in footer</CardDescription>
|
|
158
|
+
</CardHeader>
|
|
159
|
+
<CardContent>
|
|
160
|
+
<p className="text-sm text-muted-foreground">Cards support multiple action buttons.</p>
|
|
161
|
+
</CardContent>
|
|
162
|
+
<CardFooter className="flex gap-2">
|
|
163
|
+
<Button size="sm" variant="outline">
|
|
164
|
+
Cancel
|
|
165
|
+
</Button>
|
|
166
|
+
<Button size="sm">Confirm</Button>
|
|
167
|
+
</CardFooter>
|
|
168
|
+
</Card>
|
|
169
|
+
</div>
|
|
170
|
+
</section>
|
|
171
|
+
|
|
172
|
+
{/* Alerts */}
|
|
173
|
+
<section>
|
|
174
|
+
<h2 className="text-2xl font-semibold mb-6">Alerts</h2>
|
|
175
|
+
<div className="space-y-4 max-w-2xl">
|
|
176
|
+
<Alert title="Information">This is a default alert with some information.</Alert>
|
|
177
|
+
|
|
178
|
+
<Alert variant="success" title="Success!">
|
|
179
|
+
Your changes have been saved successfully.
|
|
180
|
+
</Alert>
|
|
181
|
+
|
|
182
|
+
<Alert variant="warning" title="Warning">
|
|
183
|
+
Please review your information before proceeding.
|
|
184
|
+
</Alert>
|
|
185
|
+
|
|
186
|
+
<Alert variant="error" title="Error">
|
|
187
|
+
Something went wrong. Please try again.
|
|
188
|
+
</Alert>
|
|
189
|
+
</div>
|
|
190
|
+
</section>
|
|
191
|
+
|
|
192
|
+
{/* Badges */}
|
|
193
|
+
<section>
|
|
194
|
+
<h2 className="text-2xl font-semibold mb-6">Badges</h2>
|
|
195
|
+
<div className="flex flex-wrap gap-4">
|
|
196
|
+
<Badge>Default</Badge>
|
|
197
|
+
<Badge variant="secondary">Secondary</Badge>
|
|
198
|
+
<Badge variant="success">Success</Badge>
|
|
199
|
+
<Badge variant="warning">Warning</Badge>
|
|
200
|
+
<Badge variant="error">Error</Badge>
|
|
201
|
+
<Badge variant="outline">Outline</Badge>
|
|
202
|
+
</div>
|
|
203
|
+
</section>
|
|
204
|
+
|
|
205
|
+
{/* Avatar */}
|
|
206
|
+
<section>
|
|
207
|
+
<h2 className="text-2xl font-semibold mb-6">Avatars</h2>
|
|
208
|
+
<div className="flex flex-wrap gap-4 items-center">
|
|
209
|
+
<Avatar src="https://i.pravatar.cc/150?img=1" alt="User 1" />
|
|
210
|
+
<Avatar src="https://i.pravatar.cc/150?img=2" alt="User 2" size="lg" />
|
|
211
|
+
<Avatar fallback="JD" />
|
|
212
|
+
<Avatar fallback="AB" size="sm" />
|
|
213
|
+
</div>
|
|
214
|
+
</section>
|
|
215
|
+
|
|
216
|
+
{/* Dialog */}
|
|
217
|
+
<section>
|
|
218
|
+
<h2 className="text-2xl font-semibold mb-6">Dialog</h2>
|
|
219
|
+
<Dialog>
|
|
220
|
+
<DialogTrigger asChild>
|
|
221
|
+
<Button>Open Dialog</Button>
|
|
222
|
+
</DialogTrigger>
|
|
223
|
+
<DialogContent>
|
|
224
|
+
<DialogHeader>
|
|
225
|
+
<DialogTitle>Dialog Example</DialogTitle>
|
|
226
|
+
<DialogDescription>This is a modal dialog built with Radix UI Dialog primitive.</DialogDescription>
|
|
227
|
+
</DialogHeader>
|
|
228
|
+
<div className="py-4">
|
|
229
|
+
<p className="text-sm text-muted-foreground">Dialogs are great for getting user attention and requiring action before continuing.</p>
|
|
230
|
+
</div>
|
|
231
|
+
<DialogFooter>
|
|
232
|
+
<Button variant="outline">Cancel</Button>
|
|
233
|
+
<Button>Confirm</Button>
|
|
234
|
+
</DialogFooter>
|
|
235
|
+
</DialogContent>
|
|
236
|
+
</Dialog>
|
|
237
|
+
</section>
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
240
|
+
)
|
|
241
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# UI Component Library
|
|
2
|
+
|
|
3
|
+
This module provides access to the Launch77 UI component library built with Radix UI primitives and integrated with the design system.
|
|
4
|
+
|
|
5
|
+
## Available Components
|
|
6
|
+
|
|
7
|
+
### Form Controls
|
|
8
|
+
|
|
9
|
+
- **Button** - Primary action buttons with multiple variants
|
|
10
|
+
- **Input** - Text input fields
|
|
11
|
+
- **Textarea** - Multi-line text input
|
|
12
|
+
- **Select** - Dropdown selection
|
|
13
|
+
- **Checkbox** - Toggle checkboxes
|
|
14
|
+
- **Switch** - Toggle switches
|
|
15
|
+
- **FormField** - Form field wrapper
|
|
16
|
+
- **FormHelperText** - Helper text for form fields
|
|
17
|
+
- **FormError** - Error messages for form fields
|
|
18
|
+
|
|
19
|
+
### Layout & Display
|
|
20
|
+
|
|
21
|
+
- **Card** - Container component for content sections
|
|
22
|
+
- **Alert** - Alert messages with variants (success, warning, error)
|
|
23
|
+
- **Badge** - Small status indicators
|
|
24
|
+
- **Avatar** - User avatar images with fallbacks
|
|
25
|
+
- **Dialog** - Modal dialogs
|
|
26
|
+
- **Sidebar** - Navigation sidebar layout
|
|
27
|
+
|
|
28
|
+
## Usage Examples
|
|
29
|
+
|
|
30
|
+
### Basic Button
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
import { Button } from '@launch77-shared/lib-ui'
|
|
34
|
+
|
|
35
|
+
export default function MyComponent() {
|
|
36
|
+
return <Button onClick={() => console.log('clicked')}>Click me</Button>
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Button Variants
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
<Button variant="default">Default</Button>
|
|
44
|
+
<Button variant="secondary">Secondary</Button>
|
|
45
|
+
<Button variant="outline">Outline</Button>
|
|
46
|
+
<Button variant="ghost">Ghost</Button>
|
|
47
|
+
<Button variant="destructive">Destructive</Button>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Form Field with Validation
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
import { Input, FormField } from '@launch77-shared/lib-ui'
|
|
54
|
+
|
|
55
|
+
export default function MyForm() {
|
|
56
|
+
const [email, setEmail] = useState('')
|
|
57
|
+
const [error, setError] = useState('')
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<FormField label="Email Address" error={error} helperText="We'll never share your email" required htmlFor="email">
|
|
61
|
+
<Input id="email" type="email" placeholder="Enter your email" value={email} onChange={(e) => setEmail(e.target.value)} />
|
|
62
|
+
</FormField>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Select Dropdown
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@launch77-shared/lib-ui'
|
|
71
|
+
|
|
72
|
+
export default function MySelect() {
|
|
73
|
+
const [value, setValue] = useState('')
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<Select value={value} onValueChange={setValue}>
|
|
77
|
+
<SelectTrigger>
|
|
78
|
+
<SelectValue placeholder="Choose an option" />
|
|
79
|
+
</SelectTrigger>
|
|
80
|
+
<SelectContent>
|
|
81
|
+
<SelectItem value="option1">Option 1</SelectItem>
|
|
82
|
+
<SelectItem value="option2">Option 2</SelectItem>
|
|
83
|
+
<SelectItem value="option3">Option 3</SelectItem>
|
|
84
|
+
</SelectContent>
|
|
85
|
+
</Select>
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Dialog Modal
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, Button } from '@launch77-shared/lib-ui'
|
|
94
|
+
|
|
95
|
+
export default function MyDialog() {
|
|
96
|
+
return (
|
|
97
|
+
<Dialog>
|
|
98
|
+
<DialogTrigger asChild>
|
|
99
|
+
<Button>Open Dialog</Button>
|
|
100
|
+
</DialogTrigger>
|
|
101
|
+
<DialogContent>
|
|
102
|
+
<DialogHeader>
|
|
103
|
+
<DialogTitle>Confirm Action</DialogTitle>
|
|
104
|
+
<DialogDescription>Are you sure you want to continue?</DialogDescription>
|
|
105
|
+
</DialogHeader>
|
|
106
|
+
<DialogFooter>
|
|
107
|
+
<Button variant="outline">Cancel</Button>
|
|
108
|
+
<Button>Confirm</Button>
|
|
109
|
+
</DialogFooter>
|
|
110
|
+
</DialogContent>
|
|
111
|
+
</Dialog>
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Card Layout
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter, Button } from '@launch77-shared/lib-ui'
|
|
120
|
+
|
|
121
|
+
export default function MyCard() {
|
|
122
|
+
return (
|
|
123
|
+
<Card>
|
|
124
|
+
<CardHeader>
|
|
125
|
+
<CardTitle>Card Title</CardTitle>
|
|
126
|
+
<CardDescription>Optional description text</CardDescription>
|
|
127
|
+
</CardHeader>
|
|
128
|
+
<CardContent>
|
|
129
|
+
<p>Card content goes here.</p>
|
|
130
|
+
</CardContent>
|
|
131
|
+
<CardFooter>
|
|
132
|
+
<Button>Action</Button>
|
|
133
|
+
</CardFooter>
|
|
134
|
+
</Card>
|
|
135
|
+
)
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Alerts
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
import { Alert } from '@launch77-shared/lib-ui'
|
|
143
|
+
|
|
144
|
+
export default function MyAlerts() {
|
|
145
|
+
return (
|
|
146
|
+
<>
|
|
147
|
+
<Alert variant="success" title="Success!">
|
|
148
|
+
Operation completed successfully.
|
|
149
|
+
</Alert>
|
|
150
|
+
|
|
151
|
+
<Alert variant="error" title="Error">
|
|
152
|
+
Something went wrong.
|
|
153
|
+
</Alert>
|
|
154
|
+
</>
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Checkbox & Switch
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
import { Checkbox, Switch, FormError } from '@launch77-shared/lib-ui'
|
|
163
|
+
|
|
164
|
+
export default function MyToggles() {
|
|
165
|
+
const [checked, setChecked] = useState(false)
|
|
166
|
+
const [termsAccepted, setTermsAccepted] = useState(false)
|
|
167
|
+
const [termsError, setTermsError] = useState(false)
|
|
168
|
+
const [enabled, setEnabled] = useState(false)
|
|
169
|
+
|
|
170
|
+
return (
|
|
171
|
+
<>
|
|
172
|
+
<div className="flex items-center gap-2">
|
|
173
|
+
<Checkbox id="terms" checked={checked} onCheckedChange={(value) => setChecked(value as boolean)} />
|
|
174
|
+
<label htmlFor="terms">Accept terms</label>
|
|
175
|
+
</div>
|
|
176
|
+
|
|
177
|
+
{/* Checkbox with error state */}
|
|
178
|
+
<div className="space-y-2">
|
|
179
|
+
<div className="flex items-center gap-2">
|
|
180
|
+
<Checkbox
|
|
181
|
+
id="terms-required"
|
|
182
|
+
checked={termsAccepted}
|
|
183
|
+
onCheckedChange={(value) => {
|
|
184
|
+
setTermsAccepted(value as boolean)
|
|
185
|
+
setTermsError(false)
|
|
186
|
+
}}
|
|
187
|
+
error={termsError}
|
|
188
|
+
/>
|
|
189
|
+
<label htmlFor="terms-required">I accept the terms (required)</label>
|
|
190
|
+
</div>
|
|
191
|
+
{termsError && <FormError>You must accept the terms</FormError>}
|
|
192
|
+
</div>
|
|
193
|
+
|
|
194
|
+
<div className="flex items-center gap-2">
|
|
195
|
+
<Switch id="notifications" checked={enabled} onCheckedChange={setEnabled} />
|
|
196
|
+
<label htmlFor="notifications">Enable notifications</label>
|
|
197
|
+
</div>
|
|
198
|
+
</>
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Styling
|
|
204
|
+
|
|
205
|
+
All components are styled using Tailwind CSS and the Launch77 design system tokens. You can customize component appearance using:
|
|
206
|
+
|
|
207
|
+
- **Tailwind classes** - Apply utility classes via `className` prop
|
|
208
|
+
- **Design tokens** - Use CSS variables from the design system
|
|
209
|
+
- **Brand customization** - Edit `src/modules/design-system/config/brand.css`
|
|
210
|
+
|
|
211
|
+
## Live Examples
|
|
212
|
+
|
|
213
|
+
Visit [http://localhost:3000/ui-examples](http://localhost:3000/ui-examples) to see all components in action.
|
|
214
|
+
|
|
215
|
+
## Documentation
|
|
216
|
+
|
|
217
|
+
For detailed component API documentation and advanced usage, refer to the Radix UI documentation:
|
|
218
|
+
|
|
219
|
+
- [Radix UI Primitives](https://www.radix-ui.com/primitives/docs/overview/introduction)
|
|
220
|
+
|
|
221
|
+
## Utility Functions
|
|
222
|
+
|
|
223
|
+
The library also exports the `cn` utility function for merging Tailwind classes:
|
|
224
|
+
|
|
225
|
+
```tsx
|
|
226
|
+
import { cn } from '@launch77-shared/lib-ui'
|
|
227
|
+
|
|
228
|
+
const className = cn('base-class', condition && 'conditional-class', 'another-class')
|
|
229
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI Component Library
|
|
3
|
+
*
|
|
4
|
+
* Re-exports all components from @launch77-shared/lib-ui
|
|
5
|
+
* for convenient importing throughout the application.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { Button, Input, Card } from '@/modules/ui'
|
|
9
|
+
*
|
|
10
|
+
* Or import directly from the library:
|
|
11
|
+
* import { Button, Input, Card } from '@launch77-shared/lib-ui'
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export * from '@launch77-shared/lib-ui'
|