ng-comps 1.0.2 → 2.0.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 (197) hide show
  1. package/fesm2022/ng-comps.mjs +4254 -0
  2. package/package.json +54 -58
  3. package/src/styles.css +54 -0
  4. package/types/ng-comps.d.ts +1348 -0
  5. package/.editorconfig +0 -17
  6. package/.github/copilot-instructions.md +0 -55
  7. package/.github/workflows/ci.yml +0 -29
  8. package/.prettierrc +0 -12
  9. package/.storybook/main.ts +0 -21
  10. package/.storybook/preview.ts +0 -27
  11. package/.storybook/tsconfig.doc.json +0 -10
  12. package/.storybook/tsconfig.json +0 -15
  13. package/.storybook/typings.d.ts +0 -4
  14. package/.vscode/extensions.json +0 -4
  15. package/.vscode/launch.json +0 -20
  16. package/.vscode/mcp.json +0 -9
  17. package/.vscode/tasks.json +0 -42
  18. package/ACCESSIBILITY.md +0 -127
  19. package/angular.json +0 -106
  20. package/documentation.json +0 -13394
  21. package/ng-package.json +0 -27
  22. package/public/favicon.ico +0 -0
  23. package/scripts/prepare-package.mjs +0 -80
  24. package/src/app/a11y/accessibility.utils.ts +0 -35
  25. package/src/app/a11y/index.ts +0 -6
  26. package/src/app/accessibility/ng-comps.a11y.spec.ts +0 -108
  27. package/src/app/app.config.ts +0 -11
  28. package/src/app/app.css +0 -107
  29. package/src/app/app.html +0 -48
  30. package/src/app/app.routes.ts +0 -3
  31. package/src/app/app.spec.ts +0 -23
  32. package/src/app/app.ts +0 -10
  33. package/src/app/components/accordion/index.ts +0 -2
  34. package/src/app/components/accordion/mf-accordion.component.css +0 -38
  35. package/src/app/components/accordion/mf-accordion.component.spec.ts +0 -48
  36. package/src/app/components/accordion/mf-accordion.component.ts +0 -53
  37. package/src/app/components/alert/index.ts +0 -2
  38. package/src/app/components/alert/mf-alert.component.css +0 -100
  39. package/src/app/components/alert/mf-alert.component.spec.ts +0 -59
  40. package/src/app/components/alert/mf-alert.component.ts +0 -68
  41. package/src/app/components/autocomplete/index.ts +0 -5
  42. package/src/app/components/autocomplete/mf-autocomplete.component.css +0 -105
  43. package/src/app/components/autocomplete/mf-autocomplete.component.spec.ts +0 -116
  44. package/src/app/components/autocomplete/mf-autocomplete.component.ts +0 -307
  45. package/src/app/components/avatar/index.ts +0 -2
  46. package/src/app/components/avatar/mf-avatar.component.css +0 -27
  47. package/src/app/components/avatar/mf-avatar.component.spec.ts +0 -49
  48. package/src/app/components/avatar/mf-avatar.component.ts +0 -99
  49. package/src/app/components/badge/index.ts +0 -2
  50. package/src/app/components/badge/mf-badge.component.css +0 -32
  51. package/src/app/components/badge/mf-badge.component.spec.ts +0 -40
  52. package/src/app/components/badge/mf-badge.component.ts +0 -105
  53. package/src/app/components/breadcrumb/index.ts +0 -2
  54. package/src/app/components/breadcrumb/mf-breadcrumb.component.css +0 -61
  55. package/src/app/components/breadcrumb/mf-breadcrumb.component.spec.ts +0 -61
  56. package/src/app/components/breadcrumb/mf-breadcrumb.component.ts +0 -75
  57. package/src/app/components/button/index.ts +0 -2
  58. package/src/app/components/button/mf-button.component.css +0 -136
  59. package/src/app/components/button/mf-button.component.ts +0 -174
  60. package/src/app/components/card/index.ts +0 -2
  61. package/src/app/components/card/mf-card.component.css +0 -82
  62. package/src/app/components/card/mf-card.component.ts +0 -59
  63. package/src/app/components/checkbox/index.ts +0 -1
  64. package/src/app/components/checkbox/mf-checkbox.component.css +0 -75
  65. package/src/app/components/checkbox/mf-checkbox.component.ts +0 -187
  66. package/src/app/components/chip/index.ts +0 -2
  67. package/src/app/components/chip/mf-chip.component.css +0 -69
  68. package/src/app/components/chip/mf-chip.component.spec.ts +0 -47
  69. package/src/app/components/chip/mf-chip.component.ts +0 -77
  70. package/src/app/components/datepicker/index.ts +0 -2
  71. package/src/app/components/datepicker/mf-datepicker.component.css +0 -102
  72. package/src/app/components/datepicker/mf-datepicker.component.spec.ts +0 -69
  73. package/src/app/components/datepicker/mf-datepicker.component.ts +0 -233
  74. package/src/app/components/dialog/index.ts +0 -3
  75. package/src/app/components/dialog/mf-dialog.component.css +0 -73
  76. package/src/app/components/dialog/mf-dialog.component.ts +0 -160
  77. package/src/app/components/dialog/mf-dialog.service.spec.ts +0 -61
  78. package/src/app/components/dialog/mf-dialog.service.ts +0 -52
  79. package/src/app/components/divider/index.ts +0 -2
  80. package/src/app/components/divider/mf-divider.component.css +0 -38
  81. package/src/app/components/divider/mf-divider.component.spec.ts +0 -40
  82. package/src/app/components/divider/mf-divider.component.ts +0 -44
  83. package/src/app/components/form-field/index.ts +0 -1
  84. package/src/app/components/form-field/mf-form-field.component.css +0 -51
  85. package/src/app/components/form-field/mf-form-field.component.ts +0 -74
  86. package/src/app/components/grid-list/index.ts +0 -2
  87. package/src/app/components/grid-list/mf-grid-list.component.css +0 -47
  88. package/src/app/components/grid-list/mf-grid-list.component.spec.ts +0 -57
  89. package/src/app/components/grid-list/mf-grid-list.component.ts +0 -68
  90. package/src/app/components/icon/index.ts +0 -2
  91. package/src/app/components/icon/mf-icon.component.css +0 -56
  92. package/src/app/components/icon/mf-icon.component.ts +0 -41
  93. package/src/app/components/input/index.ts +0 -2
  94. package/src/app/components/input/mf-input.component.css +0 -105
  95. package/src/app/components/input/mf-input.component.ts +0 -217
  96. package/src/app/components/menu/index.ts +0 -2
  97. package/src/app/components/menu/mf-menu.component.css +0 -31
  98. package/src/app/components/menu/mf-menu.component.spec.ts +0 -49
  99. package/src/app/components/menu/mf-menu.component.ts +0 -66
  100. package/src/app/components/paginator/index.ts +0 -1
  101. package/src/app/components/paginator/mf-paginator.component.css +0 -32
  102. package/src/app/components/paginator/mf-paginator.component.spec.ts +0 -44
  103. package/src/app/components/paginator/mf-paginator.component.ts +0 -52
  104. package/src/app/components/progress-bar/index.ts +0 -2
  105. package/src/app/components/progress-bar/mf-progress-bar.component.css +0 -53
  106. package/src/app/components/progress-bar/mf-progress-bar.component.spec.ts +0 -65
  107. package/src/app/components/progress-bar/mf-progress-bar.component.ts +0 -79
  108. package/src/app/components/progress-spinner/index.ts +0 -2
  109. package/src/app/components/progress-spinner/mf-progress-spinner.component.css +0 -38
  110. package/src/app/components/progress-spinner/mf-progress-spinner.component.spec.ts +0 -59
  111. package/src/app/components/progress-spinner/mf-progress-spinner.component.ts +0 -81
  112. package/src/app/components/radio-button/index.ts +0 -2
  113. package/src/app/components/radio-button/mf-radio-button.component.css +0 -86
  114. package/src/app/components/radio-button/mf-radio-button.component.spec.ts +0 -55
  115. package/src/app/components/radio-button/mf-radio-button.component.ts +0 -219
  116. package/src/app/components/select/index.ts +0 -2
  117. package/src/app/components/select/mf-select.component.css +0 -121
  118. package/src/app/components/select/mf-select.component.spec.ts +0 -108
  119. package/src/app/components/select/mf-select.component.ts +0 -252
  120. package/src/app/components/sidenav/index.ts +0 -2
  121. package/src/app/components/sidenav/mf-sidenav.component.css +0 -168
  122. package/src/app/components/sidenav/mf-sidenav.component.spec.ts +0 -57
  123. package/src/app/components/sidenav/mf-sidenav.component.ts +0 -126
  124. package/src/app/components/slide-toggle/index.ts +0 -1
  125. package/src/app/components/slide-toggle/mf-slide-toggle.component.css +0 -42
  126. package/src/app/components/slide-toggle/mf-slide-toggle.component.spec.ts +0 -43
  127. package/src/app/components/slide-toggle/mf-slide-toggle.component.ts +0 -188
  128. package/src/app/components/snackbar/index.ts +0 -2
  129. package/src/app/components/snackbar/mf-snackbar.service.css +0 -31
  130. package/src/app/components/snackbar/mf-snackbar.service.spec.ts +0 -81
  131. package/src/app/components/snackbar/mf-snackbar.service.ts +0 -77
  132. package/src/app/components/table/index.ts +0 -2
  133. package/src/app/components/table/mf-table.component.css +0 -68
  134. package/src/app/components/table/mf-table.component.spec.ts +0 -76
  135. package/src/app/components/table/mf-table.component.ts +0 -117
  136. package/src/app/components/tabs/index.ts +0 -2
  137. package/src/app/components/tabs/mf-tabs.component.css +0 -31
  138. package/src/app/components/tabs/mf-tabs.component.spec.ts +0 -50
  139. package/src/app/components/tabs/mf-tabs.component.ts +0 -62
  140. package/src/app/components/textarea/index.ts +0 -2
  141. package/src/app/components/textarea/mf-textarea.component.css +0 -48
  142. package/src/app/components/textarea/mf-textarea.component.spec.ts +0 -55
  143. package/src/app/components/textarea/mf-textarea.component.ts +0 -227
  144. package/src/app/components/toolbar/index.ts +0 -2
  145. package/src/app/components/toolbar/mf-toolbar.component.css +0 -77
  146. package/src/app/components/toolbar/mf-toolbar.component.ts +0 -56
  147. package/src/app/components/tooltip/index.ts +0 -3
  148. package/src/app/components/tooltip/mf-tooltip.component.css +0 -7
  149. package/src/app/components/tooltip/mf-tooltip.component.spec.ts +0 -37
  150. package/src/app/components/tooltip/mf-tooltip.component.ts +0 -47
  151. package/src/app/components/tooltip/mf-tooltip.directive.ts +0 -22
  152. package/src/index.html +0 -18
  153. package/src/main.ts +0 -6
  154. package/src/public-api.ts +0 -31
  155. package/src/stories/About.mdx +0 -72
  156. package/src/stories/Accessibility.mdx +0 -59
  157. package/src/stories/Welcome.mdx +0 -26
  158. package/src/stories/assets/accessibility.png +0 -0
  159. package/src/stories/assets/accessibility.svg +0 -1
  160. package/src/stories/assets/addon-library.png +0 -0
  161. package/src/stories/assets/assets.png +0 -0
  162. package/src/stories/assets/avif-test-image.avif +0 -0
  163. package/src/stories/assets/context.png +0 -0
  164. package/src/stories/assets/discord.svg +0 -1
  165. package/src/stories/assets/docs.png +0 -0
  166. package/src/stories/assets/figma-plugin.png +0 -0
  167. package/src/stories/assets/github.svg +0 -1
  168. package/src/stories/assets/share.png +0 -0
  169. package/src/stories/assets/styling.png +0 -0
  170. package/src/stories/assets/testing.png +0 -0
  171. package/src/stories/assets/theming.png +0 -0
  172. package/src/stories/assets/tutorials.svg +0 -1
  173. package/src/stories/assets/youtube.svg +0 -1
  174. package/src/stories/mf-a11y-contracts.stories.ts +0 -472
  175. package/src/stories/mf-autocomplete.stories.ts +0 -194
  176. package/src/stories/mf-button.stories.ts +0 -152
  177. package/src/stories/mf-card.stories.ts +0 -147
  178. package/src/stories/mf-checkbox.stories.ts +0 -88
  179. package/src/stories/mf-datepicker.stories.ts +0 -118
  180. package/src/stories/mf-dialog.stories.ts +0 -159
  181. package/src/stories/mf-form-field.stories.ts +0 -108
  182. package/src/stories/mf-grid-list.stories.ts +0 -104
  183. package/src/stories/mf-icon.stories.ts +0 -133
  184. package/src/stories/mf-input.stories.ts +0 -158
  185. package/src/stories/mf-menu.stories.ts +0 -71
  186. package/src/stories/mf-progress-bar.stories.ts +0 -119
  187. package/src/stories/mf-progress-spinner.stories.ts +0 -124
  188. package/src/stories/mf-radio-button.stories.ts +0 -111
  189. package/src/stories/mf-select.stories.ts +0 -184
  190. package/src/stories/mf-sidenav.stories.ts +0 -331
  191. package/src/stories/mf-table.stories.ts +0 -80
  192. package/src/stories/mf-toolbar.stories.ts +0 -112
  193. package/src/stories/user.ts +0 -3
  194. package/tsconfig.app.json +0 -15
  195. package/tsconfig.json +0 -33
  196. package/tsconfig.spec.json +0 -15
  197. package/vercel.json +0 -6
