@mimi-ng/cli 0.3.0 → 0.5.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 (50) hide show
  1. package/README.md +42 -1
  2. package/dist/init/css.js +1 -1
  3. package/dist/init/index.js +33 -0
  4. package/dist/init/paths.js +20 -0
  5. package/dist/registry.js +4 -1
  6. package/dist/registry.json +198 -1
  7. package/dist/templates/blocks/login-01/index.ts +1 -0
  8. package/dist/templates/blocks/login-01/login-01.html +174 -0
  9. package/dist/templates/blocks/login-01/login-01.ts +114 -0
  10. package/dist/templates/blocks/login-02/index.ts +1 -0
  11. package/dist/templates/blocks/login-02/login-02.html +186 -0
  12. package/dist/templates/blocks/login-02/login-02.ts +101 -0
  13. package/dist/templates/blocks/otp-01/index.ts +1 -0
  14. package/dist/templates/blocks/otp-01/otp-01.html +166 -0
  15. package/dist/templates/blocks/otp-01/otp-01.ts +221 -0
  16. package/dist/templates/blocks/pricing-01/index.ts +1 -0
  17. package/dist/templates/blocks/pricing-01/pricing-01.html +173 -0
  18. package/dist/templates/blocks/pricing-01/pricing-01.ts +178 -0
  19. package/dist/templates/blocks/settings-01/index.ts +1 -0
  20. package/dist/templates/blocks/settings-01/settings-01.html +424 -0
  21. package/dist/templates/blocks/settings-01/settings-01.ts +392 -0
  22. package/dist/templates/blocks/signup-01/index.ts +1 -0
  23. package/dist/templates/blocks/signup-01/signup-01.html +332 -0
  24. package/dist/templates/blocks/signup-01/signup-01.ts +186 -0
  25. package/dist/templates/blocks/table-01/index.ts +1 -0
  26. package/dist/templates/blocks/table-01/table-01.html +214 -0
  27. package/dist/templates/blocks/table-01/table-01.ts +266 -0
  28. package/dist/templates/components/checkbox/checkbox.ts +14 -5
  29. package/dist/templates/components/command/command-texts.ts +65 -0
  30. package/dist/templates/components/command/command.ts +553 -0
  31. package/dist/templates/components/command/index.ts +13 -0
  32. package/dist/templates/components/data-table/data-table-texts.ts +110 -0
  33. package/dist/templates/components/data-table/data-table.ts +1153 -0
  34. package/dist/templates/components/data-table/index.ts +18 -0
  35. package/dist/templates/components/sortable-list/index.ts +9 -0
  36. package/dist/templates/components/sortable-list/sortable-list-texts.ts +71 -0
  37. package/dist/templates/components/sortable-list/sortable-list.ts +510 -0
  38. package/dist/templates/components/tabs/tabs.ts +88 -23
  39. package/dist/templates/components/tree/index.ts +9 -0
  40. package/dist/templates/components/tree/tree-texts.ts +45 -0
  41. package/dist/templates/components/tree/tree.ts +730 -0
  42. package/dist/templates/theme/provider.ts +4 -0
  43. package/dist/templates/theme/types.ts +84 -0
  44. package/dist/ui/index.js +323 -13
  45. package/dist/ui/schema.json +50 -2
  46. package/dist/utils/pages.js +138 -0
  47. package/dist/utils/project.js +59 -0
  48. package/dist/utils/tailwind.js +84 -0
  49. package/dist/utils/write-files.js +63 -8
  50. package/package.json +1 -1
