@ng-cn/core 1.0.2 → 1.0.3
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
package/schematics/README.md
CHANGED
|
@@ -9,12 +9,43 @@ Angular schematics for installing shadcn-angular components individually.
|
|
|
9
9
|
First, add shadcn-angular to your project:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
ng add
|
|
12
|
+
ng add @ng-cn/core
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
This will:
|
|
16
16
|
- Install required dependencies (lucide-angular, class-variance-authority, clsx, tailwind-merge, @angular/cdk)
|
|
17
17
|
- Set up the project for shadcn-angular components
|
|
18
|
+
- **Prompt you to select components** to install initially (multi-select checkbox)
|
|
19
|
+
|
|
20
|
+
### Interactive Component Selection
|
|
21
|
+
|
|
22
|
+
When running `ng add @ng-cn/core`, you'll be presented with an interactive multi-select prompt:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
? Which components would you like to add? (Press space to select, enter to confirm)
|
|
26
|
+
❯◯ Button - Displays a button or a component that looks like a button
|
|
27
|
+
◯ Card - Displays a card with header, content, and footer
|
|
28
|
+
◯ Input - Displays a form input field
|
|
29
|
+
◯ Label - Renders an accessible label associated with controls
|
|
30
|
+
◯ Checkbox - A control that allows toggling between checked and not checked
|
|
31
|
+
...
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Use **space** to select/deselect components and **enter** to confirm your selection.
|
|
35
|
+
|
|
36
|
+
### Skip Component Selection
|
|
37
|
+
|
|
38
|
+
If you want to skip the component selection prompt:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
ng add @ng-cn/core --components=button,card,input
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Or to skip adding any components initially:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
ng add @ng-cn/core --components=
|
|
48
|
+
```
|
|
18
49
|
|
|
19
50
|
### Install Individual Components
|
|
20
51
|
|
|
@@ -279,6 +279,27 @@ function ngAdd(options) {
|
|
|
279
279
|
context.logger.info(' ✓ Path aliases already set');
|
|
280
280
|
}
|
|
281
281
|
}
|
|
282
|
+
// Install selected components
|
|
283
|
+
if (options.components && options.components.length > 0) {
|
|
284
|
+
context.logger.info('');
|
|
285
|
+
context.logger.info('📦 Selected Components');
|
|
286
|
+
const packageJson = JSON.parse(tree.read(packageJsonPath).toString('utf-8'));
|
|
287
|
+
for (const component of options.components) {
|
|
288
|
+
const packageName = `@ng-cn/${component}`;
|
|
289
|
+
if (!packageJson.dependencies?.[packageName]) {
|
|
290
|
+
packageJson.dependencies = packageJson.dependencies || {};
|
|
291
|
+
packageJson.dependencies[packageName] = 'latest';
|
|
292
|
+
context.logger.info(` + ${packageName}`);
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
context.logger.info(` ✓ ${packageName} already installed`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
tree.overwrite(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
299
|
+
if (!options.skipInstall) {
|
|
300
|
+
context.addTask(new tasks_1.NodePackageInstallTask());
|
|
301
|
+
}
|
|
302
|
+
}
|
|
282
303
|
// Success message with ASCII art banner
|
|
283
304
|
context.logger.info('');
|
|
284
305
|
context.logger.info('');
|
|
@@ -291,10 +312,23 @@ function ngAdd(options) {
|
|
|
291
312
|
context.logger.info('');
|
|
292
313
|
context.logger.info(' ✅ Setup complete! shadcn for Angular');
|
|
293
314
|
context.logger.info('');
|
|
315
|
+
// Show selected components summary if any were installed
|
|
316
|
+
if (options.components && options.components.length > 0) {
|
|
317
|
+
context.logger.info('╭──────────────────────────────────────────────────╮');
|
|
318
|
+
context.logger.info('│ ✨ Components installed: │');
|
|
319
|
+
context.logger.info('│ │');
|
|
320
|
+
for (const component of options.components) {
|
|
321
|
+
const paddedComponent = `@ng-cn/${component}`.padEnd(40);
|
|
322
|
+
context.logger.info(`│ ${paddedComponent}│`);
|
|
323
|
+
}
|
|
324
|
+
context.logger.info('│ │');
|
|
325
|
+
context.logger.info('╰──────────────────────────────────────────────────╯');
|
|
326
|
+
context.logger.info('');
|
|
327
|
+
}
|
|
294
328
|
context.logger.info('╭──────────────────────────────────────────────────╮');
|
|
295
329
|
context.logger.info('│ 🚀 Next steps: │');
|
|
296
330
|
context.logger.info('│ │');
|
|
297
|
-
context.logger.info('│ 1. Add components:
|
|
331
|
+
context.logger.info('│ 1. Add more components: │');
|
|
298
332
|
context.logger.info('│ ng g @ng-cn/core:c button │');
|
|
299
333
|
context.logger.info('│ ng g @ng-cn/core:c card │');
|
|
300
334
|
context.logger.info('│ │');
|
|
@@ -302,7 +336,7 @@ function ngAdd(options) {
|
|
|
302
336
|
context.logger.info('│ npm i @ng-cn/button @ng-cn/card │');
|
|
303
337
|
context.logger.info('│ │');
|
|
304
338
|
context.logger.info('│ 3. Import and use: │');
|
|
305
|
-
context.logger.info("│ import { Button } from '
|
|
339
|
+
context.logger.info("│ import { Button } from '@ng-cn/button'; │");
|
|
306
340
|
context.logger.info('│ │');
|
|
307
341
|
context.logger.info('│ 📚 Docs: https://shadcn-angular.tigayon.com │');
|
|
308
342
|
context.logger.info('╰──────────────────────────────────────────────────╯');
|
|
@@ -6,6 +6,7 @@ interface NgAddOptions {
|
|
|
6
6
|
project?: string;
|
|
7
7
|
skipInstall?: boolean;
|
|
8
8
|
skipStyles?: boolean;
|
|
9
|
+
components?: string[];
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
// CSS Variables template for shadcn theming
|
|
@@ -302,6 +303,31 @@ export function ngAdd(options: NgAddOptions): Rule {
|
|
|
302
303
|
}
|
|
303
304
|
}
|
|
304
305
|
|
|
306
|
+
// Install selected components
|
|
307
|
+
if (options.components && options.components.length > 0) {
|
|
308
|
+
context.logger.info('');
|
|
309
|
+
context.logger.info('📦 Selected Components');
|
|
310
|
+
|
|
311
|
+
const packageJson = JSON.parse(tree.read(packageJsonPath)!.toString('utf-8'));
|
|
312
|
+
|
|
313
|
+
for (const component of options.components) {
|
|
314
|
+
const packageName = `@ng-cn/${component}`;
|
|
315
|
+
if (!packageJson.dependencies?.[packageName]) {
|
|
316
|
+
packageJson.dependencies = packageJson.dependencies || {};
|
|
317
|
+
packageJson.dependencies[packageName] = 'latest';
|
|
318
|
+
context.logger.info(` + ${packageName}`);
|
|
319
|
+
} else {
|
|
320
|
+
context.logger.info(` ✓ ${packageName} already installed`);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
tree.overwrite(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
|
325
|
+
|
|
326
|
+
if (!options.skipInstall) {
|
|
327
|
+
context.addTask(new NodePackageInstallTask());
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
305
331
|
// Success message with ASCII art banner
|
|
306
332
|
context.logger.info('');
|
|
307
333
|
context.logger.info('');
|
|
@@ -314,10 +340,25 @@ export function ngAdd(options: NgAddOptions): Rule {
|
|
|
314
340
|
context.logger.info('');
|
|
315
341
|
context.logger.info(' ✅ Setup complete! shadcn for Angular');
|
|
316
342
|
context.logger.info('');
|
|
343
|
+
|
|
344
|
+
// Show selected components summary if any were installed
|
|
345
|
+
if (options.components && options.components.length > 0) {
|
|
346
|
+
context.logger.info('╭──────────────────────────────────────────────────╮');
|
|
347
|
+
context.logger.info('│ ✨ Components installed: │');
|
|
348
|
+
context.logger.info('│ │');
|
|
349
|
+
for (const component of options.components) {
|
|
350
|
+
const paddedComponent = `@ng-cn/${component}`.padEnd(40);
|
|
351
|
+
context.logger.info(`│ ${paddedComponent}│`);
|
|
352
|
+
}
|
|
353
|
+
context.logger.info('│ │');
|
|
354
|
+
context.logger.info('╰──────────────────────────────────────────────────╯');
|
|
355
|
+
context.logger.info('');
|
|
356
|
+
}
|
|
357
|
+
|
|
317
358
|
context.logger.info('╭──────────────────────────────────────────────────╮');
|
|
318
359
|
context.logger.info('│ 🚀 Next steps: │');
|
|
319
360
|
context.logger.info('│ │');
|
|
320
|
-
context.logger.info('│ 1. Add components:
|
|
361
|
+
context.logger.info('│ 1. Add more components: │');
|
|
321
362
|
context.logger.info('│ ng g @ng-cn/core:c button │');
|
|
322
363
|
context.logger.info('│ ng g @ng-cn/core:c card │');
|
|
323
364
|
context.logger.info('│ │');
|
|
@@ -325,7 +366,7 @@ export function ngAdd(options: NgAddOptions): Rule {
|
|
|
325
366
|
context.logger.info('│ npm i @ng-cn/button @ng-cn/card │');
|
|
326
367
|
context.logger.info('│ │');
|
|
327
368
|
context.logger.info('│ 3. Import and use: │');
|
|
328
|
-
context.logger.info("│ import { Button } from '
|
|
369
|
+
context.logger.info("│ import { Button } from '@ng-cn/button'; │");
|
|
329
370
|
context.logger.info('│ │');
|
|
330
371
|
context.logger.info('│ 📚 Docs: https://shadcn-angular.tigayon.com │');
|
|
331
372
|
context.logger.info('╰──────────────────────────────────────────────────╯');
|
|
@@ -20,6 +20,49 @@
|
|
|
20
20
|
"type": "boolean",
|
|
21
21
|
"default": false,
|
|
22
22
|
"description": "Skip creating ng-cn.scss styles file."
|
|
23
|
+
},
|
|
24
|
+
"components": {
|
|
25
|
+
"type": "array",
|
|
26
|
+
"items": {
|
|
27
|
+
"type": "string"
|
|
28
|
+
},
|
|
29
|
+
"default": [],
|
|
30
|
+
"description": "Components to install initially.",
|
|
31
|
+
"x-prompt": {
|
|
32
|
+
"message": "Which components would you like to add? (Press space to select, enter to confirm)",
|
|
33
|
+
"type": "list",
|
|
34
|
+
"multiselect": true,
|
|
35
|
+
"items": [
|
|
36
|
+
{ "value": "button", "label": "Button - Displays a button or a component that looks like a button" },
|
|
37
|
+
{ "value": "card", "label": "Card - Displays a card with header, content, and footer" },
|
|
38
|
+
{ "value": "input", "label": "Input - Displays a form input field" },
|
|
39
|
+
{ "value": "label", "label": "Label - Renders an accessible label associated with controls" },
|
|
40
|
+
{ "value": "checkbox", "label": "Checkbox - A control that allows toggling between checked and not checked" },
|
|
41
|
+
{ "value": "select", "label": "Select - Displays a list of options for the user to pick from" },
|
|
42
|
+
{ "value": "dialog", "label": "Dialog - A window overlaid on the primary window" },
|
|
43
|
+
{ "value": "toast", "label": "Toast - A succinct message that is displayed temporarily" },
|
|
44
|
+
{ "value": "dropdown-menu", "label": "Dropdown Menu - Displays a menu when triggered" },
|
|
45
|
+
{ "value": "tabs", "label": "Tabs - A set of layered sections of content" },
|
|
46
|
+
{ "value": "table", "label": "Table - A responsive table component" },
|
|
47
|
+
{ "value": "avatar", "label": "Avatar - An image element with a fallback for representing the user" },
|
|
48
|
+
{ "value": "badge", "label": "Badge - Displays a badge or a component that looks like a badge" },
|
|
49
|
+
{ "value": "alert", "label": "Alert - Displays a callout for user attention" },
|
|
50
|
+
{ "value": "tooltip", "label": "Tooltip - A popup that displays information related to an element" },
|
|
51
|
+
{ "value": "progress", "label": "Progress - Displays an indicator showing the completion progress" },
|
|
52
|
+
{ "value": "skeleton", "label": "Skeleton - Use to show a placeholder while content is loading" },
|
|
53
|
+
{ "value": "separator", "label": "Separator - Visually or semantically separates content" },
|
|
54
|
+
{ "value": "switch", "label": "Switch - A control that allows toggling between checked and not checked" },
|
|
55
|
+
{ "value": "textarea", "label": "Textarea - Displays a form textarea" },
|
|
56
|
+
{ "value": "accordion", "label": "Accordion - A vertically stacked set of interactive headings" },
|
|
57
|
+
{ "value": "sheet", "label": "Sheet - Extends the Dialog component to display complementary content" },
|
|
58
|
+
{ "value": "popover", "label": "Popover - Displays rich content in a portal, triggered by a button" },
|
|
59
|
+
{ "value": "calendar", "label": "Calendar - A date field component for entering and editing dates" },
|
|
60
|
+
{ "value": "command", "label": "Command - Fast, composable, unstyled command menu" },
|
|
61
|
+
{ "value": "form", "label": "Form - Building forms with validation" },
|
|
62
|
+
{ "value": "sidebar", "label": "Sidebar - A composable, themeable and customizable sidebar" },
|
|
63
|
+
{ "value": "spinner", "label": "Spinner - Loading indicator animations" }
|
|
64
|
+
]
|
|
65
|
+
}
|
|
23
66
|
}
|
|
24
67
|
}
|
|
25
68
|
}
|
|
@@ -57,15 +57,15 @@ const MOCK_STYLES_CSS = `/* You can add global styles to this file, and also imp
|
|
|
57
57
|
|
|
58
58
|
async function runTests() {
|
|
59
59
|
console.log('\n🧪 Testing ng-add schematic...\n');
|
|
60
|
-
|
|
60
|
+
|
|
61
61
|
const runner = new SchematicTestRunner('schematics', collectionPath);
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
// Create a mock project tree using HostTree
|
|
64
64
|
const appTree = new HostTree();
|
|
65
65
|
appTree.create('/package.json', MOCK_PACKAGE_JSON);
|
|
66
66
|
appTree.create('/tsconfig.json', MOCK_TSCONFIG);
|
|
67
67
|
appTree.create('/src/styles.css', MOCK_STYLES_CSS);
|
|
68
|
-
|
|
68
|
+
|
|
69
69
|
console.log('📁 Created mock project with:');
|
|
70
70
|
console.log(' - /package.json');
|
|
71
71
|
console.log(' - /tsconfig.json (with comments)');
|
|
@@ -74,9 +74,9 @@ async function runTests() {
|
|
|
74
74
|
try {
|
|
75
75
|
// Run the ng-add schematic
|
|
76
76
|
const tree = await runner.runSchematic('ng-add', { skipInstall: true }, appTree);
|
|
77
|
-
|
|
77
|
+
|
|
78
78
|
console.log('✅ Schematic executed successfully!\n');
|
|
79
|
-
|
|
79
|
+
|
|
80
80
|
// Verify files were created
|
|
81
81
|
const expectedFiles = [
|
|
82
82
|
'/src/app/lib/utils/cn.ts',
|
|
@@ -84,7 +84,7 @@ async function runTests() {
|
|
|
84
84
|
'/src/app/lib/components/ui/.gitkeep',
|
|
85
85
|
'/src/ng-cn.scss'
|
|
86
86
|
];
|
|
87
|
-
|
|
87
|
+
|
|
88
88
|
console.log('📄 Checking created files:');
|
|
89
89
|
for (const file of expectedFiles) {
|
|
90
90
|
if (tree.exists(file)) {
|
|
@@ -93,13 +93,13 @@ async function runTests() {
|
|
|
93
93
|
console.log(` ❌ Missing: ${file}`);
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
|
-
|
|
96
|
+
|
|
97
97
|
// Verify tsconfig was updated
|
|
98
98
|
console.log('\n⚙️ Checking tsconfig.json:');
|
|
99
99
|
const tsconfigContent = tree.read('/tsconfig.json').toString('utf-8');
|
|
100
100
|
const tsconfig = JSON.parse(tsconfigContent);
|
|
101
101
|
const paths = tsconfig.compilerOptions?.paths || {};
|
|
102
|
-
|
|
102
|
+
|
|
103
103
|
const expectedPaths = ['@/*', '@/lib/*', '@/ui/*', '@/utils/*'];
|
|
104
104
|
for (const p of expectedPaths) {
|
|
105
105
|
if (paths[p]) {
|
|
@@ -108,7 +108,7 @@ async function runTests() {
|
|
|
108
108
|
console.log(` ❌ Missing path alias: ${p}`);
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
|
-
|
|
111
|
+
|
|
112
112
|
// Verify styles were imported
|
|
113
113
|
console.log('\n🎨 Checking styles:');
|
|
114
114
|
const stylesContent = tree.read('/src/styles.css').toString('utf-8');
|
|
@@ -117,7 +117,7 @@ async function runTests() {
|
|
|
117
117
|
} else {
|
|
118
118
|
console.log(' ❌ ng-cn.scss not imported');
|
|
119
119
|
}
|
|
120
|
-
|
|
120
|
+
|
|
121
121
|
// Verify dependencies
|
|
122
122
|
console.log('\n📦 Checking dependencies:');
|
|
123
123
|
const packageJsonContent = tree.read('/package.json').toString('utf-8');
|
|
@@ -130,9 +130,9 @@ async function runTests() {
|
|
|
130
130
|
console.log(` ❌ Missing: ${dep}`);
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
|
-
|
|
133
|
+
|
|
134
134
|
console.log('\n✨ All tests passed!\n');
|
|
135
|
-
|
|
135
|
+
|
|
136
136
|
} catch (error) {
|
|
137
137
|
console.error('\n❌ Schematic failed with error:');
|
|
138
138
|
console.error(error);
|