@softheon/armature 21.2.0 → 21.2.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softheon/armature",
3
- "version": "21.2.0",
3
+ "version": "21.2.2",
4
4
  "dependencies": {
5
5
  "tslib": "^2.8.1"
6
6
  },
@@ -24,7 +24,8 @@
24
24
  "angular-oauth2-oidc-jwks": "~20.0.0",
25
25
  "ngx-cookie-service": "^21.1.0",
26
26
  "rxjs": "~7.8.0",
27
- "moment": "^2.29.4"
27
+ "moment": "^2.29.4",
28
+ "@types/json-schema": "^7.0.15"
28
29
  },
29
30
  "peerDependenciesMeta": {
30
31
  "ag-grid-community": {
@@ -32,6 +33,9 @@
32
33
  },
33
34
  "ag-grid-angular": {
34
35
  "optional": true
36
+ },
37
+ "@types/json-schema": {
38
+ "optional": true
35
39
  }
36
40
  },
37
41
  "module": "fesm2022/softheon-armature.mjs",
@@ -47,6 +51,10 @@
47
51
  "./ag-grid-components": {
48
52
  "types": "./types/softheon-armature-ag-grid-components.d.ts",
49
53
  "default": "./fesm2022/softheon-armature-ag-grid-components.mjs"
54
+ },
55
+ "./b2b": {
56
+ "types": "./types/softheon-armature-b2b.d.ts",
57
+ "default": "./fesm2022/softheon-armature-b2b.mjs"
50
58
  }
51
59
  },
52
60
  "sideEffects": false
