@rajdeep0510/scaffold-cli 1.0.2 → 1.3.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.
@@ -0,0 +1,10 @@
1
+ {
2
+ "files.watcherExclude": {
3
+ "**/.git/objects/**": true,
4
+ "**/.git/subtree-cache/**": true,
5
+ "**/.hg/store/**": true,
6
+ "**/.git/**": true,
7
+ "**/node_modules/**": true,
8
+ "**/.vscode/**": true
9
+ }
10
+ }
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # scaffold
2
2
 
3
+ ![NPM Version](https://img.shields.io/npm/v/%40rajdeep0510%2Fscaffold-cli)
4
+
3
5
  A CLI tool for creating versatile and modern UI components.
4
6
 
5
7
  ## Import components
package/bin/index.js CHANGED
@@ -27,15 +27,15 @@ program.addHelpText('before', () => {
27
27
  );
28
28
 
29
29
  const importInfo = chalk.blue("How to import components:") + "\n" +
30
- chalk.green(" import { Button } from '@/components/ui/button'");
30
+ chalk.green(" import { Button } from '@/components/ui/button'");
31
31
 
32
32
  return banner + "\n\n" + description + "\n\n" + importInfo + "\n\n";
33
33
  });
34
34
 
35
35
  program
36
- .command('add <component-name>')
37
- .description('Add a new component')
38
- .action(addComponent)
36
+ .command('add <component-name>')
37
+ .description('Add a new component')
38
+ .action(addComponent)
39
39
 
40
40
  program
41
41
  .command('list')
@@ -4,73 +4,99 @@ import { fileURLToPath } from 'url'
4
4
  import chalk from 'chalk'
5
5
  import figlet from 'figlet'
6
6
  import ora from 'ora'
7
+ import { execSync } from 'child_process'
7
8
 
8
9
  const __dirname = path.dirname(fileURLToPath(import.meta.url)) // ESM-safe __dirname
9
10
 
10
11
 
11
12
  // banner TODO: remove
12
13
  console.log(
13
- chalk.cyan(
14
- figlet.text('scaffold', { font: 'doom' }, function(err, data) {
15
- if (err) {
16
- console.log('Something went wrong...');
17
- console.dir(err);
18
- return;
19
- }
20
- console.log(data);
21
- })
22
-
23
- )
14
+ chalk.cyan(
15
+ figlet.text('scaffold', { font: 'doom' }, function (err, data) {
16
+ if (err) {
17
+ console.log('Something went wrong...');
18
+ console.dir(err);
19
+ return;
20
+ }
21
+ console.log(data);
22
+ })
23
+
24
+ )
24
25
  )
25
26
 
