@campfire-interactive/master-data-client 5.17.0 → 5.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/attributeDefinitions.d.ts +12 -1
- package/dist/attributeDefinitions.d.ts.map +1 -1
- package/dist/attributeDefinitions.js +20 -0
- package/dist/attributeDefinitions.js.map +1 -1
- package/dist/attributeHierarchy.d.ts +157 -0
- package/dist/attributeHierarchy.d.ts.map +1 -0
- package/dist/attributeHierarchy.js +296 -0
- package/dist/attributeHierarchy.js.map +1 -0
- package/dist/attributeHierarchyChain.d.ts +42 -0
- package/dist/attributeHierarchyChain.d.ts.map +1 -0
- package/dist/attributeHierarchyChain.js +50 -0
- package/dist/attributeHierarchyChain.js.map +1 -0
- package/dist/attributeSelectOptions.d.ts +94 -0
- package/dist/attributeSelectOptions.d.ts.map +1 -0
- package/dist/attributeSelectOptions.js +101 -0
- package/dist/attributeSelectOptions.js.map +1 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +29 -1
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +34 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +31 -31
- package/dist/partFamilies.d.ts +0 -12
- package/dist/partFamilies.d.ts.map +0 -1
- package/dist/partFamilies.js +0 -45
- package/dist/partFamilies.js.map +0 -1
- package/dist/rateLibraries.d.ts +0 -25
- package/dist/rateLibraries.d.ts.map +0 -1
- package/dist/rateLibraries.js +0 -132
- package/dist/rateLibraries.js.map +0 -1
- package/dist/suppliers.d.ts +0 -14
- package/dist/suppliers.d.ts.map +0 -1
- package/dist/suppliers.js +0 -84
- package/dist/suppliers.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HttpTransport } from './http';
|
|
2
|
-
import type { AttributeDefinition, CreateAttributeDefinitionInput, UpdateAttributeDefinitionInput, ListAttributeDefinitionsOptions, SingleResponse, ListResponse, CreateResponse, BulkArchiveResult } from './types';
|
|
2
|
+
import type { AttributeDefinition, CreateAttributeDefinitionInput, UpdateAttributeDefinitionInput, ListAttributeDefinitionsOptions, SingleResponse, ListResponse, CreateResponse, BulkArchiveResult, AttributeValuesTemplate, AttributeDefinitionImportReport } from './types';
|
|
3
3
|
export declare class AttributeDefinitionResource {
|
|
4
4
|
private readonly http;
|
|
5
5
|
private readonly basePath;
|
|
@@ -25,6 +25,17 @@ export declare class AttributeDefinitionResource {
|
|
|
25
25
|
archive(id: string): Promise<void>;
|
|
26
26
|
restore(id: string): Promise<void>;
|
|
27
27
|
batchArchive(ids: string[]): Promise<SingleResponse<BulkArchiveResult>>;
|
|
28
|
+
/** The workbook for uploading definitions: an Attributes sheet (one row per
|
|
29
|
+
* definition, any entity type) and a Values sheet (one row per value of a
|
|
30
|
+
* select attribute). Platform owner. */
|
|
31
|
+
template(): Promise<SingleResponse<AttributeValuesTemplate>>;
|
|
32
|
+
/** Check a filled-in definitions workbook (base64), or with `commit: true`
|
|
33
|
+
* create what it describes. A commit writes nothing unless every row
|
|
34
|
+
* passes; it creates, never edits — a key that already exists is an error.
|
|
35
|
+
* Platform owner. */
|
|
36
|
+
importFile(fileContent: string, options?: {
|
|
37
|
+
commit?: boolean;
|
|
38
|
+
}): Promise<SingleResponse<AttributeDefinitionImportReport>>;
|
|
28
39
|
/** Switch one STANDARD attribute (a default definition) on or off for the
|
|
29
40
|
* calling tenant. Tenant owner/admin. Not available on
|
|
30
41
|
* `defaultAttributes.definitions`, where it has no meaning. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attributeDefinitions.d.ts","sourceRoot":"","sources":["../src/attributeDefinitions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EACV,mBAAmB,EACnB,8BAA8B,EAC9B,8BAA8B,EAC9B,+BAA+B,EAC/B,cAAc,EACd,YAAY,EACZ,cAAc,EACd,iBAAiB,
|
|
1
|
+
{"version":3,"file":"attributeDefinitions.d.ts","sourceRoot":"","sources":["../src/attributeDefinitions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EACV,mBAAmB,EACnB,8BAA8B,EAC9B,8BAA8B,EAC9B,+BAA+B,EAC/B,cAAc,EACd,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,uBAAuB,EACvB,+BAA+B,EAChC,MAAM,SAAS,CAAC;AAQjB,qBAAa,2BAA2B;IAIpC,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJ3B;4FACwF;gBAErE,IAAI,EAAE,aAAa,EACnB,QAAQ,GAAE,MAAoC;IAG3D,IAAI,CAAC,OAAO,EAAE,+BAA+B,GAAG,OAAO,CAAC,YAAY,CAAC,mBAAmB,CAAC,CAAC;IAuBhG;;;;;;;0CAOsC;IAChC,GAAG,CACP,EAAE,EAAE,MAAM,EACV,OAAO,GAAE;QAAE,cAAc,CAAC,EAAE,OAAO,CAAC;QAAC,eAAe,CAAC,EAAE,OAAO,CAAA;KAAO,GACpE,OAAO,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;IAczC,MAAM,CACV,KAAK,EAAE,8BAA8B,EACrC,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;IASzC,MAAM,CACV,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,8BAA8B,EACrC,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;IAS/C,mFAAmF;IAC7E,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOlC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOlC,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;IAQ7E;;6CAEyC;IACnC,QAAQ,IAAI,OAAO,CAAC,cAAc,CAAC,uBAAuB,CAAC,CAAC;IAOlE;;;0BAGsB;IAChB,UAAU,CACd,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAO,GACjC,OAAO,CAAC,cAAc,CAAC,+BAA+B,CAAC,CAAC;IAQ3D;;oEAEgE;IAC1D,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;IAQ5F;;uCAEmC;IAC7B,WAAW,CACf,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GACrD,OAAO,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;CAOhD"}
|
|
@@ -95,6 +95,26 @@ class AttributeDefinitionResource {
|
|
|
95
95
|
body: { ids },
|
|
96
96
|
});
|
|
97
97
|
}
|
|
98
|
+
/** The workbook for uploading definitions: an Attributes sheet (one row per
|
|
99
|
+
* definition, any entity type) and a Values sheet (one row per value of a
|
|
100
|
+
* select attribute). Platform owner. */
|
|
101
|
+
async template() {
|
|
102
|
+
return this.http.request({
|
|
103
|
+
method: 'GET',
|
|
104
|
+
path: `${this.basePath}/template`,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
/** Check a filled-in definitions workbook (base64), or with `commit: true`
|
|
108
|
+
* create what it describes. A commit writes nothing unless every row
|
|
109
|
+
* passes; it creates, never edits — a key that already exists is an error.
|
|
110
|
+
* Platform owner. */
|
|
111
|
+
async importFile(fileContent, options = {}) {
|
|
112
|
+
return this.http.request({
|
|
113
|
+
method: 'POST',
|
|
114
|
+
path: `${this.basePath}/import`,
|
|
115
|
+
body: { fileContent, commit: options.commit === true },
|
|
116
|
+
});
|
|
117
|
+
}
|
|
98
118
|
/** Switch one STANDARD attribute (a default definition) on or off for the
|
|
99
119
|
* calling tenant. Tenant owner/admin. Not available on
|
|
100
120
|
* `defaultAttributes.definitions`, where it has no meaning. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attributeDefinitions.js","sourceRoot":"","sources":["../src/attributeDefinitions.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"attributeDefinitions.js","sourceRoot":"","sources":["../src/attributeDefinitions.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Picking a value out of a hierarchy from ANY level, not only the top.
|
|
3
|
+
*
|
|
4
|
+
* A hierarchy is OEM > Program > Platform, and the picker used to insist you
|
|
5
|
+
* start at OEM: every level below the chosen one was disabled and listed
|
|
6
|
+
* nothing. Someone who knows the Platform had to remember which OEM it sits
|
|
7
|
+
* under and walk down to it — the app asking the user for the answer it already
|
|
8
|
+
* holds.
|
|
9
|
+
*
|
|
10
|
+
* Nothing about the DATA required that. The bag stores ONE option value and the
|
|
11
|
+
* ancestors are derived by walking `parentId` upward, so a directly-picked
|
|
12
|
+
* Platform already implies its Program and OEM. Only the UI forbade it.
|
|
13
|
+
*
|
|
14
|
+
* The model here is one selected node. Every level lists the nodes CONSISTENT
|
|
15
|
+
* with it — its ancestors, itself, its descendants — and every node at that
|
|
16
|
+
* level when nothing is selected yet. So picking a Platform back-fills the two
|
|
17
|
+
* above it, and picking an OEM narrows the two below without forcing an order.
|
|
18
|
+
*
|
|
19
|
+
* ## Why these functions take an INDEX
|
|
20
|
+
*
|
|
21
|
+
* The rules above were first written against a plain array, which meant every
|
|
22
|
+
* parent lookup was a linear scan: `ancestryOf` scanned once per level climbed,
|
|
23
|
+
* and the descendants filter called it once per candidate node. On a 52,488-node
|
|
24
|
+
* tree one render of the picker spent ~490 ms here — and it paid that on EVERY
|
|
25
|
+
* render, because an attribute form re-renders every field when any one of
|
|
26
|
+
* them is typed in.
|
|
27
|
+
*
|
|
28
|
+
* So the callers build an `OptionIndex` once (memoized on the definition) and
|
|
29
|
+
* pass it in. Every function still accepts a bare array and indexes it on the
|
|
30
|
+
* spot, which keeps the unit tests — and any incidental caller — working
|
|
31
|
+
* unchanged. The RULES below are untouched; only how they are computed moved.
|
|
32
|
+
* The test suite is the proof: it was not edited for this.
|
|
33
|
+
*/
|
|
34
|
+
export interface HierarchyNode {
|
|
35
|
+
id: string;
|
|
36
|
+
value: string;
|
|
37
|
+
label: string;
|
|
38
|
+
level: number;
|
|
39
|
+
parentId: string | null;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* The lookups every rule below needs, built once in O(n).
|
|
43
|
+
*
|
|
44
|
+
* `childrenByParent` keys roots under `null`, which a Map handles natively.
|
|
45
|
+
* `byValue` exists because the selected node is found by its STORED value (the
|
|
46
|
+
* bag holds a value, not an id) and that lookup used to be a scan too.
|
|
47
|
+
*/
|
|
48
|
+
export interface OptionIndex {
|
|
49
|
+
readonly nodes: readonly HierarchyNode[];
|
|
50
|
+
readonly byId: ReadonlyMap<string, HierarchyNode>;
|
|
51
|
+
readonly byValue: ReadonlyMap<string, HierarchyNode>;
|
|
52
|
+
readonly byLevel: readonly (readonly HierarchyNode[])[];
|
|
53
|
+
readonly childrenByParent: ReadonlyMap<string | null, readonly HierarchyNode[]>;
|
|
54
|
+
readonly maxLevel: number;
|
|
55
|
+
}
|
|
56
|
+
/** Everything the rules need, in one pass. Order within each bucket is the
|
|
57
|
+
* source array's order, which is what the registry curated. */
|
|
58
|
+
export declare function buildOptionIndex(nodes: readonly HierarchyNode[]): OptionIndex;
|
|
59
|
+
/** Either form a caller may hold. Arrays are indexed on the spot. */
|
|
60
|
+
export type OptionSource = readonly HierarchyNode[] | OptionIndex;
|
|
61
|
+
/** The chain from a node up to its root, indexed by level. */
|
|
62
|
+
export declare function ancestryOf(src: OptionSource, node: HierarchyNode | undefined): (HierarchyNode | undefined)[];
|
|
63
|
+
/**
|
|
64
|
+
* What one level should offer, given the whole option list and the current
|
|
65
|
+
* selection.
|
|
66
|
+
*
|
|
67
|
+
* With nothing selected, every node at that level — which is what makes a
|
|
68
|
+
* bottom-up pick possible at all. With a selection, the nodes on its own branch:
|
|
69
|
+
* above it, its ancestors (so the level reads as filled in); below it, its
|
|
70
|
+
* descendants (so the next choice is narrowed); at its own level, its siblings
|
|
71
|
+
* under the same parent, so a wrong turn is one click to correct rather than a
|
|
72
|
+
* reset.
|
|
73
|
+
*/
|
|
74
|
+
export declare function optionsForLevel(src: OptionSource, selected: HierarchyNode | undefined, level: number): HierarchyNode[];
|
|
75
|
+
/**
|
|
76
|
+
* The node selected after picking `pickedId`.
|
|
77
|
+
*
|
|
78
|
+
* Re-affirming a node the selection already implies is a no-op: clicking the
|
|
79
|
+
* OEM that is already showing must not throw away the Platform beneath it,
|
|
80
|
+
* which is what setting the selection to the picked node every time would do.
|
|
81
|
+
* Picking anything else moves the selection there, because a node off the
|
|
82
|
+
* current branch invalidates what sat below.
|
|
83
|
+
*
|
|
84
|
+
* Returns the NODE, not just its value. Values repeat across branches (an
|
|
85
|
+
* "Ownership" level of "100" under every division), so a value alone cannot
|
|
86
|
+
* say which node was picked — see `resolveSelected`.
|
|
87
|
+
*/
|
|
88
|
+
export declare function nodeAfterPick(src: OptionSource, selected: HierarchyNode | undefined, pickedId: string): HierarchyNode | undefined;
|
|
89
|
+
/** The stored value after picking `pickedId`. See `nodeAfterPick`. */
|
|
90
|
+
export declare function valueAfterPick(src: OptionSource, selected: HierarchyNode | undefined, pickedId: string): string;
|
|
91
|
+
/**
|
|
92
|
+
* The node selected after CLEARING `level`: the ancestor above it, or nothing
|
|
93
|
+
* at the top. Clearing a level cannot keep a selection beneath it — that node
|
|
94
|
+
* is the very thing being cleared.
|
|
95
|
+
*/
|
|
96
|
+
export declare function nodeAfterClear(src: OptionSource, selected: HierarchyNode | undefined, level: number): HierarchyNode | undefined;
|
|
97
|
+
/** The stored value after clearing `level`. See `nodeAfterClear`. */
|
|
98
|
+
export declare function valueAfterClear(src: OptionSource, selected: HierarchyNode | undefined, level: number): string;
|
|
99
|
+
/**
|
|
100
|
+
* Walk down from `node` while there is exactly one way to go.
|
|
101
|
+
*
|
|
102
|
+
* A level with a single option under the current pick is not a question — the
|
|
103
|
+
* answer is already fixed — so asking the user to open a dropdown and click
|
|
104
|
+
* the only row in it is busywork. Descending stops at the first node with two
|
|
105
|
+
* or more children, or at a leaf.
|
|
106
|
+
*
|
|
107
|
+
* Only a PICK descends, never a clear: clearing a level and having it refill
|
|
108
|
+
* itself from its only child would make that level impossible to empty.
|
|
109
|
+
* Cycle-guarded like every other walk here.
|
|
110
|
+
*/
|
|
111
|
+
export declare function descendOnlyChildren(src: OptionSource, node: HierarchyNode | undefined): HierarchyNode | undefined;
|
|
112
|
+
/**
|
|
113
|
+
* Which node a stored value stands for, preferring the node the user picked.
|
|
114
|
+
*
|
|
115
|
+
* The bag stores an option VALUE, and a value can sit on several branches:
|
|
116
|
+
* Ownership "100" under every Country, say. `byValue` answers with the first
|
|
117
|
+
* such node, so resolving a fresh pick through it alone moved the selection
|
|
118
|
+
* to a different branch — the levels above jumped to some other Division and
|
|
119
|
+
* Country the moment "100" was chosen.
|
|
120
|
+
*
|
|
121
|
+
* So the picker remembers the id it just chose and asks for it by id first.
|
|
122
|
+
* The remembered id is only trusted while it still carries the stored value;
|
|
123
|
+
* once the value changes from outside (a prefill, a reset), resolution falls
|
|
124
|
+
* back to `byValue` as before.
|
|
125
|
+
*/
|
|
126
|
+
export declare function resolveSelected(src: OptionSource, value: unknown, preferredId: string | null | undefined): HierarchyNode | undefined;
|
|
127
|
+
/**
|
|
128
|
+
* How to label an option so two nodes that read alike can be told apart.
|
|
129
|
+
*
|
|
130
|
+
* Option values are NOT unique across a tree — "Explorer" can sit under Ford
|
|
131
|
+
* and under GM, and nothing in the schema forbids it. That was survivable while
|
|
132
|
+
* the picker was strictly top-down, because the parent was already chosen by
|
|
133
|
+
* the time you saw the child. Choosing a deep node directly is exactly when two
|
|
134
|
+
* identical labels appear side by side, so the ambiguous ones carry their
|
|
135
|
+
* parent.
|
|
136
|
+
*
|
|
137
|
+
* Only the ambiguous ones: appending a parent to every row would be noise on
|
|
138
|
+
* the overwhelming majority of lists, where labels are already distinct.
|
|
139
|
+
*
|
|
140
|
+
* Ambiguity is judged against `shown`, the rows actually offered — so a picker
|
|
141
|
+
* that renders a capped window disambiguates what the user can see, which is
|
|
142
|
+
* the only set the question means anything for.
|
|
143
|
+
*
|
|
144
|
+
* Rendering a whole level calls `labelsFor` instead: this is O(shown) per row
|
|
145
|
+
* and was the single most expensive thing in the picker before the rows were
|
|
146
|
+
* capped (46,080 rows meant 2.1 billion comparisons).
|
|
147
|
+
*/
|
|
148
|
+
export declare function labelFor(src: OptionSource, shown: readonly HierarchyNode[], node: HierarchyNode): string;
|
|
149
|
+
/**
|
|
150
|
+
* `labelFor` for a whole set of rows, in one pass instead of one pass per row.
|
|
151
|
+
*
|
|
152
|
+
* Counts each label once, then resolves only the ambiguous rows' parents.
|
|
153
|
+
* Identical output to calling `labelFor` per row; the difference is O(shown)
|
|
154
|
+
* against O(shown squared).
|
|
155
|
+
*/
|
|
156
|
+
export declare function labelsFor(src: OptionSource, shown: readonly HierarchyNode[]): Map<string, string>;
|
|
157
|
+
//# sourceMappingURL=attributeHierarchy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attributeHierarchy.d.ts","sourceRoot":"","sources":["../src/attributeHierarchy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildOptionIndex = buildOptionIndex;
|
|
4
|
+
exports.ancestryOf = ancestryOf;
|
|
5
|
+
exports.optionsForLevel = optionsForLevel;
|
|
6
|
+
exports.nodeAfterPick = nodeAfterPick;
|
|
7
|
+
exports.valueAfterPick = valueAfterPick;
|
|
8
|
+
exports.nodeAfterClear = nodeAfterClear;
|
|
9
|
+
exports.valueAfterClear = valueAfterClear;
|
|
10
|
+
exports.descendOnlyChildren = descendOnlyChildren;
|
|
11
|
+
exports.resolveSelected = resolveSelected;
|
|
12
|
+
exports.labelFor = labelFor;
|
|
13
|
+
exports.labelsFor = labelsFor;
|
|
14
|
+
/** Everything the rules need, in one pass. Order within each bucket is the
|
|
15
|
+
* source array's order, which is what the registry curated. */
|
|
16
|
+
function buildOptionIndex(nodes) {
|
|
17
|
+
const byId = new Map();
|
|
18
|
+
const byValue = new Map();
|
|
19
|
+
const byLevel = [];
|
|
20
|
+
const childrenByParent = new Map();
|
|
21
|
+
let maxLevel = 0;
|
|
22
|
+
for (const n of nodes) {
|
|
23
|
+
byId.set(n.id, n);
|
|
24
|
+
// First wins: two nodes CAN share a value across branches, and the old
|
|
25
|
+
// `find` returned the first, so this has to as well.
|
|
26
|
+
if (!byValue.has(n.value))
|
|
27
|
+
byValue.set(n.value, n);
|
|
28
|
+
(byLevel[n.level] ??= []).push(n);
|
|
29
|
+
const bucket = childrenByParent.get(n.parentId);
|
|
30
|
+
if (bucket)
|
|
31
|
+
bucket.push(n);
|
|
32
|
+
else
|
|
33
|
+
childrenByParent.set(n.parentId, [n]);
|
|
34
|
+
if (n.level > maxLevel)
|
|
35
|
+
maxLevel = n.level;
|
|
36
|
+
}
|
|
37
|
+
// A node whose CHILD carries the same value resolves to the child.
|
|
38
|
+
//
|
|
39
|
+
// The bag stores one option VALUE, and values are not unique down a branch:
|
|
40
|
+
// Magna's Product Hierarchy ends "… > ALL-WHEEL DRIVE > ALL-WHEEL DRIVE",
|
|
41
|
+
// the last level repeating the one above it. Taking the first match made the
|
|
42
|
+
// deepest node unreachable — picking it stored a string the lookup then
|
|
43
|
+
// resolved back to its ancestor, so the level below stayed empty and the row
|
|
44
|
+
// read as un-clickable. Nothing was broken except the display: the value
|
|
45
|
+
// written was already correct.
|
|
46
|
+
//
|
|
47
|
+
// Descending is free rather than a guess. The two nodes are indistinguishable
|
|
48
|
+
// to everything downstream (same stored string, so the same filters and the
|
|
49
|
+
// same reports), and the deeper reading is the more specific one, which
|
|
50
|
+
// implies its ancestors anyway. Only a value repeated PARENT-TO-CHILD moves;
|
|
51
|
+
// the same value on two different branches still takes the first, because
|
|
52
|
+
// there the choice would be a real guess.
|
|
53
|
+
for (const [value, node] of byValue) {
|
|
54
|
+
let cur = node;
|
|
55
|
+
const seen = new Set([cur.id]);
|
|
56
|
+
for (;;) {
|
|
57
|
+
const sameChild = (childrenByParent.get(cur.id) ?? [])
|
|
58
|
+
.find((c) => c.value === value && !seen.has(c.id));
|
|
59
|
+
if (!sameChild)
|
|
60
|
+
break;
|
|
61
|
+
seen.add(sameChild.id);
|
|
62
|
+
cur = sameChild;
|
|
63
|
+
}
|
|
64
|
+
if (cur !== node)
|
|
65
|
+
byValue.set(value, cur);
|
|
66
|
+
}
|
|
67
|
+
return { nodes, byId, byValue, byLevel, childrenByParent, maxLevel };
|
|
68
|
+
}
|
|
69
|
+
function toIndex(src) {
|
|
70
|
+
return Array.isArray(src) ? buildOptionIndex(src) : src;
|
|
71
|
+
}
|
|
72
|
+
/** Children of one parent, restricted to a level. The restriction is belt and
|
|
73
|
+
* braces: children of a node are all at one level in any well-formed tree,
|
|
74
|
+
* and the array version filtered by level, so this matches it exactly. */
|
|
75
|
+
function childrenAtLevel(ix, parentId, level) {
|
|
76
|
+
return (ix.childrenByParent.get(parentId) ?? []).filter((o) => o.level === level);
|
|
77
|
+
}
|
|
78
|
+
/** The ids of every node beneath `root`, exclusive. Iterative and cycle-guarded
|
|
79
|
+
* — the tree is parent-pointer only with no database constraint preventing a
|
|
80
|
+
* loop, and a bad row must not hang the form.
|
|
81
|
+
*
|
|
82
|
+
* Returns a SET rather than a list because the caller filters the level's own
|
|
83
|
+
* array by it: walking the subtree yields traversal order, and the rows have
|
|
84
|
+
* to come back in the registry's curated order, which is the order the source
|
|
85
|
+
* array is in. (The unit suite caught exactly this.) */
|
|
86
|
+
function subtreeIdsOf(ix, root) {
|
|
87
|
+
const seen = new Set([root.id]);
|
|
88
|
+
const stack = [root];
|
|
89
|
+
while (stack.length > 0) {
|
|
90
|
+
const node = stack.pop();
|
|
91
|
+
for (const child of ix.childrenByParent.get(node.id) ?? []) {
|
|
92
|
+
if (seen.has(child.id))
|
|
93
|
+
continue;
|
|
94
|
+
seen.add(child.id);
|
|
95
|
+
stack.push(child);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
seen.delete(root.id);
|
|
99
|
+
return seen;
|
|
100
|
+
}
|
|
101
|
+
/** The chain from a node up to its root, indexed by level. */
|
|
102
|
+
function ancestryOf(src, node) {
|
|
103
|
+
const ix = toIndex(src);
|
|
104
|
+
const byLevel = [];
|
|
105
|
+
let cur = node;
|
|
106
|
+
const seen = new Set();
|
|
107
|
+
// Guarded against a cycle: see subtreeIdsOf.
|
|
108
|
+
while (cur && !seen.has(cur.id)) {
|
|
109
|
+
seen.add(cur.id);
|
|
110
|
+
byLevel[cur.level] = cur;
|
|
111
|
+
cur = cur.parentId ? ix.byId.get(cur.parentId) : undefined;
|
|
112
|
+
}
|
|
113
|
+
return byLevel;
|
|
114
|
+
}
|
|
115
|
+
/** True when `node` is `maybeAncestor` or sits beneath it. */
|
|
116
|
+
function isSelfOrDescendantOf(ix, node, maybeAncestor) {
|
|
117
|
+
return ancestryOf(ix, node).some((a) => a?.id === maybeAncestor.id);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* What one level should offer, given the whole option list and the current
|
|
121
|
+
* selection.
|
|
122
|
+
*
|
|
123
|
+
* With nothing selected, every node at that level — which is what makes a
|
|
124
|
+
* bottom-up pick possible at all. With a selection, the nodes on its own branch:
|
|
125
|
+
* above it, its ancestors (so the level reads as filled in); below it, its
|
|
126
|
+
* descendants (so the next choice is narrowed); at its own level, its siblings
|
|
127
|
+
* under the same parent, so a wrong turn is one click to correct rather than a
|
|
128
|
+
* reset.
|
|
129
|
+
*/
|
|
130
|
+
function optionsForLevel(src, selected, level) {
|
|
131
|
+
const ix = toIndex(src);
|
|
132
|
+
const atLevel = ix.byLevel[level] ?? [];
|
|
133
|
+
if (!selected)
|
|
134
|
+
return [...atLevel];
|
|
135
|
+
if (level < selected.level) {
|
|
136
|
+
// Shallower: its own ancestor, plus that ancestor's siblings — changing one
|
|
137
|
+
// has to remain possible, and listing only the ancestor would make the
|
|
138
|
+
// control read as frozen.
|
|
139
|
+
const ancestor = ancestryOf(ix, selected)[level];
|
|
140
|
+
if (!ancestor)
|
|
141
|
+
return [...atLevel];
|
|
142
|
+
return childrenAtLevel(ix, ancestor.parentId, level);
|
|
143
|
+
}
|
|
144
|
+
if (level === selected.level) {
|
|
145
|
+
return childrenAtLevel(ix, selected.parentId, level);
|
|
146
|
+
}
|
|
147
|
+
// Deeper: walk DOWN the selection's subtree once and keep the level's own
|
|
148
|
+
// rows that fall inside it. Same answer and same order, at the cost of the
|
|
149
|
+
// subtree plus one pass, rather than (rows at this level x depth x array).
|
|
150
|
+
const inSubtree = subtreeIdsOf(ix, selected);
|
|
151
|
+
return atLevel.filter((o) => inSubtree.has(o.id));
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The node selected after picking `pickedId`.
|
|
155
|
+
*
|
|
156
|
+
* Re-affirming a node the selection already implies is a no-op: clicking the
|
|
157
|
+
* OEM that is already showing must not throw away the Platform beneath it,
|
|
158
|
+
* which is what setting the selection to the picked node every time would do.
|
|
159
|
+
* Picking anything else moves the selection there, because a node off the
|
|
160
|
+
* current branch invalidates what sat below.
|
|
161
|
+
*
|
|
162
|
+
* Returns the NODE, not just its value. Values repeat across branches (an
|
|
163
|
+
* "Ownership" level of "100" under every division), so a value alone cannot
|
|
164
|
+
* say which node was picked — see `resolveSelected`.
|
|
165
|
+
*/
|
|
166
|
+
function nodeAfterPick(src, selected, pickedId) {
|
|
167
|
+
const ix = toIndex(src);
|
|
168
|
+
const picked = ix.byId.get(pickedId);
|
|
169
|
+
if (!picked)
|
|
170
|
+
return undefined;
|
|
171
|
+
if (selected && isSelfOrDescendantOf(ix, selected, picked)) {
|
|
172
|
+
// Already on this branch, at or below the pick — keep the deeper answer.
|
|
173
|
+
return selected;
|
|
174
|
+
}
|
|
175
|
+
return picked;
|
|
176
|
+
}
|
|
177
|
+
/** The stored value after picking `pickedId`. See `nodeAfterPick`. */
|
|
178
|
+
function valueAfterPick(src, selected, pickedId) {
|
|
179
|
+
return nodeAfterPick(src, selected, pickedId)?.value ?? '';
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* The node selected after CLEARING `level`: the ancestor above it, or nothing
|
|
183
|
+
* at the top. Clearing a level cannot keep a selection beneath it — that node
|
|
184
|
+
* is the very thing being cleared.
|
|
185
|
+
*/
|
|
186
|
+
function nodeAfterClear(src, selected, level) {
|
|
187
|
+
if (!selected || level <= 0)
|
|
188
|
+
return undefined;
|
|
189
|
+
return ancestryOf(src, selected)[level - 1];
|
|
190
|
+
}
|
|
191
|
+
/** The stored value after clearing `level`. See `nodeAfterClear`. */
|
|
192
|
+
function valueAfterClear(src, selected, level) {
|
|
193
|
+
return nodeAfterClear(src, selected, level)?.value ?? '';
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Walk down from `node` while there is exactly one way to go.
|
|
197
|
+
*
|
|
198
|
+
* A level with a single option under the current pick is not a question — the
|
|
199
|
+
* answer is already fixed — so asking the user to open a dropdown and click
|
|
200
|
+
* the only row in it is busywork. Descending stops at the first node with two
|
|
201
|
+
* or more children, or at a leaf.
|
|
202
|
+
*
|
|
203
|
+
* Only a PICK descends, never a clear: clearing a level and having it refill
|
|
204
|
+
* itself from its only child would make that level impossible to empty.
|
|
205
|
+
* Cycle-guarded like every other walk here.
|
|
206
|
+
*/
|
|
207
|
+
function descendOnlyChildren(src, node) {
|
|
208
|
+
if (!node)
|
|
209
|
+
return undefined;
|
|
210
|
+
const ix = toIndex(src);
|
|
211
|
+
let cur = node;
|
|
212
|
+
const seen = new Set([cur.id]);
|
|
213
|
+
for (;;) {
|
|
214
|
+
const children = ix.childrenByParent.get(cur.id) ?? [];
|
|
215
|
+
if (children.length !== 1 || seen.has(children[0].id))
|
|
216
|
+
return cur;
|
|
217
|
+
cur = children[0];
|
|
218
|
+
seen.add(cur.id);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Which node a stored value stands for, preferring the node the user picked.
|
|
223
|
+
*
|
|
224
|
+
* The bag stores an option VALUE, and a value can sit on several branches:
|
|
225
|
+
* Ownership "100" under every Country, say. `byValue` answers with the first
|
|
226
|
+
* such node, so resolving a fresh pick through it alone moved the selection
|
|
227
|
+
* to a different branch — the levels above jumped to some other Division and
|
|
228
|
+
* Country the moment "100" was chosen.
|
|
229
|
+
*
|
|
230
|
+
* So the picker remembers the id it just chose and asks for it by id first.
|
|
231
|
+
* The remembered id is only trusted while it still carries the stored value;
|
|
232
|
+
* once the value changes from outside (a prefill, a reset), resolution falls
|
|
233
|
+
* back to `byValue` as before.
|
|
234
|
+
*/
|
|
235
|
+
function resolveSelected(src, value, preferredId) {
|
|
236
|
+
if (typeof value !== 'string' || value === '')
|
|
237
|
+
return undefined;
|
|
238
|
+
const ix = toIndex(src);
|
|
239
|
+
const preferred = preferredId ? ix.byId.get(preferredId) : undefined;
|
|
240
|
+
if (preferred && preferred.value === value)
|
|
241
|
+
return preferred;
|
|
242
|
+
return ix.byValue.get(value);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* How to label an option so two nodes that read alike can be told apart.
|
|
246
|
+
*
|
|
247
|
+
* Option values are NOT unique across a tree — "Explorer" can sit under Ford
|
|
248
|
+
* and under GM, and nothing in the schema forbids it. That was survivable while
|
|
249
|
+
* the picker was strictly top-down, because the parent was already chosen by
|
|
250
|
+
* the time you saw the child. Choosing a deep node directly is exactly when two
|
|
251
|
+
* identical labels appear side by side, so the ambiguous ones carry their
|
|
252
|
+
* parent.
|
|
253
|
+
*
|
|
254
|
+
* Only the ambiguous ones: appending a parent to every row would be noise on
|
|
255
|
+
* the overwhelming majority of lists, where labels are already distinct.
|
|
256
|
+
*
|
|
257
|
+
* Ambiguity is judged against `shown`, the rows actually offered — so a picker
|
|
258
|
+
* that renders a capped window disambiguates what the user can see, which is
|
|
259
|
+
* the only set the question means anything for.
|
|
260
|
+
*
|
|
261
|
+
* Rendering a whole level calls `labelsFor` instead: this is O(shown) per row
|
|
262
|
+
* and was the single most expensive thing in the picker before the rows were
|
|
263
|
+
* capped (46,080 rows meant 2.1 billion comparisons).
|
|
264
|
+
*/
|
|
265
|
+
function labelFor(src, shown, node) {
|
|
266
|
+
const duplicated = shown.filter((o) => o.label === node.label).length > 1;
|
|
267
|
+
if (!duplicated)
|
|
268
|
+
return node.label;
|
|
269
|
+
const parent = node.parentId ? toIndex(src).byId.get(node.parentId) : undefined;
|
|
270
|
+
return parent ? `${node.label} (${parent.label})` : node.label;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* `labelFor` for a whole set of rows, in one pass instead of one pass per row.
|
|
274
|
+
*
|
|
275
|
+
* Counts each label once, then resolves only the ambiguous rows' parents.
|
|
276
|
+
* Identical output to calling `labelFor` per row; the difference is O(shown)
|
|
277
|
+
* against O(shown squared).
|
|
278
|
+
*/
|
|
279
|
+
function labelsFor(src, shown) {
|
|
280
|
+
const counts = new Map();
|
|
281
|
+
for (const o of shown)
|
|
282
|
+
counts.set(o.label, (counts.get(o.label) ?? 0) + 1);
|
|
283
|
+
const out = new Map();
|
|
284
|
+
let ix;
|
|
285
|
+
for (const o of shown) {
|
|
286
|
+
if ((counts.get(o.label) ?? 0) <= 1) {
|
|
287
|
+
out.set(o.id, o.label);
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
ix ??= toIndex(src);
|
|
291
|
+
const parent = o.parentId ? ix.byId.get(o.parentId) : undefined;
|
|
292
|
+
out.set(o.id, parent ? `${o.label} (${parent.label})` : o.label);
|
|
293
|
+
}
|
|
294
|
+
return out;
|
|
295
|
+
}
|
|
296
|
+
//# sourceMappingURL=attributeHierarchy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attributeHierarchy.js","sourceRoot":"","sources":["../src/attributeHierarchy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A stored hierarchy value, expanded into the chain it stands for.
|
|
3
|
+
*
|
|
4
|
+
* The bag holds one option value: the node that was picked. Read views showed
|
|
5
|
+
* just that node's label, so "ALL-WHEEL DRIVE" arrived with none of the context
|
|
6
|
+
* that makes it mean anything, and the two other things a reader needs — which
|
|
7
|
+
* branch it sits on, and what each tier of the branch is called — were only
|
|
8
|
+
* visible by opening the editor and reading the dropdowns.
|
|
9
|
+
*
|
|
10
|
+
* The ancestors are derivable (that is the whole premise of storing one value),
|
|
11
|
+
* so this walks them and pairs each with the tier name the definition declares
|
|
12
|
+
* in `validation.levels`.
|
|
13
|
+
*/
|
|
14
|
+
import { type HierarchyNode } from './attributeHierarchy';
|
|
15
|
+
export interface HierarchyTier {
|
|
16
|
+
/** The tier's declared name, or null when the definition names no levels. */
|
|
17
|
+
tier: string | null;
|
|
18
|
+
/** The chosen node's label at this tier. */
|
|
19
|
+
label: string;
|
|
20
|
+
/** The tier's index, which is the node's own level. Stable key for a list. */
|
|
21
|
+
level: number;
|
|
22
|
+
}
|
|
23
|
+
interface ChainSource {
|
|
24
|
+
validation?: {
|
|
25
|
+
levels?: string[];
|
|
26
|
+
} | null;
|
|
27
|
+
attributeOptions?: readonly HierarchyNode[] | null;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The chain a stored value stands for, shallowest tier first.
|
|
31
|
+
*
|
|
32
|
+
* `null` when there is nothing better to show than the raw value: no options
|
|
33
|
+
* loaded, an empty value, or a value that names no node (legacy data, or an
|
|
34
|
+
* option archived since it was chosen). Callers fall back to the ordinary
|
|
35
|
+
* single-value formatter in that case rather than rendering an empty list.
|
|
36
|
+
*
|
|
37
|
+
* A single-tier chain also returns null: one row captioned "Level 1" is
|
|
38
|
+
* strictly more furniture than the plain label it replaces.
|
|
39
|
+
*/
|
|
40
|
+
export declare function hierarchyChain(def: ChainSource, value: unknown): HierarchyTier[] | null;
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=attributeHierarchyChain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attributeHierarchyChain.d.ts","sourceRoot":"","sources":["../src/attributeHierarchyChain.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAgC,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAExF,MAAM,WAAW,aAAa;IAC5B,6EAA6E;IAC7E,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,WAAW;IACnB,UAAU,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC;IAC1C,gBAAgB,CAAC,EAAE,SAAS,aAAa,EAAE,GAAG,IAAI,CAAC;CACpD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,GAAG,aAAa,EAAE,GAAG,IAAI,CAmBvF"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hierarchyChain = hierarchyChain;
|
|
4
|
+
/**
|
|
5
|
+
* A stored hierarchy value, expanded into the chain it stands for.
|
|
6
|
+
*
|
|
7
|
+
* The bag holds one option value: the node that was picked. Read views showed
|
|
8
|
+
* just that node's label, so "ALL-WHEEL DRIVE" arrived with none of the context
|
|
9
|
+
* that makes it mean anything, and the two other things a reader needs — which
|
|
10
|
+
* branch it sits on, and what each tier of the branch is called — were only
|
|
11
|
+
* visible by opening the editor and reading the dropdowns.
|
|
12
|
+
*
|
|
13
|
+
* The ancestors are derivable (that is the whole premise of storing one value),
|
|
14
|
+
* so this walks them and pairs each with the tier name the definition declares
|
|
15
|
+
* in `validation.levels`.
|
|
16
|
+
*/
|
|
17
|
+
const attributeHierarchy_1 = require("./attributeHierarchy");
|
|
18
|
+
/**
|
|
19
|
+
* The chain a stored value stands for, shallowest tier first.
|
|
20
|
+
*
|
|
21
|
+
* `null` when there is nothing better to show than the raw value: no options
|
|
22
|
+
* loaded, an empty value, or a value that names no node (legacy data, or an
|
|
23
|
+
* option archived since it was chosen). Callers fall back to the ordinary
|
|
24
|
+
* single-value formatter in that case rather than rendering an empty list.
|
|
25
|
+
*
|
|
26
|
+
* A single-tier chain also returns null: one row captioned "Level 1" is
|
|
27
|
+
* strictly more furniture than the plain label it replaces.
|
|
28
|
+
*/
|
|
29
|
+
function hierarchyChain(def, value) {
|
|
30
|
+
if (typeof value !== 'string' || value.trim() === '')
|
|
31
|
+
return null;
|
|
32
|
+
const options = def.attributeOptions;
|
|
33
|
+
if (!options || options.length === 0)
|
|
34
|
+
return null;
|
|
35
|
+
const index = (0, attributeHierarchy_1.buildOptionIndex)(options);
|
|
36
|
+
const node = index.byValue.get(value);
|
|
37
|
+
if (!node)
|
|
38
|
+
return null;
|
|
39
|
+
const levels = def.validation?.levels ?? [];
|
|
40
|
+
const chain = [];
|
|
41
|
+
(0, attributeHierarchy_1.ancestryOf)(index, node).forEach((ancestor, level) => {
|
|
42
|
+
// ancestryOf is indexed BY LEVEL and is sparse: a tree that starts at
|
|
43
|
+
// level 1, or a gap left by a malformed row, leaves holes to skip.
|
|
44
|
+
if (!ancestor)
|
|
45
|
+
return;
|
|
46
|
+
chain.push({ tier: levels[level]?.trim() || null, label: ancestor.label, level });
|
|
47
|
+
});
|
|
48
|
+
return chain.length > 1 ? chain : null;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=attributeHierarchyChain.js.map
|