@@ -0,0 +1,584 @@
1
+ import * as _angular_core from '@angular/core';
2
+ import { OnInit } from '@angular/core';
3
+ import { MatExpansionPanel } from '@angular/material/expansion';
4
+ import { JSONSchema7 } from 'json-schema';
5
+ export { JSONSchema7 as JsonSchema } from 'json-schema';
6
+
7
+ /**
8
+ * @fileoverview Models for the SofB2BJsonEditorComponent.
9
+ *
10
+ * Public API types: JsonValue, JsonObject, FieldConfig.
11
+ * Internal types: JsonEntry, JsonNode, JsonNamespace.
12
+ *
13
+ * Consumers interact with the editor via JsonObject input/output and
14
+ * FieldConfig schema only. JsonEntry is internal to the editor's
15
+ * flatten/unflatten pipeline.
16
+ *
17
+ * File is intentionally free of Angular dependencies so it can be
18
+ * imported by both Angular components and plain TypeScript utilities
19
+ * (e.g. unit tests, data-manager helpers).
20
+ */
21
+ /**
22
+ * Recursive type representing any valid JSON value.
23
+ * Replaces `any` in the public API for type safety while still
24
+ * supporting the full range of JSON structures.
25
+ *
26
+ * Usage:
27
+ * JsonEntry.value, JsonNode.editableValue
28
+ */
29
+ type JsonValue = string | number | boolean | null | JsonValue[] | JsonObject;
30
+ /**
31
+ * A JSON object — a string-keyed dictionary of JsonValue.
32
+ * Extracted as a named interface to prevent TypeScript's
33
+ * "excessively deep type instantiation" error on recursive
34
+ * inline mapped types.
35
+ */
36
+ interface JsonObject {
37
+ [key: string]: JsonValue;
38
+ }
39
+ /**
40
+ * A single key/value entry in the flattened JSON representation.
41
+ * Used internally by the editor's flatten/unflatten pipeline.
42
+ * Consumers should not reference this type — pass JsonObject directly.
43
+ *
44
+ * Keys use path notation produced by flattenJson():
45
+ * dot notation for objects: "BillingInformation.APTCAmount"
46
+ * bracket notation for arrays: "Invoice.Payments[0].Amount"
47
+ * quoted brackets for special keys: '["Discount Amount"]'
48
+ * '["Corrected1095-B"]'
49
+ *
50
+ * @internal
51
+ */
52
+ interface JsonEntry {
53
+ key: string;
54
+ value: JsonValue;
55
+ }
56
+ /**
57
+ * Supported input control types for scalar fields.
58
+ * The correct type is inferred from the value when not explicitly provided.
59
+ *
60
+ * Current rendering:
61
+ * text → <input type="text">
62
+ * number → <input type="number">
63
+ * date → <input type="date">
64
+ * toggle → <mat-slide-toggle>
65
+ * select → <mat-select>
66
+ *
67
+ * Extend this union as new input types are introduced.
68
+ * Adding a new type requires:
69
+ * 1. Adding it to this union
70
+ * 2. Adding a *ngSwitchCase in sof-b2b-json-node.component.html
71
+ * 3. Optionally updating inferInputType() in json-tree.utils.ts
72
+ */
73
+ type FieldInputType = 'text' | 'number' | 'date' | 'toggle' | 'select';
74
+ /**
75
+ * Configuration describing how a field should render and behave.
76
+ *
77
+ * Provided via companion schema: the [schema] input on SofB2BJsonEditorComponent,
78
+ * keyed by full path string (e.g. 'BillingInformation.APTCAmount').
79
+ *
80
+ * When no schema is provided, inputType is inferred from the value at
81
+ * build time via inferInputType() in json-tree.utils.ts.
82
+ */
83
+ interface FieldConfig {
84
+ /**
85
+ * Which input control to render for this field.
86
+ * Inferred from the value if not explicitly provided.
87
+ */
88
+ inputType?: FieldInputType;
89
+ /**
90
+ * Override the display label shown above the input.
91
+ * Defaults to the key segment (e.g. "APTCAmount" from "BillingInformation.APTCAmount").
92
+ */
93
+ label?: string;
94
+ /**
95
+ * Prevents user edits when true.
96
+ * Read-only fields are visually distinct and skipped in tab order.
97
+ * Defaults to false.
98
+ */
99
+ readOnly?: boolean;
100
+ /** Placeholder text shown inside the input when the value is empty. */
101
+ placeholder?: string;
102
+ /**
103
+ * Option list for inputType 'select'.
104
+ * Required when inputType is 'select' — if omitted, falls back to 'text'.
105
+ */
106
+ options?: string[];
107
+ }
108
+ /**
109
+ * Discriminated union describing the structural type of a JSON value.
110
+ *
111
+ * scalar — a primitive value (string, number, boolean, null)
112
+ * renders as an editable input
113
+ * array — a JSON array
114
+ * renders as a collapsible expansion panel with indexed children
115
+ * object — a JSON object
116
+ * renders as a collapsible expansion panel with named children
117
+ */
118
+ type JsonNodeType = 'scalar' | 'array' | 'object';
119
+ /**
120
+ * Internal representation of a single node in the JSON tree.
121
+ *
122
+ * Built once at data-load time by buildNode() in json-editor.utils.ts.
123
+ * Never mutated after construction except for editableValue (user edits)
124
+ * and key (prefix stripped by buildNamespaces()).
125
+ *
126
+ * Consumers should not reference this type — it is an implementation detail
127
+ * of the JsonEditor rendering layer.
128
+ */
129
+ interface JsonNode {
130
+ /**
131
+ * Display key with dot-prefix stripped by buildNamespaces().
132
+ * e.g. "BillingInformation.APTCAmount" becomes "APTCAmount"
133
+ * after the "BillingInformation" namespace is extracted.
134
+ *
135
+ * Mutated by buildNamespaces() — use fullKey for any path operations.
136
+ */
137
+ key: string;
138
+ /**
139
+ * Original full path key as produced by flattenJson().
140
+ * e.g. "BillingInformation.APTCAmount" or "Invoice.Payments[0].Amount"
141
+ *
142
+ * Never mutated. Used by:
143
+ * - flattenNamespaces() to reconstruct original keys on emit
144
+ * - HTML [id] and [aria-label] bindings for accessibility
145
+ * - Console warnings in guardrails
146
+ */
147
+ fullKey: string;
148
+ /** Structural type — drives which template branch renders this node. */
149
+ type: JsonNodeType;
150
+ /**
151
+ * Nesting depth starting from 0 at the top level.
152
+ * Used by the depth guardrail in buildNodesFromEntries() and json-node template.
153
+ */
154
+ depth: number;
155
+ /**
156
+ * Snapshot of the original value at tree-build time.
157
+ * Present only on scalar nodes. Never mutated after construction.
158
+ * Used by SofB2BJsonNodeComponent to compute dirty state
159
+ * (editableValue !== originalValue).
160
+ */
161
+ originalValue?: JsonValue;
162
+ /**
163
+ * The live editable value. Present only on scalar nodes.
164
+ * Bound directly to [(ngModel)] in sof-b2b-json-node.component.html.
165
+ * Initialised from the deep-cloned original value at build time —
166
+ * changes here never affect the consumer's source data.
167
+ */
168
+ editableValue?: JsonValue;
169
+ /**
170
+ * Child nodes. Present only on array and object nodes.
171
+ * Empty array means the container exists but has no items —
172
+ * rendered as an empty placeholder, not an expansion panel.
173
+ */
174
+ children?: JsonNode[];
175
+ /**
176
+ * Field rendering configuration. Always present — never undefined.
177
+ * Guarantees that sof-b2b-json-node.component.html can safely read
178
+ * node.config.inputType without null checks.
179
+ *
180
+ * Set at build time from (in priority order):
181
+ * 1. Companion schema lookup
182
+ * 2. inferInputType() result for scalars
183
+ * 3. Empty object {} for containers
184
+ */
185
+ config: FieldConfig;
186
+ }
187
+ /**
188
+ * A top-level grouping of JsonNodes sharing a dot-notation prefix.
189
+ *
190
+ * Built by buildNamespaces() in json-editor.utils.ts.
191
+ * Rendered as the outermost expansion panels in sof-b2b-json-editor.component.html.
192
+ *
193
+ * Examples from the billing JSON:
194
+ * { prefix: 'BillingInformation', nodes: [...] }
195
+ * { prefix: 'Invoice', nodes: [...] }
196
+ * { prefix: '—', nodes: [...] } ← ungrouped top-level keys
197
+ *
198
+ * The "—" namespace is always sorted to the top by buildNamespaces().
199
+ */
200
+ interface JsonNamespace {
201
+ /**
202
+ * The shared prefix extracted from node keys.
203
+ * e.g. "BillingInformation", "Invoice", "Adjustments"
204
+ * or "—" for keys without a dot-notation prefix
205
+ * (e.g. "COCOFileName", "ScanLine", "Discount Amount").
206
+ */
207
+ prefix: string;
208
+ /** All nodes belonging to this namespace, in original data order. */
209
+ nodes: JsonNode[];
210
+ }
211
+
212
+ /**
213
+ * @fileoverview Path parsing, flatten/unflatten, and normalization utilities.
214
+ *
215
+ * Converts between nested JSON objects and flat JsonEntry[] arrays using
216
+ * dot-notation and bracket-notation path strings.
217
+ *
218
+ * All functions are stateless and side-effect free.
219
+ * No Angular dependencies — fully unit-testable in isolation.
220
+ *
221
+ * Round-trip guarantee: unflattenJson(flattenJson(data)) deep-equals data
222
+ */
223
+
224
+ /**
225
+ * Parses a path string into an array of typed segments.
226
+ *
227
+ * Handles:
228
+ * "a.b.c" → ['a', 'b', 'c']
229
+ * "a.b[0].c" → ['a', 'b', 0, 'c']
230
+ * '["Discount Amount"]' → ['Discount Amount']
231
+ * 'a["Corrected1095-B"].val' → ['a', 'Corrected1095-B', 'val']
232
+ *
233
+ * Three capture groups:
234
+ * 1 — plain key segment: word chars only (no dots, brackets, quotes)
235
+ * 2 — numeric array index: [0], [1], [42]
236
+ * 3 — quoted bracket key: ["any string here"]
237
+ */
238
+ declare function parsePath(path: string): (string | number)[];
239
+ /**
240
+ * Reconstructs a nested object from JsonEntry[].
241
+ * Parses both dot notation and bracket notation paths.
242
+ *
243
+ * Inverse of flattenJson — guarantees round-trip fidelity.
244
+ *
245
+ * @param entries Flat JsonEntry[] produced by flattenJson()
246
+ */
247
+ declare function unflattenJson(entries: JsonEntry[]): JsonObject;
248
+ /**
249
+ * Flattens a raw nested JSON object into JsonEntry[].
250
+ * Convenience wrapper around flattenJson() — used by SofB2BJsonEditorComponent
251
+ * to convert its JsonObject input into flat entries for tree building.
252
+ *
253
+ * @example
254
+ * normalizeToEntries({ BillingInformation: { APTCAmount: '0.00' } })
255
+ * // → [{ key: 'BillingInformation.APTCAmount', value: '0.00' }]
256
+ */
257
+ declare function normalizeToEntries(input: JsonObject): JsonEntry[];
258
+
259
+ /**
260
+ * @fileoverview Tree construction, value reconstruction, and namespace emission.
261
+ *
262
+ * Builds a JsonNode tree from flat JsonEntry[] for rendering, and reconstructs
263
+ * flat JsonEntry[] from the tree for emission back to the consumer.
264
+ *
265
+ * Also includes type inference, field config resolution, and validation.
266
+ *
267
+ * All functions are stateless and side-effect free (except console warnings).
268
+ * No Angular dependencies — fully unit-testable in isolation.
269
+ *
270
+ * Guardrails:
271
+ * Max depth cap — MAX_DEPTH constant in buildNodesFromEntries()
272
+ * Duplicate key warning — Set check in validateDuplicateKeys()
273
+ * Undefined output guard — null coalesce in flattenNamespaces()
274
+ */
275
+
276
+ /**
277
+ * MAX_DEPTH guardrail.
278
+ * Nodes beyond this depth are skipped rather than recursed into.
279
+ * Prevents call-stack overflow on pathologically deep structures.
280
+ */
281
+ declare const MAX_DEPTH = 20;
282
+ /**
283
+ * Duplicate key guardrail.
284
+ * Warns when two entries share the same key. The second occurrence
285
+ * silently overwrites the first, producing incorrect emitted data.
286
+ *
287
+ * @param entries The JsonEntry[] to check for duplicate keys
288
+ */
289
+ declare function validateDuplicateKeys(entries: JsonEntry[]): void;
290
+ /**
291
+ * Builds a JsonNode tree from flat JsonEntry[] by grouping path segments
292
+ * into their natural hierarchy.
293
+ *
294
+ * This is the core tree-building function. It takes fully-flattened entries
295
+ * (e.g. "AdjustmentReasons[0].Code") and reconstructs the nested structure
296
+ * that the template renders as collapsible expansion panels.
297
+ *
298
+ * Handles three value shapes:
299
+ * 1. Empty [] or {} — object/array with zero length, render as empty container
300
+ * 2. Scalar / primitive — null, string, number, boolean — render as input
301
+ * 3. Container — multiple children or deeper path — recurse
302
+ *
303
+ * Depth guardrail — stops recursing beyond MAX_DEPTH.
304
+ *
305
+ * @param entries Flat JsonEntry[] with namespace prefix already stripped
306
+ * @param schema Optional companion schema for field config
307
+ * @param depth Current nesting depth (0 = top level within namespace)
308
+ */
309
+ declare function buildNodesFromEntries(entries: JsonEntry[], schema?: Record<string, FieldConfig>, depth?: number): JsonNode[];
310
+ /**
311
+ * Flattens JsonNamespace[] back into JsonEntry[] for emission.
312
+ *
313
+ * Undefined output guardrail — reconstructValue() returning undefined
314
+ * is coalesced to null rather than emitting undefined to the consumer.
315
+ *
316
+ * @param namespaces The current namespace tree to flatten
317
+ */
318
+ declare function flattenNamespaces(namespaces: JsonNamespace[]): JsonEntry[];
319
+
320
+ /**
321
+ * @fileoverview Search filtering and tree pruning utilities.
322
+ *
323
+ * Filters a JsonNamespace[] tree to only nodes matching a search term.
324
+ * Uses recursive tree pruning — non-matching branches are removed,
325
+ * matching ancestors are preserved.
326
+ *
327
+ * All functions are stateless and side-effect free.
328
+ * No Angular dependencies — fully unit-testable in isolation.
329
+ */
330
+
331
+ /**
332
+ * Filters a JsonNamespace[] tree to only namespaces and nodes
333
+ * matching the search term. Uses tree pruning — non-matching branches
334
+ * are removed, matching ancestors are preserved.
335
+ *
336
+ * Returns the original array reference unchanged if term is below
337
+ * the minimum length — no filtering overhead on short terms.
338
+ *
339
+ * @param namespaces The full namespace tree to filter
340
+ * @param term The search string (case-insensitive, min 2 chars)
341
+ */
342
+ declare function filterNamespaces(namespaces: JsonNamespace[], term: string): JsonNamespace[];
343
+
344
+ /**
345
+ * The Sof B2B JSON Editor Component
346
+ Root JsonEditor template.
347
+ Renders JsonNamespace[] as top-level expansion panels.
348
+ All child node rendering is delegated to sof-b2b-json-node.
349
+ */
350
+ declare class SofB2BJsonEditorComponent implements OnInit {
351
+ /**
352
+ * Raw nested JSON object to edit.
353
+ * Internally flattened into JsonEntry[] for tree construction.
354
+ * ⚠️ Pass a new object reference to trigger re-computation.
355
+ */
356
+ readonly data: _angular_core.InputSignal<JsonObject>;
357
+ /**
358
+ * Optional JSON Schema (Draft 7+) describing the data shape.
359
+ * When provided, the editor parses it internally and uses it to configure
360
+ * field types, labels, and dropdown options at tree-build time.
361
+ * Fields not found in the schema fall back to inferInputType().
362
+ *
363
+ * @example
364
+ * const schema: JsonSchema = {
365
+ * type: 'object',
366
+ * properties: {
367
+ * Theme: { type: 'string', enum: ['dark', 'light'] },
368
+ * MaxRetries: { type: 'integer', title: 'Max Retries' },
369
+ * },
370
+ * };
371
+ */
372
+ readonly schema: _angular_core.InputSignal<JSONSchema7>;
373
+ /** Debounce delay in ms before dataChanged emits. Default: 1500. */
374
+ readonly debounceMs: _angular_core.InputSignal<number>;
375
+ /** Message shown when data is empty. */
376
+ readonly emptyMessage: _angular_core.InputSignal<string>;
377
+ /** Message shown when search returns no matches. */
378
+ readonly noResultsMessage: _angular_core.InputSignal<string>;
379
+ /**
380
+ * Search term for filtering the tree.
381
+ * Minimum 2 characters to trigger filtering — shorter terms
382
+ * match too broadly and run the full recursive walk unnecessarily.
383
+ */
384
+ readonly searchTerm: _angular_core.InputSignal<string>;
385
+ /** Whether or not to render the json editor in read-only mode.
386
+ * When true, all scalar fields display as plaintext, toggles are disabled,
387
+ * and the change$/debounce subscription is never created. Default: false. */
388
+ readonly readOnly: _angular_core.InputSignal<boolean>;
389
+ /**
390
+ * Emits the full reconstructed nested JSON object after debounce period.
391
+ * Pass directly to any consumer expecting a JsonObject.
392
+ * 🛡️ Suppressed when readOnly is true.
393
+ */
394
+ readonly dataChanged: _angular_core.OutputEmitterRef<JsonObject>;
395
+ /**
396
+ * Emits an error message string when a parsing or validation error occurs.
397
+ * Consumers can subscribe to display errors in their own UI (snackbar, toast, etc.).
398
+ */
399
+ readonly parseError: _angular_core.OutputEmitterRef<string>;
400
+ /** Top-level namespace panels only — not nested array/object panels. */
401
+ readonly namespacePanels: _angular_core.Signal<readonly MatExpansionPanel[]>;
402
+ /**
403
+ * Parsed JSON Schema → flat Record<string, FieldConfig>.
404
+ * Recomputes when the schema input changes. Used internally by
405
+ * buildTree() for field config resolution. Consumers never see this.
406
+ */
407
+ private readonly parsedSchema;
408
+ /**
409
+ * Internal flat entries derived from the data input.
410
+ * Recomputes automatically when data() changes.
411
+ * Protected so the template can check entries().length for empty state.
412
+ */
413
+ protected readonly entries: _angular_core.Signal<JsonEntry[]>;
414
+ /**
415
+ * Full namespace tree built from internal entries.
416
+ * Recomputes automatically when data() or schema() changes.
417
+ * 🔑 Duplicate key validation runs on every recomputation.
418
+ */
419
+ protected readonly namespaces: _angular_core.Signal<JsonNamespace[]>;
420
+ /**
421
+ * Filtered namespace tree derived from namespaces + searchTerm.
422
+ * Recomputes automatically when either signal changes.
423
+ * No manual sync needed — computed() handles both inputs.
424
+ *
425
+ * This replaces the manual sync bug where filteredNamespaces
426
+ * started as [] and wasn't populated until a search fired.
427
+ */
428
+ protected readonly filteredNamespaces: _angular_core.Signal<JsonNamespace[]>;
429
+ /** The Internal state changes */
430
+ private readonly change$;
431
+ /**
432
+ * 💾 DestroyRef — takeUntilDestroyed() auto-completes the debounce
433
+ * subscription on component destroy, preventing memory leaks and
434
+ * emissions to destroyed parent components.
435
+ */
436
+ private readonly destroyRef;
437
+ ngOnInit(): void;
438
+ /** Programmatically opens all top-level namespace panels. */
439
+ openAll(): void;
440
+ /** Programmatically closes all top-level namespace panels. */
441
+ closeAll(): void;
442
+ /** Handles value changes from any child SofB2BJsonNodeComponent. Pushes into the debounce stream. */
443
+ protected onValueChanged(): void;
444
+ /**
445
+ * Groups flat JsonEntry[] into a namespace tree for rendering.
446
+ *
447
+ * Each entry's first path segment becomes the namespace prefix.
448
+ * Entries whose keys start with `[` (bracket notation) are grouped
449
+ * under a fallback "—" namespace and sorted to the top.
450
+ *
451
+ * Regex metacharacters in the prefix are escaped before stripping
452
+ * it from child keys (e.g. `$Settings.Foo` won't break the RegExp).
453
+ */
454
+ private buildTree;
455
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<SofB2BJsonEditorComponent, never>;
456
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<SofB2BJsonEditorComponent, "sof-b2b-json-editor", never, { "data": { "alias": "data"; "required": false; "isSignal": true; }; "schema": { "alias": "schema"; "required": false; "isSignal": true; }; "debounceMs": { "alias": "debounceMs"; "required": false; "isSignal": true; }; "emptyMessage": { "alias": "emptyMessage"; "required": false; "isSignal": true; }; "noResultsMessage": { "alias": "noResultsMessage"; "required": false; "isSignal": true; }; "searchTerm": { "alias": "searchTerm"; "required": false; "isSignal": true; }; "readOnly": { "alias": "readOnly"; "required": false; "isSignal": true; }; }, { "dataChanged": "dataChanged"; "parseError": "parseError"; }, never, never, true, never>;
457
+ }
458
+
459
+ /** The Sof B2B JSON Editor Node Component */
460
+ declare class SofB2BJsonNodeComponent {
461
+ /**
462
+ * ChangeDetectorRef is ONLY needed for addItem() and removeItem().
463
+ * Those methods mutate node().children in place — the node signal reference
464
+ * doesn't change, so OnPush won't re-evaluate the @for loop without an
465
+ * explicit markForCheck(). All other reactivity in this component uses signals.
466
+ */
467
+ private readonly cdr;
468
+ /** The JsonNode to render. Required input. */
469
+ readonly node: _angular_core.InputSignal<JsonNode>;
470
+ /** When true, all fields render as non-editable plaintext. Passed from SofB2BJsonEditorComponent. */
471
+ readonly readOnly: _angular_core.InputSignal<boolean>;
472
+ /** Bubbles up from any depth to the root SofB2BJsonEditorComponent. */
473
+ readonly valueChanged: _angular_core.OutputEmitterRef<void>;
474
+ /** Exposed to template for the 📏 depth guardrail check. */
475
+ protected readonly maxDepth = 20;
476
+ /** Whether this node is an empty container (array/object with no children). */
477
+ protected readonly isEmpty: _angular_core.Signal<boolean>;
478
+ /**
479
+ * Whether the scalar field has been edited (current value differs from original).
480
+ * Always false for non-scalar nodes. Updated imperatively on each ngModelChange
481
+ * because the `node` signal reference doesn't change when `editableValue` is
482
+ * mutated in place by ngModel — a computed() based on node() alone would never
483
+ * re-evaluate.
484
+ *
485
+ * Also recalculated when the `node` input changes (via effect) so that the
486
+ * initial render already reflects dirty state (e.g. when a parent rebuilds the
487
+ * tree after external data changes).
488
+ */
489
+ protected isDirty: boolean;
490
+ /**
491
+ * Whether this array node supports add/remove operations.
492
+ * True when: array type, has at least 1 child (provides template for cloning),
493
+ * and not in readOnly mode. Empty arrays show "(no schema)" instead.
494
+ */
495
+ protected readonly canModifyArray: _angular_core.Signal<boolean>;
496
+ /**
497
+ * Label for the "Add" button, e.g. "Add Member" for an array named "Members".
498
+ * Uses naive singularization (strip trailing "s") for a natural UX label.
499
+ */
500
+ protected readonly addButtonLabel: _angular_core.Signal<string>;
501
+ constructor();
502
+ /** Label describing the child count, e.g. "3 items" or "2 fields". */
503
+ protected readonly childLabel: _angular_core.Signal<string>;
504
+ /**
505
+ * Sanitized id safe for use in HTML id attributes and CSS selectors.
506
+ * node.fullKey may contain brackets and quotes (e.g. ["Discount Amount"])
507
+ * which are invalid in CSS selectors and break browser extension queries.
508
+ */
509
+ protected readonly safeId: _angular_core.Signal<string>;
510
+ /** Relays child node value changes upward to the parent. Enables event bubbling through the recursive tree. */
511
+ protected onChildChanged(): void;
512
+ /**
513
+ * Called from the template on every `(ngModelChange)` for scalar inputs.
514
+ * Updates the dirty flag by comparing the current editableValue against the
515
+ * original snapshot, then bubbles the change event upward.
516
+ */
517
+ protected onValueChanged(): void;
518
+ /**
519
+ * Adds a new item to this array node by cloning the first child as a template.
520
+ * The cloned item's scalar descendants have originalValue = editableValue,
521
+ * so they do not appear dirty. Replaces the children array reference and
522
+ * marks the view for check since the node signal itself doesn't change.
523
+ */
524
+ protected addItem(): void;
525
+ /**
526
+ * Removes an item from this array node at the given index.
527
+ * Minimum items = 1 — cannot delete the last remaining item.
528
+ * After removal, reindexes remaining children to keep display keys sequential.
529
+ */
530
+ protected removeItem(index: number): void;
531
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<SofB2BJsonNodeComponent, never>;
532
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<SofB2BJsonNodeComponent, "sof-b2b-json-node", never, { "node": { "alias": "node"; "required": true; "isSignal": true; }; "readOnly": { "alias": "readOnly"; "required": false; "isSignal": true; }; }, { "valueChanged": "valueChanged"; }, never, never, true, never>;
533
+ }
534
+
535
+ declare class SofB2BModule {
536
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<SofB2BModule, never>;
537
+ static ɵmod: _angular_core.ɵɵNgModuleDeclaration<SofB2BModule, never, [typeof SofB2BJsonEditorComponent, typeof SofB2BJsonNodeComponent], [typeof SofB2BJsonEditorComponent, typeof SofB2BJsonNodeComponent]>;
538
+ static ɵinj: _angular_core.ɵɵInjectorDeclaration<SofB2BModule>;
539
+ }
540
+
541
+ /**
542
+ * @fileoverview JSON Schema → FieldConfig parser.
543
+ *
544
+ * Converts a standard JSON Schema (Draft 7+) into a flat
545
+ * Record<string, FieldConfig> keyed by dot-notation paths.
546
+ * The output plugs directly into SofB2BJsonEditorComponent's [schema] input
547
+ * and is consumed by buildFieldConfig() at tree-build time.
548
+ *
549
+ * Supported JSON Schema keywords:
550
+ * type → maps to FieldInputType ('text', 'number', 'toggle', 'select')
551
+ * enum → options[] + inputType 'select'
552
+ * $id / title → label override
553
+ * properties → recursed into, building dot-notation paths
554
+ * items → recursed into for array element schemas
555
+ *
556
+ * Intentionally ignored (Tier 1 — basic support):
557
+ * pattern, format, minLength, maxLength, minimum, maximum,
558
+ * additionalProperties, required, allOf, anyOf, oneOf, $ref
559
+ *
560
+ * All functions are stateless and side-effect free.
561
+ * No Angular dependencies — fully unit-testable in isolation.
562
+ */
563
+
564
+ /**
565
+ * Parses a JSON Schema document into a flat Record<string, FieldConfig>
566
+ * suitable for passing to SofB2BJsonEditorComponent's [schema] input.
567
+ *
568
+ * @param schema A JSON Schema object (Draft 7+ compatible).
569
+ * Pass null/undefined for a no-op that returns {}.
570
+ * @returns A flat map of dot-notation paths → FieldConfig.
571
+ *
572
+ * @example
573
+ * const schema = await fetch('/api/schema').then(r => r.json());
574
+ * const fieldConfigs = parseJsonSchema(schema);
575
+ * // fieldConfigs = {
576
+ * // 'Settings.Theme': { inputType: 'select', options: ['dark', 'light'] },
577
+ * // 'Settings.MaxRetries': { inputType: 'number' },
578
+ * // 'Settings.Enabled': { inputType: 'toggle' },
579
+ * // }
580
+ */
581
+ declare function parseJsonSchema(schema: JSONSchema7 | null | undefined): Record<string, FieldConfig>;
582
+
583
+ export { MAX_DEPTH, SofB2BJsonEditorComponent, SofB2BJsonNodeComponent, SofB2BModule, buildNodesFromEntries, filterNamespaces, flattenNamespaces, normalizeToEntries, parseJsonSchema, parsePath, unflattenJson, validateDuplicateKeys };
584
+ export type { FieldConfig, JsonEntry, JsonNamespace, JsonNode, JsonObject, JsonValue };
@@ -2072,7 +2072,7 @@ declare class ValidationKeys {
2072
2072
  * @description
2073
2073
  * - This component can be used as a single select or multi-select.
2074
2074
  * - Can be used with a reactive formControlName.
2075
- * - Can be used with [(ngModal)] binding.
2075
+ * - Can be used with [(ngModel)] binding.
2076
2076
  * - Can be used without a form using the (selectionChange) output event.
2077
2077
  */
2078
2078
  declare class SofSelectComponent implements ControlValueAccessor, Validator, OnInit, AfterViewInit, OnDestroy {
@@ -6785,6 +6785,12 @@ declare class SofSnackbarComponent {
6785
6785
  private _snackbarService;
6786
6786
  /** Snackbars array signal */
6787
6787
  snackbars: Signal<Array<_SnackbarInternal>>;
6788
+ /** Position from top for the snackbar container */
6789
+ positionTop: string | undefined;
6790
+ /** Position from right for the snackbar container (default: 24px) */
6791
+ positionRight: string;
6792
+ /** Position from bottom for the snackbar container (default: 24px) */
6793
+ positionBottom: string;
6788
6794
  /** Live announcer for screen reader wcag */
6789
6795
  private _liveAnnouncerService;
6790
6796
  /** Translation service */
@@ -6813,7 +6819,7 @@ declare class SofSnackbarComponent {
6813
6819
  */
6814
6820
  handleAction(snackbar: _SnackbarInternal): void;
6815
6821
  static ɵfac: i0.ɵɵFactoryDeclaration<SofSnackbarComponent, never>;
6816
- static ɵcmp: i0.ɵɵComponentDeclaration<SofSnackbarComponent, "sof-snackbar", never, {}, {}, never, never, true, never>;
6822
+ static ɵcmp: i0.ɵɵComponentDeclaration<SofSnackbarComponent, "sof-snackbar", never, { "positionTop": { "alias": "positionTop"; "required": false; }; "positionRight": { "alias": "positionRight"; "required": false; }; "positionBottom": { "alias": "positionBottom"; "required": false; }; }, {}, never, never, true, never>;
6817
6823
  }
6818
6824
 
6819
6825
  /** Toast Model */
@@ -6879,6 +6885,10 @@ declare class TextOverflowEllipsisTooltipDirective implements AfterViewInit, OnD
6879
6885
  * @note If no tooltip text provided, will use the elements 'textContent'
6880
6886
  */
6881
6887
  tooltipText: string;
6888
+ /** Custom classes for the tooltip */
6889
+ tooltipClass: string | Array<string>;
6890
+ /** Possible positions for a tooltip : "left" | "right" | "above" | "below" | "before" | "after" */
6891
+ tooltipPosition: TooltipPosition;
6882
6892
  /** Is the element overflowing */
6883
6893
  private isOverflowing;
6884
6894
  /** The resize observer */
@@ -6905,7 +6915,7 @@ declare class TextOverflowEllipsisTooltipDirective implements AfterViewInit, OnD
6905
6915
  /** Hide the tooltip on mouseleave */
6906
6916
  onMouseLeave(): void;
6907
6917
  static ɵfac: i0.ɵɵFactoryDeclaration<TextOverflowEllipsisTooltipDirective, never>;
6908
- static ɵdir: i0.ɵɵDirectiveDeclaration<TextOverflowEllipsisTooltipDirective, "[textOverflowEllipsisTooltip]", never, { "tooltipText": { "alias": "textOverflowEllipsisTooltip"; "required": false; }; }, {}, never, never, true, never>;
6918
+ static ɵdir: i0.ɵɵDirectiveDeclaration<TextOverflowEllipsisTooltipDirective, "[textOverflowEllipsisTooltip]", never, { "tooltipText": { "alias": "textOverflowEllipsisTooltip"; "required": false; }; "tooltipClass": { "alias": "ellipsisTooltipClass"; "required": false; }; "tooltipPosition": { "alias": "ellipsisTooltipPosition"; "required": false; }; }, {}, never, never, true, never>;
6909
6919
  }
6910
6920
 
6911
6921
  declare class MfeModule {