@es-joy/jsoe 0.28.2 → 0.30.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.
Files changed (167) hide show
  1. package/.claude/scheduled_tasks.lock +1 -0
  2. package/CHANGES.md +25 -0
  3. package/README.md +4 -0
  4. package/dist/formatAndTypeChoices.d.ts +6 -6
  5. package/dist/formatAndTypeChoices.d.ts.map +1 -1
  6. package/dist/formats/schema.d.ts +5 -0
  7. package/dist/formats/schema.d.ts.map +1 -1
  8. package/dist/formats.d.ts +2 -2
  9. package/dist/formats.d.ts.map +1 -1
  10. package/dist/fundamentalTypes/dateType.d.ts +26 -2
  11. package/dist/fundamentalTypes/dateType.d.ts.map +1 -1
  12. package/dist/fundamentalTypes/domexceptionType.d.ts +7 -0
  13. package/dist/fundamentalTypes/domexceptionType.d.ts.map +1 -1
  14. package/dist/index.d.ts +2 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +2 -2
  17. package/dist/index.js.map +1 -1
  18. package/dist/jtltExtensions.d.ts +112 -0
  19. package/dist/jtltExtensions.d.ts.map +1 -0
  20. package/dist/search/fundamentalTypes/arraySearchType.d.ts +22 -0
  21. package/dist/search/fundamentalTypes/arraySearchType.d.ts.map +1 -0
  22. package/dist/search/fundamentalTypes/bigintSearchType.d.ts +10 -0
  23. package/dist/search/fundamentalTypes/bigintSearchType.d.ts.map +1 -0
  24. package/dist/search/fundamentalTypes/blobSearchType.d.ts +19 -0
  25. package/dist/search/fundamentalTypes/blobSearchType.d.ts.map +1 -0
  26. package/dist/search/fundamentalTypes/booleanSearchType.d.ts +11 -0
  27. package/dist/search/fundamentalTypes/booleanSearchType.d.ts.map +1 -0
  28. package/dist/search/fundamentalTypes/catchSearchType.d.ts +18 -0
  29. package/dist/search/fundamentalTypes/catchSearchType.d.ts.map +1 -0
  30. package/dist/search/fundamentalTypes/dateSearchType.d.ts +15 -0
  31. package/dist/search/fundamentalTypes/dateSearchType.d.ts.map +1 -0
  32. package/dist/search/fundamentalTypes/domexceptionSearchType.d.ts +16 -0
  33. package/dist/search/fundamentalTypes/domexceptionSearchType.d.ts.map +1 -0
  34. package/dist/search/fundamentalTypes/enumSearchType.d.ts +26 -0
  35. package/dist/search/fundamentalTypes/enumSearchType.d.ts.map +1 -0
  36. package/dist/search/fundamentalTypes/errorSearchType.d.ts +9 -0
  37. package/dist/search/fundamentalTypes/errorSearchType.d.ts.map +1 -0
  38. package/dist/search/fundamentalTypes/fileSearchType.d.ts +28 -0
  39. package/dist/search/fundamentalTypes/fileSearchType.d.ts.map +1 -0
  40. package/dist/search/fundamentalTypes/filelistSearchType.d.ts +24 -0
  41. package/dist/search/fundamentalTypes/filelistSearchType.d.ts.map +1 -0
  42. package/dist/search/fundamentalTypes/functionSearchType.d.ts +18 -0
  43. package/dist/search/fundamentalTypes/functionSearchType.d.ts.map +1 -0
  44. package/dist/search/fundamentalTypes/mapSearchType.d.ts +22 -0
  45. package/dist/search/fundamentalTypes/mapSearchType.d.ts.map +1 -0
  46. package/dist/search/fundamentalTypes/nanSearchType.d.ts +7 -0
  47. package/dist/search/fundamentalTypes/nanSearchType.d.ts.map +1 -0
  48. package/dist/search/fundamentalTypes/nullSearchType.d.ts +7 -0
  49. package/dist/search/fundamentalTypes/nullSearchType.d.ts.map +1 -0
  50. package/dist/search/fundamentalTypes/numberSearchType.d.ts +11 -0
  51. package/dist/search/fundamentalTypes/numberSearchType.d.ts.map +1 -0
  52. package/dist/search/fundamentalTypes/objectSearchType.d.ts +21 -0
  53. package/dist/search/fundamentalTypes/objectSearchType.d.ts.map +1 -0
  54. package/dist/search/fundamentalTypes/promiseSearchType.d.ts +20 -0
  55. package/dist/search/fundamentalTypes/promiseSearchType.d.ts.map +1 -0
  56. package/dist/search/fundamentalTypes/regexpSearchType.d.ts +20 -0
  57. package/dist/search/fundamentalTypes/regexpSearchType.d.ts.map +1 -0
  58. package/dist/search/fundamentalTypes/setSearchType.d.ts +15 -0
  59. package/dist/search/fundamentalTypes/setSearchType.d.ts.map +1 -0
  60. package/dist/search/fundamentalTypes/stringSearchType.d.ts +21 -0
  61. package/dist/search/fundamentalTypes/stringSearchType.d.ts.map +1 -0
  62. package/dist/search/fundamentalTypes/symbolSearchType.d.ts +16 -0
  63. package/dist/search/fundamentalTypes/symbolSearchType.d.ts.map +1 -0
  64. package/dist/search/fundamentalTypes/undefinedSearchType.d.ts +7 -0
  65. package/dist/search/fundamentalTypes/undefinedSearchType.d.ts.map +1 -0
  66. package/dist/search/index.d.ts +70 -0
  67. package/dist/search/index.d.ts.map +1 -0
  68. package/dist/search/queryTree.d.ts +12 -1
  69. package/dist/search/queryTree.d.ts.map +1 -1
  70. package/dist/search/queryTreeBuilders.d.ts +18 -0
  71. package/dist/search/queryTreeBuilders.d.ts.map +1 -1
  72. package/dist/search/searchDispatch.d.ts +44 -0
  73. package/dist/search/searchDispatch.d.ts.map +1 -1
  74. package/dist/search/searchElementUtils.d.ts +106 -0
  75. package/dist/search/searchElementUtils.d.ts.map +1 -0
  76. package/dist/search/searchUtils.d.ts +550 -0
  77. package/dist/search/searchUtils.d.ts.map +1 -0
  78. package/dist/search/subTypes/blobHTMLSearchType.d.ts +36 -0
  79. package/dist/search/subTypes/blobHTMLSearchType.d.ts.map +1 -0
  80. package/dist/search/subTypes/recordSearchType.d.ts +18 -0
  81. package/dist/search/subTypes/recordSearchType.d.ts.map +1 -0
  82. package/dist/search/subTypes/tupleSearchType.d.ts +21 -0
  83. package/dist/search/subTypes/tupleSearchType.d.ts.map +1 -0
  84. package/dist/search/superTypes/SpecialRealNumberSearchType.d.ts +14 -0
  85. package/dist/search/superTypes/SpecialRealNumberSearchType.d.ts.map +1 -0
  86. package/dist/search/superTypes/buffersourceSearchType.d.ts +17 -0
  87. package/dist/search/superTypes/buffersourceSearchType.d.ts.map +1 -0
  88. package/dist/search/superTypes/dommatrixSearchType.d.ts +11 -0
  89. package/dist/search/superTypes/dommatrixSearchType.d.ts.map +1 -0
  90. package/dist/search/superTypes/dompointSearchType.d.ts +9 -0
  91. package/dist/search/superTypes/dompointSearchType.d.ts.map +1 -0
  92. package/dist/search/superTypes/domrectSearchType.d.ts +9 -0
  93. package/dist/search/superTypes/domrectSearchType.d.ts.map +1 -0
  94. package/dist/search/superTypes/errorsSpecialSearchType.d.ts +11 -0
  95. package/dist/search/superTypes/errorsSpecialSearchType.d.ts.map +1 -0
  96. package/dist/search/unions/discriminatedUnionSearchType.d.ts +12 -0
  97. package/dist/search/unions/discriminatedUnionSearchType.d.ts.map +1 -0
  98. package/dist/search/unions/unionFamilySearchType.d.ts +36 -0
  99. package/dist/search/unions/unionFamilySearchType.d.ts.map +1 -0
  100. package/dist/search/unions/unionSearchType.d.ts +8 -0
  101. package/dist/search/unions/unionSearchType.d.ts.map +1 -0
  102. package/dist/search/unions/xorSearchType.d.ts +12 -0
  103. package/dist/search/unions/xorSearchType.d.ts.map +1 -0
  104. package/dist/superTypes/buffersourceType.d.ts.map +1 -1
  105. package/dist/typeChoices.d.ts +3 -3
  106. package/dist/typeChoices.d.ts.map +1 -1
  107. package/dist/types.d.ts +3 -3
  108. package/dist/types.d.ts.map +1 -1
  109. package/package.json +5 -4
  110. package/pnpm-workspace.yaml +1 -1
  111. package/rollup.config.js +12 -0
  112. package/src/formatAndTypeChoices.js +4 -4
  113. package/src/formats/schema.js +1 -1
  114. package/src/formats.js +1 -1
  115. package/src/fundamentalTypes/dateType.js +40 -14
  116. package/src/fundamentalTypes/domexceptionType.js +41 -33
  117. package/src/fundamentalTypes/regexpType.js +1 -1
  118. package/src/index.js +6 -0
  119. package/src/jsoe.css +204 -0
  120. package/src/jtltExtensions.js +103 -0
  121. package/src/search/fundamentalTypes/arraySearchType.js +101 -0
  122. package/src/search/fundamentalTypes/bigintSearchType.js +68 -0
  123. package/src/search/fundamentalTypes/blobSearchType.js +42 -0
  124. package/src/search/fundamentalTypes/booleanSearchType.js +40 -0
  125. package/src/search/fundamentalTypes/catchSearchType.js +59 -0
  126. package/src/search/fundamentalTypes/dateSearchType.js +166 -0
  127. package/src/search/fundamentalTypes/domexceptionSearchType.js +84 -0
  128. package/src/search/fundamentalTypes/enumSearchType.js +62 -0
  129. package/src/search/fundamentalTypes/errorSearchType.js +11 -0
  130. package/src/search/fundamentalTypes/fileSearchType.js +94 -0
  131. package/src/search/fundamentalTypes/filelistSearchType.js +103 -0
  132. package/src/search/fundamentalTypes/functionSearchType.js +102 -0
  133. package/src/search/fundamentalTypes/mapSearchType.js +117 -0
  134. package/src/search/fundamentalTypes/nanSearchType.js +11 -0
  135. package/src/search/fundamentalTypes/nullSearchType.js +11 -0
  136. package/src/search/fundamentalTypes/numberSearchType.js +59 -0
  137. package/src/search/fundamentalTypes/objectSearchType.js +367 -0
  138. package/src/search/fundamentalTypes/promiseSearchType.js +61 -0
  139. package/src/search/fundamentalTypes/regexpSearchType.js +72 -0
  140. package/src/search/fundamentalTypes/setSearchType.js +91 -0
  141. package/src/search/fundamentalTypes/stringSearchType.js +44 -0
  142. package/src/search/fundamentalTypes/symbolSearchType.js +39 -0
  143. package/src/search/fundamentalTypes/undefinedSearchType.js +11 -0
  144. package/src/search/index.js +96 -0
  145. package/src/search/queryTree.js +244 -0
  146. package/src/search/queryTreeBuilders.js +233 -0
  147. package/src/search/searchDispatch.js +291 -0
  148. package/src/search/searchElementUtils.js +373 -0
  149. package/src/search/searchUtils.js +847 -0
  150. package/src/search/subTypes/blobHTMLSearchType.js +122 -0
  151. package/src/search/subTypes/recordSearchType.js +105 -0
  152. package/src/search/subTypes/tupleSearchType.js +152 -0
  153. package/src/search/superTypes/SpecialRealNumberSearchType.js +42 -0
  154. package/src/search/superTypes/buffersourceSearchType.js +53 -0
  155. package/src/search/superTypes/dommatrixSearchType.js +24 -0
  156. package/src/search/superTypes/dompointSearchType.js +15 -0
  157. package/src/search/superTypes/domrectSearchType.js +15 -0
  158. package/src/search/superTypes/errorsSpecialSearchType.js +15 -0
  159. package/src/search/unions/discriminatedUnionSearchType.js +16 -0
  160. package/src/search/unions/unionFamilySearchType.js +168 -0
  161. package/src/search/unions/unionSearchType.js +12 -0
  162. package/src/search/unions/xorSearchType.js +16 -0
  163. package/src/superTypes/buffersourceType.js +13 -15
  164. package/src/typeChoices.js +2 -2
  165. package/src/types.js +2 -2
  166. package/tsconfig-prod.json +12 -0
  167. package/tsconfig.json +5 -10
