@mimi-ng/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.
- package/README.md +19 -1
- package/dist/collection.json +1 -1
- package/dist/init/css.js +17 -0
- package/dist/init/index.js +7 -5
- package/dist/init/items.js +9 -0
- package/dist/ng-add/index.js +17 -1
- package/dist/ng-add/schema.json +27 -0
- package/dist/registry.json +222 -1
- package/dist/templates/components/avatar/avatar.ts +1 -1
- package/dist/templates/components/avatar/avatar.variants.ts +4 -4
- package/dist/templates/components/badge/badge.ts +41 -6
- package/dist/templates/components/badge/badge.variants.ts +105 -11
- package/dist/templates/components/button/button.ts +74 -7
- package/dist/templates/components/button/button.variants.ts +145 -21
- package/dist/templates/components/card/card.ts +8 -5
- package/dist/templates/components/checkbox/checkbox.ts +3 -3
- package/dist/templates/components/confirm/confirm-texts.ts +46 -0
- package/dist/templates/components/confirm/confirm.ts +190 -0
- package/dist/templates/components/confirm/index.ts +2 -0
- package/dist/templates/components/context-menu/context-menu.ts +378 -0
- package/dist/templates/components/context-menu/index.ts +14 -0
- package/dist/templates/components/dialog/dialog-texts.ts +41 -0
- package/dist/templates/components/dialog/dialog.ts +701 -0
- package/dist/templates/components/dialog/index.ts +18 -0
- package/dist/templates/components/dropdown-menu/dropdown-menu.ts +252 -0
- package/dist/templates/components/dropdown-menu/index.ts +14 -0
- package/dist/templates/components/form-field/form-error.ts +1 -1
- package/dist/templates/components/form-field/form-field.ts +3 -2
- package/dist/templates/components/input/input.variants.ts +6 -4
- package/dist/templates/components/menu/index.ts +15 -0
- package/dist/templates/components/menu/menu.ts +656 -0
- package/dist/templates/components/pagination/index.ts +2 -0
- package/dist/templates/components/pagination/pagination-page-size.ts +74 -0
- package/dist/templates/components/pagination/pagination-texts.ts +69 -0
- package/dist/templates/components/pagination/pagination.ts +443 -0
- package/dist/templates/components/popover/index.ts +12 -0
- package/dist/templates/components/popover/popover.ts +399 -0
- package/dist/templates/components/select/index.ts +12 -0
- package/dist/templates/components/select/select-texts.ts +62 -0
- package/dist/templates/components/select/select.ts +1085 -0
- package/dist/templates/components/separator/separator.variants.ts +10 -7
- package/dist/templates/components/skeleton/skeleton.ts +4 -1
- package/dist/templates/components/switch/switch.ts +6 -6
- package/dist/templates/components/tabs/index.ts +2 -0
- package/dist/templates/components/tabs/tabs.ts +426 -0
- package/dist/templates/components/textarea/textarea.variants.ts +14 -11
- package/dist/templates/components/toast/index.ts +3 -0
- package/dist/templates/components/toast/toast-config.ts +45 -0
- package/dist/templates/components/toast/toast-texts.ts +46 -0
- package/dist/templates/components/toast/toast.ts +1000 -0
- package/dist/templates/components/toolbar/index.ts +1 -0
- package/dist/templates/components/toolbar/toolbar.ts +377 -0
- package/dist/templates/components/tooltip/index.ts +7 -0
- package/dist/templates/components/tooltip/tooltip.ts +407 -0
- package/dist/templates/theme/provider.ts +62 -6
- package/dist/templates/theme/theme-base.css +307 -9
- package/dist/templates/theme/types.ts +278 -4
- package/dist/templates/utils/cn.ts +15 -1
- package/dist/templates/utils/control-styles.ts +2 -2
- package/dist/templates/utils/overlay.ts +140 -0
- package/dist/ui/index.js +80 -8
- package/dist/ui/schema.json +44 -0
- package/dist/utils/write-files.js +156 -59
- package/package.json +6 -5
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Solo la API pública: MimiDialogShell y MimiDialogOutlet son internos (se quedan en './dialog').
|
|
2
|
+
export {
|
|
3
|
+
MIMI_DIALOG_DATA,
|
|
4
|
+
MimiDialog,
|
|
5
|
+
MimiDialogBody,
|
|
6
|
+
MimiDialogClose,
|
|
7
|
+
type MimiDialogConfig,
|
|
8
|
+
MimiDialogContent,
|
|
9
|
+
MimiDialogDescription,
|
|
10
|
+
MimiDialogFooter,
|
|
11
|
+
MimiDialogHeader,
|
|
12
|
+
MimiDialogImports,
|
|
13
|
+
MimiDialogRef,
|
|
14
|
+
MimiDialogService,
|
|
15
|
+
MimiDialogTitle,
|
|
16
|
+
MimiDialogTrigger,
|
|
17
|
+
} from './dialog';
|
|
18
|
+
export * from './dialog-texts';
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { CdkConnectedOverlay, type ConnectedOverlayPositionChange } from '@angular/cdk/overlay';
|
|
2
|
+
import {
|
|
3
|
+
ChangeDetectionStrategy,
|
|
4
|
+
Component,
|
|
5
|
+
DOCUMENT,
|
|
6
|
+
Directive,
|
|
7
|
+
ElementRef,
|
|
8
|
+
HostAttributeToken,
|
|
9
|
+
Injector,
|
|
10
|
+
TemplateRef,
|
|
11
|
+
afterNextRender,
|
|
12
|
+
computed,
|
|
13
|
+
contentChild,
|
|
14
|
+
effect,
|
|
15
|
+
inject,
|
|
16
|
+
input,
|
|
17
|
+
model,
|
|
18
|
+
signal,
|
|
19
|
+
untracked,
|
|
20
|
+
viewChild,
|
|
21
|
+
} from '@angular/core';
|
|
22
|
+
import { MimiMenuItemsImports } from '@/components/ui/menu';
|
|
23
|
+
import { MimiMenuPanel } from '@/components/ui/menu/menu';
|
|
24
|
+
import {
|
|
25
|
+
MIMI_VIEWPORT_MARGIN,
|
|
26
|
+
type MimiOverlayAlign,
|
|
27
|
+
type MimiOverlaySide,
|
|
28
|
+
mimiConnectedPositions,
|
|
29
|
+
mimiPlacedSide,
|
|
30
|
+
mimiWaitForExit,
|
|
31
|
+
} from '@/components/ui/utils/overlay';
|
|
32
|
+
|
|
33
|
+
/*
|
|
34
|
+
* Dropdown Menu (docs/design/Mimi F2 Overlays.dc.html, «5 · Dropdown Menu»): panel a 8px del
|
|
35
|
+
* trigger. Ítems, panel y tokens (`menu`, spec 13) en `@/components/ui/menu`.
|
|
36
|
+
*
|
|
37
|
+
* Por dentro: @angular/cdk/overlay posiciona el panel (popover nativo 'inline', como Popover) y
|
|
38
|
+
* @angular/aria/menu da el teclado del patrón de menú (en MimiMenuPanel). El trigger es de Mimi:
|
|
39
|
+
* ngMenuTrigger necesita una referencia al menú en el botón del usuario, que no se puede dar sin
|
|
40
|
+
* exponer aria (decisión 4.3). Nada de aria ni del CDK sale en la API pública.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
export type MimiDropdownMenuSide = MimiOverlaySide;
|
|
44
|
+
export type MimiDropdownMenuAlign = MimiOverlayAlign;
|
|
45
|
+
|
|
46
|
+
/** Distancia entre el trigger y el panel (diseño: 8px). */
|
|
47
|
+
const OFFSET = 8;
|
|
48
|
+
|
|
49
|
+
let nextId = 0;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Menú desplegable: un trigger (`[mimiDropdownMenuTrigger]`) y el contenido en un
|
|
53
|
+
* `<ng-template mimiDropdownMenuContent>`. No dibuja nada propio (`display: contents`): su
|
|
54
|
+
* `class` y su `aria-label` van al panel.
|
|
55
|
+
*/
|
|
56
|
+
@Component({
|
|
57
|
+
selector: 'mimi-dropdown-menu',
|
|
58
|
+
imports: [CdkConnectedOverlay, MimiMenuPanel],
|
|
59
|
+
template: `
|
|
60
|
+
<ng-content />
|
|
61
|
+
<ng-template
|
|
62
|
+
cdkConnectedOverlay
|
|
63
|
+
[cdkConnectedOverlayOrigin]="originElement()!"
|
|
64
|
+
[cdkConnectedOverlayOpen]="rendered()"
|
|
65
|
+
[cdkConnectedOverlayPositions]="positions()"
|
|
66
|
+
[cdkConnectedOverlayViewportMargin]="viewportMargin"
|
|
67
|
+
[cdkConnectedOverlayDisableClose]="true"
|
|
68
|
+
cdkConnectedOverlayUsePopover="inline"
|
|
69
|
+
cdkConnectedOverlayTransformOriginOn="[data-mimi-overlay-panel]"
|
|
70
|
+
(detach)="onDetach()"
|
|
71
|
+
(overlayOutsideClick)="close(false)"
|
|
72
|
+
(positionChange)="onPositionChange($event)"
|
|
73
|
+
>
|
|
74
|
+
@if (content(); as content) {
|
|
75
|
+
<mimi-menu-panel
|
|
76
|
+
[template]="content.templateRef"
|
|
77
|
+
[menuId]="menuId"
|
|
78
|
+
[label]="ariaLabel() ?? ''"
|
|
79
|
+
[labelledBy]="trigger()?.id"
|
|
80
|
+
[state]="state()"
|
|
81
|
+
[side]="placedSide()"
|
|
82
|
+
[align]="align()"
|
|
83
|
+
[initialFocus]="focusTarget"
|
|
84
|
+
[focusExempt]="trigger()?.element"
|
|
85
|
+
[class]="userClass()"
|
|
86
|
+
(closeRequest)="close($event)"
|
|
87
|
+
/>
|
|
88
|
+
}
|
|
89
|
+
</ng-template>
|
|
90
|
+
`,
|
|
91
|
+
host: {
|
|
92
|
+
style: 'display: contents',
|
|
93
|
+
// El class y el aria-label son del panel: en el host no tendrían efecto o se heredarían.
|
|
94
|
+
'[attr.class]': 'null',
|
|
95
|
+
'[attr.aria-label]': 'null',
|
|
96
|
+
'[attr.data-state]': 'open() ? "open" : "closed"',
|
|
97
|
+
},
|
|
98
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
99
|
+
})
|
|
100
|
+
export class MimiDropdownMenu {
|
|
101
|
+
private readonly document = inject(DOCUMENT);
|
|
102
|
+
private readonly injector = inject(Injector);
|
|
103
|
+
|
|
104
|
+
/** Abierto o cerrado. Admite `[(open)]`. */
|
|
105
|
+
readonly open = model(false);
|
|
106
|
+
/** Lado preferido. Si no cabe, pasa al lado opuesto. */
|
|
107
|
+
readonly side = input<MimiDropdownMenuSide>('bottom');
|
|
108
|
+
/** Alineación con el trigger. */
|
|
109
|
+
readonly align = input<MimiDropdownMenuAlign>('start');
|
|
110
|
+
/** Nombre accesible del menú (`aria-label`). Sin él, lo nombra el texto del trigger. */
|
|
111
|
+
readonly ariaLabel = input<string>(undefined, { alias: 'aria-label' });
|
|
112
|
+
/** Clases del panel, mezcladas con `cn()` (gana la del usuario). */
|
|
113
|
+
readonly userClass = input('', { alias: 'class' });
|
|
114
|
+
|
|
115
|
+
protected readonly trigger = contentChild(MimiDropdownMenuTrigger);
|
|
116
|
+
protected readonly content = contentChild(MimiDropdownMenuContent);
|
|
117
|
+
private readonly panel = viewChild(MimiMenuPanel);
|
|
118
|
+
|
|
119
|
+
/** id del menú (aria-controls del trigger). */
|
|
120
|
+
readonly menuId = `mimi-dropdown-menu-${nextId++}`;
|
|
121
|
+
|
|
122
|
+
/** El panel está en el DOM (también mientras dura la animación de salida). */
|
|
123
|
+
protected readonly rendered = signal(false);
|
|
124
|
+
protected readonly state = signal<'open' | 'closed'>('closed');
|
|
125
|
+
protected readonly placedSide = signal<MimiDropdownMenuSide>('bottom');
|
|
126
|
+
protected readonly viewportMargin = MIMI_VIEWPORT_MARGIN;
|
|
127
|
+
|
|
128
|
+
protected readonly originElement = computed(() => this.trigger()?.element ?? null);
|
|
129
|
+
protected readonly positions = computed(() =>
|
|
130
|
+
mimiConnectedPositions(this.side(), this.align(), OFFSET),
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
/** A qué ítem va el foco al abrir: el primero, o el último si se abrió con ↑. */
|
|
134
|
+
protected focusTarget: 'first' | 'last' = 'first';
|
|
135
|
+
/** Cuenta los cierres: si se vuelve a abrir durante la animación de salida, no se desmonta. */
|
|
136
|
+
private closing = 0;
|
|
137
|
+
/** Si el próximo cierre devuelve el foco al trigger. */
|
|
138
|
+
private refocus = true;
|
|
139
|
+
|
|
140
|
+
private readonly syncOpen = effect(() => {
|
|
141
|
+
const open = this.open();
|
|
142
|
+
untracked(() => (open ? this.show() : this.hide()));
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
/** Abre o cierra (lo usa el trigger). */
|
|
146
|
+
toggle(): void {
|
|
147
|
+
this.focusTarget = 'first';
|
|
148
|
+
this.open.update((open) => !open);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Abre con el foco en el primer o el último ítem (lo usa el trigger con las flechas). */
|
|
152
|
+
openAt(target: 'first' | 'last'): void {
|
|
153
|
+
this.focusTarget = target;
|
|
154
|
+
if (this.open()) this.panel()?.focusItem(target);
|
|
155
|
+
else this.open.set(true);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Cierra. Con `refocus` (al elegir un ítem o con Escape), el foco vuelve al trigger; sin él
|
|
160
|
+
* (clic fuera, Tab), queda donde el usuario lo puso.
|
|
161
|
+
*/
|
|
162
|
+
close(refocus = true): void {
|
|
163
|
+
this.refocus = refocus;
|
|
164
|
+
this.open.set(false);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
private show(): void {
|
|
168
|
+
this.closing++;
|
|
169
|
+
this.placedSide.set(this.side());
|
|
170
|
+
this.state.set('open');
|
|
171
|
+
// Reabierto durante la animación de salida: el panel sigue montado.
|
|
172
|
+
if (this.rendered()) this.panel()?.focusItem(this.focusTarget);
|
|
173
|
+
else this.rendered.set(true);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private hide(): void {
|
|
177
|
+
if (!this.rendered()) return;
|
|
178
|
+
this.state.set('closed');
|
|
179
|
+
const panel = this.panel()?.element;
|
|
180
|
+
const active = this.document.activeElement;
|
|
181
|
+
const focusInside = !active || active === this.document.body || panel?.contains(active);
|
|
182
|
+
if (this.refocus && focusInside) this.trigger()?.element.focus();
|
|
183
|
+
this.refocus = true;
|
|
184
|
+
const closing = ++this.closing;
|
|
185
|
+
afterNextRender(
|
|
186
|
+
{
|
|
187
|
+
read: () => {
|
|
188
|
+
void mimiWaitForExit(panel).then(() => {
|
|
189
|
+
if (closing === this.closing) this.rendered.set(false);
|
|
190
|
+
});
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
{ injector: this.injector },
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
protected onDetach(): void {
|
|
198
|
+
this.rendered.set(false);
|
|
199
|
+
if (this.open()) this.open.set(false);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
protected onPositionChange({ connectionPair }: ConnectedOverlayPositionChange): void {
|
|
203
|
+
this.placedSide.set(mimiPlacedSide(connectionPair));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Botón que abre y cierra el menú: `<button mimiBtn mimiDropdownMenuTrigger>`. Clic, Enter o
|
|
209
|
+
* Espacio abren en el primer ítem; ↓ también, y ↑ en el último.
|
|
210
|
+
*/
|
|
211
|
+
@Directive({
|
|
212
|
+
selector: '[mimiDropdownMenuTrigger]',
|
|
213
|
+
host: {
|
|
214
|
+
'aria-haspopup': 'menu',
|
|
215
|
+
'[id]': 'id',
|
|
216
|
+
'[attr.aria-expanded]': 'menu.open()',
|
|
217
|
+
'[attr.aria-controls]': 'menu.open() ? menu.menuId : null',
|
|
218
|
+
'[attr.data-state]': 'menu.open() ? "open" : "closed"',
|
|
219
|
+
'(click)': 'menu.toggle()',
|
|
220
|
+
'(keydown)': 'onKeydown($event)',
|
|
221
|
+
},
|
|
222
|
+
})
|
|
223
|
+
export class MimiDropdownMenuTrigger {
|
|
224
|
+
protected readonly menu = inject(MimiDropdownMenu);
|
|
225
|
+
readonly element = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;
|
|
226
|
+
/** El del usuario, o uno generado (el menú se nombra con él). */
|
|
227
|
+
readonly id =
|
|
228
|
+
inject(new HostAttributeToken('id'), { optional: true }) ?? `${this.menu.menuId}-trigger`;
|
|
229
|
+
|
|
230
|
+
protected onKeydown(event: KeyboardEvent): void {
|
|
231
|
+
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
|
|
232
|
+
event.preventDefault();
|
|
233
|
+
this.menu.openAt(event.key === 'ArrowDown' ? 'first' : 'last');
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Contenido del menú: `<ng-template mimiDropdownMenuContent>`. Se crea al abrir y se destruye al
|
|
240
|
+
* cerrar. El `class` y el `label` del panel van en `<mimi-dropdown-menu>`.
|
|
241
|
+
*/
|
|
242
|
+
@Directive({ selector: 'ng-template[mimiDropdownMenuContent]' })
|
|
243
|
+
export class MimiDropdownMenuContent {
|
|
244
|
+
readonly templateRef = inject(TemplateRef);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export const MimiDropdownMenuImports = [
|
|
248
|
+
MimiDropdownMenu,
|
|
249
|
+
MimiDropdownMenuTrigger,
|
|
250
|
+
MimiDropdownMenuContent,
|
|
251
|
+
...MimiMenuItemsImports,
|
|
252
|
+
] as const;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export * from './dropdown-menu';
|
|
2
|
+
// Los ítems y sus tipos, para importar todo desde @/components/ui/dropdown-menu.
|
|
3
|
+
export {
|
|
4
|
+
MimiMenuCheckboxItem,
|
|
5
|
+
MimiMenuItem,
|
|
6
|
+
type MimiMenuItemTone,
|
|
7
|
+
MimiMenuLabel,
|
|
8
|
+
MimiMenuRadioGroup,
|
|
9
|
+
MimiMenuRadioItem,
|
|
10
|
+
MimiMenuSeparator,
|
|
11
|
+
MimiMenuSub,
|
|
12
|
+
MimiMenuSubContent,
|
|
13
|
+
MimiMenuSubTrigger,
|
|
14
|
+
} from '@/components/ui/menu';
|
|
@@ -73,7 +73,7 @@ export class MimiFormError {
|
|
|
73
73
|
|
|
74
74
|
protected readonly classes = computed(() =>
|
|
75
75
|
cn(
|
|
76
|
-
'flex items-center gap-1.5 text-[13px] text-destructive [&_svg]:size-3.5 [&_svg]:shrink-0',
|
|
76
|
+
'flex items-center gap-1.5 text-[13px] text-[color:var(--mimi-field-error,var(--mimi-destructive))] [&_svg]:size-3.5 [&_svg]:shrink-0',
|
|
77
77
|
this.userClass(),
|
|
78
78
|
// Sin error: vacío y fuera del flujo, pero en el DOM para que aria-live funcione.
|
|
79
79
|
!this.context.showError() && 'sr-only',
|
|
@@ -80,12 +80,13 @@ export class MimiFormField implements MimiFormFieldContext {
|
|
|
80
80
|
protected readonly classes = computed(() =>
|
|
81
81
|
cn(
|
|
82
82
|
// La etiqueta (la de label="…" o la tuya) se pone roja con error.
|
|
83
|
-
'flex flex-col gap-
|
|
83
|
+
'flex flex-col gap-[var(--mimi-field-gap,0.375rem)] [&[data-invalid]>label]:text-[color:var(--mimi-field-error,var(--mimi-destructive))]',
|
|
84
84
|
this.userClass(),
|
|
85
85
|
),
|
|
86
86
|
);
|
|
87
87
|
|
|
88
|
-
protected readonly labelClasses =
|
|
88
|
+
protected readonly labelClasses =
|
|
89
|
+
'text-[length:var(--mimi-field-label-size,0.875rem)] leading-[calc(1.25/0.875)] font-[weight:var(--mimi-field-label-weight,500)]';
|
|
89
90
|
|
|
90
91
|
constructor() {
|
|
91
92
|
// Conecta el control: id, etiquetas propias sin `for` y aria-describedby del mensaje.
|
|
@@ -13,14 +13,16 @@ import {
|
|
|
13
13
|
export const inputVariants = cva(
|
|
14
14
|
[
|
|
15
15
|
'w-full min-w-0 rounded-[var(--mimi-input-radius,var(--mimi-radius,0.75rem))]',
|
|
16
|
-
'border-[length:var(--mimi-input-border-width,1px)] border-input bg-input-background',
|
|
17
|
-
'px-[var(--mimi-input-padding-x,0.75rem)] font-sans text-foreground mimi-transition',
|
|
18
|
-
'placeholder:text-[color:var(--mimi-input-placeholder-color,var(--mimi-muted-foreground))]',
|
|
16
|
+
'border-[length:var(--mimi-input-border-width,1px)] border-[color:var(--mimi-input-border,var(--mimi-input))] bg-[color:var(--mimi-input-bg,var(--mimi-input-background))]',
|
|
17
|
+
'px-[var(--mimi-input-px,var(--mimi-input-padding-x,0.75rem))] font-sans text-[color:var(--mimi-input-fg,var(--mimi-foreground))] mimi-transition',
|
|
18
|
+
'placeholder:text-[color:var(--mimi-input-placeholder,var(--mimi-input-placeholder-color,var(--mimi-muted-foreground)))]',
|
|
19
19
|
fieldFocusStyles,
|
|
20
|
+
'focus-visible:border-[color:var(--mimi-input-border-focus,var(--mimi-ring))]',
|
|
20
21
|
'focus-visible:ring-[length:var(--mimi-input-focus-ring-width,3px)]',
|
|
21
22
|
controlDisabledStyles,
|
|
22
|
-
'disabled:opacity-[var(--mimi-input-disabled-opacity,0.5)]',
|
|
23
|
+
'disabled:opacity-[var(--mimi-input-disabled-opacity,var(--mimi-disabled-opacity,0.5))]',
|
|
23
24
|
controlInvalidStyles,
|
|
25
|
+
'aria-invalid:border-[color:var(--mimi-input-error,var(--mimi-destructive))] aria-invalid:focus-visible:border-[color:var(--mimi-input-error,var(--mimi-destructive))]',
|
|
24
26
|
],
|
|
25
27
|
{
|
|
26
28
|
variants: {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Solo la API pública. Las piezas internas (MimiMenuPanel, MimiMenuOutlet, MIMI_MENU_HOST…) se
|
|
2
|
+
// importan desde './menu' y solo las usan Dropdown Menu y Context Menu.
|
|
3
|
+
export {
|
|
4
|
+
MimiMenuCheckboxItem,
|
|
5
|
+
MimiMenuItem,
|
|
6
|
+
type MimiMenuItemTone,
|
|
7
|
+
MimiMenuItemsImports,
|
|
8
|
+
MimiMenuLabel,
|
|
9
|
+
MimiMenuRadioGroup,
|
|
10
|
+
MimiMenuRadioItem,
|
|
11
|
+
MimiMenuSeparator,
|
|
12
|
+
MimiMenuSub,
|
|
13
|
+
MimiMenuSubContent,
|
|
14
|
+
MimiMenuSubTrigger,
|
|
15
|
+
} from './menu';
|