@salas-ds/cli 0.1.0 → 0.2.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.
Files changed (92) hide show
  1. package/dist/index.js +253 -86
  2. package/package.json +4 -5
  3. package/templates/angular/accordion/accordion-content.component.ts +9 -0
  4. package/templates/angular/accordion/accordion-item.component.ts +138 -0
  5. package/templates/angular/accordion/accordion-trigger.component.ts +9 -0
  6. package/templates/angular/accordion/accordion.component.ts +120 -0
  7. package/templates/angular/accordion/accordion.module.ts +21 -0
  8. package/templates/angular/autocomplete/autocomplete.component.ts +707 -0
  9. package/templates/angular/autocomplete/autocomplete.module.ts +8 -0
  10. package/templates/angular/avatar/avatar-badge.component.ts +18 -0
  11. package/templates/angular/avatar/avatar-fallback.component.ts +39 -0
  12. package/templates/angular/avatar/avatar-group-count.component.ts +46 -0
  13. package/templates/angular/avatar/avatar-group.component.ts +33 -0
  14. package/templates/angular/avatar/avatar-image.component.ts +57 -0
  15. package/templates/angular/avatar/avatar.component.ts +73 -0
  16. package/templates/angular/avatar/avatar.module.ts +27 -0
  17. package/templates/angular/badge/badge.component.ts +84 -0
  18. package/templates/angular/badge/badge.module.ts +9 -0
  19. package/templates/angular/button/button.component.ts +24 -4
  20. package/templates/angular/card/card.component.ts +100 -0
  21. package/templates/angular/card/card.module.ts +8 -0
  22. package/templates/angular/checkbox/checkbox.component.ts +172 -0
  23. package/templates/angular/checkbox/checkbox.module.ts +8 -0
  24. package/templates/angular/datepicker/datepicker.component.ts +660 -0
  25. package/templates/angular/datepicker/datepicker.module.ts +8 -0
  26. package/templates/angular/dialog/dialog-content.component.ts +9 -0
  27. package/templates/angular/dialog/dialog-description.component.ts +17 -0
  28. package/templates/angular/dialog/dialog-footer.component.ts +17 -0
  29. package/templates/angular/dialog/dialog-header.component.ts +14 -0
  30. package/templates/angular/dialog/dialog-title.component.ts +18 -0
  31. package/templates/angular/dialog/dialog-trigger.component.ts +9 -0
  32. package/templates/angular/dialog/dialog.component.ts +212 -0
  33. package/templates/angular/dialog/dialog.module.ts +31 -0
  34. package/templates/angular/input/input.component.ts +229 -0
  35. package/templates/angular/input/input.module.ts +8 -0
  36. package/templates/angular/scroll-area/scroll-area.component.ts +72 -0
  37. package/templates/angular/scroll-area/scroll-area.module.ts +9 -0
  38. package/templates/angular/scroll-area/scroll-bar.component.ts +15 -0
  39. package/templates/angular/select/select.component.ts +292 -0
  40. package/templates/angular/select/select.module.ts +8 -0
  41. package/templates/angular/separator/separator.component.ts +63 -0
  42. package/templates/angular/separator/separator.module.ts +9 -0
  43. package/templates/angular/sheet/sheet-content.component.ts +13 -0
  44. package/templates/angular/sheet/sheet-description.component.ts +29 -0
  45. package/templates/angular/sheet/sheet-footer.component.ts +27 -0
  46. package/templates/angular/sheet/sheet-header.component.ts +26 -0
  47. package/templates/angular/sheet/sheet-title.component.ts +31 -0
  48. package/templates/angular/sheet/sheet-trigger.component.ts +11 -0
  49. package/templates/angular/sheet/sheet.component.ts +251 -0
  50. package/templates/angular/sheet/sheet.module.ts +30 -0
  51. package/templates/angular/sidebar/sidebar-content.component.ts +18 -0
  52. package/templates/angular/sidebar/sidebar-footer.component.ts +20 -0
  53. package/templates/angular/sidebar/sidebar-group-content.component.ts +16 -0
  54. package/templates/angular/sidebar/sidebar-group-label.component.ts +20 -0
  55. package/templates/angular/sidebar/sidebar-group.component.ts +14 -0
  56. package/templates/angular/sidebar/sidebar-header.component.ts +20 -0
  57. package/templates/angular/sidebar/sidebar-inset.component.ts +85 -0
  58. package/templates/angular/sidebar/sidebar-menu-button.component.ts +75 -0
  59. package/templates/angular/sidebar/sidebar-menu-item.component.ts +14 -0
  60. package/templates/angular/sidebar/sidebar-menu.component.ts +19 -0
  61. package/templates/angular/sidebar/sidebar-provider.component.ts +68 -0
  62. package/templates/angular/sidebar/sidebar-trigger.component.ts +58 -0
  63. package/templates/angular/sidebar/sidebar.component.ts +196 -0
  64. package/templates/angular/sidebar/sidebar.module.ts +48 -0
  65. package/templates/angular/sidebar/sidebar.service.ts +93 -0
  66. package/templates/angular/skeleton/skeleton.component.ts +44 -0
  67. package/templates/angular/skeleton/skeleton.module.ts +8 -0
  68. package/templates/angular/spinner/spinner.component.ts +75 -0
  69. package/templates/angular/spinner/spinner.module.ts +8 -0
  70. package/templates/angular/table/table-body.component.ts +23 -0
  71. package/templates/angular/table/table-caption.component.ts +29 -0
  72. package/templates/angular/table/table-cell.component.ts +49 -0
  73. package/templates/angular/table/table-footer.component.ts +32 -0
  74. package/templates/angular/table/table-head.component.ts +48 -0
  75. package/templates/angular/table/table-header.component.ts +28 -0
  76. package/templates/angular/table/table-row.component.ts +36 -0
  77. package/templates/angular/table/table.component.ts +35 -0
  78. package/templates/angular/table/table.module.ts +33 -0
  79. package/templates/angular/tabs/tabs-content.component.ts +71 -0
  80. package/templates/angular/tabs/tabs-list.component.ts +70 -0
  81. package/templates/angular/tabs/tabs-trigger.component.ts +149 -0
  82. package/templates/angular/tabs/tabs.component.ts +155 -0
  83. package/templates/angular/tabs/tabs.module.ts +21 -0
  84. package/templates/angular/textarea/textarea.component.ts +268 -0
  85. package/templates/angular/textarea/textarea.module.ts +8 -0
  86. package/templates/angular/toast/toast.module.ts +8 -0
  87. package/templates/angular/toast/toast.service.ts +104 -0
  88. package/templates/angular/toast/toaster.component.ts +329 -0
  89. package/templates/angular/tooltip/tooltip-content.component.ts +43 -0
  90. package/templates/angular/tooltip/tooltip-trigger.component.ts +13 -0
  91. package/templates/angular/tooltip/tooltip.component.ts +243 -0
  92. package/templates/angular/tooltip/tooltip.module.ts +10 -0
