UNPKG

uicore-ts

Version:

UICore is a library to build native-like user interfaces using pure Typescript. No HTML is needed at all. Components are described as TS classes and all user interactions are handled explicitly. This library is strongly inspired by the UIKit framework tha

595 lines (594 loc) 26 kB
import { UIColor } from "./UIColor"; import { UICore } from "./UICore"; import "./UICoreExtensions"; import type { UIDialogView } from "./UIDialogView"; import { UILocalizedTextObject } from "./UIInterfaces"; import { UIObject } from "./UIObject"; import { UIPoint } from "./UIPoint"; import { UIRectangle } from "./UIRectangle"; import { UIViewController } from "./UIViewController"; declare module AutoLayout { class Constraint { [key: string]: any; } class View { [key: string]: any; } class VisualFormat { static parse(arg0: any, arg1: any): any; [key: string]: any; } const enum Attribute { LEFT = 0, RIGHT = 1, BOTTOM = 2, TOP = 3, CENTERX = 4, CENTERY = 5, WIDTH = 6, HEIGHT = 7, ZINDEX = 8, VARIABLE = 9, NOTANATTRIBUTE = 10 } const enum Relation { EQU = 0, LEQ = 1, GEQ = 2 } } declare global { interface HTMLElement { UIViewObject?: UIView; } } export interface LooseObject { [key: string]: any; } export interface ControlEventTargetsObject { [key: string]: Function[]; } export interface UIViewBroadcastEvent { name: string; parameters?: { [key: string]: string | string[]; }; } type Mutable<T> = { -readonly [P in keyof T]: T[P]; }; type ColorStyleProxy = { [K in keyof CSSStyleDeclaration as K extends string ? (Lowercase<K> extends `${string}color${string}` ? K : never) : never]?: UIColor; }; export type UIViewAddControlEventTargetObject<T extends { controlEvent: Record<string, any>; }> = Mutable<{ -readonly [K in keyof T["controlEvent"]]: ((sender: UIView, event: Event) => void) & Partial<UIViewAddControlEventTargetObject<T>>; }>; interface Constraint { constant: number; multiplier: number; view1: any; attr2: any; priority: number; attr1: any; view2: any; relation: any; } export interface IUILoadingView extends UIView { theme: "light" | "dark"; } /** * A template literal tag to enable CSS highlighting in IDEs. * Example: css` .myClass { color: red; } ` */ export declare function css(strings: TemplateStringsArray, ...values: any[]): string; export declare function UIComponentView(target: Function, context: ClassDecoratorContext): void; export declare class UIView extends UIObject { _nativeSelectionEnabled: boolean; _shouldLayout?: boolean; _UITableViewRowIndex?: number; _UITableViewReusabilityIdentifier: any; _UIViewIntrinsicTemporaryWidth?: string; _UIViewIntrinsicTemporaryHeight?: string; _enabled: boolean; _frame?: UIRectangle & { zIndex?: number; }; _frameCache?: UIRectangle; _backgroundColor: UIColor; _colorStyleProxy?: ColorStyleProxy; _liveCSSValues: Map<string, { keys: string[]; producer: () => string; }>; _liveCSSCallback: () => void; _overlayView?: UIView; get overlayView(): UIView | undefined; set overlayView(view: UIView); _viewHTMLElement: HTMLElement & LooseObject; _innerHTMLKey?: string; _defaultInnerHTML?: string; _parameters?: { [x: string]: (string | UILocalizedTextObject); }; _localizedTextObject?: UILocalizedTextObject; _controlEventTargets: ControlEventTargetsObject; _broadcastEventTargets: { [eventName: string]: ((event: UIViewBroadcastEvent) => void)[]; }; _ongoingControlEventHandlers: { _controlEventKey: string; }[]; _frameTransform: string; viewController?: UIViewController; _updateLayoutFunction?: Function; _constraints: any[]; superview?: UIView; subviews: UIView[]; _styleClasses: any[]; _isHidden: boolean; pausesPointerEvents: boolean; stopsPointerEventPropagation: boolean; pointerDraggingPoint: UIPoint; _previousClientPoint: UIPoint; _isPointerInside?: boolean; _isPointerValid?: boolean; _isPointerValidForMovement?: boolean; _isPointerDown: boolean; _initialPointerPosition?: UIPoint; _hasPointerDragged?: boolean; _pointerDragThreshold: number; ignoresTouches: boolean; ignoresMouse: boolean; core: UICore; static _UIViewIndex: number; _UIViewIndex: number; static _viewsToLayout: UIView[]; forceIntrinsicSizeZero: boolean; _touchEventTime?: number; static _pageScale: number; _scale: number; isInternalScaling: boolean; static _fontReadyHook: boolean; static resizeObserver: ResizeObserver; private _isMovable; makeNotMovable?: () => void; private _isResizable; makeNotResizable?: () => void; resizingHandles: UIView[]; static LoadingViewClass: new () => IUILoadingView; private _loadingView?; set loading(isLoading: boolean); get loading(): boolean; private _isMoving; _isCBEditorTemporaryResizable: boolean; _isCBEditorTemporaryMovable: boolean; static _onWindowTouchMove: (event: TouchEvent) => void; static _onWindowMouseMove: (event: MouseEvent) => void; static _onWindowMouseup: (event: MouseEvent) => void; private _resizeObserverEntry?; protected _intrinsicSizesCache: Record<string, UIRectangle>; /** When set, this view reads and writes its intrinsic size cache entries from the * static shared map keyed by this identifier rather than from its own per-instance * cache. All views that share the same identifier share the same cached sizes, so * only the first measurement in a group is ever computed. * * Invalidate with UIView.invalidateSharedIntrinsicSizeCache(identifier). */ sharedIntrinsicSizeCacheIdentifier?: string; /** Static shared intrinsic size cache, keyed by sharedIntrinsicSizeCacheIdentifier. * Each bucket mirrors the per-instance _intrinsicSizesCache format. */ static _sharedIntrinsicSizeCaches: Map<string, Record<string, UIRectangle>>; static invalidateSharedIntrinsicSizeCache(identifier: string): void; static invalidateAllSharedIntrinsicSizeCaches(): void; private static _virtualLayoutingDepth; static get isVirtualLayouting(): boolean; get isVirtualLayouting(): boolean; startVirtualLayout(): void; finishVirtualLayout(): void; _frameForVirtualLayouting?: UIRectangle; _frameCacheForVirtualLayouting?: UIRectangle; usesVirtualLayoutingForIntrinsicSizing: boolean; _contentInsets: { top: number; left: number; bottom: number; right: number; }; get contentInsets(): { left: number; right: number; bottom: number; top: number; }; set contentInsets(insets: { left: number; right: number; bottom: number; top: number; }); constructor(elementID?: string, viewHTMLElement?: HTMLElement & LooseObject | null, elementType?: string | null, preInitConfiguratorObject?: any); static get nextIndex(): number; private static _cachedPageWidth; private static _cachedPageHeight; private static _pageDimensionsCacheValid; private static _resizeObserverInitialized; /** * Initialize resize observer to invalidate cache when page dimensions change. * This is called lazily on first access. */ private static _initializePageDimensionsCacheIfNeeded; /** * Compute and cache page dimensions. * Only triggers reflow when cache is invalid. */ private static _updatePageDimensionsCacheIfNeeded; static get pageWidth(): number; static get pageHeight(): number; /** * Manually invalidate the page dimensions cache. * Useful if you know dimensions changed and want to force a recalculation. */ static invalidatePageDimensionsCache(): void; centerInContainer(): void; centerXInContainer(): void; centerYInContainer(): void; _initViewHTMLElement(elementID: string, viewHTMLElement: (HTMLElement & LooseObject) | null, elementType?: string | null): void; set nativeSelectionEnabled(selectable: boolean); get nativeSelectionEnabled(): boolean; get styleClassName(): string; protected _initViewCSSSelectorsIfNeeded(): void; initViewStyleSelectors(): void; initStyleSelector(selector: string, style: string): void; createElement(elementID: string, elementType: string): HTMLElement; get viewHTMLElement(): HTMLElement & LooseObject; get elementID(): string; setInnerHTML(key: string, defaultString: string, parameters?: { [x: string]: string | UILocalizedTextObject; }): void; protected _setInnerHTMLFromKeyIfPossible(): void; protected _setInnerHTMLFromLocalizedTextObjectIfPossible(): void; get localizedTextObject(): UILocalizedTextObject | undefined; set localizedTextObject(localizedTextObject: UILocalizedTextObject | undefined); get innerHTML(): string; set innerHTML(innerHTML: string); set hoverText(hoverText: string | undefined | null); get hoverText(): string | undefined | null; get scrollSize(): UIRectangle; get dialogView(): UIDialogView | undefined; get rootView(): UIView; set enabled(enabled: boolean); get enabled(): boolean; updateContentForCurrentEnabledState(): void; get tabIndex(): number; set tabIndex(index: number); get propertyDescriptors(): { object: UIObject; name: string; }[]; get styleClasses(): any[]; set styleClasses(styleClasses: any[]); hasStyleClass(styleClass: string): boolean; addStyleClass(styleClass: string): void; removeStyleClass(styleClass: string): void; static findViewWithElementID(elementID: string): UIView | undefined; static injectCSS(cssText: string, id?: string): void; static createStyleSelector(selector: string, style: string): void; static getStyleRules(selector: string): CSSRule[]; get style(): CSSStyleDeclaration; get computedStyle(): CSSStyleDeclaration; get hidden(): boolean; set hidden(v: boolean); static set pageScale(scale: number); static get pageScale(): number; set scale(scale: number); get scale(): number; calculateAndSetViewFrame(): void; get frame(): UIRectangle & { zIndex?: number; }; set frame(rectangle: UIRectangle & { zIndex?: number; }); hasWeakFrame: boolean; set weakFrame(rectangle: UIRectangle & { zIndex?: number; }); set strongFrame(rectangle: UIRectangle & { zIndex?: number; }); setFrame(rectangle: UIRectangle & { zIndex?: number; }, zIndex?: number, performUncheckedLayout?: boolean): void; get bounds(): UIRectangle; set bounds(rectangle: UIRectangle); boundsDidChange(bounds: UIRectangle): void; didResize(entry: ResizeObserverEntry): void; get frameTransform(): string; set frameTransform(value: string); setPosition(left?: number | string, right?: number | string, bottom?: number | string, top?: number | string, height?: number | string, width?: number | string): void; setSizes(height?: number | string, width?: number | string): void; setMinSizes(height?: number | string, width?: number | string): void; setMaxSizes(height?: number | string, width?: number | string): void; setMargin(margin?: number | string): void; setMargins(left?: number | string, right?: number | string, bottom?: number | string, top?: number | string): void; setPadding(padding?: number | string): void; setPaddings(left?: number | string, right?: number | string, bottom?: number | string, top?: number | string): void; setBorder(radius?: number | string, width?: number | string, color?: UIColor, style?: string): void; setStyleProperty(propertyName: string, value?: number | string): void; get userInteractionEnabled(): boolean; set userInteractionEnabled(userInteractionEnabled: boolean); get backgroundColor(): UIColor; set backgroundColor(backgroundColor: UIColor); get colorStyleProxy(): ColorStyleProxy; /** * Registers a producer function that generates a composite CSS string value * (e.g. a gradient or box-shadow with embedded colors) and re-runs it * automatically whenever any of the given semantic keys are updated via * applySemanticColors(). Replaces any previous registration for the same * CSS property. The initial value is written immediately. */ registerLiveCSSValue(property: string, keys: string[], producer: () => string): void; get alpha(): number; set alpha(alpha: number); static animateViewOrViewsWithDurationDelayAndFunction(viewOrViews: UIView | HTMLElement | UIView[] | HTMLElement[], duration: number, delay: number, timingStyle: string | undefined, transformFunction: Function, transitioncompletionFunction: Function): { finishImmediately: () => void; didFinish: () => void; views: HTMLElement | UIView | UIView[] | HTMLElement[]; registrationTime: number; }; animationDidFinish(): void; static _transformAttribute: string; static _setAbsoluteSizeAndPosition(element: HTMLElement & LooseObject, left: number, top: number, width: string | number, height: string | number, zIndex?: number, frameTransform?: string): void; static performAutoLayout(parentElement: HTMLElement & LooseObject, visualFormatArray: string | any[] | null, constraintsArray: string | any[]): () => void; static runFunctionBeforeNextFrame(step: () => void): void; static _isLayoutViewsIfNeededScheduled: boolean; static scheduleLayoutViewsIfNeeded(): void; /** * Persistent layout-cycle tracking map, shared across all iterations within a * single layout pass. Lifted out of the local scope so that * `resetLayoutCycleTracking()` can clear it in response to intentional events * (data changes, window resize) that would otherwise trip the cycle guard. */ static _layoutCounts: Map<UIView, number>; /** * Maximum number of while-loop iterations allowed in a single layout pass * before the pass is forcibly terminated. Each iteration processes all views * that were queued since the previous iteration. Raise this if deep view * hierarchies with many cascading height changes legitimately need more rounds. * Default: 10. */ static layoutPassMaxIterations: number; /** * Maximum number of times a single view may be laid out within one layout pass * before it is considered a cycle and skipped with a warning. Raise this if a * view legitimately re-queues itself many times due to cascading content changes. * Default: 5. */ static layoutCycleThreshold: number; /** * Resets layout-cycle tracking for all views globally. Call this when an * intentional external event (window resize, theme change, etc.) triggers a * full re-layout so that no view is incorrectly blocked by the cycle detector. */ static resetLayoutCycleTrackingForAllViews(): void; /** * Resets layout-cycle tracking for this view, pardoning it from its * accumulated layout count. Call this when this view's data changes * and it legitimately needs to be laid out again within the current pass. */ resetLayoutCycleTracking(): void; /** * Resets layout-cycle tracking for this view and every view in its subtree. * Call this when a container's contents change (e.g. rows reloaded, child * views swapped) and the whole subtree legitimately needs re-layout. */ resetLayoutCycleTrackingForSubtree(): void; static layoutViewsIfNeeded(): void; _lastReportedHeight: number; setNeedsLayout(): void; get needsLayout(): boolean | undefined; layoutIfNeeded(): void; layoutSubviews(): void; applyClassesAndStyles(): void; willLayoutSubviews(): void; didLayoutSubviews(): void; get constraints(): any[]; set constraints(constraints: any[]); addConstraint(constraint: any): void; addConstraintsWithVisualFormat(visualFormatArray: string[]): void; static constraintWithView(view: { isKindOfClass: (arg0: typeof UIView) => any; viewHTMLElement: any; id: any; }, attribute: any, relation: any, toView: { isKindOfClass: any; viewHTMLElement: any; id: any; }, toAttribute: any, multiplier: number, constant: number, priority: number): Constraint; static constraintAttribute: { readonly left: AutoLayout.Attribute.LEFT; readonly right: AutoLayout.Attribute.RIGHT; readonly bottom: AutoLayout.Attribute.BOTTOM; readonly top: AutoLayout.Attribute.TOP; readonly centerX: AutoLayout.Attribute.CENTERX; readonly centerY: AutoLayout.Attribute.CENTERY; readonly height: AutoLayout.Attribute.HEIGHT; readonly width: AutoLayout.Attribute.WIDTH; readonly zIndex: AutoLayout.Attribute.ZINDEX; readonly constant: AutoLayout.Attribute.NOTANATTRIBUTE; readonly variable: AutoLayout.Attribute.VARIABLE; }; static constraintRelation: { readonly equal: AutoLayout.Relation.EQU; readonly lessThanOrEqual: AutoLayout.Relation.LEQ; readonly greaterThanOrEqual: AutoLayout.Relation.GEQ; }; subviewWithID(viewID: string): UIView | undefined; rectangleContainingSubviews(): UIRectangle; hasSubview(view: UIView): boolean; get viewBelowThisView(): UIView; get viewAboveThisView(): UIView; addSubview(view: UIView, aboveView?: UIView): void; addSubviews(views: UIView[]): void; addedAsSubviewToView(view: UIView | undefined, aboveView?: UIView): this; moveToBottomOfSuperview(): void; moveToTopOfSuperview(): void; cancelNeedsLayout(): void; removeFromSuperview(): void; willAppear(): void; willMoveToSuperview(superview: UIView): void; didMoveToSuperview(superview: UIView): void; static annotatePathOnViewHTMLElements: boolean; _annotatePathOnViewHTMLElementsInSubtree(): void; _annotatePathOnViewHTMLElement(): void; wasAddedToViewTree(): void; documentFontsDidLoad(): void; wasRemovedFromViewTree(): void; get isMemberOfViewTree(): boolean; get withAllSuperviews(): UIView[]; get elementWithAllSuperviewElements(): HTMLElement[]; setNeedsLayoutOnAllSuperviews(): void; setNeedsLayoutUpToRootView(): void; focus(): void; blur(): void; get isMovable(): boolean; set isMovable(isMovable: boolean); makeMovable(optionalParameters?: { shouldMoveWithMouseEvent?: (sender: UIView, event: MouseEvent) => boolean; viewDidMoveToPosition?: (view: UIView, isMovementCompleted: boolean) => void; }): void; get isResizable(): boolean; set isResizable(isResizable: boolean); makeResizable(optionalParameters?: { overlayElement?: HTMLElement; borderWidth?: number; borderColor?: UIColor; cornerSize?: string; maxCornerSize?: string; viewDidChangeToSize?: (view: UIView, isMovementCompleted: boolean) => void; }): void; static shouldCallPointerUpInsideOnView(view: UIView, event: MouseEvent | TouchEvent): Promise<boolean>; static shouldCallPointerHoverOnView(view: UIView, event: MouseEvent | TouchEvent): Promise<boolean>; static shouldCallPointerLeaveOnView(view: UIView, event: MouseEvent | TouchEvent): Promise<boolean>; static shouldCallPointerCancelOnView(view: UIView, event: MouseEvent | TouchEvent): Promise<boolean>; shouldCallPointerUpInside(event: MouseEvent | TouchEvent): Promise<boolean>; shouldCallPointerCancel(event: MouseEvent | TouchEvent): Promise<boolean>; shouldCallPointerHover(event: MouseEvent | TouchEvent): Promise<boolean>; shouldCallPointerLeave(event: MouseEvent | TouchEvent): Promise<boolean>; protected _loadUIEvents(): void; static controlEvent: { readonly PointerDown: "PointerDown"; readonly PointerMove: "PointerMove"; readonly PointerDrag: "PointerDrag"; readonly PointerLeave: "PointerLeave"; readonly PointerEnter: "PointerEnter"; readonly PointerUpInside: "PointerUpInside"; readonly PointerTap: "PointerTap"; readonly PointerUp: "PointerUp"; readonly MultipleTouches: "PointerZoom"; readonly PointerCancel: "PointerCancel"; readonly PointerHover: "PointerHover"; readonly EnterDown: "EnterDown"; readonly EnterUp: "EnterUp"; readonly SpaceDown: "SpaceDown"; readonly EscDown: "EscDown"; readonly TabDown: "TabDown"; readonly LeftArrowDown: "LeftArrowDown"; readonly RightArrowDown: "RightArrowDown"; readonly DownArrowDown: "DownArrowDown"; readonly UpArrowDown: "UpArrowDown"; readonly Focus: "Focus"; readonly Blur: "Blur"; }; controlEvent: { readonly PointerDown: "PointerDown"; readonly PointerMove: "PointerMove"; readonly PointerDrag: "PointerDrag"; readonly PointerLeave: "PointerLeave"; readonly PointerEnter: "PointerEnter"; readonly PointerUpInside: "PointerUpInside"; readonly PointerTap: "PointerTap"; readonly PointerUp: "PointerUp"; readonly MultipleTouches: "PointerZoom"; readonly PointerCancel: "PointerCancel"; readonly PointerHover: "PointerHover"; readonly EnterDown: "EnterDown"; readonly EnterUp: "EnterUp"; readonly SpaceDown: "SpaceDown"; readonly EscDown: "EscDown"; readonly TabDown: "TabDown"; readonly LeftArrowDown: "LeftArrowDown"; readonly RightArrowDown: "RightArrowDown"; readonly DownArrowDown: "DownArrowDown"; readonly UpArrowDown: "UpArrowDown"; readonly Focus: "Focus"; readonly Blur: "Blur"; }; get controlEventTargetAccumulator(): UIViewAddControlEventTargetObject<UIView>; addTargetForControlEvents(eventKeys: string[], targetFunction: (sender: UIView, event: Event) => void): void; addTargetForControlEvent(eventKey: string, targetFunction: (sender: UIView, event: Event, callbackControl: { allowConcurrentCalls: () => void; }) => any | Promise<any>): void; removeTargetForControlEvent(eventKey: string, targetFunction: (sender: UIView, event: Event) => void): void; removeTargetForControlEvents(eventKeys: string[], targetFunction: (sender: UIView, event: Event) => void): void; performFunctionAfterBroadcastEvent(event: string | string[], targetFunction: (event: UIViewBroadcastEvent) => void): void; removePerformFunctionAfterBroadcastEvent(event: string | string[], targetFunction: (event: UIViewBroadcastEvent) => void): void; sendControlEventForKey(eventKey: string, nativeEvent?: Event): void; static broadcastEventName: { readonly LanguageChanged: "LanguageChanged"; readonly RemovedFromViewTree: "RemovedFromViewTree"; readonly AddedToViewTree: "AddedToViewTree"; readonly PageDidScroll: "PageDidScroll"; }; broadcastEventInSubtree(event: UIViewBroadcastEvent): void; didReceiveBroadcastEvent(event: UIViewBroadcastEvent): void; forEachViewInSubtree(functionToCall: (view: UIView) => void): void; rectangleInView(rectangle: UIRectangle, view: UIView, forceIfNotInViewTree?: boolean): any; rectangleFromView(rectangle: UIRectangle, view: UIView): any; get contentBounds(): UIRectangle; contentBoundsWithInset(inset: number): UIRectangle; contentBoundsWithInsets(left: number, right: number, bottom: number, top: number): UIRectangle; protected _getIntrinsicSizeCacheKey(constrainingHeight: number, constrainingWidth: number): string; protected _getCachedIntrinsicSize(cacheKey: string): UIRectangle | undefined; protected _setCachedIntrinsicSize(cacheKey: string, size: UIRectangle): void; clearIntrinsicSizeCache(): void; intrinsicContentSizeWithConstraints(constrainingHeight?: number, constrainingWidth?: number): UIRectangle; private _intrinsicFrameFromSubviewFrames; intrinsicContentWidth(constrainingHeight?: number): number; intrinsicContentHeight(constrainingWidth?: number): number; intrinsicContentSize(): UIRectangle; /** * ⚠️ NUCLEAR OPTION — DO NOT CALL IN NORMAL CODE ⚠️ * * Performs a full-scorched-earth cache purge across the entire view subtree rooted * at `this`, then drives an immediate synchronous layout pass, as if the app had * just cold-started. * * What gets destroyed: * • Every per-instance intrinsic-size cache entry (`_intrinsicSizesCache`) * • Every shared intrinsic-size cache bucket (`UIView._sharedIntrinsicSizeCaches`) * • Every frame cache (`_frameCache`) and virtual-layout frame cache * (`_frameCacheForVirtualLayouting`) on every view in the subtree * • Every UITextMeasurement style cache (canvas glyph metrics, * computed-style snapshots, etc.) * * After the purge every view in the subtree is unconditionally marked dirty via * `setNeedsLayout()`, then `UIView.layoutViewsIfNeeded()` is called immediately * to flush the queue rather than waiting for the next rAF tick. * * Legitimate uses (exhaustive list): * - Recovery from a confirmed cache-corruption bug while a proper fix ships * - Test harness reset between isolated rendering assertions * - Post-hot-module-replacement refresh in development tooling * * Do NOT use this to fix a layout glitch you don't understand. * Fix the root cause instead. If you find yourself calling this in production * code for any reason other than the above, that is a bug — file it. * * Complexity: O(n) where n = total views in the subtree. * All shared caches are wiped globally, not just for the subtree. */ performForcedSubtreeLayout(): void; } export {};