26
27
  export async function addComponent(name) {
27
- const useSrc = await fs.pathExists('src')
28
- const hasApp = await fs.pathExists(path.join(useSrc ? 'src' : '', 'app'))
29
- const hasPages = await fs.pathExists(path.join(useSrc ? 'src' : '', 'pages'))
28
+ const useSrc = await fs.pathExists('src')
29
+ const hasApp = await fs.pathExists(path.join(useSrc ? 'src' : '', 'app'))
30
+ const hasPages = await fs.pathExists(path.join(useSrc ? 'src' : '', 'pages'))
30
31
 
31
- if (!hasApp && !hasPages) {
32
- console.log(chalk.red('Could not detect Next.js routing structure (no app/ or pages/ directory)'))
33
- return
34
- }
32
+ if (!hasApp && !hasPages) {
33
+ console.log(chalk.red('Could not detect Next.js routing structure (no app/ or pages/ directory)'))
34
+ return
35
+ }
35
36
 
36
- const baseDir = useSrc ? 'src/components/ui' : 'components/ui'
37
- const targetDir = path.join(baseDir, name)
38
- const templateDir = path.resolve(__dirname, '../templates', name)
37
+ const baseDir = useSrc ? 'src/components/ui' : 'components/ui'
38
+ const targetDir = path.join(baseDir, name)
39
+ const templateDir = path.resolve(__dirname, '../templates', name)
39
40
 
40
- // 🔎 check if template folder exists
41
- if (!(await fs.pathExists(templateDir))) {
42
- console.log(chalk.red(`Template folder '${templateDir}' not found.`))
43
- return
44
- }
41
+ // 🔎 check if template folder exists
42
+ if (!(await fs.pathExists(templateDir))) {
43
+ console.log(chalk.red(`Template folder '${templateDir}' not found.`))
44
+ return
45
+ }
45
46
 
46
- // 🔎 validate meta.json before copying
47
- const metaPath = path.join(templateDir, 'meta.json')
48
- if (!(await fs.pathExists(metaPath))) {
49
- console.log(chalk.red(`No meta.json found in template '${name}'`))
50
- return
51
- }
47
+ // 🔎 validate meta.json before copying
48
+ const metaPath = path.join(templateDir, 'meta.json')
49
+ if (!(await fs.pathExists(metaPath))) {
50
+ console.log(chalk.red(`No meta.json found in template '${name}'`))
51
+ return
52
+ }
52
53
 
53
- const meta = await fs.readJson(metaPath)
54
- if (!meta.name || !meta.description || !meta.version) {
55
- console.log(chalk.red(`Invalid meta.json for '${name}'. Missing required fields.`))
56
- return
57
- }
54
+ const meta = await fs.readJson(metaPath)
55
+ if (!meta.name || !meta.description || !meta.version) {
56
+ console.log(chalk.red(`Invalid meta.json for '${name}'. Missing required fields.`))
57
+ return
58
+ }
58
59
 
59
- // 🌀 Spinner while copying
60
- const spinner = ora(`Adding component '${name}'...`).start()
60
+ // 🌀 Spinner while copying
61
+ const spinner = ora(`Adding component '${name}'...`).start()
61
62
 
62
- try {
63
- await fs.ensureDir(targetDir)
64
- await fs.copy(templateDir, targetDir)
63
+ try {
64
+ await fs.ensureDir(targetDir)
65
+ await fs.copy(templateDir, targetDir)
65
66
 
66
- spinner.succeed(chalk.green(`Component '${meta.name}' created at ${targetDir}`))
67
-
68
- console.log(chalk.yellow(`ℹ️ Description:`), chalk.white(meta.description))
69
- console.log(chalk.magenta(`📦 Version:`), chalk.white(meta.version))
70
- console.log(chalk.cyan(`\n👉 Import in Next.js:`))
71
- console.log(chalk.blueBright(` import { ${meta.name} } from "@/components/ui/${name}"\n`))
72
- } catch (err) {
73
- spinner.fail(chalk.red(`Failed to add component '${name}'`))
67
+ // Install dependencies if specified in meta.json
68
+ if (meta.dependencies && (meta.dependencies.npm?.length > 0 || meta.dependencies.peer?.length > 0)) {
69
+ spinner.text = `Installing dependencies for '${name}'...`
70
+
71
+ try {
72
+ if (meta.dependencies.npm?.length > 0) {
73
+ const npmDeps = meta.dependencies.npm.join(' ')
74
+ execSync(`npm install ${npmDeps}`, { stdio: 'inherit' })
75
+ console.log(chalk.green(`✅ Installed npm dependencies: ${npmDeps}`))
76
+ }
77
+
78
+ if (meta.dependencies.peer?.length > 0) {
79
+ const peerDeps = meta.dependencies.peer.join(' ')
80
+ execSync(`npm install --save-peer ${peerDeps}`, { stdio: 'inherit' })
81
+ console.log(chalk.green(`✅ Installed peer dependencies: ${peerDeps}`))
82
+ }
83
+ } catch (err) {
84
+ console.log(chalk.yellow(`⚠️ Warning: Failed to install some dependencies. You may need to install them manually.`))
74
85
  console.error(err)
86
+ }
75
87
  }
88
+
89
+ spinner.succeed(chalk.green(`Component '${meta.name}' created at ${targetDir}`))
90
+
91
+ console.log(chalk.yellow(`ℹ️ Description:`), chalk.white(meta.description))
92
+ console.log(chalk.magenta(`📦 Version:`), chalk.white(meta.version))
93
+ console.log(chalk.magenta(`Author :`), chalk.white(meta.author))
94
+ console.log(chalk.cyan(`\n👉 Import in Next.js:`))
95
+ console.log(chalk.blueBright(` import { ${meta.name} } from "@/components/ui/${name}"\n`))
96
+ } catch (err) {
97
+ spinner.fail(chalk.red(`Failed to add component '${name}'`))
98
+ console.error(err)
99
+ }
76
100
  }
101
+
102
+ // change made TODO:remove
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rajdeep0510/scaffold-cli",
3
- "version": "1.0.2",
3
+ "version": "1.3.0",
4
4
  "description": "This is a CLI app for a UI library which is used for creating versatile and modern UI components.",
5
5
  "main": "bin/index.js",
6
6
  "type": "module",
@@ -39,4 +39,4 @@
39
39
  "ora": "^8.2.0",
40
40
  "prop-types": "^15.8.1"
41
41
  }