package/dist/index.js CHANGED
@@ -5,7 +5,6 @@ import fs2 from 'fs-extra';
5
5
  import path2 from 'path';
6
6
  import { fileURLToPath } from 'url';
7
7
  import { glob } from 'glob';
8
- import inquirer from 'inquirer';
9
8
 
10
9
  // src/index.ts
11
10
 
@@ -175,118 +174,286 @@ var THEME_CSS = `/* Salas Design System - Light & Dark theme */
175
174
  [data-theme="dark"] .salas-autocomplete-loading { color: var(--salas-text-muted); }
176
175
  `;
177
176
 
177
+ // src/utils-template.ts
178
+ var UTILS_TS = `/**
179
+ * Salas Design System - Utility functions
180
+ * Generated by @salas-ds/cli
181
+ */
182
+
183
+ export const cn = (...classes: (string | undefined | null | false)[]): string => {
184
+ return classes.filter(Boolean).join(' ');
185
+ };
186
+
187
+ export const isEqual = <T>(a: T, b: T): boolean => {
188
+ if (Object.is(a, b)) return true;
189
+ if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) {
190
+ return false;
191
+ }
192
+ const keysA = Object.keys(a);
193
+ const keysB = Object.keys(b);
194
+ if (keysA.length !== keysB.length) return false;
195
+ for (const key of keysA) {
196
+ if (!keysB.includes(key)) return false;
197
+ if (!isEqual((a as any)[key], (b as any)[key])) return false;
198
+ }
199
+ return true;
200
+ };
201
+
202
+ export const debounce = <T extends (...args: any[]) => any>(
203
+ func: T,
204
+ wait: number
205
+ ): ((...args: Parameters<T>) => void) => {
206
+ let timeout: ReturnType<typeof setTimeout> | null = null;
207
+ return (...args: Parameters<T>) => {
208
+ if (timeout) clearTimeout(timeout);
209
+ timeout = setTimeout(() => func(...args), wait);
210
+ };
211
+ };
212
+
213
+ export const throttle = <T extends (...args: any[]) => any>(
214
+ func: T,
215
+ limit: number
216
+ ): ((...args: Parameters<T>) => void) => {
217
+ let inThrottle: boolean;
218
+ return (...args: Parameters<T>) => {
219
+ if (!inThrottle) {
220
+ func(...args);
221
+ inThrottle = true;
222
+ setTimeout(() => (inThrottle = false), limit);
223
+ }
224
+ };
225
+ };
226
+ `;
227
+
178
228
  // src/commands/init.ts
