feat(ivy): add generics to the SelectorMatcher API (#26203)
This commit adds a generic type parameter to the SelectorMatcher class and its associated response types. This makes the API for matching selectors and obtaining information about the matched directives significantly more ergonomic and type-safe. PR Close #26203
This commit is contained in:
parent
9cb17ecc39
commit
a2da485d90
@ -150,29 +150,29 @@ export class CssSelector {
|
|||||||
* Reads a list of CssSelectors and allows to calculate which ones
|
* Reads a list of CssSelectors and allows to calculate which ones
|
||||||
* are contained in a given CssSelector.
|
* are contained in a given CssSelector.
|
||||||
*/
|
*/
|
||||||
export class SelectorMatcher {
|
export class SelectorMatcher<T = any> {
|
||||||
static createNotMatcher(notSelectors: CssSelector[]): SelectorMatcher {
|
static createNotMatcher(notSelectors: CssSelector[]): SelectorMatcher<null> {
|
||||||
const notMatcher = new SelectorMatcher();
|
const notMatcher = new SelectorMatcher<null>();
|
||||||
notMatcher.addSelectables(notSelectors, null);
|
notMatcher.addSelectables(notSelectors, null);
|
||||||
return notMatcher;
|
return notMatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _elementMap = new Map<string, SelectorContext[]>();
|
private _elementMap = new Map<string, SelectorContext<T>[]>();
|
||||||
private _elementPartialMap = new Map<string, SelectorMatcher>();
|
private _elementPartialMap = new Map<string, SelectorMatcher<T>>();
|
||||||
private _classMap = new Map<string, SelectorContext[]>();
|
private _classMap = new Map<string, SelectorContext<T>[]>();
|
||||||
private _classPartialMap = new Map<string, SelectorMatcher>();
|
private _classPartialMap = new Map<string, SelectorMatcher<T>>();
|
||||||
private _attrValueMap = new Map<string, Map<string, SelectorContext[]>>();
|
private _attrValueMap = new Map<string, Map<string, SelectorContext<T>[]>>();
|
||||||
private _attrValuePartialMap = new Map<string, Map<string, SelectorMatcher>>();
|
private _attrValuePartialMap = new Map<string, Map<string, SelectorMatcher<T>>>();
|
||||||
private _listContexts: SelectorListContext[] = [];
|
private _listContexts: SelectorListContext[] = [];
|
||||||
|
|
||||||
addSelectables(cssSelectors: CssSelector[], callbackCtxt?: any) {
|
addSelectables(cssSelectors: CssSelector[], callbackCtxt?: T) {
|
||||||
let listContext: SelectorListContext = null !;
|
let listContext: SelectorListContext = null !;
|
||||||
if (cssSelectors.length > 1) {
|
if (cssSelectors.length > 1) {
|
||||||
listContext = new SelectorListContext(cssSelectors);
|
listContext = new SelectorListContext(cssSelectors);
|
||||||
this._listContexts.push(listContext);
|
this._listContexts.push(listContext);
|
||||||
}
|
}
|
||||||
for (let i = 0; i < cssSelectors.length; i++) {
|
for (let i = 0; i < cssSelectors.length; i++) {
|
||||||
this._addSelectable(cssSelectors[i], callbackCtxt, listContext);
|
this._addSelectable(cssSelectors[i], callbackCtxt as T, listContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,8 +182,8 @@ export class SelectorMatcher {
|
|||||||
* @param callbackCtxt An opaque object that will be given to the callback of the `match` function
|
* @param callbackCtxt An opaque object that will be given to the callback of the `match` function
|
||||||
*/
|
*/
|
||||||
private _addSelectable(
|
private _addSelectable(
|
||||||
cssSelector: CssSelector, callbackCtxt: any, listContext: SelectorListContext) {
|
cssSelector: CssSelector, callbackCtxt: T, listContext: SelectorListContext) {
|
||||||
let matcher: SelectorMatcher = this;
|
let matcher: SelectorMatcher<T> = this;
|
||||||
const element = cssSelector.element;
|
const element = cssSelector.element;
|
||||||
const classNames = cssSelector.classNames;
|
const classNames = cssSelector.classNames;
|
||||||
const attrs = cssSelector.attrs;
|
const attrs = cssSelector.attrs;
|
||||||
@ -219,7 +219,7 @@ export class SelectorMatcher {
|
|||||||
const terminalMap = matcher._attrValueMap;
|
const terminalMap = matcher._attrValueMap;
|
||||||
let terminalValuesMap = terminalMap.get(name);
|
let terminalValuesMap = terminalMap.get(name);
|
||||||
if (!terminalValuesMap) {
|
if (!terminalValuesMap) {
|
||||||
terminalValuesMap = new Map<string, SelectorContext[]>();
|
terminalValuesMap = new Map<string, SelectorContext<T>[]>();
|
||||||
terminalMap.set(name, terminalValuesMap);
|
terminalMap.set(name, terminalValuesMap);
|
||||||
}
|
}
|
||||||
this._addTerminal(terminalValuesMap, value, selectable);
|
this._addTerminal(terminalValuesMap, value, selectable);
|
||||||
@ -227,7 +227,7 @@ export class SelectorMatcher {
|
|||||||
const partialMap = matcher._attrValuePartialMap;
|
const partialMap = matcher._attrValuePartialMap;
|
||||||
let partialValuesMap = partialMap.get(name);
|
let partialValuesMap = partialMap.get(name);
|
||||||
if (!partialValuesMap) {
|
if (!partialValuesMap) {
|
||||||
partialValuesMap = new Map<string, SelectorMatcher>();
|
partialValuesMap = new Map<string, SelectorMatcher<T>>();
|
||||||
partialMap.set(name, partialValuesMap);
|
partialMap.set(name, partialValuesMap);
|
||||||
}
|
}
|
||||||
matcher = this._addPartial(partialValuesMap, value);
|
matcher = this._addPartial(partialValuesMap, value);
|
||||||
@ -237,7 +237,7 @@ export class SelectorMatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _addTerminal(
|
private _addTerminal(
|
||||||
map: Map<string, SelectorContext[]>, name: string, selectable: SelectorContext) {
|
map: Map<string, SelectorContext<T>[]>, name: string, selectable: SelectorContext<T>) {
|
||||||
let terminalList = map.get(name);
|
let terminalList = map.get(name);
|
||||||
if (!terminalList) {
|
if (!terminalList) {
|
||||||
terminalList = [];
|
terminalList = [];
|
||||||
@ -246,10 +246,10 @@ export class SelectorMatcher {
|
|||||||
terminalList.push(selectable);
|
terminalList.push(selectable);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _addPartial(map: Map<string, SelectorMatcher>, name: string): SelectorMatcher {
|
private _addPartial(map: Map<string, SelectorMatcher<T>>, name: string): SelectorMatcher<T> {
|
||||||
let matcher = map.get(name);
|
let matcher = map.get(name);
|
||||||
if (!matcher) {
|
if (!matcher) {
|
||||||
matcher = new SelectorMatcher();
|
matcher = new SelectorMatcher<T>();
|
||||||
map.set(name, matcher);
|
map.set(name, matcher);
|
||||||
}
|
}
|
||||||
return matcher;
|
return matcher;
|
||||||
@ -262,8 +262,7 @@ export class SelectorMatcher {
|
|||||||
* @param matchedCallback This callback will be called with the object handed into `addSelectable`
|
* @param matchedCallback This callback will be called with the object handed into `addSelectable`
|
||||||
* @return boolean true if a match was found
|
* @return boolean true if a match was found
|
||||||
*/
|
*/
|
||||||
match(cssSelector: CssSelector, matchedCallback: ((c: CssSelector, a: any) => void)|null):
|
match(cssSelector: CssSelector, matchedCallback: ((c: CssSelector, a: T) => void)|null): boolean {
|
||||||
boolean {
|
|
||||||
let result = false;
|
let result = false;
|
||||||
const element = cssSelector.element !;
|
const element = cssSelector.element !;
|
||||||
const classNames = cssSelector.classNames;
|
const classNames = cssSelector.classNames;
|
||||||
@ -314,21 +313,21 @@ export class SelectorMatcher {
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_matchTerminal(
|
_matchTerminal(
|
||||||
map: Map<string, SelectorContext[]>, name: string, cssSelector: CssSelector,
|
map: Map<string, SelectorContext<T>[]>, name: string, cssSelector: CssSelector,
|
||||||
matchedCallback: ((c: CssSelector, a: any) => void)|null): boolean {
|
matchedCallback: ((c: CssSelector, a: any) => void)|null): boolean {
|
||||||
if (!map || typeof name !== 'string') {
|
if (!map || typeof name !== 'string') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let selectables: SelectorContext[] = map.get(name) || [];
|
let selectables: SelectorContext<T>[] = map.get(name) || [];
|
||||||
const starSelectables: SelectorContext[] = map.get('*') !;
|
const starSelectables: SelectorContext<T>[] = map.get('*') !;
|
||||||
if (starSelectables) {
|
if (starSelectables) {
|
||||||
selectables = selectables.concat(starSelectables);
|
selectables = selectables.concat(starSelectables);
|
||||||
}
|
}
|
||||||
if (selectables.length === 0) {
|
if (selectables.length === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let selectable: SelectorContext;
|
let selectable: SelectorContext<T>;
|
||||||
let result = false;
|
let result = false;
|
||||||
for (let i = 0; i < selectables.length; i++) {
|
for (let i = 0; i < selectables.length; i++) {
|
||||||
selectable = selectables[i];
|
selectable = selectables[i];
|
||||||
@ -339,7 +338,7 @@ export class SelectorMatcher {
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_matchPartial(
|
_matchPartial(
|
||||||
map: Map<string, SelectorMatcher>, name: string, cssSelector: CssSelector,
|
map: Map<string, SelectorMatcher<T>>, name: string, cssSelector: CssSelector,
|
||||||
matchedCallback: ((c: CssSelector, a: any) => void)|null): boolean {
|
matchedCallback: ((c: CssSelector, a: any) => void)|null): boolean {
|
||||||
if (!map || typeof name !== 'string') {
|
if (!map || typeof name !== 'string') {
|
||||||
return false;
|
return false;
|
||||||
@ -364,16 +363,15 @@ export class SelectorListContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Store context to pass back selector and context when a selector is matched
|
// Store context to pass back selector and context when a selector is matched
|
||||||
export class SelectorContext {
|
export class SelectorContext<T = any> {
|
||||||
notSelectors: CssSelector[];
|
notSelectors: CssSelector[];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public selector: CssSelector, public cbContext: any,
|
public selector: CssSelector, public cbContext: T, public listContext: SelectorListContext) {
|
||||||
public listContext: SelectorListContext) {
|
|
||||||
this.notSelectors = selector.notSelectors;
|
this.notSelectors = selector.notSelectors;
|
||||||
}
|
}
|
||||||
|
|
||||||
finalize(cssSelector: CssSelector, callback: ((c: CssSelector, a: any) => void)|null): boolean {
|
finalize(cssSelector: CssSelector, callback: ((c: CssSelector, a: T) => void)|null): boolean {
|
||||||
let result = true;
|
let result = true;
|
||||||
if (this.notSelectors.length > 0 && (!this.listContext || !this.listContext.alreadyMatched)) {
|
if (this.notSelectors.length > 0 && (!this.listContext || !this.listContext.alreadyMatched)) {
|
||||||
const notMatcher = SelectorMatcher.createNotMatcher(this.notSelectors);
|
const notMatcher = SelectorMatcher.createNotMatcher(this.notSelectors);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user