42
- }
42
+ }
@@ -0,0 +1,62 @@
1
+ // component.jsx
2
+ import { useState } from "react";
3
+ import PropTypes from "prop-types";
4
+
5
+ export default function Avatar({
6
+ src,
7
+ alt,
8
+ name,
9
+ info,
10
+ size = "md",
11
+ cardPosition = "bottom",
12
+ }) {
13
+ const [showCard, setShowCard] = useState(false);
14
+
15
+ // Avatar size styles
16
+ const sizeClasses = {
17
+ sm: "w-8 h-8",
18
+ md: "w-12 h-12",
19
+ lg: "w-16 h-16",
20
+ };
21
+
22
+ // Hover card position
23
+ const positionClasses =
24
+ cardPosition === "top" ? "bottom-full mb-2" : "top-full mt-2";
25
+
26
+ return (
27
+ <div
28
+ className="relative inline-block"
29
+ onMouseEnter={() => setShowCard(true)}
30
+ onMouseLeave={() => setShowCard(false)}
31
+ >
32
+ {/* Avatar Image */}
33
+ <img
34
+ src={src}
35
+ alt={alt || "Avatar"}
36
+ className={`${sizeClasses[size]} rounded-full object-cover border shadow-sm cursor-pointer`}
37
+ />
38
+
39
+ {/* Hover Card */}
40
+ {showCard && (
41
+ <div
42
+ className={`absolute left-1/2 -translate-x-1/2 ${positionClasses}
43
+ bg-white shadow-lg rounded-2xl p-3 w-48
44
+ border text-sm z-10`}
45
+ >
46
+ <p className="font-extrabold">{name}</p>
47
+ <p className="text-gray-600">{info}</p>
48
+ </div>
49
+ )}
50
+ </div>
51
+ );
52
+ }
53
+
54
+ // PropTypes validation
55
+ Avatar.propTypes = {
56
+ src: PropTypes.string.isRequired,
57
+ alt: PropTypes.string,
58
+ name: PropTypes.string.isRequired,
59
+ info: PropTypes.string,
60
+ size: PropTypes.oneOf(["sm", "md", "lg"]),
61
+ cardPosition: PropTypes.oneOf(["top", "bottom"]),
62
+ };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Avatar component barrel export
3
+ */
4
+ export { default } from "./component";
5
+ export { default as Avatar } from "./component";
@@ -0,0 +1,92 @@
1
+ {
2
+ "name": "Avatar",
3
+ "description": "An avatar component that displays a profile picture and shows a hover card with name and info.",
4
+ "category": "display",
5
+ "tags": [
6
+ "avatar",
7
+ "profile",
8
+ "hover",
9
+ "info",
10
+ "card",
11
+ "ui",
12
+ "tailwind"
13
+ ],
14
+ "version": "1.0.0",
15
+ "author": "@rajdeep",
16
+ "created": "2025-08-29",
17
+ "dependencies": {
18
+ "npm": [],
19
+ "peer": [
20
+ "react",
21
+ "prop-types"
22
+ ]
23
+ },
24
+ "props": [
25
+ {
26
+ "name": "src",
27
+ "type": "string",
28
+ "required": true,
29
+ "default": "undefined",
30
+ "description": "Image URL or path from public folder for the avatar"
31
+ },
32
+ {
33
+ "name": "alt",
34
+ "type": "string",
35
+ "required": false,
36
+ "default": "Avatar",
37
+ "description": "Alternative text for accessibility"
38
+ },
39
+ {
40
+ "name": "name",
41
+ "type": "string",
42
+ "required": true,
43
+ "default": "undefined",
44
+ "description": "Name displayed in hover card"
45
+ },
46
+ {
47
+ "name": "info",
48
+ "type": "string",
49
+ "required": false,
50
+ "default": "undefined",
51
+ "description": "Additional info displayed in hover card"
52
+ },
53
+ {
54
+ "name": "size",
55
+ "type": "string",
56
+ "required": false,
57
+ "default": "md",
58
+ "description": "Avatar size",
59
+ "options": [
60
+ "sm",
61
+ "md",
62
+ "lg"
63
+ ]
64
+ },
65
+ {
66
+ "name": "cardPosition",
67
+ "type": "string",
68
+ "required": false,
69
+ "default": "bottom",
70
+ "description": "Position of hover card",
71
+ "options": [
72
+ "top",
73
+ "bottom"
74
+ ]
75
+ }
76
+ ],
77
+ "files": [
78
+ "component.jsx",
79
+ "index.js",
80
+ "meta.json"
81
+ ],
82
+ "customization": {
83
+ "styling_notes": "Avatar size is controlled via the 'size' prop. Hover card colors, shadow, and border radius can be customized via Tailwind classes in the component."
84
+ },
85
+ "accessibility": {
86
+ "features": [
87
+ "Hover card provides additional info",
88
+ "Alt text for image for screen readers",
89
+ "Keyboard focus not required but can be extended"
90
+ ]
91
+ }
92
+ }
@@ -0,0 +1,187 @@
1
+ import React from "react";
2
+
3
+ const VARIANTS = {
4
+ elevated: "shadow-xl border-transparent",
5
+ outlined: "border border-opacity-20 shadow-sm",
6
+ flat: "shadow-none border-transparent",
7
+ glass: "backdrop-blur-xl bg-opacity-70 border border-white/10 shadow-lg",
8
+ gradient: "border-none shadow-2xl bg-gradient-to-br",
9
+ };
10
+
11
+ const THEMES = {
12
+ light: {
13
+ base: "bg-white text-slate-800",
14
+ glass: "bg-white/70 text-slate-900 border-slate-200/50",
15
+ border: "border-slate-200",
16
+ subtext: "text-slate-500",
17
+ divider: "border-slate-100",
18
+ },
19
+ dark: {
20
+ base: "bg-slate-900 text-slate-100",
21
+ glass: "bg-slate-900/60 text-white border-white/10",
22
+ border: "border-slate-800",
23
+ subtext: "text-slate-400",
24
+ divider: "border-slate-800",
25
+ },
26
+ };
27
+
28
+ const PADDINGS = {
29
+ none: "p-0",
30
+ sm: "p-3",
31
+ md: "p-6",
32
+ lg: "p-8",
33
+ xl: "p-10",
34
+ };
35
+
36
+ const ROUNDED = {
37
+ none: "rounded-none",
38
+ sm: "rounded-sm",
39
+ md: "rounded-md",
40
+ lg: "rounded-lg",
41
+ xl: "rounded-xl",
42
+ "2xl": "rounded-2xl",
43
+ "3xl": "rounded-3xl",
44
+ full: "rounded-full",
45
+ };
46
+
47
+ const HOVER_EFFECTS = {
48
+ none: "",
49
+ lift: "hover:-translate-y-1 hover:shadow-2xl",
50
+ scale: "hover:scale-[1.02]",
51
+ glow: "hover:shadow-indigo-500/20 hover:border-indigo-500/30",
52
+ shimmer: "hover:bg-opacity-90",
53
+ };
54
+
55
+ /**
56
+ * @param {Object} props
57
+ * @param {React.ReactNode} [props.children]
58
+ * @param {string} [props.title]
59
+ * @param {string} [props.subtitle]
60
+ * @param {string} [props.image]
61
+ * @param {boolean} [props.imageFull]
62
+ * @param {React.ReactNode} [props.footer]
63
+ * @param {React.ReactNode} [props.action]
64
+ * @param {string} [props.badge]
65
+ * @param {string} [props.theme]
66
+ * @param {string} [props.variant]
67
+ * @param {string} [props.padding]
68
+ * @param {string} [props.rounded]
69
+ * @param {string} [props.hoverEffect]
70
+ * @param {string} [props.className]
71
+ * @param {string} [props.width]
72
+ * @param {function} [props.onClick]
73
+ */
74
+ export default function Card({
75
+ children = undefined,
76
+ title = undefined,
77
+ subtitle = undefined,
78
+ image = undefined,
79
+ imageFull = false, // If true, image covers the background or top area completely without padding
80
+ footer = undefined,
81
+ action = undefined,
82
+ badge = undefined,
83
+ theme = "dark",
84
+ variant = "elevated",
85
+ padding = "md",
86
+ rounded = "2xl",
87
+ hoverEffect = "lift",
88
+ className = "",
89
+ width = "w-full max-w-sm", // Default width constraint
90
+ onClick = undefined,
91
+ ...props
92
+ }) {
93
+ const isGlass = variant === "glass";
94
+ const currentTheme = THEMES[theme] || THEMES.dark;
95
+ const baseStyle = isGlass ? currentTheme.glass : currentTheme.base;
96
+ const borderColor = currentTheme.border;
97
+
98
+ return (
99
+ <div
100
+ onClick={onClick}
101
+ className={`
102
+ relative flex flex-col overflow-hidden transition-all duration-300 ease-out group
103
+ ${width}
104
+ ${ROUNDED[rounded] || ROUNDED["2xl"]}
105
+ ${VARIANTS[variant] || VARIANTS.elevated}
106
+ ${baseStyle}
107
+ ${!isGlass && variant === "outlined" ? borderColor : ""}
108
+ ${HOVER_EFFECTS[hoverEffect] || ""}
109
+ ${onClick ? "cursor-pointer" : ""}
110
+ ${className}
111
+ `}
112
+ {...props}
113
+ >
114
+ {/* Background Image (Optional absolute overlay) */}
115
+ {image && imageFull && (
116
+ <div className="absolute inset-0 z-0">
117
+ <img
118
+ src={image}
119
+ alt={title || "Card background"}
120
+ className="h-full w-full object-cover opacity-20 group-hover:scale-105 transition-transform duration-700"
121
+ />
122
+ <div
123
+ className={`absolute inset-0 bg-gradient-to-t ${
124
+ theme === "dark" ? "from-slate-900" : "from-white"
125
+ } via-transparent to-transparent`}
126
+ />
127
+ </div>
128
+ )}
129
+
130
+ {/* Header Image (Standard top image) */}
131
+ {image && !imageFull && (
132
+ <div className="relative h-48 w-full overflow-hidden z-10">
133
+ <img
134
+ src={image}
135
+ alt={title}
136
+ className="h-full w-full object-cover transition-transform duration-500 group-hover:scale-110"
137
+ />
138
+ {badge && (
139
+ <div className="absolute top-4 left-4">
140
+ <span className="px-3 py-1 text-xs font-bold uppercase tracking-wider text-white bg-black/50 backdrop-blur-md rounded-full">
141
+ {badge}
142
+ </span>
143
+ </div>
144
+ )}
145
+ </div>
146
+ )}
147
+
148
+ {/* Content Container */}
149
+ <div className={`relative z-10 flex flex-col h-full ${PADDINGS[padding]}`}>
150
+ {/* Header Section */}
151
+ {(title || subtitle || action) && (
152
+ <div className="flex justify-between items-start mb-4">
153
+ <div className="flex-1">
154
+ {title && (
155
+ <h3 className="text-xl font-bold tracking-tight mb-1">
156
+ {title}
157
+ </h3>
158
+ )}
159
+ {subtitle && (
160
+ <p className={`text-sm font-medium ${currentTheme.subtext}`}>
161
+ {subtitle}
162
+ </p>
163
+ )}
164
+ </div>
165
+ {action && <div className="ml-4">{action}</div>}
166
+ </div>
167
+ )}
168
+
169
+ {/* Main Body */}
170
+ <div className={`flex-1 ${currentTheme.subtext} leading-relaxed`}>
171
+ {children}
172
+ </div>
173
+
174
+ {/* Footer Section */}
175
+ {footer && (
176
+ <div
177
+ className={`mt-6 pt-4 border-t ${currentTheme.divider} flex items-center justify-between`}
178
+ >
179
+ {footer}
180
+ </div>
181
+ )}
182
+ </div>
183
+ </div>
184
+ );
185
+ }
186
+
187
+ Card.displayName = "Card";
@@ -0,0 +1,2 @@
1
+ export { default as Card } from "./Card";
2
+ export { default } from "./Card";
@@ -0,0 +1,100 @@
1
+ {
2
+ "name": "Card",
3
+ "description": "A versatile card component with support for multiple themes, variants, and interactive states.",
4
+ "props": {
5
+ "title": {
6
+ "type": "string",
7
+ "description": "The main heading of the card."
8
+ },
9
+ "subtitle": {
10
+ "type": "string",
11
+ "description": "Secondary text usually placed below the title."
12
+ },
13
+ "image": {
14
+ "type": "string",
15
+ "description": "URL of the image to display."
16
+ },
17
+ "imageFull": {
18
+ "type": "boolean",
19
+ "default": false,
20
+ "description": "If true, the image is used as a background with an overlay."
21
+ },
22
+ "badge": {
23
+ "type": "string",
24
+ "description": "Top-left badge text overlaying the image."
25
+ },
26
+ "action": {
27
+ "type": "node",
28
+ "description": "Element to display in the top-right corner (e.g., button, icon)."
29
+ },
30
+ "footer": {
31
+ "type": "node",
32
+ "description": "Content to display at the bottom of the card with a divider."
33
+ },
34
+ "theme": {
35
+ "type": "enum",
36
+ "options": [
37
+ "light",
38
+ "dark"
39
+ ],
40
+ "default": "dark",
41
+ "description": "Color scheme of the card."
42
+ },
43
+ "variant": {
44
+ "type": "enum",
45
+ "options": [
46
+ "elevated",
47
+ "outlined",
48
+ "flat",
49
+ "glass",
50
+ "gradient"
51
+ ],
52
+ "default": "elevated",
53
+ "description": "Visual style of the card container."
54
+ },
55
+ "padding": {
56
+ "type": "enum",
57
+ "options": [
58
+ "none",
59
+ "sm",
60
+ "md",
61
+ "lg",
62
+ "xl"
63
+ ],
64
+ "default": "md",
65
+ "description": "Internal padding of the content area."
66
+ },
67
+ "rounded": {
68
+ "type": "enum",
69
+ "options": [
70
+ "none",
71
+ "sm",
72
+ "md",
73
+ "lg",
74
+ "xl",
75
+ "2xl",
76
+ "3xl",
77
+ "full"
78
+ ],
79
+ "default": "2xl",
80
+ "description": "Border radius of the card."
81
+ },
82
+ "hoverEffect": {
83
+ "type": "enum",
84
+ "options": [
85
+ "none",
86
+ "lift",
87
+ "scale",
88
+ "glow",
89
+ "shimmer"
90
+ ],
91
+ "default": "lift",
92
+ "description": "Animation effect on hover."
93
+ },
94
+ "width": {
95
+ "type": "string",
96
+ "default": "w-full max-w-sm",
97
+ "description": "Width classes to control the card's dimensions."
98
+ }
99
+ }
100
+ }