@gilav21/shadcn-angular 0.0.16 ā 0.0.18
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/dist/commands/add.js +33 -10
- package/dist/commands/init.js +4 -5
- package/dist/registry/index.d.ts +1 -0
- package/dist/registry/index.js +2 -0
- package/dist/templates/utils.js +31 -1
- package/dist/utils/config.js +1 -1
- package/dist/utils/shortcut-registry.js +3 -7
- package/package.json +1 -1
- package/src/commands/add.ts +310 -285
- package/src/commands/init.ts +173 -174
- package/src/registry/index.ts +3 -0
- package/src/templates/utils.ts +31 -1
- package/src/utils/config.ts +1 -1
- package/src/utils/shortcut-registry.ts +3 -7
package/src/commands/init.ts
CHANGED
|
@@ -38,7 +38,7 @@ async function fetchLibFileContent(file: string): Promise<string> {
|
|
|
38
38
|
}
|
|
39
39
|
return response.text();
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
interface InitOptions {
|
|
43
43
|
yes?: boolean;
|
|
44
44
|
defaults?: boolean;
|
|
@@ -61,18 +61,18 @@ function resolveAliasOrPath(cwd: string, aliasOrPath: string): string {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
export async function init(options: InitOptions) {
|
|
64
|
-
console.log(chalk.bold('\nšØ Welcome to shadcn-angular!\n'));
|
|
65
|
-
|
|
66
|
-
const cwd = process.cwd();
|
|
67
|
-
|
|
68
|
-
// Check if this is an Angular project
|
|
69
|
-
const angularJsonPath = path.join(cwd, 'angular.json');
|
|
70
|
-
if (!await fs.pathExists(angularJsonPath)) {
|
|
71
|
-
console.log(chalk.red('Error: This does not appear to be an Angular project.'));
|
|
72
|
-
console.log(chalk.dim('Please run this command in the root of your Angular project.'));
|
|
73
|
-
process.exit(1);
|
|
74
|
-
}
|
|
75
|
-
|
|
64
|
+
console.log(chalk.bold('\nšØ Welcome to shadcn-angular!\n'));
|
|
65
|
+
|
|
66
|
+
const cwd = process.cwd();
|
|
67
|
+
|
|
68
|
+
// Check if this is an Angular project
|
|
69
|
+
const angularJsonPath = path.join(cwd, 'angular.json');
|
|
70
|
+
if (!await fs.pathExists(angularJsonPath)) {
|
|
71
|
+
console.log(chalk.red('Error: This does not appear to be an Angular project.'));
|
|
72
|
+
console.log(chalk.dim('Please run this command in the root of your Angular project.'));
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
76
|
// Check if already initialized
|
|
77
77
|
const componentsJsonPath = path.join(cwd, 'components.json');
|
|
78
78
|
if (await fs.pathExists(componentsJsonPath)) {
|
|
@@ -87,93 +87,93 @@ export async function init(options: InitOptions) {
|
|
|
87
87
|
if (!overwrite) {
|
|
88
88
|
console.log(chalk.dim('Initialization cancelled.'));
|
|
89
89
|
return;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
93
|
let config: Config;
|
|
94
94
|
let createShortcutRegistry = true;
|
|
95
|
-
|
|
95
|
+
|
|
96
96
|
if (options.defaults || options.yes) {
|
|
97
97
|
config = getDefaultConfig();
|
|
98
98
|
createShortcutRegistry = true;
|
|
99
99
|
} else {
|
|
100
|
-
const THEME_COLORS: Record<string, string> = {
|
|
101
|
-
zinc: '#71717a',
|
|
102
|
-
slate: '#64748b',
|
|
103
|
-
stone: '#78716c',
|
|
104
|
-
gray: '#6b7280',
|
|
105
|
-
neutral: '#737373',
|
|
106
|
-
red: '#ef4444',
|
|
107
|
-
rose: '#f43f5e',
|
|
108
|
-
orange: '#f97316', // bright orange
|
|
109
|
-
green: '#22c55e',
|
|
110
|
-
blue: '#3b82f6',
|
|
111
|
-
yellow: '#eab308',
|
|
112
|
-
violet: '#8b5cf6',
|
|
113
|
-
amber: '#d97706', // warm amber for preview
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
const themeChoices = [
|
|
117
|
-
{ title: 'Zinc', value: 'zinc' },
|
|
118
|
-
{ title: 'Slate', value: 'slate' },
|
|
119
|
-
{ title: 'Stone', value: 'stone' },
|
|
120
|
-
{ title: 'Gray', value: 'gray' },
|
|
121
|
-
{ title: 'Neutral', value: 'neutral' },
|
|
122
|
-
{ title: 'Red', value: 'red' },
|
|
123
|
-
{ title: 'Rose', value: 'rose' },
|
|
124
|
-
{ title: 'Orange', value: 'orange' },
|
|
125
|
-
{ title: 'Green', value: 'green' },
|
|
126
|
-
{ title: 'Blue', value: 'blue' },
|
|
127
|
-
{ title: 'Yellow', value: 'yellow' },
|
|
128
|
-
{ title: 'Violet', value: 'violet' },
|
|
129
|
-
{ title: 'Amber', value: 'amber' },
|
|
130
|
-
].map(c => ({
|
|
131
|
-
...c,
|
|
132
|
-
title: `${chalk.hex(THEME_COLORS[c.value])('āā')} ${c.title}`
|
|
133
|
-
}));
|
|
134
|
-
|
|
135
|
-
const baseColorChoices = [
|
|
136
|
-
{ title: 'Neutral', value: 'neutral' },
|
|
137
|
-
{ title: 'Slate', value: 'slate' },
|
|
138
|
-
{ title: 'Stone', value: 'stone' },
|
|
139
|
-
{ title: 'Gray', value: 'gray' },
|
|
140
|
-
{ title: 'Zinc', value: 'zinc' },
|
|
141
|
-
].map(c => ({
|
|
142
|
-
...c,
|
|
143
|
-
title: `${chalk.hex(THEME_COLORS[c.value])('āā')} ${c.title}`
|
|
144
|
-
}));
|
|
145
|
-
|
|
146
|
-
const responses = await prompts([
|
|
147
|
-
|
|
148
|
-
{
|
|
149
|
-
type: 'select',
|
|
150
|
-
name: 'baseColor',
|
|
151
|
-
message: 'Which color would you like to use as base color?',
|
|
152
|
-
choices: baseColorChoices,
|
|
153
|
-
initial: 0,
|
|
154
|
-
},
|
|
155
|
-
{
|
|
156
|
-
type: 'select',
|
|
157
|
-
name: 'theme',
|
|
158
|
-
message: 'Which color would you like to use for the main theme?',
|
|
159
|
-
choices: themeChoices,
|
|
160
|
-
initial: (prev: string) => {
|
|
161
|
-
const index = themeChoices.findIndex(c => c.value === prev);
|
|
162
|
-
return index === -1 ? 0 : index;
|
|
163
|
-
},
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
type: 'text',
|
|
167
|
-
name: 'componentsPath',
|
|
168
|
-
message: 'Where would you like to install components?',
|
|
169
|
-
initial: 'src/components/ui',
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
type: 'text',
|
|
173
|
-
name: 'utilsPath',
|
|
174
|
-
message: 'Where would you like to install utils?',
|
|
175
|
-
initial: 'src/components/lib',
|
|
176
|
-
},
|
|
100
|
+
const THEME_COLORS: Record<string, string> = {
|
|
101
|
+
zinc: '#71717a',
|
|
102
|
+
slate: '#64748b',
|
|
103
|
+
stone: '#78716c',
|
|
104
|
+
gray: '#6b7280',
|
|
105
|
+
neutral: '#737373',
|
|
106
|
+
red: '#ef4444',
|
|
107
|
+
rose: '#f43f5e',
|
|
108
|
+
orange: '#f97316', // bright orange
|
|
109
|
+
green: '#22c55e',
|
|
110
|
+
blue: '#3b82f6',
|
|
111
|
+
yellow: '#eab308',
|
|
112
|
+
violet: '#8b5cf6',
|
|
113
|
+
amber: '#d97706', // warm amber for preview
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const themeChoices = [
|
|
117
|
+
{ title: 'Zinc', value: 'zinc' },
|
|
118
|
+
{ title: 'Slate', value: 'slate' },
|
|
119
|
+
{ title: 'Stone', value: 'stone' },
|
|
120
|
+
{ title: 'Gray', value: 'gray' },
|
|
121
|
+
{ title: 'Neutral', value: 'neutral' },
|
|
122
|
+
{ title: 'Red', value: 'red' },
|
|
123
|
+
{ title: 'Rose', value: 'rose' },
|
|
124
|
+
{ title: 'Orange', value: 'orange' },
|
|
125
|
+
{ title: 'Green', value: 'green' },
|
|
126
|
+
{ title: 'Blue', value: 'blue' },
|
|
127
|
+
{ title: 'Yellow', value: 'yellow' },
|
|
128
|
+
{ title: 'Violet', value: 'violet' },
|
|
129
|
+
{ title: 'Amber', value: 'amber' },
|
|
130
|
+
].map(c => ({
|
|
131
|
+
...c,
|
|
132
|
+
title: `${chalk.hex(THEME_COLORS[c.value])('āā')} ${c.title}`
|
|
133
|
+
}));
|
|
134
|
+
|
|
135
|
+
const baseColorChoices = [
|
|
136
|
+
{ title: 'Neutral', value: 'neutral' },
|
|
137
|
+
{ title: 'Slate', value: 'slate' },
|
|
138
|
+
{ title: 'Stone', value: 'stone' },
|
|
139
|
+
{ title: 'Gray', value: 'gray' },
|
|
140
|
+
{ title: 'Zinc', value: 'zinc' },
|
|
141
|
+
].map(c => ({
|
|
142
|
+
...c,
|
|
143
|
+
title: `${chalk.hex(THEME_COLORS[c.value])('āā')} ${c.title}`
|
|
144
|
+
}));
|
|
145
|
+
|
|
146
|
+
const responses = await prompts([
|
|
147
|
+
|
|
148
|
+
{
|
|
149
|
+
type: 'select',
|
|
150
|
+
name: 'baseColor',
|
|
151
|
+
message: 'Which color would you like to use as base color?',
|
|
152
|
+
choices: baseColorChoices,
|
|
153
|
+
initial: 0,
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
type: 'select',
|
|
157
|
+
name: 'theme',
|
|
158
|
+
message: 'Which color would you like to use for the main theme?',
|
|
159
|
+
choices: themeChoices,
|
|
160
|
+
initial: (prev: string) => {
|
|
161
|
+
const index = themeChoices.findIndex(c => c.value === prev);
|
|
162
|
+
return index === -1 ? 0 : index;
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
type: 'text',
|
|
167
|
+
name: 'componentsPath',
|
|
168
|
+
message: 'Where would you like to install components?',
|
|
169
|
+
initial: 'src/components/ui',
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
type: 'text',
|
|
173
|
+
name: 'utilsPath',
|
|
174
|
+
message: 'Where would you like to install utils?',
|
|
175
|
+
initial: 'src/components/lib',
|
|
176
|
+
},
|
|
177
177
|
{
|
|
178
178
|
type: 'text',
|
|
179
179
|
name: 'globalCss',
|
|
@@ -187,47 +187,46 @@ export async function init(options: InitOptions) {
|
|
|
187
187
|
initial: true,
|
|
188
188
|
},
|
|
189
189
|
]);
|
|
190
|
-
|
|
190
|
+
|
|
191
191
|
config = {
|
|
192
192
|
$schema: 'https://shadcn-angular.dev/schema.json',
|
|
193
|
-
style: 'default',
|
|
194
|
-
tailwind: {
|
|
195
|
-
css: responses.globalCss,
|
|
196
|
-
baseColor: responses.baseColor,
|
|
197
|
-
theme: responses.theme,
|
|
198
|
-
cssVariables: true,
|
|
199
|
-
},
|
|
200
|
-
aliases: {
|
|
201
|
-
components: responses.componentsPath.replace('src/', '@/'), // Basic heuristic
|
|
202
|
-
utils: responses.utilsPath.replace('src/', '@/').replace('.ts', ''),
|
|
203
|
-
ui: responses.componentsPath.replace('src/', '@/'),
|
|
193
|
+
style: 'default',
|
|
194
|
+
tailwind: {
|
|
195
|
+
css: responses.globalCss,
|
|
196
|
+
baseColor: responses.baseColor,
|
|
197
|
+
theme: responses.theme,
|
|
198
|
+
cssVariables: true,
|
|
199
|
+
},
|
|
200
|
+
aliases: {
|
|
201
|
+
components: responses.componentsPath.replace('src/', '@/'), // Basic heuristic
|
|
202
|
+
utils: responses.utilsPath.replace('src/', '@/').replace('.ts', ''),
|
|
203
|
+
ui: responses.componentsPath.replace('src/', '@/'),
|
|
204
204
|
},
|
|
205
205
|
};
|
|
206
206
|
createShortcutRegistry = responses.createShortcutRegistry ?? true;
|
|
207
207
|
}
|
|
208
|
-
|
|
209
|
-
const spinner = ora('Initializing project...').start();
|
|
210
|
-
|
|
211
|
-
try {
|
|
212
|
-
// Write components.json
|
|
213
|
-
await fs.writeJson(componentsJsonPath, config, { spaces: 2 });
|
|
214
|
-
spinner.text = 'Created components.json';
|
|
215
|
-
|
|
216
|
-
// Create utils directory and file
|
|
217
|
-
// Resolve path from the config alias, assuming @/ maps to src/ logic for file creation if not provided directly
|
|
218
|
-
// But we have the 'responses' object from CLI prompt only in the else block above!
|
|
219
|
-
// So we should rely on config to reconstruct the path, or better yet, if we are in 'defaults' mode, check what config is.
|
|
220
|
-
// If config came from defaults, aliases are set.
|
|
221
|
-
// We can reverse-map alias to path: @/ -> src/
|
|
222
|
-
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
await fs.
|
|
227
|
-
await fs.writeFile(utilsPathResolved, getUtilsTemplate());
|
|
208
|
+
|
|
209
|
+
const spinner = ora('Initializing project...').start();
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
// Write components.json
|
|
213
|
+
await fs.writeJson(componentsJsonPath, config, { spaces: 2 });
|
|
214
|
+
spinner.text = 'Created components.json';
|
|
215
|
+
|
|
216
|
+
// Create utils directory and file
|
|
217
|
+
// Resolve path from the config alias, assuming @/ maps to src/ logic for file creation if not provided directly
|
|
218
|
+
// But we have the 'responses' object from CLI prompt only in the else block above!
|
|
219
|
+
// So we should rely on config to reconstruct the path, or better yet, if we are in 'defaults' mode, check what config is.
|
|
220
|
+
// If config came from defaults, aliases are set.
|
|
221
|
+
// We can reverse-map alias to path: @/ -> src/
|
|
222
|
+
|
|
223
|
+
const libDir = resolveAliasOrPath(cwd, config.aliases.utils);
|
|
224
|
+
|
|
225
|
+
await fs.ensureDir(libDir);
|
|
226
|
+
await fs.writeFile(path.join(libDir, 'utils.ts'), getUtilsTemplate());
|
|
228
227
|
spinner.text = 'Created utils.ts';
|
|
229
228
|
|
|
230
|
-
const shortcutServicePath = path.join(
|
|
229
|
+
const shortcutServicePath = path.join(libDir, 'shortcut-binding.service.ts');
|
|
231
230
|
const shortcutServiceContent = await fetchLibFileContent('shortcut-binding.service.ts');
|
|
232
231
|
await fs.writeFile(shortcutServicePath, shortcutServiceContent);
|
|
233
232
|
spinner.text = 'Created shortcut-binding.service.ts';
|
|
@@ -251,63 +250,63 @@ export async function init(options: InitOptions) {
|
|
|
251
250
|
let userStyles = await fs.pathExists(userStylesPath)
|
|
252
251
|
? await fs.readFile(userStylesPath, 'utf-8')
|
|
253
252
|
: '';
|
|
254
|
-
|
|
255
|
-
const tailwindImport = '@import "./tailwind.css";';
|
|
256
|
-
if (!userStyles.includes('tailwind.css')) {
|
|
257
|
-
// Add import at the top of the file
|
|
258
|
-
userStyles = tailwindImport + '\n\n' + userStyles;
|
|
259
|
-
await fs.writeFile(userStylesPath, userStyles);
|
|
260
|
-
spinner.text = 'Added tailwind.css import to styles';
|
|
253
|
+
|
|
254
|
+
const tailwindImport = '@import "./tailwind.css";';
|
|
255
|
+
if (!userStyles.includes('tailwind.css')) {
|
|
256
|
+
// Add import at the top of the file
|
|
257
|
+
userStyles = tailwindImport + '\n\n' + userStyles;
|
|
258
|
+
await fs.writeFile(userStylesPath, userStyles);
|
|
259
|
+
spinner.text = 'Added tailwind.css import to styles';
|
|
261
260
|
}
|
|
262
261
|
|
|
263
262
|
// Create components/ui directory
|
|
264
263
|
const uiDir = resolveAliasOrPath(cwd, config.aliases.ui);
|
|
265
264
|
await fs.ensureDir(uiDir);
|
|
266
265
|
spinner.text = 'Created components directory';
|
|
267
|
-
|
|
268
|
-
// Install dependencies
|
|
269
|
-
spinner.text = 'Installing dependencies...';
|
|
266
|
+
|
|
267
|
+
// Install dependencies
|
|
268
|
+
spinner.text = 'Installing dependencies...';
|
|
270
269
|
const dependencies = [
|
|
271
270
|
'clsx',
|
|
272
271
|
'tailwind-merge',
|
|
273
|
-
'class-variance-authority',
|
|
274
|
-
'tailwindcss',
|
|
272
|
+
'class-variance-authority',
|
|
273
|
+
'tailwindcss',
|
|
275
274
|
'postcss',
|
|
276
275
|
'@tailwindcss/postcss'
|
|
277
276
|
];
|
|
278
277
|
await installPackages(dependencies, { cwd });
|
|
279
|
-
|
|
280
|
-
// Setup PostCSS - create .postcssrc.json which is the preferred format for Angular
|
|
281
|
-
spinner.text = 'Configuring PostCSS...';
|
|
282
|
-
const postcssrcPath = path.join(cwd, '.postcssrc.json');
|
|
283
|
-
|
|
284
|
-
if (!await fs.pathExists(postcssrcPath)) {
|
|
285
|
-
const configContent = {
|
|
286
|
-
plugins: {
|
|
287
|
-
'@tailwindcss/postcss': {}
|
|
288
|
-
}
|
|
289
|
-
};
|
|
290
|
-
await fs.writeJson(postcssrcPath, configContent, { spaces: 4 });
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
spinner.succeed(chalk.green('Project initialized successfully!'));
|
|
295
|
-
|
|
296
|
-
console.log('\n' + chalk.bold('Next steps:'));
|
|
297
|
-
console.log(chalk.dim(' 1. Add components: ') + chalk.cyan('npx @gilav21/shadcn-angular add button'));
|
|
298
|
-
console.log(chalk.dim(' 2. Import and use in your templates'));
|
|
299
|
-
console.log(chalk.dim(' 3. Update your ') + chalk.bold('tsconfig.json') + chalk.dim(' paths:'));
|
|
300
|
-
console.log(chalk.dim(' "compilerOptions": {'));
|
|
301
|
-
console.log(chalk.dim(' "baseUrl": ".",'));
|
|
302
|
-
console.log(chalk.dim(' "paths": {'));
|
|
303
|
-
console.log(chalk.dim(' "@/*": ["./src/*"]'));
|
|
304
|
-
console.log(chalk.dim(' }'));
|
|
305
|
-
console.log(chalk.dim(' }'));
|
|
306
|
-
console.log('');
|
|
307
|
-
|
|
308
|
-
} catch (error) {
|
|
309
|
-
spinner.fail('Failed to initialize project');
|
|
310
|
-
console.error(error);
|
|
311
|
-
process.exit(1);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
278
|
+
|
|
279
|
+
// Setup PostCSS - create .postcssrc.json which is the preferred format for Angular
|
|
280
|
+
spinner.text = 'Configuring PostCSS...';
|
|
281
|
+
const postcssrcPath = path.join(cwd, '.postcssrc.json');
|
|
282
|
+
|
|
283
|
+
if (!await fs.pathExists(postcssrcPath)) {
|
|
284
|
+
const configContent = {
|
|
285
|
+
plugins: {
|
|
286
|
+
'@tailwindcss/postcss': {}
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
await fs.writeJson(postcssrcPath, configContent, { spaces: 4 });
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
spinner.succeed(chalk.green('Project initialized successfully!'));
|
|
294
|
+
|
|
295
|
+
console.log('\n' + chalk.bold('Next steps:'));
|
|
296
|
+
console.log(chalk.dim(' 1. Add components: ') + chalk.cyan('npx @gilav21/shadcn-angular add button'));
|
|
297
|
+
console.log(chalk.dim(' 2. Import and use in your templates'));
|
|
298
|
+
console.log(chalk.dim(' 3. Update your ') + chalk.bold('tsconfig.json') + chalk.dim(' paths:'));
|
|
299
|
+
console.log(chalk.dim(' "compilerOptions": {'));
|
|
300
|
+
console.log(chalk.dim(' "baseUrl": ".",'));
|
|
301
|
+
console.log(chalk.dim(' "paths": {'));
|
|
302
|
+
console.log(chalk.dim(' "@/*": ["./src/*"]'));
|
|
303
|
+
console.log(chalk.dim(' }'));
|
|
304
|
+
console.log(chalk.dim(' }'));
|
|
305
|
+
console.log('');
|
|
306
|
+
|
|
307
|
+
} catch (error) {
|
|
308
|
+
spinner.fail('Failed to initialize project');
|
|
309
|
+
console.error(error);
|
|
310
|
+
process.exit(1);
|
|
311
|
+
}
|
|
312
|
+
}
|
package/src/registry/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ export interface ComponentDefinition {
|
|
|
6
6
|
files: string[]; // Relative paths to component files
|
|
7
7
|
dependencies?: string[]; // Other components this depends on
|
|
8
8
|
npmDependencies?: string[]; // NPM packages this depends on
|
|
9
|
+
libFiles?: string[]; // Lib utility files this component requires (e.g. 'xlsx.ts')
|
|
9
10
|
shortcutDefinitions?: {
|
|
10
11
|
exportName: string;
|
|
11
12
|
componentName: string;
|
|
@@ -155,6 +156,7 @@ export const registry: Record<string, ComponentDefinition> = {
|
|
|
155
156
|
'component-outlet',
|
|
156
157
|
'icon',
|
|
157
158
|
],
|
|
159
|
+
libFiles: ['xlsx.ts'],
|
|
158
160
|
},
|
|
159
161
|
dialog: {
|
|
160
162
|
name: 'dialog',
|
|
@@ -392,6 +394,7 @@ export const registry: Record<string, ComponentDefinition> = {
|
|
|
392
394
|
'dialog',
|
|
393
395
|
'scroll-area',
|
|
394
396
|
],
|
|
397
|
+
libFiles: ['pdf-parser.ts', 'image-validator.ts', 'svg-sanitizer.ts'],
|
|
395
398
|
shortcutDefinitions: [
|
|
396
399
|
{
|
|
397
400
|
exportName: 'RICH_TEXT_SHORTCUT_DEFINITIONS',
|
package/src/templates/utils.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { twMerge } from 'tailwind-merge';
|
|
|
6
6
|
* Utility function for merging Tailwind CSS classes with proper precedence
|
|
7
7
|
*/
|
|
8
8
|
export function cn(...inputs: ClassValue[]): string {
|
|
9
|
-
|
|
9
|
+
return twMerge(clsx(inputs));
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -17,5 +17,35 @@ export function isRtl(el: HTMLElement): boolean {
|
|
|
17
17
|
return getComputedStyle(el).direction === 'rtl';
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Returns the bounding rect of the nearest ancestor that clips overflow
|
|
22
|
+
* (overflow: hidden | auto | scroll | clip on either axis).
|
|
23
|
+
* Falls back to the full viewport rect when no such ancestor exists.
|
|
24
|
+
*
|
|
25
|
+
* Use this instead of \`window.innerWidth/innerHeight\` when calculating
|
|
26
|
+
* popup collision boundaries so that containers like sidebars or
|
|
27
|
+
* fixed-height scroll panes are respected.
|
|
28
|
+
*/
|
|
29
|
+
export function getClippingRect(element: HTMLElement): DOMRect {
|
|
30
|
+
let parent = element.parentElement;
|
|
31
|
+
while (parent && parent !== document.documentElement) {
|
|
32
|
+
const style = window.getComputedStyle(parent);
|
|
33
|
+
if (
|
|
34
|
+
/^(hidden|auto|scroll|clip)$/.test(style.overflowX) ||
|
|
35
|
+
/^(hidden|auto|scroll|clip)$/.test(style.overflowY)
|
|
36
|
+
) {
|
|
37
|
+
return parent.getBoundingClientRect();
|
|
38
|
+
}
|
|
39
|
+
parent = parent.parentElement;
|
|
40
|
+
}
|
|
41
|
+
return new DOMRect(0, 0, window.innerWidth, window.innerHeight);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Check if the user prefers reduced motion via the OS-level accessibility setting.
|
|
46
|
+
*/
|
|
47
|
+
export function prefersReducedMotion(): boolean {
|
|
48
|
+
return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
49
|
+
}
|
|
20
50
|
`;
|
|
21
51
|
}
|
package/src/utils/config.ts
CHANGED
|
@@ -24,9 +24,8 @@ function resolveProjectPath(cwd: string, inputPath: string): string {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
function getShortcutRegistryIndexPath(cwd: string, config: Config): string {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
return path.join(utilsDir, 'shortcut-registry.index.ts');
|
|
27
|
+
const libDir = resolveProjectPath(cwd, aliasToProjectPath(config.aliases.utils));
|
|
28
|
+
return path.join(libDir, 'shortcut-registry.index.ts');
|
|
30
29
|
}
|
|
31
30
|
|
|
32
31
|
export async function writeShortcutRegistryIndex(
|
|
@@ -41,10 +40,7 @@ export async function writeShortcutRegistryIndex(
|
|
|
41
40
|
.sort((a, b) => a.exportName.localeCompare(b.exportName));
|
|
42
41
|
|
|
43
42
|
const uiAlias = config.aliases.ui;
|
|
44
|
-
const
|
|
45
|
-
? config.aliases.utils.slice(0, config.aliases.utils.lastIndexOf('/'))
|
|
46
|
-
: config.aliases.utils;
|
|
47
|
-
const shortcutServiceImport = `${utilsAliasDir}/shortcut-binding.service`;
|
|
43
|
+
const shortcutServiceImport = `${config.aliases.utils}/shortcut-binding.service`;
|
|
48
44
|
|
|
49
45
|
const imports = uniqueEntries
|
|
50
46
|
.map(entry => {
|