@@ -1,217 +0,0 @@
1
- import {
2
- ChangeDetectionStrategy,
3
- ChangeDetectorRef,
4
- Component,
5
- computed,
6
- effect,
7
- forwardRef,
8
- inject,
9
- input,
10
- output,
11
- signal,
12
- } from '@angular/core';
13
- import { ControlValueAccessor, NG_VALUE_ACCESSOR, NgControl } from '@angular/forms';
14
- import { ErrorStateMatcher } from '@angular/material/core';
15
- import { MatFormFieldModule } from '@angular/material/form-field';
16
- import { MatInputModule } from '@angular/material/input';
17
- import { MatIconModule } from '@angular/material/icon';
18
- import {
19
- createUniqueId,
20
- hasAccessibleName,
21
- mergeAriaIds,
22
- warnInDev,
23
- } from '../../a11y';
24
-
25
- export type MfInputSize = 'sm' | 'md' | 'lg';
26
-
27
- /**
28
- * Campo de texto de la librería ng-comps.
29
- * Envuelve Angular Material `mat-form-field` + `matInput`
30
- * y expone una API uniforme con look and feel de marca.
31
- */
32
- @Component({
33
- selector: 'mf-input',
34
- imports: [MatFormFieldModule, MatInputModule, MatIconModule],
35
- providers: [
36
- {
37
- provide: NG_VALUE_ACCESSOR,
38
- useExisting: forwardRef(() => MfInputComponent),
39
- multi: true,
40
- },
41
- ],
42
- template: `
43
- <mat-form-field [appearance]="'outline'" [class]="hostClasses()">
44
- @if (label()) {
45
- <mat-label>{{ label() }}</mat-label>
46
- }
47
- @if (leadingIcon()) {
48
- <mat-icon matPrefix aria-hidden="true">{{ leadingIcon() }}</mat-icon>
49
- }
50
- <input
51
- matInput
52
- [id]="controlId()"
53
- [type]="type()"
54
- [placeholder]="placeholder()"
55
- [disabled]="isDisabled()"
56
- [readonly]="readonly()"
57
- [required]="required()"
58
- [value]="internalValue()"
59
- [errorStateMatcher]="errorStateMatcher"
60
- [attr.aria-label]="resolvedAriaLabel()"
61
- [attr.aria-labelledby]="ariaLabelledby() || null"
62
- [attr.aria-describedby]="describedBy()"
63
- [attr.aria-invalid]="isInvalid() ? 'true' : null"
64
- [attr.aria-required]="required() ? 'true' : null"
65
- (input)="onInput($event)"
66
- (blur)="onBlur()"
67
- />
68
- @if (trailingIcon()) {
69
- <mat-icon matSuffix aria-hidden="true">{{ trailingIcon() }}</mat-icon>
70
- }
71
- @if (hint()) {
72
- <mat-hint [attr.id]="hintId()">{{ hint() }}</mat-hint>
73
- }
74
- </mat-form-field>
75
- @if (error()) {
76
- <p class="mf-input__error" [attr.id]="errorId()" role="alert">{{ error() }}</p>
77
- }
78
- `,
79
- styleUrl: './mf-input.component.css',
80
- changeDetection: ChangeDetectionStrategy.OnPush,
81
- })
82
- export class MfInputComponent implements ControlValueAccessor {
83
- private readonly cdr = inject(ChangeDetectorRef);
84
- private readonly ngControl = inject(NgControl, { self: true, optional: true });
85
- private readonly generatedId = createUniqueId('mf-input');
86
- private readonly disabledFromForm = signal(false);
87
- protected readonly internalValue = signal('');
88
- private onControlChange: (value: string) => void = () => undefined;
89
- private onControlTouched: () => void = () => undefined;
90
- readonly errorStateMatcher: ErrorStateMatcher = {
91
- isErrorState: (control) =>
92
- Boolean(
93
- this.error() || (control?.invalid && (control.touched || control.dirty)),
94
- ),
95
- };
96
-
97
- /** ID del control */
98
- readonly id = input<string | undefined>(undefined);
99
- /** Etiqueta flotante del campo */
100
- readonly label = input<string | undefined>(undefined);
101
- /** Etiqueta accesible alternativa cuando no existe label visible */
102
- readonly ariaLabel = input<string | undefined>(undefined);
103
- /** Referencia externa a elementos que etiquetan el control */
104
- readonly ariaLabelledby = input<string | undefined>(undefined);
105
- /** Referencia externa a elementos descriptivos adicionales */
106
- readonly ariaDescribedby = input<string | undefined>(undefined);
107
- /** Placeholder del input */
108
- readonly placeholder = input('');
109
- /** Tipo de input HTML */
110
- readonly type = input<
111
- 'text' | 'email' | 'password' | 'number' | 'search' | 'tel' | 'url'
112
- >('text');
113
- /** Tamaño del campo */
114
- readonly size = input<MfInputSize>('md');
115
- /** Valor actual del campo */
116
- readonly value = input('');
117
- /** Deshabilitado */
118
- readonly disabled = input(false);
119
- /** Solo lectura */
120
- readonly readonly = input(false);
121
- /** Requerido */
122
- readonly required = input(false);
123
- /** Texto de ayuda debajo del campo */
124
- readonly hint = input<string | undefined>(undefined);
125
- /** Mensaje de error */
126
- readonly error = input<string | undefined>(undefined);
127
- /** Icono al inicio */
128
- readonly leadingIcon = input<string | undefined>(undefined);
129
- /** Icono al final */
130
- readonly trailingIcon = input<string | undefined>(undefined);
131
- /** Ancho completo */
132
- readonly fullWidth = input(false);
133
-
134
- readonly mfInput = output<string>();
135
- readonly mfBlur = output<void>();
136
-
137
- constructor() {
138
- effect(() => {
139
- this.internalValue.set(this.value());
140
- });
141
-
142
- effect(() => {
143
- if (
144
- !hasAccessibleName(
145
- this.label(),
146
- this.ariaLabel(),
147
- this.ariaLabelledby(),
148
- )
149
- ) {
150
- warnInDev(
151
- 'mf-input requiere `label`, `ariaLabel` o `ariaLabelledby` para exponer un nombre accesible.',
152
- );
153
- }
154
- });
155
- }
156
-
157
- readonly controlId = computed(() => this.id() ?? this.generatedId);
158
- readonly hintId = computed(() =>
159
- this.hint() ? `${this.controlId()}-hint` : null,
160
- );
161
- readonly errorId = computed(() =>
162
- this.error() ? `${this.controlId()}-error` : null,
163
- );
164
- readonly describedBy = computed(() =>
165
- mergeAriaIds(this.ariaDescribedby(), this.hintId(), this.errorId()),
166
- );
167
- readonly resolvedAriaLabel = computed(() =>
168
- this.label() ? null : this.ariaLabel() ?? null,
169
- );
170
- readonly isDisabled = computed(
171
- () => this.disabled() || this.disabledFromForm(),
172
- );
173
-
174
- readonly hostClasses = computed(() => {
175
- const classes = ['mf-input', `mf-input--${this.size()}`];
176
- if (this.fullWidth()) classes.push('mf-input--full');
177
- if (this.error()) classes.push('mf-input--error');
178
- return classes.join(' ');
179
- });
180
-
181
- writeValue(value: string | null): void {
182
- this.internalValue.set(value ?? '');
183
- this.cdr.markForCheck();
184
- }
185
-
186
- registerOnChange(fn: (value: string) => void): void {
187
- this.onControlChange = fn;
188
- }
189
-
190
- registerOnTouched(fn: () => void): void {
191
- this.onControlTouched = fn;
192
- }
193
-
194
- setDisabledState(isDisabled: boolean): void {
195
- this.disabledFromForm.set(isDisabled);
196
- this.cdr.markForCheck();
197
- }
198
-
199
- isInvalid(): boolean {
200
- const control = this.ngControl?.control;
201
- return Boolean(
202
- this.error() || (control?.invalid && (control.touched || control.dirty)),
203
- );
204
- }
205
-
206
- onInput(event: Event): void {
207
- const target = event.target as HTMLInputElement;
208
- this.internalValue.set(target.value);
209
- this.onControlChange(target.value);
210
- this.mfInput.emit(target.value);
211
- }
212
-
213
- onBlur(): void {
214
- this.onControlTouched();
215
- this.mfBlur.emit();
216
- }
217
- }
@@ -1,2 +0,0 @@
1
- export { MfMenuComponent } from './mf-menu.component';
2
- export type { MfMenuItem } from './mf-menu.component';
@@ -1,31 +0,0 @@
1
- :host {
2
- display: inline-block;
3
- }
4
-
5
- .mf-menu__trigger {
6
- color: var(--mf-color-neutral-600) !important;
7
- }
8
-
9
- .mf-menu__trigger:hover {
10
- color: var(--mf-color-brand) !important;
11
- }
12
-
13
- .mf-menu .mat-mdc-menu-panel {
14
- border-radius: var(--mf-radius-md) !important;
15
- box-shadow: var(--mf-shadow-lg) !important;
16
- }
17
-
18
- .mf-menu .mat-mdc-menu-item {
19
- font-family: var(--mf-font-base) !important;
20
- font-size: var(--mf-text-sm) !important;
21
- color: var(--mf-color-on-surface) !important;
22
- }
23
-
24
- .mf-menu .mat-mdc-menu-item:hover {
25
- background-color: var(--mf-color-brand-light) !important;
26
- }
27
-
28
- .mf-menu .mat-mdc-menu-item .mat-icon {
29
- color: var(--mf-color-neutral-400) !important;
30
- margin-right: var(--mf-space-2) !important;
31
- }
@@ -1,49 +0,0 @@
1
- import { ComponentFixture, TestBed } from '@angular/core/testing';
2
- import { MfMenuComponent } from './mf-menu.component';
3
- import { NoopAnimationsModule } from '@angular/platform-browser/animations';
4
-
5
- describe('MfMenuComponent', () => {
6
- let fixture: ComponentFixture<MfMenuComponent>;
7
- let component: MfMenuComponent;
8
-
9
- const testItems = [
10
- { label: 'Edit', icon: 'edit', value: 'edit' },
11
- { label: 'Delete', icon: 'delete', value: 'delete' },
12
- { label: 'Disabled', value: 'disabled', disabled: true },
13
- ];
14
-
15
- beforeEach(async () => {
16
- await TestBed.configureTestingModule({
17
- imports: [MfMenuComponent, NoopAnimationsModule],
18
- }).compileComponents();
19
-
20
- fixture = TestBed.createComponent(MfMenuComponent);
21
- component = fixture.componentInstance;
22
- fixture.componentRef.setInput('items', testItems);
23
- fixture.detectChanges();
24
- });
25
-
26
- it('should create', () => {
27
- expect(component).toBeTruthy();
28
- });
29
-
30
- it('should render trigger button', () => {
31
- const button = fixture.nativeElement.querySelector('button');
32
- expect(button).toBeTruthy();
33
- });
34
-
35
- it('should use more_vert icon by default', () => {
36
- expect(component.triggerIcon()).toBe('more_vert');
37
- });
38
-
39
- it('should have accessible trigger label', () => {
40
- expect(component.triggerLabel()).toBe('Open menu');
41
- });
42
-
43
- it('should emit item click', () => {
44
- const spy = vi.fn();
45
- component.mfItemClick.subscribe(spy);
46
- component.mfItemClick.emit('edit');
47
- expect(spy).toHaveBeenCalledWith('edit');
48
- });
49
- });
@@ -1,66 +0,0 @@
1
- import {
2
- ChangeDetectionStrategy,
3
- Component,
4
- computed,
5
- input,
6
- output,
7
- } from '@angular/core';
8
- import { MatMenuModule } from '@angular/material/menu';
9
- import { MatButtonModule } from '@angular/material/button';
10
- import { MatIconModule } from '@angular/material/icon';
11
-
12
- export interface MfMenuItem {
13
- label: string;
14
- icon?: string;
15
- disabled?: boolean;
16
- value: string;
17
- }
18
-
19
- /**
20
- * Menu de la librería ng-comps.
21
- * Envuelve Angular Material `mat-menu` y expone una API uniforme
22
- * con look and feel de marca.
23
- */
24
- @Component({
25
- selector: 'mf-menu',
26
- imports: [MatMenuModule, MatButtonModule, MatIconModule],
27
- template: `
28
- <button
29
- mat-icon-button
30
- [matMenuTriggerFor]="menu"
31
- [class]="triggerClasses()"
32
- [attr.aria-label]="triggerLabel()"
33
- >
34
- <mat-icon>{{ triggerIcon() }}</mat-icon>
35
- </button>
36
- <mat-menu #menu="matMenu" [class]="hostClasses()">
37
- @for (item of items(); track item.value) {
38
- <button
39
- mat-menu-item
40
- [disabled]="item.disabled ?? false"
41
- (click)="mfItemClick.emit(item.value)"
42
- >
43
- @if (item.icon) {
44
- <mat-icon aria-hidden="true">{{ item.icon }}</mat-icon>
45
- }
46
- <span>{{ item.label }}</span>
47
- </button>
48
- }
49
- </mat-menu>
50
- `,
51
- styleUrl: './mf-menu.component.css',
52
- changeDetection: ChangeDetectionStrategy.OnPush,
53
- })
54
- export class MfMenuComponent {
55
- /** Items del menú */
56
- readonly items = input.required<MfMenuItem[]>();
57
- /** Icono del trigger */
58
- readonly triggerIcon = input<string>('more_vert');
59
- /** Label accesible del trigger */
60
- readonly triggerLabel = input<string>('Open menu');
61
-
62
- readonly mfItemClick = output<string>();
63
-
64
- readonly hostClasses = computed(() => 'mf-menu');
65
- readonly triggerClasses = computed(() => 'mf-menu__trigger');
66
- }
@@ -1 +0,0 @@
1
- export { MfPaginatorComponent } from './mf-paginator.component';
@@ -1,32 +0,0 @@
1
- :host {
2
- display: block;
3
- }
4
-
5
- .mf-paginator.mat-mdc-paginator {
6
- font-family: var(--mf-font-base) !important;
7
- font-size: var(--mf-text-sm) !important;
8
- background-color: transparent !important;
9
- }
10
-
11
- .mf-paginator .mat-mdc-paginator-container {
12
- padding: var(--mf-space-2) 0 !important;
13
- }
14
-
15
- .mf-paginator .mat-mdc-paginator-range-label {
16
- color: var(--mf-color-neutral-600) !important;
17
- font-family: var(--mf-font-base) !important;
18
- }
19
-
20
- .mf-paginator .mat-mdc-paginator-navigation-previous,
21
- .mf-paginator .mat-mdc-paginator-navigation-next,
22
- .mf-paginator .mat-mdc-paginator-navigation-first,
23
- .mf-paginator .mat-mdc-paginator-navigation-last {
24
- color: var(--mf-color-brand) !important;
25
- }
26
-
27
- .mf-paginator .mat-mdc-paginator-navigation-previous:disabled,
28
- .mf-paginator .mat-mdc-paginator-navigation-next:disabled,
29
- .mf-paginator .mat-mdc-paginator-navigation-first:disabled,
30
- .mf-paginator .mat-mdc-paginator-navigation-last:disabled {
31
- color: var(--mf-color-neutral-300) !important;
32
- }
@@ -1,44 +0,0 @@
1
- import { ComponentFixture, TestBed } from '@angular/core/testing';
2
- import { MfPaginatorComponent } from './mf-paginator.component';
3
- import { NoopAnimationsModule } from '@angular/platform-browser/animations';
4
-
5
- describe('MfPaginatorComponent', () => {
6
- let fixture: ComponentFixture<MfPaginatorComponent>;
7
- let component: MfPaginatorComponent;
8
-
9
- beforeEach(async () => {
10
- await TestBed.configureTestingModule({
11
- imports: [MfPaginatorComponent, NoopAnimationsModule],
12
- }).compileComponents();
13
-
14
- fixture = TestBed.createComponent(MfPaginatorComponent);
15
- component = fixture.componentInstance;
16
- fixture.componentRef.setInput('length', 100);
17
- fixture.detectChanges();
18
- });
19
-
20
- it('should create', () => {
21
- expect(component).toBeTruthy();
22
- });
23
-
24
- it('should render mat-paginator', () => {
25
- const paginator = fixture.nativeElement.querySelector('mat-paginator');
26
- expect(paginator).toBeTruthy();
27
- });
28
-
29
- it('should have default page size of 10', () => {
30
- expect(component.pageSize()).toBe(10);
31
- });
32
-
33
- it('should have default page size options', () => {
34
- expect(component.pageSizeOptions()).toEqual([5, 10, 25, 50]);
35
- });
36
-
37
- it('should show first/last buttons by default', () => {
38
- expect(component.showFirstLastButtons()).toBe(true);
39
- });
40
-
41
- it('should apply host classes', () => {
42
- expect(component.hostClasses()).toBe('mf-paginator');
43
- });
44
- });
@@ -1,52 +0,0 @@
1
- import {
2
- ChangeDetectionStrategy,
3
- Component,
4
- computed,
5
- input,
6
- output,
7
- } from '@angular/core';
8
- import { MatPaginatorModule, PageEvent } from '@angular/material/paginator';
9
-
10
- /**
11
- * Paginator de la librería ng-comps.
12
- * Envuelve Angular Material `mat-paginator` y expone una API uniforme
13
- * con look and feel de marca.
14
- */
15
- @Component({
16
- selector: 'mf-paginator',
17
- imports: [MatPaginatorModule],
18
- template: `
19
- <mat-paginator
20
- [length]="length()"
21
- [pageSize]="pageSize()"
22
- [pageIndex]="pageIndex()"
23
- [pageSizeOptions]="pageSizeOptions()"
24
- [showFirstLastButtons]="showFirstLastButtons()"
25
- [hidePageSize]="hidePageSize()"
26
- [class]="hostClasses()"
27
- (page)="mfPageChange.emit($event)"
28
- />
29
- `,
30
- styleUrl: './mf-paginator.component.css',
31
- changeDetection: ChangeDetectionStrategy.OnPush,
32
- })
33
- export class MfPaginatorComponent {
34
- /** Total de elementos */
35
- readonly length = input.required<number>();
36
- /** Tamaño de página */
37
- readonly pageSize = input(10);
38
- /** Índice de página actual */
39
- readonly pageIndex = input(0);
40
- /** Opciones de tamaño de página */
41
- readonly pageSizeOptions = input<number[]>([5, 10, 25, 50]);
42
- /** Mostrar botones de primera/última página */
43
- readonly showFirstLastButtons = input(true);
44
- /** Ocultar selector de tamaño de página */
45
- readonly hidePageSize = input(false);
46
-
47
- readonly mfPageChange = output<PageEvent>();
48
-
49
- readonly hostClasses = computed(() => {
50
- return 'mf-paginator';
51
- });
52
- }
@@ -1,2 +0,0 @@
1
- export { MfProgressBarComponent } from './mf-progress-bar.component';
2
- export type { MfProgressBarMode, MfProgressBarColor } from './mf-progress-bar.component';
@@ -1,53 +0,0 @@
1
- :host {
2
- display: block;
3
- }
4
-
5
- /* ── Wrapper ───────────────────────────────────────────────────── */
6
- .mf-progress-bar__wrapper {
7
- display: flex;
8
- flex-direction: column;
9
- gap: var(--mf-space-2);
10
- }
11
-
12
- .mf-progress-bar__label {
13
- font-family: var(--mf-font-base);
14
- font-size: var(--mf-text-sm);
15
- font-weight: var(--mf-weight-medium);
16
- color: var(--mf-color-on-surface);
17
- }
18
-
19
- .mf-progress-bar__value {
20
- font-family: var(--mf-font-base);
21
- font-size: var(--mf-text-xs);
22
- font-weight: var(--mf-weight-medium);
23
- color: var(--mf-color-neutral-600);
24
- align-self: flex-end;
25
- }
26
-
27
- /* ── Base ──────────────────────────────────────────────────────── */
28
- .mf-progress-bar {
29
- border-radius: var(--mf-radius-full) !important;
30
- overflow: hidden;
31
- }
32
-
33
- .mf-progress-bar.mat-mdc-progress-bar {
34
- --mdc-linear-progress-track-shape: 9999px;
35
- }
36
-
37
- /* ── Color: brand ─────────────────────────────────────────────── */
38
- .mf-progress-bar--brand.mat-mdc-progress-bar {
39
- --mdc-linear-progress-active-indicator-color: var(--mf-color-brand) !important;
40
- --mdc-linear-progress-track-color: var(--mf-color-primary-100) !important;
41
- }
42
-
43
- /* ── Color: accent ────────────────────────────────────────────── */
44
- .mf-progress-bar--accent.mat-mdc-progress-bar {
45
- --mdc-linear-progress-active-indicator-color: var(--mf-color-accent-500) !important;
46
- --mdc-linear-progress-track-color: var(--mf-color-accent-300) !important;
47
- }
48
-
49
- /* ── Color: warn ──────────────────────────────────────────────── */
50
- .mf-progress-bar--warn.mat-mdc-progress-bar {
51
- --mdc-linear-progress-active-indicator-color: var(--mf-color-error-500) !important;
52
- --mdc-linear-progress-track-color: #fecaca !important;
53
- }
@@ -1,65 +0,0 @@
1
- import { ComponentFixture, TestBed } from '@angular/core/testing';
2
- import { MfProgressBarComponent } from './mf-progress-bar.component';
3
- import { NoopAnimationsModule } from '@angular/platform-browser/animations';
4
-
5
- describe('MfProgressBarComponent', () => {
6
- let fixture: ComponentFixture<MfProgressBarComponent>;
7
- let component: MfProgressBarComponent;
8
-
9
- beforeEach(async () => {
10
- await TestBed.configureTestingModule({
11
- imports: [MfProgressBarComponent, NoopAnimationsModule],
12
- }).compileComponents();
13
-
14
- fixture = TestBed.createComponent(MfProgressBarComponent);
15
- component = fixture.componentInstance;
16
- fixture.detectChanges();
17
- });
18
-
19
- it('should create', () => {
20
- expect(component).toBeTruthy();
21
- });
22
-
23
- it('should render mat-progress-bar', () => {
24
- const bar = fixture.nativeElement.querySelector('mat-progress-bar');
25
- expect(bar).toBeTruthy();
26
- });
27
-
28
- it('should apply brand class by default', () => {
29
- expect(component.hostClasses()).toContain('mf-progress-bar--brand');
30
- });
31
-
32
- it('should apply accent class when color is accent', () => {
33
- fixture.componentRef.setInput('color', 'accent');
34
- expect(component.hostClasses()).toContain('mf-progress-bar--accent');
35
- });
36
-
37
- it('should apply warn class when color is warn', () => {
38
- fixture.componentRef.setInput('color', 'warn');
39
- expect(component.hostClasses()).toContain('mf-progress-bar--warn');
40
- });
41
-
42
- it('should render label when provided', () => {
43
- fixture.componentRef.setInput('label', 'Loading...');
44
- fixture.detectChanges();
45
- const label = fixture.nativeElement.querySelector('.mf-progress-bar__label');
46
- expect(label?.textContent).toContain('Loading...');
47
- });
48
-
49
- it('should show percentage when showValue is true and mode is determinate', () => {
50
- fixture.componentRef.setInput('mode', 'determinate');
51
- fixture.componentRef.setInput('value', 75);
52
- fixture.componentRef.setInput('showValue', true);
53
- fixture.detectChanges();
54
- const valueEl = fixture.nativeElement.querySelector('.mf-progress-bar__value');
55
- expect(valueEl?.textContent).toContain('75%');
56
- });
57
-
58
- it('should not show percentage in indeterminate mode', () => {
59
- fixture.componentRef.setInput('mode', 'indeterminate');
60
- fixture.componentRef.setInput('showValue', true);
61
- fixture.detectChanges();
62
- const valueEl = fixture.nativeElement.querySelector('.mf-progress-bar__value');
63
- expect(valueEl).toBeFalsy();
64
- });
65
- });