@@ -0,0 +1,424 @@
1
+ <header class="flex flex-col gap-1 border-b px-4 pt-5 pb-3.5 md:px-10 md:pt-7 md:pb-5">
2
+ <h1 class="text-2xl font-extrabold tracking-[-0.025em] md:text-[28px]">Settings</h1>
3
+ <p class="text-sm text-muted-foreground">Manage your profile, notifications and security.</p>
4
+ </header>
5
+
6
+ <div class="flex-1 md:px-10 md:pt-7 md:pb-10">
7
+ <!-- Una sola lista de pestañas: vertical en escritorio, píldoras horizontales en móvil (B.10). -->
8
+ <mimi-tabs
9
+ aria-label="Settings sections"
10
+ [orientation]="wide() ? 'vertical' : 'horizontal'"
11
+ [class]="wide() ? sideNavClasses : pillNavClasses"
12
+ [panelClass]="panelClasses"
13
+ [style.gap.px]="wide() ? 40 : 0"
14
+ [value]="tab()"
15
+ (valueChange)="selectTab($event)"
16
+ >
17
+ @for (item of tabs; track item.value) {
18
+ <ng-template [mimiTabLabel]="item.value">
19
+ <svg
20
+ xmlns="http://www.w3.org/2000/svg"
21
+ width="17"
22
+ height="17"
23
+ viewBox="0 0 24 24"
24
+ fill="none"
25
+ stroke="currentColor"
26
+ stroke-width="2"
27
+ stroke-linecap="round"
28
+ stroke-linejoin="round"
29
+ aria-hidden="true"
30
+ >
31
+ @switch (item.value) {
32
+ @case ('profile') {
33
+ <!-- Lucide user-round (ISC) -->
34
+ <circle cx="12" cy="8" r="5" />
35
+ <path d="M20 21a8 8 0 0 0-16 0" />
36
+ }
37
+ @case ('notifications') {
38
+ <!-- Lucide bell (ISC) -->
39
+ <path d="M10.268 21a2 2 0 0 0 3.464 0" />
40
+ <path
41
+ d="M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326"
42
+ />
43
+ }
44
+ @case ('security') {
45
+ <!-- Lucide shield (ISC) -->
46
+ <path
47
+ d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"
48
+ />
49
+ }
50
+ @case ('billing') {
51
+ <!-- Lucide credit-card (ISC) -->
52
+ <rect width="20" height="14" x="2" y="5" rx="2" />
53
+ <line x1="2" x2="22" y1="10" y2="10" />
54
+ }
55
+ }
56
+ </svg>
57
+ {{ item.label }}
58
+ </ng-template>
59
+ }
60
+
61
+ <!-- Perfil -->
62
+ <ng-template mimiTab="profile" label="Profile">
63
+ <mimi-card>
64
+ <div class="flex flex-col gap-0.5 px-5 pt-5 pb-1">
65
+ <h2 class="text-base font-bold">Public profile</h2>
66
+ <p class="text-[13.5px] text-muted-foreground">Visible to people on your team.</p>
67
+ </div>
68
+ <div class="flex flex-col gap-4 px-5 py-4">
69
+ <div class="flex flex-wrap items-center gap-3.5">
70
+ <mimi-avatar size="lg">
71
+ @if (profile().avatarSrc; as src) {
72
+ <img mimiAvatarImage [src]="src" [alt]="model().name" />
73
+ }
74
+ <mimi-avatar-fallback class="bg-info-soft font-bold text-info-soft-foreground">{{
75
+ profile().initials
76
+ }}</mimi-avatar-fallback>
77
+ </mimi-avatar>
78
+ <button
79
+ mimiBtn
80
+ type="button"
81
+ variant="outline"
82
+ tone="secondary"
83
+ size="sm"
84
+ (click)="avatarChange.emit()"
85
+ >
86
+ <svg
87
+ xmlns="http://www.w3.org/2000/svg"
88
+ width="14"
89
+ height="14"
90
+ viewBox="0 0 24 24"
91
+ fill="none"
92
+ stroke="currentColor"
93
+ stroke-width="2"
94
+ stroke-linecap="round"
95
+ stroke-linejoin="round"
96
+ aria-hidden="true"
97
+ >
98
+ <!-- Lucide upload (ISC) -->
99
+ <path d="M12 3v12" />
100
+ <path d="m17 8-5-5-5 5" />
101
+ <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
102
+ </svg>
103
+ Change photo
104
+ </button>
105
+ <button
106
+ mimiBtn
107
+ type="button"
108
+ variant="ghost"
109
+ tone="danger"
110
+ size="sm"
111
+ (click)="avatarRemove.emit()"
112
+ >
113
+ Remove
114
+ </button>
115
+ </div>
116
+
117
+ <div class="grid grid-cols-1 gap-3.5 md:grid-cols-2">
118
+ <mimi-form-field label="Name">
119
+ <input mimiInput autocomplete="name" [formField]="settings.name" />
120
+ </mimi-form-field>
121
+ <mimi-form-field>
122
+ <label [attr.for]="id + '-username'" class="text-sm font-medium">Username</label>
123
+ <!-- Prefijo fijo con los colores de Input (no hay un grupo de input con prefijo). -->
124
+ <div
125
+ class="flex h-[var(--mimi-control-height,2.5rem)] items-center overflow-hidden rounded-[var(--mimi-radius)] border border-input bg-input-background mimi-transition focus-within:border-ring focus-within:ring-3 focus-within:ring-ring-soft has-[[aria-invalid=true]]:border-destructive"
126
+ >
127
+ <span
128
+ aria-hidden="true"
129
+ class="flex h-full items-center border-e border-input bg-muted px-2.5 text-[13px] text-muted-foreground"
130
+ >example.com/</span
131
+ >
132
+ <input
133
+ mimiInput
134
+ autocomplete="username"
135
+ class="h-full min-w-0 flex-1 rounded-none border-0 bg-transparent px-2.5 shadow-none focus-visible:ring-0"
136
+ [id]="id + '-username'"
137
+ [formField]="settings.username"
138
+ />
139
+ </div>
140
+ </mimi-form-field>
141
+ </div>
142
+
143
+ <mimi-form-field class="relative">
144
+ <label [attr.for]="id + '-bio'" class="text-sm font-medium">Bio</label>
145
+ <!-- El contador describe el campo y no se anuncia con cada tecla (no es una región viva). -->
146
+ <span
147
+ [id]="bioId"
148
+ class="absolute top-0 right-0 text-xs text-muted-foreground tabular-nums"
149
+ >{{ model().bio.length }}/160</span
150
+ >
151
+ <textarea
152
+ mimiTextarea
153
+ rows="3"
154
+ [id]="id + '-bio'"
155
+ [attr.aria-describedby]="bioId"
156
+ [formField]="settings.bio"
157
+ ></textarea>
158
+ </mimi-form-field>
159
+ </div>
160
+ </mimi-card>
161
+
162
+ <mimi-card>
163
+ <h2 class="px-5 pt-5 pb-2 text-base font-bold">Preferences</h2>
164
+ @for (row of profileSwitches; track row.key) {
165
+ <!-- Una fila con interruptor: el título lo nombra y la descripción lo describe. -->
166
+ <div class="flex items-center justify-between gap-4 border-t px-5 py-3.5">
167
+ <span class="flex flex-col leading-[1.4]">
168
+ <label [attr.for]="id + '-' + row.key" class="cursor-pointer text-sm font-medium">{{
169
+ row.label
170
+ }}</label>
171
+ <span [id]="id + '-' + row.key + '-desc'" class="text-[13px] text-muted-foreground">{{
172
+ row.description
173
+ }}</span>
174
+ </span>
175
+ <mimi-switch
176
+ [id]="id + '-' + row.key"
177
+ [attr.data-describedby]="id + '-' + row.key + '-desc'"
178
+ [formField]="field(row.key)"
179
+ />
180
+ </div>
181
+ }
182
+ </mimi-card>
183
+ </ng-template>
184
+
185
+ <!-- Notificaciones -->
186
+ <ng-template mimiTab="notifications" label="Notifications">
187
+ @for (group of notificationGroups; track group.title) {
188
+ <mimi-card>
189
+ <div class="flex items-center gap-2.5 px-5 pt-[18px] pb-2.5">
190
+ <svg
191
+ xmlns="http://www.w3.org/2000/svg"
192
+ width="17"
193
+ height="17"
194
+ viewBox="0 0 24 24"
195
+ fill="none"
196
+ stroke="currentColor"
197
+ stroke-width="2"
198
+ stroke-linecap="round"
199
+ stroke-linejoin="round"
200
+ aria-hidden="true"
201
+ class="text-muted-foreground"
202
+ >
203
+ @if (group.icon === 'mail') {
204
+ <!-- Lucide mail (ISC) -->
205
+ <path d="m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7" />
206
+ <rect x="2" y="4" width="20" height="16" rx="2" />
207
+ } @else {
208
+ <!-- Lucide smartphone (ISC) -->
209
+ <rect width="14" height="20" x="5" y="2" rx="2" ry="2" />
210
+ <path d="M12 18h.01" />
211
+ }
212
+ </svg>
213
+ <h2 class="text-base font-bold">{{ group.title }}</h2>
214
+ </div>
215
+ @for (row of group.rows; track row.key) {
216
+ <div class="flex items-center justify-between gap-4 border-t px-5 py-3.5">
217
+ <span class="flex flex-col leading-[1.4]">
218
+ <label [attr.for]="id + '-' + row.key" class="cursor-pointer text-sm font-medium">{{
219
+ row.label
220
+ }}</label>
221
+ <span
222
+ [id]="id + '-' + row.key + '-desc'"
223
+ class="text-[13px] text-muted-foreground"
224
+ >{{ row.description }}</span
225
+ >
226
+ </span>
227
+ <mimi-switch
228
+ [id]="id + '-' + row.key"
229
+ [attr.data-describedby]="id + '-' + row.key + '-desc'"
230
+ [formField]="field(row.key)"
231
+ />
232
+ </div>
233
+ }
234
+ </mimi-card>
235
+ }
236
+ </ng-template>
237
+
238
+ <!-- Seguridad -->
239
+ <ng-template mimiTab="security" label="Security">
240
+ <mimi-card class="gap-3.5 p-5">
241
+ <div class="flex items-center justify-between gap-3">
242
+ <div class="flex flex-col gap-0.5">
243
+ <h2 class="flex flex-wrap items-center gap-2 text-base font-bold">
244
+ <label [attr.for]="id + '-twoFactor'" class="cursor-pointer"
245
+ >Two-step verification</label
246
+ >
247
+ <span mimiBadge variant="soft" tone="success">Recommended</span>
248
+ </h2>
249
+ <p [id]="id + '-twoFactor-desc'" class="text-[13.5px] text-muted-foreground">
250
+ Ask for a code from your app when signing in.
251
+ </p>
252
+ </div>
253
+ <mimi-switch
254
+ [id]="id + '-twoFactor'"
255
+ [attr.data-describedby]="id + '-twoFactor-desc'"
256
+ [formField]="settings.twoFactor"
257
+ />
258
+ </div>
259
+ <mimi-separator />
260
+ <h3 class="text-sm font-bold">Active sessions</h3>
261
+ <ul class="flex flex-col gap-3">
262
+ @for (session of sessions(); track session.id) {
263
+ <li class="flex items-center gap-3">
264
+ <span
265
+ aria-hidden="true"
266
+ class="inline-flex size-9 shrink-0 items-center justify-center rounded-[var(--mimi-radius-sm)] bg-muted"
267
+ >
268
+ <svg
269
+ xmlns="http://www.w3.org/2000/svg"
270
+ width="17"
271
+ height="17"
272
+ viewBox="0 0 24 24"
273
+ fill="none"
274
+ stroke="currentColor"
275
+ stroke-width="2"
276
+ stroke-linecap="round"
277
+ stroke-linejoin="round"
278
+ >
279
+ @if (session.kind === 'mobile') {
280
+ <!-- Lucide smartphone (ISC) -->
281
+ <rect width="14" height="20" x="5" y="2" rx="2" ry="2" />
282
+ <path d="M12 18h.01" />
283
+ } @else {
284
+ <!-- Lucide monitor (ISC) -->
285
+ <rect width="20" height="14" x="2" y="3" rx="2" />
286
+ <line x1="8" x2="16" y1="21" y2="21" />
287
+ <line x1="12" x2="12" y1="17" y2="21" />
288
+ }
289
+ </svg>
290
+ </span>
291
+ <span class="flex min-w-0 flex-1 flex-col leading-[1.35]">
292
+ <span class="text-sm font-medium">{{ session.device }}</span>
293
+ <span class="text-[12.5px] text-muted-foreground">{{ session.detail }}</span>
294
+ </span>
295
+ @if (session.current) {
296
+ <span mimiBadge variant="soft" tone="info">This session</span>
297
+ } @else {
298
+ <button
299
+ mimiBtn
300
+ type="button"
301
+ variant="outline"
302
+ tone="secondary"
303
+ size="sm"
304
+ [attr.aria-label]="'Sign out of ' + session.device"
305
+ (click)="sessionEnd.emit(session.id)"
306
+ >
307
+ Sign out
308
+ </button>
309
+ }
310
+ </li>
311
+ }
312
+ </ul>
313
+ </mimi-card>
314
+
315
+ <section
316
+ [attr.aria-labelledby]="id + '-danger'"
317
+ class="flex flex-wrap items-center justify-between gap-4 rounded-card border border-[color:color-mix(in_oklab,var(--mimi-destructive)_40%,var(--mimi-border))] bg-destructive-soft-bg p-5"
318
+ >
319
+ <div class="flex flex-col gap-0.5 text-destructive-soft-foreground">
320
+ <h2 [id]="id + '-danger'" class="text-base font-bold">Delete account</h2>
321
+ <p class="text-[13.5px]">Your data is deleted after 30 days. This can't be undone.</p>
322
+ </div>
323
+ <button mimiBtn type="button" tone="danger" size="sm" (click)="deleteAccount.emit()">
324
+ <svg
325
+ xmlns="http://www.w3.org/2000/svg"
326
+ width="15"
327
+ height="15"
328
+ viewBox="0 0 24 24"
329
+ fill="none"
330
+ stroke="currentColor"
331
+ stroke-width="2"
332
+ stroke-linecap="round"
333
+ stroke-linejoin="round"
334
+ aria-hidden="true"
335
+ >
336
+ <!-- Lucide trash (ISC) -->
337
+ <path d="M10 11v6" />
338
+ <path d="M14 11v6" />
339
+ <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" />
340
+ <path d="M3 6h18" />
341
+ <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
342
+ </svg>
343
+ Delete account
344
+ </button>
345
+ </section>
346
+ </ng-template>
347
+
348
+ <!-- Facturación -->
349
+ <ng-template mimiTab="billing" label="Billing">
350
+ <mimi-card class="gap-3.5 p-5">
351
+ <div class="flex flex-wrap items-center justify-between gap-3">
352
+ <div class="flex flex-col gap-0.5">
353
+ <h2 class="text-[13px] font-normal text-muted-foreground">Current plan</h2>
354
+ <span class="text-[22px] font-extrabold">{{ price() }}</span>
355
+ </div>
356
+ <button mimiBtn type="button" size="sm" (click)="changePlan.emit()">Change plan</button>
357
+ </div>
358
+ <div class="flex flex-col gap-1.5">
359
+ <div class="flex justify-between text-[13px]">
360
+ <span [id]="id + '-seats'">Seats used</span>
361
+ <span class="text-muted-foreground">{{ plan().seatsUsed }} of {{ plan().seats }}</span>
362
+ </div>
363
+ <mimi-progress
364
+ [aria-labelledby]="id + '-seats'"
365
+ [value]="plan().seatsUsed"
366
+ [max]="plan().seats"
367
+ />
368
+ </div>
369
+ <span class="flex items-center gap-2 text-[13.5px] text-muted-foreground">
370
+ <svg
371
+ xmlns="http://www.w3.org/2000/svg"
372
+ width="15"
373
+ height="15"
374
+ viewBox="0 0 24 24"
375
+ fill="none"
376
+ stroke="currentColor"
377
+ stroke-width="2"
378
+ stroke-linecap="round"
379
+ stroke-linejoin="round"
380
+ aria-hidden="true"
381
+ >
382
+ <!-- Lucide credit-card (ISC) -->
383
+ <rect width="20" height="14" x="2" y="5" rx="2" />
384
+ <line x1="2" x2="22" y1="10" y2="10" />
385
+ </svg>
386
+ {{ plan().card }} · next invoice on {{ nextInvoice() }}
387
+ </span>
388
+ </mimi-card>
389
+ </ng-template>
390
+ </mimi-tabs>
391
+ </div>
392
+
393
+ <!-- La barra de cambios: la región existe siempre, así el lector la anuncia al aparecer. -->
394
+ <div role="status" class="contents">
395
+ @if (changed()) {
396
+ <div
397
+ class="sticky bottom-0 flex items-center justify-between gap-3 border-t border-glass-border bg-glass px-5 py-3 backdrop-blur-glass [@media(prefers-reduced-transparency:reduce)]:bg-background"
398
+ >
399
+ <span class="text-[13.5px] font-medium">You have unsaved changes</span>
400
+ <div class="flex gap-2">
401
+ <button
402
+ mimiBtn
403
+ type="button"
404
+ variant="outline"
405
+ tone="secondary"
406
+ size="sm"
407
+ (click)="discard()"
408
+ >
409
+ Discard
410
+ </button>
411
+ <button
412
+ mimiBtn
413
+ type="button"
414
+ size="sm"
415
+ [attr.aria-disabled]="saving() ? 'true' : null"
416
+ [attr.aria-busy]="saving() ? 'true' : null"
417
+ (click)="save()"
418
+ >
419
+ {{ saving() ? 'Saving…' : 'Save' }}
420
+ </button>
421
+ </div>
422
+ </div>
423
+ }
424
+ </div>