@blotch/angular-lynx 22.2.4 → 22.2.6
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/fesm2022/blotch-angular-lynx-arena-Cycr_O6y.mjs +755 -0
- package/fesm2022/blotch-angular-lynx-arena-Cycr_O6y.mjs.map +1 -0
- package/fesm2022/blotch-angular-lynx-const-MkK6UdZ7.mjs +30 -0
- package/fesm2022/blotch-angular-lynx-const-MkK6UdZ7.mjs.map +1 -0
- package/fesm2022/blotch-angular-lynx-drawn-scale-ExB6Yx-p.mjs +9 -0
- package/fesm2022/blotch-angular-lynx-drawn-scale-ExB6Yx-p.mjs.map +1 -0
- package/fesm2022/{blotch-angular-lynx-web-glass-ciDHw7Bw.mjs → blotch-angular-lynx-web-glass-Bmp-vSYh.mjs} +96 -36
- package/fesm2022/blotch-angular-lynx-web-glass-Bmp-vSYh.mjs.map +1 -0
- package/fesm2022/blotch-angular-lynx.mjs +3832 -3583
- package/fesm2022/blotch-angular-lynx.mjs.map +1 -1
- package/package.json +1 -1
- package/types/blotch-angular-lynx.d.ts +251 -58
- package/fesm2022/blotch-angular-lynx-web-glass-ciDHw7Bw.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blotch-angular-lynx-arena-Cycr_O6y.mjs","names":[],"sources":["../../src/lib/gesture/web/touch-point.ts","../../src/lib/gesture/web/fling-trigger.ts","../../src/lib/gesture/web/handlers.ts","../../src/lib/gesture/web/arena.ts"],"sourcesContent":["import { drawnScale } from '../../web-glass/drawn-scale';\n\n/**\n * The phases of a touch, named as Lynx's gesture handlers name them.\n */\nexport type TouchType = 'touchstart' | 'touchmove' | 'touchend' | 'touchcancel';\n\nexport type Point = { readonly x: number; readonly y: number };\n\nexport const ORIGIN: Point = { x: 0, y: 0 };\n\n/**\n * Where a pointer is during a touch, in the fields Lynx's handlers put in a\n * gesture event's params (`eventParamsFromTouchEvent` in\n * LynxBaseGestureHandler.m). Every length is in the page's CSS pixels, so a\n * preview scaled down to fit (as the docs' are) still measures a 10px\n * `maxDistance` as 10 of the page's pixels, and a pan's translation moves an\n * element under the pointer.\n */\nexport type TouchPoint = {\n readonly type: TouchType;\n readonly timestamp: number;\n /**\n * From the top left of the element the touch started on.\n */\n readonly x: number;\n readonly y: number;\n /**\n * From the top left of the page (the `<lynx-view>`).\n */\n readonly pageX: number;\n readonly pageY: number;\n /**\n * The browser's own viewport coordinates, untouched, as web-core keeps them.\n */\n readonly clientX: number;\n readonly clientY: number;\n};\n\n/**\n * The page a Lynx for Web element is drawn in: the `<lynx-view>` whose shadow\n * root holds it, or the document's root element outside one. The host is\n * found by its `host` property, not `instanceof ShadowRoot`: the main thread\n * runs in an iframe, whose ShadowRoot isn't the page's.\n */\nexport const pageOf = (element: Element): Element => {\n const root = element.getRootNode() as Partial<ShadowRoot>;\n return root.host ?? element.ownerDocument.documentElement;\n};\n\n/**\n * Where `event` is, as a point of the touch that started on `target`.\n */\nexport const touchPointOf = (\n event: PointerEvent,\n type: TouchType,\n target: Element,\n): TouchPoint => {\n const page = pageOf(target);\n const scale = drawnScale(page as HTMLElement);\n const pageBox = page.getBoundingClientRect();\n const box = target.getBoundingClientRect();\n return {\n type,\n timestamp: event.timeStamp,\n x: (event.clientX - box.left) / scale,\n y: (event.clientY - box.top) / scale,\n pageX: (event.clientX - pageBox.left) / scale,\n pageY: (event.clientY - pageBox.top) / scale,\n clientX: event.clientX,\n clientY: event.clientY,\n };\n};\n\n/**\n * How far back VelocityTracker looks, in ms. A finger that stopped before\n * lifting has no velocity, and so starts no fling.\n */\nconst VELOCITY_WINDOW = 100;\n\n/**\n * The pointer's velocity at the end of a touch, which decides whether a fling\n * follows. Lynx reads it from a UIPanGestureRecognizer on its root view on\n * iOS (LynxGestureVelocityTracker); this averages the touch's last 100 ms.\n */\nexport class VelocityTracker {\n #samples: { t: number; x: number; y: number }[] = [];\n\n reset(): void {\n this.#samples = [];\n }\n\n add(touch: TouchPoint): void {\n const t = touch.timestamp;\n this.#samples.push({ t, x: touch.pageX, y: touch.pageY });\n this.#samples = this.#samples.filter(\n (sample) => t - sample.t <= VELOCITY_WINDOW,\n );\n }\n\n /**\n * In the page's CSS pixels per second.\n */\n velocity(): Point {\n const first = this.#samples[0];\n const last = this.#samples[this.#samples.length - 1];\n const seconds = first ? (last.t - first.t) / 1000 : 0;\n if (seconds <= 0) return ORIGIN;\n return {\n x: (last.x - first.x) / seconds,\n y: (last.y - first.y) / seconds,\n };\n }\n}\n","import { ORIGIN, type Point } from './touch-point';\n\n/**\n * Below this speed (px/s) in an axis, a release doesn't fling in it; Lynx's\n * kLynxMinVelocity.\n */\nconst MIN_VELOCITY = 50;\n\n/**\n * Releases faster than this (px/s) fling as fast as this; kLynxMaxVelocity.\n */\nconst MAX_VELOCITY = 10000;\n\n/**\n * How much speed a fling keeps each millisecond, as on iOS.\n */\nconst DECELERATION_RATE = 0.998;\n\nconst COEFFICIENT = 1000 * Math.log(DECELERATION_RATE);\n\n/**\n * What the trigger needs from the page's window: its frames and clock.\n */\nexport type FlingFrames = Pick<\n Window,\n 'requestAnimationFrame' | 'cancelAnimationFrame' | 'devicePixelRatio'\n> & { readonly performance: Pick<Performance, 'now'> };\n\nexport type FlingTriggerState = 'start' | 'update' | 'end';\n\n/**\n * Distance traveled after `seconds` of a fling that lasts `duration`.\n */\nconst distanceAt = (\n seconds: number,\n duration: number,\n velocity: number,\n): number => {\n const progress = Math.min(1, seconds / duration);\n return (\n ((DECELERATION_RATE ** (1000 * progress * duration) - 1) / COEFFICIENT) *\n velocity\n );\n};\n\n/**\n * The momentum after a touch: LynxGestureFlingTrigger.m. Started with the\n * release's velocity, it decelerates as a UIScrollView does and reports each\n * frame (`onFrame`) until it moves less than half a device pixel a frame.\n * The arena hands each frame's step to the fling gestures.\n */\nexport class FlingTrigger {\n state: FlingTriggerState = 'end';\n /**\n * How far the fling has gone, and had gone at the frame before.\n */\n distance = ORIGIN;\n lastDistance = ORIGIN;\n\n readonly #onFrame: (trigger: FlingTrigger) => void;\n #frames: FlingFrames | null = null;\n #frame: number | undefined;\n #running = false;\n #begin = 0;\n #velocity = ORIGIN;\n #flingX = false;\n #flingY = false;\n #durationX = 0;\n #durationY = 0;\n\n constructor(onFrame: (trigger: FlingTrigger) => void) {\n this.#onFrame = onFrame;\n }\n\n /**\n * Flings at `velocity` (px/s), unless it's too slow in both axes. Returns\n * whether it did.\n */\n start(velocity: Point, frames: FlingFrames): boolean {\n const clamp = (v: number) =>\n Math.max(Math.min(MAX_VELOCITY, v), -MAX_VELOCITY);\n const v = { x: clamp(velocity.x), y: clamp(velocity.y) };\n const flingX = Math.abs(v.x) >= MIN_VELOCITY;\n const flingY = Math.abs(v.y) >= MIN_VELOCITY;\n if (!flingX && !flingY) return false;\n this.stop();\n // The fling stops once a frame would move it less than half a device\n // pixel: Lynx's threshold, 0.5 / UIScreen.scale.\n const threshold = 0.5 / (frames.devicePixelRatio || 1);\n const duration = (speed: number) =>\n Math.log((-COEFFICIENT * threshold) / Math.abs(speed)) / COEFFICIENT;\n this.#frames = frames;\n this.#velocity = v;\n this.#flingX = flingX;\n this.#flingY = flingY;\n this.#durationX = duration(v.x);\n this.#durationY = duration(v.y);\n this.#begin = frames.performance.now();\n this.distance = ORIGIN;\n this.lastDistance = ORIGIN;\n this.state = 'start';\n this.#running = true;\n this.#frame = frames.requestAnimationFrame(this.#tick);\n return true;\n }\n\n stop(): void {\n this.#running = false;\n if (this.#frame !== undefined) {\n this.#frames?.cancelAnimationFrame(this.#frame);\n }\n this.#frame = undefined;\n }\n\n reset(): void {\n this.stop();\n this.distance = ORIGIN;\n this.lastDistance = ORIGIN;\n this.state = 'end';\n }\n\n isFinished(): boolean {\n return !this.#running;\n }\n\n // A frame's handlers can stop the fling, so it checks after each call.\n readonly #tick = (now: number): void => {\n this.#frame = undefined;\n if (this.state === 'start') {\n this.#onFrame(this);\n if (!this.#running) return;\n this.state = 'update';\n }\n // iOS steps a fixed frame length each tick, at the display's rate; a\n // browser's frame rate varies, so this goes by the clock.\n const seconds = Math.max(0, now - this.#begin) / 1000;\n const done = (fling: boolean, duration: number) =>\n !fling || seconds >= duration;\n const willEnd =\n done(this.#flingX, this.#durationX) &&\n done(this.#flingY, this.#durationY);\n this.distance = {\n x: this.#flingX\n ? distanceAt(seconds, this.#durationX, this.#velocity.x)\n : this.lastDistance.x,\n y: this.#flingY\n ? distanceAt(seconds, this.#durationY, this.#velocity.y)\n : this.lastDistance.y,\n };\n this.#onFrame(this);\n if (!this.#running) return;\n this.lastDistance = this.distance;\n if (willEnd) {\n this.state = 'end';\n this.#onFrame(this);\n this.stop();\n return;\n }\n this.#frame = this.#frames!.requestAnimationFrame(this.#tick);\n };\n}\n","import {\n DEFAULT_GESTURE_MAX_DISTANCE,\n DEFAULT_LONG_PRESS_DURATION,\n DEFAULT_PAN_MIN_DISTANCE,\n DEFAULT_TAP_MAX_DURATION,\n} from '../const';\nimport { GestureType } from '../types';\nimport {\n ORIGIN,\n type Point,\n type TouchPoint,\n type TouchType,\n} from './touch-point';\n\n/**\n * Ports of Lynx's native gesture handlers (platform/darwin/ios/lynx/gesture\n * in lynx-family/lynx: LynxPanGestureHandler.m, LynxTapGestureHandler.m,\n * LynxLongPressGestureHandler.m and LynxFlingGestureHandler.m) for Lynx for\n * Web, whose web-core recognizes no gestures. Each one follows a touch and\n * calls its gesture's callbacks when the native one would, so a gesture\n * behaves in the web preview as it does on iOS, quirks included (a tap's\n * `onEnd` also fires when it fails, a pan's `onEnd` after every touch). The\n * arena (./arena.ts) decides which element's handlers see each touch.\n */\n\n/**\n * A handler's state: LynxGestureHandlerState in LynxBaseGestureHandler.h.\n * The arena compares them, so the order matters: from Fail on, the gesture\n * is out of this touch. An object rather than an enum, whose compiled IIFE\n * native bundles couldn't drop.\n */\nexport const HandlerState = {\n Init: 0,\n Begin: 1,\n Active: 2,\n Fail: 3,\n /**\n * What every failure sets, in Lynx's handlers.\n */\n Cancel: 4,\n /**\n * Ended from code, with the state manager's `end()`.\n */\n End: 5,\n /**\n * Sat this event out, as a pan does a fling's frames.\n */\n Undetermined: 6,\n} as const;\n\nexport type HandlerState = (typeof HandlerState)[keyof typeof HandlerState];\n\n/**\n * The step a fling's last frame passes. Lynx passes `FLT_EPSILON` in both\n * axes for it; this is compared by identity instead.\n */\nexport const FLING_END: Point = { x: Number.EPSILON, y: Number.EPSILON };\n\n/**\n * A gesture as `__SetGestureDetector` registered it.\n */\nexport type WebGestureDetector = {\n readonly id: number;\n readonly type: number;\n /**\n * The gesture's callbacks by name. Only these are called.\n */\n readonly callbacks: ReadonlyMap<string, unknown>;\n readonly config: Readonly<Record<string, unknown>> | undefined;\n readonly waitFor: readonly number[];\n readonly simultaneous: readonly number[];\n readonly continueWith: readonly number[];\n};\n\n/**\n * Calls one of a gesture's callbacks with an event's params.\n */\nexport type Emit = (\n detector: WebGestureDetector,\n name: string,\n params: Record<string, unknown>,\n) => void;\n\n/**\n * Reads a numeric option the way Lynx's iOS handlers do: their defaults hold\n * only when no config is sent at all, and once one is, a key it leaves out\n * reads as 0 (`[nil floatValue]`). See ../const.ts.\n */\nconst option = (\n config: WebGestureDetector['config'],\n key: string,\n fallback: number,\n): number => (config ? Number(config[key]) || 0 : fallback);\n\n/**\n * A touch's params (`eventParamsFromTouchEvent`). None for a fling's frames.\n */\nconst touchParams = (touch: TouchPoint | null): Record<string, unknown> =>\n touch\n ? {\n timestamp: touch.timestamp,\n type: touch.type,\n x: touch.x,\n y: touch.y,\n pageX: touch.pageX,\n pageY: touch.pageY,\n clientX: touch.clientX,\n clientY: touch.clientY,\n }\n : {};\n\n/**\n * What a pan or fling adds about its element's scrolling. A plain view\n * doesn't scroll, and web-core's scroll views aren't arena members, so it's\n * always at rest.\n */\nconst SCROLL_PARAMS = {\n scrollX: 0,\n scrollY: 0,\n isAtStart: false,\n isAtEnd: false,\n};\n\nconst pagePoint = (touch: TouchPoint): Point => ({\n x: touch.pageX,\n y: touch.pageY,\n});\n\n/**\n * Whether `to` is more than `distance` from `from` in either axis.\n */\nconst beyond = (from: Point, to: Point, distance: number): boolean =>\n Math.abs(to.x - from.x) > distance || Math.abs(to.y - from.y) > distance;\n\nexport abstract class WebGestureHandler {\n status: HandlerState = HandlerState.Init;\n readonly #emit: Emit;\n\n constructor(\n readonly detector: WebGestureDetector,\n emit: Emit,\n ) {\n this.#emit = emit;\n }\n\n /**\n * Follows the touch. `touch` is null for a fling's frames, which come with\n * how far the fling moved since the last one (`step`).\n */\n abstract handle(\n type: TouchType | null,\n touch: TouchPoint | null,\n step: Point,\n ): void;\n\n /**\n * Stops the handler's timers, once the arena no longer uses it.\n */\n dispose(): void {}\n\n reset(): void {\n this.status = HandlerState.Init;\n }\n\n begin(): void {\n this.status = HandlerState.Begin;\n }\n\n activate(): void {\n this.status = HandlerState.Active;\n }\n\n fail(): void {\n if (this.status !== HandlerState.End) this.status = HandlerState.Cancel;\n }\n\n end(): void {\n this.status = HandlerState.End;\n }\n\n ignore(): void {\n this.status = HandlerState.Undetermined;\n }\n\n /**\n * `onTouchesDown` and the like, which every element of the touch gets.\n */\n touches(type: TouchType, touch: TouchPoint): void {\n const names: Record<TouchType, string> = {\n touchstart: 'onTouchesDown',\n touchmove: 'onTouchesMove',\n touchend: 'onTouchesUp',\n touchcancel: 'onTouchesCancel',\n };\n this.send(names[type], touchParams(touch));\n }\n\n /**\n * Calls the gesture's callback `name`, when it has one.\n */\n protected send(name: string, params: Record<string, unknown>): void {\n if (this.detector.callbacks.has(name)) {\n this.#emit(this.detector, name, params);\n }\n }\n}\n\n/**\n * LynxPanGestureHandler: begins when the touch does, activates once it has\n * moved more than `minDistance` in either axis, and ends (as a failure) when\n * it lifts. Its `onBegin`, `onStart` and `onEnd` fire once a touch.\n */\nclass PanHandler extends WebGestureHandler {\n readonly #minDistance = option(\n this.detector.config,\n 'minDistance',\n DEFAULT_PAN_MIN_DISTANCE,\n );\n #start = ORIGIN;\n #last = ORIGIN;\n #lastTouch: TouchPoint | null = null;\n #begun = false;\n #started = false;\n #ended = false;\n\n handle(type: TouchType | null, touch: TouchPoint | null): void {\n this.#lastTouch = touch;\n if (!touch) {\n this.ignore();\n return;\n }\n if (this.status >= HandlerState.Fail) return;\n if (type === 'touchstart') {\n this.#start = pagePoint(touch);\n this.#ended = false;\n this.begin();\n this.#onBegin(touch);\n } else if (type === 'touchmove') {\n this.#last = pagePoint(touch);\n if (this.status === HandlerState.Init) {\n this.begin();\n this.#onBegin(touch);\n }\n if (beyond(this.#start, this.#last, this.#minDistance)) {\n this.activate();\n this.#onStart(touch);\n }\n if (this.status === HandlerState.Active) {\n this.send('onUpdate', { ...SCROLL_PARAMS, ...touchParams(touch) });\n }\n } else {\n this.fail();\n }\n }\n\n override fail(): void {\n super.fail();\n this.#onEnd();\n }\n\n override end(): void {\n super.end();\n this.#onEnd();\n }\n\n override reset(): void {\n super.reset();\n this.#begun = false;\n this.#started = false;\n this.#ended = false;\n }\n\n #onBegin(touch: TouchPoint): void {\n if (this.#begun) return;\n this.#begun = true;\n this.send('onBegin', { ...SCROLL_PARAMS, ...touchParams(touch) });\n }\n\n #onStart(touch: TouchPoint): void {\n if (this.#started || !this.#begun) return;\n this.#started = true;\n this.send('onStart', { ...SCROLL_PARAMS, ...touchParams(touch) });\n }\n\n #onEnd(): void {\n if (this.#ended || !this.#begun) return;\n this.#ended = true;\n this.send('onEnd', { ...SCROLL_PARAMS, ...touchParams(this.#lastTouch) });\n }\n}\n\n/**\n * LynxTapGestureHandler: begins when the touch does, fails once it moves\n * more than `maxDistance` in either axis or lasts longer than `maxDuration`,\n * and otherwise activates when it lifts, firing `onStart` then `onEnd`.\n */\nclass TapHandler extends WebGestureHandler {\n readonly #maxDuration = option(\n this.detector.config,\n 'maxDuration',\n DEFAULT_TAP_MAX_DURATION,\n );\n readonly #maxDistance = option(\n this.detector.config,\n 'maxDistance',\n DEFAULT_GESTURE_MAX_DISTANCE,\n );\n #start = ORIGIN;\n #lastTouch: TouchPoint | null = null;\n #timer: ReturnType<typeof setTimeout> | undefined;\n\n handle(type: TouchType | null, touch: TouchPoint | null): void {\n this.#lastTouch = touch;\n if (!touch) {\n this.ignore();\n return;\n }\n if (this.status >= HandlerState.Fail) {\n this.dispose();\n return;\n }\n if (type === 'touchstart') {\n this.#start = pagePoint(touch);\n this.begin();\n this.send('onBegin', touchParams(touch));\n this.#startTimer();\n return;\n }\n if (type === 'touchmove') {\n if (beyond(this.#start, pagePoint(touch), this.#maxDistance)) {\n this.fail();\n }\n return;\n }\n // Lynx counts a canceled touch as a tap. On the web the browser cancels\n // a touch when it scrolls or zooms the page instead, so here it fails, as\n // a tap that turns into a scroll does on iOS and Android.\n if (type === 'touchcancel') {\n this.fail();\n } else {\n this.activate();\n this.send('onStart', touchParams(touch));\n this.send('onEnd', touchParams(touch));\n }\n this.dispose();\n }\n\n override dispose(): void {\n // The main thread's clearTimeout throws for undefined.\n if (this.#timer !== undefined) clearTimeout(this.#timer);\n this.#timer = undefined;\n }\n\n override fail(): void {\n super.fail();\n this.send('onEnd', touchParams(this.#lastTouch));\n }\n\n override end(): void {\n super.end();\n this.send('onEnd', touchParams(this.#lastTouch));\n }\n\n #startTimer(): void {\n this.dispose();\n this.#timer = setTimeout(() => {\n this.#timer = undefined;\n this.fail();\n }, this.#maxDuration);\n }\n}\n\n/**\n * LynxLongPressGestureHandler: begins when the touch does, activates (firing\n * `onStart`) once it has lasted `minDuration` without moving more than\n * `maxDistance`, and ends (as a failure, firing `onEnd`) when it lifts.\n */\nclass LongPressHandler extends WebGestureHandler {\n readonly #minDuration = option(\n this.detector.config,\n 'minDuration',\n DEFAULT_LONG_PRESS_DURATION,\n );\n readonly #maxDistance = option(\n this.detector.config,\n 'maxDistance',\n DEFAULT_GESTURE_MAX_DISTANCE,\n );\n #start = ORIGIN;\n #lastTouch: TouchPoint | null = null;\n #timer: ReturnType<typeof setTimeout> | undefined;\n\n handle(type: TouchType | null, touch: TouchPoint | null): void {\n this.#lastTouch = touch;\n if (!touch) {\n this.ignore();\n return;\n }\n if (this.status >= HandlerState.Fail) {\n this.dispose();\n return;\n }\n if (type === 'touchstart') {\n this.#start = pagePoint(touch);\n this.begin();\n this.send('onBegin', touchParams(touch));\n this.#startTimer();\n } else if (type === 'touchmove') {\n if (\n this.status === HandlerState.Begin &&\n beyond(this.#start, pagePoint(touch), this.#maxDistance)\n ) {\n this.fail();\n this.dispose();\n }\n } else {\n this.dispose();\n this.fail();\n }\n }\n\n override dispose(): void {\n if (this.#timer !== undefined) clearTimeout(this.#timer);\n this.#timer = undefined;\n }\n\n override fail(): void {\n super.fail();\n this.send('onEnd', touchParams(this.#lastTouch));\n }\n\n override end(): void {\n super.end();\n this.send('onEnd', touchParams(this.#lastTouch));\n }\n\n #startTimer(): void {\n this.dispose();\n this.#timer = setTimeout(() => {\n this.#timer = undefined;\n // Lynx activates unless the press failed or is active already, but its\n // failures set Cancel, so a press another gesture beat still activated.\n // Here only one still waiting does.\n if (this.status !== HandlerState.Begin) return;\n this.activate();\n this.send('onStart', touchParams(this.#lastTouch));\n }, this.#minDuration);\n }\n}\n\n/**\n * LynxFlingGestureHandler: the momentum after a touch, driven by the arena's\n * fling trigger. It sits the touch out, begins when the finger lifts, gets\n * each frame's step as `deltaX`/`deltaY` in `onUpdate`, and ends with the\n * fling. Lynx never activates it after a touch, so its `onStart` doesn't fire.\n */\nclass FlingHandler extends WebGestureHandler {\n #begun = false;\n #started = false;\n #ended = false;\n\n handle(type: TouchType | null, touch: TouchPoint | null, step: Point): void {\n if (touch) {\n if (type === 'touchend') {\n this.begin();\n this.#onBegin(ORIGIN);\n } else {\n // Lynx flings after a canceled touch too. The browser cancels one\n // when it takes the touch to scroll, so here that's no fling.\n this.ignore();\n }\n return;\n }\n if (this.status >= HandlerState.Fail && this.status <= HandlerState.End) {\n this.#onEnd(ORIGIN);\n return;\n }\n if (step === FLING_END) {\n this.fail();\n return;\n }\n if (\n this.status === HandlerState.Init ||\n this.status === HandlerState.Undetermined\n ) {\n this.begin();\n this.activate();\n this.#onBegin(ORIGIN);\n this.#onStart(ORIGIN);\n return;\n }\n this.send('onUpdate', this.#params(step));\n }\n\n override fail(): void {\n super.fail();\n this.#onEnd(ORIGIN);\n }\n\n override end(): void {\n super.end();\n this.#onEnd(ORIGIN);\n }\n\n override reset(): void {\n super.reset();\n this.#begun = false;\n this.#started = false;\n this.#ended = false;\n }\n\n #onBegin(step: Point): void {\n // Unlike the pan's, a fling's `begun` is set only when it has an onBegin\n // to call, so without one its onStart and onEnd never fire either.\n if (this.#begun || !this.detector.callbacks.has('onBegin')) return;\n this.#begun = true;\n this.send('onBegin', this.#params(step));\n }\n\n #onStart(step: Point): void {\n if (this.#started || !this.#begun) return;\n this.#started = true;\n this.send('onStart', this.#params(step));\n }\n\n #onEnd(step: Point): void {\n if (this.#ended || !this.#begun) return;\n this.#ended = true;\n this.send('onEnd', this.#params(step));\n }\n\n #params(step: Point): Record<string, unknown> {\n return { ...SCROLL_PARAMS, deltaX: step.x, deltaY: step.y };\n }\n}\n\n/**\n * The handlers for an element's gestures, in the order they see each event:\n * the order of LynxGestureHandlerOption's bits, as Android's map of them\n * iterates. So on a tap in `Gesture.Exclusive(pan, tap)`, the pan's `onEnd`\n * runs before the tap's. Pinch and rotation have no handler on any platform\n * (see investigations/lynx-vs-web-differences.md), and a native gesture is a\n * pan to Lynx.\n */\nexport const createHandlers = (\n detectors: Iterable<WebGestureDetector>,\n emit: Emit,\n): WebGestureHandler[] => {\n // Built here, not at the top of the module, so that native bundles, which\n // never call this, drop it with the classes.\n const handlers: [\n GestureType,\n new (detector: WebGestureDetector, emit: Emit) => WebGestureHandler,\n ][] = [\n [GestureType.PAN, PanHandler],\n [GestureType.FLING, FlingHandler],\n [GestureType.TAP, TapHandler],\n [GestureType.LONGPRESS, LongPressHandler],\n [GestureType.NATIVE, PanHandler],\n ];\n const all = [...detectors];\n return handlers.flatMap(([type, Handler]) =>\n all\n .filter((detector) => detector.type === type)\n .map((detector) => new Handler(detector, emit)),\n );\n};\n","import type { ElementRef } from '../../types/lynx';\nimport { GestureType } from '../types';\nimport { FlingTrigger } from './fling-trigger';\nimport {\n createHandlers,\n FLING_END,\n HandlerState,\n type WebGestureDetector,\n type WebGestureHandler,\n} from './handlers';\nimport {\n ORIGIN,\n type Point,\n type TouchPoint,\n type TouchType,\n touchPointOf,\n VelocityTracker,\n} from './touch-point';\n\n/**\n * Gestures on Lynx for Web.\n *\n * `@lynx-js/web-core` recognizes no gestures: its `__SetGestureDetector` and\n * `__RemoveGestureDetector` do nothing, and it has no `__SetGestureState` or\n * `__ConsumeGesture`. So `[lynxGesture]` did nothing in the web preview. This\n * is Lynx's own gesture arena, ported from its iOS sources\n * (LynxGestureArenaManager.m, LynxGestureHandlerTrigger.m and\n * LynxGestureDetectorManager.m in lynx-family/lynx) and fed with the\n * browser's pointer events instead of UITouches, so that gestures compete,\n * wait for each other and fling in the preview as they do on a phone.\n *\n * How the arena works, as on iOS:\n * - Every element with a gesture is a member. When a touch starts, the\n * members it's on, from the innermost out, form its chain, and the first\n * of them is the winner: only the winner's gestures (and those set to\n * recognize simultaneously with them) see the touch.\n * - When all of the winner's gestures fail, the next member in the chain\n * gets its gestures reset and becomes the winner, and is handed the event.\n * `waitFor` an ancestor's gesture moves that ancestor ahead in the chain.\n * - On one element, gestures race: the first to activate fails the others,\n * except those it's simultaneous with. `waitFor` between gestures of the\n * same element changes nothing, on iOS as here.\n * - When the touch lifts fast enough, a fling follows, whose frames go to the\n * winner's fling gestures.\n *\n * The browser can't see Lynx's arena, so it would scroll the page under a\n * pan, or select the text a mouse drags over. While a pan of the touch is\n * still in play, its `touchmove`s, text selections and drags are canceled\n * (see #panning).\n */\n\n/**\n * Lynx's state codes for `__SetGestureState` (LynxGestureState).\n */\nconst FAIL_STATE = 2;\nconst END_STATE = 3;\n\nconst POINTER_EVENTS = ['pointermove', 'pointerup', 'pointercancel'];\n\n/**\n * What a mouse drag does in a browser by default, which a pan's drag mustn't:\n * select text, or drag it (or an image) out, which also cancels the pointer.\n */\nconst DRAG_EVENTS = ['selectstart', 'dragstart'];\n\ntype Member = {\n readonly element: HTMLElement;\n /**\n * By gesture ID, in the order they were set.\n */\n readonly detectors: Map<number, WebGestureDetector>;\n /**\n * Built when a touch first needs them, and again after `detectors` change.\n */\n handlers: WebGestureHandler[] | null;\n};\n\n/**\n * The pointer the arena is following. Lynx follows one finger at a time.\n */\ntype Pointer = {\n readonly id: number;\n /**\n * The element the touch started on, which `x`/`y` are measured from.\n */\n readonly target: Element;\n readonly document: Document;\n};\n\nexport class WebGestureArena {\n readonly #afterDispatch: () => void;\n /**\n * By element: the native ref, which on the web is the DOM element.\n */\n readonly #members = new Map<object, Member>();\n /**\n * Which members have each gesture, for the relations between them.\n */\n readonly #gestureMembers = new Map<number, Set<Member>>();\n readonly #velocity = new VelocityTracker();\n readonly #flinger = new FlingTrigger(() => this.#onFling());\n #pointer: Pointer | null = null;\n /**\n * The members the touch is on, innermost first.\n */\n #bubble: Member[] = [];\n /**\n * The order the members compete in.\n */\n #chain: Member[] = [];\n #winner: Member | null = null;\n #lastWinner: Member | null = null;\n #lastFlingWinner: Member | null = null;\n #simultaneousMembers = new Set<Member>();\n #simultaneousIds = new Set<number>();\n\n /**\n * @param afterDispatch Runs after each gesture callback. Callbacks run\n * from pointer events, outside Angular's change detection, so whatever\n * they change in the element tree needs committing, as an event\n * handler's changes do (see LynxElement's #dispatch).\n */\n constructor(afterDispatch: () => void) {\n this.#afterDispatch = afterDispatch;\n }\n\n /**\n * `__SetGestureDetector`: adds a gesture to an element, or replaces it.\n */\n setDetector(\n node: ElementRef,\n id: number,\n type: number,\n config: {\n callbacks: Array<{ name: string; callback: unknown }>;\n config?: Record<string, unknown>;\n },\n relations: Record<string, number[]>,\n ): void {\n let member = this.#members.get(node);\n if (!member) {\n const element = node as HTMLElement;\n member = { element, detectors: new Map(), handlers: null };\n this.#members.set(node, member);\n element.addEventListener('pointerdown', this.#onPointerDown);\n // Not passive, or it couldn't cancel the move (see #onTouchMove).\n element.addEventListener('touchmove', this.#onTouchMove, {\n passive: false,\n });\n }\n member.detectors.set(id, {\n id,\n type,\n config: config.config,\n callbacks: new Map(\n config.callbacks.map(({ name, callback }) => [name, callback]),\n ),\n waitFor: relations['waitFor'] ?? [],\n simultaneous: relations['simultaneous'] ?? [],\n continueWith: relations['continueWith'] ?? [],\n });\n this.#invalidate(member);\n let members = this.#gestureMembers.get(id);\n if (!members) {\n members = new Set();\n this.#gestureMembers.set(id, members);\n }\n members.add(member);\n }\n\n /**\n * `__RemoveGestureDetector`.\n */\n removeDetector(node: ElementRef, id: number): void {\n const member = this.#members.get(node);\n if (!member?.detectors.delete(id)) return;\n this.#invalidate(member);\n const members = this.#gestureMembers.get(id)!;\n members.delete(member);\n if (!members.size) this.#gestureMembers.delete(id);\n if (member.detectors.size) return;\n this.#members.delete(node);\n member.element.removeEventListener('pointerdown', this.#onPointerDown);\n member.element.removeEventListener('touchmove', this.#onTouchMove);\n }\n\n /**\n * `__SetGestureState`, from a callback's state manager. As on iOS, failing\n * or ending a gesture takes effect at once, and activating one does nothing\n * (LynxGestureHandlerTrigger's handleGestureDetectorState ignores it).\n */\n setState(node: ElementRef, id: number, state: number): void {\n const handler = this.#members\n .get(node)\n ?.handlers?.find((other) => other.detector.id === id);\n if (state === FAIL_STATE) handler?.fail();\n else if (state === END_STATE) handler?.end();\n }\n\n /**\n * A member's gestures changed: its handlers start over.\n */\n #invalidate(member: Member): void {\n for (const handler of member.handlers ?? []) handler.dispose();\n member.handlers = null;\n }\n\n #handlersOf(member: Member): WebGestureHandler[] {\n member.handlers ??= createHandlers(member.detectors.values(), this.#emit);\n return member.handlers;\n }\n\n /**\n * Calls a gesture's callback with Lynx's event envelope, whose data is\n * under `params` (see mapGestureEvent). A callback that throws mustn't\n * leave the arena halfway through an event, so its error is thrown again\n * once the event is done.\n */\n readonly #emit = (\n detector: WebGestureDetector,\n name: string,\n params: Record<string, unknown>,\n ): void => {\n // LynxGestureDetector registers each callback as `{ _fn }` (see its\n // NativeCallbackEntry).\n const callback = detector.callbacks.get(name) as {\n _fn?: (event: unknown) => void;\n };\n try {\n callback._fn?.({ type: name, timestamp: Date.now(), params });\n } catch (error) {\n queueMicrotask(() => {\n throw error;\n });\n }\n this.#afterDispatch();\n };\n\n // Pointer events\n\n readonly #onPointerDown = (event: PointerEvent): void => {\n // The first member the event bubbles to takes the touch for all of them,\n // so the others see a pointer already followed. A second finger, or a\n // mouse's other buttons, aren't followed.\n if (this.#pointer || event.button > 0) return;\n const bubble = [\n ...new Set(\n event\n .composedPath()\n .map((node) => this.#members.get(node))\n .filter((member) => member !== undefined),\n ),\n ];\n const target = event.target as Element;\n const { ownerDocument } = target;\n this.#pointer = { id: event.pointerId, target, document: ownerDocument };\n // On the document, and capturing, so that the touch is followed off the\n // element and past listeners that stop the events.\n for (const type of POINTER_EVENTS) {\n ownerDocument.addEventListener(type, this.#onPointer, true);\n }\n for (const type of DRAG_EVENTS) {\n ownerDocument.addEventListener(type, this.#onDrag, true);\n }\n // A new touch stops a fling still running. Lynx stops it only when the\n // touch is on the flinging element, but it then gives both touches the\n // same handlers.\n this.#flinger.stop();\n this.#bubble = bubble;\n this.#chain = this.#competeChain(bubble);\n const [winner] = this.#chain;\n this.#winner = winner;\n this.#lastWinner = winner;\n this.#updateSimultaneous(winner);\n this.#velocity.reset();\n this.#touch(event, 'touchstart');\n };\n\n /**\n * `pointermove`, `pointerup` and `pointercancel`, on the whole document.\n */\n readonly #onPointer = (domEvent: Event): void => {\n const event = domEvent as PointerEvent;\n if (event.pointerId !== this.#pointer?.id) return;\n if (event.type === 'pointermove') {\n this.#touch(event, 'touchmove');\n return;\n }\n const { document } = this.#pointer;\n for (const type of POINTER_EVENTS) {\n document.removeEventListener(type, this.#onPointer, true);\n }\n for (const type of DRAG_EVENTS) {\n document.removeEventListener(type, this.#onDrag, true);\n }\n try {\n this.#touch(\n event,\n event.type === 'pointerup' ? 'touchend' : 'touchcancel',\n );\n } finally {\n this.#pointer = null;\n }\n };\n\n /**\n * Keeps the browser from scrolling while a pan could still take the touch,\n * as Lynx's arena keeps a scroll view from moving under a pan on iOS.\n */\n readonly #onTouchMove = (event: Event): void => {\n if (this.#pointer && event.cancelable && this.#panning()) {\n event.preventDefault();\n }\n };\n\n /**\n * Keeps a mouse drag from selecting or dragging out what's under a pan.\n */\n readonly #onDrag = (event: Event): void => {\n if (this.#panning()) event.preventDefault();\n };\n\n /**\n * Whether a pan of the touch is still in play.\n */\n #panning(): boolean {\n const members = [...this.#chain, ...this.#simultaneousMembers];\n return members.some((member) =>\n this.#handlersOf(member).some(\n (handler) =>\n handler.detector.type === GestureType.PAN &&\n handler.status < HandlerState.Fail,\n ),\n );\n }\n\n #touch(event: PointerEvent, type: TouchType): void {\n const touch = touchPointOf(event, type, this.#pointer!.target);\n this.#velocity.add(touch);\n // As on iOS, every member of the touch hears of it first\n // (`onTouchesDown` and the like), then the arena resolves it.\n if (this.#winner) {\n for (const member of this.#bubble) {\n for (const handler of this.#handlersOf(member)) {\n handler.touches(type, touch);\n }\n }\n }\n this.#resolve(type, touch);\n }\n\n // LynxGestureHandlerTrigger\n\n /**\n * resolveTouchEvent: hands a touch to the winner, which may change.\n */\n #resolve(type: TouchType, touch: TouchPoint): void {\n if (type === 'touchstart') {\n for (const member of this.#chain) this.#reset(member);\n this.#dispatch(type, touch, this.#winner, ORIGIN, true);\n this.#findNextWinner(type, touch);\n return;\n }\n if (type === 'touchmove') {\n this.#winner = this.#reCompete(this.#winner);\n if (this.#winner === this.#lastWinner) {\n this.#dispatch(type, touch, this.#winner, ORIGIN, false);\n }\n this.#findNextWinner(type, touch);\n return;\n }\n this.#dispatch(type, touch, this.#winner, ORIGIN, true);\n // A touch the browser canceled has no momentum to follow.\n const velocity = type === 'touchend' ? this.#velocity.velocity() : ORIGIN;\n const view = this.#pointer!.document.defaultView!;\n if (this.#winner && this.#flinger.start(velocity, view)) {\n this.#lastFlingWinner = this.#lastWinner;\n return;\n }\n this.#flinger.reset();\n this.#dispatch(null, null, this.#winner, FLING_END, true);\n if (this.#winner) this.#onFling();\n }\n\n /**\n * handleFling: one of the fling's frames.\n */\n #onFling(): void {\n const { distance, lastDistance, state } = this.#flinger;\n const step = {\n x: lastDistance.x - distance.x,\n y: lastDistance.y - distance.y,\n };\n this.#lastFlingWinner = this.#reCompete(this.#lastFlingWinner);\n this.#findNextWinner(null, null);\n if (!this.#winner || !this.#lastFlingWinner) {\n this.#lastFlingWinner = null;\n this.#flinger.stop();\n return;\n }\n if (state === 'end') {\n this.#dispatch(null, null, this.#lastFlingWinner, FLING_END, true);\n } else {\n this.#dispatch(null, null, this.#lastFlingWinner, step, false);\n }\n }\n\n /**\n * dispatchUIEventWithSimultaneousAndReCompete: hands an event to a\n * member's handlers and to the members simultaneous with the winner, then\n * (when `reCompete`) lets the winner change.\n */\n #dispatch(\n type: TouchType | null,\n touch: TouchPoint | null,\n member: Member | null,\n step: Point,\n reCompete: boolean,\n ): void {\n if (!member) return;\n for (const handler of this.#handlersOf(member)) {\n handler.handle(type, touch, step);\n }\n for (const other of this.#simultaneousMembers) {\n for (const handler of this.#handlersOf(other)) {\n handler.handle(type, touch, step);\n }\n }\n if (reCompete) this.#winner = this.#reCompete(this.#winner);\n }\n\n /**\n * findNextWinnerInBegin: while the winner keeps changing, hands the event\n * to each new one.\n */\n #findNextWinner(type: TouchType | null, touch: TouchPoint | null): void {\n for (let i = 0; i < this.#chain.length; i++) {\n if (this.#winner === this.#lastWinner || !this.#winner) return;\n this.#lastWinner = this.#winner;\n this.#updateSimultaneous(this.#winner);\n this.#dispatch(type, touch, this.#winner, ORIGIN, true);\n }\n this.#dispatch(type, touch, this.#lastWinner, ORIGIN, true);\n }\n\n /**\n * reCompeteByGestures: the winner while its gestures are still in play,\n * else the next member of the chain (after it, then before it) whose\n * gestures are, after resetting them. None once a gesture was ended from\n * code.\n */\n #reCompete(current: Member | null): Member | null {\n if (!current && !this.#lastWinner) return null;\n let lost = false;\n if (!current) {\n // On iOS, a touch whose winner lost goes back to it on the next event,\n // with its gestures reset.\n current = this.#lastWinner!;\n lost = true;\n this.#resetWithSimultaneous(current);\n }\n const state = this.#stateOf(current);\n if (state <= HandlerState.Active) return current;\n if (state === HandlerState.End || lost) return null;\n // The winner is always in the chain (it starts as its first member, and\n // only ever changes to another), so Lynx's checks for one that isn't, or\n // is in it twice, aren't needed.\n const index = this.#chain.indexOf(current);\n const next = [\n ...this.#chain.slice(index + 1),\n ...this.#chain.slice(0, index),\n ];\n // Lynx checks each member's state twice, before and after resetting it,\n // and gives up on one that was ended. The first check stays for what it\n // does (it resets an ended member and forgets the last winner), which is\n // also why the second never sees the member ended.\n return (\n next.find((member) => {\n this.#stateOf(member);\n this.#resetWithSimultaneous(member);\n return this.#stateOf(member) <= HandlerState.Active;\n }) ?? null\n );\n }\n\n /**\n * getCurrentMemberState: End when one of its gestures was ended (which\n * resets them all), Active when one is active (which fails the others it\n * isn't simultaneous with), else the least advanced of their states.\n */\n #stateOf(member: Member): HandlerState {\n const handlers = this.#handlersOf(member);\n if (!handlers.length) return HandlerState.Fail;\n let least: HandlerState = HandlerState.Undetermined;\n for (const handler of handlers) {\n if (handler.status === HandlerState.End) {\n this.#resetWithSimultaneous(member);\n this.#lastWinner = null;\n return HandlerState.End;\n }\n if (handler.status === HandlerState.Active) {\n for (const other of handlers) {\n if (\n other !== handler &&\n !this.#simultaneousIds.has(other.detector.id)\n ) {\n other.fail();\n }\n }\n return HandlerState.Active;\n }\n least = Math.min(least, handler.status) as HandlerState;\n }\n return least;\n }\n\n #reset(member: Member): void {\n for (const handler of this.#handlersOf(member)) handler.reset();\n }\n\n #resetWithSimultaneous(member: Member): void {\n this.#reset(member);\n for (const other of this.#simultaneousMembers) this.#reset(other);\n }\n\n // LynxGestureDetectorManager\n\n /**\n * convertResponseChainToCompeteChain: the touch's members in the order\n * they compete. A member whose first related gesture waits for an\n * ancestor's goes after that ancestor (and the chain ends there); one that\n * continues with other gestures is followed by their members only.\n */\n #competeChain(bubble: Member[]): Member[] {\n const chain: Member[] = [];\n for (const [index, member] of bubble.entries()) {\n // Lynx reads the relations of one gesture: the first, by ID, with any.\n const related = [...member.detectors.values()]\n .sort((a, b) => a.id - b.id)\n .find(\n (detector) => detector.waitFor.length + detector.continueWith.length,\n );\n const ahead = related\n ? this.#membersOf(related.waitFor).filter((other) =>\n bubble.slice(index + 1).includes(other),\n )\n : [];\n chain.push(...ahead, member);\n // A gesture waiting for no other member of the touch keeps none of its\n // relations, on iOS: not even its continueWith.\n if (related?.waitFor.length && !ahead.length) continue;\n if (related?.continueWith.length) {\n chain.push(...this.#membersOf(related.continueWith));\n break;\n }\n if (ahead.length) break;\n }\n return [...new Set(chain)];\n }\n\n #membersOf(ids: readonly number[]): Member[] {\n return [\n ...new Set(\n ids.flatMap((id) => [...(this.#gestureMembers.get(id) ?? [])]),\n ),\n ];\n }\n\n /**\n * handleSimultaneousWinner: the other members whose gestures the winner's\n * recognize simultaneously with, and which of the winner's own gestures\n * are simultaneous (and so don't fail each other).\n */\n #updateSimultaneous(winner: Member): void {\n this.#simultaneousMembers = new Set();\n this.#simultaneousIds = new Set();\n for (const detector of winner.detectors.values()) {\n for (const id of detector.simultaneous) {\n if (winner.detectors.has(id)) {\n this.#simultaneousIds.add(id);\n continue;\n }\n // The winner isn't among them: it would have had the gesture.\n for (const member of this.#gestureMembers.get(id) ?? []) {\n this.#simultaneousMembers.add(member);\n }\n }\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
var GestureType;
|
|
2
|
+
(function(GestureType) {
|
|
3
|
+
GestureType[GestureType["PAN"] = 0] = "PAN";
|
|
4
|
+
GestureType[GestureType["FLING"] = 1] = "FLING";
|
|
5
|
+
GestureType[GestureType["DEFAULT"] = 2] = "DEFAULT";
|
|
6
|
+
GestureType[GestureType["TAP"] = 3] = "TAP";
|
|
7
|
+
GestureType[GestureType["LONGPRESS"] = 4] = "LONGPRESS";
|
|
8
|
+
GestureType[GestureType["ROTATION"] = 5] = "ROTATION";
|
|
9
|
+
GestureType[GestureType["PINCH"] = 6] = "PINCH";
|
|
10
|
+
GestureType[GestureType["NATIVE"] = 7] = "NATIVE";
|
|
11
|
+
})(GestureType || (GestureType = {}));
|
|
12
|
+
var GestureState;
|
|
13
|
+
(function(GestureState) {
|
|
14
|
+
GestureState[GestureState["UNDETERMINED"] = 0] = "UNDETERMINED";
|
|
15
|
+
GestureState[GestureState["BEGAN"] = 1] = "BEGAN";
|
|
16
|
+
GestureState[GestureState["ACTIVE"] = 2] = "ACTIVE";
|
|
17
|
+
GestureState[GestureState["END"] = 3] = "END";
|
|
18
|
+
GestureState[GestureState["FAILED"] = 4] = "FAILED";
|
|
19
|
+
GestureState[GestureState["CANCELED"] = 5] = "CANCELED";
|
|
20
|
+
})(GestureState || (GestureState = {}));
|
|
21
|
+
var FlingDirection;
|
|
22
|
+
(function(FlingDirection) {
|
|
23
|
+
FlingDirection[FlingDirection["RIGHT"] = 1] = "RIGHT";
|
|
24
|
+
FlingDirection[FlingDirection["LEFT"] = 2] = "LEFT";
|
|
25
|
+
FlingDirection[FlingDirection["UP"] = 4] = "UP";
|
|
26
|
+
FlingDirection[FlingDirection["DOWN"] = 8] = "DOWN";
|
|
27
|
+
})(FlingDirection || (FlingDirection = {}));
|
|
28
|
+
export { GestureState as n, GestureType as r, FlingDirection as t };
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=blotch-angular-lynx-const-MkK6UdZ7.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blotch-angular-lynx-const-MkK6UdZ7.mjs","names":[],"sources":["../../src/lib/gesture/types.ts","../../src/lib/gesture/const.ts"],"sourcesContent":["/**\n * Gesture type IDs matching the Lynx native engine (from @lynx-js/react gesture-runtime).\n */\nexport enum GestureType {\n PAN = 0,\n FLING = 1,\n DEFAULT = 2,\n TAP = 3,\n LONGPRESS = 4,\n ROTATION = 5,\n PINCH = 6,\n NATIVE = 7,\n}\n\nexport enum GestureState {\n UNDETERMINED = 0,\n BEGAN = 1,\n ACTIVE = 2,\n END = 3,\n FAILED = 4,\n CANCELED = 5,\n}\n\nexport type GestureEvent = {\n /**\n * Optional: the native engine conveys the phase through the callback that\n * fires (onBegin/onStart/onUpdate/onEnd), not a field on the event payload,\n * so `state` is usually absent at runtime.\n */\n state?: GestureState;\n /**\n * Absolute (page-relative) coordinates, derived at runtime by mapGestureEvent\n * from the native `pageX`/`pageY`. Optional (was required) because they're\n * only present when the gesture's payload carries coordinates — making the\n * type promise a `number` that isn't always there was a latent footgun.\n */\n absoluteX?: number;\n absoluteY?: number;\n /**\n * The untouched native params dict (Lynx field names: scrollX, pageX, x, y,\n * isAtStart, …). Escape hatch for reading fields that have no flat alias —\n * the flat properties above are derived from it. See mapGestureEvent.\n */\n params?: Record<string, unknown>;\n};\n\nexport type PanGestureEvent = {\n translationX: number;\n translationY: number;\n velocityX: number;\n velocityY: number;\n} & GestureEvent;\n\nexport type TapGestureEvent = {\n x: number;\n y: number;\n} & GestureEvent;\n\nexport type FlingGestureEvent = {\n velocityX: number;\n velocityY: number;\n} & GestureEvent;\n\nexport type PinchGestureEvent = {\n scale: number;\n velocity: number;\n} & GestureEvent;\n\nexport type RotationGestureEvent = {\n rotation: number;\n velocity: number;\n} & GestureEvent;\n\nexport type LongPressGestureEvent = {\n x: number;\n y: number;\n duration: number;\n} & GestureEvent;\n\n/**\n * Bitmask values — combine with bitwise OR for multi-direction fling:\n * e.g. FlingDirection.LEFT | FlingDirection.RIGHT for horizontal fling.\n */\nexport enum FlingDirection {\n RIGHT = 1,\n LEFT = 2,\n UP = 4,\n DOWN = 8,\n}\n\nexport type GestureCallback<TEvent extends GestureEvent = GestureEvent> = (\n event: TEvent,\n) => void;\n","/**\n * Default gesture config values, mirrored from Lynx's own gesture-runtime\n * (https://github.com/lynx-family/lynx-stack/blob/main/packages/lynx/gesture-runtime/src/utils/const.ts).\n *\n * Why these defaults live in the JS gesture classes instead of relying on the\n * native handlers' own defaults: the iOS handlers read each config key with\n * `[[config objectForKey:@\"maxDuration\"] floatValue]`, and `[nil floatValue]`\n * is `0`. Those built-in defaults (`maxDistance = 10`, etc.) are only applied\n * when the WHOLE config is absent (`handleConfigMap` early-returns on a nil\n * map). The moment ANY config key is present, every *missing* key reads back as\n * `0` — so sending a partial config like `{ numberOfTaps: 2 }` silently zeroes\n * `maxDuration`/`maxDistance` and the tap fails on a 0ms timer the instant a\n * finger touches down. Initializing the full default set on every gesture\n * guarantees a complete config is always sent. See LynxTapGestureHandler.m /\n * LynxLongPressGestureHandler.m `handleConfigMap`.\n */\n\n/** Max finger travel (px) before a tap/long-press is rejected as a drag. */\nexport const DEFAULT_GESTURE_MAX_DISTANCE = 10;\n\n/** Max time (ms) between finger-down and finger-up for a tap to register. */\nexport const DEFAULT_TAP_MAX_DURATION = 500;\n\n/** Min hold time (ms) before a long-press is recognized. */\nexport const DEFAULT_LONG_PRESS_DURATION = 500;\n\n/** Min finger travel (px) before a pan activates (0 = activates immediately). */\nexport const DEFAULT_PAN_MIN_DISTANCE = 0;\n"],"mappings":"AAGA,IAAY;CAAZ,SAAY,aAAW;CACrB,YAAA,YAAA,SAAA,KAAA;CACA,YAAA,YAAA,WAAA,KAAA;CACA,YAAA,YAAA,aAAA,KAAA;CACA,YAAA,YAAA,SAAA,KAAA;CACA,YAAA,YAAA,eAAA,KAAA;CACA,YAAA,YAAA,cAAA,KAAA;CACA,YAAA,YAAA,WAAA,KAAA;CACA,YAAA,YAAA,YAAA,KAAA;AACF,EAAA,CATY,gBAAA,cAAW,CAAA,EAAA;AAWvB,IAAY;CAAZ,SAAY,cAAY;CACtB,aAAA,aAAA,kBAAA,KAAA;CACA,aAAA,aAAA,WAAA,KAAA;CACA,aAAA,aAAA,YAAA,KAAA;CACA,aAAA,aAAA,SAAA,KAAA;CACA,aAAA,aAAA,YAAA,KAAA;CACA,aAAA,aAAA,cAAA,KAAA;AACF,EAAA,CAPY,iBAAA,eAAY,CAAA,EAAA;AAqExB,IAAY;CAAZ,SAAY,gBAAc;CACxB,eAAA,eAAA,WAAA,KAAA;CACA,eAAA,eAAA,UAAA,KAAA;CACA,eAAA,eAAA,QAAA,KAAA;CACA,eAAA,eAAA,UAAA,KAAA;AACF,EAAA,CALY,mBAAA,iBAAc,CAAA,EAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const drawnScale = (element) => {
|
|
2
|
+
const drawn = element.getBoundingClientRect().width;
|
|
3
|
+
const laidOut = element.offsetWidth;
|
|
4
|
+
if (!laidOut || Math.abs(drawn - laidOut) < 1) return 1;
|
|
5
|
+
return drawn / laidOut;
|
|
6
|
+
};
|
|
7
|
+
export { drawnScale as t };
|
|
8
|
+
|
|
9
|
+
//# sourceMappingURL=blotch-angular-lynx-drawn-scale-ExB6Yx-p.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blotch-angular-lynx-drawn-scale-ExB6Yx-p.mjs","names":[],"sources":["../../src/lib/web-glass/drawn-scale.ts"],"sourcesContent":["/**\n * Screen pixels per CSS pixel that `element` is drawn at: below 1 when a\n * transform on it or an ancestor scales it down, as the docs' previews do to\n * fit a phone-sized page in their pane. getBoundingClientRect() measures in\n * screen pixels, but layout and every CSS length are in CSS pixels, so the\n * glass code divides boxes by this to keep to CSS pixels.\n */\nexport const drawnScale = (element: HTMLElement): number => {\n const drawn = element.getBoundingClientRect().width;\n // A transform doesn't change offsetWidth.\n const laidOut = element.offsetWidth;\n // offsetWidth is rounded to a whole pixel, so a difference of less than one\n // is rounding, not scale.\n if (!laidOut || Math.abs(drawn - laidOut) < 1) return 1;\n return drawn / laidOut;\n};\n"],"mappings":"AAOA,MAAa,cAAc,YAAgC;CACzD,MAAM,QAAQ,QAAQ,sBAAqB,CAAE,CAAC;CAE9C,MAAM,UAAU,QAAQ;CAGxB,IAAI,CAAC,WAAW,KAAK,IAAI,QAAQ,OAAO,IAAI,GAAG,OAAO;CACtD,OAAO,QAAQ;AACjB"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { t as drawnScale } from "./blotch-angular-lynx-drawn-scale-ExB6Yx-p.mjs";
|
|
1
2
|
const BLUR_VIEW_LAYOUT_CSS = ":host{--lynx-display-toggle:var(--lynx-display-linear);--lynx-display-linear:var(--lynx-display-toggle,);--lynx-display-flex:var(--lynx-display-toggle,);--lynx-linear-orientation-toggle:var(--lynx-linear-orientation-vertical);--lynx-linear-orientation-horizontal:var(--lynx-linear-orientation-toggle,);--lynx-linear-orientation-vertical:var(--lynx-linear-orientation-toggle,);--lynx-linear-orientation-horizontal-reverse:var(--lynx-linear-orientation-toggle,);--lynx-linear-orientation-vertical-reverse:var(--lynx-linear-orientation-toggle,);--linear-flex-direction:var(--lynx-linear-orientation-horizontal,row) var(--lynx-linear-orientation-vertical,column) var(--lynx-linear-orientation-horizontal-reverse,row-reverse) var(--lynx-linear-orientation-vertical-reverse,column-reverse);--linear-justify-content:var(--lynx-linear-orientation-horizontal,var(--justify-content-row)) var(--lynx-linear-orientation-vertical,var(--justify-content-column)) var(--lynx-linear-orientation-horizontal-reverse,var(--justify-content-row-reverse)) var(--lynx-linear-orientation-vertical-reverse,var(--justify-content-column-reverse));flex-wrap:var(--lynx-display-linear,nowrap) var(--lynx-display-flex,var(--flex-wrap));flex-direction:var(--lynx-display-linear,var(--linear-flex-direction)) var(--lynx-display-flex,var(--flex-direction));justify-content:var(--lynx-display-linear,var(--linear-justify-content))}slot{--lynx-display:inherit;--lynx-linear-orientation:inherit;--lynx-linear-weight-sum:inherit}";
|
|
2
3
|
const applyBlurViewLayout = (element) => {
|
|
3
4
|
const root = element.shadowRoot;
|
|
@@ -80,12 +81,6 @@ const resolveCssColor = (doc, value) => {
|
|
|
80
81
|
return color;
|
|
81
82
|
};
|
|
82
83
|
const luminance = ([r, g, b]) => .2126 * r + .7152 * g + .0722 * b;
|
|
83
|
-
const drawnScale = (element) => {
|
|
84
|
-
const drawn = element.getBoundingClientRect().width;
|
|
85
|
-
const laidOut = element.offsetWidth;
|
|
86
|
-
if (!laidOut || Math.abs(drawn - laidOut) < 1) return 1;
|
|
87
|
-
return drawn / laidOut;
|
|
88
|
-
};
|
|
89
84
|
const SKIPPED_PROPERTY = /^(--|transition|animation|will-change|cursor|pointer-events|user-select|-webkit-user-)/;
|
|
90
85
|
const ALWAYS_INLINED = /^(border-(top|right|bottom|left)-width|outline-width|column-rule-width)$/;
|
|
91
86
|
const SKIPPED_TAGS = /* @__PURE__ */ new Set([
|
|
@@ -101,11 +96,16 @@ const defaultsByDocument = /* @__PURE__ */ new WeakMap();
|
|
|
101
96
|
const defaultStyle = (doc, win) => {
|
|
102
97
|
let defaults = defaultsByDocument.get(doc);
|
|
103
98
|
if (!defaults) {
|
|
99
|
+
const host = doc.createElement("div");
|
|
100
|
+
const root = host.attachShadow({ mode: "open" });
|
|
101
|
+
const reset = doc.createElement("style");
|
|
102
|
+
reset.textContent = ":host{all:initial}";
|
|
104
103
|
const probe = doc.createElement("div");
|
|
105
|
-
|
|
104
|
+
root.append(reset, probe);
|
|
105
|
+
doc.documentElement.appendChild(host);
|
|
106
106
|
const style = win.getComputedStyle(probe);
|
|
107
107
|
defaults = new Map(Array.from(style, (name) => [name, style.getPropertyValue(name)]));
|
|
108
|
-
|
|
108
|
+
host.remove();
|
|
109
109
|
defaultsByDocument.set(doc, defaults);
|
|
110
110
|
}
|
|
111
111
|
return defaults;
|
|
@@ -218,10 +218,11 @@ const snapshotBackdrop = async (root, options) => {
|
|
|
218
218
|
const width = (right - left) / zoom;
|
|
219
219
|
const height = (bottom - top) / zoom;
|
|
220
220
|
const scale = Math.min(options.scale, MAX_IMAGE_SIZE / Math.max(width, height));
|
|
221
|
-
const markup = new win.XMLSerializer().serializeToString(cloneBackdrop(root, options));
|
|
222
221
|
const pixelWidth = Math.max(1, Math.round(width * scale));
|
|
223
222
|
const pixelHeight = Math.max(1, Math.round(height * scale));
|
|
224
|
-
const
|
|
223
|
+
const frame = cloneBackdrop(root, options);
|
|
224
|
+
frame.style.cssText += `transform-origin:0 0;transform:scale(${pixelWidth / width},${pixelHeight / height}) translate(${-x}px,${-y}px);`;
|
|
225
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${pixelWidth}" height="${pixelHeight}"><foreignObject width="${pixelWidth}" height="${pixelHeight}">${new win.XMLSerializer().serializeToString(frame)}</foreignObject></svg>`;
|
|
225
226
|
const picture = new win.Image();
|
|
226
227
|
picture.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
|
|
227
228
|
await picture.decode();
|
|
@@ -375,7 +376,13 @@ void main() {
|
|
|
375
376
|
float reach = length(p - u_pointer.xy);
|
|
376
377
|
color += u_pointer.z * 0.2 * exp(-reach * reach / (1.2 * lens * lens));
|
|
377
378
|
|
|
378
|
-
|
|
379
|
+
// Light glass over white is white on white: the frost, the rim and the glow
|
|
380
|
+
// all brighten. UIKit outlines it with a gray hairline, about two device
|
|
381
|
+
// pixels deep, and so does this. The dark material keeps its bright rim.
|
|
382
|
+
float hairline = (1.0 - smoothstep(0.0, 2.5, depth * u_scale)) * (1.0 - u_dark);
|
|
383
|
+
color = clamp(color, 0.0, 1.0) * (1.0 - 0.32 * hairline);
|
|
384
|
+
|
|
385
|
+
gl_FragColor = vec4(color * coverage, coverage + shadow * (1.0 - coverage));
|
|
379
386
|
}`;
|
|
380
387
|
const compile = (gl, fragmentSource) => {
|
|
381
388
|
const program = gl.createProgram();
|
|
@@ -645,18 +652,46 @@ const readShape = (element, origin, zoom, elapsed, grow) => {
|
|
|
645
652
|
};
|
|
646
653
|
};
|
|
647
654
|
const CAPTURE_MARGIN = 48;
|
|
655
|
+
const CONTAINER_MARGIN = 32;
|
|
648
656
|
const MIN_CAPTURE_INTERVAL = 100;
|
|
649
657
|
const MAX_SCALE = 2;
|
|
650
658
|
const DARK_LUMINANCE = .4;
|
|
659
|
+
const isFixed = (win, element) => win.getComputedStyle(element).position === "fixed";
|
|
651
660
|
const backdropOf = (root) => {
|
|
652
661
|
const win = root.ownerDocument.defaultView;
|
|
662
|
+
let layer = isFixed(win, root) ? root : null;
|
|
653
663
|
let element = root.parentElement;
|
|
654
664
|
while (element?.parentElement) {
|
|
655
|
-
|
|
656
|
-
|
|
665
|
+
if (!layer) {
|
|
666
|
+
const style = win.getComputedStyle(element);
|
|
667
|
+
if (style.backgroundImage && style.backgroundImage !== "none" || parseNormalizedColor(style.backgroundColor)[3] >= 1) return {
|
|
668
|
+
element,
|
|
669
|
+
layer
|
|
670
|
+
};
|
|
671
|
+
if (style.position === "fixed") layer = element;
|
|
672
|
+
}
|
|
657
673
|
element = element.parentElement;
|
|
658
674
|
}
|
|
659
|
-
return element
|
|
675
|
+
return element && {
|
|
676
|
+
element,
|
|
677
|
+
layer
|
|
678
|
+
};
|
|
679
|
+
};
|
|
680
|
+
const drawnAbove = (root, { element: backdrop, layer }) => {
|
|
681
|
+
const win = root.ownerDocument.defaultView;
|
|
682
|
+
if (layer) {
|
|
683
|
+
const zIndex = (element) => parseInt(win.getComputedStyle(element).zIndex, 10) || 0;
|
|
684
|
+
const layerZ = zIndex(layer);
|
|
685
|
+
return (element) => {
|
|
686
|
+
if (element === layer) return true;
|
|
687
|
+
if (element.contains(layer) || !isFixed(win, element)) return false;
|
|
688
|
+
const z = zIndex(element);
|
|
689
|
+
return z > layerZ || z === layerZ && (layer.compareDocumentPosition(element) & 4) !== 0;
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
const above = /* @__PURE__ */ new Set([root]);
|
|
693
|
+
for (let node = root; node && node !== backdrop; node = node.parentElement) for (let sibling = node.nextElementSibling; sibling; sibling = sibling.nextElementSibling) above.add(sibling);
|
|
694
|
+
return (element) => above.has(element);
|
|
660
695
|
};
|
|
661
696
|
var GlassGroup = class {
|
|
662
697
|
root;
|
|
@@ -665,6 +700,7 @@ var GlassGroup = class {
|
|
|
665
700
|
#canvas;
|
|
666
701
|
#renderer;
|
|
667
702
|
#backdrop;
|
|
703
|
+
#margin;
|
|
668
704
|
#stopObserving = null;
|
|
669
705
|
#snapshot = null;
|
|
670
706
|
#stale = true;
|
|
@@ -680,12 +716,14 @@ var GlassGroup = class {
|
|
|
680
716
|
this.#win = doc.defaultView;
|
|
681
717
|
this.#canvas = doc.createElement("canvas");
|
|
682
718
|
this.#canvas.setAttribute("aria-hidden", "true");
|
|
683
|
-
this.#
|
|
719
|
+
this.#margin = container ? CONTAINER_MARGIN : 0;
|
|
720
|
+
const size = `calc(100% + ${2 * this.#margin}px)`;
|
|
721
|
+
this.#canvas.style.cssText = `position:absolute;left:${-this.#margin}px;top:${-this.#margin}px;width:${size};height:${size};pointer-events:none;z-index:-1;opacity:0;transition:opacity 0.2s`;
|
|
684
722
|
const shadow = root.shadowRoot;
|
|
685
723
|
shadow?.insertBefore(this.#canvas, shadow.querySelector("slot"));
|
|
686
724
|
this.#renderer = shadow ? GlassRenderer.create(this.#canvas) : null;
|
|
687
725
|
this.#backdrop = backdropOf(root);
|
|
688
|
-
if (this.#renderer && this.#backdrop) this.#stopObserving = observeBackdrop(this.#backdrop, root, () => {
|
|
726
|
+
if (this.#renderer && this.#backdrop) this.#stopObserving = observeBackdrop(this.#backdrop.element, this.#backdrop.layer ?? root, () => {
|
|
689
727
|
this.#stale = true;
|
|
690
728
|
});
|
|
691
729
|
}
|
|
@@ -700,7 +738,7 @@ var GlassGroup = class {
|
|
|
700
738
|
}
|
|
701
739
|
frame(elapsed) {
|
|
702
740
|
const renderer = this.#renderer;
|
|
703
|
-
const backdrop = this.#backdrop;
|
|
741
|
+
const backdrop = this.#backdrop?.element;
|
|
704
742
|
if (!renderer || !backdrop || this.#failed) return;
|
|
705
743
|
const origin = this.#canvas.getBoundingClientRect();
|
|
706
744
|
const area = backdrop.getBoundingClientRect();
|
|
@@ -710,14 +748,14 @@ var GlassGroup = class {
|
|
|
710
748
|
if (!snapshot) return;
|
|
711
749
|
const scale = Math.min(this.#win.devicePixelRatio || 1, MAX_SCALE) * zoom;
|
|
712
750
|
const elements = this.shapes;
|
|
713
|
-
const
|
|
751
|
+
const read = elements.map((element) => readShape(element, origin, zoom, elapsed, this.container));
|
|
714
752
|
let pointer = [
|
|
715
753
|
0,
|
|
716
754
|
0,
|
|
717
755
|
0
|
|
718
756
|
];
|
|
719
757
|
elements.forEach((element, i) => {
|
|
720
|
-
const value =
|
|
758
|
+
const value = read[i].press;
|
|
721
759
|
if (value > pointer[2]) {
|
|
722
760
|
const { x, y } = pressOf(element);
|
|
723
761
|
pointer = [
|
|
@@ -727,6 +765,7 @@ var GlassGroup = class {
|
|
|
727
765
|
];
|
|
728
766
|
}
|
|
729
767
|
});
|
|
768
|
+
const shapes = read.filter((shape) => shape.halfWidth >= .5 && shape.halfHeight >= .5);
|
|
730
769
|
const spacing = parseFloat(this.root.getAttribute("spacing") ?? "") || 0;
|
|
731
770
|
const frame = {
|
|
732
771
|
scale,
|
|
@@ -778,11 +817,9 @@ var GlassGroup = class {
|
|
|
778
817
|
this.#capturing = true;
|
|
779
818
|
this.#stale = false;
|
|
780
819
|
const started = win.performance.now();
|
|
781
|
-
const
|
|
782
|
-
for (let node = this.root; node && node !== backdrop; node = node.parentElement) for (let sibling = node.nextElementSibling; sibling; sibling = sibling.nextElementSibling) above.add(sibling);
|
|
783
|
-
const margin = CAPTURE_MARGIN * zoom;
|
|
820
|
+
const margin = (CAPTURE_MARGIN - this.#margin) * zoom;
|
|
784
821
|
snapshotBackdrop(backdrop, {
|
|
785
|
-
exclude: (
|
|
822
|
+
exclude: drawnAbove(this.root, this.#backdrop),
|
|
786
823
|
region: {
|
|
787
824
|
left: origin.left - margin,
|
|
788
825
|
top: origin.top - margin,
|
|
@@ -809,7 +846,16 @@ const DEFAULT_FALLBACK_BLUR = 12;
|
|
|
809
846
|
const fallbackGlassCss = (element) => {
|
|
810
847
|
const clear = element.getAttribute("glass-style") === "clear";
|
|
811
848
|
const filter = `blur(${parseFloat(element.getAttribute("blur-radius") ?? "") || (clear ? DEFAULT_FALLBACK_BLUR / 3 : DEFAULT_FALLBACK_BLUR)}px) saturate(${clear ? 140 : 180}%)`;
|
|
812
|
-
return `:host{backdrop-filter:${filter};-webkit-backdrop-filter:${filter};background-color:${element.getAttribute("glass-tint-color") || `rgba(255, 255, 255, ${clear ? .04 : .18})`}
|
|
849
|
+
return `:host{backdrop-filter:${filter};-webkit-backdrop-filter:${filter};background-color:${element.getAttribute("glass-tint-color") || `rgba(255, 255, 255, ${clear ? .04 : .18})`}}`;
|
|
850
|
+
};
|
|
851
|
+
const FALLBACK_EDGES = "inset 0 0 0 1px rgba(0, 0, 0, 0.08), inset 0 2px 0 rgba(255, 255, 255, 0.5), inset 0 0 0 2px rgba(255, 255, 255, 0.25)";
|
|
852
|
+
const SMALL_SHADOW_SIDE = 44;
|
|
853
|
+
const glassShadow = (element) => {
|
|
854
|
+
const reach = (Math.min(element.offsetWidth, element.offsetHeight) - SMALL_SHADOW_SIDE) / 116;
|
|
855
|
+
const t = Math.round(Math.min(Math.max(reach, 0), 1) * 10) / 10;
|
|
856
|
+
const mix = (small, large) => Math.round((small + (large - small) * t) * 1e3) / 1e3;
|
|
857
|
+
const shadow = `0 ${mix(2, 8)}px ${mix(10, 32)}px rgba(0, 0, 0, ${mix(.06, .08)})`;
|
|
858
|
+
return t ? `${shadow}, 0 1px 4px rgba(0, 0, 0, ${mix(0, .04)})` : shadow;
|
|
813
859
|
};
|
|
814
860
|
const materialCss = (element) => {
|
|
815
861
|
const effect = element.getAttribute("blur-effect") ?? "light";
|
|
@@ -820,12 +866,18 @@ const materialCss = (element) => {
|
|
|
820
866
|
const filter = `blur(${radius}px) brightness(1.35)`;
|
|
821
867
|
return `:host{backdrop-filter:${filter};-webkit-backdrop-filter:${filter}}`;
|
|
822
868
|
};
|
|
823
|
-
const hostStyleCss = (element, state) =>
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
869
|
+
const hostStyleCss = (element, state) => {
|
|
870
|
+
const shadows = [state.shadow ? glassShadow(element) : "", state.fallback ? FALLBACK_EDGES : ""].filter(Boolean);
|
|
871
|
+
return [
|
|
872
|
+
materialCss(element),
|
|
873
|
+
element.getAttribute("blur-effect") === "glass-container" ? ":host{overflow:visible!important}" : "",
|
|
874
|
+
state.hostsCanvas ? ":host{isolation:isolate}" : "",
|
|
875
|
+
state.rendered ? ":host{backdrop-filter:none!important;-webkit-backdrop-filter:none!important}" : "",
|
|
876
|
+
state.fallback ? fallbackGlassCss(element) : "",
|
|
877
|
+
shadows.length ? `:host{box-shadow:${shadows.join(", ")}}` : "",
|
|
878
|
+
state.scale !== 1 ? `:host{scale:${Math.round(state.scale * 1e3) / 1e3}}` : ""
|
|
879
|
+
].join("");
|
|
880
|
+
};
|
|
829
881
|
const applyHostStyle = (element, state) => {
|
|
830
882
|
const root = element.shadowRoot;
|
|
831
883
|
if (!root) return;
|
|
@@ -937,11 +989,19 @@ var GlassManager = class GlassManager {
|
|
|
937
989
|
if (group.active) rendered.add(shape);
|
|
938
990
|
}
|
|
939
991
|
}
|
|
940
|
-
for (const element of styled)
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
fallback
|
|
944
|
-
|
|
992
|
+
for (const element of styled) {
|
|
993
|
+
const group = this.#groups.get(element);
|
|
994
|
+
const glass = element.getAttribute("blur-effect") === "glass";
|
|
995
|
+
const fallback = glass && !rendered.has(element);
|
|
996
|
+
const lone = group !== void 0 && !group.container;
|
|
997
|
+
applyHostStyle(element, {
|
|
998
|
+
hostsCanvas: group?.active ?? false,
|
|
999
|
+
rendered: rendered.has(element),
|
|
1000
|
+
fallback,
|
|
1001
|
+
shadow: glass && (lone || fallback),
|
|
1002
|
+
scale: lone ? 1 + PRESS_GROWTH * (pressOf(element)?.value ?? 0) : 1
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
945
1005
|
if (this.#groups.size) this.#wake();
|
|
946
1006
|
else this.#last = 0;
|
|
947
1007
|
}
|
|
@@ -958,4 +1018,4 @@ const attachWebGlass = (element) => {
|
|
|
958
1018
|
};
|
|
959
1019
|
export { attachWebGlass };
|
|
960
1020
|
|
|
961
|
-
//# sourceMappingURL=blotch-angular-lynx-web-glass-
|
|
1021
|
+
//# sourceMappingURL=blotch-angular-lynx-web-glass-Bmp-vSYh.mjs.map
|