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.
- package/fesm2022/ng-comps.mjs +4254 -0
- package/package.json +54 -58
- package/src/styles.css +54 -0
- package/types/ng-comps.d.ts +1348 -0
- package/.editorconfig +0 -17
- package/.github/copilot-instructions.md +0 -55
- package/.github/workflows/ci.yml +0 -29
- package/.prettierrc +0 -12
- package/.storybook/main.ts +0 -21
- package/.storybook/preview.ts +0 -27
- package/.storybook/tsconfig.doc.json +0 -10
- package/.storybook/tsconfig.json +0 -15
- package/.storybook/typings.d.ts +0 -4
- package/.vscode/extensions.json +0 -4
- package/.vscode/launch.json +0 -20
- package/.vscode/mcp.json +0 -9
- package/.vscode/tasks.json +0 -42
- package/ACCESSIBILITY.md +0 -127
- package/angular.json +0 -106
- package/documentation.json +0 -13394
- package/ng-package.json +0 -27
- package/public/favicon.ico +0 -0
- package/scripts/prepare-package.mjs +0 -80
- package/src/app/a11y/accessibility.utils.ts +0 -35
- package/src/app/a11y/index.ts +0 -6
- package/src/app/accessibility/ng-comps.a11y.spec.ts +0 -108
- package/src/app/app.config.ts +0 -11
- package/src/app/app.css +0 -107
- package/src/app/app.html +0 -48
- package/src/app/app.routes.ts +0 -3
- package/src/app/app.spec.ts +0 -23
- package/src/app/app.ts +0 -10
- package/src/app/components/accordion/index.ts +0 -2
- package/src/app/components/accordion/mf-accordion.component.css +0 -38
- package/src/app/components/accordion/mf-accordion.component.spec.ts +0 -48
- package/src/app/components/accordion/mf-accordion.component.ts +0 -53
- package/src/app/components/alert/index.ts +0 -2
- package/src/app/components/alert/mf-alert.component.css +0 -100
- package/src/app/components/alert/mf-alert.component.spec.ts +0 -59
- package/src/app/components/alert/mf-alert.component.ts +0 -68
- package/src/app/components/autocomplete/index.ts +0 -5
- package/src/app/components/autocomplete/mf-autocomplete.component.css +0 -105
- package/src/app/components/autocomplete/mf-autocomplete.component.spec.ts +0 -116
- package/src/app/components/autocomplete/mf-autocomplete.component.ts +0 -307
- package/src/app/components/avatar/index.ts +0 -2
- package/src/app/components/avatar/mf-avatar.component.css +0 -27
- package/src/app/components/avatar/mf-avatar.component.spec.ts +0 -49
- package/src/app/components/avatar/mf-avatar.component.ts +0 -99
- package/src/app/components/badge/index.ts +0 -2
- package/src/app/components/badge/mf-badge.component.css +0 -32
- package/src/app/components/badge/mf-badge.component.spec.ts +0 -40
- package/src/app/components/badge/mf-badge.component.ts +0 -105
- package/src/app/components/breadcrumb/index.ts +0 -2
- package/src/app/components/breadcrumb/mf-breadcrumb.component.css +0 -61
- package/src/app/components/breadcrumb/mf-breadcrumb.component.spec.ts +0 -61
- package/src/app/components/breadcrumb/mf-breadcrumb.component.ts +0 -75
- package/src/app/components/button/index.ts +0 -2
- package/src/app/components/button/mf-button.component.css +0 -136
- package/src/app/components/button/mf-button.component.ts +0 -174
- package/src/app/components/card/index.ts +0 -2
- package/src/app/components/card/mf-card.component.css +0 -82
- package/src/app/components/card/mf-card.component.ts +0 -59
- package/src/app/components/checkbox/index.ts +0 -1
- package/src/app/components/checkbox/mf-checkbox.component.css +0 -75
- package/src/app/components/checkbox/mf-checkbox.component.ts +0 -187
- package/src/app/components/chip/index.ts +0 -2
- package/src/app/components/chip/mf-chip.component.css +0 -69
- package/src/app/components/chip/mf-chip.component.spec.ts +0 -47
- package/src/app/components/chip/mf-chip.component.ts +0 -77
- package/src/app/components/datepicker/index.ts +0 -2
- package/src/app/components/datepicker/mf-datepicker.component.css +0 -102
- package/src/app/components/datepicker/mf-datepicker.component.spec.ts +0 -69
- package/src/app/components/datepicker/mf-datepicker.component.ts +0 -233
- package/src/app/components/dialog/index.ts +0 -3
- package/src/app/components/dialog/mf-dialog.component.css +0 -73
- package/src/app/components/dialog/mf-dialog.component.ts +0 -160
- package/src/app/components/dialog/mf-dialog.service.spec.ts +0 -61
- package/src/app/components/dialog/mf-dialog.service.ts +0 -52
- package/src/app/components/divider/index.ts +0 -2
- package/src/app/components/divider/mf-divider.component.css +0 -38
- package/src/app/components/divider/mf-divider.component.spec.ts +0 -40
- package/src/app/components/divider/mf-divider.component.ts +0 -44
- package/src/app/components/form-field/index.ts +0 -1
- package/src/app/components/form-field/mf-form-field.component.css +0 -51
- package/src/app/components/form-field/mf-form-field.component.ts +0 -74
- package/src/app/components/grid-list/index.ts +0 -2
- package/src/app/components/grid-list/mf-grid-list.component.css +0 -47
- package/src/app/components/grid-list/mf-grid-list.component.spec.ts +0 -57
- package/src/app/components/grid-list/mf-grid-list.component.ts +0 -68
- package/src/app/components/icon/index.ts +0 -2
- package/src/app/components/icon/mf-icon.component.css +0 -56
- package/src/app/components/icon/mf-icon.component.ts +0 -41
- package/src/app/components/input/index.ts +0 -2
- package/src/app/components/input/mf-input.component.css +0 -105
- package/src/app/components/input/mf-input.component.ts +0 -217
- package/src/app/components/menu/index.ts +0 -2
- package/src/app/components/menu/mf-menu.component.css +0 -31
- package/src/app/components/menu/mf-menu.component.spec.ts +0 -49
- package/src/app/components/menu/mf-menu.component.ts +0 -66
- package/src/app/components/paginator/index.ts +0 -1
- package/src/app/components/paginator/mf-paginator.component.css +0 -32
- package/src/app/components/paginator/mf-paginator.component.spec.ts +0 -44
- package/src/app/components/paginator/mf-paginator.component.ts +0 -52
- package/src/app/components/progress-bar/index.ts +0 -2
- package/src/app/components/progress-bar/mf-progress-bar.component.css +0 -53
- package/src/app/components/progress-bar/mf-progress-bar.component.spec.ts +0 -65
- package/src/app/components/progress-bar/mf-progress-bar.component.ts +0 -79
- package/src/app/components/progress-spinner/index.ts +0 -2
- package/src/app/components/progress-spinner/mf-progress-spinner.component.css +0 -38
- package/src/app/components/progress-spinner/mf-progress-spinner.component.spec.ts +0 -59
- package/src/app/components/progress-spinner/mf-progress-spinner.component.ts +0 -81
- package/src/app/components/radio-button/index.ts +0 -2
- package/src/app/components/radio-button/mf-radio-button.component.css +0 -86
- package/src/app/components/radio-button/mf-radio-button.component.spec.ts +0 -55
- package/src/app/components/radio-button/mf-radio-button.component.ts +0 -219
- package/src/app/components/select/index.ts +0 -2
- package/src/app/components/select/mf-select.component.css +0 -121
- package/src/app/components/select/mf-select.component.spec.ts +0 -108
- package/src/app/components/select/mf-select.component.ts +0 -252
- package/src/app/components/sidenav/index.ts +0 -2
- package/src/app/components/sidenav/mf-sidenav.component.css +0 -168
- package/src/app/components/sidenav/mf-sidenav.component.spec.ts +0 -57
- package/src/app/components/sidenav/mf-sidenav.component.ts +0 -126
- package/src/app/components/slide-toggle/index.ts +0 -1
- package/src/app/components/slide-toggle/mf-slide-toggle.component.css +0 -42
- package/src/app/components/slide-toggle/mf-slide-toggle.component.spec.ts +0 -43
- package/src/app/components/slide-toggle/mf-slide-toggle.component.ts +0 -188
- package/src/app/components/snackbar/index.ts +0 -2
- package/src/app/components/snackbar/mf-snackbar.service.css +0 -31
- package/src/app/components/snackbar/mf-snackbar.service.spec.ts +0 -81
- package/src/app/components/snackbar/mf-snackbar.service.ts +0 -77
- package/src/app/components/table/index.ts +0 -2
- package/src/app/components/table/mf-table.component.css +0 -68
- package/src/app/components/table/mf-table.component.spec.ts +0 -76
- package/src/app/components/table/mf-table.component.ts +0 -117
- package/src/app/components/tabs/index.ts +0 -2
- package/src/app/components/tabs/mf-tabs.component.css +0 -31
- package/src/app/components/tabs/mf-tabs.component.spec.ts +0 -50
- package/src/app/components/tabs/mf-tabs.component.ts +0 -62
- package/src/app/components/textarea/index.ts +0 -2
- package/src/app/components/textarea/mf-textarea.component.css +0 -48
- package/src/app/components/textarea/mf-textarea.component.spec.ts +0 -55
- package/src/app/components/textarea/mf-textarea.component.ts +0 -227
- package/src/app/components/toolbar/index.ts +0 -2
- package/src/app/components/toolbar/mf-toolbar.component.css +0 -77
- package/src/app/components/toolbar/mf-toolbar.component.ts +0 -56
- package/src/app/components/tooltip/index.ts +0 -3
- package/src/app/components/tooltip/mf-tooltip.component.css +0 -7
- package/src/app/components/tooltip/mf-tooltip.component.spec.ts +0 -37
- package/src/app/components/tooltip/mf-tooltip.component.ts +0 -47
- package/src/app/components/tooltip/mf-tooltip.directive.ts +0 -22
- package/src/index.html +0 -18
- package/src/main.ts +0 -6
- package/src/public-api.ts +0 -31
- package/src/stories/About.mdx +0 -72
- package/src/stories/Accessibility.mdx +0 -59
- package/src/stories/Welcome.mdx +0 -26
- package/src/stories/assets/accessibility.png +0 -0
- package/src/stories/assets/accessibility.svg +0 -1
- package/src/stories/assets/addon-library.png +0 -0
- package/src/stories/assets/assets.png +0 -0
- package/src/stories/assets/avif-test-image.avif +0 -0
- package/src/stories/assets/context.png +0 -0
- package/src/stories/assets/discord.svg +0 -1
- package/src/stories/assets/docs.png +0 -0
- package/src/stories/assets/figma-plugin.png +0 -0
- package/src/stories/assets/github.svg +0 -1
- package/src/stories/assets/share.png +0 -0
- package/src/stories/assets/styling.png +0 -0
- package/src/stories/assets/testing.png +0 -0
- package/src/stories/assets/theming.png +0 -0
- package/src/stories/assets/tutorials.svg +0 -1
- package/src/stories/assets/youtube.svg +0 -1
- package/src/stories/mf-a11y-contracts.stories.ts +0 -472
- package/src/stories/mf-autocomplete.stories.ts +0 -194
- package/src/stories/mf-button.stories.ts +0 -152
- package/src/stories/mf-card.stories.ts +0 -147
- package/src/stories/mf-checkbox.stories.ts +0 -88
- package/src/stories/mf-datepicker.stories.ts +0 -118
- package/src/stories/mf-dialog.stories.ts +0 -159
- package/src/stories/mf-form-field.stories.ts +0 -108
- package/src/stories/mf-grid-list.stories.ts +0 -104
- package/src/stories/mf-icon.stories.ts +0 -133
- package/src/stories/mf-input.stories.ts +0 -158
- package/src/stories/mf-menu.stories.ts +0 -71
- package/src/stories/mf-progress-bar.stories.ts +0 -119
- package/src/stories/mf-progress-spinner.stories.ts +0 -124
- package/src/stories/mf-radio-button.stories.ts +0 -111
- package/src/stories/mf-select.stories.ts +0 -184
- package/src/stories/mf-sidenav.stories.ts +0 -331
- package/src/stories/mf-table.stories.ts +0 -80
- package/src/stories/mf-toolbar.stories.ts +0 -112
- package/src/stories/user.ts +0 -3
- package/tsconfig.app.json +0 -15
- package/tsconfig.json +0 -33
- package/tsconfig.spec.json +0 -15
- 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,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,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
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';
|
package/src/stories/About.mdx
DELETED
|
@@ -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.
|