valixa-angular-material 0.0.0-stage → 1.0.0-rc.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Valixa contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,164 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
1
+ # valixa-angular-material
2
+
3
+ Angular Material 21.2 control bindings for Valixa's Angular signal bridge. Angular
4
+ and Material remain peers. Built in partial-Ivy Angular Package Format.
5
+
6
+ ## Getting started
7
+
8
+ ```ts
9
+ import { Component } from '@angular/core';
10
+ import { MatInputModule } from '@angular/material/input';
11
+ import { MatFormFieldModule } from '@angular/material/form-field';
12
+ import { createValixa } from 'valixa-angular';
13
+ import { VALIXA_MATERIAL } from 'valixa-angular-material';
14
+
15
+ @Component({
16
+ standalone: true,
17
+ imports: [...VALIXA_MATERIAL, MatInputModule, MatFormFieldModule],
18
+ template: `
19
+ <mat-form-field>
20
+ <mat-label>Email</mat-label>
21
+ <input matInput [valixaField]="email" required>
22
+ <mat-error>{{ email.error() }}</mat-error>
23
+ </mat-form-field>`
24
+ })
25
+ export class EmailField {
26
+ form = createValixa({ email: ['', 'required | email'] }, { id: 'email-form' });
27
+ email = this.form.field('email');
28
+ }
29
+ ```
30
+
31
+ `VALIXA_MATERIAL` contains `ValixaFieldDirective` and `ValixaMaterialDirective`.
32
+ Import your actual Material modules and styles normally. Do not add Angular Forms
33
+ directives on the same control. No FormControl/Group/Validators are created.
34
+
35
+ ## Component contracts
36
+
37
+ | Control | Binding behavior |
38
+ | --- | --- |
39
+ | MatInput text/textarea | Native input events, initial/programmatic value writes, blur, flags, Material ID and error state |
40
+ | MatInput numeric | Explicit `numberCodec(emptyValue)` required; finite numeric/plain values |
41
+ | MatSelect | Its actual CVA, scalar/object/multiple values and reset; native `compareWith` remains application-owned |
42
+ | MatCheckbox | Its actual checked CVA; disabled/readonly and external error descriptions |
43
+ | MatRadioGroup | Its actual CVA, initial selection before children initialize, child events and blur/touch |
44
+ | MatSlideToggle | Its actual CVA and switch semantics |
45
+ | MatAutocomplete input | Its trigger CVA plus required codec; typed drafts distinguished from selected objects |
46
+ | MatDatepicker input | Its date-input CVA plus required codec; dates/parse failures encoded as plain values |
47
+ | MatError | Error-state matcher follows known core errors, including manual/server errors without an Angular control |
48
+
49
+ Material selectors and actual DI tokens identify these supported hosts. No runtime
50
+ class-name guessing or universal event extractor is used. CVAs already normalize
51
+ component-specific events, so their callbacks connect directly to core through codecs.
52
+
53
+ Input fields retain native readonly semantics. Select/Checkbox/RadioGroup/SlideToggle
54
+ lack equivalent readonly behavior, so readonly maps to disabled presentation; core
55
+ still records readonly separately and allows programmatic writes. Autocomplete is
56
+ readonly and closes/disables its panel. Date input readonly blocks typing and its
57
+ picker accessor disables selection. Disabled applies normally. Guarded writers also
58
+ restore values if an already captured event arrives after flags changed.
59
+ Radio group names use the unique form/field ID, preventing same-named fields in
60
+ separate Valixa forms from unchecking each other's native radios.
61
+
62
+ Labels and required metadata belong to the UI. Material manages form-field label
63
+ and helper IDs; the adapter keeps the input/control ID consistent and uses public
64
+ error-state APIs. Standalone checked/group controls can use explicit descriptions:
65
+
66
+ ```html
67
+ <mat-checkbox [valixaField]="terms" required>Agree</mat-checkbox>
68
+ @if (terms.error()) { <mat-error [id]="terms.errorId">{{ terms.error() }}</mat-error> }
69
+ ```
70
+
71
+ Known core errors display immediately; no extra touched-based hiding policy or
72
+ Angular Validator engine is introduced. Core still controls when validation runs.
73
+
74
+ ## Object/multiple selections and clearing
75
+
76
+ ```html
77
+ <mat-select multiple [compareWith]="compareById" [valixaField]="offices">
78
+ @for (office of cities; track office.id) {
79
+ <mat-option [value]="office">{{ office.title }}</mat-option>
80
+ }
81
+ </mat-select>
82
+ ```
83
+
84
+ Core copies plain objects. Supply a stable `compareWith` when identity matters:
85
+ `compareById = (a: City, b: City) => a?.id === b?.id`. Multiple selections use arrays;
86
+ null/undefined scalar models display unselected through the Material CVA. Ensure
87
+ option/clearing values match the field type. Annotate nullable fields or provide
88
+ an explicit codec such as `encode: value => value ?? ''` for a nonnullable string
89
+ clear policy. Valixa does not silently coerce every Material selection.
90
+
91
+ ## Autocomplete: committed options and typed drafts
92
+
93
+ ```ts
94
+ type City = { id: number; title: string };
95
+ const isCity = (value: unknown): value is City =>
96
+ value !== null && typeof value === 'object' && 'id' in value && 'title' in value;
97
+ form = createValixa({ city: { initial: null as City | null, rules: 'required' } });
98
+ city = this.form.field('city');
99
+ cityCodec = autocompleteCodec(isCity);
100
+ displayCity = (value: City | null) => value?.title ?? '';
101
+ ```
102
+
103
+ ```html
104
+ <input matInput [matAutocomplete]="auto" [valixaField]="city" [valixaCodec]="cityCodec">
105
+ <mat-autocomplete #auto="matAutocomplete" [displayWith]="displayCity">
106
+ @for (city of cities; track city.id) { <mat-option [value]="city">{{ city.title }}</mat-option> }
107
+ </mat-autocomplete>
108
+ ```
109
+
110
+ `autocompleteCodec(predicate)` commits selected objects and maps typed strings or
111
+ clearing to null. Editing an existing selection therefore cannot submit a stale
112
+ selected object. Own CVA updates do not echo back and erase the search draft;
113
+ programmatic changes/reset still write through the CVA. Match `required` or custom
114
+ rules to your draft/null policy. The helper is for object options; use a custom
115
+ codec/adapter for free text or string-option contracts that need selection tracking.
116
+
117
+ ## Datepicker and numeric codecs
118
+
119
+ ```ts
120
+ form = createValixa({ date: { initial: null as string | null,
121
+ validate: (value: string | null) => value !== 'invalid' || 'Choose a valid date.' }
122
+ });
123
+ date = this.form.field('date');
124
+ dateCodec = dateOnlyCodec;
125
+ // Component providers: [provideNativeDateAdapter()]
126
+ ```
127
+
128
+ ```html
129
+ <input matInput [matDatepicker]="picker" [valixaField]="date" [valixaCodec]="dateCodec">
130
+ <mat-datepicker-toggle matIconSuffix [for]="picker" />
131
+ <mat-datepicker #picker />
132
+ ```
133
+
134
+ `dateOnlyCodec` is for NativeDateAdapter: valid local civil dates encode as
135
+ YYYY-MM-DD, clearing as null, invalid dates as `'invalid'`. Decode uses local
136
+ components, avoiding UTC day shifts. The adapter distinguishes parse failure from
137
+ empty input using the input text and DateAdapter's invalid-date value. No Date
138
+ instance reaches core. Declare the sentinel validator, and declare matching core
139
+ rules for any Material min/max/filter constraints; picker UI constraints do not
140
+ replace submission validation. Other date libraries need an explicit matching codec
141
+ and have not been tested here.
142
+
143
+ Numeric MatInput uses `numberCodec` from `valixa-angular`, with a typed empty value
144
+ such as null or zero. Unsupported implicit numeric coercion is rejected at runtime
145
+ when no codec is supplied. Strict consumer templates also check codec/field types.
146
+
147
+ ## Requests, custom controls and packaging
148
+
149
+ Use the Angular wrapper's existing `submit` / setup-created `handleSubmit` APIs,
150
+ signals, `setError`, reset and cancellation. Catch request rejections explicitly;
151
+ map your known server payloads rather than guessing HTTP response shapes. Core
152
+ semantics, including conservative error invalidation and stale-run protection,
153
+ remain authoritative. No dependency graph or scheduler is included.
154
+
155
+ Custom controls use `provideValixaControl` or an existing CVA from `valixa-angular`.
156
+ No wrapper component is required. Explicit adapters map value/status/events and own
157
+ their teardown; controlled input/output components get accepted values echoed back,
158
+ while native/CVA bindings preserve their already-updated view.
159
+
160
+ The npm artifact is the `dist/` ng-packagr output with FESM2022, partial declarations,
161
+ maps, peers and this guide. Angular Material is not bundled. The base Angular package
162
+ works without Material installed. Production consumer compilation and browser checks
163
+ use independently installed tarballs. Supported evidence is Angular/Material 21.2
164
+ and reported Chromium on Windows, not other majors or full WCAG conformance.
@@ -0,0 +1,136 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, ElementRef, Renderer2, ChangeDetectorRef, forwardRef, Directive } from '@angular/core';
3
+ import { MatInput } from '@angular/material/input';
4
+ import { MatSelect } from '@angular/material/select';
5
+ import { MatCheckbox } from '@angular/material/checkbox';
6
+ import { MatRadioGroup } from '@angular/material/radio';
7
+ import { MatSlideToggle } from '@angular/material/slide-toggle';
8
+ import { MatAutocompleteTrigger } from '@angular/material/autocomplete';
9
+ import { MatDatepickerInput } from '@angular/material/datepicker';
10
+ import { DateAdapter } from '@angular/material/core';
11
+ import { valueAccessorAdapter, nativeControlAdapter, VALIXA_CONTROL, ValixaFieldDirective } from 'valixa-angular';
12
+
13
+ /** Actual Material selectors and DI tokens define supported hosts; no component-name guessing. */
14
+ class ValixaMaterialDirective {
15
+ get requiresCodec() { return Boolean(this.autocomplete || this.date || this.input?.type === 'number'); }
16
+ constructor() {
17
+ this.preserveViewOnChange = true;
18
+ this.input = inject(MatInput, { self: true, optional: true });
19
+ this.select = inject(MatSelect, { self: true, optional: true });
20
+ this.checkbox = inject(MatCheckbox, { self: true, optional: true });
21
+ this.radio = inject(MatRadioGroup, { self: true, optional: true });
22
+ this.toggle = inject(MatSlideToggle, { self: true, optional: true });
23
+ this.autocomplete = inject(MatAutocompleteTrigger, { self: true, optional: true });
24
+ this.date = inject(MatDatepickerInput, { self: true, optional: true });
25
+ this.dateAdapter = inject(DateAdapter, { optional: true });
26
+ this.host = inject(ElementRef).nativeElement;
27
+ this.renderer = inject(Renderer2);
28
+ this.changeDetector = inject(ChangeDetectorRef);
29
+ this.invalid = false;
30
+ this.matcher = { isErrorState: () => this.invalid };
31
+ if (this.date && this.autocomplete)
32
+ throw new Error('Do not combine datepicker and autocomplete on one Valixa control');
33
+ const accessor = this.date ?? this.autocomplete ?? this.select ?? this.checkbox ?? this.radio ?? this.toggle;
34
+ this.adapter = accessor ? valueAccessorAdapter(accessor) : nativeControlAdapter(this.host, this.renderer);
35
+ if (this.input)
36
+ this.input.errorStateMatcher = this.matcher;
37
+ if (this.select)
38
+ this.select.errorStateMatcher = this.matcher;
39
+ }
40
+ writeValue(value) { this.adapter.writeValue(value); }
41
+ connect(change, touched) {
42
+ return this.adapter.connect(value => {
43
+ // A date CVA emits null for both clearing and parse failure. Preserve invalid drafts explicitly.
44
+ if (this.date && value === null && typeof this.host.value === 'string' && this.host.value.trim())
45
+ change(this.dateAdapter.invalid());
46
+ else
47
+ change(value);
48
+ }, touched);
49
+ }
50
+ setState(state) {
51
+ this.adapter.setState(state);
52
+ this.invalid = state.errors.length > 0;
53
+ this.renderer.setAttribute(this.host, 'aria-required', String(state.required ?? false));
54
+ this.renderer.setAttribute(this.host, 'aria-readonly', String(state.readonly));
55
+ if (this.input) {
56
+ this.input.id = state.id;
57
+ this.input.name = state.name;
58
+ this.input.readonly = state.readonly;
59
+ this.input.disabled = state.disabled;
60
+ this.input.updateErrorState();
61
+ }
62
+ if (this.select) {
63
+ this.select.id = state.id;
64
+ this.select.updateErrorState();
65
+ }
66
+ if (this.checkbox) {
67
+ this.checkbox.id = state.id;
68
+ this.checkbox.name = state.name;
69
+ this.checkbox.ariaDescribedby = state.errors.length ? state.errorId : '';
70
+ }
71
+ if (this.radio)
72
+ this.radio.name = state.id;
73
+ if (this.toggle) {
74
+ this.toggle.id = state.id;
75
+ this.toggle.name = state.name;
76
+ }
77
+ if (this.autocomplete) {
78
+ this.autocomplete.autocompleteDisabled = state.disabled || state.readonly;
79
+ if (this.autocomplete.autocompleteDisabled)
80
+ this.autocomplete.closePanel();
81
+ }
82
+ if (this.checkbox || this.radio || this.toggle) {
83
+ this.renderer.setAttribute(this.host, 'id', state.id);
84
+ if (state.errors.length)
85
+ this.renderer.setAttribute(this.host, 'aria-describedby', state.errorId);
86
+ else
87
+ this.renderer.removeAttribute(this.host, 'aria-describedby');
88
+ for (const input of this.host.querySelectorAll('input'))
89
+ this.renderer.setAttribute(input, 'aria-invalid', String(this.invalid));
90
+ }
91
+ this.changeDetector.markForCheck();
92
+ }
93
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.26", ngImport: i0, type: ValixaMaterialDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
94
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.26", type: ValixaMaterialDirective, isStandalone: true, selector: "input[matInput][valixaField],textarea[matInput][valixaField],mat-select[valixaField],mat-checkbox[valixaField],mat-radio-group[valixaField],mat-slide-toggle[valixaField]", providers: [{ provide: VALIXA_CONTROL, useExisting: forwardRef(() => ValixaMaterialDirective) }], ngImport: i0 }); }
95
+ }
96
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.26", ngImport: i0, type: ValixaMaterialDirective, decorators: [{
97
+ type: Directive,
98
+ args: [{
99
+ selector: 'input[matInput][valixaField],textarea[matInput][valixaField],mat-select[valixaField],mat-checkbox[valixaField],mat-radio-group[valixaField],mat-slide-toggle[valixaField]',
100
+ standalone: true,
101
+ providers: [{ provide: VALIXA_CONTROL, useExisting: forwardRef(() => ValixaMaterialDirective) }]
102
+ }]
103
+ }], ctorParameters: () => [] });
104
+ /** Import this tuple into a standalone component's imports. */
105
+ const VALIXA_MATERIAL = [ValixaFieldDirective, ValixaMaterialDirective];
106
+
107
+ /** NativeDateAdapter civil date codec. 'invalid' must be rejected by a blueprint validator. */
108
+ const dateOnlyCodec = Object.freeze({
109
+ encode(value) {
110
+ if (value === null)
111
+ return null;
112
+ if (!Number.isFinite(value.getTime()))
113
+ return 'invalid';
114
+ return `${String(value.getFullYear()).padStart(4, '0')}-${String(value.getMonth() + 1).padStart(2, '0')}-${String(value.getDate()).padStart(2, '0')}`;
115
+ },
116
+ decode(value) {
117
+ if (!value || !/^\d{4}-\d{2}-\d{2}$/.test(value))
118
+ return null;
119
+ const [year, month, day] = value.split('-').map(Number);
120
+ const date = new Date(0);
121
+ date.setHours(0, 0, 0, 0);
122
+ date.setFullYear(year, month - 1, day);
123
+ return date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day ? date : null;
124
+ }
125
+ });
126
+ /** Object options commit; search strings/clearing commit null, preventing stale selection submission. */
127
+ function autocompleteCodec(isOption) {
128
+ return { encode: value => isOption(value) ? value : null, decode: value => value };
129
+ }
130
+
131
+ /**
132
+ * Generated bundle index. Do not edit.
133
+ */
134
+
135
+ export { VALIXA_MATERIAL, ValixaMaterialDirective, autocompleteCodec, dateOnlyCodec };
136
+ //# sourceMappingURL=valixa-angular-material.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"valixa-angular-material.mjs","sources":["../../src/material.ts","../../src/codecs.ts","../../src/valixa-angular-material.ts"],"sourcesContent":["import { ChangeDetectorRef, Directive, ElementRef, Renderer2, forwardRef, inject } from '@angular/core';\r\nimport type { ControlValueAccessor } from '@angular/forms';\r\nimport { MatInput } from '@angular/material/input';\r\nimport { MatSelect } from '@angular/material/select';\r\nimport { MatCheckbox } from '@angular/material/checkbox';\r\nimport { MatRadioGroup } from '@angular/material/radio';\r\nimport { MatSlideToggle } from '@angular/material/slide-toggle';\r\nimport { MatAutocompleteTrigger } from '@angular/material/autocomplete';\r\nimport { MatDatepickerInput } from '@angular/material/datepicker';\r\nimport { DateAdapter } from '@angular/material/core';\r\nimport { VALIXA_CONTROL, ValixaFieldDirective, nativeControlAdapter, valueAccessorAdapter } from 'valixa-angular';\r\nimport type { ControlStatus, ValixaControlAdapter } from 'valixa-angular';\r\n\r\n/** Actual Material selectors and DI tokens define supported hosts; no component-name guessing. */\r\n@Directive({\r\n selector: 'input[matInput][valixaField],textarea[matInput][valixaField],mat-select[valixaField],mat-checkbox[valixaField],mat-radio-group[valixaField],mat-slide-toggle[valixaField]',\r\n standalone: true,\r\n providers: [{ provide: VALIXA_CONTROL, useExisting: forwardRef(() => ValixaMaterialDirective) }]\r\n})\r\nexport class ValixaMaterialDirective implements ValixaControlAdapter<unknown> {\r\n readonly preserveViewOnChange = true;\r\n private readonly input = inject(MatInput, { self: true, optional: true });\r\n private readonly select = inject(MatSelect, { self: true, optional: true });\r\n private readonly checkbox = inject(MatCheckbox, { self: true, optional: true });\r\n private readonly radio = inject(MatRadioGroup, { self: true, optional: true });\r\n private readonly toggle = inject(MatSlideToggle, { self: true, optional: true });\r\n private readonly autocomplete = inject(MatAutocompleteTrigger, { self: true, optional: true });\r\n private readonly date = inject(MatDatepickerInput, { self: true, optional: true });\r\n private readonly dateAdapter = inject(DateAdapter, { optional: true });\r\n get requiresCodec(): boolean { return Boolean(this.autocomplete || this.date || this.input?.type === 'number'); }\r\n private readonly host = inject(ElementRef).nativeElement;\r\n private readonly renderer = inject(Renderer2);\r\n private readonly changeDetector = inject(ChangeDetectorRef);\r\n private readonly adapter: ValixaControlAdapter<unknown>;\r\n private invalid = false;\r\n private readonly matcher = { isErrorState: () => this.invalid };\r\n constructor() {\r\n if (this.date && this.autocomplete) throw new Error('Do not combine datepicker and autocomplete on one Valixa control');\r\n const accessor: ControlValueAccessor | null = this.date ?? this.autocomplete ?? this.select ?? this.checkbox ?? this.radio ?? this.toggle;\r\n this.adapter = accessor ? valueAccessorAdapter(accessor) : nativeControlAdapter(this.host, this.renderer);\r\n if (this.input) this.input.errorStateMatcher = this.matcher;\r\n if (this.select) this.select.errorStateMatcher = this.matcher;\r\n }\r\n writeValue(value: unknown): void { this.adapter.writeValue(value); }\r\n connect(change: (value: unknown) => void, touched: () => void): () => void {\r\n return this.adapter.connect(value => {\r\n // A date CVA emits null for both clearing and parse failure. Preserve invalid drafts explicitly.\r\n if (this.date && value === null && typeof this.host.value === 'string' && this.host.value.trim()) change(this.dateAdapter!.invalid());\r\n else change(value);\r\n }, touched);\r\n }\r\n setState(state: ControlStatus): void {\r\n this.adapter.setState(state); this.invalid = state.errors.length > 0;\r\n this.renderer.setAttribute(this.host, 'aria-required', String(state.required ?? false));\r\n this.renderer.setAttribute(this.host, 'aria-readonly', String(state.readonly));\r\n if (this.input) { this.input.id = state.id; this.input.name = state.name; this.input.readonly = state.readonly; this.input.disabled = state.disabled; this.input.updateErrorState(); }\r\n if (this.select) { this.select.id = state.id; this.select.updateErrorState(); }\r\n if (this.checkbox) { this.checkbox.id = state.id; this.checkbox.name = state.name; this.checkbox.ariaDescribedby = state.errors.length ? state.errorId : ''; }\r\n if (this.radio) this.radio.name = state.id;\r\n if (this.toggle) { this.toggle.id = state.id; this.toggle.name = state.name; }\r\n if (this.autocomplete) { this.autocomplete.autocompleteDisabled = state.disabled || state.readonly; if (this.autocomplete.autocompleteDisabled) this.autocomplete.closePanel(); }\r\n if (this.checkbox || this.radio || this.toggle) {\r\n this.renderer.setAttribute(this.host, 'id', state.id);\r\n if (state.errors.length) this.renderer.setAttribute(this.host, 'aria-describedby', state.errorId); else this.renderer.removeAttribute(this.host, 'aria-describedby');\r\n for (const input of this.host.querySelectorAll('input')) this.renderer.setAttribute(input, 'aria-invalid', String(this.invalid));\r\n }\r\n this.changeDetector.markForCheck();\r\n }\r\n}\r\n/** Import this tuple into a standalone component's imports. */\r\nexport const VALIXA_MATERIAL = [ValixaFieldDirective, ValixaMaterialDirective] as const;\r\n","import type { ValueCodec } from 'valixa-angular';\r\n\r\n/** NativeDateAdapter civil date codec. 'invalid' must be rejected by a blueprint validator. */\r\nexport const dateOnlyCodec: ValueCodec<string | null, Date | null> = Object.freeze({\r\n encode(value: Date | null): string | null {\r\n if (value === null) return null;\r\n if (!Number.isFinite(value.getTime())) return 'invalid';\r\n return `${String(value.getFullYear()).padStart(4, '0')}-${String(value.getMonth() + 1).padStart(2, '0')}-${String(value.getDate()).padStart(2, '0')}`;\r\n },\r\n decode(value: string | null): Date | null {\r\n if (!value || !/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) return null;\r\n const [year, month, day] = value.split('-').map(Number) as [number, number, number];\r\n const date = new Date(0); date.setHours(0, 0, 0, 0); date.setFullYear(year, month - 1, day);\r\n return date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day ? date : null;\r\n }\r\n});\r\n/** Object options commit; search strings/clearing commit null, preventing stale selection submission. */\r\nexport function autocompleteCodec<T extends object>(isOption: (value: unknown) => value is T): ValueCodec<T | null, T | string | null> {\r\n return { encode: value => isOption(value) ? value : null, decode: value => value };\r\n}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,64 @@
1
1
  {
2
2
  "name": "valixa-angular-material",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "1.0.0-rc.1",
4
+ "type": "module",
5
+ "license": "MIT",
6
+ "sideEffects": false,
7
+ "description": "Angular Material value-accessor and error presentation bindings for Valixa",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./types/valixa-angular-material.d.ts",
11
+ "default": "./fesm2022/valixa-angular-material.mjs"
12
+ },
13
+ "./package.json": {
14
+ "default": "./package.json"
15
+ }
16
+ },
17
+ "peerDependencies": {
18
+ "@angular/core": "~21.2.0",
19
+ "@angular/common": "~21.2.0",
20
+ "@angular/forms": "~21.2.0",
21
+ "@angular/material": "~21.2.0",
22
+ "@angular/cdk": "~21.2.0",
23
+ "valixa-angular": "^1.0.0-rc.1",
24
+ "valixa": "^1.0.0-rc.1",
25
+ "rxjs": "^7.8.0"
26
+ },
27
+ "dependencies": {
28
+ "tslib": "^2.8.1"
29
+ },
30
+ "publishConfig": {
31
+ "access": "public",
32
+ "registry": "https://registry.npmjs.org/",
33
+ "tag": "next"
34
+ },
35
+ "keywords": [
36
+ "forms",
37
+ "validation",
38
+ "typescript",
39
+ "headless",
40
+ "valixa",
41
+ "angular"
42
+ ],
43
+ "engines": {
44
+ "node": ">=20.19.0"
45
+ },
46
+ "types": "./types/valixa-angular-material.d.ts",
47
+ "files": [
48
+ "fesm2022",
49
+ "types",
50
+ "README.md",
51
+ "LICENSE"
52
+ ],
53
+ "repository": {
54
+ "type": "git",
55
+ "url": "https://github.com/Heshammesalam/Valixa.git",
56
+ "directory": "packages/angular-material"
57
+ },
58
+ "homepage": "https://github.com/Heshammesalam/Valixa#readme",
59
+ "bugs": {
60
+ "url": "https://github.com/Heshammesalam/Valixa/issues"
61
+ },
62
+ "module": "fesm2022/valixa-angular-material.mjs",
63
+ "typings": "types/valixa-angular-material.d.ts"
6
64
  }
