be-enhanced
Version:
be-enhanced provides a base class that enables casting spells, or enhancing server-rendered DOM elements based on cross-cutting custom attributes
490 lines (371 loc) • 11.4 kB
TypeScript
import {RHS, ToTransformer} from '../types';
export type matchTypes = 'parts'| 'part' | 'id' | 'classes' | 'class' | 'attribs' | 'attrib' | 'elements' | 'element' | 'names' | 'name' | 'props' | 'placeholders';
export interface QueryInfo{
query: string;
match: string;
attrib?: string;
lhsProp?: string;
first?: boolean;
verb?: string;
havingAlso?: QueryInfo[];
havingInner?: QueryInfo;
}
export type PropSettings<T extends Partial<HTMLElement> = HTMLElement, HostProps = any> = {
[P in keyof T]?: keyof HostProps;
};
export type EventSettings = {[key: string] : ((e: Event) => void) | string | INotify};
export interface IMinimalNotify{
nudge?: boolean;
debug?: boolean;
eventListenerOptions?: boolean | AddEventListenerOptions | undefined;
doInit?: boolean;
}
export interface IDIYNotify extends IMinimalNotify{
doOnly?: (target: Element, key: string, mn: IMinimalNotify, e?: Event) => void;
}
export interface IValFromEventInstructions<TSelf = any, TProps = any, TActions = TProps> {
/**
* Hardcoded value to set on recipient element.
*/
val?: any
/**
* path to get value from target
*/
valFromTarget?: keyof TSelf;
/**
* path to get value from target
*/
vft?: keyof TSelf | `${keyof TSelf & string}.${string}`;
/**
* path to get value from event
*/
valFromEvent?: string;
/**
* path to get value from event
*/
vfe?: string;
propName?: string;
clone?: boolean;
parseValAs?: 'int' | 'float' | 'bool' | 'date' | 'truthy' | 'falsy' | undefined | 'string' | 'object';
trueVal?: any;
falseVal?: any;
}
export interface INotify<TSelf = any, TProps = any, TActions = TProps> extends IMinimalNotify, IValFromEventInstructions<TSelf, TProps, TActions>{
navTo?: string;
/**
* Pass property or invoke fn onto custom or built-in element hosting the contents of p-u element.
*/
//toHost: boolean;
/**
* Id of Dom Element. Uses import-like syntax:
* ./my-id searches for #my-id within ShadowDOM realm of instance.
* ../my-id searches for #my-id one ShadowDOM level up.
* /my-id searches from outside any ShadowDOM.
* @attr
*/
toUpShadow?: string;
/**
* abbrev of toUpShadow
*/
to?: string
/**
* Pass property or invoke fn onto itself
*/
toSelf?: boolean;
/**
* Pass property to the nearest previous sibling / ancestor element matching this css pattern, using .previousElement(s)/.parentElement.matches method.
* Does not pass outside ShadowDOM realm.
*/
toNearestUpMatch?: string;
toClosest?: string;
/**
* to closest or host ("itemscope" attribute or shadow DOM boundary)
*/
tocoho?: boolean | string; //to closest or host
/**
* Name of property to set on matching (upstream) element.
* @attr
*/
prop?: keyof TProps & string;
/**
* Name of method or property arrow function to invoke on matching (upstream) element.
*/
fn?: keyof TActions & string;
dispatch?: string;
withArgs?: ('self' | 'val' | 'event')[];
plusEq?: boolean | string | number;
eqConst?: any;
toggleProp?: boolean;
as?: 'str-attr' | 'bool-attr' | 'obj-attr',
thenDo?: (target: Element, key: string, n: INotify, e?: Event) => void;
}
export interface INotifyHookupInfo{
controller: AbortController;
}
export interface IConditional<MCProps extends Partial<HTMLElement> = HTMLElement>{
/**
* Name of property to check if truthy
*/
if?: keyof MCProps;
/**
* If condition value
*/
ifVal?: boolean;
lhs?: string;
lhsVal?: any;
rhs?: string;
rhsVal?: any;
op?: '==' | '!=' | '>' | '<' | '>=' | '<=' | '===' | '!==' | undefined;
}
export type AttribsSettings = { [key: string]: string | boolean | number | undefined | null | string[]};
export type PSettings<T extends Partial<HTMLElement> = HTMLElement, HostProps = any> = [PropSettings<T, HostProps> | undefined];
export type PESettings<T extends Partial<HTMLElement> = HTMLElement> = [props: PropSettings<T> | undefined, on: EventSettings | undefined];
export type PEUnionSettings<T extends Partial<HTMLElement> = HTMLElement> = PSettings<T> | PESettings<T>;
export type PEASettings<T extends Partial<HTMLElement> = HTMLElement> =
[props: PropSettings<T> | undefined, events: EventSettings | undefined, attribs: AttribsSettings | undefined];
export type PEAUnionSettings<T extends Partial<HTMLElement> = HTMLElement> = PEUnionSettings<T> | PEASettings<T>;
export type ConditionalSettings<T extends Partial<HTMLElement> = HTMLElement> =
[boolean, IConditional<T>, ...any]
export interface TRElementProps {
propChangeQueue?: Set<string>;
inReflectMode?: boolean;
}
export interface TRElementActions{
attachQR(): void;
detachQR(): void;
attributeChangedCallback?(n: string, ov: string, nv: string): void;
}
// export interface Transform<TMixinComposite = any> extends LogicOp<TMixinComposite>{
// match: {[key: string]: MatchRHS<TMixinComposite>}
// }
export type MatchRHS<TMixinComposite = any> = string;
export interface HasPropChangeQueue{
propChangeQueue: Set<string> | undefined;
QR: undefined | ((name: string, self: HasPropChangeQueue) => void);
}
export interface ProxyPropChangeInfo{
oldVal: any,
newVal: any,
prop: string
}
export interface ITSChecker{
notChanged(host: Element, fragment: Element | DocumentFragment): boolean;
}
export type TargetTuple =
/**
* Use the native .closest() function to get the target
*/
| ['closest', string]
/**
* abbrev for closet
*/
| ['c', string]
/**
* Find nearest previous sibling, parent, previous sibling of parent, etc that matches this string.
*/
| ['upSearch', string]
/**
* abbrev for upSearch
*/
| ['us', string]
/**
* Tries .closest([string value]).
* If that comes out to null, do .getRootNode().host
*/
| ['closestOrHost', string]
/**
* abbrev for closestOrHost
*/
| ['coh', string]
/**
* Find nearest previous element sibling that matches this string
*/
| ['previous', string]
/**
* Find first element matching string within root node.
*/
| ['withinRootNode', string]
/**
* abbrev for withinRootNode
*/
| ['wrn', string]
/**
* hostish
*/
| ['h', (boolean | string)?, boolean?]
| ['hostish', (boolean | string)?, boolean?]
/**
* Find first element matching string within itemscope.
*/
| ['withinItemScope', string, boolean?]
/**
* abbrev for within item scope
*/
| ['wis', string, boolean?]
| ['within', string, string]
| ['wi', string, string]
/**
* First first element matching string within form.
*/
| ['withinForm', string]
/**
* abbrev for within form
*/
| ['wf', string]
;
/**
* Target selector in upward direction.
*/
export type Target =
| TargetTuple
/**
* Use the parent element as the target
*/
| 'parent'
/**
* abbrev for parent
*/
| 'p'
/**
* Use the previous element sibling as the target.
*/
| 'previousElementSibling'
/**
* abbrev for previous element sibling.
*/
| 'pes'
/**
* Search previous element siblings for target that matches the query.
*/
| `previous${camelQry}` |
/**
* Use the parent as the target. If no parent, use root node.
*/
'parentOrRootNode' |
/**
* abbrev for parent or root node
*/
'porn' |
/**
* Do "closest" for element with "-"" in the name. If non found, get host of getRootNode()
*/
'hostish' |
/**
* Use the element itself as the target
*/
'self' |
/**
* abbrev for self
*/
's' |
`closest${camelQry}` |
/**
* [TODO]
* do upsearch for matching itemref
*/
'nearestScope' |
/**
* Find nearest previous sibling, parent, previous sibling of parent, etc that matches this string.
*/
`upSearchFor${camelQry}` |
/**
* Tries .closest matching string. If that's null, does .getRootNode().host
*/
`closest${camelQry}OrHost` |
/**
* Searches for first element within the rootNode that matches the camelQry.
*/
`${camelQry}WithinRootNode` |
/**
* get host
*/
'host' |
/**
* abbrev for host
*/
'h'
;
export type ScopeTuple = TargetTuple
/**
* Similar to closestOrHost, but just get the root fragment via getRootNode()
*/
| ['closestOrRootNode', string]
/**
* abbrev for closestOrRootNode
*/
| ['corn', string]
;
/**
* Outer boundary for transformation
*/
export type Scope = Target | ScopeTuple |
/**
* use native function getRootNode() to set the target
*
*/
'rootNode' |
/**
* abbrev for rootNode
*/
'rn' |
`closest${camelQry}OrRootNode` |
'shadowDOM' |
/**
* abbrev for shadow DOM
*/
'sd'
;
//https://stackoverflow.com/questions/38123222/proper-way-to-declare-json-object-in-typescript
type JSONValue =
| string
| number
| boolean
| null
| JSONValue[]
| {[key: string]: JSONValue}
interface JSONObject {
[k: string]: JSONValue
}
interface JSONArray extends Array<JSONValue> {}
export interface getValArg {
host?: any;
}
export type keyOfCtxNavElement = keyof ICTXNavElement & string;
export type keyofICTXCamelQryPrompt = `${keyof ICTXCamelQryPrompt & string}.${camelQry}`;
export type keyofCTXNavRecursive = (keyof ICTXNavRecursive & string) | keyofICTXCamelQryPrompt;
export type AffectOptions =
`${keyOfCtxNavElement}`
| `${keyofCTXNavRecursive}`
| `${keyofCTXNavRecursive}.${keyofCTXNavRecursive}`
//| `${keyofCTXNavRecursive}.${keyofCTXNavRecursive}.${keyofCTXNavRecursive}`
// | `${keyofCTXNavRecursive}.${keyofCTXNavRecursive}.${keyofCTXNavRecursive}.${keyofCTXNavRecursive}`
;
export interface ICTXNavRecursive<T = any>{
$?: T;
hostCtx?: T;
}
/**
* E = element
* P = part
* C = class
* I = itemscope
* A = attribute
* N = name
*/
export type camelQry = `${string}E` | `${string}P` | `${string}C` | `${string}Id` | `${string}I` | `${string}A` | `${string}N` | `${string}M`;
export interface ExpectedCamelQry<T = any>{
[key: camelQry] : ICtxNav<T>
}
export interface ICTXNavElement{
self?: Element;
host?: Element;
}
export interface ICTXCamelQryPrompt<T = any>{
ancestor?: ExpectedCamelQry<T>;
elder?: ExpectedCamelQry<T>;
}
export interface ICtxNav<T = any> extends ICTXNavRecursive<T>, ICTXNavElement, ICTXCamelQryPrompt<T> {
beScoped?: EventTarget;
xtalState(): Promise<EventTarget | undefined>;
}
export type ConvertOptions = 'bool' | 'int' | 'float' | 'date' | 'truthy' | 'falsy' | 'string' | 'object' | 'regExp' | 'number';