@es-joy/jsoe 0.31.0 → 0.32.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/CHANGES.md +4 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/search/fundamentalTypes/arraySearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/bigintSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/blobSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/booleanSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/catchSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/dateSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/domexceptionSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/enumSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/fileSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/filelistSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/functionSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/mapSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/numberSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/objectSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/promiseSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/regexpSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/setSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/stringSearchType.d.ts.map +1 -1
- package/dist/search/fundamentalTypes/symbolSearchType.d.ts.map +1 -1
- package/dist/search/index.d.ts +2 -0
- package/dist/search/index.d.ts.map +1 -1
- package/dist/search/searchDispatch.d.ts +11 -3
- package/dist/search/searchDispatch.d.ts.map +1 -1
- package/dist/search/searchElementUtils.d.ts +31 -1
- package/dist/search/searchElementUtils.d.ts.map +1 -1
- package/dist/search/searchUtils.d.ts +180 -0
- package/dist/search/searchUtils.d.ts.map +1 -1
- package/dist/search/subTypes/blobHTMLSearchType.d.ts.map +1 -1
- package/dist/search/subTypes/recordSearchType.d.ts.map +1 -1
- package/dist/search/subTypes/tupleSearchType.d.ts.map +1 -1
- package/dist/search/superTypes/SpecialRealNumberSearchType.d.ts.map +1 -1
- package/dist/search/superTypes/buffersourceSearchType.d.ts.map +1 -1
- package/dist/search/unions/unionFamilySearchType.d.ts.map +1 -1
- package/dist/utils/json6Stringify.d.ts +2 -0
- package/dist/utils/json6Stringify.d.ts.map +1 -0
- package/dist/utils/rawTypesonEditor.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/search/fundamentalTypes/arraySearchType.js +28 -5
- package/src/search/fundamentalTypes/bigintSearchType.js +14 -3
- package/src/search/fundamentalTypes/blobSearchType.js +21 -3
- package/src/search/fundamentalTypes/booleanSearchType.js +15 -3
- package/src/search/fundamentalTypes/catchSearchType.js +20 -3
- package/src/search/fundamentalTypes/dateSearchType.js +42 -3
- package/src/search/fundamentalTypes/domexceptionSearchType.js +18 -4
- package/src/search/fundamentalTypes/enumSearchType.js +15 -3
- package/src/search/fundamentalTypes/fileSearchType.js +15 -3
- package/src/search/fundamentalTypes/filelistSearchType.js +25 -5
- package/src/search/fundamentalTypes/functionSearchType.js +31 -4
- package/src/search/fundamentalTypes/mapSearchType.js +33 -5
- package/src/search/fundamentalTypes/numberSearchType.js +15 -3
- package/src/search/fundamentalTypes/objectSearchType.js +94 -5
- package/src/search/fundamentalTypes/promiseSearchType.js +20 -3
- package/src/search/fundamentalTypes/regexpSearchType.js +24 -4
- package/src/search/fundamentalTypes/setSearchType.js +25 -5
- package/src/search/fundamentalTypes/stringSearchType.js +25 -3
- package/src/search/fundamentalTypes/symbolSearchType.js +21 -3
- package/src/search/index.js +22 -0
- package/src/search/searchDispatch.js +14 -5
- package/src/search/searchElementUtils.js +98 -10
- package/src/search/searchUtils.js +368 -0
- package/src/search/subTypes/blobHTMLSearchType.js +43 -3
- package/src/search/subTypes/recordSearchType.js +30 -4
- package/src/search/subTypes/tupleSearchType.js +43 -5
- package/src/search/superTypes/SpecialRealNumberSearchType.js +15 -3
- package/src/search/superTypes/buffersourceSearchType.js +14 -3
- package/src/search/unions/unionFamilySearchType.js +45 -3
- package/src/utils/json6Stringify.js +50 -0
- package/src/utils/rawTypesonEditor.js +1 -51
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {schemaLabel} from '../utils/schemaMeta.js';
|
|
2
2
|
import {getJSONPointerParts} from '../utils/jsonPointer.js';
|
|
3
|
+
import {combineAnd} from './queryTreeBuilders.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* @typedef {import('../types.js').JamilihArray} JamilihArray
|
|
@@ -905,3 +906,370 @@ export function setDescendantsRequired (root, required) {
|
|
|
905
906
|
).required = required;
|
|
906
907
|
});
|
|
907
908
|
}
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* @typedef {import('./queryTree.js').QueryNode} QueryNode
|
|
912
|
+
*/
|
|
913
|
+
|
|
914
|
+
/**
|
|
915
|
+
* The "Edit raw" round-trip (`SearchChoicesControl.$applyQuery`,
|
|
916
|
+
* `src/search/index.js`) needs the inverse of every `getQuery`: given a
|
|
917
|
+
* previously-serialized (or hand-edited) `QueryNode`, drive the same DOM
|
|
918
|
+
* controls `getQuery` reads back into that state, so the two stay in sync.
|
|
919
|
+
* `unwrapAndClauses`/`nodeTouchesPath`/`extractLeafOfKind`/
|
|
920
|
+
* `extractClauseForPath` below are the shared groundwork every
|
|
921
|
+
* `applyQuery` implementation builds on to undo `combineAnd`'s own
|
|
922
|
+
* flattening; the `apply*` functions below those are the direct inverse of
|
|
923
|
+
* one `build*`/`read*` pair each.
|
|
924
|
+
*
|
|
925
|
+
* A plain leaf becomes a one-element array; `undefined` (no constraint)
|
|
926
|
+
* becomes `[]`; an `$and` node's own `.$and` array is returned as-is (not
|
|
927
|
+
* further flattened - a nested `$and` stays a single clause, so
|
|
928
|
+
* `nodeTouchesPath` below is what actually looks inside one).
|
|
929
|
+
* @param {QueryNode|undefined} queryNode
|
|
930
|
+
* @returns {QueryNode[]}
|
|
931
|
+
*/
|
|
932
|
+
export function unwrapAndClauses (queryNode) {
|
|
933
|
+
if (queryNode === undefined) {
|
|
934
|
+
return [];
|
|
935
|
+
}
|
|
936
|
+
if ('$and' in queryNode) {
|
|
937
|
+
return queryNode.$and;
|
|
938
|
+
}
|
|
939
|
+
return [queryNode];
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
/**
|
|
943
|
+
* Whether any leaf reachable from `node` (recursing through `$and`/`$or`
|
|
944
|
+
* combinators and a `not`/`passThrough` leaf's own wrapped `query`) targets
|
|
945
|
+
* `path` itself or somewhere nested under it - used to decide which of a
|
|
946
|
+
* container widget's several recursed children (`tupleSearchType.js`'s
|
|
947
|
+
* positions/rest, `functionSearchType.js`'s args/output,
|
|
948
|
+
* `objectSearchType.js`'s properties, `unionFamilySearchType.js`'s chosen
|
|
949
|
+
* branch) a given top-level clause belongs to, since a recursed child's own
|
|
950
|
+
* `getQuery()` result is threaded through untouched (whatever shape it is)
|
|
951
|
+
* rather than re-wrapped with the parent's path.
|
|
952
|
+
* @param {QueryNode|undefined} node
|
|
953
|
+
* @param {string} path
|
|
954
|
+
* @returns {boolean}
|
|
955
|
+
*/
|
|
956
|
+
export function nodeTouchesPath (node, path) {
|
|
957
|
+
// `QueryNotLeaf` is the only leaf kind with no `path` of its own - unwrap
|
|
958
|
+
// its `query` (iteratively, in case of a `not` wrapping another `not`)
|
|
959
|
+
// before dispatching on the combinator/leaf shapes below.
|
|
960
|
+
let current = node;
|
|
961
|
+
while (
|
|
962
|
+
current !== undefined && !('$and' in current) && !('$or' in current) &&
|
|
963
|
+
!('path' in current) && 'query' in current
|
|
964
|
+
) {
|
|
965
|
+
({query: current} = current);
|
|
966
|
+
}
|
|
967
|
+
if (current === undefined) {
|
|
968
|
+
return false;
|
|
969
|
+
}
|
|
970
|
+
if ('$and' in current) {
|
|
971
|
+
return current.$and.some((child) => nodeTouchesPath(child, path));
|
|
972
|
+
}
|
|
973
|
+
if ('$or' in current) {
|
|
974
|
+
return current.$or.some((child) => nodeTouchesPath(child, path));
|
|
975
|
+
}
|
|
976
|
+
if ('path' in current) {
|
|
977
|
+
return current.path === path || current.path.startsWith(`${path}/`);
|
|
978
|
+
}
|
|
979
|
+
return false;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
/**
|
|
983
|
+
* Pulls the (at most one) top-level clause of `queryNode` whose own `kind`
|
|
984
|
+
* matches out of the `$and` it's combined into, leaving the rest re-combined
|
|
985
|
+
* - the inverse half of `combineAnd([ownLeaf, ...])`. Safe to use for any
|
|
986
|
+
* facet whose own leaf kind is unique within its widget (every `kind` this
|
|
987
|
+
* module's callers pass is exactly that: `lengthSize`, `hasProperty`,
|
|
988
|
+
* `mapRecordJoint`, `typeOf`), since a recursed child's own (arbitrarily
|
|
989
|
+
* nested) contribution never surfaces as a *bare* top-level leaf sharing the
|
|
990
|
+
* parent's own facet kind.
|
|
991
|
+
* `kind`'s own type parameter narrows `matched`'s type to exactly the leaf
|
|
992
|
+
* shape that `kind` names (`Extract<QueryLeaf, {kind: K}>`), so a call like
|
|
993
|
+
* `extractLeafOfKind(queryNode, 'range')` gives back a properly-typed
|
|
994
|
+
* `QueryRangeLeaf|undefined` with no cast needed at the call site.
|
|
995
|
+
* @template {import('./queryTree.js').QueryLeaf['kind']} K
|
|
996
|
+
* @param {QueryNode|undefined} queryNode
|
|
997
|
+
* @param {K} kind
|
|
998
|
+
* @returns {{
|
|
999
|
+
* matched: Extract<import('./queryTree.js').QueryLeaf, {kind: K}>|undefined,
|
|
1000
|
+
* rest: QueryNode|undefined
|
|
1001
|
+
* }}
|
|
1002
|
+
*/
|
|
1003
|
+
export function extractLeafOfKind (queryNode, kind) {
|
|
1004
|
+
const clauses = unwrapAndClauses(queryNode);
|
|
1005
|
+
const idx = clauses.findIndex((clause) => 'kind' in clause && clause.kind === kind);
|
|
1006
|
+
if (idx === -1) {
|
|
1007
|
+
return {matched: undefined, rest: queryNode};
|
|
1008
|
+
}
|
|
1009
|
+
return {
|
|
1010
|
+
matched: /** @type {Extract<import('./queryTree.js').QueryLeaf, {kind: K}>} */ (
|
|
1011
|
+
clauses[idx]
|
|
1012
|
+
),
|
|
1013
|
+
rest: combineAnd(clauses.filter((_clause, i) => i !== idx))
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
/**
|
|
1018
|
+
* Pulls the (at most one) top-level clause of `queryNode` that
|
|
1019
|
+
* `nodeTouchesPath` says belongs to `path` out of the `$and` it's combined
|
|
1020
|
+
* into, leaving the rest re-combined - the path-based counterpart of
|
|
1021
|
+
* `extractLeafOfKind`, for a recursed child's own contribution (whose shape
|
|
1022
|
+
* isn't a single known leaf `kind`, unlike a container's own facets).
|
|
1023
|
+
* @param {QueryNode|undefined} queryNode
|
|
1024
|
+
* @param {string} path
|
|
1025
|
+
* @returns {{matched: QueryNode|undefined, rest: QueryNode|undefined}}
|
|
1026
|
+
*/
|
|
1027
|
+
export function extractClauseForPath (queryNode, path) {
|
|
1028
|
+
const clauses = unwrapAndClauses(queryNode);
|
|
1029
|
+
const idx = clauses.findIndex((clause) => nodeTouchesPath(clause, path));
|
|
1030
|
+
if (idx === -1) {
|
|
1031
|
+
return {matched: undefined, rest: queryNode};
|
|
1032
|
+
}
|
|
1033
|
+
return {
|
|
1034
|
+
matched: clauses[idx],
|
|
1035
|
+
rest: combineAnd(clauses.filter((_clause, i) => i !== idx))
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
/**
|
|
1040
|
+
* The inverse of `readLiteralRegexQuery` - sets `buildLiteralRegexControls`'s
|
|
1041
|
+
* Mode/Value/Flags back from a previously-read leaf (or resets to defaults
|
|
1042
|
+
* for `undefined`, meaning the facet no longer has a constraint at all).
|
|
1043
|
+
* Dispatches `change` on the Mode select (so its own `handleModeChange`
|
|
1044
|
+
* shows/hides the Flags control and re-validates) and `input` on the Value
|
|
1045
|
+
* input (so `syncLiteralRegexValidity` re-runs), the same events a real user
|
|
1046
|
+
* interacting with these controls would fire.
|
|
1047
|
+
* @param {Element} el
|
|
1048
|
+
* @param {import('./queryTree.js').QueryLiteralSetLeaf|
|
|
1049
|
+
* import('./queryTree.js').QueryRegexLeaf|
|
|
1050
|
+
* import('./queryTree.js').QueryNotContainsLeaf|undefined} leaf
|
|
1051
|
+
* @param {string} [key]
|
|
1052
|
+
* @returns {void}
|
|
1053
|
+
*/
|
|
1054
|
+
export function applyLiteralRegexQuery (el, leaf, key = '') {
|
|
1055
|
+
const modeEl = /** @type {HTMLSelectElement|undefined} */ (
|
|
1056
|
+
findOwnControl(el, `select.jsoeSearchMode--${key}`)
|
|
1057
|
+
);
|
|
1058
|
+
const valueEl = /** @type {HTMLInputElement|undefined} */ (
|
|
1059
|
+
findOwnControl(el, `input.jsoeSearchValue--${key}`)
|
|
1060
|
+
);
|
|
1061
|
+
if (!modeEl || !valueEl) {
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
const flagsEl = /** @type {HTMLSelectElement|undefined} */ (
|
|
1065
|
+
findOwnControl(el, `select.jsoeSearchRegexFlags--${key}`)
|
|
1066
|
+
);
|
|
1067
|
+
let mode = 'literal';
|
|
1068
|
+
let value = '';
|
|
1069
|
+
let flags = '';
|
|
1070
|
+
switch (leaf?.kind) {
|
|
1071
|
+
case 'regex': {
|
|
1072
|
+
mode = 'regex';
|
|
1073
|
+
({$regex: value} = leaf);
|
|
1074
|
+
flags = leaf.$options ?? '';
|
|
1075
|
+
|
|
1076
|
+
break;
|
|
1077
|
+
}
|
|
1078
|
+
case 'notContains': {
|
|
1079
|
+
mode = 'notContains';
|
|
1080
|
+
({value} = leaf);
|
|
1081
|
+
|
|
1082
|
+
break;
|
|
1083
|
+
}
|
|
1084
|
+
case 'literalSet': {
|
|
1085
|
+
value = (leaf.$in ?? []).map(String).join(', ');
|
|
1086
|
+
|
|
1087
|
+
break;
|
|
1088
|
+
}
|
|
1089
|
+
// No default
|
|
1090
|
+
}
|
|
1091
|
+
modeEl.value = mode;
|
|
1092
|
+
valueEl.value = value;
|
|
1093
|
+
if (flagsEl) {
|
|
1094
|
+
const flagChars = new Set(flags.split(''));
|
|
1095
|
+
[...flagsEl.options].forEach((opt) => {
|
|
1096
|
+
opt.selected = flagChars.has(opt.value);
|
|
1097
|
+
});
|
|
1098
|
+
}
|
|
1099
|
+
modeEl.dispatchEvent(new Event('change'));
|
|
1100
|
+
valueEl.dispatchEvent(new Event('input'));
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
/**
|
|
1104
|
+
* The inverse of `readRangeInputsPair` - sets the From/To inputs back from a
|
|
1105
|
+
* previously-read `range` leaf (or clears both for `undefined`). An
|
|
1106
|
+
* exclusive `$gt`/`$lt` bound (never produced by any `getQuery` in this
|
|
1107
|
+
* codebase, but tolerated on a hand-edited raw query) is treated the same as
|
|
1108
|
+
* its inclusive `$gte`/`$lte` counterpart, since the UI has only one plain
|
|
1109
|
+
* bound per side, not a separate inclusive/exclusive toggle.
|
|
1110
|
+
* @param {Element} el
|
|
1111
|
+
* @param {import('./queryTree.js').QueryRangeLeaf|undefined} leaf
|
|
1112
|
+
* @param {string} [key]
|
|
1113
|
+
* @returns {void}
|
|
1114
|
+
*/
|
|
1115
|
+
export function applyRangeQuery (el, leaf, key = '') {
|
|
1116
|
+
const gteEl = /** @type {HTMLInputElement|undefined} */ (
|
|
1117
|
+
findOwnControl(el, `input.jsoeSearchRangeGte--${key}`)
|
|
1118
|
+
);
|
|
1119
|
+
const lteEl = /** @type {HTMLInputElement|undefined} */ (
|
|
1120
|
+
findOwnControl(el, `input.jsoeSearchRangeLte--${key}`)
|
|
1121
|
+
);
|
|
1122
|
+
if (!gteEl || !lteEl) {
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
const gte = leaf?.$gte ?? leaf?.$gt;
|
|
1126
|
+
const lte = leaf?.$lte ?? leaf?.$lt;
|
|
1127
|
+
gteEl.value = gte === undefined ? '' : String(gte);
|
|
1128
|
+
lteEl.value = lte === undefined ? '' : String(lte);
|
|
1129
|
+
gteEl.dispatchEvent(new Event('input'));
|
|
1130
|
+
lteEl.dispatchEvent(new Event('input'));
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
/**
|
|
1134
|
+
* The inverse of `readTriStateSelect`/`buildHasPropertyToggle` - `undefined`
|
|
1135
|
+
* maps back to "(any)".
|
|
1136
|
+
* @param {Element} el
|
|
1137
|
+
* @param {boolean|undefined} value
|
|
1138
|
+
* @param {string} [key]
|
|
1139
|
+
* @returns {void}
|
|
1140
|
+
*/
|
|
1141
|
+
export function applyTriState (el, value, key = '') {
|
|
1142
|
+
const select = /** @type {HTMLSelectElement|undefined} */ (
|
|
1143
|
+
findOwnControl(el, `select.jsoeSearchTriState--${key}`)
|
|
1144
|
+
);
|
|
1145
|
+
if (!select) {
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1148
|
+
select.value = value === undefined ? '' : String(value);
|
|
1149
|
+
select.dispatchEvent(new Event('change'));
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
/**
|
|
1153
|
+
* The inverse of `readCheckbox`.
|
|
1154
|
+
* @param {Element} el
|
|
1155
|
+
* @param {boolean} checked
|
|
1156
|
+
* @returns {void}
|
|
1157
|
+
*/
|
|
1158
|
+
export function applyCheckbox (el, checked) {
|
|
1159
|
+
const checkbox = /** @type {HTMLInputElement|undefined} */ (
|
|
1160
|
+
findOwnControl(el, 'input.jsoeSearchCheckbox')
|
|
1161
|
+
);
|
|
1162
|
+
if (!checkbox) {
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1165
|
+
checkbox.checked = checked;
|
|
1166
|
+
checkbox.dispatchEvent(new Event('change'));
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
/**
|
|
1170
|
+
* The inverse of `readMultiSelect` - `values` is compared against each
|
|
1171
|
+
* `<option>`'s own `value` as a string (`String(item)`), matching how
|
|
1172
|
+
* `enumSearchType.js`/`SpecialRealNumberSearchType.js` build their own
|
|
1173
|
+
* options from stringified values.
|
|
1174
|
+
* @param {Element} el
|
|
1175
|
+
* @param {unknown[]} values
|
|
1176
|
+
* @returns {void}
|
|
1177
|
+
*/
|
|
1178
|
+
export function applyMultiSelect (el, values) {
|
|
1179
|
+
const select = /** @type {HTMLSelectElement|undefined} */ (
|
|
1180
|
+
findOwnControl(el, 'select.jsoeSearchMultiSelect')
|
|
1181
|
+
);
|
|
1182
|
+
if (!select) {
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
const strValues = new Set(values.map(String));
|
|
1186
|
+
[...select.options].forEach((opt) => {
|
|
1187
|
+
opt.selected = strValues.has(opt.value);
|
|
1188
|
+
});
|
|
1189
|
+
select.dispatchEvent(new Event('change'));
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
/**
|
|
1193
|
+
* The inverse of `readLengthSizeQuery` - clears both the size input and the
|
|
1194
|
+
* sparse tri-state for `undefined`.
|
|
1195
|
+
* @param {Element} el
|
|
1196
|
+
* @param {import('./queryTree.js').QueryLengthSizeLeaf|undefined} leaf
|
|
1197
|
+
* @returns {void}
|
|
1198
|
+
*/
|
|
1199
|
+
export function applyLengthSizeQuery (el, leaf) {
|
|
1200
|
+
const sizeEl = /** @type {HTMLInputElement|undefined} */ (
|
|
1201
|
+
findOwnControl(el, 'input.jsoeSearchSize')
|
|
1202
|
+
);
|
|
1203
|
+
if (sizeEl) {
|
|
1204
|
+
sizeEl.value = leaf?.$size === undefined ? '' : String(leaf.$size);
|
|
1205
|
+
sizeEl.dispatchEvent(new Event('input'));
|
|
1206
|
+
}
|
|
1207
|
+
applyTriState(el, leaf?.sparseCheck);
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
/**
|
|
1211
|
+
* The inverse of `readOptInChecked` - also dispatches `change` so
|
|
1212
|
+
* `wireOptInFieldset`'s own listener toggles the paired fieldset's
|
|
1213
|
+
* `disabled` state and runs whatever `onToggle` it was wired with (e.g. a
|
|
1214
|
+
* widget's own `syncAtLeastOneCheck` re-run).
|
|
1215
|
+
* @param {Element} el
|
|
1216
|
+
* @param {boolean} checked
|
|
1217
|
+
* @param {string} [key]
|
|
1218
|
+
* @returns {void}
|
|
1219
|
+
*/
|
|
1220
|
+
export function applyOptIn (el, checked, key = '') {
|
|
1221
|
+
const checkbox = /** @type {HTMLInputElement|undefined} */ (
|
|
1222
|
+
findOwnControl(el, `input.jsoeSearchOptIn--${key}`)
|
|
1223
|
+
);
|
|
1224
|
+
if (!checkbox) {
|
|
1225
|
+
return;
|
|
1226
|
+
}
|
|
1227
|
+
checkbox.checked = checked;
|
|
1228
|
+
checkbox.dispatchEvent(new Event('change'));
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
/**
|
|
1232
|
+
* A `buildOptInFieldset`-wrapped `buildLiteralRegexControls` facet
|
|
1233
|
+
* (`fileSearchType.js`'s name/content-type, `domexceptionSearchType.js`'s
|
|
1234
|
+
* message, `makeErrorFamilySearchType`'s string properties): opts the facet
|
|
1235
|
+
* in exactly when `queryNode` has a clause touching `path`, and applies that
|
|
1236
|
+
* clause (a bare literal/regex/does-not-contain leaf, since none of these
|
|
1237
|
+
* facets recurse any deeper) to its Mode/Value/Flags controls.
|
|
1238
|
+
* @param {Element} root
|
|
1239
|
+
* @param {QueryNode|undefined} queryNode
|
|
1240
|
+
* @param {string} path
|
|
1241
|
+
* @param {string} [key]
|
|
1242
|
+
* @returns {void}
|
|
1243
|
+
*/
|
|
1244
|
+
export function applyOptInLiteralRegexFacet (root, queryNode, path, key = '') {
|
|
1245
|
+
const {matched} = extractClauseForPath(queryNode, path);
|
|
1246
|
+
applyOptIn(root, matched !== undefined, key);
|
|
1247
|
+
applyLiteralRegexQuery(
|
|
1248
|
+
root,
|
|
1249
|
+
/**
|
|
1250
|
+
* @type {import('./queryTree.js').QueryLiteralSetLeaf|
|
|
1251
|
+
*import('./queryTree.js').QueryRegexLeaf|
|
|
1252
|
+
import('./queryTree.js').QueryNotContainsLeaf|undefined} */ (matched),
|
|
1253
|
+
key
|
|
1254
|
+
);
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
/**
|
|
1258
|
+
* A `buildOptInFieldset`-wrapped `buildRangeInputsPair` facet
|
|
1259
|
+
* (`makeErrorFamilySearchType`'s number properties,
|
|
1260
|
+
* `makeDomShapeSearchType`'s dimensions): opts the facet in exactly when
|
|
1261
|
+
* `queryNode` has a clause touching `path`, and applies that clause (a bare
|
|
1262
|
+
* `range` leaf) to its From/To controls.
|
|
1263
|
+
* @param {Element} root
|
|
1264
|
+
* @param {QueryNode|undefined} queryNode
|
|
1265
|
+
* @param {string} path
|
|
1266
|
+
* @param {string} [key]
|
|
1267
|
+
* @returns {void}
|
|
1268
|
+
*/
|
|
1269
|
+
export function applyOptInRangeFacet (root, queryNode, path, key = '') {
|
|
1270
|
+
const {matched} = extractClauseForPath(queryNode, path);
|
|
1271
|
+
applyOptIn(root, matched !== undefined, key);
|
|
1272
|
+
applyRangeQuery(
|
|
1273
|
+
root, /** @type {import('./queryTree.js').QueryRangeLeaf|undefined} */ (matched), key
|
|
1274
|
+
);
|
|
1275
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
-
buildPathLabel, findOwnControl, isExemptedByAncestorHasProperty
|
|
2
|
+
buildPathLabel, findOwnControl, isExemptedByAncestorHasProperty, extractLeafOfKind
|
|
3
3
|
} from '../searchUtils.js';
|
|
4
4
|
import {makeBlobHTMLLeaf} from '../queryTreeBuilders.js';
|
|
5
|
-
import {getQueryViaElement} from '../searchElementUtils.js';
|
|
5
|
+
import {getQueryViaElement, applyQueryViaElement} from '../searchElementUtils.js';
|
|
6
6
|
import regexpType from '../../fundamentalTypes/regexpType.js';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -173,6 +173,45 @@ const blobHTMLSearchType = {
|
|
|
173
173
|
value,
|
|
174
174
|
flags || undefined
|
|
175
175
|
);
|
|
176
|
+
},
|
|
177
|
+
/**
|
|
178
|
+
* @this {HTMLElement}
|
|
179
|
+
* @param {import('../queryTree.js').QueryNode|undefined} queryNode
|
|
180
|
+
* @returns {void}
|
|
181
|
+
*/
|
|
182
|
+
applyQuery (queryNode) {
|
|
183
|
+
const {matched} = extractLeafOfKind(queryNode, 'blobHTML');
|
|
184
|
+
const modeEl = /** @type {HTMLSelectElement|undefined} */ (
|
|
185
|
+
findOwnControl(this, 'select.jsoeSearchBlobHTMLMode')
|
|
186
|
+
);
|
|
187
|
+
if (!modeEl) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const mode = matched?.mode ?? 'cssSelector';
|
|
191
|
+
modeEl.value = mode;
|
|
192
|
+
// Dispatching `change` runs the Mode `<select>`'s own handler
|
|
193
|
+
// first, which shows/hides the correct Value control and the
|
|
194
|
+
// Flags label before either is touched below.
|
|
195
|
+
modeEl.dispatchEvent(new Event('change'));
|
|
196
|
+
const activeEl = /** @type {HTMLInputElement|HTMLTextAreaElement|undefined} */ (
|
|
197
|
+
findOwnControl(
|
|
198
|
+
this, `${mode === 'fullText' ? 'textarea' : 'input'}.jsoeSearchBlobHTMLValue`
|
|
199
|
+
)
|
|
200
|
+
);
|
|
201
|
+
if (activeEl) {
|
|
202
|
+
activeEl.value = matched?.value ?? '';
|
|
203
|
+
activeEl.dispatchEvent(new Event('input'));
|
|
204
|
+
}
|
|
205
|
+
const flagsEl = /** @type {HTMLSelectElement|undefined} */ (
|
|
206
|
+
findOwnControl(this, 'select.jsoeSearchBlobHTMLFlags')
|
|
207
|
+
);
|
|
208
|
+
if (flagsEl) {
|
|
209
|
+
const flagChars = new Set((matched?.$options ?? '').split(''));
|
|
210
|
+
[...flagsEl.options].forEach((opt) => {
|
|
211
|
+
opt.selected = flagChars.has(opt.value);
|
|
212
|
+
});
|
|
213
|
+
flagsEl.dispatchEvent(new Event('change'));
|
|
214
|
+
}
|
|
176
215
|
}
|
|
177
216
|
}
|
|
178
217
|
}, [
|
|
@@ -252,7 +291,8 @@ const blobHTMLSearchType = {
|
|
|
252
291
|
]]
|
|
253
292
|
]];
|
|
254
293
|
},
|
|
255
|
-
getQuery: getQueryViaElement
|
|
294
|
+
getQuery: getQueryViaElement,
|
|
295
|
+
applyQuery: applyQueryViaElement
|
|
256
296
|
};
|
|
257
297
|
|
|
258
298
|
export default blobHTMLSearchType;
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
-
buildPathLabel, buildCheckbox, readCheckbox, buildOptInFieldset,
|
|
3
|
-
readOptInChecked, wireOptInFieldset, buildAtLeastOneSentinel,
|
|
2
|
+
buildPathLabel, buildCheckbox, readCheckbox, applyCheckbox, buildOptInFieldset,
|
|
3
|
+
readOptInChecked, wireOptInFieldset, applyOptIn, buildAtLeastOneSentinel,
|
|
4
|
+
syncAtLeastOneCheck, extractLeafOfKind
|
|
4
5
|
} from '../searchUtils.js';
|
|
5
6
|
import {makeMapRecordJointLeaf} from '../queryTreeBuilders.js';
|
|
6
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
findSearchElement, getQueryViaElement, hasGetQuery,
|
|
9
|
+
applyQueryViaElement, hasApplyQuery
|
|
10
|
+
} from '../searchElementUtils.js';
|
|
7
11
|
import {buildSearchWidget} from '../searchDispatch.js';
|
|
8
12
|
|
|
9
13
|
/**
|
|
@@ -85,6 +89,27 @@ const recordSearchType = {
|
|
|
85
89
|
}
|
|
86
90
|
const joint = readCheckbox(this);
|
|
87
91
|
return makeMapRecordJointLeaf(searchPath, joint, keyQuery, valueQuery);
|
|
92
|
+
},
|
|
93
|
+
/**
|
|
94
|
+
* @this {HTMLElement}
|
|
95
|
+
* @param {import('../queryTree.js').QueryNode|undefined} queryNode
|
|
96
|
+
* @returns {void}
|
|
97
|
+
*/
|
|
98
|
+
applyQuery (queryNode) {
|
|
99
|
+
const searchPath = this.dataset.searchPath ?? '';
|
|
100
|
+
const {matched: jointLeaf} = extractLeafOfKind(queryNode, 'mapRecordJoint');
|
|
101
|
+
applyCheckbox(this, Boolean(jointLeaf?.joint));
|
|
102
|
+
applyOptIn(this, jointLeaf?.keyQuery !== undefined, 'key');
|
|
103
|
+
applyOptIn(this, jointLeaf?.valueQuery !== undefined, 'value');
|
|
104
|
+
const keyEl = findSearchElement(this, `${searchPath}/*key`);
|
|
105
|
+
if (keyEl && hasApplyQuery(keyEl)) {
|
|
106
|
+
keyEl.applyQuery(jointLeaf?.keyQuery);
|
|
107
|
+
}
|
|
108
|
+
const valueEl = findSearchElement(this, `${searchPath}/*value`);
|
|
109
|
+
if (valueEl && hasApplyQuery(valueEl)) {
|
|
110
|
+
valueEl.applyQuery(jointLeaf?.valueQuery);
|
|
111
|
+
}
|
|
112
|
+
syncKeyValueValidity(this);
|
|
88
113
|
}
|
|
89
114
|
}
|
|
90
115
|
}, [
|
|
@@ -99,7 +124,8 @@ const recordSearchType = {
|
|
|
99
124
|
buildAtLeastOneSentinel()
|
|
100
125
|
]];
|
|
101
126
|
},
|
|
102
|
-
getQuery: getQueryViaElement
|
|
127
|
+
getQuery: getQueryViaElement,
|
|
128
|
+
applyQuery: applyQueryViaElement
|
|
103
129
|
};
|
|
104
130
|
|
|
105
131
|
export default recordSearchType;
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
-
buildPathLabel, buildLengthSizeControls, readLengthSizeQuery,
|
|
3
|
-
buildOptInFieldset, readOptInChecked, wireOptInFieldset,
|
|
4
|
-
buildAtLeastOneSentinel, syncAtLeastOneCheck
|
|
2
|
+
buildPathLabel, buildLengthSizeControls, readLengthSizeQuery, applyLengthSizeQuery,
|
|
3
|
+
buildOptInFieldset, readOptInChecked, wireOptInFieldset, applyOptIn,
|
|
4
|
+
buildAtLeastOneSentinel, syncAtLeastOneCheck, extractLeafOfKind, extractClauseForPath
|
|
5
5
|
} from '../searchUtils.js';
|
|
6
6
|
import {combineAnd} from '../queryTreeBuilders.js';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
findSearchElement, getQueryViaElement, hasGetQuery,
|
|
9
|
+
applyQueryViaElement, hasApplyQuery
|
|
10
|
+
} from '../searchElementUtils.js';
|
|
8
11
|
import {buildSearchWidget} from '../searchDispatch.js';
|
|
9
12
|
|
|
10
13
|
/**
|
|
@@ -142,11 +145,46 @@ const tupleSearchType = {
|
|
|
142
145
|
? restEl.getQuery()
|
|
143
146
|
: undefined;
|
|
144
147
|
return combineAnd([...itemLeaves, lengthLeaf, restLeaf]);
|
|
148
|
+
},
|
|
149
|
+
/**
|
|
150
|
+
* @this {HTMLElement}
|
|
151
|
+
* @param {import('../queryTree.js').QueryNode|undefined} queryNode
|
|
152
|
+
* @returns {void}
|
|
153
|
+
*/
|
|
154
|
+
applyQuery (queryNode) {
|
|
155
|
+
const searchPath = this.dataset.searchPath ?? '';
|
|
156
|
+
const itemCount = Number(this.dataset.itemCount ?? '0');
|
|
157
|
+
const hasRest = this.dataset.hasRest === 'true';
|
|
158
|
+
let remaining = queryNode;
|
|
159
|
+
Array.from({length: itemCount}, (_v, idx) => idx).forEach((idx) => {
|
|
160
|
+
const childPath = `${searchPath}/${idx}`;
|
|
161
|
+
const {matched, rest} = extractClauseForPath(remaining, childPath);
|
|
162
|
+
remaining = rest;
|
|
163
|
+
applyOptIn(this, matched !== undefined, String(idx));
|
|
164
|
+
const itemEl = findSearchElement(this, childPath);
|
|
165
|
+
if (itemEl && hasApplyQuery(itemEl)) {
|
|
166
|
+
itemEl.applyQuery(matched);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
if (hasRest) {
|
|
170
|
+
const {matched: lengthLeaf, rest: afterLength} = extractLeafOfKind(remaining, 'lengthSize');
|
|
171
|
+
applyLengthSizeQuery(this, lengthLeaf);
|
|
172
|
+
remaining = afterLength;
|
|
173
|
+
const restPath = `${searchPath}/*`;
|
|
174
|
+
const {matched: restMatched} = extractClauseForPath(remaining, restPath);
|
|
175
|
+
applyOptIn(this, restMatched !== undefined, 'rest');
|
|
176
|
+
const restEl = findSearchElement(this, restPath);
|
|
177
|
+
if (restEl && hasApplyQuery(restEl)) {
|
|
178
|
+
restEl.applyQuery(restMatched);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
syncTupleValidity(this);
|
|
145
182
|
}
|
|
146
183
|
}
|
|
147
184
|
}, children];
|
|
148
185
|
},
|
|
149
|
-
getQuery: getQueryViaElement
|
|
186
|
+
getQuery: getQueryViaElement,
|
|
187
|
+
applyQuery: applyQueryViaElement
|
|
150
188
|
};
|
|
151
189
|
|
|
152
190
|
export default tupleSearchType;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
buildPathLabel, buildMultiSelect, readMultiSelect, applyMultiSelect, extractLeafOfKind
|
|
3
|
+
} from '../searchUtils.js';
|
|
2
4
|
import {makeMultiSelectLeaf} from '../queryTreeBuilders.js';
|
|
3
|
-
import {getQueryViaElement} from '../searchElementUtils.js';
|
|
5
|
+
import {getQueryViaElement, applyQueryViaElement} from '../searchElementUtils.js';
|
|
4
6
|
|
|
5
7
|
/**
|
|
6
8
|
* @typedef {import('../searchDispatch.js').SearchTypeObject} SearchTypeObject
|
|
@@ -27,6 +29,15 @@ const SpecialRealNumberSearchType = {
|
|
|
27
29
|
return selected.length
|
|
28
30
|
? makeMultiSelectLeaf(this.dataset.searchPath ?? '', {$in: selected})
|
|
29
31
|
: undefined;
|
|
32
|
+
},
|
|
33
|
+
/**
|
|
34
|
+
* @this {HTMLElement}
|
|
35
|
+
* @param {import('../queryTree.js').QueryNode|undefined} queryNode
|
|
36
|
+
* @returns {void}
|
|
37
|
+
*/
|
|
38
|
+
applyQuery (queryNode) {
|
|
39
|
+
const {matched} = extractLeafOfKind(queryNode, 'multiSelect');
|
|
40
|
+
applyMultiSelect(this, matched?.$in ?? []);
|
|
30
41
|
}
|
|
31
42
|
}
|
|
32
43
|
}, [
|
|
@@ -36,7 +47,8 @@ const SpecialRealNumberSearchType = {
|
|
|
36
47
|
})
|
|
37
48
|
]];
|
|
38
49
|
},
|
|
39
|
-
getQuery: getQueryViaElement
|
|
50
|
+
getQuery: getQueryViaElement,
|
|
51
|
+
applyQuery: applyQueryViaElement
|
|
40
52
|
};
|
|
41
53
|
|
|
42
54
|
export default SpecialRealNumberSearchType;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
-
buildPathLabel, buildRangeInputsPair, readRangeInputsPair, syncRangeValidity
|
|
2
|
+
buildPathLabel, buildRangeInputsPair, readRangeInputsPair, syncRangeValidity,
|
|
3
|
+
applyRangeQuery, extractLeafOfKind
|
|
3
4
|
} from '../searchUtils.js';
|
|
4
5
|
import {makeRangeLeaf} from '../queryTreeBuilders.js';
|
|
5
|
-
import {getQueryViaElement} from '../searchElementUtils.js';
|
|
6
|
+
import {getQueryViaElement, applyQueryViaElement} from '../searchElementUtils.js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* @typedef {import('../searchDispatch.js').SearchTypeObject} SearchTypeObject
|
|
@@ -40,6 +41,15 @@ const buffersourceSearchType = {
|
|
|
40
41
|
...(gte === '' ? {} : {$gte: Number(gte)}),
|
|
41
42
|
...(lte === '' ? {} : {$lte: Number(lte)})
|
|
42
43
|
});
|
|
44
|
+
},
|
|
45
|
+
/**
|
|
46
|
+
* @this {HTMLElement}
|
|
47
|
+
* @param {import('../queryTree.js').QueryNode|undefined} queryNode
|
|
48
|
+
* @returns {void}
|
|
49
|
+
*/
|
|
50
|
+
applyQuery (queryNode) {
|
|
51
|
+
const {matched} = extractLeafOfKind(queryNode, 'range');
|
|
52
|
+
applyRangeQuery(this, matched);
|
|
43
53
|
}
|
|
44
54
|
}
|
|
45
55
|
}, [
|
|
@@ -47,7 +57,8 @@ const buffersourceSearchType = {
|
|
|
47
57
|
...buildRangeInputsPair({name, min: 0})
|
|
48
58
|
]];
|
|
49
59
|
},
|
|
50
|
-
getQuery: getQueryViaElement
|
|
60
|
+
getQuery: getQueryViaElement,
|
|
61
|
+
applyQuery: applyQueryViaElement
|
|
51
62
|
};
|
|
52
63
|
|
|
53
64
|
export default buffersourceSearchType;
|