@@ -0,0 +1,38 @@
1
+ import { ValixaFieldDirective, ValixaControlAdapter, ControlStatus, ValueCodec } from 'valixa-angular';
2
+ import * as i0 from '@angular/core';
3
+
4
+ /** Actual Material selectors and DI tokens define supported hosts; no component-name guessing. */
5
+ declare class ValixaMaterialDirective implements ValixaControlAdapter<unknown> {
6
+ readonly preserveViewOnChange = true;
7
+ private readonly input;
8
+ private readonly select;
9
+ private readonly checkbox;
10
+ private readonly radio;
11
+ private readonly toggle;
12
+ private readonly autocomplete;
13
+ private readonly date;
14
+ private readonly dateAdapter;
15
+ get requiresCodec(): boolean;
16
+ private readonly host;
17
+ private readonly renderer;
18
+ private readonly changeDetector;
19
+ private readonly adapter;
20
+ private invalid;
21
+ private readonly matcher;
22
+ constructor();
23
+ writeValue(value: unknown): void;
24
+ connect(change: (value: unknown) => void, touched: () => void): () => void;
25
+ setState(state: ControlStatus): void;
26
+ static ɵfac: i0.ɵɵFactoryDeclaration<ValixaMaterialDirective, never>;
27
+ static ɵdir: i0.ɵɵDirectiveDeclaration<ValixaMaterialDirective, "input[matInput][valixaField],textarea[matInput][valixaField],mat-select[valixaField],mat-checkbox[valixaField],mat-radio-group[valixaField],mat-slide-toggle[valixaField]", never, {}, {}, never, never, true, never>;
28
+ }
29
+ /** Import this tuple into a standalone component's imports. */
30
+ declare const VALIXA_MATERIAL: readonly [typeof ValixaFieldDirective, typeof ValixaMaterialDirective];
31
+
32
+ /** NativeDateAdapter civil date codec. 'invalid' must be rejected by a blueprint validator. */
33
+ declare const dateOnlyCodec: ValueCodec<string | null, Date | null>;
34
+ /** Object options commit; search strings/clearing commit null, preventing stale selection submission. */
35
+ declare function autocompleteCodec<T extends object>(isOption: (value: unknown) => value is T): ValueCodec<T | null, T | string | null>;
36
+
37
+ export { VALIXA_MATERIAL, ValixaMaterialDirective, autocompleteCodec, dateOnlyCodec };
38
+ //# sourceMappingURL=valixa-angular-material.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"valixa-angular-material.d.ts","sources":["../../src/material.ts","../../src/codecs.ts"],"mappings":";;;AAaA;AACA,cAKa,uBAAwB,YAAW,oBAAoB;;AAElE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;;AAQA;AACA;AAOA,oBAAgB,aAAa;yCAhClB,uBAAuB;2CAAvB,uBAAuB;AAiDnC;AACD;AACA,cAAa,eAAe,mBAAA,oBAAA,SAAA,uBAAA;;ACpE5B;AACA,cAAa,aAAa,EAAE,UAAU,gBAAgB,IAAI;AAa1D;AACA,iBAAgB,iBAAiB,8DAA8D,UAAU;;","names":[]}