@@ -0,0 +1,373 @@
1
+ import {
2
+ buildPathLabel, buildCheckbox, readCheckbox,
3
+ buildLiteralRegexControls, readLiteralRegexQuery,
4
+ buildRangeInputsPair, readRangeInputsPair, syncRangeValidity,
5
+ buildTriStateSelect, readTriStateSelect,
6
+ buildOptInFieldset, readOptInChecked, wireOptInFieldset,
7
+ buildAtLeastOneSentinel, syncAtLeastOneCheck
8
+ } from './searchUtils.js';
9
+ import {combineAnd, makeRangeLeaf, makeDomShapeLeaf} from './queryTreeBuilders.js';
10
+ import regexpType from '../fundamentalTypes/regexpType.js';
11
+
12
+ /**
13
+ * Locates a built search element by the JSON-Pointer path it was built for.
14
+ * Compares `dataset.searchPath` in JS rather than interpolating `path` into
15
+ * a `querySelector` attribute-value string, since a schema path can embed
16
+ * arbitrary property-name characters (only `~`/`/` are escaped by
17
+ * `makeJSONPointer`) that would otherwise need CSS-selector escaping.
18
+ * @param {ParentNode} root
19
+ * @param {string} path
20
+ * @returns {HTMLElement|undefined}
21
+ */
22
+ export function findSearchElement (root, path) {
23
+ return /** @type {HTMLElement[]} */ (
24
+ [...root.querySelectorAll('[data-search-path]')]
25
+ ).find((el) => el.dataset.searchPath === path);
26
+ }
27
+
28
+ /**
29
+ * @typedef {HTMLElement & {
30
+ * getQuery: () => import('./queryTree.js').QueryNode|undefined
31
+ * }} SearchElement
32
+ */
33
+
34
+ /**
35
+ * The `SearchTypeObject.getQuery` every leaf module implements identically:
36
+ * locate the element it built under `root` and delegate to its own
37
+ * (per-element, `dataset`-driven) `getQuery()` method.
38
+ * @param {{root: HTMLElement, path: string}} cfg
39
+ * @returns {import('./queryTree.js').QueryNode|undefined}
40
+ */
41
+ export function getQueryViaElement ({root, path}) {
42
+ const el = /** @type {SearchElement|undefined} */ (
43
+ findSearchElement(root, path)
44
+ );
45
+ return el?.getQuery();
46
+ }
47
+
48
+ /**
49
+ * Type-predicate guard distinguishing a built search element (leaf or
50
+ * container, either answers to `.getQuery()`) from a plain DOM node - used
51
+ * by container elements (e.g. `objectSearchType.js`'s `<jsoe-search-object>`)
52
+ * that walk `this.children` polymorphically (search plan §8) without
53
+ * knowing which children are search elements and which are plain layout
54
+ * (a controls `<div>`, etc.).
55
+ * @param {Element} el
56
+ * @returns {el is SearchElement}
57
+ */
58
+ export function hasGetQuery (el) {
59
+ return typeof (/** @type {{getQuery?: unknown}} */ (el)).getQuery ===
60
+ 'function';
61
+ }
62
+
63
+ /**
64
+ * Factory for the README's "no variants to allow for distinct search"
65
+ * leaves (`undefined`/`void`, `null`, `NaN`): the only meaningful question
66
+ * is whether the path is present at all, which only matters once the path
67
+ * is optional or nested in a union - so all three share one tiny, single-
68
+ * checkbox implementation, differing only in tag name/label. Unlike every
69
+ * other leaf, this one is never left for the user to configure at all: its
70
+ * checkbox is pre-checked and `disabled` (`buildCheckbox`'s doc), since a
71
+ * type with only one possible value has nothing else worth offering a
72
+ * choice between - it's simply valid, unconditionally, the moment it
73
+ * exists, rather than needing a `required`-style rule of its own.
74
+ * @param {{tagName: string}} cfg
75
+ * @returns {import('./searchDispatch.js').SearchTypeObject}
76
+ */
77
+ export function makePresenceOnlySearchType ({tagName}) {
78
+ return {
79
+ buildUI ({schemaObject, path, typeNamespace}) {
80
+ const label = buildPathLabel(schemaObject, path);
81
+ const name = `${typeNamespace}-${tagName}`;
82
+ return [tagName, {
83
+ dataset: {searchPath: path, searchKind: tagName},
84
+ title: label,
85
+ $define: {
86
+ /** @this {HTMLElement} */
87
+ getQuery () {
88
+ const checked = readCheckbox(this);
89
+ return checked
90
+ ? {kind: 'presence', path: this.dataset.searchPath ?? '', $exists: true}
91
+ : undefined;
92
+ }
93
+ }
94
+ }, [
95
+ ['span', {class: 'searchLabel'}, [label]],
96
+ buildCheckbox({
97
+ name, label: `Require ${label} present`, checked: true, disabled: true
98
+ })
99
+ ]];
100
+ },
101
+ getQuery: getQueryViaElement
102
+ };
103
+ }
104
+
105
+ /**
106
+ * The Error family's fixed known properties (`errorType.js`'s own
107
+ * `setValue`/`getValue` hard-code this same set - `message`/`name`/
108
+ * `fileName`/`stack` as strings, `lineNumber`/`columnNumber` as numbers -
109
+ * since a `checked`-type schema like `error`/`errors` carries no structural
110
+ * detail of its own to derive property names/types from, unlike `object`).
111
+ * @type {string[]}
112
+ */
113
+ const errorStringProps = ['message', 'name', 'fileName', 'stack'];
114
+
115
+ /** @type {string[]} */
116
+ const errorNumberProps = ['lineNumber', 'columnNumber'];
117
+
118
+ /**
119
+ * @param {{label: string, name: string}} cfg
120
+ * @returns {import('./searchUtils.js').JamilihArray[]}
121
+ */
122
+ function buildErrorFamilyChildren ({label, name}) {
123
+ /** @type {import('./searchUtils.js').JamilihArray[]} */
124
+ const children = [['span', {class: 'searchLabel'}, [label]]];
125
+ errorStringProps.forEach((prop) => {
126
+ children.push(...buildOptInFieldset({
127
+ name: `${name}-${prop}`, key: prop, label: prop,
128
+ children: [buildLiteralRegexControls({
129
+ name: `${name}-${prop}`, key: prop, flagOptions: regexpType.allowedFlags
130
+ })]
131
+ }));
132
+ });
133
+ errorNumberProps.forEach((prop) => {
134
+ children.push(...buildOptInFieldset({
135
+ name: `${name}-${prop}`, key: prop, label: prop,
136
+ children: buildRangeInputsPair({name: `${name}-${prop}`, key: prop})
137
+ }));
138
+ });
139
+ children.push(buildAtLeastOneSentinel());
140
+ return children;
141
+ }
142
+
143
+ /**
144
+ * @param {Element} root - a `jsoe-search-error`/`jsoe-search-errors-special`
145
+ * element
146
+ * @returns {void}
147
+ */
148
+ function syncErrorFamilyValidity (root) {
149
+ syncAtLeastOneCheck(
150
+ root,
151
+ () => [...errorStringProps, ...errorNumberProps].some(
152
+ (prop) => readOptInChecked(root, prop)
153
+ )
154
+ );
155
+ }
156
+
157
+ /**
158
+ * Factory for `errorSearchType.js`/`errorsSpecialSearchType.js`, offering
159
+ * literal/regex search of child string properties, numeric of number
160
+ * children (README) - one `buildLiteralRegexControls`/`buildRangeInputsPair`
161
+ * pair per known property (`key`-distinguished so they coexist in one
162
+ * widget; see those helpers' docs), each wrapped in its own
163
+ * `buildOptInFieldset` (so picking, say, just `message` doesn't also force
164
+ * `name`/`fileName`/`stack`/`lineNumber`/`columnNumber` to be filled in),
165
+ * combined via `$and`, with `buildAtLeastOneSentinel` requiring at least one
166
+ * of the six to actually be opted into. `.cause` (a recursive, arbitrarily-
167
+ * typed property) and `errorsSpecial`'s `AggregateError.errors` array are
168
+ * deliberately left out of this pass - the README's bullet asks only for
169
+ * the flat string/number children, and open-ended recursion into an
170
+ * arbitrary `cause` chain is a materially bigger feature than "the same
171
+ * shape, twice more".
172
+ * @param {{tagName: string}} cfg
173
+ * @returns {import('./searchDispatch.js').SearchTypeObject}
174
+ */
175
+ export function makeErrorFamilySearchType ({tagName}) {
176
+ return {
177
+ buildUI ({schemaObject, path, typeNamespace}) {
178
+ const label = buildPathLabel(schemaObject, path);
179
+ const name = `${typeNamespace}-${tagName}`;
180
+ return [tagName, {
181
+ dataset: {searchPath: path, searchKind: tagName},
182
+ title: label,
183
+ $define: {
184
+ /** @this {HTMLElement} */
185
+ connectedCallback () {
186
+ errorNumberProps.forEach((prop) => syncRangeValidity(this, prop));
187
+ [...errorStringProps, ...errorNumberProps].forEach((prop) => (
188
+ wireOptInFieldset(this, prop, () => syncErrorFamilyValidity(this))
189
+ ));
190
+ syncErrorFamilyValidity(this);
191
+ },
192
+ /** @this {HTMLElement} */
193
+ getQuery () {
194
+ const searchPath = this.dataset.searchPath ?? '';
195
+ const stringLeaves = errorStringProps.map((prop) => (
196
+ readOptInChecked(this, prop)
197
+ ? readLiteralRegexQuery(this, `${searchPath}/${prop}`, prop)
198
+ : undefined
199
+ ));
200
+ const numberLeaves = errorNumberProps.map((prop) => {
201
+ if (!readOptInChecked(this, prop)) {
202
+ return undefined;
203
+ }
204
+ const {gte, lte} = readRangeInputsPair(this, prop);
205
+ if (gte === '' && lte === '') {
206
+ return undefined;
207
+ }
208
+ return makeRangeLeaf(`${searchPath}/${prop}`, 'number', {
209
+ ...(gte === '' ? {} : {$gte: Number(gte)}),
210
+ ...(lte === '' ? {} : {$lte: Number(lte)})
211
+ });
212
+ });
213
+ return combineAnd([...stringLeaves, ...numberLeaves]);
214
+ }
215
+ }
216
+ }, buildErrorFamilyChildren({label, name})];
217
+ },
218
+ getQuery: getQueryViaElement
219
+ };
220
+ }
221
+
222
+ /**
223
+ * @param {{
224
+ * label: string, name: string, dimensionKeys: string[],
225
+ * includeReadonly: boolean, includeDimensionCheck: boolean
226
+ * }} cfg
227
+ * @returns {import('./searchUtils.js').JamilihArray[]}
228
+ */
229
+ function buildDomShapeChildren ({
230
+ label, name, dimensionKeys, includeReadonly, includeDimensionCheck
231
+ }) {
232
+ /** @type {import('./searchUtils.js').JamilihArray[]} */
233
+ const children = [['span', {class: 'searchLabel'}, [label]]];
234
+ dimensionKeys.forEach((dim) => {
235
+ children.push(...buildOptInFieldset({
236
+ name: `${name}-${dim}`, key: dim, label: dim,
237
+ children: buildRangeInputsPair({name: `${name}-${dim}`, key: dim})
238
+ }));
239
+ });
240
+ if (includeReadonly) {
241
+ children.push(['label', [
242
+ 'Readonly: ',
243
+ buildTriStateSelect({
244
+ name: `${name}-readonly`, key: 'readonly',
245
+ trueLabel: 'Readonly', falseLabel: 'Not readonly'
246
+ })
247
+ ]]);
248
+ }
249
+ if (includeDimensionCheck) {
250
+ children.push(['label', [
251
+ '3d: ',
252
+ buildTriStateSelect({
253
+ name: `${name}-dimension`, key: 'dimension',
254
+ trueLabel: '3d', falseLabel: 'Not 3d'
255
+ })
256
+ ]]);
257
+ }
258
+ children.push(buildAtLeastOneSentinel());
259
+ return children;
260
+ }
261
+
262
+ /**
263
+ * @param {{
264
+ * root: Element, dimensionKeys: string[], includeReadonly: boolean,
265
+ * includeDimensionCheck: boolean
266
+ * }} cfg
267
+ * @returns {void}
268
+ */
269
+ function syncDomShapeValidity ({root, dimensionKeys, includeReadonly, includeDimensionCheck}) {
270
+ syncAtLeastOneCheck(root, () => (
271
+ dimensionKeys.some((dim) => readOptInChecked(root, dim)) ||
272
+ (includeReadonly && readTriStateSelect(root, 'readonly') !== undefined) ||
273
+ (includeDimensionCheck && readTriStateSelect(root, 'dimension') !== undefined)
274
+ ));
275
+ }
276
+
277
+ /**
278
+ * Factory for `domrectSearchType.js`/`dompointSearchType.js`/
279
+ * `dommatrixSearchType.js`: numeric search ranges for children; "Is/Is not
280
+ * Readonly"; DOMMatrix: "Is/Is not 3d" (README) - one `buildRangeInputsPair`
281
+ * per known dimension (`key`-distinguished so they coexist in one widget;
282
+ * `dimensionKeys` covers DOMMatrix's 2D (`a`-`f`) *and* 3D (`m11`-`m44`)
283
+ * shapes at once rather than switching the control set on an "Is/Is not 3d"
284
+ * answer that's itself just another optional search constraint, not a
285
+ * schema fact - `domrect`/`dompoint`/`dommatrix` are all "checked" types
286
+ * with no structural schema to derive dimension names from in the first
287
+ * place, same reasoning as `makeErrorFamilySearchType`), combined into one
288
+ * `domShape` leaf (`queryTree.js`) rather than separate leaves via `$and`.
289
+ * @param {{
290
+ * tagName: string,
291
+ * dimensionKeys: string[],
292
+ * includeReadonly?: boolean,
293
+ * includeDimensionCheck?: boolean
294
+ * }} cfg
295
+ * @returns {import('./searchDispatch.js').SearchTypeObject}
296
+ */
297
+ export function makeDomShapeSearchType ({
298
+ tagName, dimensionKeys, includeReadonly = false, includeDimensionCheck = false
299
+ }) {
300
+ return {
301
+ buildUI ({schemaObject, path, typeNamespace}) {
302
+ const label = buildPathLabel(schemaObject, path);
303
+ const name = `${typeNamespace}-${tagName}`;
304
+ return [tagName, {
305
+ dataset: {searchPath: path, searchKind: tagName},
306
+ title: label,
307
+ $define: {
308
+ /** @this {HTMLElement} */
309
+ connectedCallback () {
310
+ const syncValidity = () => syncDomShapeValidity({
311
+ root: this, dimensionKeys, includeReadonly, includeDimensionCheck
312
+ });
313
+ dimensionKeys.forEach((dim) => {
314
+ syncRangeValidity(this, dim);
315
+ wireOptInFieldset(this, dim, syncValidity);
316
+ });
317
+ if (includeReadonly) {
318
+ this.querySelector('select.jsoeSearchTriState--readonly')?.addEventListener(
319
+ 'change', syncValidity
320
+ );
321
+ }
322
+ if (includeDimensionCheck) {
323
+ this.querySelector('select.jsoeSearchTriState--dimension')?.addEventListener(
324
+ 'change', syncValidity
325
+ );
326
+ }
327
+ syncValidity();
328
+ },
329
+ /** @this {HTMLElement} */
330
+ getQuery () {
331
+ const searchPath = this.dataset.searchPath ?? '';
332
+ /** @type {{[dim: string]: import('./queryTree.js').QueryRangeLeaf}} */
333
+ const dimensions = {};
334
+ dimensionKeys.forEach((dim) => {
335
+ if (!readOptInChecked(this, dim)) {
336
+ return;
337
+ }
338
+ const {gte, lte} = readRangeInputsPair(this, dim);
339
+ if (gte === '' && lte === '') {
340
+ return;
341
+ }
342
+ dimensions[dim] = makeRangeLeaf(`${searchPath}/${dim}`, 'number', {
343
+ ...(gte === '' ? {} : {$gte: Number(gte)}),
344
+ ...(lte === '' ? {} : {$lte: Number(lte)})
345
+ });
346
+ });
347
+ const readonlyCheck = includeReadonly
348
+ ? readTriStateSelect(this, 'readonly')
349
+ : undefined;
350
+ const dimensionCheck = includeDimensionCheck
351
+ ? readTriStateSelect(this, 'dimension')
352
+ : undefined;
353
+ if (
354
+ readonlyCheck === undefined && dimensionCheck === undefined &&
355
+ Object.keys(dimensions).length === 0
356
+ ) {
357
+ return undefined;
358
+ }
359
+ return makeDomShapeLeaf(searchPath, dimensions, {
360
+ ...(readonlyCheck === undefined ? {} : {readonlyCheck}),
361
+ ...(dimensionCheck === undefined
362
+ ? {}
363
+ : {dimensionCheck: dimensionCheck ? 3 : 2})
364
+ });
365
+ }
366
+ }
367
+ }, buildDomShapeChildren({
368
+ label, name, dimensionKeys, includeReadonly, includeDimensionCheck
369
+ })];
370
+ },
371
+ getQuery: getQueryViaElement
372
+ };
373
+ }