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
570 lines (479 loc) • 17.8 kB
TypeScript
import { MountContext, PipelineStage } from "../mount-observer/types";
import { ConvertOptions, Scope } from "./lib/types";
import { EMC} from './be/types';
export type PropAttrQueryType =
| '|' //microdata itemprop
| '@' //form element name
| '#' //id
| '%' //part
| '.' //class
//| '-' //marker
| '$' //microdata itemprop + itemscope attributes (nested)
| '-o'
export type PropAttrPair<TProps> = `${PropAttrQueryType} ${keyof TProps & string}`;
export type PropQueryExpression<TProps, TElement = {}> =
| `* ${CSSQuery}`
| `:root`
| `${keyof HTMLElementTagNameMap}`
| `${PropAttrPair<TProps>}`
| `${PropAttrPair<TProps>} -s ${keyof TElement & string}`
| `${PropAttrPair<TProps>} ${PropAttrPair<TProps>}`
| `${PropAttrPair<TProps>} ${PropAttrPair<TProps>} -s ${keyof TElement & string}`
// | `${PropAttrPair<TProps>} ${PropAttrPair<TProps>} ${PropAttrPair<TProps>}`
// | `${PropAttrPair<TProps>} ${PropAttrPair<TProps>} ${PropAttrPair<TProps>} -s ${keyof TElement & string}`
//
;
//#region derived expressions
export type Expr0 = [string, number];
export type Expr1 = [...Expr0, string];
export type Expr2 = [...Expr1, number];
export type Expr3 = [...Expr2, string];
export type Expr4 = [...Expr3, number];
export type Expr5 = [...Expr4, string];
export type Expr6 = [...Expr5, number];
export type Expr7 = [...Expr6, string];
export type Expr8 = [...Expr7, number];
export type Expr9 = [...Expr8, string];
export type Expr10 = [...Expr9, number];
export type Expr11 = [...Expr10, string];
export type Expr12 = [...Expr11, number];
//export type Action<TProps> = (matchingElement: Element, pique: IMountOrchestrator<TProps>) => Promise<Derivative<TProps>> | Promise<void>;
export type InterpolatingExpression = Expr0 | Expr1 | Expr2 | Expr3 | Expr4 | Expr5 | Expr6 | Expr7 | Expr8 | Expr9 | Expr10 | Expr11 | Expr12;
export type NumberExpression = [number];
export type DerivationCriteria<TProps, TMethods> = {
//TODO
path: string,
from?: number,
//TODO
as?: ConvertOptions,
//TODO - applicable to arrays
filter?: keyof TModhods & string | ((val: any) => boolean),
//TODO
//map?: keyof TModhods & string | ((val: any) => any,
};
export interface TransformOptions{
propagator?: MarkedUpEventTarget,
propagatorIsReady?: boolean,
skipInit?: boolean,
useViewTransition?: boolean,
}
export type Derivative<TProps, TMethods, TElement = {}> =
| number
| InterpolatingExpression
| ((model: TProps & TMethods, transform: ITransformer<TProps, TMethods, TElement>, uow: UnitOfWork<TProps, TMethods, TElement>, matchingElement: Element) => any)
| NumberExpression
| DerivationCriteria<TProps, TMethods>
// only works if lhs has field/property
| keyof TMethods & string
// combined observe an 0
| keyof TProps & string
| boolean
;
//#endregion
export interface Engagement<TMethods>{
/** Invoked when the element is encountered. */
do?: keyof TMethods & string,
/** Invoked when a previously matching element is no longer matching. */
undo?: keyof TMethods & string,
/** Invoked when a previously matching element is disconnected. */
forget?: keyof TMethods & string,
/**
* Can be used for any kind of label, but most common use is for specifying a behavior/enhancement
* to attach.
*/
be?: EMC,
with?: any,
waitForResolved?: boolean,
dep?: () => void;
}
export type onMountStatusChange = 'onMount' | 'onDismount' | 'onDisconnect';
export interface EngagementCtx<TModel> {
be?: string,
with?: any,
type: onMountStatusChange,
mountContext: MountContext
}
export type EngagementOrKeyOrEMC<TMethods, TProps = TMethods> =
| (keyof TMethods & string)
| Engagement<TMethods>
| EMC<any, TProps>
;
export type EngagementOrEMC<TMethods, TProps = TMethods> =
| Engagement<TMethods>
| EMC<any, TProps>
export type Engagements<TMethods, TProps = TMethods> =
| EngagementOrKeyOrEMC<TMethods>
| Array<EngagementOrKeyOrEMC<TMethods>>
;
export interface IMountOrchestrator<TProps, TMethods = TProps>{
//TODO add all the methods
}
// export type OneOrMore<TProps> =
// | keyof TProps & string
// | `${keyof TProps & string} ${keyof TProps & string}`
export type LHS<TProps, TElement={}> = PropQueryExpression<TProps, TElement>;
export type CSSQuery = string;
export interface ConditionGate<TProps, TMethods, TElement = {}>{
ifAllOf?: number[],
ifNoneOf?: number[],
ifEqual?: [number, number | [number] | string],
d?: Derivative<TProps, TMethods, TElement>,
}
export interface ScopingConfig<TProps=any, TMethods = TProps> {
name: string;
config?: IshConfig<TProps, TMethods>;
}
export type ScopeInstructions<TProps=any, TMethods=TProps> =
| ScopingConfig
;
export interface ScopedLoop<TProps = any, TMethods = TProps>{
config?: IshConfig<TProps, TMethods>;
options: Partial<Clone$Options>
}
export type WhereConditions =
| string //css matches
| {
matches: string,
mediaMatches: string,
containerQuery: string,
}
export type IfInstructions<TProps, TMethods, TElement = {}> = string | boolean | number | [number] | ConditionGate<TProps, TMethods, TElement> ;
export interface ObservePropParams {
derivePropFrom?: string,
}
export type PropOrComputedProp<TProps, TMethods = TProps> =
| keyof TProps & string
| [keyof TProps & string, (val: any) => any]
| [keyof TProps & string, keyof TMethods & string]
| ObservePropParams
| `:${string}`
export interface CrossProduct<TProps, TMethods> {
x: string | Array<string>,
y: (keyof TProps & TMethods & string) | Array<keyof TProps & TMethods & string>
}
export interface ForEach<TProps, TMethods, TElement = {}>{
each?: 0,
/** css query to locate the template */
clone?: string,
/** within scope for locating the template */
wi?: 'node' | 'rootNode' | 'upShadowSearch'
indexProp?: string,
xform: XForm<TProps, TMethods, TElement> & Info,
appendTo?: string,
timestampProp?: string,
outOfRangeAction?: string,
outOfRangeProp?: string,
}
// export interface MapInstructions<TProps, TMethods, TElement = {}>{
// // itemCss: CSSQuery,
// // each: string | [string, IshConfig<TProps, TMethods, TElement>],
// // in: string | [string, IshConfig<TProps, TMethods, TElement>],
// }
export interface ForEachInterface{
init(): Promise<void>;
update(model: any[]): Promise<void>;
}
export interface UnitOfWork<TProps, TMethods = TProps, TElement = {}>{
/**
* add event listener
*/
addEventListener?: AddEventListenerType<TProps, TMethods> | Array<AddEventListenerType<TProps, TMethods>>,
/**
* abbrev. for addEventListener
*/
a?: AddEventListenerType<TProps, TMethods> | Array<AddEventListenerType<TProps, TMethods>>,
/**
* Specify how the value we want to apply to the target element should be derived from the observed props.
* derived value from observed props
*/
derivedValFromModel?: Derivative<TProps, TMethods, TElement>,
/**
* Specify how the value we want to apply to the target element should be derived from the observed props.
* abbrev. for derivedValSpecs
*/
d?: Derivative<TProps, TMethods, TElement>,
/**
* Specify what to do when the element is encountered, and/or when it goes out of scope.
* Register the found element in some way.
* Actions not tied to observed props or user actions.
*/
engage?: Engagements<TMethods>
/**
* Specify what to do when the element is encountered, and/or when it goes out of scope.
* Register the found element in some way.
* Actions not tied to observed props or user actions.
* Abbrev. for engagementActions
*/
e?: Engagements<TMethods>,
forEachBinding?: ForEach<any, any, any>
/**
* for each -- deprecated?
*/
f?: ForEach<any, any, any>,
/**
* ifs ands or buts -- conditions on the model
*/
ifs?: IfInstructions<TProps, TMethods, TElement>,
/**
* ifs ands or buts -- conditions on the model
* abbrev for ifs
*/
i?: IfInstructions<TProps, TMethods, TElement>,
/**
* method of matching element to pass derived value into
* [TODO]
*/
invoke?: string,
/**
* modify the model in a (mostly) declarative way
*/
modifyModel?: ModificationUnitOfWork<TProps, TMethods, TElement> | Array<ModificationUnitOfWork<TProps, TMethods, TElement>>,
/**
* modify the model in a (mostly) declarative way
* abbreviation for modifyModel
*/
m?: ModificationUnitOfWork<TProps, TMethods, TElement> | Array<ModificationUnitOfWork<TProps, TMethods, TElement>>,
/**
* List of props to observe from the model
*/
observedProps?: keyof TProps & string | PropOrComputedProp<TProps, TMethods> | PropOrComputedProp<TProps, TMethods>[],
/**
* List of props to observe from the model
* abbrev. for observedProps
*/
o?: keyof TProps & string | PropOrComputedProp<TProps, TMethods> | PropOrComputedProp<TProps, TMethods>[],
/**
* set specified property of the matching element to the (derived) value
*/
setProp?: (keyof TElement & string) | {},
/**
* set specified property of the matching element to the (derived) value
* abbrev of setProp
*/
s?: (keyof TElement & string) | {},
/**
* set specified attribute of the matching element to the (derived) value
*
*/
setAttr?: string,
/**
* set specified attribute of the matching element to the (derived) value
* abbrev of setAttr
*/
sa?: string,
/**
* set specified style of the matching element to the (derived) value
*/
ss?: string,
/**
* negate to
*/
negTo?: string,
/**
* Where condition for selecting the target elements.
*/
whereConditions?: WhereConditions,
/**
* Where conditions for selecting the target elements
* abbrev. for whereConditions
*/
w?: WhereConditions,
y?: number | YieldSettings<TProps>,
$?: ScopeInstructions<TProps, TMethods>,
$$?: ScopedLoop<TProps, TMethods>,
}
export interface YieldSettings<TProps>{
to?: keyof TProps,
as?: 'date' | 'number'
}
export type ValueFromElement<TProps, TMethods, TElement = {}> =
(
matchingElement: Element,
transformer: ITransformer<TProps, TMethods, TElement>,
mod: ModificationUnitOfWork<TProps, TMethods, TElement>
) => any
export interface ModificationUnitOfWork<TProps, TMethods, TElement = {}>{
on: string,
/**
* Increment
*/
inc?: keyof TProps & string,
/**
* Increment by specified number, or by specified property coming from matching element
*/
byAmt?: number | string,
/**
* Set this prop on the host
*/
s?: keyof TProps & string,
/**
* [TODO] -- Set Custom State --only available for xtal-element
*/
ss?: string,
/**
* [TODO] -- Set attribute
*/
sa?: string,
/**
* [TODO] enhance / engage the host, or register the host in some way
* don't implement this until a good use case is found, make sure it makes sense.
*/
e?: Engagements<TMethods>,
/**
* [TODO] Set hardcoded value
*/
to?: any,
toValFrom?: string | ValueFromElement<TProps, TMethods, TElement>;
toggle?: keyof TProps & string,
}
export interface QuenitOfWork<TProps, TMethods, TElement = {}> extends UnitOfWork<TProps, TMethods, TElement>{
q: string,
qi?: QueryInfo,
}
export type UnitOfWorkRHS<TProps, TMethods, TElement = {}> =
| 0
| keyof TMethods & string
| keyof TProps & string
| UnitOfWork<TProps, TMethods, TElement>
| XForm<any, any, any> & Info //unclear if this is necessary
;
export type RHS<TProps, TMethods, TElements = Element> = UnitOfWorkRHS<TProps, TMethods, TElements> | Array<UnitOfWork<TProps, TMethods, TElements>>;
export interface AttrMap{
type: PropAttrQueryType,
name: string
}
export interface QueryInfo{
isRootQry?: boolean,
localPropCamelCase?: string,
cssQuery?: string,
o?: string[],
s?: string[],
localName?: string,
//w?: WhereConditions,
css?: string,
hostPropToAttrMap?: Array<AttrMap>
}
export type TransformerTarget = Element | DocumentFragment | Element[] | ShadowRoot | Document;
export type Model = {
[key: string]: any
}
export type EventListenerAction<TProps, TMethods> = (keyof TMethods & string) | ((e: Event, t: ITransformer<TProps, TMethods>, uow: UnitOfWork<TProps, TMethods>) => void);
export type AddEventListenerType<TProps, TMethods> =
| keyof TMethods & string
| AddEventListener<TProps, TMethods>;
export interface AddEventListener<TProps, TMethods>{
on: string,
do: EventListenerAction<TProps, TMethods>,
options?: boolean | EventListenerOptions,
}
export type XForm<TProps, TMethods, TElement = {}> = Partial<{
[key in LHS<TProps, TElement>]: RHS<TProps, TMethods, TElement>;
}>;
export interface Info {
411?: {
w?: string,
//idxFrom?: string
}
}
export interface ITransformer<TProps, TMethods, TElement = {}>{
target: TransformerTarget,
model: TProps & TMethods,
xform: XForm<TProps, TMethods, TElement> & Info,
options: TransformOptions,
initializedMods: Set<ModificationUnitOfWork<TProps, TMethods, TElement>>
//propagator?: EventTarget,
}
export type ToTransformer<TProps, TMethods, TElement = {}> = (
target: TransformerTarget,
model: TProps & TMethods,
xform: XForm<TProps, TMethods, TElement>,
propagator?: EventTarget
) => ITransformer<TProps, TMethods>;
export interface MarkedUpEventTarget extends EventTarget{
___props?: Set<string>;
___nestedProps?: Map<string, any>;
}
export interface TransRenderEndUserProps<ModelProps, ModelMethods = ModelProps, TElement = {}>{
xform: XForm<ModelProps, ModelMethods, TElement>;
scope: Scope;
//model?: ModelProps & ModelMethods;
options?: TransformOptions;
}
export interface TransRenderProps<ModelProps, ModelMethods = ModelProps> extends TransRenderEndUserProps<ModelProps, ModelMethods>{
}
export interface TransRenderMethods{
getTarget(): Promise<Document | ShadowRoot | DocumentFragment | Element>,
getXForm(): Promise<XForm<any, any>>,
getModel(): Promise<any>,
skipInit: boolean,
}
import {IshConfig, OConfig} from './froop/types';
export interface MntCfg<TProps = any, TActions = TProps, ETProps = TProps> extends OConfig<TProps, TActions, ETProps>{
mainTemplate: string | HTMLTemplateElement,
/**
* Only set to true if shadow dom is used and the light children play a critical role as far as
* progressive enhancement.
*/
appendOnClone?: boolean,
/**
* transform within ShadowRoot if applicable
*/
xform?: XForm<TProps, TActions>,
/**
* transform applied to light children, if applicable
* Use ":root" to match on the root element
*/
lcXform?: XForm<TProps, TActions>,
/**
* transforms within ShadowRoot if applicable that uses view transitions
*/
xxform?: XForm<TProps, TActions>
styles?: /*CSSStyleSheet[] |*/ string | string[] | CSSStyleSheet | Array<CSSStyleSheet>;
shadowRootInit?: ShadowRootInit,
assumeCSR?: boolean,
}
export interface MountProps<TProps = any, TActions = TProps, ETProps = TProps>{
readonly clonedTemplate?: DocumentFragment;
deferHydration?: boolean;
readonly hydrated?: boolean;
readonly csr?: boolean;
readonly xform?: XForm<TProps, TActions>,
readonly xxform?: XForm<TProps, TActions>,
}
export type PMP<TProps = any, TActions = TProps, ETProps = TProps> = Partial<MountProps<TProps, TActions, ETProps>>;
export type ProPMP<TProps = any, TActions = TProps, ETProps = TProps> = Promise<PMP<TProps, TActions, ETProps>>
export interface MountActions<TProps = any, TActions = TProps, ETProps = TProps>{
cloneMT(self: this): PMP;
// inspect(self: this): PMP
// mount(self: this): ProPMP
initCSRXform(self: this): ProPMP<TProps, TActions, ETProps>;
initSSRXform(self: this): ProPMP<TProps, TActions, ETProps>;
onNoXForm(self: this): ProPMP<TProps, TActions, ETProps>;
mountClone(self: this): Partial<MountProps<TProps, TActions, ETProps>>;
}
export interface IObject$tring{
strVal: string | undefined;
objVal: any;
arrVal: any[] | undefined;
parse(): Promise<void>;
}
export type ZeroOrMore<T> = T | Array<T> | undefined;
/**
* https://x.com/mattpocockuk/status/1821926395380986219
*/
export type StringWithAutocompleteOptions<TOptions> =
| (string & {})
| TOptions;
export interface Clone$Options{
ish: HasIshList,
ishContainer: Element,
seedEl: Element,
idxStart: number,
itemProp: string,
mapIdxTo?: string,
itemTemplate: HTMLTemplateElement;
baseCrumb: string,
idleTimeout: number,
//model?: any,
listProp?: string,
}