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,227 +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 {
14
- ControlValueAccessor,
15
- NG_VALUE_ACCESSOR,
16
- NgControl,
17
- } from '@angular/forms';
18
- import { ErrorStateMatcher } from '@angular/material/core';
19
- import { MatFormFieldModule } from '@angular/material/form-field';
20
- import { MatInputModule } from '@angular/material/input';
21
- import {
22
- createUniqueId,
23
- hasAccessibleName,
24
- mergeAriaIds,
25
- warnInDev,
26
- } from '../../a11y';
27
-
28
- export type MfTextareaSize = 'sm' | 'md' | 'lg';
29
- export type MfTextareaResize = 'none' | 'vertical' | 'horizontal' | 'both';
30
-
31
- /**
32
- * Textarea de la librería ng-comps.
33
- * Envuelve Angular Material `matInput` con textarea y expone una API uniforme
34
- * con look and feel de marca.
35
- */
36
- @Component({
37
- selector: 'mf-textarea',
38
- imports: [MatInputModule, MatFormFieldModule],
39
- providers: [
40
- {
41
- provide: NG_VALUE_ACCESSOR,
42
- useExisting: forwardRef(() => MfTextareaComponent),
43
- multi: true,
44
- },
45
- ],
46
- template: `
47
- <mat-form-field [appearance]="'outline'" [class]="hostClasses()">
48
- @if (label()) {
49
- <mat-label>{{ label() }}</mat-label>
50
- }
51
- <textarea
52
- matInput
53
- [id]="controlId()"
54
- [placeholder]="placeholder()"
55
- [disabled]="isDisabled()"
56
- [readonly]="readonly()"
57
- [required]="required()"
58
- [rows]="rows()"
59
- [attr.maxlength]="maxLength() ?? null"
60
- [value]="internalValue()"
61
- [errorStateMatcher]="errorStateMatcher"
62
- [attr.aria-label]="resolvedAriaLabel()"
63
- [attr.aria-labelledby]="ariaLabelledby() || null"
64
- [attr.aria-describedby]="describedBy()"
65
- [attr.aria-invalid]="isInvalid() ? 'true' : null"
66
- [attr.aria-required]="required() ? 'true' : null"
67
- (input)="onInput($event)"
68
- (blur)="onBlur()"
69
- ></textarea>
70
- @if (hint()) {
71
- <mat-hint [attr.id]="hintId()">{{ hint() }}</mat-hint>
72
- }
73
- @if (maxLength()) {
74
- <mat-hint [attr.id]="counterId()" align="end">
75
- {{ currentLength() }} / {{ maxLength() }}
76
- </mat-hint>
77
- }
78
- </mat-form-field>
79
- @if (error()) {
80
- <p class="mf-textarea__error" [attr.id]="errorId()" role="alert">{{ error() }}</p>
81
- }
82
- `,
83
- styleUrl: './mf-textarea.component.css',
84
- changeDetection: ChangeDetectionStrategy.OnPush,
85
- })
86
- export class MfTextareaComponent implements ControlValueAccessor {
87
- private readonly cdr = inject(ChangeDetectorRef);
88
- private readonly ngControl = inject(NgControl, { self: true, optional: true });
89
- private readonly generatedId = createUniqueId('mf-textarea');
90
- private readonly disabledFromForm = signal(false);
91
- protected readonly internalValue = signal('');
92
- private onControlChange: (value: string) => void = () => undefined;
93
- private onControlTouched: () => void = () => undefined;
94
- readonly errorStateMatcher: ErrorStateMatcher = {
95
- isErrorState: (control) =>
96
- Boolean(
97
- this.error() || (control?.invalid && (control.touched || control.dirty)),
98
- ),
99
- };
100
-
101
- /** ID del control */
102
- readonly id = input<string | undefined>(undefined);
103
- /** Etiqueta */
104
- readonly label = input<string | undefined>(undefined);
105
- /** Etiqueta accesible alternativa cuando no existe label visible */
106
- readonly ariaLabel = input<string | undefined>(undefined);
107
- /** Referencia externa a elementos que etiquetan el control */
108
- readonly ariaLabelledby = input<string | undefined>(undefined);
109
- /** Referencia externa a elementos descriptivos adicionales */
110
- readonly ariaDescribedby = input<string | undefined>(undefined);
111
- /** Placeholder */
112
- readonly placeholder = input<string>('');
113
- /** Valor */
114
- readonly value = input<string>('');
115
- /** Filas visibles */
116
- readonly rows = input(4);
117
- /** Tamaño */
118
- readonly size = input<MfTextareaSize>('md');
119
- /** Máximo de caracteres */
120
- readonly maxLength = input<number | undefined>(undefined);
121
- /** Deshabilitado */
122
- readonly disabled = input(false);
123
- /** Solo lectura */
124
- readonly readonly = input(false);
125
- /** Requerido */
126
- readonly required = input(false);
127
- /** Texto de ayuda */
128
- readonly hint = input<string | undefined>(undefined);
129
- /** Mensaje de error */
130
- readonly error = input<string | undefined>(undefined);
131
- /** Resize */
132
- readonly resize = input<MfTextareaResize>('vertical');
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-textarea 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 counterId = computed(() =>
165
- this.maxLength() ? `${this.controlId()}-count` : null,
166
- );
167
- readonly describedBy = computed(() =>
168
- mergeAriaIds(
169
- this.ariaDescribedby(),
170
- this.hintId(),
171
- this.errorId(),
172
- this.counterId(),
173
- ),
174
- );
175
- readonly resolvedAriaLabel = computed(() =>
176
- this.label() ? null : this.ariaLabel() ?? null,
177
- );
178
- readonly isDisabled = computed(
179
- () => this.disabled() || this.disabledFromForm(),
180
- );
181
- readonly currentLength = computed(() => this.internalValue().length);
182
-
183
- readonly hostClasses = computed(() => {
184
- return [
185
- 'mf-textarea',
186
- `mf-textarea--${this.size()}`,
187
- `mf-textarea--resize-${this.resize()}`,
188
- ].join(' ');
189
- });
190
-
191
- writeValue(value: string | null): void {
192
- this.internalValue.set(value ?? '');
193
- this.cdr.markForCheck();
194
- }
195
-
196
- registerOnChange(fn: (value: string) => void): void {
197
- this.onControlChange = fn;
198
- }
199
-
200
- registerOnTouched(fn: () => void): void {
201
- this.onControlTouched = fn;
202
- }
203
-
204
- setDisabledState(isDisabled: boolean): void {
205
- this.disabledFromForm.set(isDisabled);
206
- this.cdr.markForCheck();
207
- }
208
-
209
- isInvalid(): boolean {
210
- const control = this.ngControl?.control;
211
- return Boolean(
212
- this.error() || (control?.invalid && (control.touched || control.dirty)),
213
- );
214
- }
215
-
216
- onInput(event: Event): void {
217
- const value = (event.target as HTMLTextAreaElement).value;
218
- this.internalValue.set(value);
219
- this.onControlChange(value);
220
- this.mfInput.emit(value);
221
- }
222
-
223
- onBlur(): void {
224
- this.onControlTouched();
225
- this.mfBlur.emit();
226
- }
227
- }
@@ -1,2 +0,0 @@
1
- export { MfToolbarComponent } from './mf-toolbar.component';
2
- export type { MfToolbarVariant } from './mf-toolbar.component';
@@ -1,77 +0,0 @@
1
- :host {
2
- display: block;
3
- }
4
-
5
- /* ── Base ──────────────────────────────────────────────────────── */
6
- .mf-toolbar.mat-toolbar {
7
- font-family: var(--mf-font-base) !important;
8
- padding: 0 var(--mf-space-6) !important;
9
- min-height: 56px !important;
10
- display: flex !important;
11
- align-items: center !important;
12
- transition:
13
- background-color var(--mf-duration-base) var(--mf-ease-standard),
14
- box-shadow var(--mf-duration-base) var(--mf-ease-standard);
15
- }
16
-
17
- /* ── Variants ──────────────────────────────────────────────────── */
18
- .mf-toolbar--surface.mat-toolbar {
19
- background-color: var(--mf-color-surface) !important;
20
- color: var(--mf-color-on-surface) !important;
21
- }
22
-
23
- .mf-toolbar--brand.mat-toolbar {
24
- background-color: var(--mf-color-brand) !important;
25
- color: var(--mf-color-on-brand) !important;
26
- }
27
-
28
- .mf-toolbar--transparent.mat-toolbar {
29
- background-color: transparent !important;
30
- color: var(--mf-color-on-surface) !important;
31
- }
32
-
33
- /* ── Bordered ──────────────────────────────────────────────────── */
34
- .mf-toolbar--bordered.mat-toolbar {
35
- border-bottom: 1px solid var(--mf-color-border);
36
- }
37
-
38
- .mf-toolbar--brand.mf-toolbar--bordered.mat-toolbar {
39
- border-bottom-color: rgba(255, 255, 255, 0.15);
40
- }
41
-
42
- /* ── Sticky ────────────────────────────────────────────────────── */
43
- .mf-toolbar--sticky.mat-toolbar {
44
- position: sticky;
45
- top: 0;
46
- z-index: 1000;
47
- }
48
-
49
- /* ── Elevated ──────────────────────────────────────────────────── */
50
- .mf-toolbar--elevated.mat-toolbar {
51
- box-shadow: var(--mf-shadow-sm) !important;
52
- }
53
-
54
- /* ── Layout ────────────────────────────────────────────────────── */
55
- .mf-toolbar__start {
56
- display: flex;
57
- align-items: center;
58
- gap: var(--mf-space-3);
59
- }
60
-
61
- .mf-toolbar__spacer {
62
- flex: 1;
63
- }
64
-
65
- .mf-toolbar__end {
66
- display: flex;
67
- align-items: center;
68
- gap: var(--mf-space-2);
69
- }
70
-
71
- /* ── Title ─────────────────────────────────────────────────────── */
72
- .mf-toolbar__title {
73
- font-family: var(--mf-font-display) !important;
74
- font-size: var(--mf-text-lg) !important;
75
- font-weight: var(--mf-weight-bold) !important;
76
- letter-spacing: -0.01em;
77
- }
@@ -1,56 +0,0 @@
1
- import {
2
- ChangeDetectionStrategy,
3
- Component,
4
- computed,
5
- input,
6
- } from '@angular/core';
7
- import { MatToolbarModule } from '@angular/material/toolbar';
8
-
9
- export type MfToolbarVariant = 'surface' | 'brand' | 'transparent';
10
-
11
- /**
12
- * Toolbar de la librería ng-comps.
13
- * Envuelve Angular Material `mat-toolbar` y expone una API uniforme
14
- * con look and feel de marca. Usa content projection para acciones.
15
- */
16
- @Component({
17
- selector: 'mf-toolbar',
18
- imports: [MatToolbarModule],
19
- template: `
20
- <mat-toolbar [class]="hostClasses()">
21
- <div class="mf-toolbar__start">
22
- @if (title()) {
23
- <span class="mf-toolbar__title">{{ title() }}</span>
24
- }
25
- <ng-content select="[mfToolbarStart]" />
26
- </div>
27
- <div class="mf-toolbar__spacer"></div>
28
- <div class="mf-toolbar__end">
29
- <ng-content select="[mfToolbarEnd]" />
30
- <ng-content />
31
- </div>
32
- </mat-toolbar>
33
- `,
34
- styleUrl: './mf-toolbar.component.css',
35
- changeDetection: ChangeDetectionStrategy.OnPush,
36
- })
37
- export class MfToolbarComponent {
38
- /** Título que se muestra en la toolbar */
39
- readonly title = input<string | undefined>(undefined);
40
- /** Variante visual */
41
- readonly variant = input<MfToolbarVariant>('surface');
42
- /** Muestra borde inferior */
43
- readonly bordered = input(true);
44
- /** Toolbar fija en la parte superior */
45
- readonly sticky = input(false);
46
- /** Elevación sutil */
47
- readonly elevated = input(false);
48
-
49
- readonly hostClasses = computed(() => {
50
- const classes = ['mf-toolbar', `mf-toolbar--${this.variant()}`];
51
- if (this.bordered()) classes.push('mf-toolbar--bordered');
52
- if (this.sticky()) classes.push('mf-toolbar--sticky');
53
- if (this.elevated()) classes.push('mf-toolbar--elevated');
54
- return classes.join(' ');
55
- });
56
- }
@@ -1,3 +0,0 @@
1
- export { MfTooltipComponent } from './mf-tooltip.component';
2
- export { MfTooltipDirective } from './mf-tooltip.directive';
3
- export type { MfTooltipPosition } from './mf-tooltip.directive';
@@ -1,7 +0,0 @@
1
- :host {
2
- display: inline-block;
3
- }
4
-
5
- .mf-tooltip {
6
- cursor: default;
7
- }
@@ -1,37 +0,0 @@
1
- import { ComponentFixture, TestBed } from '@angular/core/testing';
2
- import { MfTooltipComponent } from './mf-tooltip.component';
3
- import { NoopAnimationsModule } from '@angular/platform-browser/animations';
4
-
5
- describe('MfTooltipComponent', () => {
6
- let fixture: ComponentFixture<MfTooltipComponent>;
7
- let component: MfTooltipComponent;
8
-
9
- beforeEach(async () => {
10
- await TestBed.configureTestingModule({
11
- imports: [MfTooltipComponent, NoopAnimationsModule],
12
- }).compileComponents();
13
-
14
- fixture = TestBed.createComponent(MfTooltipComponent);
15
- component = fixture.componentInstance;
16
- fixture.componentRef.setInput('text', 'Tooltip text');
17
- fixture.detectChanges();
18
- });
19
-
20
- it('should create', () => {
21
- expect(component).toBeTruthy();
22
- });
23
-
24
- it('should apply above position by default', () => {
25
- expect(component.hostClasses()).toContain('mf-tooltip--above');
26
- });
27
-
28
- it('should apply custom position', () => {
29
- fixture.componentRef.setInput('position', 'below');
30
- expect(component.hostClasses()).toContain('mf-tooltip--below');
31
- });
32
-
33
- it('should have correct default delays', () => {
34
- expect(component.showDelay()).toBe(200);
35
- expect(component.hideDelay()).toBe(0);
36
- });
37
- });
@@ -1,47 +0,0 @@
1
- import {
2
- ChangeDetectionStrategy,
3
- Component,
4
- computed,
5
- input,
6
- } from '@angular/core';
7
- import { MfTooltipDirective, MfTooltipPosition } from './mf-tooltip.directive';
8
-
9
- /**
10
- * Tooltip de la librería ng-comps.
11
- * Envuelve Angular Material `matTooltip` y expone una API uniforme
12
- * con look and feel de marca.
13
- */
14
- @Component({
15
- selector: 'mf-tooltip',
16
- imports: [MfTooltipDirective],
17
- template: `
18
- <span
19
- [mfTooltip]="text()"
20
- [mfTooltipPosition]="position()"
21
- [mfTooltipDisabled]="disabled()"
22
- [mfTooltipShowDelay]="showDelay()"
23
- [mfTooltipHideDelay]="hideDelay()"
24
- [class]="hostClasses()"
25
- >
26
- <ng-content />
27
- </span>
28
- `,
29
- styleUrl: './mf-tooltip.component.css',
30
- changeDetection: ChangeDetectionStrategy.OnPush,
31
- })
32
- export class MfTooltipComponent {
33
- /** Texto del tooltip */
34
- readonly text = input.required<string>();
35
- /** Posición del tooltip */
36
- readonly position = input<MfTooltipPosition>('above');
37
- /** Deshabilitado */
38
- readonly disabled = input(false);
39
- /** Delay para mostrar (ms) */
40
- readonly showDelay = input(200);
41
- /** Delay para ocultar (ms) */
42
- readonly hideDelay = input(0);
43
-
44
- readonly hostClasses = computed(() => {
45
- return ['mf-tooltip', `mf-tooltip--${this.position()}`].join(' ');
46
- });
47
- }
@@ -1,22 +0,0 @@
1
- import { Directive } from '@angular/core';
2
- import { MatTooltip } from '@angular/material/tooltip';
3
-
4
- export type MfTooltipPosition = 'above' | 'below' | 'left' | 'right';
5
-
6
- @Directive({
7
- selector: '[mfTooltip]',
8
- standalone: true,
9
- hostDirectives: [
10
- {
11
- directive: MatTooltip,
12
- inputs: [
13
- 'matTooltip: mfTooltip',
14
- 'matTooltipPosition: mfTooltipPosition',
15
- 'matTooltipDisabled: mfTooltipDisabled',
16
- 'matTooltipShowDelay: mfTooltipShowDelay',
17
- 'matTooltipHideDelay: mfTooltipHideDelay',
18
- ],
19
- },
20
- ],
21
- })
22
- export class MfTooltipDirective {}
package/src/index.html DELETED
@@ -1,18 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <title>MiApp</title>
6
- <base href="/">
7
- <meta name="viewport" content="width=device-width, initial-scale=1">
8
- <link rel="icon" type="image/x-icon" href="favicon.ico">
9
- <!-- Preconnect hints for Google Fonts -->
10
- <link rel="preconnect" href="https://fonts.googleapis.com">
11
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
12
- <!-- Material Icons (Google Fonts CDN) -->
13
- <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
14
- </head>
15
- <body>
16
- <app-root></app-root>
17
- </body>
18
- </html>
package/src/main.ts DELETED
@@ -1,6 +0,0 @@
1
- import { bootstrapApplication } from '@angular/platform-browser';
2
- import { appConfig } from './app/app.config';
3
- import { App } from './app/app';
4
-
5
- bootstrapApplication(App, appConfig)
6
- .catch((err) => console.error(err));
package/src/public-api.ts DELETED
@@ -1,31 +0,0 @@
1
- export * from './app/components/accordion';
2
- export * from './app/components/alert';
3
- export * from './app/components/autocomplete';
4
- export * from './app/components/avatar';
5
- export * from './app/components/badge';
6
- export * from './app/components/breadcrumb';
7
- export * from './app/components/button';
8
- export * from './app/components/card';
9
- export * from './app/components/checkbox';
10
- export * from './app/components/chip';
11
- export * from './app/components/datepicker';
12
- export * from './app/components/dialog';
13
- export * from './app/components/divider';
14
- export * from './app/components/form-field';
15
- export * from './app/components/grid-list';
16
- export * from './app/components/icon';
17
- export * from './app/components/input';
18
- export * from './app/components/menu';
19
- export * from './app/components/paginator';
20
- export * from './app/components/progress-bar';
21
- export * from './app/components/progress-spinner';
22
- export * from './app/components/radio-button';
23
- export * from './app/components/select';
24
- export * from './app/components/sidenav';
25
- export * from './app/components/slide-toggle';
26
- export * from './app/components/snackbar';
27
- export * from './app/components/table';
28
- export * from './app/components/tabs';
29
- export * from './app/components/textarea';
30
- export * from './app/components/toolbar';
31
- export * from './app/components/tooltip';
@@ -1,72 +0,0 @@
1
- import { Meta } from '@storybook/addon-docs/blocks';
2
-
3
- <Meta title="About" />
4
-
5
- # ng-comps: Quick Start Guide
6
-
7
- [ng-comps](https://www.npmjs.com/package/ng-comps) is an Angular component library with a custom design layer built on top of Angular Material.
8
-
9
- The library was created by [Manuel Ferrer](https://manuelferrer.vercel.app/).
10
-
11
- <div style={{ border: '1px solid #e5e7eb', borderRadius: '8px', padding: '12px', display: 'flex', gap: '12px', alignItems: 'center', maxWidth: '560px' }}>
12
- <div style={{ flex: '0 0 64px', height: '64px', background: '#f3f4f6', borderRadius: '6px', display: 'flex', alignItems: 'center', justifyContent: 'center', fontWeight: 700, color: '#111827' }}>PE</div>
13
- <div>
14
- <a href="https://preguntas-entrevista.vercel.app/" target="_blank" rel="noreferrer" style={{ fontWeight: 600, color: '#111827', textDecoration: 'none' }}>preguntas-entrevista</a>
15
- <div style={{ color: '#6b7280', fontSize: '0.9rem' }}>Sample project built with ng-comps.</div>
16
- </div>
17
- </div>
18
-
19
- ## 1) Install
20
-
21
- ```bash
22
- npm i ng-comps
23
- ```
24
-
25
- ## 2) Load global styles
26
-
27
- In your app global stylesheet, for example `src/styles.css`, import:
28
-
29
- ```css
30
- @import 'ng-comps/theme/tokens.css';
31
- @import 'ng-comps/styles.css';
32
- ```
33
-
34
- ## 3) Use components (Standalone)
35
-
36
- Import only the components you actually use to maximize tree-shaking:
37
-
38
- ```ts
39
- import { Component } from '@angular/core';
40
- import { MfButtonComponent, MfInputComponent } from 'ng-comps';
41
-
42
- @Component({
43
- selector: 'app-example',
44
- imports: [MfButtonComponent, MfInputComponent],
45
- template: `
46
- <mf-input label="Email" placeholder="you@company.com" />
47
- <mf-button label="Save" variant="filled" />
48
- `,
49
- })
50
- export class ExampleComponent {}
51
- ```
52
-
53
- ## 4) Consumer project requirements
54
-
55
- `ng-comps` uses `peerDependencies`, so your project must have Angular and RxJS installed.
56
-
57
- Recommended:
58
-
59
- - Angular `^21`
60
- - RxJS `~7.8`
61
-
62
- ## 5) Integration best practices
63
-
64
- - Import components directly from `ng-comps` as needed.
65
- - Keep your global styles aligned with the library `tokens.css`.
66
- - Use Storybook as the visual and API reference for each component.
67
-
68
- ## Useful references in this repo
69
-
70
- - Design tokens: `src/theme/tokens.css`
71
- - Material theme: `src/theme/material-theme.scss`
72
- - Base example: `src/app/components/button/mf-button.component.ts`
@@ -1,59 +0,0 @@
1
- import { Meta } from '@storybook/addon-docs/blocks';
2
-
3
- <Meta title="Accessibility" />
4
-
5
- # Accessibility in ng-comps v1
6
-
7
- `ng-comps` v1 is documented as an accessibility-first component library, not as an automatic compliance shortcut.
8
-
9
- ## Contract summary
10
-
11
- - Components are designed around WCAG 2.2 AA implementation targets.
12
- - Procurement evidence is documented against EN 301 549 v3.2.1 / WCAG 2.1 AA until the next EN revision is formally published.
13
- - Final application conformance still depends on consumer content, integration, routing, async state changes, and theme overrides.
14
-
15
- Read the full repository contract in `ACCESSIBILITY.md` at the project root.
16
-
17
- ## Defaults enforced by the library
18
-
19
- - Accessible naming inputs on interactive controls.
20
- - Visible global `:focus-visible` styles.
21
- - Form controls wired to hints, counters, and errors through `aria-describedby`.
22
- - Safe dialog defaults through `MfDialogService`.
23
- - Explicit row actions in `MfTableComponent`.
24
- - Snackbar politeness and announcement options.
25
- - Tooltip directive usage on the real host element with `mfTooltip`.
26
-
27
- ## What to review manually
28
-
29
- - Keyboard-only navigation
30
- - Focus visibility
31
- - 200% zoom
32
- - Reflow at 320 CSS px equivalent
33
- - NVDA on Windows
34
- - VoiceOver on macOS
35
- - Contrast after any token override
36
-
37
- ## Storybook contract stories
38
-
39
- - [Dialog focus contract](?path=/story/accessibility-contracts--dialog-focus-contract)
40
- - [Menu keyboard contract](?path=/story/accessibility-contracts--menu-keyboard-contract)
41
- - [Select keyboard contract](?path=/story/accessibility-contracts--select-keyboard-contract)
42
- - [Autocomplete keyboard contract](?path=/story/accessibility-contracts--autocomplete-keyboard-contract)
43
- - [Datepicker keyboard contract](?path=/story/accessibility-contracts--datepicker-keyboard-contract)
44
- - [Table explicit action contract](?path=/story/accessibility-contracts--table-explicit-action-contract)
45
- - [Sidenav keyboard contract](?path=/story/accessibility-contracts--sidenav-keyboard-contract)
46
-
47
- ## Correct and incorrect usage
48
-
49
- Use the contract stories as the reference implementation. Anti-pattern stories intentionally document what not to ship:
50
-
51
- - missing labels on icon-only triggers
52
- - placeholders used as labels
53
- - clickable rows instead of explicit actions
54
- - menus and dialogs that do not restore focus
55
- - theme overrides that remove focus rings or break contrast
56
-
57
- ## Procurement note
58
-
59
- This library now provides a stronger technical base for WCAG and EN 301 549 claims, plus evidence-oriented docs and tests. It does not replace product-level accessibility validation or a formal VPAT process.