179
- var __filename$1 = fileURLToPath(import.meta.url);
180
- path2.dirname(__filename$1);
229
+ var CONFIG_FILE = "salas-ds.json";
181
230
  async function initCommand(options) {
182
231
  console.log(chalk2.blue("\u{1F680} Inicializando Salas Design System...\n"));
183
232
  const projectRoot = process.cwd();
184
233
  const componentsPath = path2.join(projectRoot, options.path);
185
234
  try {
235
+ const angularJsonPath = path2.join(projectRoot, "angular.json");
236
+ const isAngular = await fs2.pathExists(angularJsonPath);
237
+ if (!isAngular) {
238
+ console.error(chalk2.red("\u274C Projeto Angular n\xE3o detectado (angular.json n\xE3o encontrado)."));
239
+ console.log(chalk2.gray("Execute este comando na raiz de um projeto Angular."));
240
+ process.exit(1);
241
+ }
186
242
  await fs2.ensureDir(componentsPath);
243
+ const config = {
244
+ $schema: "https://salas-ds.dev/schema.json",
245
+ framework: options.framework,
246
+ componentsDir: options.path,
247
+ utils: `${options.path}/utils.ts`,
248
+ styles: `${options.path}/styles.css`
249
+ };
250
+ await fs2.writeJSON(path2.join(projectRoot, CONFIG_FILE), config, { spaces: 2 });
187
251
  const cssPath = path2.join(componentsPath, "styles.css");
188
252
  await fs2.writeFile(cssPath, THEME_CSS);
189
- console.log(chalk2.green("\u2705 Salas Design System inicializado com sucesso!"));
190
- console.log(chalk2.gray(` Componentes ser\xE3o adicionados em: ${options.path}`));
191
- console.log(chalk2.gray(` CSS variables criado em: ${options.path}/styles.css
253
+ const utilsPath = path2.join(componentsPath, "utils.ts");
254
+ await fs2.writeFile(utilsPath, UTILS_TS);
255
+ console.log(chalk2.green("\u2705 Salas Design System inicializado com sucesso!\n"));
256
+ console.log(chalk2.gray("Arquivos criados:"));
257
+ console.log(chalk2.gray(` ${CONFIG_FILE}`));
258
+ console.log(chalk2.gray(` ${options.path}/styles.css`));
259
+ console.log(chalk2.gray(` ${options.path}/utils.ts
192
260
  `));
193
261
  console.log(chalk2.blue("\u{1F4A1} Pr\xF3ximos passos:"));
194
- console.log(chalk2.gray(` npx @salas-ds/cli add button`));
195
- console.log(chalk2.gray(` npx @salas-ds/cli add input`));
262
+ console.log(chalk2.gray(` 1. Importe o CSS no seu styles.css ou angular.json:`));
263
+ console.log(chalk2.white(` @import './${options.path}/styles.css';`));
264
+ console.log(chalk2.gray(`
265
+ 2. Adicione componentes:`));
266
+ console.log(chalk2.white(` npx @salas-ds/cli add button`));
267
+ console.log(chalk2.white(` npx @salas-ds/cli add input`));
268
+ console.log(chalk2.white(` npx @salas-ds/cli add --all`));
269
+ console.log(chalk2.gray(`
270
+ 3. Componentes dispon\xEDveis:`));
271
+ console.log(chalk2.gray(` accordion, autocomplete, avatar, badge, button, card,`));
272
+ console.log(chalk2.gray(` checkbox, datepicker, dialog, input, scroll-area, select,`));
273
+ console.log(chalk2.gray(` separator, sheet, sidebar, skeleton, spinner, table,`));
274
+ console.log(chalk2.gray(` tabs, textarea, toast, tooltip`));
196
275
  } catch (error) {
197
276
  console.error(chalk2.red("\u274C Erro ao inicializar:"), error);
198
277
  process.exit(1);
199
278
  }
200
279
  }
201
- var __filename2 = fileURLToPath(import.meta.url);
202
- var __dirname2 = path2.dirname(__filename2);
203
- var COMPONENTS = ["button", "input", "select", "autocomplete", "datepicker"];
204
- var DEFAULT_PATH = "src/components";
280
+ var __filename$1 = fileURLToPath(import.meta.url);
281
+ var __dirname$1 = path2.dirname(__filename$1);
282
+ var COMPONENTS = [
283
+ "accordion",
284
+ "autocomplete",
285
+ "avatar",
286
+ "badge",
287
+ "button",
288
+ "card",
289
+ "checkbox",
290
+ "datepicker",
291
+ "dialog",
292
+ "input",
293
+ "scroll-area",
294
+ "select",
295
+ "separator",
296
+ "sheet",
297
+ "sidebar",
298
+ "skeleton",
299
+ "spinner",
300
+ "table",
301
+ "tabs",
302
+ "textarea",
303
+ "toast",
304
+ "tooltip"
305
+ ];
306
+ var LUCIDE_COMPONENTS = ["autocomplete", "datepicker", "input", "select", "textarea"];
307
+ var CONFIG_FILE2 = "salas-ds.json";
308
+ function toPascalCase(str) {
309
+ return str.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
310
+ }
311
+ async function readConfig() {
312
+ const projectRoot = process.cwd();
313
+ const configPath = path2.join(projectRoot, CONFIG_FILE2);
314
+ if (!await fs2.pathExists(configPath)) {
315
+ return null;
316
+ }
317
+ return fs2.readJSON(configPath);
318
+ }
319
+ async function addSingleComponent(componentName, componentsDir, options) {
320
+ const projectRoot = process.cwd();
321
+ const componentPath = path2.join(projectRoot, componentsDir, componentName);
322
+ if (await fs2.pathExists(componentPath)) {
323
+ if (!options.overwrite) {
324
+ console.log(chalk2.yellow(` \u26A0 ${componentName} j\xE1 existe. Use --overwrite para substituir.`));
325
+ return false;
326
+ }
327
+ await fs2.remove(componentPath);
328
+ }
329
+ await fs2.ensureDir(componentPath);
330
+ const templatePath = path2.join(__dirname$1, "..", "templates", "angular", componentName);
331
+ if (!await fs2.pathExists(templatePath)) {
332
+ console.error(chalk2.red(` \u274C Template n\xE3o encontrado para: ${componentName}`));
333
+ return false;
334
+ }
335
+ const templateFiles = await glob("**/*", {
336
+ cwd: templatePath,
337
+ dot: false,
338
+ nodir: true
339
+ });
340
+ for (const file of templateFiles) {
341
+ const srcPath = path2.join(templatePath, file);
342
+ const destPath = path2.join(componentPath, file);
343
+ await fs2.ensureDir(path2.dirname(destPath));
344
+ await fs2.copyFile(srcPath, destPath);
345
+ }
346
+ return true;
347
+ }
205
348
  async function addCommand(componentName, options) {
206
- if (!COMPONENTS.includes(componentName)) {
207
- console.error(
208
- chalk2.red(`\u274C Componente "${componentName}" n\xE3o encontrado.`)
209
- );
210
- console.log(chalk2.gray(`Componentes dispon\xEDveis: ${COMPONENTS.join(", ")}`));
349
+ const config = await readConfig();
350
+ if (!config) {
351
+ console.error(chalk2.red("\u274C Projeto n\xE3o inicializado."));
352
+ console.log(chalk2.gray("Execute primeiro: npx @salas-ds/cli init"));
211
353
  process.exit(1);
212
354
  }
213
- console.log(chalk2.blue(`\u{1F4E6} Adicionando componente: ${componentName}
355
+ const componentsDir = options.path ?? config.componentsDir;
356
+ const componentsToAdd = [];
357
+ if (options.all) {
358
+ componentsToAdd.push(...COMPONENTS);
359
+ console.log(chalk2.blue(`\u{1F4E6} Adicionando todos os ${COMPONENTS.length} componentes...
214
360
  `));
215
- const defaultPath = options.path ?? DEFAULT_PATH;
216
- const { path: chosenPath } = await inquirer.prompt([
217
- {
218
- type: "input",
219
- name: "path",
220
- message: `Pasta onde o componente ser\xE1 criado (ser\xE1 criado: ${defaultPath}/${componentName})`,
221
- default: defaultPath,
222
- transformer: (input) => input.trim() || defaultPath
223
- }
224
- ]);
225
- const basePath = (chosenPath || defaultPath).trim();
226
- const projectRoot = process.cwd();
227
- const componentsPath = path2.join(projectRoot, basePath);
228
- const componentPath = path2.join(componentsPath, componentName);
229
- try {
230
- const angularJsonPath = path2.join(projectRoot, "angular.json");
231
- const isAngular = await fs2.pathExists(angularJsonPath);
232
- if (!isAngular) {
233
- console.error(chalk2.red("\u274C Projeto Angular n\xE3o detectado."));
234
- console.log(chalk2.gray("Execute este comando em um projeto Angular."));
235
- process.exit(1);
236
- }
237
- await fs2.ensureDir(componentPath);
238
- const templatePath = path2.join(
239
- __dirname2,
240
- "..",
241
- "..",
242
- "templates",
243
- "angular",
244
- componentName
245
- );
246
- if (!await fs2.pathExists(templatePath)) {
247
- console.error(chalk2.red(`\u274C Template n\xE3o encontrado para: ${componentName}`));
361
+ } else if (componentName) {
362
+ if (!COMPONENTS.includes(componentName)) {
363
+ console.error(chalk2.red(`\u274C Componente "${componentName}" n\xE3o encontrado.`));
364
+ console.log(chalk2.gray(`
365
+ Componentes dispon\xEDveis:`));
366
+ console.log(chalk2.gray(` ${COMPONENTS.join(", ")}`));
248
367
  process.exit(1);
249
368
  }
250
- const templateFiles = await glob("**/*", {
251
- cwd: templatePath,
252
- dot: false,
253
- ignore: ["node_modules/**"]
254
- });
255
- for (const file of templateFiles) {
256
- const srcPath = path2.join(templatePath, file);
257
- const destPath = path2.join(componentPath, file);
258
- await fs2.ensureDir(path2.dirname(destPath));
259
- let content = await fs2.readFile(srcPath, "utf-8");
260
- content = content.replace(/{{COMPONENT_NAME}}/g, componentName);
261
- content = content.replace(/{{COMPONENT_CLASS}}/g, toPascalCase(componentName));
262
- await fs2.writeFile(destPath, content);
263
- }
264
- const relativePath = path2.relative(projectRoot, componentPath);
265
- console.log(chalk2.green(`\u2705 Componente ${componentName} adicionado com sucesso!`));
266
- console.log(chalk2.gray(` Localiza\xE7\xE3o: ${relativePath}
369
+ componentsToAdd.push(componentName);
370
+ console.log(chalk2.blue(`\u{1F4E6} Adicionando componente: ${componentName}
267
371
  `));
268
- console.log(chalk2.blue("\u{1F4A1} Pr\xF3ximos passos:"));
269
- console.log(chalk2.gray(` 1. Importe o componente (ex.: no seu m\xF3dulo ou standalone):`));
270
- const importPath = `./${path2.join(basePath, componentName, componentName).replace(/\\/g, "/")}.module`;
271
- console.log(
272
- chalk2.gray(
273
- ` import { Salas${toPascalCase(componentName)}Module } from '${importPath}';`
274
- )
275
- );
276
- console.log(chalk2.gray(` 2. Use no template:`));
277
- console.log(chalk2.gray(` <salas-${componentName}></salas-${componentName}>`));
278
- } catch (error) {
279
- console.error(chalk2.red("\u274C Erro ao adicionar componente:"), error);
372
+ } else {
373
+ console.error(chalk2.red("\u274C Especifique um componente ou use --all."));
374
+ console.log(chalk2.gray(`
375
+ Exemplos:`));
376
+ console.log(chalk2.gray(` npx @salas-ds/cli add button`));
377
+ console.log(chalk2.gray(` npx @salas-ds/cli add --all`));
280
378
  process.exit(1);
281
379
  }
282
- }
283
- function toPascalCase(str) {
284
- return str.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
380
+ const added = [];
381
+ const needsLucide = /* @__PURE__ */ new Set();
382
+ for (const name of componentsToAdd) {
383
+ const success = await addSingleComponent(name, componentsDir, options);
384
+ if (success) {
385
+ added.push(name);
386
+ if (LUCIDE_COMPONENTS.includes(name)) {
387
+ needsLucide.add(name);
388
+ }
389
+ console.log(chalk2.green(` \u2713 ${name}`));
390
+ }
391
+ }
392
+ if (added.length === 0) {
393
+ console.log(chalk2.yellow("\nNenhum componente adicionado."));
394
+ return;
395
+ }
396
+ process.cwd();
397
+ console.log(chalk2.green(`
398
+ \u2705 ${added.length} componente(s) adicionado(s) com sucesso!`));
399
+ console.log(chalk2.gray(` Localiza\xE7\xE3o: ${componentsDir}/
400
+ `));
401
+ if (needsLucide.size > 0) {
402
+ console.log(chalk2.yellow("\u26A0 Os seguintes componentes precisam do lucide-angular:"));
403
+ console.log(chalk2.gray(` ${[...needsLucide].join(", ")}`));
404
+ console.log(chalk2.gray(` Instale com: npm install lucide-angular
405
+ `));
406
+ }
407
+ console.log(chalk2.blue("\u{1F4A1} Como usar:"));
408
+ const example = added[0];
409
+ const exampleClass = `Salas${toPascalCase(example)}Component`;
410
+ const relPath = `./${path2.join(componentsDir, example, example).replace(/\\/g, "/")}`;
411
+ console.log(chalk2.gray(` // Standalone component:`));
412
+ console.log(chalk2.white(` import { ${exampleClass} } from '${relPath}.component';`));
413
+ console.log(chalk2.gray(`
414
+ // Ou via module:`));
415
+ console.log(chalk2.white(` import { Salas${toPascalCase(example)}Module } from '${relPath}.module';`));
285
416
  }
286
417
 
287
418
  // src/index.ts
288
419
  var program = new Command();
289
- program.name("salas-ds").description("CLI para adicionar componentes do Salas Design System").version("0.1.0");
290
- program.command("init").description("Inicializa o Salas Design System no projeto").option("-f, --framework <framework>", "Framework (angular)", "angular").option("-p, --path <path>", "Caminho para componentes", "src/app/ui").action(initCommand);
291
- program.command("add").description("Adiciona um componente ao projeto").argument("<component>", "Nome do componente").option("-p, --path <path>", "Pasta onde o componente ser\xE1 criado (ex: src/components)", "src/components").action(addCommand);
420
+ program.name("salas-ds").description("CLI para adicionar componentes do Salas Design System ao seu projeto Angular (estilo shadcn)").version("0.1.0");
421
+ program.command("init").description("Inicializa o Salas Design System no projeto").option("-f, --framework <framework>", "Framework (angular)", "angular").option("-p, --path <path>", "Caminho para componentes", "src/components/ui").action(initCommand);
422
+ program.command("add [component]").description("Adiciona um componente ao projeto").option("-p, --path <path>", "Pasta onde os componentes ser\xE3o criados").option("-a, --all", "Adiciona todos os componentes").option("--overwrite", "Sobrescreve componentes existentes").action(addCommand);
423
+ program.command("list").description("Lista todos os componentes dispon\xEDveis").action(() => {
424
+ const components = [
425
+ "accordion",
426
+ "autocomplete",
427
+ "avatar",
428
+ "badge",
429
+ "button",
430
+ "card",
431
+ "checkbox",
432
+ "datepicker",
433
+ "dialog",
434
+ "input",
435
+ "scroll-area",
436
+ "select",
437
+ "separator",
438
+ "sheet",
439
+ "sidebar",
440
+ "skeleton",
441
+ "spinner",
442
+ "table",
443
+ "tabs",
444
+ "textarea",
445
+ "toast",
446
+ "tooltip"
447
+ ];
448
+ console.log(chalk2.blue("\n\u{1F4E6} Componentes dispon\xEDveis:\n"));
449
+ for (const c of components) {
450
+ console.log(chalk2.gray(` \u2022 ${c}`));
451
+ }
452
+ console.log(chalk2.gray(`
453
+ Total: ${components.length} componentes`));
454
+ console.log(chalk2.gray(`
455
+ Uso: npx @salas-ds/cli add <componente>`));
456
+ console.log(chalk2.gray(` npx @salas-ds/cli add --all
457
+ `));
458
+ });
292
459
  program.parse();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@salas-ds/cli",
3
- "version": "0.1.0",
4
- "description": "CLI para adicionar componentes do Salas Design System ao projeto",
3
+ "version": "0.2.0",
4
+ "description": "CLI para adicionar componentes do Salas Design System ao projeto (estilo shadcn)",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "salas-ds": "./dist/index.js"
@@ -16,12 +16,10 @@
16
16
  "chalk": "^5.3.0",
17
17
  "fs-extra": "^11.2.0",
18
18
  "glob": "^10.3.10",
19
- "inquirer": "^9.2.12",
20
19
  "prettier": "^3.2.0"
21
20
  },
22
21
  "devDependencies": {
23
22
  "@types/fs-extra": "^11.0.4",
24
- "@types/inquirer": "^9.0.7",
25
23
  "@types/node": "^20.11.0",
26
24
  "tsup": "^8.0.0",
27
25
  "typescript": "~5.5.0"
@@ -35,7 +33,8 @@
35
33
  "author": "",
36
34
  "license": "MIT",
37
35
  "scripts": {
38
- "build": "tsup",
36
+ "generate-templates": "node scripts/generate-templates.mjs",
37
+ "build": "node scripts/generate-templates.mjs && tsup",
39
38
  "dev": "tsup --watch",
40
39
  "clean": "rm -rf dist"
41
40
  }
@@ -0,0 +1,9 @@
1
+ import { Component } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'salas-accordion-content',
5
+ standalone: true,
6
+ template: '<ng-content></ng-content>',
7
+ styles: [`:host { display: contents; }`],
8
+ })
9
+ export class SalasAccordionContentComponent {}
@@ -0,0 +1,138 @@
1
+ import { Component, Input, Output, EventEmitter, ViewEncapsulation } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'salas-accordion-item',
5
+ standalone: true,
6
+ imports: [],
7
+ encapsulation: ViewEncapsulation.None,
8
+ template: `
9
+ <div
10
+ class="salas-accordion-item"
11
+ [class.salas-accordion-item--disabled]="disabled"
12
+ >
13
+ <button
14
+ type="button"
15
+ class="salas-accordion-trigger"
16
+ [class.salas-accordion-trigger--open]="open"
17
+ [disabled]="disabled"
18
+ (click)="onTriggerClick()"
19
+ [attr.aria-expanded]="open"
20
+ >
21
+ <ng-content select="salas-accordion-trigger"></ng-content>
22
+ <svg
23
+ xmlns="http://www.w3.org/2000/svg"
24
+ width="16"
25
+ height="16"
26
+ viewBox="0 0 24 24"
27
+ fill="none"
28
+ stroke="currentColor"
29
+ stroke-width="2"
30
+ stroke-linecap="round"
31
+ stroke-linejoin="round"
32
+ class="salas-accordion-trigger-icon"
33
+ [class.salas-accordion-trigger-icon--open]="open"
34
+ >
35
+ <path d="m6 9 6 6 6-6"/>
36
+ </svg>
37
+ </button>
38
+ <div
39
+ class="salas-accordion-content"
40
+ [class.salas-accordion-content--open]="open"
41
+ [attr.aria-hidden]="!open"
42
+ >
43
+ <div class="salas-accordion-content-inner">
44
+ <ng-content select="salas-accordion-content"></ng-content>
45
+ </div>
46
+ </div>
47
+ </div>
48
+ `,
49
+ styles: [`
50
+ .salas-accordion-item {
51
+ border-bottom: 1px solid var(--salas-gray-200);
52
+ }
53
+
54
+ .salas-accordion-item:last-child {
55
+ border-bottom: none;
56
+ }
57
+
58
+ .salas-accordion-item--disabled {
59
+ opacity: 0.5;
60
+ pointer-events: none;
61
+ }
62
+
63
+ :host-context([data-theme="dark"]) .salas-accordion-item {
64
+ border-bottom-color: var(--salas-gray-800);
65
+ }
66
+
67
+ .salas-accordion-trigger {
68
+ display: flex;
69
+ align-items: center;
70
+ justify-content: space-between;
71
+ width: 100%;
72
+ padding: 1rem 0;
73
+ font-size: 0.875rem;
74
+ font-weight: 500;
75
+ color: var(--salas-text);
76
+ background: none;
77
+ border: none;
78
+ cursor: pointer;
79
+ transition: color 0.2s;
80
+ text-align: left;
81
+ font-family: inherit;
82
+ }
83
+
84
+ .salas-accordion-trigger:hover:not(:disabled) {
85
+ text-decoration: underline;
86
+ }
87
+
88
+ .salas-accordion-trigger:disabled {
89
+ cursor: not-allowed;
90
+ opacity: 0.5;
91
+ }
92
+
93
+ .salas-accordion-trigger-icon {
94
+ flex-shrink: 0;
95
+ width: 1rem;
96
+ height: 1rem;
97
+ transition: transform 0.2s ease;
98
+ color: var(--salas-text-muted);
99
+ }
100
+
101
+ .salas-accordion-trigger-icon--open {
102
+ transform: rotate(180deg);
103
+ }
104
+
105
+ .salas-accordion-content {
106
+ overflow: hidden;
107
+ max-height: 0;
108
+ opacity: 0;
109
+ transition: max-height 0.3s ease, opacity 0.2s ease;
110
+ }
111
+
112
+ .salas-accordion-content--open {
113
+ max-height: 500px;
114
+ opacity: 1;
115
+ }
116
+
117
+ .salas-accordion-content-inner {
118
+ padding-bottom: 1rem;
119
+ color: var(--salas-text-muted);
120
+ font-size: 0.875rem;
121
+ line-height: 1.5;
122
+ }
123
+ `],
124
+ })
125
+ export class SalasAccordionItemComponent {
126
+ @Input() value!: string;
127
+ @Input() disabled = false;
128
+
129
+ open = false;
130
+
131
+ @Output() toggled = new EventEmitter<string>();
132
+
133
+ onTriggerClick(): void {
134
+ if (!this.disabled) {
135
+ this.toggled.emit(this.value);
136
+ }
137
+ }
138
+ }
@@ -0,0 +1,9 @@
1
+ import { Component } from '@angular/core';
2
+
3
+ @Component({
4
+ selector: 'salas-accordion-trigger',
5
+ standalone: true,
6
+ template: '<ng-content></ng-content>',
7
+ styles: [`:host { display: contents; }`],
8
+ })
9
+ export class SalasAccordionTriggerComponent {}
@@ -0,0 +1,120 @@
1
+ import {
2
+ Component,
3
+ Input,
4
+ Output,
5
+ EventEmitter,
6
+ ContentChildren,
7
+ QueryList,
8
+ AfterContentInit,
9
+ OnDestroy,
10
+ ViewEncapsulation,
11
+ } from '@angular/core';
12
+ import { SalasAccordionItemComponent } from './accordion-item.component';
13
+
14
+ export type AccordionType = 'single' | 'multiple';
15
+
16
+ export interface AccordionProps {
17
+ type?: AccordionType;
18
+ collapsible?: boolean;
19
+ defaultValue?: string | string[];
20
+ value?: string | string[];
21
+ disabled?: boolean;
22
+ }
23
+
24
+ export interface AccordionItemProps {
25
+ value: string;
26
+ disabled?: boolean;
27
+ }
28
+
29
+
30
+ @Component({
31
+ selector: 'salas-accordion',
32
+ standalone: true,
33
+ imports: [],
34
+ encapsulation: ViewEncapsulation.None,
35
+ template: `
36
+ <div class="salas-accordion">
37
+ <ng-content></ng-content>
38
+ </div>
39
+ `,
40
+ styles: [`
41
+ .salas-accordion {
42
+ display: flex;
43
+ flex-direction: column;
44
+ }
45
+ `],
46
+ })
47
+ export class SalasAccordionComponent implements AfterContentInit, OnDestroy {
48
+ @Input() type: AccordionType = 'single';
49
+ @Input() collapsible = true;
50
+ @Input() defaultValue?: string | string[];
51
+ @Input() disabled = false;
52
+
53
+ @Output() valueChange = new EventEmitter<string | string[] | undefined>();
54
+
55
+ @ContentChildren(SalasAccordionItemComponent) items!: QueryList<SalasAccordionItemComponent>;
56
+
57
+ private unsubscribes: Array<() => void> = [];
58
+
59
+ ngAfterContentInit(): void {
60
+ this.initItems();
61
+
62
+ this.items.changes.subscribe(() => {
63
+ this.clearSubscriptions();
64
+ this.initItems();
65
+ });
66
+ }
67
+
68
+ ngOnDestroy(): void {
69
+ this.clearSubscriptions();
70
+ }
71
+
72
+ private initItems(): void {
73
+ if (this.defaultValue) {
74
+ const defaults = Array.isArray(this.defaultValue)
75
+ ? this.defaultValue
76
+ : [this.defaultValue];
77
+ this.items.forEach((item) => {
78
+ item.open = defaults.includes(item.value);
79
+ });
80
+ }
81
+
82
+ this.items.forEach((item) => {
83
+ const sub = item.toggled.subscribe((value: string) => {
84
+ this.handleToggle(item);
85
+ });
86
+ this.unsubscribes.push(() => sub.unsubscribe());
87
+ });
88
+ }
89
+
90
+ private handleToggle(toggledItem: SalasAccordionItemComponent): void {
91
+ if (this.type === 'single') {
92
+ const wasOpen = toggledItem.open;
93
+ this.items.forEach((item) => {
94
+ item.open = false;
95
+ });
96
+ toggledItem.open = this.collapsible ? !wasOpen : true;
97
+ } else {
98
+ toggledItem.open = !toggledItem.open;
99
+ }
100
+
101
+ this.emitValue();
102
+ }
103
+
104
+ private emitValue(): void {
105
+ const openItems = this.items
106
+ .filter((item) => item.open)
107
+ .map((item) => item.value);
108
+
109
+ if (this.type === 'single') {
110
+ this.valueChange.emit(openItems[0] ?? undefined);
111
+ } else {
112
+ this.valueChange.emit(openItems.length > 0 ? openItems : undefined);
113
+ }
114
+ }
115
+
116
+ private clearSubscriptions(): void {
117
+ this.unsubscribes.forEach((unsub) => unsub());
118
+ this.unsubscribes = [];
119
+ }
120
+ }