ngx-com 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -3
- package/fesm2022/ngx-com-components-form-field.mjs +35 -4
- package/fesm2022/ngx-com-components-form-field.mjs.map +1 -1
- package/fesm2022/ngx-com-theme.mjs +234 -0
- package/fesm2022/ngx-com-theme.mjs.map +1 -0
- package/package.json +5 -1
- package/types/ngx-com-components-form-field.d.ts +31 -1
- package/types/ngx-com-theme.d.ts +125 -0
package/README.md
CHANGED
|
@@ -64,12 +64,19 @@ Or import individual files for finer control:
|
|
|
64
64
|
| `animations.css` | Keyframe animations used by components |
|
|
65
65
|
| `utilities.css` | Utility classes |
|
|
66
66
|
|
|
67
|
-
Switch themes at runtime by setting `data-theme` on the `<html>` element
|
|
67
|
+
Switch themes at runtime by setting `data-theme` on the `<html>` element.
|
|
68
|
+
The library provides a theme service that handles this automatically:
|
|
68
69
|
|
|
69
|
-
```
|
|
70
|
-
|
|
70
|
+
```typescript
|
|
71
|
+
import { provideComTheme } from 'ngx-com/theme';
|
|
72
|
+
|
|
73
|
+
export const appConfig: ApplicationConfig = {
|
|
74
|
+
providers: [provideComTheme()],
|
|
75
|
+
};
|
|
71
76
|
```
|
|
72
77
|
|
|
78
|
+
See the [Theme service](#theme-service) section for details.
|
|
79
|
+
|
|
73
80
|
### 2. Add Tailwind source for ngx-com
|
|
74
81
|
|
|
75
82
|
Library components use Tailwind utility classes in their templates. Tell
|
|
@@ -164,6 +171,41 @@ import { ComDropdown, ComDropdownOption } from 'ngx-com/components/dropdown';
|
|
|
164
171
|
| Toast | `ngx-com/components/toast` | Toast notification service |
|
|
165
172
|
| Tooltip | `ngx-com/components/tooltip` | Hover/focus tooltip |
|
|
166
173
|
|
|
174
|
+
## Services
|
|
175
|
+
|
|
176
|
+
### Theme service
|
|
177
|
+
|
|
178
|
+
Import path: `ngx-com/theme`
|
|
179
|
+
|
|
180
|
+
SSR-safe theme management with localStorage persistence and system
|
|
181
|
+
`prefers-color-scheme` detection. Applies the active theme via a `data-theme`
|
|
182
|
+
attribute on the document element.
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
import { provideComTheme, ComTheme } from 'ngx-com/theme';
|
|
186
|
+
|
|
187
|
+
// 1. Configure in app providers (zero-config works out of the box)
|
|
188
|
+
providers: [provideComTheme()]
|
|
189
|
+
|
|
190
|
+
// 2. Inject and use
|
|
191
|
+
readonly theme = inject(ComTheme);
|
|
192
|
+
this.theme.setTheme('dark'); // explicit override, persisted to localStorage
|
|
193
|
+
this.theme.clearPreference(); // revert to following system preference
|
|
194
|
+
this.theme.theme(); // current theme (Signal<string>)
|
|
195
|
+
this.theme.isAutomatic(); // true when following system preference
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Configuration options:
|
|
199
|
+
|
|
200
|
+
| Option | Type | Default | Description |
|
|
201
|
+
| --- | --- | --- | --- |
|
|
202
|
+
| `defaultTheme` | `string` | `'light'` | Fallback when no stored/system preference |
|
|
203
|
+
| `storageKey` | `string \| null` | `'com-theme'` | localStorage key (`null` to disable) |
|
|
204
|
+
| `darkSchemeTheme` | `string \| null` | `'dark'` | Theme for `prefers-color-scheme: dark` (`null` to disable) |
|
|
205
|
+
| `lightSchemeTheme` | `string` | `defaultTheme` | Theme for light system preference |
|
|
206
|
+
| `followSystemPreference` | `boolean` | `true` | Watch for live system preference changes |
|
|
207
|
+
| `attribute` | `string` | `'data-theme'` | HTML attribute applied to `documentElement` |
|
|
208
|
+
|
|
167
209
|
## Utilities
|
|
168
210
|
|
|
169
211
|
General-purpose utilities are available from `ngx-com/utils`.
|
|
@@ -35,15 +35,32 @@ let nextId$3 = 0;
|
|
|
35
35
|
* The form field automatically associates this label with the inner control
|
|
36
36
|
* and positions it appropriately based on appearance and float state.
|
|
37
37
|
*
|
|
38
|
+
* The label renders as `inline-flex` so it can contain inline content such as
|
|
39
|
+
* icons with tooltips. Icons inside the label are visually hidden when the
|
|
40
|
+
* label floats to keep the scaled-down state clean.
|
|
41
|
+
*
|
|
38
42
|
* @tokens none
|
|
39
43
|
*
|
|
40
|
-
* @example
|
|
44
|
+
* @example Plain label
|
|
41
45
|
* ```html
|
|
42
46
|
* <com-form-field>
|
|
43
47
|
* <label comLabel>Email address</label>
|
|
44
48
|
* <input comInput formControlName="email" />
|
|
45
49
|
* </com-form-field>
|
|
46
50
|
* ```
|
|
51
|
+
*
|
|
52
|
+
* @example Label with help icon and tooltip
|
|
53
|
+
* ```html
|
|
54
|
+
* <com-form-field>
|
|
55
|
+
* <label comLabel>
|
|
56
|
+
* User Role
|
|
57
|
+
* <button type="button" aria-label="Help" comTooltip="Admins have full access.">
|
|
58
|
+
* <com-icon [img]="CircleHelpIcon" size="xs" color="muted" />
|
|
59
|
+
* </button>
|
|
60
|
+
* </label>
|
|
61
|
+
* <input comInput />
|
|
62
|
+
* </com-form-field>
|
|
63
|
+
* ```
|
|
47
64
|
*/
|
|
48
65
|
class ComLabel {
|
|
49
66
|
labelId = `com-label-${nextId$3++}`;
|
|
@@ -54,7 +71,7 @@ class ComLabel {
|
|
|
54
71
|
this._forId.set(id);
|
|
55
72
|
}
|
|
56
73
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComLabel, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
57
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.0", type: ComLabel, isStandalone: true, selector: "[comLabel]", host: { properties: { "attr.for": "forId()", "id": "labelId" } }, exportAs: ["comLabel"], ngImport: i0 });
|
|
74
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.0", type: ComLabel, isStandalone: true, selector: "[comLabel]", host: { properties: { "attr.for": "forId()", "id": "labelId" }, classAttribute: "inline-flex items-center gap-1" }, exportAs: ["comLabel"], ngImport: i0 });
|
|
58
75
|
}
|
|
59
76
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComLabel, decorators: [{
|
|
60
77
|
type: Directive,
|
|
@@ -62,6 +79,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
|
|
|
62
79
|
selector: '[comLabel]',
|
|
63
80
|
exportAs: 'comLabel',
|
|
64
81
|
host: {
|
|
82
|
+
'class': 'inline-flex items-center gap-1',
|
|
65
83
|
'[attr.for]': 'forId()',
|
|
66
84
|
'[id]': 'labelId',
|
|
67
85
|
},
|
|
@@ -788,6 +806,19 @@ const FORM_FIELD_DEFAULTS = new InjectionToken('FORM_FIELD_DEFAULTS', {
|
|
|
788
806
|
* <span comSuffix>.00</span>
|
|
789
807
|
* </com-form-field>
|
|
790
808
|
* ```
|
|
809
|
+
*
|
|
810
|
+
* @example Label with icon and tooltip
|
|
811
|
+
* ```html
|
|
812
|
+
* <com-form-field>
|
|
813
|
+
* <label comLabel>
|
|
814
|
+
* User Role
|
|
815
|
+
* <button type="button" aria-label="Help" comTooltip="Admins have full access.">
|
|
816
|
+
* <com-icon [img]="CircleHelpIcon" size="xs" color="muted" />
|
|
817
|
+
* </button>
|
|
818
|
+
* </label>
|
|
819
|
+
* <input comInput />
|
|
820
|
+
* </com-form-field>
|
|
821
|
+
* ```
|
|
791
822
|
*/
|
|
792
823
|
class ComFormField {
|
|
793
824
|
defaults = inject(FORM_FIELD_DEFAULTS, { optional: true });
|
|
@@ -925,7 +956,7 @@ class ComFormField {
|
|
|
925
956
|
<ng-content select="[comHint][align='end']" />
|
|
926
957
|
</div>
|
|
927
958
|
</div>
|
|
928
|
-
`, isInline: true, styles: ["com-form-field.com-form-field--has-label:not(.com-form-field--floating) input::placeholder,com-form-field.com-form-field--has-label:not(.com-form-field--floating) textarea::placeholder{color:transparent}com-form-field.com-form-field--has-label:not(.com-form-field--floating) com-dropdown .text-placeholder{display:none}com-form-field com-dropdown
|
|
959
|
+
`, isInline: true, styles: ["com-form-field.com-form-field--has-label:not(.com-form-field--floating) input::placeholder,com-form-field.com-form-field--has-label:not(.com-form-field--floating) textarea::placeholder{color:transparent}com-form-field.com-form-field--has-label:not(.com-form-field--floating) com-dropdown .text-placeholder{display:none}com-form-field com-dropdown,com-form-field com-datepicker,com-form-field com-date-range-picker,com-form-field com-time-picker{display:block;width:100%}com-form-field.com-form-field--has-label:not(.com-form-field--floating) com-datepicker input::placeholder,com-form-field.com-form-field--has-label:not(.com-form-field--floating) com-date-range-picker input::placeholder{color:transparent}com-form-field.com-form-field--has-label:not(.com-form-field--floating) com-time-picker span[aria-hidden],com-form-field.com-form-field--has-label:not(.com-form-field--floating) com-time-picker button{visibility:hidden}com-form-field .com-form-field__label [comtooltip]{pointer-events:auto;cursor:help}com-form-field.com-form-field--floating .com-form-field__label com-icon{visibility:hidden;width:0;overflow:hidden}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
929
960
|
}
|
|
930
961
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComFormField, decorators: [{
|
|
931
962
|
type: Component,
|
|
@@ -968,7 +999,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
|
|
|
968
999
|
'[class.com-form-field--error]': 'hasError()',
|
|
969
1000
|
'[class.com-form-field--floating]': 'shouldLabelFloat()',
|
|
970
1001
|
'[class.com-form-field--has-label]': '!!labelChild()',
|
|
971
|
-
}, styles: ["com-form-field.com-form-field--has-label:not(.com-form-field--floating) input::placeholder,com-form-field.com-form-field--has-label:not(.com-form-field--floating) textarea::placeholder{color:transparent}com-form-field.com-form-field--has-label:not(.com-form-field--floating) com-dropdown .text-placeholder{display:none}com-form-field com-dropdown
|
|
1002
|
+
}, styles: ["com-form-field.com-form-field--has-label:not(.com-form-field--floating) input::placeholder,com-form-field.com-form-field--has-label:not(.com-form-field--floating) textarea::placeholder{color:transparent}com-form-field.com-form-field--has-label:not(.com-form-field--floating) com-dropdown .text-placeholder{display:none}com-form-field com-dropdown,com-form-field com-datepicker,com-form-field com-date-range-picker,com-form-field com-time-picker{display:block;width:100%}com-form-field.com-form-field--has-label:not(.com-form-field--floating) com-datepicker input::placeholder,com-form-field.com-form-field--has-label:not(.com-form-field--floating) com-date-range-picker input::placeholder{color:transparent}com-form-field.com-form-field--has-label:not(.com-form-field--floating) com-time-picker span[aria-hidden],com-form-field.com-form-field--has-label:not(.com-form-field--floating) com-time-picker button{visibility:hidden}com-form-field .com-form-field__label [comtooltip]{pointer-events:auto;cursor:help}com-form-field.com-form-field--floating .com-form-field__label com-icon{visibility:hidden;width:0;overflow:hidden}\n"] }]
|
|
972
1003
|
}], ctorParameters: () => [], propDecorators: { control: [{ type: i0.ContentChild, args: [i0.forwardRef(() => FormFieldControl), { isSignal: true }] }], inputDirective: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ComInput), { isSignal: true }] }], labelChild: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ComLabel), { isSignal: true }] }], hintChildren: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => ComHint), { isSignal: true }] }], errorChildren: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => ComError), { isSignal: true }] }], prefixChild: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ComPrefix), { isSignal: true }] }], suffixChild: [{ type: i0.ContentChild, args: [i0.forwardRef(() => ComSuffix), { isSignal: true }] }], appearance: [{ type: i0.Input, args: [{ isSignal: true, alias: "appearance", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], floatLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "floatLabel", required: false }] }], hideRequiredMarker: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideRequiredMarker", required: false }] }], subscriptSizing: [{ type: i0.Input, args: [{ isSignal: true, alias: "subscriptSizing", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
|
|
973
1004
|
|
|
974
1005
|
// Public API for the form-field component
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngx-com-components-form-field.mjs","sources":["../../../projects/com/components/form-field/form-field-control.ts","../../../projects/com/components/form-field/label.directive.ts","../../../projects/com/components/form-field/form-field.variants.ts","../../../projects/com/components/form-field/hint.directive.ts","../../../projects/com/components/form-field/error.directive.ts","../../../projects/com/components/form-field/prefix.directive.ts","../../../projects/com/components/form-field/suffix.directive.ts","../../../projects/com/components/form-field/error-state-matcher.ts","../../../projects/com/components/form-field/input.directive.ts","../../../projects/com/components/form-field/form-field-defaults.ts","../../../projects/com/components/form-field/form-field.component.ts","../../../projects/com/components/form-field/index.ts","../../../projects/com/components/form-field/ngx-com-components-form-field.ts"],"sourcesContent":["import type { Signal } from '@angular/core';\nimport type { NgControl } from '@angular/forms';\n\n\n/**\n * Contract that any control inside a form field must implement.\n *\n * This allows the form field to read state from any inner control\n * (input, textarea, custom controls) without knowing implementation details.\n *\n * @example Implementing for a custom phone input\n * ```ts\n * @Directive({\n * selector: 'com-phone-input',\n * providers: [{ provide: FormFieldControl, useExisting: PhoneInputComponent }],\n * })\n * export class PhoneInputComponent extends FormFieldControl<string> {\n * // ... implement all abstract members\n * }\n * ```\n */\nexport abstract class FormFieldControl<_T = unknown> {\n /** The NgControl bound to this control (if any). */\n abstract readonly ngControl: NgControl | null;\n\n /** Whether the control is focused. */\n abstract readonly focused: Signal<boolean>;\n\n /** Whether the label should float (focused or non-empty). */\n abstract readonly shouldLabelFloat: Signal<boolean>;\n\n /** Whether the control is required. */\n abstract readonly required: Signal<boolean>;\n\n /** Whether the control is disabled. */\n abstract readonly disabled: Signal<boolean>;\n\n /** Whether the control is in an error state. */\n abstract readonly errorState: Signal<boolean>;\n\n /** Unique ID for the control element. */\n abstract readonly id: Signal<string>;\n\n /** Structured validation errors (Signal Forms path). */\n readonly errors?: Signal<readonly unknown[] | null>;\n\n /** Called when the form field container is clicked. */\n abstract onContainerClick(event: MouseEvent): void;\n}\n","import { Directive, signal } from '@angular/core';\nimport type { Signal } from '@angular/core';\n\n/** Auto-incrementing ID counter. */\nlet nextId = 0;\n\n/**\n * Directive to mark the label element inside a form field.\n *\n * The form field automatically associates this label with the inner control\n * and positions it appropriately based on appearance and float state.\n *\n * @tokens none\n *\n * @example\n * ```html\n * <com-form-field>\n * <label comLabel>Email address</label>\n * <input comInput formControlName=\"email\" />\n * </com-form-field>\n * ```\n */\n@Directive({\n selector: '[comLabel]',\n exportAs: 'comLabel',\n host: {\n '[attr.for]': 'forId()',\n '[id]': 'labelId',\n },\n})\nexport class ComLabel {\n readonly labelId: string = `com-label-${nextId++}`;\n\n private readonly _forId = signal<string | null>(null);\n readonly forId: Signal<string | null> = this._forId.asReadonly();\n\n /** Sets the `for` attribute to link to the control. Called by form field. */\n setForId(id: string): void {\n this._forId.set(id);\n }\n}\n","import { cva } from 'class-variance-authority';\nimport type { VariantProps } from 'class-variance-authority';\n\nexport type FormFieldAppearance = 'fill' | 'outline';\nexport type FormFieldColor = 'primary' | 'accent' | 'warn';\nexport type FormFieldFloatLabel = 'auto' | 'always';\nexport type FormFieldSubscriptSizing = 'fixed' | 'dynamic';\n\n/**\n * CVA variants for the form field wrapper.\n *\n * @tokens `--color-foreground`\n */\nexport const formFieldVariants: (props?: {\n disabled?: boolean | null | undefined;\n}) => string = cva(\n ['com-form-field', 'relative block w-full', 'text-foreground'],\n {\n variants: {\n disabled: {\n true: 'pointer-events-none',\n false: '',\n },\n },\n defaultVariants: {\n disabled: false,\n },\n }\n);\n\nexport type FormFieldVariants = VariantProps<typeof formFieldVariants>;\n\n/**\n * CVA variants for the input container (the bordered/filled area).\n *\n * @tokens `--color-input-border`, `--color-input-background`, `--color-primary`,\n * `--color-accent`, `--color-warn`, `--color-ring`, `--color-muted`,\n * `--color-disabled`, `--radius-input`\n */\nexport const formFieldContainerVariants: (props?: {\n appearance?: FormFieldAppearance | null | undefined;\n color?: FormFieldColor | null | undefined;\n focused?: boolean | null | undefined;\n error?: boolean | null | undefined;\n disabled?: boolean | null | undefined;\n}) => string = cva(\n [\n 'com-form-field__container',\n 'relative flex items-center',\n 'w-full min-h-11',\n 'transition-all duration-normal',\n 'rounded-input',\n ],\n {\n variants: {\n appearance: {\n outline: [\n 'border border-input-border',\n 'bg-transparent',\n 'mt-2.5', // Reserve space for floating label to prevent height jump\n ],\n fill: [\n 'rounded-b-none',\n 'bg-muted',\n 'border-b-2 border-input-border',\n ],\n },\n color: {\n primary: '',\n accent: '',\n warn: '',\n },\n focused: {\n true: '',\n false: '',\n },\n error: {\n true: '',\n false: '',\n },\n disabled: {\n true: 'bg-disabled border-disabled',\n false: '',\n },\n },\n compoundVariants: [\n // Outline + focused (no error)\n { appearance: 'outline', focused: true, error: false, class: 'border-primary ring-1 ring-primary' },\n { appearance: 'outline', focused: true, error: false, color: 'accent', class: 'border-accent ring-accent' },\n { appearance: 'outline', focused: true, error: false, color: 'warn', class: 'border-warn ring-warn' },\n // Outline + error\n { appearance: 'outline', error: true, class: 'border-warn ring-1 ring-warn' },\n // Fill + focused (no error)\n { appearance: 'fill', focused: true, error: false, class: 'border-b-primary' },\n { appearance: 'fill', focused: true, error: false, color: 'accent', class: 'border-b-accent' },\n { appearance: 'fill', focused: true, error: false, color: 'warn', class: 'border-b-warn' },\n // Fill + error\n { appearance: 'fill', error: true, class: 'border-b-warn' },\n ],\n defaultVariants: {\n appearance: 'outline',\n color: 'primary',\n focused: false,\n error: false,\n disabled: false,\n },\n }\n);\n\nexport type FormFieldContainerVariants = VariantProps<typeof formFieldContainerVariants>;\n\n/**\n * CVA variants for the floating label.\n *\n * @tokens `--color-muted-foreground`, `--color-primary`, `--color-accent`,\n * `--color-warn`, `--color-background`\n */\nexport const formFieldLabelVariants: (props?: {\n appearance?: FormFieldAppearance | null | undefined;\n floating?: boolean | null | undefined;\n color?: FormFieldColor | null | undefined;\n error?: boolean | null | undefined;\n focused?: boolean | null | undefined;\n disabled?: boolean | null | undefined;\n}) => string = cva(\n [\n 'com-form-field__label',\n 'pointer-events-none',\n 'text-muted-foreground text-sm',\n 'origin-top-left',\n 'transition-all duration-slow ease-in-out',\n ],\n {\n variants: {\n appearance: {\n outline: '',\n fill: '',\n },\n floating: {\n // Floating: absolute positioning at container border\n true: 'absolute',\n // Not floating: relative positioning in flex flow (after prefix)\n false: 'relative flex-shrink-0 pl-3',\n },\n color: {\n primary: '',\n accent: '',\n warn: '',\n },\n error: {\n true: 'text-warn',\n false: '',\n },\n focused: {\n true: '',\n false: '',\n },\n disabled: {\n true: 'text-disabled-foreground',\n false: '',\n },\n },\n compoundVariants: [\n // Outline floating (label above border with notch effect)\n {\n appearance: 'outline',\n floating: true,\n class: 'left-2 -top-2.5 scale-75 bg-background px-1',\n },\n // Fill floating\n {\n appearance: 'fill',\n floating: true,\n class: 'left-3 top-1 scale-75 text-xs',\n },\n // Focused + floating colors (no error)\n { floating: true, focused: true, error: false, color: 'primary', class: 'text-primary' },\n { floating: true, focused: true, error: false, color: 'accent', class: 'text-accent' },\n { floating: true, focused: true, error: false, color: 'warn', class: 'text-warn' },\n // Error overrides\n { floating: true, error: true, class: 'text-warn' },\n ],\n defaultVariants: {\n appearance: 'outline',\n floating: false,\n color: 'primary',\n error: false,\n focused: false,\n disabled: false,\n },\n }\n);\n\nexport type FormFieldLabelVariants = VariantProps<typeof formFieldLabelVariants>;\n\n/**\n * CVA variants for the subscript area (hints/errors).\n *\n * @tokens (inherits from children)\n */\nexport const formFieldSubscriptVariants: (props?: {\n sizing?: FormFieldSubscriptSizing | null | undefined;\n}) => string = cva(\n ['com-form-field__subscript', 'text-xs mt-1 px-3'],\n {\n variants: {\n sizing: {\n fixed: 'min-h-5',\n dynamic: 'min-h-0',\n },\n },\n defaultVariants: {\n sizing: 'fixed',\n },\n }\n);\n\nexport type FormFieldSubscriptVariants = VariantProps<typeof formFieldSubscriptVariants>;\n\n/**\n * CVA variants for hint text.\n *\n * @tokens `--color-muted-foreground`\n */\nexport const hintVariants: () => string = cva(['com-form-field__hint', 'text-muted-foreground text-xs']);\n\n/**\n * CVA variants for error messages.\n *\n * @tokens `--color-warn`\n */\nexport const errorVariants: () => string = cva(['com-form-field__error', 'text-warn text-xs']);\n\n/**\n * CVA variants for prefix slot.\n *\n * @tokens `--color-muted-foreground`\n */\nexport const prefixVariants: () => string = cva([\n 'com-form-field__prefix',\n 'flex items-center text-muted-foreground pl-3',\n]);\n\n/**\n * CVA variants for suffix slot.\n *\n * @tokens `--color-muted-foreground`\n */\nexport const suffixVariants: () => string = cva([\n 'com-form-field__suffix',\n 'flex items-center text-muted-foreground pr-3',\n]);\n","import { computed, Directive, input } from '@angular/core';\nimport type { InputSignal, Signal } from '@angular/core';\nimport { hintVariants } from './form-field.variants';\nimport { mergeClasses } from 'ngx-com/utils';\n\n/** Alignment of hint text in the subscript area. */\nexport type HintAlign = 'start' | 'end';\n\n/** Auto-incrementing ID counter. */\nlet nextId = 0;\n\n/**\n * Directive for hint text displayed below the form field.\n *\n * The hint provides supplementary information to help users fill out the field.\n * It is automatically added to the control's `aria-describedby`.\n *\n * @tokens `--color-muted-foreground`\n *\n * @example Basic hint\n * ```html\n * <com-form-field>\n * <label comLabel>Password</label>\n * <input comInput formControlName=\"password\" />\n * <span comHint>At least 8 characters</span>\n * </com-form-field>\n * ```\n *\n * @example Right-aligned hint (e.g., character count)\n * ```html\n * <com-form-field>\n * <label comLabel>Bio</label>\n * <textarea comInput formControlName=\"bio\"></textarea>\n * <span comHint>Keep it brief</span>\n * <span comHint align=\"end\">{{ bioLength }}/150</span>\n * </com-form-field>\n * ```\n */\n@Directive({\n selector: '[comHint]',\n exportAs: 'comHint',\n host: {\n '[id]': 'id',\n '[class]': 'hostClasses()',\n },\n})\nexport class ComHint {\n readonly id: string = `com-hint-${nextId++}`;\n readonly align: InputSignal<HintAlign> = input<HintAlign>('start');\n\n protected readonly hostClasses: Signal<string> = computed(() =>\n mergeClasses(hintVariants(), this.align() === 'end' && 'ml-auto')\n );\n}\n","import { computed, Directive, input, signal } from '@angular/core';\nimport type { AbstractControl } from '@angular/forms';\nimport type { InputSignal, Signal, WritableSignal } from '@angular/core';\nimport { errorVariants } from './form-field.variants';\n\n/** Auto-incrementing ID counter. */\nlet nextId = 0;\n\n/**\n * Directive for error messages displayed below the form field.\n *\n * Errors replace hints when the control is in an error state.\n * Uses `role=\"alert\"` and `aria-live=\"polite\"` for screen reader announcement.\n *\n * The optional `match` input allows showing errors only for specific validation errors.\n *\n * @tokens `--color-warn`\n *\n * @example Basic error\n * ```html\n * <com-form-field>\n * <label comLabel>Email</label>\n * <input comInput formControlName=\"email\" />\n * <span comError>Please enter a valid email</span>\n * </com-form-field>\n * ```\n *\n * @example Matching specific errors\n * ```html\n * <com-form-field>\n * <label comLabel>Email</label>\n * <input comInput formControlName=\"email\" />\n * <span comError match=\"required\">Email is required.</span>\n * <span comError match=\"email\">Must be a valid email address.</span>\n * </com-form-field>\n * ```\n */\n@Directive({\n selector: '[comError]',\n exportAs: 'comError',\n host: {\n '[id]': 'id',\n '[class]': 'errorVariants()',\n '[class.hidden]': '!shouldShow()',\n 'role': 'alert',\n 'aria-live': 'polite',\n },\n})\nexport class ComError {\n readonly id: string = `com-error-${nextId++}`;\n\n /** Reference to the form control (Reactive Forms path). */\n private readonly _control: WritableSignal<AbstractControl | null> = signal(null);\n\n /** Structured errors from Signal Forms. */\n private readonly _signalErrors: WritableSignal<readonly unknown[]> = signal([]);\n\n /**\n * Show this error only when a specific validation error key is present.\n * If empty, the error is always shown when the control is in error state.\n */\n readonly match: InputSignal<string> = input<string>('');\n\n protected readonly errorVariants: () => string = errorVariants;\n\n /**\n * Whether this error should be displayed based on the match condition.\n * Used by the form field to filter which errors to show.\n */\n readonly shouldShow: Signal<boolean> = computed(() => {\n const matchKey = this.match();\n if (!matchKey) return true;\n\n // Signal Forms path: match on error.kind\n const signalErrors = this._signalErrors();\n if (signalErrors.length > 0) {\n return signalErrors.some((e) => (e as { kind?: string })?.kind === matchKey);\n }\n\n // Reactive Forms path: match on AbstractControl.errors keys\n const errors = this._control()?.errors;\n return errors ? matchKey in errors : false;\n });\n\n /**\n * Sets the form control reference (Reactive Forms).\n * Called by the parent form field component.\n */\n setControl(control: AbstractControl | null): void {\n this._control.set(control);\n }\n\n /**\n * Sets structured validation errors (Signal Forms).\n * Called by the parent form field component.\n */\n setSignalErrors(errors: readonly unknown[]): void {\n this._signalErrors.set(errors);\n }\n}\n","import { Directive } from '@angular/core';\nimport { prefixVariants } from './form-field.variants';\n\n/**\n * Directive to mark content as the prefix slot in a form field.\n *\n * Prefix content appears before the input (e.g., currency symbol, icon).\n *\n * @tokens `--color-muted-foreground`\n *\n * @example Currency prefix\n * ```html\n * <com-form-field>\n * <label comLabel>Amount</label>\n * <span comPrefix>$</span>\n * <input comInput type=\"number\" formControlName=\"amount\" />\n * </com-form-field>\n * ```\n *\n * @example Icon prefix\n * ```html\n * <com-form-field>\n * <label comLabel>Search</label>\n * <svg comPrefix class=\"size-4\">...</svg>\n * <input comInput formControlName=\"query\" />\n * </com-form-field>\n * ```\n */\n@Directive({\n selector: '[comPrefix]',\n exportAs: 'comPrefix',\n host: {\n '[class]': 'prefixVariants()',\n },\n})\nexport class ComPrefix {\n protected readonly prefixVariants: () => string = prefixVariants;\n}\n","import { Directive } from '@angular/core';\nimport { suffixVariants } from './form-field.variants';\n\n/**\n * Directive to mark content as the suffix slot in a form field.\n *\n * Suffix content appears after the input (e.g., unit, clear button, visibility toggle).\n *\n * @tokens `--color-muted-foreground`\n *\n * @example Unit suffix\n * ```html\n * <com-form-field>\n * <label comLabel>Weight</label>\n * <input comInput type=\"number\" formControlName=\"weight\" />\n * <span comSuffix>kg</span>\n * </com-form-field>\n * ```\n *\n * @example Clear button suffix\n * ```html\n * <com-form-field>\n * <label comLabel>Search</label>\n * <input comInput formControlName=\"query\" />\n * <button comSuffix type=\"button\" (click)=\"clearSearch()\">\n * <svg class=\"size-4\">...</svg>\n * </button>\n * </com-form-field>\n * ```\n *\n * @example Password visibility toggle\n * ```html\n * <com-form-field>\n * <label comLabel>Password</label>\n * <input comInput [type]=\"showPassword ? 'text' : 'password'\" formControlName=\"password\" />\n * <button comSuffix type=\"button\" (click)=\"showPassword = !showPassword\">\n * <svg class=\"size-4\">...</svg>\n * </button>\n * </com-form-field>\n * ```\n */\n@Directive({\n selector: '[comSuffix]',\n exportAs: 'comSuffix',\n host: {\n '[class]': 'suffixVariants()',\n },\n})\nexport class ComSuffix {\n protected readonly suffixVariants: () => string = suffixVariants;\n}\n","import { Injectable } from '@angular/core';\nimport type { AbstractControl, FormGroupDirective, NgForm } from '@angular/forms';\n\n/**\n * Strategy for determining when to display errors in a form field.\n *\n * The default behavior shows errors when the control is invalid AND\n * either touched OR the parent form has been submitted.\n *\n * @example Override globally with eager error display\n * ```ts\n * @Injectable()\n * export class EagerErrorStateMatcher extends ErrorStateMatcher {\n * override isErrorState(control: AbstractControl | null): boolean {\n * return !!(control?.invalid && control.dirty);\n * }\n * }\n *\n * // In app config\n * providers: [{ provide: ErrorStateMatcher, useClass: EagerErrorStateMatcher }]\n * ```\n *\n * @example Per-field override\n * ```html\n * <com-form-field>\n * <input comInput [errorStateMatcher]=\"eagerMatcher\" />\n * </com-form-field>\n * ```\n */\n@Injectable({ providedIn: 'root' })\nexport class ErrorStateMatcher {\n isErrorState(\n control: AbstractControl | null,\n form: FormGroupDirective | NgForm | null\n ): boolean {\n return !!(control?.invalid && (control.touched || form?.submitted));\n }\n}\n","import {\n booleanAttribute,\n computed,\n DestroyRef,\n Directive,\n ElementRef,\n forwardRef,\n inject,\n input,\n signal,\n} from '@angular/core';\nimport type { DoCheck, InputSignal, InputSignalWithTransform, OnInit, Signal, WritableSignal } from '@angular/core';\n\nimport { FormGroupDirective, NgControl, NgForm } from '@angular/forms';\nimport { AutofillMonitor } from '@angular/cdk/text-field';\nimport { FormFieldControl } from './form-field-control';\nimport { ErrorStateMatcher } from './error-state-matcher';\nimport type { FormFieldAppearance } from './form-field-defaults';\n\n/** Auto-incrementing ID counter. */\nlet nextId = 0;\n\n/**\n * Directive applied to native `<input>` and `<textarea>` elements to bridge\n * them to the form field wrapper. Implements the `FormFieldControl` contract.\n *\n * This directive does NOT implement `ControlValueAccessor` - it relies on\n * Angular's built-in value accessors (`DefaultValueAccessor`, `NumberValueAccessor`, etc.)\n * that are already applied to native elements with `formControlName` or `ngModel`.\n *\n * Instead, this directive reports state (focused, empty, disabled, error) to the\n * parent form field for proper visual presentation.\n *\n * @tokens (styling is controlled by the parent form field)\n *\n * @example Basic usage\n * ```html\n * <com-form-field>\n * <label comLabel>Email</label>\n * <input comInput formControlName=\"email\" placeholder=\"you@example.com\" />\n * </com-form-field>\n * ```\n *\n * @example Textarea\n * ```html\n * <com-form-field>\n * <label comLabel>Description</label>\n * <textarea comInput formControlName=\"description\"></textarea>\n * </com-form-field>\n * ```\n *\n * @example Custom error state matcher\n * ```html\n * <com-form-field>\n * <label comLabel>Code</label>\n * <input comInput formControlName=\"code\" [errorStateMatcher]=\"eagerMatcher\" />\n * </com-form-field>\n * ```\n */\n@Directive({\n selector: 'input[comInput], textarea[comInput]',\n exportAs: 'comInput',\n providers: [{ provide: FormFieldControl, useExisting: forwardRef(() => ComInput) }],\n host: {\n '[id]': 'id()',\n '[disabled]': 'disabled()',\n '[required]': 'required()',\n '[attr.aria-invalid]': 'errorState() || null',\n '[attr.aria-required]': 'required() || null',\n '[attr.aria-describedby]': 'ariaDescribedBy() || null',\n '[class]': 'hostClasses()',\n '(focus)': 'onFocus()',\n '(blur)': 'onBlur()',\n '(input)': 'onInput()',\n },\n})\nexport class ComInput implements FormFieldControl<string>, OnInit, DoCheck {\n private readonly elementRef: ElementRef<HTMLInputElement | HTMLTextAreaElement> = inject(ElementRef);\n private readonly destroyRef = inject(DestroyRef);\n private readonly autofillMonitor = inject(AutofillMonitor);\n private readonly defaultErrorStateMatcher = inject(ErrorStateMatcher);\n private readonly parentForm = inject(NgForm, { optional: true });\n private readonly parentFormGroup = inject(FormGroupDirective, { optional: true });\n\n /** NgControl bound to this input (if using reactive forms). */\n readonly ngControl: NgControl | null = inject(NgControl, { optional: true, self: true });\n\n // Inputs\n readonly inputId: InputSignal<string | undefined> = input<string>(undefined, { alias: 'id' });\n readonly inputDisabled: InputSignalWithTransform<boolean, unknown> = input(false, {\n alias: 'disabled',\n transform: booleanAttribute,\n });\n readonly inputRequired: InputSignalWithTransform<boolean, unknown> = input(false, {\n alias: 'required',\n transform: booleanAttribute,\n });\n readonly userAriaDescribedBy: InputSignal<string> = input<string>('', { alias: 'aria-describedby' });\n readonly errorStateMatcher: InputSignal<ErrorStateMatcher | undefined> = input<ErrorStateMatcher>();\n\n // Signal Forms inputs — set automatically by [formField] via setInputOnDirectives\n readonly sfInvalid: InputSignal<boolean> = input(false, { alias: 'invalid' });\n readonly sfTouched: InputSignalWithTransform<boolean, unknown> = input(false, {\n alias: 'touched',\n transform: booleanAttribute,\n });\n readonly sfErrors: InputSignal<readonly unknown[]> = input<readonly unknown[]>([], {\n alias: 'errors',\n });\n\n /** Whether the form system is Signal Forms (no NgControl present). */\n private readonly isSignalForms: boolean = !this.ngControl;\n\n // Internal state\n private readonly _focused = signal(false);\n private readonly _autofilled = signal(false);\n private readonly _empty = signal(true);\n private readonly _uniqueId: string = `com-input-${nextId++}`;\n private readonly _appearance: WritableSignal<FormFieldAppearance> = signal<FormFieldAppearance>('outline');\n private _previousNativeValue: string = '';\n\n // Public signals implementing FormFieldControl\n readonly focused: Signal<boolean> = this._focused.asReadonly();\n\n readonly id: Signal<string> = computed(() => this.inputId() ?? this._uniqueId);\n\n readonly shouldLabelFloat: Signal<boolean> = computed(() => {\n return this._focused() || !this._empty() || this._autofilled();\n });\n\n readonly disabled: Signal<boolean> = computed(() => {\n if (this.inputDisabled()) return true;\n return this.ngControl?.control?.disabled ?? false;\n });\n\n readonly required: Signal<boolean> = computed(() => {\n if (this.inputRequired()) return true;\n const control = this.ngControl?.control;\n if (control?.validator) {\n const result = control.validator({ value: '' } as never);\n return result?.['required'] !== undefined;\n }\n return false;\n });\n\n readonly errorState: Signal<boolean> = computed(() => {\n if (this.isSignalForms) {\n // Signal Forms: gate on both invalid AND touched — mirrors ErrorStateMatcher default.\n // Signal Forms' invalid is pure validation state (not gated by touched).\n return this.sfInvalid() && this.sfTouched();\n }\n // Reactive Forms: use ErrorStateMatcher (existing logic)\n this._focused();\n this._empty();\n const matcher = this.errorStateMatcher() ?? this.defaultErrorStateMatcher;\n const form = this.parentFormGroup ?? this.parentForm;\n return matcher.isErrorState(this.ngControl?.control ?? null, form);\n });\n\n /** Structured validation errors from Signal Forms, exposed for the parent form field. */\n readonly errors: Signal<readonly unknown[] | null> = computed(() =>\n this.isSignalForms ? this.sfErrors() : null\n );\n\n /** Combined aria-describedby including user-provided and form-field-generated IDs. */\n private readonly _describedByIds = signal('');\n\n readonly ariaDescribedBy: Signal<string> = computed(() => {\n const userIds = this.userAriaDescribedBy();\n const fieldIds = this._describedByIds();\n return [userIds, fieldIds].filter(Boolean).join(' ');\n });\n\n /** Computed host classes including appearance-based padding. */\n protected readonly hostClasses: Signal<string> = computed(() => {\n const base =\n 'peer w-full bg-transparent text-foreground placeholder:text-input-placeholder outline-none border-none disabled:cursor-not-allowed disabled:text-disabled-foreground px-3';\n const padding = this._appearance() === 'fill' ? 'pt-5 pb-1.5' : 'py-2.5';\n return `${base} ${padding}`;\n });\n\n ngOnInit(): void {\n this._previousNativeValue = this.elementRef.nativeElement.value;\n this.updateEmpty();\n\n const autofillSub = this.autofillMonitor.monitor(this.elementRef).subscribe((event) => {\n this._autofilled.set(event.isAutofilled);\n });\n\n this.destroyRef.onDestroy(() => {\n autofillSub.unsubscribe();\n this.autofillMonitor.stopMonitoring(this.elementRef);\n });\n }\n\n ngDoCheck(): void {\n this.dirtyCheckNativeValue();\n }\n\n private dirtyCheckNativeValue(): void {\n const newValue = this.elementRef.nativeElement.value;\n if (this._previousNativeValue !== newValue) {\n this._previousNativeValue = newValue;\n this.updateEmpty();\n }\n }\n\n protected onFocus(): void {\n this._focused.set(true);\n }\n\n protected onBlur(): void {\n this._focused.set(false);\n }\n\n protected onInput(): void {\n this.updateEmpty();\n }\n\n private updateEmpty(): void {\n this._empty.set(!this.elementRef.nativeElement.value);\n }\n\n // FormFieldControl methods\n onContainerClick(event: MouseEvent): void {\n if (!this.disabled() && event.target !== this.elementRef.nativeElement) {\n this.elementRef.nativeElement.focus();\n }\n }\n\n /**\n * Sets the describedBy IDs from the form field.\n * Called by the parent form field component.\n */\n setDescribedByIds(ids: string): void {\n this._describedByIds.set(ids);\n }\n\n /**\n * Sets the appearance for styling.\n * Called by the parent form field component.\n */\n setAppearance(appearance: FormFieldAppearance): void {\n this._appearance.set(appearance);\n }\n\n /** Focus the native element. */\n focus(): void {\n this.elementRef.nativeElement.focus();\n }\n}\n","import { InjectionToken } from '@angular/core';\nimport type {\n FormFieldAppearance,\n FormFieldColor,\n FormFieldFloatLabel,\n FormFieldSubscriptSizing,\n} from './form-field.variants';\n\n// Re-export types for convenience\nexport type { FormFieldAppearance, FormFieldColor, FormFieldFloatLabel, FormFieldSubscriptSizing };\n\n/**\n * Global configuration defaults for form fields.\n *\n * @example Set defaults in app config\n * ```ts\n * providers: [\n * {\n * provide: FORM_FIELD_DEFAULTS,\n * useValue: {\n * appearance: 'fill',\n * floatLabel: 'always',\n * color: 'primary',\n * }\n * }\n * ]\n * ```\n */\nexport interface FormFieldDefaults {\n appearance?: FormFieldAppearance;\n color?: FormFieldColor;\n floatLabel?: FormFieldFloatLabel;\n hideRequiredMarker?: boolean;\n subscriptSizing?: FormFieldSubscriptSizing;\n}\n\nexport const FORM_FIELD_DEFAULTS: InjectionToken<FormFieldDefaults> = new InjectionToken<FormFieldDefaults>(\n 'FORM_FIELD_DEFAULTS',\n {\n providedIn: 'root',\n factory: (): FormFieldDefaults => ({}),\n }\n);\n","import {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n contentChild,\n contentChildren,\n effect,\n inject,\n input,\n ViewEncapsulation,\n} from '@angular/core';\nimport type { InputSignal, InputSignalWithTransform, Signal } from '@angular/core';\nimport { FormFieldControl } from './form-field-control';\nimport { ComLabel } from './label.directive';\nimport { ComHint } from './hint.directive';\nimport { ComError } from './error.directive';\nimport { ComPrefix } from './prefix.directive';\nimport { ComSuffix } from './suffix.directive';\nimport { ComInput } from './input.directive';\nimport {\n FORM_FIELD_DEFAULTS,\n type FormFieldAppearance,\n type FormFieldColor,\n type FormFieldFloatLabel,\n type FormFieldSubscriptSizing,\n} from './form-field-defaults';\nimport {\n formFieldVariants,\n formFieldContainerVariants,\n formFieldLabelVariants,\n formFieldSubscriptVariants,\n} from './form-field.variants';\nimport { mergeClasses } from 'ngx-com/utils';\n\n/**\n * Form field wrapper providing visual structure for form inputs.\n *\n * Provides floating labels, borders (outline/fill appearance), hints, errors,\n * and prefix/suffix slots. Automatically wires ARIA attributes for accessibility.\n *\n * The form field reads state from its inner `FormFieldControl` child (typically\n * a `ComInput` directive) and renders UI accordingly.\n *\n * @tokens `--color-foreground`, `--color-input-border`, `--color-input-background`,\n * `--color-primary`, `--color-accent`, `--color-warn`, `--color-ring`,\n * `--color-muted`, `--color-muted-foreground`, `--color-disabled`,\n * `--color-disabled-foreground`, `--color-background`, `--radius-input`\n *\n * @example Basic outline field\n * ```html\n * <com-form-field>\n * <label comLabel>Email</label>\n * <input comInput formControlName=\"email\" />\n * <span comHint>We'll never share your email.</span>\n * <span comError match=\"required\">Email is required.</span>\n * </com-form-field>\n * ```\n *\n * @example Fill appearance\n * ```html\n * <com-form-field appearance=\"fill\">\n * <label comLabel>Username</label>\n * <input comInput formControlName=\"username\" />\n * </com-form-field>\n * ```\n *\n * @example With prefix and suffix\n * ```html\n * <com-form-field>\n * <label comLabel>Amount</label>\n * <span comPrefix>$</span>\n * <input comInput type=\"number\" formControlName=\"amount\" />\n * <span comSuffix>.00</span>\n * </com-form-field>\n * ```\n */\n@Component({\n selector: 'com-form-field',\n exportAs: 'comFormField',\n template: `\n <div [class]=\"containerClasses()\" role=\"group\" tabindex=\"-1\" (click)=\"onContainerClick($event)\" (keydown.enter)=\"onContainerActivate()\">\n <div class=\"flex items-center w-full\">\n @if (hasPrefix()) {\n <ng-content select=\"[comPrefix]\" />\n }\n @if (labelChild()) {\n <span [class]=\"labelClasses()\">\n <ng-content select=\"[comLabel]\" />\n @if (showRequiredMarker()) {\n <span class=\"text-warn\" aria-hidden=\"true\"> *</span>\n }\n </span>\n }\n <div class=\"flex-1 min-w-0\">\n <ng-content />\n </div>\n @if (hasSuffix()) {\n <ng-content select=\"[comSuffix]\" />\n }\n </div>\n </div>\n\n <div [class]=\"subscriptClasses()\">\n <div [class.hidden]=\"!showErrors()\">\n <ng-content select=\"[comError]\" />\n </div>\n <div [class.hidden]=\"showErrors()\" class=\"flex justify-between w-full gap-2\">\n <ng-content select=\"[comHint]:not([align='end'])\" />\n <ng-content select=\"[comHint][align='end']\" />\n </div>\n </div>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n styles: `\n /* Hide placeholder when label exists and is not floating (label acts as placeholder) */\n com-form-field.com-form-field--has-label:not(.com-form-field--floating) input::placeholder,\n com-form-field.com-form-field--has-label:not(.com-form-field--floating) textarea::placeholder {\n color: transparent;\n }\n\n /* Hide dropdown placeholder when label exists and is not floating */\n com-form-field.com-form-field--has-label:not(.com-form-field--floating) com-dropdown .text-placeholder {\n display: none;\n }\n\n /* Make dropdown fill available space inside form-field */\n com-form-field com-dropdown {\n display: block;\n width: 100%;\n }\n\n /* Make datepicker fill available space inside form-field */\n com-form-field com-datepicker {\n display: block;\n width: 100%;\n }\n\n /* Make date-range-picker fill available space inside form-field */\n com-form-field com-date-range-picker {\n display: block;\n width: 100%;\n }\n\n /* Make time-picker fill available space inside form-field */\n com-form-field com-time-picker {\n display: block;\n width: 100%;\n }\n\n /* Hide datepicker placeholder when label exists and is not floating */\n com-form-field.com-form-field--has-label:not(.com-form-field--floating) com-datepicker input::placeholder {\n color: transparent;\n }\n\n com-form-field.com-form-field--has-label:not(.com-form-field--floating) com-date-range-picker input::placeholder {\n color: transparent;\n }\n\n /* Hide time picker separators and period button when label is not floating */\n com-form-field.com-form-field--has-label:not(.com-form-field--floating) com-time-picker span[aria-hidden],\n com-form-field.com-form-field--has-label:not(.com-form-field--floating) com-time-picker button {\n visibility: hidden;\n }\n `,\n host: {\n '[class]': 'hostClasses()',\n '[class.com-form-field--focused]': 'isFocused()',\n '[class.com-form-field--disabled]': 'isDisabled()',\n '[class.com-form-field--error]': 'hasError()',\n '[class.com-form-field--floating]': 'shouldLabelFloat()',\n '[class.com-form-field--has-label]': '!!labelChild()',\n },\n})\nexport class ComFormField {\n private readonly defaults = inject(FORM_FIELD_DEFAULTS, { optional: true });\n\n // Content children\n readonly control: Signal<FormFieldControl | undefined> = contentChild<FormFieldControl>(FormFieldControl);\n readonly inputDirective: Signal<ComInput | undefined> = contentChild<ComInput>(ComInput);\n readonly labelChild: Signal<ComLabel | undefined> = contentChild<ComLabel>(ComLabel);\n readonly hintChildren: Signal<readonly ComHint[]> = contentChildren<ComHint>(ComHint);\n readonly errorChildren: Signal<readonly ComError[]> = contentChildren<ComError>(ComError);\n readonly prefixChild: Signal<ComPrefix | undefined> = contentChild<ComPrefix>(ComPrefix);\n readonly suffixChild: Signal<ComSuffix | undefined> = contentChild<ComSuffix>(ComSuffix);\n\n // Inputs with defaults fallback\n readonly appearance: InputSignal<FormFieldAppearance> = input<FormFieldAppearance>(\n this.defaults?.appearance ?? 'outline'\n );\n readonly color: InputSignal<FormFieldColor> = input<FormFieldColor>(this.defaults?.color ?? 'primary');\n readonly floatLabel: InputSignal<FormFieldFloatLabel> = input<FormFieldFloatLabel>(\n this.defaults?.floatLabel ?? 'auto'\n );\n readonly hideRequiredMarker: InputSignalWithTransform<boolean, unknown> = input(\n this.defaults?.hideRequiredMarker ?? false,\n { transform: booleanAttribute }\n );\n readonly subscriptSizing: InputSignal<FormFieldSubscriptSizing> = input<FormFieldSubscriptSizing>(\n this.defaults?.subscriptSizing ?? 'fixed'\n );\n readonly userClass: InputSignal<string> = input<string>('', { alias: 'class' });\n\n // Derived state from inner control\n readonly shouldLabelFloat: Signal<boolean> = computed(() => {\n if (this.floatLabel() === 'always') return true;\n return this.control()?.shouldLabelFloat() ?? false;\n });\n\n readonly isFocused: Signal<boolean> = computed(() => this.control()?.focused() ?? false);\n readonly isDisabled: Signal<boolean> = computed(() => this.control()?.disabled() ?? false);\n readonly hasError: Signal<boolean> = computed(() => this.control()?.errorState() ?? false);\n readonly hasPrefix: Signal<boolean> = computed(() => !!this.prefixChild());\n readonly hasSuffix: Signal<boolean> = computed(() => !!this.suffixChild());\n\n readonly showRequiredMarker: Signal<boolean> = computed(() => {\n if (this.hideRequiredMarker()) return false;\n return this.control()?.required() ?? false;\n });\n\n readonly showErrors: Signal<boolean> = computed(() => this.hasError() && this.errorChildren().length > 0);\n\n // Computed classes\n protected readonly hostClasses: Signal<string> = computed(() =>\n mergeClasses(formFieldVariants({ disabled: this.isDisabled() }), this.userClass())\n );\n\n protected readonly containerClasses: Signal<string> = computed(() =>\n formFieldContainerVariants({\n appearance: this.appearance(),\n color: this.color(),\n focused: this.isFocused(),\n error: this.hasError(),\n disabled: this.isDisabled(),\n })\n );\n\n protected readonly labelClasses: Signal<string> = computed(() =>\n formFieldLabelVariants({\n appearance: this.appearance(),\n floating: this.shouldLabelFloat(),\n color: this.color(),\n error: this.hasError(),\n focused: this.isFocused(),\n disabled: this.isDisabled(),\n })\n );\n\n protected readonly subscriptClasses: Signal<string> = computed(() =>\n formFieldSubscriptVariants({ sizing: this.subscriptSizing() })\n );\n\n constructor() {\n // Wire up label to control\n effect(() => {\n const label = this.labelChild();\n const ctrl = this.control();\n if (label && ctrl) {\n label.setForId(ctrl.id());\n }\n });\n\n // Wire up aria-describedby on the control\n effect(() => {\n const ctrl = this.control();\n if (!ctrl || !('setDescribedByIds' in ctrl)) return;\n\n const ids = this.showErrors()\n ? this.errorChildren().filter((e) => e.shouldShow()).map((e) => e.id)\n : this.hintChildren().map((h) => h.id);\n (ctrl as { setDescribedByIds: (ids: string) => void }).setDescribedByIds(ids.join(' '));\n });\n\n // Wire up appearance to control for proper styling\n effect(() => {\n const ctrl = this.control();\n if (ctrl && 'setAppearance' in ctrl) {\n (ctrl as { setAppearance: (appearance: FormFieldAppearance) => void }).setAppearance(this.appearance());\n }\n });\n\n // Wire up control reference to error directives (re-runs on error state change)\n effect(() => {\n // Read hasError to trigger re-evaluation when validation state changes\n this.hasError();\n const ctrl = this.control();\n if (!ctrl) return;\n\n const errors = ctrl.errors?.() ?? null;\n if (errors !== null) {\n // Signal Forms path: pass structured errors to each comError\n this.errorChildren().forEach((error) => error.setSignalErrors(errors as readonly unknown[]));\n } else {\n // Reactive Forms path: pass AbstractControl reference\n const ngControl = ctrl.ngControl?.control ?? null;\n this.errorChildren().forEach((error) => error.setControl(ngControl));\n }\n });\n }\n\n protected onContainerClick(event: MouseEvent): void {\n this.control()?.onContainerClick(event);\n }\n\n protected onContainerActivate(): void {\n this.control()?.onContainerClick(new MouseEvent('click'));\n }\n}\n","// Public API for the form-field component\n\n// Main component\nexport { ComFormField } from './form-field.component';\n\n// Directives\nexport { ComInput } from './input.directive';\nexport { ComLabel } from './label.directive';\nexport { ComHint, type HintAlign } from './hint.directive';\nexport { ComError } from './error.directive';\nexport { ComPrefix } from './prefix.directive';\nexport { ComSuffix } from './suffix.directive';\n\n// Abstract class for custom controls\nexport { FormFieldControl } from './form-field-control';\n\n// Error state matching\nexport { ErrorStateMatcher } from './error-state-matcher';\n\n// Configuration\nexport {\n FORM_FIELD_DEFAULTS,\n type FormFieldDefaults,\n type FormFieldAppearance,\n type FormFieldColor,\n type FormFieldFloatLabel,\n type FormFieldSubscriptSizing,\n} from './form-field-defaults';\n\n// Variants (for advanced customization)\nexport {\n formFieldVariants,\n formFieldContainerVariants,\n formFieldLabelVariants,\n formFieldSubscriptVariants,\n hintVariants,\n errorVariants,\n prefixVariants,\n suffixVariants,\n type FormFieldVariants,\n type FormFieldContainerVariants,\n type FormFieldLabelVariants,\n type FormFieldSubscriptVariants,\n} from './form-field.variants';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["nextId"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngx-com-components-form-field.mjs","sources":["../../../projects/com/components/form-field/form-field-control.ts","../../../projects/com/components/form-field/label.directive.ts","../../../projects/com/components/form-field/form-field.variants.ts","../../../projects/com/components/form-field/hint.directive.ts","../../../projects/com/components/form-field/error.directive.ts","../../../projects/com/components/form-field/prefix.directive.ts","../../../projects/com/components/form-field/suffix.directive.ts","../../../projects/com/components/form-field/error-state-matcher.ts","../../../projects/com/components/form-field/input.directive.ts","../../../projects/com/components/form-field/form-field-defaults.ts","../../../projects/com/components/form-field/form-field.component.ts","../../../projects/com/components/form-field/index.ts","../../../projects/com/components/form-field/ngx-com-components-form-field.ts"],"sourcesContent":["import type { Signal } from '@angular/core';\nimport type { NgControl } from '@angular/forms';\n\n\n/**\n * Contract that any control inside a form field must implement.\n *\n * This allows the form field to read state from any inner control\n * (input, textarea, custom controls) without knowing implementation details.\n *\n * @example Implementing for a custom phone input\n * ```ts\n * @Directive({\n * selector: 'com-phone-input',\n * providers: [{ provide: FormFieldControl, useExisting: PhoneInputComponent }],\n * })\n * export class PhoneInputComponent extends FormFieldControl<string> {\n * // ... implement all abstract members\n * }\n * ```\n */\nexport abstract class FormFieldControl<_T = unknown> {\n /** The NgControl bound to this control (if any). */\n abstract readonly ngControl: NgControl | null;\n\n /** Whether the control is focused. */\n abstract readonly focused: Signal<boolean>;\n\n /** Whether the label should float (focused or non-empty). */\n abstract readonly shouldLabelFloat: Signal<boolean>;\n\n /** Whether the control is required. */\n abstract readonly required: Signal<boolean>;\n\n /** Whether the control is disabled. */\n abstract readonly disabled: Signal<boolean>;\n\n /** Whether the control is in an error state. */\n abstract readonly errorState: Signal<boolean>;\n\n /** Unique ID for the control element. */\n abstract readonly id: Signal<string>;\n\n /** Structured validation errors (Signal Forms path). */\n readonly errors?: Signal<readonly unknown[] | null>;\n\n /** Called when the form field container is clicked. */\n abstract onContainerClick(event: MouseEvent): void;\n}\n","import { Directive, signal } from '@angular/core';\nimport type { Signal } from '@angular/core';\n\n/** Auto-incrementing ID counter. */\nlet nextId = 0;\n\n/**\n * Directive to mark the label element inside a form field.\n *\n * The form field automatically associates this label with the inner control\n * and positions it appropriately based on appearance and float state.\n *\n * The label renders as `inline-flex` so it can contain inline content such as\n * icons with tooltips. Icons inside the label are visually hidden when the\n * label floats to keep the scaled-down state clean.\n *\n * @tokens none\n *\n * @example Plain label\n * ```html\n * <com-form-field>\n * <label comLabel>Email address</label>\n * <input comInput formControlName=\"email\" />\n * </com-form-field>\n * ```\n *\n * @example Label with help icon and tooltip\n * ```html\n * <com-form-field>\n * <label comLabel>\n * User Role\n * <button type=\"button\" aria-label=\"Help\" comTooltip=\"Admins have full access.\">\n * <com-icon [img]=\"CircleHelpIcon\" size=\"xs\" color=\"muted\" />\n * </button>\n * </label>\n * <input comInput />\n * </com-form-field>\n * ```\n */\n@Directive({\n selector: '[comLabel]',\n exportAs: 'comLabel',\n host: {\n 'class': 'inline-flex items-center gap-1',\n '[attr.for]': 'forId()',\n '[id]': 'labelId',\n },\n})\nexport class ComLabel {\n readonly labelId: string = `com-label-${nextId++}`;\n\n private readonly _forId = signal<string | null>(null);\n readonly forId: Signal<string | null> = this._forId.asReadonly();\n\n /** Sets the `for` attribute to link to the control. Called by form field. */\n setForId(id: string): void {\n this._forId.set(id);\n }\n}\n","import { cva } from 'class-variance-authority';\nimport type { VariantProps } from 'class-variance-authority';\n\nexport type FormFieldAppearance = 'fill' | 'outline';\nexport type FormFieldColor = 'primary' | 'accent' | 'warn';\nexport type FormFieldFloatLabel = 'auto' | 'always';\nexport type FormFieldSubscriptSizing = 'fixed' | 'dynamic';\n\n/**\n * CVA variants for the form field wrapper.\n *\n * @tokens `--color-foreground`\n */\nexport const formFieldVariants: (props?: {\n disabled?: boolean | null | undefined;\n}) => string = cva(\n ['com-form-field', 'relative block w-full', 'text-foreground'],\n {\n variants: {\n disabled: {\n true: 'pointer-events-none',\n false: '',\n },\n },\n defaultVariants: {\n disabled: false,\n },\n }\n);\n\nexport type FormFieldVariants = VariantProps<typeof formFieldVariants>;\n\n/**\n * CVA variants for the input container (the bordered/filled area).\n *\n * @tokens `--color-input-border`, `--color-input-background`, `--color-primary`,\n * `--color-accent`, `--color-warn`, `--color-ring`, `--color-muted`,\n * `--color-disabled`, `--radius-input`\n */\nexport const formFieldContainerVariants: (props?: {\n appearance?: FormFieldAppearance | null | undefined;\n color?: FormFieldColor | null | undefined;\n focused?: boolean | null | undefined;\n error?: boolean | null | undefined;\n disabled?: boolean | null | undefined;\n}) => string = cva(\n [\n 'com-form-field__container',\n 'relative flex items-center',\n 'w-full min-h-11',\n 'transition-all duration-normal',\n 'rounded-input',\n ],\n {\n variants: {\n appearance: {\n outline: [\n 'border border-input-border',\n 'bg-transparent',\n 'mt-2.5', // Reserve space for floating label to prevent height jump\n ],\n fill: [\n 'rounded-b-none',\n 'bg-muted',\n 'border-b-2 border-input-border',\n ],\n },\n color: {\n primary: '',\n accent: '',\n warn: '',\n },\n focused: {\n true: '',\n false: '',\n },\n error: {\n true: '',\n false: '',\n },\n disabled: {\n true: 'bg-disabled border-disabled',\n false: '',\n },\n },\n compoundVariants: [\n // Outline + focused (no error)\n { appearance: 'outline', focused: true, error: false, class: 'border-primary ring-1 ring-primary' },\n { appearance: 'outline', focused: true, error: false, color: 'accent', class: 'border-accent ring-accent' },\n { appearance: 'outline', focused: true, error: false, color: 'warn', class: 'border-warn ring-warn' },\n // Outline + error\n { appearance: 'outline', error: true, class: 'border-warn ring-1 ring-warn' },\n // Fill + focused (no error)\n { appearance: 'fill', focused: true, error: false, class: 'border-b-primary' },\n { appearance: 'fill', focused: true, error: false, color: 'accent', class: 'border-b-accent' },\n { appearance: 'fill', focused: true, error: false, color: 'warn', class: 'border-b-warn' },\n // Fill + error\n { appearance: 'fill', error: true, class: 'border-b-warn' },\n ],\n defaultVariants: {\n appearance: 'outline',\n color: 'primary',\n focused: false,\n error: false,\n disabled: false,\n },\n }\n);\n\nexport type FormFieldContainerVariants = VariantProps<typeof formFieldContainerVariants>;\n\n/**\n * CVA variants for the floating label.\n *\n * @tokens `--color-muted-foreground`, `--color-primary`, `--color-accent`,\n * `--color-warn`, `--color-background`\n */\nexport const formFieldLabelVariants: (props?: {\n appearance?: FormFieldAppearance | null | undefined;\n floating?: boolean | null | undefined;\n color?: FormFieldColor | null | undefined;\n error?: boolean | null | undefined;\n focused?: boolean | null | undefined;\n disabled?: boolean | null | undefined;\n}) => string = cva(\n [\n 'com-form-field__label',\n 'pointer-events-none',\n 'text-muted-foreground text-sm',\n 'origin-top-left',\n 'transition-all duration-slow ease-in-out',\n ],\n {\n variants: {\n appearance: {\n outline: '',\n fill: '',\n },\n floating: {\n // Floating: absolute positioning at container border\n true: 'absolute',\n // Not floating: relative positioning in flex flow (after prefix)\n false: 'relative flex-shrink-0 pl-3',\n },\n color: {\n primary: '',\n accent: '',\n warn: '',\n },\n error: {\n true: 'text-warn',\n false: '',\n },\n focused: {\n true: '',\n false: '',\n },\n disabled: {\n true: 'text-disabled-foreground',\n false: '',\n },\n },\n compoundVariants: [\n // Outline floating (label above border with notch effect)\n {\n appearance: 'outline',\n floating: true,\n class: 'left-2 -top-2.5 scale-75 bg-background px-1',\n },\n // Fill floating\n {\n appearance: 'fill',\n floating: true,\n class: 'left-3 top-1 scale-75 text-xs',\n },\n // Focused + floating colors (no error)\n { floating: true, focused: true, error: false, color: 'primary', class: 'text-primary' },\n { floating: true, focused: true, error: false, color: 'accent', class: 'text-accent' },\n { floating: true, focused: true, error: false, color: 'warn', class: 'text-warn' },\n // Error overrides\n { floating: true, error: true, class: 'text-warn' },\n ],\n defaultVariants: {\n appearance: 'outline',\n floating: false,\n color: 'primary',\n error: false,\n focused: false,\n disabled: false,\n },\n }\n);\n\nexport type FormFieldLabelVariants = VariantProps<typeof formFieldLabelVariants>;\n\n/**\n * CVA variants for the subscript area (hints/errors).\n *\n * @tokens (inherits from children)\n */\nexport const formFieldSubscriptVariants: (props?: {\n sizing?: FormFieldSubscriptSizing | null | undefined;\n}) => string = cva(\n ['com-form-field__subscript', 'text-xs mt-1 px-3'],\n {\n variants: {\n sizing: {\n fixed: 'min-h-5',\n dynamic: 'min-h-0',\n },\n },\n defaultVariants: {\n sizing: 'fixed',\n },\n }\n);\n\nexport type FormFieldSubscriptVariants = VariantProps<typeof formFieldSubscriptVariants>;\n\n/**\n * CVA variants for hint text.\n *\n * @tokens `--color-muted-foreground`\n */\nexport const hintVariants: () => string = cva(['com-form-field__hint', 'text-muted-foreground text-xs']);\n\n/**\n * CVA variants for error messages.\n *\n * @tokens `--color-warn`\n */\nexport const errorVariants: () => string = cva(['com-form-field__error', 'text-warn text-xs']);\n\n/**\n * CVA variants for prefix slot.\n *\n * @tokens `--color-muted-foreground`\n */\nexport const prefixVariants: () => string = cva([\n 'com-form-field__prefix',\n 'flex items-center text-muted-foreground pl-3',\n]);\n\n/**\n * CVA variants for suffix slot.\n *\n * @tokens `--color-muted-foreground`\n */\nexport const suffixVariants: () => string = cva([\n 'com-form-field__suffix',\n 'flex items-center text-muted-foreground pr-3',\n]);\n","import { computed, Directive, input } from '@angular/core';\nimport type { InputSignal, Signal } from '@angular/core';\nimport { hintVariants } from './form-field.variants';\nimport { mergeClasses } from 'ngx-com/utils';\n\n/** Alignment of hint text in the subscript area. */\nexport type HintAlign = 'start' | 'end';\n\n/** Auto-incrementing ID counter. */\nlet nextId = 0;\n\n/**\n * Directive for hint text displayed below the form field.\n *\n * The hint provides supplementary information to help users fill out the field.\n * It is automatically added to the control's `aria-describedby`.\n *\n * @tokens `--color-muted-foreground`\n *\n * @example Basic hint\n * ```html\n * <com-form-field>\n * <label comLabel>Password</label>\n * <input comInput formControlName=\"password\" />\n * <span comHint>At least 8 characters</span>\n * </com-form-field>\n * ```\n *\n * @example Right-aligned hint (e.g., character count)\n * ```html\n * <com-form-field>\n * <label comLabel>Bio</label>\n * <textarea comInput formControlName=\"bio\"></textarea>\n * <span comHint>Keep it brief</span>\n * <span comHint align=\"end\">{{ bioLength }}/150</span>\n * </com-form-field>\n * ```\n */\n@Directive({\n selector: '[comHint]',\n exportAs: 'comHint',\n host: {\n '[id]': 'id',\n '[class]': 'hostClasses()',\n },\n})\nexport class ComHint {\n readonly id: string = `com-hint-${nextId++}`;\n readonly align: InputSignal<HintAlign> = input<HintAlign>('start');\n\n protected readonly hostClasses: Signal<string> = computed(() =>\n mergeClasses(hintVariants(), this.align() === 'end' && 'ml-auto')\n );\n}\n","import { computed, Directive, input, signal } from '@angular/core';\nimport type { AbstractControl } from '@angular/forms';\nimport type { InputSignal, Signal, WritableSignal } from '@angular/core';\nimport { errorVariants } from './form-field.variants';\n\n/** Auto-incrementing ID counter. */\nlet nextId = 0;\n\n/**\n * Directive for error messages displayed below the form field.\n *\n * Errors replace hints when the control is in an error state.\n * Uses `role=\"alert\"` and `aria-live=\"polite\"` for screen reader announcement.\n *\n * The optional `match` input allows showing errors only for specific validation errors.\n *\n * @tokens `--color-warn`\n *\n * @example Basic error\n * ```html\n * <com-form-field>\n * <label comLabel>Email</label>\n * <input comInput formControlName=\"email\" />\n * <span comError>Please enter a valid email</span>\n * </com-form-field>\n * ```\n *\n * @example Matching specific errors\n * ```html\n * <com-form-field>\n * <label comLabel>Email</label>\n * <input comInput formControlName=\"email\" />\n * <span comError match=\"required\">Email is required.</span>\n * <span comError match=\"email\">Must be a valid email address.</span>\n * </com-form-field>\n * ```\n */\n@Directive({\n selector: '[comError]',\n exportAs: 'comError',\n host: {\n '[id]': 'id',\n '[class]': 'errorVariants()',\n '[class.hidden]': '!shouldShow()',\n 'role': 'alert',\n 'aria-live': 'polite',\n },\n})\nexport class ComError {\n readonly id: string = `com-error-${nextId++}`;\n\n /** Reference to the form control (Reactive Forms path). */\n private readonly _control: WritableSignal<AbstractControl | null> = signal(null);\n\n /** Structured errors from Signal Forms. */\n private readonly _signalErrors: WritableSignal<readonly unknown[]> = signal([]);\n\n /**\n * Show this error only when a specific validation error key is present.\n * If empty, the error is always shown when the control is in error state.\n */\n readonly match: InputSignal<string> = input<string>('');\n\n protected readonly errorVariants: () => string = errorVariants;\n\n /**\n * Whether this error should be displayed based on the match condition.\n * Used by the form field to filter which errors to show.\n */\n readonly shouldShow: Signal<boolean> = computed(() => {\n const matchKey = this.match();\n if (!matchKey) return true;\n\n // Signal Forms path: match on error.kind\n const signalErrors = this._signalErrors();\n if (signalErrors.length > 0) {\n return signalErrors.some((e) => (e as { kind?: string })?.kind === matchKey);\n }\n\n // Reactive Forms path: match on AbstractControl.errors keys\n const errors = this._control()?.errors;\n return errors ? matchKey in errors : false;\n });\n\n /**\n * Sets the form control reference (Reactive Forms).\n * Called by the parent form field component.\n */\n setControl(control: AbstractControl | null): void {\n this._control.set(control);\n }\n\n /**\n * Sets structured validation errors (Signal Forms).\n * Called by the parent form field component.\n */\n setSignalErrors(errors: readonly unknown[]): void {\n this._signalErrors.set(errors);\n }\n}\n","import { Directive } from '@angular/core';\nimport { prefixVariants } from './form-field.variants';\n\n/**\n * Directive to mark content as the prefix slot in a form field.\n *\n * Prefix content appears before the input (e.g., currency symbol, icon).\n *\n * @tokens `--color-muted-foreground`\n *\n * @example Currency prefix\n * ```html\n * <com-form-field>\n * <label comLabel>Amount</label>\n * <span comPrefix>$</span>\n * <input comInput type=\"number\" formControlName=\"amount\" />\n * </com-form-field>\n * ```\n *\n * @example Icon prefix\n * ```html\n * <com-form-field>\n * <label comLabel>Search</label>\n * <svg comPrefix class=\"size-4\">...</svg>\n * <input comInput formControlName=\"query\" />\n * </com-form-field>\n * ```\n */\n@Directive({\n selector: '[comPrefix]',\n exportAs: 'comPrefix',\n host: {\n '[class]': 'prefixVariants()',\n },\n})\nexport class ComPrefix {\n protected readonly prefixVariants: () => string = prefixVariants;\n}\n","import { Directive } from '@angular/core';\nimport { suffixVariants } from './form-field.variants';\n\n/**\n * Directive to mark content as the suffix slot in a form field.\n *\n * Suffix content appears after the input (e.g., unit, clear button, visibility toggle).\n *\n * @tokens `--color-muted-foreground`\n *\n * @example Unit suffix\n * ```html\n * <com-form-field>\n * <label comLabel>Weight</label>\n * <input comInput type=\"number\" formControlName=\"weight\" />\n * <span comSuffix>kg</span>\n * </com-form-field>\n * ```\n *\n * @example Clear button suffix\n * ```html\n * <com-form-field>\n * <label comLabel>Search</label>\n * <input comInput formControlName=\"query\" />\n * <button comSuffix type=\"button\" (click)=\"clearSearch()\">\n * <svg class=\"size-4\">...</svg>\n * </button>\n * </com-form-field>\n * ```\n *\n * @example Password visibility toggle\n * ```html\n * <com-form-field>\n * <label comLabel>Password</label>\n * <input comInput [type]=\"showPassword ? 'text' : 'password'\" formControlName=\"password\" />\n * <button comSuffix type=\"button\" (click)=\"showPassword = !showPassword\">\n * <svg class=\"size-4\">...</svg>\n * </button>\n * </com-form-field>\n * ```\n */\n@Directive({\n selector: '[comSuffix]',\n exportAs: 'comSuffix',\n host: {\n '[class]': 'suffixVariants()',\n },\n})\nexport class ComSuffix {\n protected readonly suffixVariants: () => string = suffixVariants;\n}\n","import { Injectable } from '@angular/core';\nimport type { AbstractControl, FormGroupDirective, NgForm } from '@angular/forms';\n\n/**\n * Strategy for determining when to display errors in a form field.\n *\n * The default behavior shows errors when the control is invalid AND\n * either touched OR the parent form has been submitted.\n *\n * @example Override globally with eager error display\n * ```ts\n * @Injectable()\n * export class EagerErrorStateMatcher extends ErrorStateMatcher {\n * override isErrorState(control: AbstractControl | null): boolean {\n * return !!(control?.invalid && control.dirty);\n * }\n * }\n *\n * // In app config\n * providers: [{ provide: ErrorStateMatcher, useClass: EagerErrorStateMatcher }]\n * ```\n *\n * @example Per-field override\n * ```html\n * <com-form-field>\n * <input comInput [errorStateMatcher]=\"eagerMatcher\" />\n * </com-form-field>\n * ```\n */\n@Injectable({ providedIn: 'root' })\nexport class ErrorStateMatcher {\n isErrorState(\n control: AbstractControl | null,\n form: FormGroupDirective | NgForm | null\n ): boolean {\n return !!(control?.invalid && (control.touched || form?.submitted));\n }\n}\n","import {\n booleanAttribute,\n computed,\n DestroyRef,\n Directive,\n ElementRef,\n forwardRef,\n inject,\n input,\n signal,\n} from '@angular/core';\nimport type { DoCheck, InputSignal, InputSignalWithTransform, OnInit, Signal, WritableSignal } from '@angular/core';\n\nimport { FormGroupDirective, NgControl, NgForm } from '@angular/forms';\nimport { AutofillMonitor } from '@angular/cdk/text-field';\nimport { FormFieldControl } from './form-field-control';\nimport { ErrorStateMatcher } from './error-state-matcher';\nimport type { FormFieldAppearance } from './form-field-defaults';\n\n/** Auto-incrementing ID counter. */\nlet nextId = 0;\n\n/**\n * Directive applied to native `<input>` and `<textarea>` elements to bridge\n * them to the form field wrapper. Implements the `FormFieldControl` contract.\n *\n * This directive does NOT implement `ControlValueAccessor` - it relies on\n * Angular's built-in value accessors (`DefaultValueAccessor`, `NumberValueAccessor`, etc.)\n * that are already applied to native elements with `formControlName` or `ngModel`.\n *\n * Instead, this directive reports state (focused, empty, disabled, error) to the\n * parent form field for proper visual presentation.\n *\n * @tokens (styling is controlled by the parent form field)\n *\n * @example Basic usage\n * ```html\n * <com-form-field>\n * <label comLabel>Email</label>\n * <input comInput formControlName=\"email\" placeholder=\"you@example.com\" />\n * </com-form-field>\n * ```\n *\n * @example Textarea\n * ```html\n * <com-form-field>\n * <label comLabel>Description</label>\n * <textarea comInput formControlName=\"description\"></textarea>\n * </com-form-field>\n * ```\n *\n * @example Custom error state matcher\n * ```html\n * <com-form-field>\n * <label comLabel>Code</label>\n * <input comInput formControlName=\"code\" [errorStateMatcher]=\"eagerMatcher\" />\n * </com-form-field>\n * ```\n */\n@Directive({\n selector: 'input[comInput], textarea[comInput]',\n exportAs: 'comInput',\n providers: [{ provide: FormFieldControl, useExisting: forwardRef(() => ComInput) }],\n host: {\n '[id]': 'id()',\n '[disabled]': 'disabled()',\n '[required]': 'required()',\n '[attr.aria-invalid]': 'errorState() || null',\n '[attr.aria-required]': 'required() || null',\n '[attr.aria-describedby]': 'ariaDescribedBy() || null',\n '[class]': 'hostClasses()',\n '(focus)': 'onFocus()',\n '(blur)': 'onBlur()',\n '(input)': 'onInput()',\n },\n})\nexport class ComInput implements FormFieldControl<string>, OnInit, DoCheck {\n private readonly elementRef: ElementRef<HTMLInputElement | HTMLTextAreaElement> = inject(ElementRef);\n private readonly destroyRef = inject(DestroyRef);\n private readonly autofillMonitor = inject(AutofillMonitor);\n private readonly defaultErrorStateMatcher = inject(ErrorStateMatcher);\n private readonly parentForm = inject(NgForm, { optional: true });\n private readonly parentFormGroup = inject(FormGroupDirective, { optional: true });\n\n /** NgControl bound to this input (if using reactive forms). */\n readonly ngControl: NgControl | null = inject(NgControl, { optional: true, self: true });\n\n // Inputs\n readonly inputId: InputSignal<string | undefined> = input<string>(undefined, { alias: 'id' });\n readonly inputDisabled: InputSignalWithTransform<boolean, unknown> = input(false, {\n alias: 'disabled',\n transform: booleanAttribute,\n });\n readonly inputRequired: InputSignalWithTransform<boolean, unknown> = input(false, {\n alias: 'required',\n transform: booleanAttribute,\n });\n readonly userAriaDescribedBy: InputSignal<string> = input<string>('', { alias: 'aria-describedby' });\n readonly errorStateMatcher: InputSignal<ErrorStateMatcher | undefined> = input<ErrorStateMatcher>();\n\n // Signal Forms inputs — set automatically by [formField] via setInputOnDirectives\n readonly sfInvalid: InputSignal<boolean> = input(false, { alias: 'invalid' });\n readonly sfTouched: InputSignalWithTransform<boolean, unknown> = input(false, {\n alias: 'touched',\n transform: booleanAttribute,\n });\n readonly sfErrors: InputSignal<readonly unknown[]> = input<readonly unknown[]>([], {\n alias: 'errors',\n });\n\n /** Whether the form system is Signal Forms (no NgControl present). */\n private readonly isSignalForms: boolean = !this.ngControl;\n\n // Internal state\n private readonly _focused = signal(false);\n private readonly _autofilled = signal(false);\n private readonly _empty = signal(true);\n private readonly _uniqueId: string = `com-input-${nextId++}`;\n private readonly _appearance: WritableSignal<FormFieldAppearance> = signal<FormFieldAppearance>('outline');\n private _previousNativeValue: string = '';\n\n // Public signals implementing FormFieldControl\n readonly focused: Signal<boolean> = this._focused.asReadonly();\n\n readonly id: Signal<string> = computed(() => this.inputId() ?? this._uniqueId);\n\n readonly shouldLabelFloat: Signal<boolean> = computed(() => {\n return this._focused() || !this._empty() || this._autofilled();\n });\n\n readonly disabled: Signal<boolean> = computed(() => {\n if (this.inputDisabled()) return true;\n return this.ngControl?.control?.disabled ?? false;\n });\n\n readonly required: Signal<boolean> = computed(() => {\n if (this.inputRequired()) return true;\n const control = this.ngControl?.control;\n if (control?.validator) {\n const result = control.validator({ value: '' } as never);\n return result?.['required'] !== undefined;\n }\n return false;\n });\n\n readonly errorState: Signal<boolean> = computed(() => {\n if (this.isSignalForms) {\n // Signal Forms: gate on both invalid AND touched — mirrors ErrorStateMatcher default.\n // Signal Forms' invalid is pure validation state (not gated by touched).\n return this.sfInvalid() && this.sfTouched();\n }\n // Reactive Forms: use ErrorStateMatcher (existing logic)\n this._focused();\n this._empty();\n const matcher = this.errorStateMatcher() ?? this.defaultErrorStateMatcher;\n const form = this.parentFormGroup ?? this.parentForm;\n return matcher.isErrorState(this.ngControl?.control ?? null, form);\n });\n\n /** Structured validation errors from Signal Forms, exposed for the parent form field. */\n readonly errors: Signal<readonly unknown[] | null> = computed(() =>\n this.isSignalForms ? this.sfErrors() : null\n );\n\n /** Combined aria-describedby including user-provided and form-field-generated IDs. */\n private readonly _describedByIds = signal('');\n\n readonly ariaDescribedBy: Signal<string> = computed(() => {\n const userIds = this.userAriaDescribedBy();\n const fieldIds = this._describedByIds();\n return [userIds, fieldIds].filter(Boolean).join(' ');\n });\n\n /** Computed host classes including appearance-based padding. */\n protected readonly hostClasses: Signal<string> = computed(() => {\n const base =\n 'peer w-full bg-transparent text-foreground placeholder:text-input-placeholder outline-none border-none disabled:cursor-not-allowed disabled:text-disabled-foreground px-3';\n const padding = this._appearance() === 'fill' ? 'pt-5 pb-1.5' : 'py-2.5';\n return `${base} ${padding}`;\n });\n\n ngOnInit(): void {\n this._previousNativeValue = this.elementRef.nativeElement.value;\n this.updateEmpty();\n\n const autofillSub = this.autofillMonitor.monitor(this.elementRef).subscribe((event) => {\n this._autofilled.set(event.isAutofilled);\n });\n\n this.destroyRef.onDestroy(() => {\n autofillSub.unsubscribe();\n this.autofillMonitor.stopMonitoring(this.elementRef);\n });\n }\n\n ngDoCheck(): void {\n this.dirtyCheckNativeValue();\n }\n\n private dirtyCheckNativeValue(): void {\n const newValue = this.elementRef.nativeElement.value;\n if (this._previousNativeValue !== newValue) {\n this._previousNativeValue = newValue;\n this.updateEmpty();\n }\n }\n\n protected onFocus(): void {\n this._focused.set(true);\n }\n\n protected onBlur(): void {\n this._focused.set(false);\n }\n\n protected onInput(): void {\n this.updateEmpty();\n }\n\n private updateEmpty(): void {\n this._empty.set(!this.elementRef.nativeElement.value);\n }\n\n // FormFieldControl methods\n onContainerClick(event: MouseEvent): void {\n if (!this.disabled() && event.target !== this.elementRef.nativeElement) {\n this.elementRef.nativeElement.focus();\n }\n }\n\n /**\n * Sets the describedBy IDs from the form field.\n * Called by the parent form field component.\n */\n setDescribedByIds(ids: string): void {\n this._describedByIds.set(ids);\n }\n\n /**\n * Sets the appearance for styling.\n * Called by the parent form field component.\n */\n setAppearance(appearance: FormFieldAppearance): void {\n this._appearance.set(appearance);\n }\n\n /** Focus the native element. */\n focus(): void {\n this.elementRef.nativeElement.focus();\n }\n}\n","import { InjectionToken } from '@angular/core';\nimport type {\n FormFieldAppearance,\n FormFieldColor,\n FormFieldFloatLabel,\n FormFieldSubscriptSizing,\n} from './form-field.variants';\n\n// Re-export types for convenience\nexport type { FormFieldAppearance, FormFieldColor, FormFieldFloatLabel, FormFieldSubscriptSizing };\n\n/**\n * Global configuration defaults for form fields.\n *\n * @example Set defaults in app config\n * ```ts\n * providers: [\n * {\n * provide: FORM_FIELD_DEFAULTS,\n * useValue: {\n * appearance: 'fill',\n * floatLabel: 'always',\n * color: 'primary',\n * }\n * }\n * ]\n * ```\n */\nexport interface FormFieldDefaults {\n appearance?: FormFieldAppearance;\n color?: FormFieldColor;\n floatLabel?: FormFieldFloatLabel;\n hideRequiredMarker?: boolean;\n subscriptSizing?: FormFieldSubscriptSizing;\n}\n\nexport const FORM_FIELD_DEFAULTS: InjectionToken<FormFieldDefaults> = new InjectionToken<FormFieldDefaults>(\n 'FORM_FIELD_DEFAULTS',\n {\n providedIn: 'root',\n factory: (): FormFieldDefaults => ({}),\n }\n);\n","import {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n contentChild,\n contentChildren,\n effect,\n inject,\n input,\n ViewEncapsulation,\n} from '@angular/core';\nimport type { InputSignal, InputSignalWithTransform, Signal } from '@angular/core';\nimport { FormFieldControl } from './form-field-control';\nimport { ComLabel } from './label.directive';\nimport { ComHint } from './hint.directive';\nimport { ComError } from './error.directive';\nimport { ComPrefix } from './prefix.directive';\nimport { ComSuffix } from './suffix.directive';\nimport { ComInput } from './input.directive';\nimport {\n FORM_FIELD_DEFAULTS,\n type FormFieldAppearance,\n type FormFieldColor,\n type FormFieldFloatLabel,\n type FormFieldSubscriptSizing,\n} from './form-field-defaults';\nimport {\n formFieldVariants,\n formFieldContainerVariants,\n formFieldLabelVariants,\n formFieldSubscriptVariants,\n} from './form-field.variants';\nimport { mergeClasses } from 'ngx-com/utils';\n\n/**\n * Form field wrapper providing visual structure for form inputs.\n *\n * Provides floating labels, borders (outline/fill appearance), hints, errors,\n * and prefix/suffix slots. Automatically wires ARIA attributes for accessibility.\n *\n * The form field reads state from its inner `FormFieldControl` child (typically\n * a `ComInput` directive) and renders UI accordingly.\n *\n * @tokens `--color-foreground`, `--color-input-border`, `--color-input-background`,\n * `--color-primary`, `--color-accent`, `--color-warn`, `--color-ring`,\n * `--color-muted`, `--color-muted-foreground`, `--color-disabled`,\n * `--color-disabled-foreground`, `--color-background`, `--radius-input`\n *\n * @example Basic outline field\n * ```html\n * <com-form-field>\n * <label comLabel>Email</label>\n * <input comInput formControlName=\"email\" />\n * <span comHint>We'll never share your email.</span>\n * <span comError match=\"required\">Email is required.</span>\n * </com-form-field>\n * ```\n *\n * @example Fill appearance\n * ```html\n * <com-form-field appearance=\"fill\">\n * <label comLabel>Username</label>\n * <input comInput formControlName=\"username\" />\n * </com-form-field>\n * ```\n *\n * @example With prefix and suffix\n * ```html\n * <com-form-field>\n * <label comLabel>Amount</label>\n * <span comPrefix>$</span>\n * <input comInput type=\"number\" formControlName=\"amount\" />\n * <span comSuffix>.00</span>\n * </com-form-field>\n * ```\n *\n * @example Label with icon and tooltip\n * ```html\n * <com-form-field>\n * <label comLabel>\n * User Role\n * <button type=\"button\" aria-label=\"Help\" comTooltip=\"Admins have full access.\">\n * <com-icon [img]=\"CircleHelpIcon\" size=\"xs\" color=\"muted\" />\n * </button>\n * </label>\n * <input comInput />\n * </com-form-field>\n * ```\n */\n@Component({\n selector: 'com-form-field',\n exportAs: 'comFormField',\n template: `\n <div [class]=\"containerClasses()\" role=\"group\" tabindex=\"-1\" (click)=\"onContainerClick($event)\" (keydown.enter)=\"onContainerActivate()\">\n <div class=\"flex items-center w-full\">\n @if (hasPrefix()) {\n <ng-content select=\"[comPrefix]\" />\n }\n @if (labelChild()) {\n <span [class]=\"labelClasses()\">\n <ng-content select=\"[comLabel]\" />\n @if (showRequiredMarker()) {\n <span class=\"text-warn\" aria-hidden=\"true\"> *</span>\n }\n </span>\n }\n <div class=\"flex-1 min-w-0\">\n <ng-content />\n </div>\n @if (hasSuffix()) {\n <ng-content select=\"[comSuffix]\" />\n }\n </div>\n </div>\n\n <div [class]=\"subscriptClasses()\">\n <div [class.hidden]=\"!showErrors()\">\n <ng-content select=\"[comError]\" />\n </div>\n <div [class.hidden]=\"showErrors()\" class=\"flex justify-between w-full gap-2\">\n <ng-content select=\"[comHint]:not([align='end'])\" />\n <ng-content select=\"[comHint][align='end']\" />\n </div>\n </div>\n `,\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n styles: `\n /* Hide placeholder when label exists and is not floating (label acts as placeholder) */\n com-form-field.com-form-field--has-label:not(.com-form-field--floating) input::placeholder,\n com-form-field.com-form-field--has-label:not(.com-form-field--floating) textarea::placeholder {\n color: transparent;\n }\n\n /* Hide dropdown placeholder when label exists and is not floating */\n com-form-field.com-form-field--has-label:not(.com-form-field--floating) com-dropdown .text-placeholder {\n display: none;\n }\n\n /* Make nested controls fill available space inside form-field */\n com-form-field com-dropdown,\n com-form-field com-datepicker,\n com-form-field com-date-range-picker,\n com-form-field com-time-picker {\n display: block;\n width: 100%;\n }\n\n /* Hide datepicker/date-range placeholder when label exists and is not floating */\n com-form-field.com-form-field--has-label:not(.com-form-field--floating) com-datepicker input::placeholder,\n com-form-field.com-form-field--has-label:not(.com-form-field--floating) com-date-range-picker input::placeholder {\n color: transparent;\n }\n\n /* Hide time picker separators and period button when label is not floating */\n com-form-field.com-form-field--has-label:not(.com-form-field--floating) com-time-picker span[aria-hidden],\n com-form-field.com-form-field--has-label:not(.com-form-field--floating) com-time-picker button {\n visibility: hidden;\n }\n\n /* Re-enable pointer events on tooltip icons inside the label */\n com-form-field .com-form-field__label [comtooltip] {\n pointer-events: auto;\n cursor: help;\n }\n\n /* Visually hide icons inside label when floating to keep the scaled label clean */\n com-form-field.com-form-field--floating .com-form-field__label com-icon {\n visibility: hidden;\n width: 0;\n overflow: hidden;\n }\n `,\n host: {\n '[class]': 'hostClasses()',\n '[class.com-form-field--focused]': 'isFocused()',\n '[class.com-form-field--disabled]': 'isDisabled()',\n '[class.com-form-field--error]': 'hasError()',\n '[class.com-form-field--floating]': 'shouldLabelFloat()',\n '[class.com-form-field--has-label]': '!!labelChild()',\n },\n})\nexport class ComFormField {\n private readonly defaults = inject(FORM_FIELD_DEFAULTS, { optional: true });\n\n // Content children\n readonly control: Signal<FormFieldControl | undefined> = contentChild<FormFieldControl>(FormFieldControl);\n readonly inputDirective: Signal<ComInput | undefined> = contentChild<ComInput>(ComInput);\n readonly labelChild: Signal<ComLabel | undefined> = contentChild<ComLabel>(ComLabel);\n readonly hintChildren: Signal<readonly ComHint[]> = contentChildren<ComHint>(ComHint);\n readonly errorChildren: Signal<readonly ComError[]> = contentChildren<ComError>(ComError);\n readonly prefixChild: Signal<ComPrefix | undefined> = contentChild<ComPrefix>(ComPrefix);\n readonly suffixChild: Signal<ComSuffix | undefined> = contentChild<ComSuffix>(ComSuffix);\n\n // Inputs with defaults fallback\n readonly appearance: InputSignal<FormFieldAppearance> = input<FormFieldAppearance>(\n this.defaults?.appearance ?? 'outline'\n );\n readonly color: InputSignal<FormFieldColor> = input<FormFieldColor>(this.defaults?.color ?? 'primary');\n readonly floatLabel: InputSignal<FormFieldFloatLabel> = input<FormFieldFloatLabel>(\n this.defaults?.floatLabel ?? 'auto'\n );\n readonly hideRequiredMarker: InputSignalWithTransform<boolean, unknown> = input(\n this.defaults?.hideRequiredMarker ?? false,\n { transform: booleanAttribute }\n );\n readonly subscriptSizing: InputSignal<FormFieldSubscriptSizing> = input<FormFieldSubscriptSizing>(\n this.defaults?.subscriptSizing ?? 'fixed'\n );\n readonly userClass: InputSignal<string> = input<string>('', { alias: 'class' });\n\n // Derived state from inner control\n readonly shouldLabelFloat: Signal<boolean> = computed(() => {\n if (this.floatLabel() === 'always') return true;\n return this.control()?.shouldLabelFloat() ?? false;\n });\n\n readonly isFocused: Signal<boolean> = computed(() => this.control()?.focused() ?? false);\n readonly isDisabled: Signal<boolean> = computed(() => this.control()?.disabled() ?? false);\n readonly hasError: Signal<boolean> = computed(() => this.control()?.errorState() ?? false);\n readonly hasPrefix: Signal<boolean> = computed(() => !!this.prefixChild());\n readonly hasSuffix: Signal<boolean> = computed(() => !!this.suffixChild());\n\n readonly showRequiredMarker: Signal<boolean> = computed(() => {\n if (this.hideRequiredMarker()) return false;\n return this.control()?.required() ?? false;\n });\n\n readonly showErrors: Signal<boolean> = computed(() => this.hasError() && this.errorChildren().length > 0);\n\n // Computed classes\n protected readonly hostClasses: Signal<string> = computed(() =>\n mergeClasses(formFieldVariants({ disabled: this.isDisabled() }), this.userClass())\n );\n\n protected readonly containerClasses: Signal<string> = computed(() =>\n formFieldContainerVariants({\n appearance: this.appearance(),\n color: this.color(),\n focused: this.isFocused(),\n error: this.hasError(),\n disabled: this.isDisabled(),\n })\n );\n\n protected readonly labelClasses: Signal<string> = computed(() =>\n formFieldLabelVariants({\n appearance: this.appearance(),\n floating: this.shouldLabelFloat(),\n color: this.color(),\n error: this.hasError(),\n focused: this.isFocused(),\n disabled: this.isDisabled(),\n })\n );\n\n protected readonly subscriptClasses: Signal<string> = computed(() =>\n formFieldSubscriptVariants({ sizing: this.subscriptSizing() })\n );\n\n constructor() {\n // Wire up label to control\n effect(() => {\n const label = this.labelChild();\n const ctrl = this.control();\n if (label && ctrl) {\n label.setForId(ctrl.id());\n }\n });\n\n // Wire up aria-describedby on the control\n effect(() => {\n const ctrl = this.control();\n if (!ctrl || !('setDescribedByIds' in ctrl)) return;\n\n const ids = this.showErrors()\n ? this.errorChildren().filter((e) => e.shouldShow()).map((e) => e.id)\n : this.hintChildren().map((h) => h.id);\n (ctrl as { setDescribedByIds: (ids: string) => void }).setDescribedByIds(ids.join(' '));\n });\n\n // Wire up appearance to control for proper styling\n effect(() => {\n const ctrl = this.control();\n if (ctrl && 'setAppearance' in ctrl) {\n (ctrl as { setAppearance: (appearance: FormFieldAppearance) => void }).setAppearance(this.appearance());\n }\n });\n\n // Wire up control reference to error directives (re-runs on error state change)\n effect(() => {\n // Read hasError to trigger re-evaluation when validation state changes\n this.hasError();\n const ctrl = this.control();\n if (!ctrl) return;\n\n const errors = ctrl.errors?.() ?? null;\n if (errors !== null) {\n // Signal Forms path: pass structured errors to each comError\n this.errorChildren().forEach((error) => error.setSignalErrors(errors as readonly unknown[]));\n } else {\n // Reactive Forms path: pass AbstractControl reference\n const ngControl = ctrl.ngControl?.control ?? null;\n this.errorChildren().forEach((error) => error.setControl(ngControl));\n }\n });\n }\n\n protected onContainerClick(event: MouseEvent): void {\n this.control()?.onContainerClick(event);\n }\n\n protected onContainerActivate(): void {\n this.control()?.onContainerClick(new MouseEvent('click'));\n }\n}\n","// Public API for the form-field component\n\n// Main component\nexport { ComFormField } from './form-field.component';\n\n// Directives\nexport { ComInput } from './input.directive';\nexport { ComLabel } from './label.directive';\nexport { ComHint, type HintAlign } from './hint.directive';\nexport { ComError } from './error.directive';\nexport { ComPrefix } from './prefix.directive';\nexport { ComSuffix } from './suffix.directive';\n\n// Abstract class for custom controls\nexport { FormFieldControl } from './form-field-control';\n\n// Error state matching\nexport { ErrorStateMatcher } from './error-state-matcher';\n\n// Configuration\nexport {\n FORM_FIELD_DEFAULTS,\n type FormFieldDefaults,\n type FormFieldAppearance,\n type FormFieldColor,\n type FormFieldFloatLabel,\n type FormFieldSubscriptSizing,\n} from './form-field-defaults';\n\n// Variants (for advanced customization)\nexport {\n formFieldVariants,\n formFieldContainerVariants,\n formFieldLabelVariants,\n formFieldSubscriptVariants,\n hintVariants,\n errorVariants,\n prefixVariants,\n suffixVariants,\n type FormFieldVariants,\n type FormFieldContainerVariants,\n type FormFieldLabelVariants,\n type FormFieldSubscriptVariants,\n} from './form-field.variants';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["nextId"],"mappings":"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"}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, inject, PLATFORM_ID, RendererFactory2, DestroyRef, signal, computed, effect, Injectable } from '@angular/core';
|
|
3
|
+
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Injection token for theme service configuration.
|
|
7
|
+
*
|
|
8
|
+
* Prefer using `provideComTheme()` instead of providing this token directly.
|
|
9
|
+
*/
|
|
10
|
+
const COM_THEME_CONFIG = new InjectionToken('COM_THEME_CONFIG');
|
|
11
|
+
/**
|
|
12
|
+
* Provides theme service configuration.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* // Minimal — defaults to light/dark, localStorage, system preference watching
|
|
17
|
+
* bootstrapApplication(AppComponent, {
|
|
18
|
+
* providers: [provideComTheme()],
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* // Custom default theme and storage key
|
|
22
|
+
* bootstrapApplication(AppComponent, {
|
|
23
|
+
* providers: [
|
|
24
|
+
* provideComTheme({
|
|
25
|
+
* defaultTheme: 'ocean',
|
|
26
|
+
* storageKey: 'my-app-theme',
|
|
27
|
+
* }),
|
|
28
|
+
* ],
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
function provideComTheme(config) {
|
|
33
|
+
return { provide: COM_THEME_CONFIG, useValue: config ?? {} };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** @internal Default configuration values. */
|
|
37
|
+
const COM_THEME_DEFAULTS = {
|
|
38
|
+
defaultTheme: 'light',
|
|
39
|
+
storageKey: 'com-theme',
|
|
40
|
+
darkSchemeTheme: 'dark',
|
|
41
|
+
lightSchemeTheme: 'light',
|
|
42
|
+
followSystemPreference: true,
|
|
43
|
+
attribute: 'data-theme',
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* SSR-safe theme management service.
|
|
48
|
+
*
|
|
49
|
+
* Manages the active theme by setting a `data-theme` attribute on the document
|
|
50
|
+
* element. Supports localStorage persistence, system `prefers-color-scheme`
|
|
51
|
+
* detection with optional live watching, and a signal-based reactive API.
|
|
52
|
+
*
|
|
53
|
+
* Configure via `provideComTheme()` in your application providers.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* // In your app config
|
|
58
|
+
* providers: [provideComTheme({ defaultTheme: 'ocean' })]
|
|
59
|
+
*
|
|
60
|
+
* // In a component
|
|
61
|
+
* readonly theme = inject(ComTheme);
|
|
62
|
+
* this.theme.setTheme('dark');
|
|
63
|
+
* this.theme.clearPreference(); // revert to following system
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
class ComTheme {
|
|
67
|
+
document = inject(DOCUMENT);
|
|
68
|
+
platformId = inject(PLATFORM_ID);
|
|
69
|
+
renderer = inject(RendererFactory2).createRenderer(null, null);
|
|
70
|
+
destroyRef = inject(DestroyRef);
|
|
71
|
+
config;
|
|
72
|
+
/**
|
|
73
|
+
* Whether the current theme was automatically determined by system preference
|
|
74
|
+
* rather than explicitly set by the user.
|
|
75
|
+
*/
|
|
76
|
+
_isAutomatic;
|
|
77
|
+
/** Current active theme. */
|
|
78
|
+
theme;
|
|
79
|
+
/** Whether the theme is currently following system preference. */
|
|
80
|
+
isAutomatic;
|
|
81
|
+
_theme;
|
|
82
|
+
constructor() {
|
|
83
|
+
const userConfig = inject(COM_THEME_CONFIG, { optional: true });
|
|
84
|
+
this.config = this.resolveConfig(userConfig ?? {});
|
|
85
|
+
const { initial, automatic } = this.determineInitialTheme();
|
|
86
|
+
this._theme = signal(initial, ...(ngDevMode ? [{ debugName: "_theme" }] : []));
|
|
87
|
+
this._isAutomatic = signal(automatic, ...(ngDevMode ? [{ debugName: "_isAutomatic" }] : []));
|
|
88
|
+
this.theme = this._theme.asReadonly();
|
|
89
|
+
this.isAutomatic = computed(() => this._isAutomatic(), ...(ngDevMode ? [{ debugName: "isAutomatic" }] : []));
|
|
90
|
+
// Apply theme + persist on every change
|
|
91
|
+
effect(() => {
|
|
92
|
+
const theme = this._theme();
|
|
93
|
+
this.applyTheme(theme);
|
|
94
|
+
if (!this._isAutomatic()) {
|
|
95
|
+
this.persistTheme(theme);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
this.setupSystemPreferenceWatcher();
|
|
99
|
+
}
|
|
100
|
+
/** Set the active theme explicitly. Persists to localStorage and stops following system preference. */
|
|
101
|
+
setTheme(theme) {
|
|
102
|
+
this._isAutomatic.set(false);
|
|
103
|
+
this._theme.set(theme);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Remove the stored theme preference and revert to following system preference.
|
|
107
|
+
* If system preference watching is enabled, the theme updates to match the current
|
|
108
|
+
* system color scheme. Otherwise, falls back to the configured default theme.
|
|
109
|
+
*/
|
|
110
|
+
clearPreference() {
|
|
111
|
+
this.removeStoredTheme();
|
|
112
|
+
this._isAutomatic.set(true);
|
|
113
|
+
if (this.config.followSystemPreference && isPlatformBrowser(this.platformId)) {
|
|
114
|
+
this._theme.set(this.getSystemTheme());
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
this._theme.set(this.config.defaultTheme);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
resolveConfig(userConfig) {
|
|
121
|
+
const defaultTheme = userConfig.defaultTheme ?? COM_THEME_DEFAULTS.defaultTheme;
|
|
122
|
+
return {
|
|
123
|
+
defaultTheme,
|
|
124
|
+
storageKey: userConfig.storageKey !== undefined
|
|
125
|
+
? userConfig.storageKey
|
|
126
|
+
: COM_THEME_DEFAULTS.storageKey,
|
|
127
|
+
darkSchemeTheme: userConfig.darkSchemeTheme !== undefined
|
|
128
|
+
? userConfig.darkSchemeTheme
|
|
129
|
+
: COM_THEME_DEFAULTS.darkSchemeTheme,
|
|
130
|
+
lightSchemeTheme: userConfig.lightSchemeTheme ?? defaultTheme,
|
|
131
|
+
followSystemPreference: userConfig.followSystemPreference ?? COM_THEME_DEFAULTS.followSystemPreference,
|
|
132
|
+
attribute: userConfig.attribute ?? COM_THEME_DEFAULTS.attribute,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
determineInitialTheme() {
|
|
136
|
+
// 1. Check localStorage for explicit user choice
|
|
137
|
+
const stored = this.getStoredTheme();
|
|
138
|
+
if (stored !== null) {
|
|
139
|
+
return { initial: stored, automatic: false };
|
|
140
|
+
}
|
|
141
|
+
// 2. Check system preference
|
|
142
|
+
if (this.config.darkSchemeTheme !== null && isPlatformBrowser(this.platformId)) {
|
|
143
|
+
return { initial: this.getSystemTheme(), automatic: true };
|
|
144
|
+
}
|
|
145
|
+
// 3. Fall back to default
|
|
146
|
+
return { initial: this.config.defaultTheme, automatic: true };
|
|
147
|
+
}
|
|
148
|
+
getSystemTheme() {
|
|
149
|
+
const win = this.document.defaultView;
|
|
150
|
+
if (!win) {
|
|
151
|
+
return this.config.lightSchemeTheme;
|
|
152
|
+
}
|
|
153
|
+
const prefersDark = win.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
154
|
+
return prefersDark
|
|
155
|
+
? (this.config.darkSchemeTheme ?? this.config.lightSchemeTheme)
|
|
156
|
+
: this.config.lightSchemeTheme;
|
|
157
|
+
}
|
|
158
|
+
setupSystemPreferenceWatcher() {
|
|
159
|
+
if (!this.config.followSystemPreference ||
|
|
160
|
+
this.config.darkSchemeTheme === null ||
|
|
161
|
+
!isPlatformBrowser(this.platformId)) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const win = this.document.defaultView;
|
|
165
|
+
if (!win) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const mediaQuery = win.matchMedia('(prefers-color-scheme: dark)');
|
|
169
|
+
const handler = (event) => {
|
|
170
|
+
// Only react if the user hasn't explicitly overridden
|
|
171
|
+
if (!this._isAutomatic()) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
this._theme.set(event.matches
|
|
175
|
+
? (this.config.darkSchemeTheme ?? this.config.lightSchemeTheme)
|
|
176
|
+
: this.config.lightSchemeTheme);
|
|
177
|
+
};
|
|
178
|
+
mediaQuery.addEventListener('change', handler);
|
|
179
|
+
this.destroyRef.onDestroy(() => {
|
|
180
|
+
mediaQuery.removeEventListener('change', handler);
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
applyTheme(theme) {
|
|
184
|
+
this.renderer.setAttribute(this.document.documentElement, this.config.attribute, theme);
|
|
185
|
+
}
|
|
186
|
+
persistTheme(theme) {
|
|
187
|
+
if (this.config.storageKey === null || !isPlatformBrowser(this.platformId)) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
localStorage.setItem(this.config.storageKey, theme);
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
// Storage full or unavailable — silently ignore
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
getStoredTheme() {
|
|
198
|
+
if (this.config.storageKey === null || !isPlatformBrowser(this.platformId)) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
try {
|
|
202
|
+
return localStorage.getItem(this.config.storageKey);
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
removeStoredTheme() {
|
|
209
|
+
if (this.config.storageKey === null || !isPlatformBrowser(this.platformId)) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
localStorage.removeItem(this.config.storageKey);
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
// Silently ignore
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComTheme, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
220
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComTheme, providedIn: 'root' });
|
|
221
|
+
}
|
|
222
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: ComTheme, decorators: [{
|
|
223
|
+
type: Injectable,
|
|
224
|
+
args: [{ providedIn: 'root' }]
|
|
225
|
+
}], ctorParameters: () => [] });
|
|
226
|
+
|
|
227
|
+
// Service
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Generated bundle index. Do not edit.
|
|
231
|
+
*/
|
|
232
|
+
|
|
233
|
+
export { COM_THEME_CONFIG, ComTheme, provideComTheme };
|
|
234
|
+
//# sourceMappingURL=ngx-com-theme.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ngx-com-theme.mjs","sources":["../../../projects/com/theme/src/theme.providers.ts","../../../projects/com/theme/src/theme.models.ts","../../../projects/com/theme/src/theme.service.ts","../../../projects/com/theme/src/index.ts","../../../projects/com/theme/src/ngx-com-theme.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\nimport type { Provider } from '@angular/core';\nimport type { ComThemeConfig } from './theme.models';\n\n/**\n * Injection token for theme service configuration.\n *\n * Prefer using `provideComTheme()` instead of providing this token directly.\n */\nexport const COM_THEME_CONFIG: InjectionToken<ComThemeConfig> =\n new InjectionToken<ComThemeConfig>('COM_THEME_CONFIG');\n\n/**\n * Provides theme service configuration.\n *\n * @example\n * ```typescript\n * // Minimal — defaults to light/dark, localStorage, system preference watching\n * bootstrapApplication(AppComponent, {\n * providers: [provideComTheme()],\n * });\n *\n * // Custom default theme and storage key\n * bootstrapApplication(AppComponent, {\n * providers: [\n * provideComTheme({\n * defaultTheme: 'ocean',\n * storageKey: 'my-app-theme',\n * }),\n * ],\n * });\n * ```\n */\nexport function provideComTheme(config?: ComThemeConfig): Provider {\n return { provide: COM_THEME_CONFIG, useValue: config ?? {} };\n}\n","/** Consumer-facing configuration for the theme service. */\nexport interface ComThemeConfig {\n /** Default theme when no stored/system preference is found. Default: `'light'`. */\n defaultTheme?: string;\n\n /**\n * localStorage key for persistence. Set to `null` to disable persistence.\n * Default: `'com-theme'`.\n */\n storageKey?: string | null;\n\n /**\n * Theme to apply when the system reports `prefers-color-scheme: dark`.\n * Set to `null` to disable system dark preference detection.\n * Default: `'dark'`.\n */\n darkSchemeTheme?: string | null;\n\n /**\n * Theme to apply when the system reports `prefers-color-scheme: light`\n * (or no preference). Defaults to `defaultTheme`.\n */\n lightSchemeTheme?: string;\n\n /**\n * When `true`, the service listens for live `prefers-color-scheme` changes\n * and applies the mapped theme — unless the user has explicitly set a theme\n * (stored in localStorage). Call `clearPreference()` to revert to system following.\n * Default: `true`.\n */\n followSystemPreference?: boolean;\n\n /**\n * HTML attribute name used to apply the theme on `documentElement`.\n * Default: `'data-theme'`.\n */\n attribute?: string;\n}\n\n/** @internal Resolved config with all defaults applied. */\nexport interface ComThemeResolvedConfig {\n defaultTheme: string;\n storageKey: string | null;\n darkSchemeTheme: string | null;\n lightSchemeTheme: string;\n followSystemPreference: boolean;\n attribute: string;\n}\n\n/** @internal Default configuration values. */\nexport const COM_THEME_DEFAULTS: ComThemeResolvedConfig = {\n defaultTheme: 'light',\n storageKey: 'com-theme',\n darkSchemeTheme: 'dark',\n lightSchemeTheme: 'light',\n followSystemPreference: true,\n attribute: 'data-theme',\n};\n","import {\n Injectable,\n DestroyRef,\n RendererFactory2,\n PLATFORM_ID,\n inject,\n signal,\n computed,\n effect,\n} from '@angular/core';\nimport { DOCUMENT, isPlatformBrowser } from '@angular/common';\nimport type { Renderer2, Signal, WritableSignal } from '@angular/core';\n\nimport { COM_THEME_CONFIG } from './theme.providers';\nimport { COM_THEME_DEFAULTS } from './theme.models';\nimport type { ComThemeResolvedConfig } from './theme.models';\n\n/**\n * SSR-safe theme management service.\n *\n * Manages the active theme by setting a `data-theme` attribute on the document\n * element. Supports localStorage persistence, system `prefers-color-scheme`\n * detection with optional live watching, and a signal-based reactive API.\n *\n * Configure via `provideComTheme()` in your application providers.\n *\n * @example\n * ```typescript\n * // In your app config\n * providers: [provideComTheme({ defaultTheme: 'ocean' })]\n *\n * // In a component\n * readonly theme = inject(ComTheme);\n * this.theme.setTheme('dark');\n * this.theme.clearPreference(); // revert to following system\n * ```\n */\n@Injectable({ providedIn: 'root' })\nexport class ComTheme {\n private readonly document = inject(DOCUMENT);\n private readonly platformId = inject(PLATFORM_ID);\n private readonly renderer: Renderer2 = inject(RendererFactory2).createRenderer(null, null);\n private readonly destroyRef = inject(DestroyRef);\n private readonly config: ComThemeResolvedConfig;\n\n /**\n * Whether the current theme was automatically determined by system preference\n * rather than explicitly set by the user.\n */\n private readonly _isAutomatic: WritableSignal<boolean>;\n\n /** Current active theme. */\n readonly theme: Signal<string>;\n\n /** Whether the theme is currently following system preference. */\n readonly isAutomatic: Signal<boolean>;\n\n private readonly _theme: WritableSignal<string>;\n\n constructor() {\n const userConfig = inject(COM_THEME_CONFIG, { optional: true });\n this.config = this.resolveConfig(userConfig ?? {});\n\n const { initial, automatic } = this.determineInitialTheme();\n this._theme = signal(initial);\n this._isAutomatic = signal(automatic);\n this.theme = this._theme.asReadonly();\n this.isAutomatic = computed(() => this._isAutomatic());\n\n // Apply theme + persist on every change\n effect(() => {\n const theme = this._theme();\n this.applyTheme(theme);\n\n if (!this._isAutomatic()) {\n this.persistTheme(theme);\n }\n });\n\n this.setupSystemPreferenceWatcher();\n }\n\n /** Set the active theme explicitly. Persists to localStorage and stops following system preference. */\n setTheme(theme: string): void {\n this._isAutomatic.set(false);\n this._theme.set(theme);\n }\n\n /**\n * Remove the stored theme preference and revert to following system preference.\n * If system preference watching is enabled, the theme updates to match the current\n * system color scheme. Otherwise, falls back to the configured default theme.\n */\n clearPreference(): void {\n this.removeStoredTheme();\n this._isAutomatic.set(true);\n\n if (this.config.followSystemPreference && isPlatformBrowser(this.platformId)) {\n this._theme.set(this.getSystemTheme());\n } else {\n this._theme.set(this.config.defaultTheme);\n }\n }\n\n private resolveConfig(\n userConfig: Partial<ComThemeResolvedConfig>,\n ): ComThemeResolvedConfig {\n const defaultTheme = userConfig.defaultTheme ?? COM_THEME_DEFAULTS.defaultTheme;\n return {\n defaultTheme,\n storageKey: userConfig.storageKey !== undefined\n ? userConfig.storageKey\n : COM_THEME_DEFAULTS.storageKey,\n darkSchemeTheme: userConfig.darkSchemeTheme !== undefined\n ? userConfig.darkSchemeTheme\n : COM_THEME_DEFAULTS.darkSchemeTheme,\n lightSchemeTheme: userConfig.lightSchemeTheme ?? defaultTheme,\n followSystemPreference:\n userConfig.followSystemPreference ?? COM_THEME_DEFAULTS.followSystemPreference,\n attribute: userConfig.attribute ?? COM_THEME_DEFAULTS.attribute,\n };\n }\n\n private determineInitialTheme(): { initial: string; automatic: boolean } {\n // 1. Check localStorage for explicit user choice\n const stored = this.getStoredTheme();\n if (stored !== null) {\n return { initial: stored, automatic: false };\n }\n\n // 2. Check system preference\n if (this.config.darkSchemeTheme !== null && isPlatformBrowser(this.platformId)) {\n return { initial: this.getSystemTheme(), automatic: true };\n }\n\n // 3. Fall back to default\n return { initial: this.config.defaultTheme, automatic: true };\n }\n\n private getSystemTheme(): string {\n const win = this.document.defaultView;\n if (!win) {\n return this.config.lightSchemeTheme;\n }\n const prefersDark = win.matchMedia('(prefers-color-scheme: dark)').matches;\n return prefersDark\n ? (this.config.darkSchemeTheme ?? this.config.lightSchemeTheme)\n : this.config.lightSchemeTheme;\n }\n\n private setupSystemPreferenceWatcher(): void {\n if (\n !this.config.followSystemPreference ||\n this.config.darkSchemeTheme === null ||\n !isPlatformBrowser(this.platformId)\n ) {\n return;\n }\n\n const win = this.document.defaultView;\n if (!win) {\n return;\n }\n\n const mediaQuery = win.matchMedia('(prefers-color-scheme: dark)');\n const handler = (event: MediaQueryListEvent): void => {\n // Only react if the user hasn't explicitly overridden\n if (!this._isAutomatic()) {\n return;\n }\n this._theme.set(\n event.matches\n ? (this.config.darkSchemeTheme ?? this.config.lightSchemeTheme)\n : this.config.lightSchemeTheme,\n );\n };\n\n mediaQuery.addEventListener('change', handler);\n this.destroyRef.onDestroy(() => {\n mediaQuery.removeEventListener('change', handler);\n });\n }\n\n private applyTheme(theme: string): void {\n this.renderer.setAttribute(\n this.document.documentElement,\n this.config.attribute,\n theme,\n );\n }\n\n private persistTheme(theme: string): void {\n if (this.config.storageKey === null || !isPlatformBrowser(this.platformId)) {\n return;\n }\n try {\n localStorage.setItem(this.config.storageKey, theme);\n } catch {\n // Storage full or unavailable — silently ignore\n }\n }\n\n private getStoredTheme(): string | null {\n if (this.config.storageKey === null || !isPlatformBrowser(this.platformId)) {\n return null;\n }\n try {\n return localStorage.getItem(this.config.storageKey);\n } catch {\n return null;\n }\n }\n\n private removeStoredTheme(): void {\n if (this.config.storageKey === null || !isPlatformBrowser(this.platformId)) {\n return;\n }\n try {\n localStorage.removeItem(this.config.storageKey);\n } catch {\n // Silently ignore\n }\n }\n}\n","// Service\nexport { ComTheme } from './theme.service';\n\n// Types\nexport type { ComThemeConfig } from './theme.models';\n\n// Providers\nexport { COM_THEME_CONFIG, provideComTheme } from './theme.providers';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ngx-com",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "A modern Angular component library built with signals, Tailwind CSS, and semantic design tokens",
|
|
6
6
|
"keywords": [
|
|
@@ -232,6 +232,10 @@
|
|
|
232
232
|
"types": "./types/ngx-com-components-tooltip.d.ts",
|
|
233
233
|
"default": "./fesm2022/ngx-com-components-tooltip.mjs"
|
|
234
234
|
},
|
|
235
|
+
"./theme": {
|
|
236
|
+
"types": "./types/ngx-com-theme.d.ts",
|
|
237
|
+
"default": "./fesm2022/ngx-com-theme.mjs"
|
|
238
|
+
},
|
|
235
239
|
"./tokens": {
|
|
236
240
|
"types": "./types/ngx-com-tokens.d.ts",
|
|
237
241
|
"default": "./fesm2022/ngx-com-tokens.mjs"
|
|
@@ -47,15 +47,32 @@ declare abstract class FormFieldControl<_T = unknown> {
|
|
|
47
47
|
* The form field automatically associates this label with the inner control
|
|
48
48
|
* and positions it appropriately based on appearance and float state.
|
|
49
49
|
*
|
|
50
|
+
* The label renders as `inline-flex` so it can contain inline content such as
|
|
51
|
+
* icons with tooltips. Icons inside the label are visually hidden when the
|
|
52
|
+
* label floats to keep the scaled-down state clean.
|
|
53
|
+
*
|
|
50
54
|
* @tokens none
|
|
51
55
|
*
|
|
52
|
-
* @example
|
|
56
|
+
* @example Plain label
|
|
53
57
|
* ```html
|
|
54
58
|
* <com-form-field>
|
|
55
59
|
* <label comLabel>Email address</label>
|
|
56
60
|
* <input comInput formControlName="email" />
|
|
57
61
|
* </com-form-field>
|
|
58
62
|
* ```
|
|
63
|
+
*
|
|
64
|
+
* @example Label with help icon and tooltip
|
|
65
|
+
* ```html
|
|
66
|
+
* <com-form-field>
|
|
67
|
+
* <label comLabel>
|
|
68
|
+
* User Role
|
|
69
|
+
* <button type="button" aria-label="Help" comTooltip="Admins have full access.">
|
|
70
|
+
* <com-icon [img]="CircleHelpIcon" size="xs" color="muted" />
|
|
71
|
+
* </button>
|
|
72
|
+
* </label>
|
|
73
|
+
* <input comInput />
|
|
74
|
+
* </com-form-field>
|
|
75
|
+
* ```
|
|
59
76
|
*/
|
|
60
77
|
declare class ComLabel {
|
|
61
78
|
readonly labelId: string;
|
|
@@ -514,6 +531,19 @@ declare class ComInput implements FormFieldControl<string>, OnInit, DoCheck {
|
|
|
514
531
|
* <span comSuffix>.00</span>
|
|
515
532
|
* </com-form-field>
|
|
516
533
|
* ```
|
|
534
|
+
*
|
|
535
|
+
* @example Label with icon and tooltip
|
|
536
|
+
* ```html
|
|
537
|
+
* <com-form-field>
|
|
538
|
+
* <label comLabel>
|
|
539
|
+
* User Role
|
|
540
|
+
* <button type="button" aria-label="Help" comTooltip="Admins have full access.">
|
|
541
|
+
* <com-icon [img]="CircleHelpIcon" size="xs" color="muted" />
|
|
542
|
+
* </button>
|
|
543
|
+
* </label>
|
|
544
|
+
* <input comInput />
|
|
545
|
+
* </com-form-field>
|
|
546
|
+
* ```
|
|
517
547
|
*/
|
|
518
548
|
declare class ComFormField {
|
|
519
549
|
private readonly defaults;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { Signal, InjectionToken, Provider } from '@angular/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* SSR-safe theme management service.
|
|
6
|
+
*
|
|
7
|
+
* Manages the active theme by setting a `data-theme` attribute on the document
|
|
8
|
+
* element. Supports localStorage persistence, system `prefers-color-scheme`
|
|
9
|
+
* detection with optional live watching, and a signal-based reactive API.
|
|
10
|
+
*
|
|
11
|
+
* Configure via `provideComTheme()` in your application providers.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* // In your app config
|
|
16
|
+
* providers: [provideComTheme({ defaultTheme: 'ocean' })]
|
|
17
|
+
*
|
|
18
|
+
* // In a component
|
|
19
|
+
* readonly theme = inject(ComTheme);
|
|
20
|
+
* this.theme.setTheme('dark');
|
|
21
|
+
* this.theme.clearPreference(); // revert to following system
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
declare class ComTheme {
|
|
25
|
+
private readonly document;
|
|
26
|
+
private readonly platformId;
|
|
27
|
+
private readonly renderer;
|
|
28
|
+
private readonly destroyRef;
|
|
29
|
+
private readonly config;
|
|
30
|
+
/**
|
|
31
|
+
* Whether the current theme was automatically determined by system preference
|
|
32
|
+
* rather than explicitly set by the user.
|
|
33
|
+
*/
|
|
34
|
+
private readonly _isAutomatic;
|
|
35
|
+
/** Current active theme. */
|
|
36
|
+
readonly theme: Signal<string>;
|
|
37
|
+
/** Whether the theme is currently following system preference. */
|
|
38
|
+
readonly isAutomatic: Signal<boolean>;
|
|
39
|
+
private readonly _theme;
|
|
40
|
+
constructor();
|
|
41
|
+
/** Set the active theme explicitly. Persists to localStorage and stops following system preference. */
|
|
42
|
+
setTheme(theme: string): void;
|
|
43
|
+
/**
|
|
44
|
+
* Remove the stored theme preference and revert to following system preference.
|
|
45
|
+
* If system preference watching is enabled, the theme updates to match the current
|
|
46
|
+
* system color scheme. Otherwise, falls back to the configured default theme.
|
|
47
|
+
*/
|
|
48
|
+
clearPreference(): void;
|
|
49
|
+
private resolveConfig;
|
|
50
|
+
private determineInitialTheme;
|
|
51
|
+
private getSystemTheme;
|
|
52
|
+
private setupSystemPreferenceWatcher;
|
|
53
|
+
private applyTheme;
|
|
54
|
+
private persistTheme;
|
|
55
|
+
private getStoredTheme;
|
|
56
|
+
private removeStoredTheme;
|
|
57
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ComTheme, never>;
|
|
58
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<ComTheme>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Consumer-facing configuration for the theme service. */
|
|
62
|
+
interface ComThemeConfig {
|
|
63
|
+
/** Default theme when no stored/system preference is found. Default: `'light'`. */
|
|
64
|
+
defaultTheme?: string;
|
|
65
|
+
/**
|
|
66
|
+
* localStorage key for persistence. Set to `null` to disable persistence.
|
|
67
|
+
* Default: `'com-theme'`.
|
|
68
|
+
*/
|
|
69
|
+
storageKey?: string | null;
|
|
70
|
+
/**
|
|
71
|
+
* Theme to apply when the system reports `prefers-color-scheme: dark`.
|
|
72
|
+
* Set to `null` to disable system dark preference detection.
|
|
73
|
+
* Default: `'dark'`.
|
|
74
|
+
*/
|
|
75
|
+
darkSchemeTheme?: string | null;
|
|
76
|
+
/**
|
|
77
|
+
* Theme to apply when the system reports `prefers-color-scheme: light`
|
|
78
|
+
* (or no preference). Defaults to `defaultTheme`.
|
|
79
|
+
*/
|
|
80
|
+
lightSchemeTheme?: string;
|
|
81
|
+
/**
|
|
82
|
+
* When `true`, the service listens for live `prefers-color-scheme` changes
|
|
83
|
+
* and applies the mapped theme — unless the user has explicitly set a theme
|
|
84
|
+
* (stored in localStorage). Call `clearPreference()` to revert to system following.
|
|
85
|
+
* Default: `true`.
|
|
86
|
+
*/
|
|
87
|
+
followSystemPreference?: boolean;
|
|
88
|
+
/**
|
|
89
|
+
* HTML attribute name used to apply the theme on `documentElement`.
|
|
90
|
+
* Default: `'data-theme'`.
|
|
91
|
+
*/
|
|
92
|
+
attribute?: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Injection token for theme service configuration.
|
|
97
|
+
*
|
|
98
|
+
* Prefer using `provideComTheme()` instead of providing this token directly.
|
|
99
|
+
*/
|
|
100
|
+
declare const COM_THEME_CONFIG: InjectionToken<ComThemeConfig>;
|
|
101
|
+
/**
|
|
102
|
+
* Provides theme service configuration.
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* // Minimal — defaults to light/dark, localStorage, system preference watching
|
|
107
|
+
* bootstrapApplication(AppComponent, {
|
|
108
|
+
* providers: [provideComTheme()],
|
|
109
|
+
* });
|
|
110
|
+
*
|
|
111
|
+
* // Custom default theme and storage key
|
|
112
|
+
* bootstrapApplication(AppComponent, {
|
|
113
|
+
* providers: [
|
|
114
|
+
* provideComTheme({
|
|
115
|
+
* defaultTheme: 'ocean',
|
|
116
|
+
* storageKey: 'my-app-theme',
|
|
117
|
+
* }),
|
|
118
|
+
* ],
|
|
119
|
+
* });
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
declare function provideComTheme(config?: ComThemeConfig): Provider;
|
|
123
|
+
|
|
124
|
+
export { COM_THEME_CONFIG, ComTheme, provideComTheme };
|
|
125
|
+
export type { ComThemeConfig };
|