@nvidia-elements/core 2.10.0 → 2.11.1
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/CHANGELOG.md +17 -0
- package/dist/accordion/accordion2.js +4 -4
- package/dist/alert/alert-group2.js +1 -1
- package/dist/alert/alert2.js +1 -1
- package/dist/avatar/avatar-group2.js +1 -1
- package/dist/avatar/avatar2.js +1 -1
- package/dist/badge/badge2.js +1 -1
- package/dist/breadcrumb/breadcrumb2.js +1 -1
- package/dist/bundles/index.d.ts +176 -43
- package/dist/bundles/index.js +6 -6
- package/dist/button/button2.js +1 -1
- package/dist/button-group/button-group2.js +1 -1
- package/dist/card/card2.js +4 -4
- package/dist/chat-message/chat-message2.js +1 -1
- package/dist/checkbox/checkbox-group2.js +1 -1
- package/dist/checkbox/checkbox2.js +1 -1
- package/dist/color/color2.js +1 -1
- package/dist/combobox/combobox2.js +1 -1
- package/dist/copy-button/copy-button2.js +1 -1
- package/dist/custom-elements-jsx.d.ts +101 -9
- package/dist/custom-elements-vue.d.ts +99 -9
- package/dist/custom-elements.json +1055 -322
- package/dist/data.html.json +51 -3
- package/dist/date/date2.js +1 -1
- package/dist/datetime/datetime2.js +1 -1
- package/dist/dialog/dialog-footer2.js +1 -1
- package/dist/dialog/dialog-header2.js +1 -1
- package/dist/dialog/dialog2.js +1 -1
- package/dist/divider/divider2.js +1 -1
- package/dist/dot/dot2.js +1 -1
- package/dist/drawer/drawer-content2.js +1 -1
- package/dist/drawer/drawer-footer2.js +1 -1
- package/dist/drawer/drawer-header2.js +1 -1
- package/dist/drawer/drawer2.js +1 -1
- package/dist/dropdown/dropdown-footer2.js +1 -1
- package/dist/dropdown/dropdown-header2.js +1 -1
- package/dist/dropdown/dropdown2.js +1 -1
- package/dist/dropdown-group/dropdown-group.js +1 -1
- package/dist/dropzone/dropzone2.js +1 -1
- package/dist/file/file2.js +1 -1
- package/dist/format-bytes/format-bytes2.js +1 -1
- package/dist/format-datetime/format-datetime2.js +1 -1
- package/dist/format-number/format-number2.js +1 -1
- package/dist/format-relative-time/format-relative-time2.js +1 -1
- package/dist/format-truncate/format-truncate2.js +1 -1
- package/dist/forms/control/control2.js +1 -1
- package/dist/forms/control-group/control-group2.js +1 -1
- package/dist/forms/control-message/control-message2.js +1 -1
- package/dist/gauge/gauge2.js +1 -1
- package/dist/grid/cell/cell2.js +1 -1
- package/dist/grid/column/column2.js +1 -1
- package/dist/grid/footer/footer2.js +1 -1
- package/dist/grid/grid2.js +1 -1
- package/dist/grid/header/header2.js +1 -1
- package/dist/grid/placeholder/placeholder2.js +1 -1
- package/dist/grid/row/row2.js +1 -1
- package/dist/icon/icon2.js +2 -2
- package/dist/icon-button/icon-button2.js +1 -1
- package/dist/index.js +1 -1
- package/dist/input/input-group2.js +1 -1
- package/dist/input/input2.js +1 -1
- package/dist/internal/controllers/state-scroll.controller.js +27 -17
- package/dist/internal/controllers/state-scroll.controller.js.map +1 -1
- package/dist/internal/services/global.service.js +1 -1
- package/dist/internal/types/index.d.ts +6 -2
- package/dist/internal/types/index.js.map +1 -1
- package/dist/internal/utils/audit.js +1 -1
- package/dist/internal/utils/audit.js.map +1 -1
- package/dist/logo/logo2.js +1 -1
- package/dist/menu/define.d.ts +2 -1
- package/dist/menu/define.js +3 -2
- package/dist/menu/define.js.map +1 -1
- package/dist/menu/index.d.ts +1 -0
- package/dist/menu/index.js +3 -2
- package/dist/menu/menu-group.d.ts +50 -0
- package/dist/menu/menu-group.js +6 -0
- package/dist/menu/menu-group.js.map +1 -0
- package/dist/menu/menu-group2.js +53 -0
- package/dist/menu/menu-group2.js.map +1 -0
- package/dist/menu/menu-item.d.ts +1 -0
- package/dist/menu/menu-item.js +1 -1
- package/dist/menu/menu-item.js.map +1 -1
- package/dist/menu/menu-item2.js +2 -2
- package/dist/menu/menu-item2.js.map +1 -1
- package/dist/menu/menu.d.ts +6 -1
- package/dist/menu/menu.examples.js.map +1 -1
- package/dist/menu/menu.examples.json +53 -13
- package/dist/menu/menu.global.js +1 -1
- package/dist/menu/menu.global.js.map +1 -1
- package/dist/menu/menu2.js +16 -8
- package/dist/menu/menu2.js.map +1 -1
- package/dist/month/month2.js +1 -1
- package/dist/notification/notification-group2.js +1 -1
- package/dist/notification/notification2.js +1 -1
- package/dist/page/page-panel/page-panel-content2.js +1 -1
- package/dist/page/page-panel/page-panel-footer2.js +1 -1
- package/dist/page/page-panel/page-panel-header2.js +1 -1
- package/dist/page/page-panel/page-panel2.js +1 -1
- package/dist/page/page2.js +1 -1
- package/dist/page-header/page-header2.js +1 -1
- package/dist/page-loader/page-loader2.js +1 -1
- package/dist/pagination/pagination2.js +1 -1
- package/dist/panel/panel2.js +4 -4
- package/dist/password/password2.js +1 -1
- package/dist/preferences-input/preferences-input2.js +1 -1
- package/dist/progress-bar/progress-bar2.js +1 -1
- package/dist/progress-ring/progress-ring2.js +1 -1
- package/dist/progressive-filter-chip/progressive-filter-chip2.js +1 -1
- package/dist/pulse/pulse2.js +1 -1
- package/dist/radio/radio-group2.js +1 -1
- package/dist/radio/radio2.js +1 -1
- package/dist/range/range2.js +1 -1
- package/dist/resize-handle/resize-handle2.js +1 -1
- package/dist/search/search2.js +1 -1
- package/dist/select/select2.js +1 -1
- package/dist/skeleton/skeleton2.js +1 -1
- package/dist/sort-button/sort-button2.js +1 -1
- package/dist/sparkline/sparkline2.js +1 -1
- package/dist/star-rating/star-rating2.js +1 -1
- package/dist/steps/steps.d.ts +3 -3
- package/dist/steps/steps2.js +2 -2
- package/dist/steps/steps2.js.map +1 -1
- package/dist/switch/switch-group2.js +1 -1
- package/dist/switch/switch2.js +1 -1
- package/dist/tabs/tabs-group2.js +1 -1
- package/dist/tabs/tabs2.js +2 -2
- package/dist/tag/tag2.js +1 -1
- package/dist/textarea/textarea2.js +1 -1
- package/dist/time/time2.js +1 -1
- package/dist/toast/toast2.js +1 -1
- package/dist/toggletip/toggletip-footer2.js +1 -1
- package/dist/toggletip/toggletip-header2.js +1 -1
- package/dist/toggletip/toggletip2.js +1 -1
- package/dist/toolbar/toolbar2.js +1 -1
- package/dist/tooltip/tooltip2.js +1 -1
- package/dist/tree/tree-node2.js +1 -1
- package/dist/tree/tree2.js +1 -1
- package/dist/viewport/define.d.ts +2 -1
- package/dist/viewport/define.js +2 -1
- package/dist/viewport/define.js.map +1 -1
- package/dist/viewport/index.d.ts +2 -1
- package/dist/viewport/index.js +2 -1
- package/dist/viewport/viewport-gesture-navigation.controller.d.ts +8 -10
- package/dist/viewport/viewport-gesture-navigation.controller.js +73 -66
- package/dist/viewport/viewport-gesture-navigation.controller.js.map +1 -1
- package/dist/viewport/viewport-gridlines2.js +3 -3
- package/dist/viewport/viewport-gridlines2.js.map +1 -1
- package/dist/viewport/viewport-minimap.d.ts +36 -0
- package/dist/viewport/viewport-minimap.js +6 -0
- package/dist/viewport/viewport-minimap.js.map +1 -0
- package/dist/viewport/viewport-minimap.utils.d.ts +13 -0
- package/dist/viewport/viewport-minimap.utils.js +49 -0
- package/dist/viewport/viewport-minimap.utils.js.map +1 -0
- package/dist/viewport/viewport-minimap2.js +309 -0
- package/dist/viewport/viewport-minimap2.js.map +1 -0
- package/dist/viewport/viewport-navigation.controller.d.ts +7 -13
- package/dist/viewport/viewport-navigation.controller.js +14 -20
- package/dist/viewport/viewport-navigation.controller.js.map +1 -1
- package/dist/viewport/viewport-navigation.types.d.ts +0 -8
- package/dist/viewport/viewport.d.ts +30 -5
- package/dist/viewport/viewport.examples.js.map +1 -1
- package/dist/viewport/viewport.examples.json +15 -6
- package/dist/viewport/viewport.js +1 -1
- package/dist/viewport/viewport.js.map +1 -1
- package/dist/viewport/viewport.types.d.ts +50 -23
- package/dist/viewport/viewport2.js +173 -118
- package/dist/viewport/viewport2.js.map +1 -1
- package/dist/week/week2.js +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viewport-gesture-navigation.controller.js","names":[],"sources":["../../src/viewport/viewport-gesture-navigation.controller.ts"],"sourcesContent":["// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { ReactiveController, ReactiveControllerHost } from 'lit';\nimport {\n GestureController,\n KeyStateController,\n isImmediateRootActiveElement,\n type Gesture,\n type GestureInput,\n type PinchStart,\n type PointerEndGesture,\n type WheelGestureInput\n} from '@nvidia-elements/core/internal';\nimport type { PointerMovementGesture } from '@nvidia-elements/core/internal';\nimport type {\n ViewportPanBehavior,\n ViewportPanEndDetail,\n ViewportPanEndReason,\n ViewportPoint,\n ViewportPointerPanDetail,\n ViewportTransform,\n ViewportWheelPanDetail,\n ViewportZoomDetail\n} from './viewport.types.js';\nimport type { ViewportNavigationEventDelegate } from './viewport-navigation.types.js';\nimport { anchoredTransform, contentPointFromViewport } from './viewport-projection.utils.js';\n\ntype PointerPanDefaultAction = 'apply' | 'skip';\n\ntype PointerPanLifecycle =\n | { readonly state: 'pending' }\n | { readonly state: 'active'; readonly defaultAction: PointerPanDefaultAction }\n | { readonly state: 'suppressed' };\n\ninterface PointerPanSession {\n readonly button: number;\n readonly dragThreshold: number;\n lastTotalDisplacementX: number;\n lastTotalDisplacementY: number;\n lifecycle: PointerPanLifecycle;\n readonly pointerType: string;\n readonly start: ViewportTransform;\n transformStart: ViewportTransform;\n transformStartDisplacementX: number;\n transformStartDisplacementY: number;\n}\n\ninterface PinchContext {\n readonly anchor: ViewportPoint;\n readonly start: ViewportTransform;\n}\n\ntype ViewportGestureNavigationHost = HTMLElement &\n ReactiveControllerHost & {\n readonly _internals: ElementInternals;\n readonly behaviorPan: ViewportPanBehavior;\n readonly behaviorZoom: boolean;\n readonly dragThreshold: number;\n };\n\nexport interface ViewportGestureNavigationDelegate {\n readonly commitTransform: (next: ViewportTransform) => boolean;\n readonly cancelAnimation: () => void;\n readonly clampScale: (value: number) => number;\n readonly clientToViewport: (clientX: number, clientY: number) => ViewportPoint;\n readonly consumeAutoFit: () => void;\n readonly events: ViewportNavigationEventDelegate;\n readonly getTransform: () => ViewportTransform;\n}\n\n/** Coordinates the viewport's opt-in pointer, touch, pinch, and wheel navigation behavior. */\nexport class ViewportGestureNavigationController implements ReactiveController {\n readonly #host: ViewportGestureNavigationHost;\n readonly #delegate: ViewportGestureNavigationDelegate;\n readonly #gestureController: GestureController<PinchContext>;\n readonly #keyStateController: KeyStateController;\n readonly #pointerPanSessions = new Map<number, PointerPanSession>();\n readonly #suppressedClicks = new Map<\n number,\n { eventType: 'auxclick' | 'click'; timer: ReturnType<typeof setTimeout> }\n >();\n #connected = false;\n #clickListenersActive = false;\n #spaceKeyListenerTarget?: Window;\n #nativeInteractionHovered = false;\n #pinchPointerIds?: ReadonlySet<number>;\n\n constructor(host: ViewportGestureNavigationHost, delegate: ViewportGestureNavigationDelegate) {\n this.#host = host;\n this.#delegate = delegate;\n host.addController(this);\n this.#gestureController = new GestureController(host, {\n beginPinch: this.#beginPinch,\n claimPriority: 'descendant-first',\n shouldIgnoreEvent: this.#shouldIgnoreEvent,\n shouldPreventContextMenu: this.#shouldPreventContextMenu,\n shouldReservePointerDown: this.#shouldReservePointerDown,\n suppressNativeDrag: true,\n touchAction: this.#getTouchAction\n });\n this.#keyStateController = new KeyStateController(host, { watchedCodes: ['Space'] });\n }\n\n hostConnected(): void {\n this.#connected = true;\n this.#host.addEventListener('nve-gesture', this.#handleGesture as EventListener);\n this.#host.addEventListener('nve-gesture-input', this.#handleGestureInput as EventListener);\n this.#host.addEventListener('blur', this.#handleBlur);\n this.#host.addEventListener('focus', this.#handleFocus);\n this.#host.addEventListener('pointerover', this.#handlePointerOver as EventListener);\n this.#syncBehaviorPolicy();\n }\n\n hostDisconnected(): void {\n this.#connected = false;\n this.#keyStateController.enabled = false;\n this.#gestureController.target = undefined;\n this.#host.removeEventListener('nve-gesture', this.#handleGesture as EventListener);\n this.#host.removeEventListener('nve-gesture-input', this.#handleGestureInput as EventListener);\n this.#host.removeEventListener('blur', this.#handleBlur);\n this.#host.removeEventListener('focus', this.#handleFocus);\n this.#host.removeEventListener('pointerover', this.#handlePointerOver as EventListener);\n this.#setClickListeners(false);\n this.#setSpaceKeyListener(false);\n this.#clearPointerState();\n }\n\n hostUpdated(): void {\n this.#syncBehaviorPolicy();\n this.#syncPanEligibleState();\n }\n\n /** Rebase in-progress pointer math synchronously after an independent transform change. */\n rebasePointerSessions(): void {\n this.#rebasePointerPans();\n }\n\n #syncBehaviorPolicy(): void {\n const panBehavior = this.#host.behaviorPan;\n const zoomBehavior = this.#host.behaviorZoom;\n const behaviorEnabled = Boolean(panBehavior) || zoomBehavior;\n const handlesClaimedPointers = this.#pointerPanSessions.size > 0;\n this.#keyStateController.enabled = this.#connected && panBehavior === 'space';\n this.#gestureController.target =\n this.#connected && (behaviorEnabled || handlesClaimedPointers) ? this.#host : undefined;\n this.#setSpaceKeyListener(this.#connected && panBehavior === 'space');\n this.#setClickListeners(this.#connected && (Boolean(panBehavior) || this.#suppressedClicks.size > 0));\n this.#syncDisabledPanEligibility(panBehavior);\n }\n\n #syncDisabledPanEligibility(panBehavior: ViewportPanBehavior): void {\n if (panBehavior) return;\n this.#nativeInteractionHovered = false;\n this.#host._internals?.states.delete('pan-eligible');\n }\n\n #handleGestureInput = (event: CustomEvent<GestureInput>): void => {\n const input = event.detail;\n if (input.kind === 'pointerdown') {\n this.#handlePointerDown(input);\n } else if (input.kind === 'pointerend') {\n this.#handlePointerEnd(input);\n } else {\n this.#handleWheel(input);\n }\n };\n\n #handlePointerDown(input: Extract<GestureInput, { kind: 'pointerdown' }>): void {\n const nativeEvent = input.event;\n this.#clearSuppressedClick(nativeEvent.pointerId);\n const isTouch = nativeEvent.pointerType === 'touch';\n const canPan = isTouch ? Boolean(this.#host.behaviorPan) : this.#acceptsPointerPan(nativeEvent);\n const canPinch = isTouch && this.#host.behaviorZoom;\n if (!canPan && !canPinch) return this.#focusSpaceViewportFromUnclaimedPointer(nativeEvent);\n const claimed = input.claim({ kind: canPan ? 'pan' : undefined, pinchCandidate: canPinch });\n if (!claimed) return;\n if (!isTouch) this.#host.focus({ preventScroll: true });\n if (nativeEvent.button === 1) nativeEvent.preventDefault();\n this.#delegate.consumeAutoFit();\n const start = this.#delegate.getTransform();\n this.#pointerPanSessions.set(nativeEvent.pointerId, {\n button: nativeEvent.button,\n dragThreshold: this.#host.dragThreshold,\n lastTotalDisplacementX: 0,\n lastTotalDisplacementY: 0,\n lifecycle: canPan ? { state: 'pending' } : { state: 'suppressed' },\n pointerType: nativeEvent.pointerType,\n start,\n transformStart: start,\n transformStartDisplacementX: 0,\n transformStartDisplacementY: 0\n });\n }\n\n #focusSpaceViewportFromUnclaimedPointer(event: PointerEvent): void {\n if (this.#host.behaviorPan !== 'space' || event.pointerType === 'touch' || event.button !== 0) return;\n this.#host.focus({ preventScroll: true });\n }\n\n #handlePointerEnd(input: PointerEndGesture): void {\n const pointerId = input.event.pointerId;\n const session = this.#pointerPanSessions.get(pointerId);\n const endedPinch = this.#endPinchIfNeeded(pointerId);\n if (session) this.#finishPointerPan(input, session, endedPinch);\n if (session && this.#pointerPanSessions.get(pointerId) === session) this.#pointerPanSessions.delete(pointerId);\n this.#syncPanningState();\n this.#syncBehaviorPolicy();\n }\n\n #endPinchIfNeeded(pointerId: number): boolean {\n if (!this.#pinchPointerIds?.has(pointerId)) return false;\n for (const [candidatePointerId, candidate] of this.#pointerPanSessions) {\n if (candidatePointerId !== pointerId && candidate.pointerType === 'touch') {\n this.#suppressPointerPan(candidate);\n }\n }\n this.#pinchPointerIds = undefined;\n return true;\n }\n\n #finishPointerPan(input: PointerEndGesture, session: PointerPanSession, endedPinch: boolean): void {\n if (session.lifecycle.state !== 'active') return;\n this.#suppressPointerPan(session);\n this.#syncPanningState();\n const interrupted = input.interrupted || endedPinch;\n this.#dispatchPanEnd(input, session, interrupted);\n if (\n !interrupted &&\n this.#getAuthoritativePointerPanLifecycle(input.event.pointerId, session)?.state === 'suppressed'\n ) {\n this.#queueClickSuppression(input.event.pointerId, session);\n }\n }\n\n #handleWheel(input: WheelGestureInput): void {\n if (!isImmediateRootActiveElement(this.#host)) return;\n if (input.event.ctrlKey || input.event.metaKey) {\n this.#handleWheelZoom(input);\n } else {\n this.#handleWheelPan(input);\n }\n }\n\n #handleWheelZoom(input: WheelGestureInput): void {\n if (!this.#host.behaviorZoom || !input.claim()) return;\n this.#delegate.consumeAutoFit();\n this.#delegate.cancelAnimation();\n const start = this.#delegate.getTransform();\n const viewport = this.#delegate.clientToViewport(input.clientX, input.clientY);\n const anchor = contentPointFromViewport(viewport.x, viewport.y, start);\n const factor = this.#gestureController.getWheelZoomFactor(input);\n const next = anchoredTransform(anchor, viewport, this.#delegate.clampScale(start.scale * factor));\n const detail: ViewportZoomDetail = {\n anchor,\n clientX: input.clientX,\n clientY: input.clientY,\n event: input.event,\n factor,\n next,\n source: 'wheel',\n start\n };\n if (this.#delegate.events.dispatchZoom(detail) && this.#delegate.commitTransform(next)) {\n this.rebasePointerSessions();\n }\n }\n\n #handleWheelPan(input: WheelGestureInput): void {\n if (!this.#host.behaviorPan || !input.claim()) return;\n this.#delegate.consumeAutoFit();\n this.#delegate.cancelAnimation();\n const start = this.#delegate.getTransform();\n const next = {\n scale: start.scale,\n x: start.x + input.deltaX / start.scale,\n y: start.y + input.deltaY / start.scale\n };\n const detail: ViewportWheelPanDetail = {\n clientX: input.clientX,\n clientY: input.clientY,\n deltaX: input.deltaX,\n deltaY: input.deltaY,\n event: input.event,\n next,\n source: 'wheel',\n start\n };\n if (this.#delegate.events.dispatchPan(detail) && this.#delegate.commitTransform(next)) {\n this.rebasePointerSessions();\n }\n }\n\n #handleGesture = (event: CustomEvent<Gesture<PinchContext>>): void => {\n const gesture = event.detail;\n if (gesture.kind === 'pinch') {\n this.#handlePinchGesture(gesture);\n } else if (gesture.kind === 'pan') {\n this.#handlePointerPan(gesture);\n }\n };\n\n #handlePointerPan(gesture: PointerMovementGesture & { readonly kind: 'pan' }): void {\n const pointerId = gesture.event.pointerId;\n const session = this.#pointerPanSessions.get(pointerId);\n if (!session || session.lifecycle.state === 'suppressed') return;\n session.lastTotalDisplacementX = gesture.totalDisplacementX;\n session.lastTotalDisplacementY = gesture.totalDisplacementY;\n if (session.lifecycle.state === 'pending' && !this.#activatePendingPan(pointerId, session, gesture)) return;\n const lifecycle = session.lifecycle;\n if (lifecycle.state !== 'active') return;\n this.#delegate.cancelAnimation();\n const displacementX = gesture.totalDisplacementX - session.transformStartDisplacementX;\n const displacementY = gesture.totalDisplacementY - session.transformStartDisplacementY;\n const next = {\n scale: session.transformStart.scale,\n x: session.transformStart.x - displacementX / session.transformStart.scale,\n y: session.transformStart.y - displacementY / session.transformStart.scale\n };\n const detail: ViewportPointerPanDetail = { ...gesture, next, source: 'pointer', start: session.start };\n this.#dispatchPointerPanAndApplyDefault(pointerId, session, detail);\n }\n\n #dispatchPointerPanAndApplyDefault(\n pointerId: number,\n session: PointerPanSession,\n detail: ViewportPointerPanDetail\n ): void {\n const appliesPanDefault = this.#delegate.events.dispatchPan(detail);\n const authoritativeLifecycle = this.#getAuthoritativePointerPanLifecycle(pointerId, session);\n if (\n appliesPanDefault &&\n authoritativeLifecycle?.state === 'active' &&\n authoritativeLifecycle.defaultAction === 'apply'\n ) {\n this.#delegate.commitTransform(detail.next);\n }\n }\n\n #activatePendingPan(pointerId: number, session: PointerPanSession, gesture: PointerMovementGesture): boolean {\n if (session.lifecycle.state !== 'pending') return false;\n const distanceSquared = gesture.totalDisplacementX ** 2 + gesture.totalDisplacementY ** 2;\n if (distanceSquared < session.dragThreshold ** 2) return false;\n const displacementX = gesture.totalDisplacementX - session.transformStartDisplacementX;\n const displacementY = gesture.totalDisplacementY - session.transformStartDisplacementY;\n const next = {\n scale: session.transformStart.scale,\n x: session.transformStart.x - displacementX / session.transformStart.scale,\n y: session.transformStart.y - displacementY / session.transformStart.scale\n };\n const detail: ViewportPointerPanDetail = { ...gesture, next, source: 'pointer', start: session.start };\n const defaultAction: PointerPanDefaultAction = this.#delegate.events.dispatchPanStart(detail) ? 'apply' : 'skip';\n if (this.#getAuthoritativePointerPanLifecycle(pointerId, session)?.state !== 'pending') return false;\n session.lifecycle = { defaultAction, state: 'active' };\n this.#syncPanningState();\n return true;\n }\n\n #getAuthoritativePointerPanLifecycle(pointerId: number, session: PointerPanSession): PointerPanLifecycle | undefined {\n const authoritativeSession = this.#pointerPanSessions.get(pointerId);\n return authoritativeSession === session ? authoritativeSession.lifecycle : undefined;\n }\n\n #handlePinchGesture(gesture: Extract<Gesture<PinchContext>, { kind: 'pinch' }>): void {\n this.#delegate.cancelAnimation();\n this.#interruptPointerPansForPinch(gesture.event);\n const viewport = this.#delegate.clientToViewport(gesture.centerClientX, gesture.centerClientY);\n const nextScale = this.#delegate.clampScale(gesture.context.start.scale * gesture.scale);\n const next = anchoredTransform(gesture.context.anchor, viewport, nextScale);\n const detail: ViewportZoomDetail = {\n anchor: gesture.context.anchor,\n clientX: gesture.centerClientX,\n clientY: gesture.centerClientY,\n event: gesture.event,\n factor: gesture.scale,\n next,\n source: 'pinch',\n start: gesture.context.start\n };\n if (this.#delegate.events.dispatchZoom(detail) && this.#delegate.commitTransform(next)) {\n this.rebasePointerSessions();\n }\n }\n\n #beginPinch = (pinch: PinchStart): { context: PinchContext } | undefined => {\n const start = this.#delegate.getTransform();\n const viewport = this.#delegate.clientToViewport(pinch.startCenterClientX, pinch.startCenterClientY);\n const context: PinchContext = {\n anchor: contentPointFromViewport(viewport.x, viewport.y, start),\n start\n };\n this.#pinchPointerIds = new Set([pinch.firstPointerId, pinch.secondPointerId]);\n return { context };\n };\n\n #interruptPointerPansForPinch(event: PointerEvent): void {\n const pinchPointerIds = this.#pinchPointerIds;\n if (!pinchPointerIds) return;\n for (const pointerId of pinchPointerIds) {\n const session = this.#pointerPanSessions.get(pointerId);\n if (!session) continue;\n this.#interruptPointerPan({ event, reason: 'pinch' }, session);\n }\n this.#syncPanningState();\n }\n\n #interruptPointerPan(\n end: { readonly event: PointerEvent; readonly reason: ViewportPanEndReason },\n session: PointerPanSession\n ): void {\n const wasActive = session.lifecycle.state === 'active';\n this.#suppressPointerPan(session);\n if (wasActive) this.#dispatchPanEnd(end, session, true);\n }\n\n #suppressPointerPan(session: PointerPanSession): void {\n if (session.lifecycle.state === 'suppressed') return;\n session.lifecycle = { state: 'suppressed' };\n }\n\n #dispatchPanEnd(\n end: { readonly event: PointerEvent; readonly reason: ViewportPanEndReason },\n session: PointerPanSession,\n interrupted: boolean\n ): void {\n const detail: ViewportPanEndDetail = {\n event: end.event,\n interrupted,\n reason: end.reason,\n source: 'pointer',\n start: session.start,\n transform: this.#delegate.getTransform()\n };\n this.#delegate.events.dispatchPanEnd(detail);\n }\n\n #handleSpaceKeyDown = (event: KeyboardEvent): void => {\n if (event.code !== 'Space' || this.#host.behaviorPan !== 'space') return;\n if (!isImmediateRootActiveElement(this.#host)) return;\n event.preventDefault();\n };\n\n #handleFocus = (): void => {\n this.#syncPanEligibleState();\n };\n\n #handleBlur = (): void => {\n this.#host._internals.states.delete('pan-eligible');\n };\n\n #handlePointerOver = (event: PointerEvent): void => {\n if (!this.#host.behaviorPan) return;\n this.#nativeInteractionHovered = eventTargetsNativeInteraction(event, this.#host);\n this.#syncPanEligibleState();\n };\n\n #acceptsPointerPan(event: PointerEvent): boolean {\n if (event.button === 0) {\n return this.#host.behaviorPan === true || (this.#host.behaviorPan === 'space' && this.#isSpacePanEligible());\n }\n if (event.button === 1) return Boolean(this.#host.behaviorPan);\n return false;\n }\n\n #isSpacePanEligible(): boolean {\n return this.#keyStateController.isPressed('Space') && isImmediateRootActiveElement(this.#host);\n }\n\n #shouldIgnoreEvent = (event: Event): boolean => {\n if (event.type === 'dragstart' && !this.#host.behaviorPan) return true;\n return eventTargetsNativeInteraction(event, this.#host);\n };\n\n #shouldPreventContextMenu = (event: MouseEvent): boolean => {\n if (!event.ctrlKey) return false;\n return this.#host.behaviorPan === true || (this.#host.behaviorPan === 'space' && this.#isSpacePanEligible());\n };\n\n #shouldReservePointerDown = (event: PointerEvent): boolean =>\n this.#host.behaviorPan === 'space' &&\n this.#isSpacePanEligible() &&\n (event.pointerType === 'mouse' || event.pointerType === 'pen') &&\n event.button === 0;\n\n #getTouchAction = (): string | undefined =>\n this.#host.behaviorPan || this.#host.behaviorZoom || this.#pointerPanSessions.size > 0 ? 'none' : undefined;\n\n #rebasePointerPans(): void {\n const transformStart = this.#delegate.getTransform();\n for (const session of this.#pointerPanSessions.values()) {\n this.#rebaseEligiblePointerPan(session, transformStart);\n }\n }\n\n #rebaseEligiblePointerPan(session: PointerPanSession, transformStart: ViewportTransform): void {\n if (session.lifecycle.state === 'suppressed') return;\n session.transformStart = transformStart;\n session.transformStartDisplacementX = session.lastTotalDisplacementX;\n session.transformStartDisplacementY = session.lastTotalDisplacementY;\n }\n\n #handleClickCapture = (event: PointerEvent): void => {\n const suppression = this.#suppressedClicks.get(event.pointerId);\n if (!suppression || suppression.eventType !== event.type) return;\n this.#clearSuppressedClick(event.pointerId);\n this.#syncBehaviorPolicy();\n event.preventDefault();\n event.stopImmediatePropagation();\n };\n\n #queueClickSuppression(pointerId: number, session: PointerPanSession): void {\n const eventType = session.pointerType === 'touch' || session.button === 0 ? 'click' : 'auxclick';\n this.#clearSuppressedClick(pointerId);\n const timer = globalThis.setTimeout(() => {\n this.#clearSuppressedClick(pointerId);\n this.#syncBehaviorPolicy();\n });\n this.#suppressedClicks.set(pointerId, { eventType, timer });\n this.#setClickListeners(true);\n }\n\n #clearSuppressedClick(pointerId: number): void {\n const suppression = this.#suppressedClicks.get(pointerId);\n if (!suppression) return;\n clearTimeout(suppression.timer);\n this.#suppressedClicks.delete(pointerId);\n }\n\n #clearPointerState(): void {\n this.#pointerPanSessions.clear();\n this.#pinchPointerIds = undefined;\n for (const pointerId of this.#suppressedClicks.keys()) this.#clearSuppressedClick(pointerId);\n this.#syncPanningState();\n }\n\n #syncPanningState(): void {\n const panning = [...this.#pointerPanSessions.values()].some(session => session.lifecycle.state === 'active');\n if (panning) this.#host._internals.states.add('panning');\n else this.#host._internals.states.delete('panning');\n }\n\n #syncPanEligibleState(): void {\n const eligible =\n !this.#nativeInteractionHovered &&\n (this.#host.behaviorPan === true || (this.#host.behaviorPan === 'space' && this.#isSpacePanEligible()));\n if (eligible) this.#host._internals.states.add('pan-eligible');\n else this.#host._internals.states.delete('pan-eligible');\n }\n\n #setSpaceKeyListener(enabled: boolean): void {\n const target = enabled ? (this.#host.ownerDocument.defaultView ?? undefined) : undefined;\n if (target === this.#spaceKeyListenerTarget) return;\n this.#spaceKeyListenerTarget?.removeEventListener('keydown', this.#handleSpaceKeyDown as EventListener);\n this.#spaceKeyListenerTarget = target;\n this.#spaceKeyListenerTarget?.addEventListener('keydown', this.#handleSpaceKeyDown as EventListener);\n }\n\n #setClickListeners(enabled: boolean): void {\n if (enabled === this.#clickListenersActive) return;\n this.#clickListenersActive = enabled;\n if (enabled) {\n this.#host.addEventListener('click', this.#handleClickCapture as EventListener, true);\n this.#host.addEventListener('auxclick', this.#handleClickCapture as EventListener, true);\n } else {\n this.#host.removeEventListener('click', this.#handleClickCapture as EventListener, true);\n this.#host.removeEventListener('auxclick', this.#handleClickCapture as EventListener, true);\n }\n }\n}\n\nfunction eventTargetsNativeInteraction(event: Event, boundary: Element): boolean {\n for (const target of event.composedPath()) {\n if (target === boundary) break;\n if (!(target instanceof Element)) continue;\n if (isNativeInteractionElement(target)) return true;\n }\n return false;\n}\n\nfunction isNativeInteractionElement(element: Element): boolean {\n return (\n element.matches('button, input, textarea, select, a[href], [contenteditable], [tabindex]') ||\n isFormAssociatedCustomElement(element)\n );\n}\n\nfunction isFormAssociatedCustomElement(element: Element): boolean {\n if (!element.localName.includes('-')) return false;\n return (element.constructor as typeof HTMLElement & { formAssociated?: boolean }).formAssociated === true;\n}\n"],"mappings":";;;;;AAwEA,IAAa,IAAb,MAA+E;CAC7E;CACA;CACA;CACA;CACA,qBAA+B,IAAI,IAA+B;CAClE,qBAA6B,IAAI,IAG/B;CACF,KAAa;CACb,KAAwB;CACxB;CACA,KAA4B;CAC5B;CAEA,YAAY,GAAqC,GAA6C;EAa5F,AAZA,KAAK,KAAQ,GACb,KAAK,KAAY,GACjB,EAAK,cAAc,IAAI,GACvB,KAAK,KAAqB,IAAI,EAAkB,GAAM;GACpD,YAAY,KAAK;GACjB,eAAe;GACf,mBAAmB,KAAK;GACxB,0BAA0B,KAAK;GAC/B,0BAA0B,KAAK;GAC/B,oBAAoB;GACpB,aAAa,KAAK;EACpB,CAAC,GACD,KAAK,KAAsB,IAAI,EAAmB,GAAM,EAAE,cAAc,CAAC,OAAO,EAAE,CAAC;CACrF;CAEA,gBAAsB;EAOpB,AANA,KAAK,KAAa,IAClB,KAAK,GAAM,iBAAiB,eAAe,KAAK,EAA+B,GAC/E,KAAK,GAAM,iBAAiB,qBAAqB,KAAK,EAAoC,GAC1F,KAAK,GAAM,iBAAiB,QAAQ,KAAK,EAAW,GACpD,KAAK,GAAM,iBAAiB,SAAS,KAAK,EAAY,GACtD,KAAK,GAAM,iBAAiB,eAAe,KAAK,EAAmC,GACnF,KAAK,GAAoB;CAC3B;CAEA,mBAAyB;EAWvB,AAVA,KAAK,KAAa,IAClB,KAAK,GAAoB,UAAU,IACnC,KAAK,GAAmB,SAAS,KAAA,GACjC,KAAK,GAAM,oBAAoB,eAAe,KAAK,EAA+B,GAClF,KAAK,GAAM,oBAAoB,qBAAqB,KAAK,EAAoC,GAC7F,KAAK,GAAM,oBAAoB,QAAQ,KAAK,EAAW,GACvD,KAAK,GAAM,oBAAoB,SAAS,KAAK,EAAY,GACzD,KAAK,GAAM,oBAAoB,eAAe,KAAK,EAAmC,GACtF,KAAK,GAAmB,EAAK,GAC7B,KAAK,GAAqB,EAAK,GAC/B,KAAK,GAAmB;CAC1B;CAEA,cAAoB;EAElB,AADA,KAAK,GAAoB,GACzB,KAAK,GAAsB;CAC7B;CAGA,wBAA8B;EAC5B,KAAK,GAAmB;CAC1B;CAEA,KAA4B;EAC1B,IAAM,IAAc,KAAK,GAAM,aACzB,IAAe,KAAK,GAAM,cAC1B,IAAkB,EAAQ,KAAgB,GAC1C,IAAyB,KAAK,GAAoB,OAAO;EAM/D,AALA,KAAK,GAAoB,UAAU,KAAK,MAAc,MAAgB,SACtE,KAAK,GAAmB,SACtB,KAAK,OAAe,KAAmB,KAA0B,KAAK,KAAQ,KAAA,GAChF,KAAK,GAAqB,KAAK,MAAc,MAAgB,OAAO,GACpE,KAAK,GAAmB,KAAK,OAAe,EAAQ,KAAgB,KAAK,GAAkB,OAAO,EAAE,GACpG,KAAK,GAA4B,CAAW;CAC9C;CAEA,GAA4B,GAAwC;EAC9D,MACJ,KAAK,KAA4B,IACjC,KAAK,GAAM,YAAY,OAAO,OAAO,cAAc;CACrD;CAEA,MAAuB,MAA2C;EAChE,IAAM,IAAQ,EAAM;EACpB,AAAI,EAAM,SAAS,gBACjB,KAAK,GAAmB,CAAK,IACpB,EAAM,SAAS,eACxB,KAAK,GAAkB,CAAK,IAE5B,KAAK,GAAa,CAAK;CAE3B;CAEA,GAAmB,GAA6D;EAC9E,IAAM,IAAc,EAAM;EAC1B,KAAK,GAAsB,EAAY,SAAS;EAChD,IAAM,IAAU,EAAY,gBAAgB,SACtC,IAAS,IAAU,EAAQ,KAAK,GAAM,cAAe,KAAK,GAAmB,CAAW,GACxF,IAAW,KAAW,KAAK,GAAM;EACvC,IAAI,CAAC,KAAU,CAAC,GAAU,OAAO,KAAK,GAAwC,CAAW;EAEzF,IAAI,CADY,EAAM,MAAM;GAAE,MAAM,IAAS,QAAQ,KAAA;GAAW,gBAAgB;EAAS,CACpF,GAAS;EAGd,AAFK,KAAS,KAAK,GAAM,MAAM,EAAE,eAAe,GAAK,CAAC,GAClD,EAAY,WAAW,KAAG,EAAY,eAAe,GACzD,KAAK,GAAU,eAAe;EAC9B,IAAM,IAAQ,KAAK,GAAU,aAAa;EAC1C,KAAK,GAAoB,IAAI,EAAY,WAAW;GAClD,QAAQ,EAAY;GACpB,eAAe,KAAK,GAAM;GAC1B,wBAAwB;GACxB,wBAAwB;GACxB,WAAW,IAAS,EAAE,OAAO,UAAU,IAAI,EAAE,OAAO,aAAa;GACjE,aAAa,EAAY;GACzB;GACA,gBAAgB;GAChB,6BAA6B;GAC7B,6BAA6B;EAC/B,CAAC;CACH;CAEA,GAAwC,GAA2B;EAC7D,KAAK,GAAM,gBAAgB,WAAW,EAAM,gBAAgB,WAAW,EAAM,WAAW,KAC5F,KAAK,GAAM,MAAM,EAAE,eAAe,GAAK,CAAC;CAC1C;CAEA,GAAkB,GAAgC;EAChD,IAAM,IAAY,EAAM,MAAM,WACxB,IAAU,KAAK,GAAoB,IAAI,CAAS,GAChD,IAAa,KAAK,GAAkB,CAAS;EAInD,AAHI,KAAS,KAAK,GAAkB,GAAO,GAAS,CAAU,GAC1D,KAAW,KAAK,GAAoB,IAAI,CAAS,MAAM,KAAS,KAAK,GAAoB,OAAO,CAAS,GAC7G,KAAK,GAAkB,GACvB,KAAK,GAAoB;CAC3B;CAEA,GAAkB,GAA4B;EAC5C,IAAI,CAAC,KAAK,IAAkB,IAAI,CAAS,GAAG,OAAO;EACnD,KAAK,IAAM,CAAC,GAAoB,MAAc,KAAK,IACjD,AAAI,MAAuB,KAAa,EAAU,gBAAgB,WAChE,KAAK,GAAoB,CAAS;EAItC,OADA,KAAK,KAAmB,KAAA,GACjB;CACT;CAEA,GAAkB,GAA0B,GAA4B,GAA2B;EACjG,IAAI,EAAQ,UAAU,UAAU,UAAU;EAE1C,AADA,KAAK,GAAoB,CAAO,GAChC,KAAK,GAAkB;EACvB,IAAM,IAAc,EAAM,eAAe;EAEzC,AADA,KAAK,GAAgB,GAAO,GAAS,CAAW,GAE9C,CAAC,KACD,KAAK,GAAqC,EAAM,MAAM,WAAW,CAAO,CAAC,EAAE,UAAU,gBAErF,KAAK,GAAuB,EAAM,MAAM,WAAW,CAAO;CAE9D;CAEA,GAAa,GAAgC;EACtC,EAA6B,KAAK,EAAK,MACxC,EAAM,MAAM,WAAW,EAAM,MAAM,UACrC,KAAK,GAAiB,CAAK,IAE3B,KAAK,GAAgB,CAAK;CAE9B;CAEA,GAAiB,GAAgC;EAC/C,IAAI,CAAC,KAAK,GAAM,gBAAgB,CAAC,EAAM,MAAM,GAAG;EAEhD,AADA,KAAK,GAAU,eAAe,GAC9B,KAAK,GAAU,gBAAgB;EAC/B,IAAM,IAAQ,KAAK,GAAU,aAAa,GACpC,IAAW,KAAK,GAAU,iBAAiB,EAAM,SAAS,EAAM,OAAO,GACvE,IAAS,EAAyB,EAAS,GAAG,EAAS,GAAG,CAAK,GAC/D,IAAS,KAAK,GAAmB,mBAAmB,CAAK,GACzD,IAAO,EAAkB,GAAQ,GAAU,KAAK,GAAU,WAAW,EAAM,QAAQ,CAAM,CAAC,GAC1F,IAA6B;GACjC;GACA,SAAS,EAAM;GACf,SAAS,EAAM;GACf,OAAO,EAAM;GACb;GACA;GACA,QAAQ;GACR;EACF;EACA,AAAI,KAAK,GAAU,OAAO,aAAa,CAAM,KAAK,KAAK,GAAU,gBAAgB,CAAI,KACnF,KAAK,sBAAsB;CAE/B;CAEA,GAAgB,GAAgC;EAC9C,IAAI,CAAC,KAAK,GAAM,eAAe,CAAC,EAAM,MAAM,GAAG;EAE/C,AADA,KAAK,GAAU,eAAe,GAC9B,KAAK,GAAU,gBAAgB;EAC/B,IAAM,IAAQ,KAAK,GAAU,aAAa,GACpC,IAAO;GACX,OAAO,EAAM;GACb,GAAG,EAAM,IAAI,EAAM,SAAS,EAAM;GAClC,GAAG,EAAM,IAAI,EAAM,SAAS,EAAM;EACpC,GACM,IAAiC;GACrC,SAAS,EAAM;GACf,SAAS,EAAM;GACf,QAAQ,EAAM;GACd,QAAQ,EAAM;GACd,OAAO,EAAM;GACb;GACA,QAAQ;GACR;EACF;EACA,AAAI,KAAK,GAAU,OAAO,YAAY,CAAM,KAAK,KAAK,GAAU,gBAAgB,CAAI,KAClF,KAAK,sBAAsB;CAE/B;CAEA,MAAkB,MAAoD;EACpE,IAAM,IAAU,EAAM;EACtB,AAAI,EAAQ,SAAS,UACnB,KAAK,GAAoB,CAAO,IACvB,EAAQ,SAAS,SAC1B,KAAK,GAAkB,CAAO;CAElC;CAEA,GAAkB,GAAkE;EAClF,IAAM,IAAY,EAAQ,MAAM,WAC1B,IAAU,KAAK,GAAoB,IAAI,CAAS;EAMtD,IALI,CAAC,KAAW,EAAQ,UAAU,UAAU,iBAC5C,EAAQ,yBAAyB,EAAQ,oBACzC,EAAQ,yBAAyB,EAAQ,oBACrC,EAAQ,UAAU,UAAU,aAAa,CAAC,KAAK,GAAoB,GAAW,GAAS,CAAO,MAChF,EAAQ,UACZ,UAAU,UAAU;EAClC,KAAK,GAAU,gBAAgB;EAC/B,IAAM,IAAgB,EAAQ,qBAAqB,EAAQ,6BACrD,IAAgB,EAAQ,qBAAqB,EAAQ,6BACrD,IAAO;GACX,OAAO,EAAQ,eAAe;GAC9B,GAAG,EAAQ,eAAe,IAAI,IAAgB,EAAQ,eAAe;GACrE,GAAG,EAAQ,eAAe,IAAI,IAAgB,EAAQ,eAAe;EACvE,GACM,IAAmC;GAAE,GAAG;GAAS;GAAM,QAAQ;GAAW,OAAO,EAAQ;EAAM;EACrG,KAAK,GAAmC,GAAW,GAAS,CAAM;CACpE;CAEA,GACE,GACA,GACA,GACM;EACN,IAAM,IAAoB,KAAK,GAAU,OAAO,YAAY,CAAM,GAC5D,IAAyB,KAAK,GAAqC,GAAW,CAAO;EAC3F,AACE,KACA,GAAwB,UAAU,YAClC,EAAuB,kBAAkB,WAEzC,KAAK,GAAU,gBAAgB,EAAO,IAAI;CAE9C;CAEA,GAAoB,GAAmB,GAA4B,GAA0C;EAG3G,IAFI,EAAQ,UAAU,UAAU,aACR,EAAQ,sBAAsB,IAAI,EAAQ,sBAAsB,IAClE,EAAQ,iBAAiB,GAAG,OAAO;EACzD,IAAM,IAAgB,EAAQ,qBAAqB,EAAQ,6BACrD,IAAgB,EAAQ,qBAAqB,EAAQ,6BACrD,IAAO;GACX,OAAO,EAAQ,eAAe;GAC9B,GAAG,EAAQ,eAAe,IAAI,IAAgB,EAAQ,eAAe;GACrE,GAAG,EAAQ,eAAe,IAAI,IAAgB,EAAQ,eAAe;EACvE,GACM,IAAmC;GAAE,GAAG;GAAS;GAAM,QAAQ;GAAW,OAAO,EAAQ;EAAM,GAC/F,IAAyC,KAAK,GAAU,OAAO,iBAAiB,CAAM,IAAI,UAAU;EAI1G,OAHI,KAAK,GAAqC,GAAW,CAAO,CAAC,EAAE,UAAU,cAC7E,EAAQ,YAAY;GAAE;GAAe,OAAO;EAAS,GACrD,KAAK,GAAkB,GAChB;CACT;CAEA,GAAqC,GAAmB,GAA6D;EACnH,IAAM,IAAuB,KAAK,GAAoB,IAAI,CAAS;EACnE,OAAO,MAAyB,IAAU,EAAqB,YAAY,KAAA;CAC7E;CAEA,GAAoB,GAAkE;EAEpF,AADA,KAAK,GAAU,gBAAgB,GAC/B,KAAK,GAA8B,EAAQ,KAAK;EAChD,IAAM,IAAW,KAAK,GAAU,iBAAiB,EAAQ,eAAe,EAAQ,aAAa,GACvF,IAAY,KAAK,GAAU,WAAW,EAAQ,QAAQ,MAAM,QAAQ,EAAQ,KAAK,GACjF,IAAO,EAAkB,EAAQ,QAAQ,QAAQ,GAAU,CAAS,GACpE,IAA6B;GACjC,QAAQ,EAAQ,QAAQ;GACxB,SAAS,EAAQ;GACjB,SAAS,EAAQ;GACjB,OAAO,EAAQ;GACf,QAAQ,EAAQ;GAChB;GACA,QAAQ;GACR,OAAO,EAAQ,QAAQ;EACzB;EACA,AAAI,KAAK,GAAU,OAAO,aAAa,CAAM,KAAK,KAAK,GAAU,gBAAgB,CAAI,KACnF,KAAK,sBAAsB;CAE/B;CAEA,MAAe,MAA6D;EAC1E,IAAM,IAAQ,KAAK,GAAU,aAAa,GACpC,IAAW,KAAK,GAAU,iBAAiB,EAAM,oBAAoB,EAAM,kBAAkB,GAC7F,IAAwB;GAC5B,QAAQ,EAAyB,EAAS,GAAG,EAAS,GAAG,CAAK;GAC9D;EACF;EAEA,OADA,KAAK,qBAAmB,IAAI,IAAI,CAAC,EAAM,gBAAgB,EAAM,eAAe,CAAC,GACtE,EAAE,WAAQ;CACnB;CAEA,GAA8B,GAA2B;EACvD,IAAM,IAAkB,KAAK;EACxB,OACL;QAAK,IAAM,KAAa,GAAiB;IACvC,IAAM,IAAU,KAAK,GAAoB,IAAI,CAAS;IACjD,KACL,KAAK,GAAqB;KAAE;KAAO,QAAQ;IAAQ,GAAG,CAAO;GAC/D;GACA,KAAK,GAAkB;EADvB;CAEF;CAEA,GACE,GACA,GACM;EACN,IAAM,IAAY,EAAQ,UAAU,UAAU;EAE9C,AADA,KAAK,GAAoB,CAAO,GAC5B,KAAW,KAAK,GAAgB,GAAK,GAAS,EAAI;CACxD;CAEA,GAAoB,GAAkC;EAChD,EAAQ,UAAU,UAAU,iBAChC,EAAQ,YAAY,EAAE,OAAO,aAAa;CAC5C;CAEA,GACE,GACA,GACA,GACM;EACN,IAAM,IAA+B;GACnC,OAAO,EAAI;GACX;GACA,QAAQ,EAAI;GACZ,QAAQ;GACR,OAAO,EAAQ;GACf,WAAW,KAAK,GAAU,aAAa;EACzC;EACA,KAAK,GAAU,OAAO,eAAe,CAAM;CAC7C;CAEA,MAAuB,MAA+B;EAChD,EAAM,SAAS,WAAW,KAAK,GAAM,gBAAgB,WACpD,EAA6B,KAAK,EAAK,KAC5C,EAAM,eAAe;CACvB;CAEA,WAA2B;EACzB,KAAK,GAAsB;CAC7B;CAEA,WAA0B;EACxB,KAAK,GAAM,WAAW,OAAO,OAAO,cAAc;CACpD;CAEA,MAAsB,MAA8B;EAC7C,KAAK,GAAM,gBAChB,KAAK,KAA4B,EAA8B,GAAO,KAAK,EAAK,GAChF,KAAK,GAAsB;CAC7B;CAEA,GAAmB,GAA8B;EAK/C,OAJI,EAAM,WAAW,IACZ,KAAK,GAAM,gBAAgB,MAAS,KAAK,GAAM,gBAAgB,WAAW,KAAK,GAAoB,IAExG,EAAM,WAAW,KAAU,EAAQ,KAAK,GAAM;CAEpD;CAEA,KAA+B;EAC7B,OAAO,KAAK,GAAoB,UAAU,OAAO,KAAK,EAA6B,KAAK,EAAK;CAC/F;CAEA,MAAsB,MAChB,EAAM,SAAS,eAAe,CAAC,KAAK,GAAM,eACvC,EAA8B,GAAO,KAAK,EAAK;CAGxD,MAA6B,MACtB,EAAM,UACJ,KAAK,GAAM,gBAAgB,MAAS,KAAK,GAAM,gBAAgB,WAAW,KAAK,GAAoB,IAD/E;CAI7B,MAA6B,MAC3B,KAAK,GAAM,gBAAgB,WAC3B,KAAK,GAAoB,MACxB,EAAM,gBAAgB,WAAW,EAAM,gBAAgB,UACxD,EAAM,WAAW;CAEnB,WACE,KAAK,GAAM,eAAe,KAAK,GAAM,gBAAgB,KAAK,GAAoB,OAAO,IAAI,SAAS,KAAA;CAEpG,KAA2B;EACzB,IAAM,IAAiB,KAAK,GAAU,aAAa;EACnD,KAAK,IAAM,KAAW,KAAK,GAAoB,OAAO,GACpD,KAAK,GAA0B,GAAS,CAAc;CAE1D;CAEA,GAA0B,GAA4B,GAAyC;EACzF,EAAQ,UAAU,UAAU,iBAChC,EAAQ,iBAAiB,GACzB,EAAQ,8BAA8B,EAAQ,wBAC9C,EAAQ,8BAA8B,EAAQ;CAChD;CAEA,MAAuB,MAA8B;EACnD,IAAM,IAAc,KAAK,GAAkB,IAAI,EAAM,SAAS;EAC1D,CAAC,KAAe,EAAY,cAAc,EAAM,SACpD,KAAK,GAAsB,EAAM,SAAS,GAC1C,KAAK,GAAoB,GACzB,EAAM,eAAe,GACrB,EAAM,yBAAyB;CACjC;CAEA,GAAuB,GAAmB,GAAkC;EAC1E,IAAM,IAAY,EAAQ,gBAAgB,WAAW,EAAQ,WAAW,IAAI,UAAU;EACtF,KAAK,GAAsB,CAAS;EACpC,IAAM,IAAQ,WAAW,iBAAiB;GAExC,AADA,KAAK,GAAsB,CAAS,GACpC,KAAK,GAAoB;EAC3B,CAAC;EAED,AADA,KAAK,GAAkB,IAAI,GAAW;GAAE;GAAW;EAAM,CAAC,GAC1D,KAAK,GAAmB,EAAI;CAC9B;CAEA,GAAsB,GAAyB;EAC7C,IAAM,IAAc,KAAK,GAAkB,IAAI,CAAS;EACnD,MACL,aAAa,EAAY,KAAK,GAC9B,KAAK,GAAkB,OAAO,CAAS;CACzC;CAEA,KAA2B;EAEzB,AADA,KAAK,GAAoB,MAAM,GAC/B,KAAK,KAAmB,KAAA;EACxB,KAAK,IAAM,KAAa,KAAK,GAAkB,KAAK,GAAG,KAAK,GAAsB,CAAS;EAC3F,KAAK,GAAkB;CACzB;CAEA,KAA0B;EAExB,AADgB,CAAC,GAAG,KAAK,GAAoB,OAAO,CAAC,CAAC,CAAC,MAAK,MAAW,EAAQ,UAAU,UAAU,QAC/F,IAAS,KAAK,GAAM,WAAW,OAAO,IAAI,SAAS,IAClD,KAAK,GAAM,WAAW,OAAO,OAAO,SAAS;CACpD;CAEA,KAA8B;EAI5B,AAFE,CAAC,KAAK,OACL,KAAK,GAAM,gBAAgB,MAAS,KAAK,GAAM,gBAAgB,WAAW,KAAK,GAAoB,KACxF,KAAK,GAAM,WAAW,OAAO,IAAI,cAAc,IACxD,KAAK,GAAM,WAAW,OAAO,OAAO,cAAc;CACzD;CAEA,GAAqB,GAAwB;EAC3C,IAAM,IAAS,IAAW,KAAK,GAAM,cAAc,eAAe,KAAA,IAAa,KAAA;EAC3E,MAAW,KAAK,OACpB,KAAK,IAAyB,oBAAoB,WAAW,KAAK,EAAoC,GACtG,KAAK,KAA0B,GAC/B,KAAK,IAAyB,iBAAiB,WAAW,KAAK,EAAoC;CACrG;CAEA,GAAmB,GAAwB;EACrC,MAAY,KAAK,OACrB,KAAK,KAAwB,GACzB,KACF,KAAK,GAAM,iBAAiB,SAAS,KAAK,IAAsC,EAAI,GACpF,KAAK,GAAM,iBAAiB,YAAY,KAAK,IAAsC,EAAI,MAEvF,KAAK,GAAM,oBAAoB,SAAS,KAAK,IAAsC,EAAI,GACvF,KAAK,GAAM,oBAAoB,YAAY,KAAK,IAAsC,EAAI;CAE9F;AACF;AAEA,SAAS,EAA8B,GAAc,GAA4B;CAC/E,KAAK,IAAM,KAAU,EAAM,aAAa,GAAG;EACzC,IAAI,MAAW,GAAU;EACnB,iBAAkB,WACpB,EAA2B,CAAM,GAAG,OAAO;CACjD;CACA,OAAO;AACT;AAEA,SAAS,EAA2B,GAA2B;CAC7D,OACE,EAAQ,QAAQ,yEAAyE,KACzF,EAA8B,CAAO;AAEzC;AAEA,SAAS,EAA8B,GAA2B;CAEhE,OADK,EAAQ,UAAU,SAAS,GAAG,IAC3B,EAAQ,YAAkE,mBAAmB,KADxD;AAE/C"}
|
|
1
|
+
{"version":3,"file":"viewport-gesture-navigation.controller.js","names":[],"sources":["../../src/viewport/viewport-gesture-navigation.controller.ts"],"sourcesContent":["// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { ReactiveController, ReactiveControllerHost } from 'lit';\nimport {\n GestureController,\n KeyStateController,\n isImmediateRootActiveElement,\n type Gesture,\n type GestureInput,\n type PinchStart,\n type PointerEndGesture,\n type PointerMovementGesture,\n type WheelGestureInput\n} from '@nvidia-elements/core/internal';\nimport type {\n ViewportPanBehavior,\n ViewportPanSession,\n ViewportPanProposal,\n ViewportPanUpdateProposal,\n ViewportPanEndRequest,\n ViewportPanEndReason,\n ViewportPoint,\n ViewportTransform,\n ViewportZoomProposal\n} from './viewport.types.js';\nimport { anchoredTransform, contentPointFromViewport } from './viewport-projection.utils.js';\n\ntype PointerPanLifecycle =\n | { readonly state: 'pending' }\n | { readonly state: 'active'; readonly navigation: ViewportPanSession }\n | { readonly state: 'suppressed' };\n\ninterface PointerPanSession {\n readonly button: number;\n readonly dragThreshold: number;\n lastTotalDisplacementX: number;\n lastTotalDisplacementY: number;\n lifecycle: PointerPanLifecycle;\n pendingEnd?: ViewportPanEndRequest;\n readonly pointerType: string;\n readonly start: ViewportTransform;\n transformStart: ViewportTransform;\n transformStartDisplacementX: number;\n transformStartDisplacementY: number;\n}\n\ninterface PinchContext {\n readonly anchor: ViewportPoint;\n readonly start: ViewportTransform;\n}\n\ntype ViewportGestureNavigationHost = HTMLElement &\n ReactiveControllerHost & {\n readonly _internals: ElementInternals;\n readonly behaviorPan: ViewportPanBehavior;\n readonly pannable: boolean;\n readonly zoomable: boolean;\n readonly dragThreshold: number;\n requestPan(proposal: ViewportPanProposal): boolean;\n requestZoom(proposal: ViewportZoomProposal): boolean;\n startPan(proposal: ViewportPanProposal): ViewportPanSession | undefined;\n };\n\ninterface ViewportGestureNavigationDelegate {\n readonly clampScale: (value: number) => number;\n readonly clientToViewport: (clientX: number, clientY: number) => ViewportPoint;\n readonly getTransform: () => ViewportTransform;\n readonly requestAdmittedPinchZoom: (proposal: ViewportZoomProposal) => boolean;\n}\n\nexport class ViewportGestureNavigationController implements ReactiveController {\n readonly #host: ViewportGestureNavigationHost;\n readonly #delegate: ViewportGestureNavigationDelegate;\n readonly #gestureController: GestureController<PinchContext>;\n readonly #keyStateController: KeyStateController;\n readonly #pointerPanSessions = new Map<number, PointerPanSession>();\n readonly #suppressedClicks = new Map<\n number,\n { eventType: 'auxclick' | 'click'; timer: ReturnType<typeof setTimeout> }\n >();\n #connected = false;\n #clickListenersActive = false;\n #spaceKeyListenerTarget?: Window;\n #nativeInteractionHovered = false;\n #pinchPointerIds?: ReadonlySet<number>;\n\n constructor(host: ViewportGestureNavigationHost, delegate: ViewportGestureNavigationDelegate) {\n this.#host = host;\n this.#delegate = delegate;\n host.addController(this);\n this.#gestureController = new GestureController(host, {\n beginPinch: this.#beginPinch,\n claimPriority: 'descendant-first',\n shouldIgnoreEvent: this.#shouldIgnoreEvent,\n shouldPreventContextMenu: this.#shouldPreventContextMenu,\n shouldReservePointerDown: this.#shouldReservePointerDown,\n suppressNativeDrag: true,\n touchAction: this.#getTouchAction\n });\n this.#keyStateController = new KeyStateController(host, { watchedCodes: ['Space'] });\n }\n\n hostConnected(): void {\n this.#connected = true;\n this.#host.addEventListener('nve-gesture', this.#handleGesture as EventListener);\n this.#host.addEventListener('nve-gesture-input', this.#handleGestureInput as EventListener);\n this.#host.addEventListener('blur', this.#handleBlur);\n this.#host.addEventListener('focus', this.#handleFocus);\n this.#host.addEventListener('pointerover', this.#handlePointerOver as EventListener);\n this.#syncBehaviorPolicy();\n }\n\n hostDisconnected(): void {\n this.#connected = false;\n this.#keyStateController.enabled = false;\n this.#gestureController.target = undefined;\n this.#host.removeEventListener('nve-gesture', this.#handleGesture as EventListener);\n this.#host.removeEventListener('nve-gesture-input', this.#handleGestureInput as EventListener);\n this.#host.removeEventListener('blur', this.#handleBlur);\n this.#host.removeEventListener('focus', this.#handleFocus);\n this.#host.removeEventListener('pointerover', this.#handlePointerOver as EventListener);\n this.#setClickListeners(false);\n this.#setSpaceKeyListener(false);\n this.#clearPointerState();\n }\n\n hostUpdated(): void {\n this.#syncBehaviorPolicy();\n this.#syncPanEligibleState();\n }\n\n rebasePointerSessions(): void {\n this.#rebasePointerPans();\n }\n\n #syncBehaviorPolicy(): void {\n const panBehavior = this.#host.behaviorPan;\n const behaviorEnabled = this.#host.pannable || this.#host.zoomable;\n const handlesClaimedPointers = this.#pointerPanSessions.size > 0;\n this.#keyStateController.enabled = this.#connected && panBehavior === 'space';\n this.#gestureController.target =\n this.#connected && (behaviorEnabled || handlesClaimedPointers) ? this.#host : undefined;\n this.#setSpaceKeyListener(this.#connected && panBehavior === 'space');\n this.#setClickListeners(this.#connected && (this.#host.pannable || this.#suppressedClicks.size > 0));\n this.#syncDisabledPanEligibility();\n }\n\n #syncDisabledPanEligibility(): void {\n if (this.#host.pannable) return;\n this.#nativeInteractionHovered = false;\n this.#host._internals?.states.delete('pan-eligible');\n }\n\n #handleGestureInput = (event: CustomEvent<GestureInput>): void => {\n const input = event.detail;\n if (input.kind === 'pointerdown') {\n this.#handlePointerDown(input);\n } else if (input.kind === 'pointerend') {\n this.#handlePointerEnd(input);\n } else {\n this.#handleWheel(input);\n }\n };\n\n #handlePointerDown(input: Extract<GestureInput, { kind: 'pointerdown' }>): void {\n const nativeEvent = input.event;\n this.#clearSuppressedClick(nativeEvent.pointerId);\n const isTouch = nativeEvent.pointerType === 'touch';\n const canPan = isTouch ? this.#host.pannable : this.#acceptsPointerPan(nativeEvent);\n const canPinch = isTouch && this.#host.zoomable;\n if (!canPan && !canPinch) return this.#focusSpaceViewportFromUnclaimedPointer(nativeEvent);\n const claimed = input.claim({ kind: canPan ? 'pan' : undefined, pinchCandidate: canPinch });\n if (!claimed) return;\n if (!isTouch) this.#host.focus({ preventScroll: true });\n if (nativeEvent.button === 1) nativeEvent.preventDefault();\n const start = this.#delegate.getTransform();\n this.#pointerPanSessions.set(nativeEvent.pointerId, {\n button: nativeEvent.button,\n dragThreshold: this.#host.dragThreshold,\n lastTotalDisplacementX: 0,\n lastTotalDisplacementY: 0,\n lifecycle: canPan ? { state: 'pending' } : { state: 'suppressed' },\n pointerType: nativeEvent.pointerType,\n start,\n transformStart: start,\n transformStartDisplacementX: 0,\n transformStartDisplacementY: 0\n });\n }\n\n #focusSpaceViewportFromUnclaimedPointer(event: PointerEvent): void {\n if (this.#host.behaviorPan !== 'space' || event.pointerType === 'touch' || event.button !== 0) return;\n this.#host.focus({ preventScroll: true });\n }\n\n #handlePointerEnd(input: PointerEndGesture): void {\n const pointerId = input.event.pointerId;\n const session = this.#pointerPanSessions.get(pointerId);\n const endedPinch = this.#endPinchIfNeeded(pointerId);\n if (session) this.#finishPointerPan(input, session, endedPinch);\n if (session && this.#pointerPanSessions.get(pointerId) === session) this.#pointerPanSessions.delete(pointerId);\n this.#syncPanningState();\n this.#syncBehaviorPolicy();\n }\n\n #endPinchIfNeeded(pointerId: number): boolean {\n if (!this.#pinchPointerIds?.has(pointerId)) return false;\n for (const [candidatePointerId, candidate] of this.#pointerPanSessions) {\n if (candidatePointerId !== pointerId && candidate.pointerType === 'touch') {\n this.#suppressPointerPan(candidate);\n }\n }\n this.#pinchPointerIds = undefined;\n return true;\n }\n\n #finishPointerPan(input: PointerEndGesture, session: PointerPanSession, endedPinch: boolean): void {\n if (session.lifecycle.state === 'pending') {\n session.pendingEnd = { event: input.event, interrupted: input.interrupted || endedPinch, reason: input.reason };\n return;\n }\n if (session.lifecycle.state !== 'active') return;\n const interrupted = input.interrupted || endedPinch;\n this.#dispatchPanEnd(input, session, interrupted);\n this.#suppressPointerPan(session);\n this.#syncPanningState();\n if (\n !interrupted &&\n this.#getAuthoritativePointerPanLifecycle(input.event.pointerId, session)?.state === 'suppressed'\n ) {\n this.#queueClickSuppression(input.event.pointerId, session);\n }\n }\n\n #handleWheel(input: WheelGestureInput): void {\n if (!isImmediateRootActiveElement(this.#host)) return;\n if (input.event.ctrlKey || input.event.metaKey) {\n this.#handleWheelZoom(input);\n } else {\n this.#handleWheelPan(input);\n }\n }\n\n #handleWheelZoom(input: WheelGestureInput): void {\n if (!this.#host.zoomable || !input.claim()) return;\n const start = this.#delegate.getTransform();\n const viewport = this.#delegate.clientToViewport(input.clientX, input.clientY);\n const anchor = contentPointFromViewport(viewport.x, viewport.y, start);\n const factor = this.#gestureController.getWheelZoomFactor(input);\n const next = anchoredTransform(anchor, viewport, this.#delegate.clampScale(start.scale * factor));\n const proposal: ViewportZoomProposal = {\n anchor,\n clientX: input.clientX,\n clientY: input.clientY,\n event: input.event,\n factor,\n next,\n source: 'wheel'\n };\n this.#host.requestZoom(proposal);\n }\n\n #handleWheelPan(input: WheelGestureInput): void {\n if (!this.#host.pannable || !input.claim()) return;\n const start = this.#delegate.getTransform();\n const next = {\n scale: start.scale,\n x: start.x + input.deltaX / start.scale,\n y: start.y + input.deltaY / start.scale\n };\n const proposal: ViewportPanProposal = {\n clientX: input.clientX,\n clientY: input.clientY,\n deltaX: input.deltaX,\n deltaY: input.deltaY,\n event: input.event,\n next,\n source: 'wheel'\n };\n this.#host.requestPan(proposal);\n }\n\n #handleGesture = (event: CustomEvent<Gesture<PinchContext>>): void => {\n const gesture = event.detail;\n if (gesture.kind === 'pinch') {\n this.#handlePinchGesture(gesture);\n } else if (gesture.kind === 'pan') {\n this.#handlePointerPan(gesture);\n }\n };\n\n #handlePointerPan(gesture: PointerMovementGesture & { readonly kind: 'pan' }): void {\n const pointerId = gesture.event.pointerId;\n const session = this.#pointerPanSessions.get(pointerId);\n if (!session || session.lifecycle.state === 'suppressed') return;\n session.lastTotalDisplacementX = gesture.totalDisplacementX;\n session.lastTotalDisplacementY = gesture.totalDisplacementY;\n const proposal = this.#pointerPanProposal(gesture, session);\n if (session.lifecycle.state === 'pending') this.#activatePointerPan(gesture, session, proposal);\n else session.lifecycle.navigation.update(proposal);\n }\n\n #pointerPanProposal(\n gesture: PointerMovementGesture & { readonly kind: 'pan' },\n session: PointerPanSession\n ): ViewportPanUpdateProposal {\n const displacementX = gesture.totalDisplacementX - session.transformStartDisplacementX;\n const displacementY = gesture.totalDisplacementY - session.transformStartDisplacementY;\n const next = {\n scale: session.transformStart.scale,\n x: session.transformStart.x - displacementX / session.transformStart.scale,\n y: session.transformStart.y - displacementY / session.transformStart.scale\n };\n return { ...gesture, next };\n }\n\n #activatePointerPan(\n gesture: PointerMovementGesture & { readonly kind: 'pan' },\n session: PointerPanSession,\n proposal: ViewportPanUpdateProposal\n ): void {\n const distanceSquared = gesture.totalDisplacementX ** 2 + gesture.totalDisplacementY ** 2;\n if (distanceSquared < session.dragThreshold ** 2) return;\n const navigation = this.#host.startPan({ ...proposal, source: 'pointer' });\n if (!navigation) return;\n if (this.#getAuthoritativePointerPanLifecycle(gesture.event.pointerId, session)?.state !== 'pending') {\n if (session.pendingEnd) navigation.end(session.pendingEnd);\n return;\n }\n session.lifecycle = { state: 'active', navigation };\n this.#syncPanningState();\n }\n\n #getAuthoritativePointerPanLifecycle(pointerId: number, session: PointerPanSession): PointerPanLifecycle | undefined {\n const authoritativeSession = this.#pointerPanSessions.get(pointerId);\n return authoritativeSession === session ? authoritativeSession.lifecycle : undefined;\n }\n\n #handlePinchGesture(gesture: Extract<Gesture<PinchContext>, { kind: 'pinch' }>): void {\n this.#interruptPointerPansForPinch(gesture.event);\n const viewport = this.#delegate.clientToViewport(gesture.centerClientX, gesture.centerClientY);\n const nextScale = this.#delegate.clampScale(gesture.context.start.scale * gesture.scale);\n const next = anchoredTransform(gesture.context.anchor, viewport, nextScale);\n const proposal: ViewportZoomProposal = {\n anchor: gesture.context.anchor,\n clientX: gesture.centerClientX,\n clientY: gesture.centerClientY,\n event: gesture.event,\n factor: gesture.scale,\n next,\n source: 'pinch'\n };\n this.#delegate.requestAdmittedPinchZoom(proposal);\n }\n\n #beginPinch = (pinch: PinchStart): { context: PinchContext } | undefined => {\n if (!this.#host.zoomable) return undefined;\n const start = this.#delegate.getTransform();\n const viewport = this.#delegate.clientToViewport(pinch.startCenterClientX, pinch.startCenterClientY);\n const context: PinchContext = {\n anchor: contentPointFromViewport(viewport.x, viewport.y, start),\n start\n };\n this.#pinchPointerIds = new Set([pinch.firstPointerId, pinch.secondPointerId]);\n return { context };\n };\n\n #interruptPointerPansForPinch(event: PointerEvent): void {\n const pinchPointerIds = this.#pinchPointerIds;\n if (!pinchPointerIds) return;\n for (const pointerId of pinchPointerIds) {\n const session = this.#pointerPanSessions.get(pointerId);\n if (!session) continue;\n this.#interruptPointerPan({ event, reason: 'pinch' }, session);\n }\n this.#syncPanningState();\n }\n\n #interruptPointerPan(\n end: { readonly event: PointerEvent; readonly reason: ViewportPanEndReason },\n session: PointerPanSession\n ): void {\n const wasActive = session.lifecycle.state === 'active';\n if (session.lifecycle.state === 'pending') session.pendingEnd = { ...end, interrupted: true };\n if (wasActive) this.#dispatchPanEnd(end, session, true);\n this.#suppressPointerPan(session);\n }\n\n #suppressPointerPan(session: PointerPanSession): void {\n if (session.lifecycle.state === 'suppressed') return;\n session.lifecycle = { state: 'suppressed' };\n }\n\n #dispatchPanEnd(\n end: { readonly event: PointerEvent; readonly reason: ViewportPanEndReason },\n session: PointerPanSession,\n interrupted: boolean\n ): void {\n if (session.lifecycle.state !== 'active') return;\n session.lifecycle.navigation.end({ event: end.event, interrupted, reason: end.reason });\n }\n\n #handleSpaceKeyDown = (event: KeyboardEvent): void => {\n if (event.code !== 'Space' || this.#host.behaviorPan !== 'space') return;\n if (!isImmediateRootActiveElement(this.#host)) return;\n event.preventDefault();\n };\n\n #handleFocus = (): void => {\n this.#syncPanEligibleState();\n };\n\n #handleBlur = (): void => {\n this.#host._internals.states.delete('pan-eligible');\n };\n\n #handlePointerOver = (event: PointerEvent): void => {\n if (!this.#host.pannable) return;\n this.#nativeInteractionHovered = eventTargetsNativeInteraction(event, this.#host);\n this.#syncPanEligibleState();\n };\n\n #acceptsPointerPan(event: PointerEvent): boolean {\n if (event.button === 0) {\n return this.#host.behaviorPan === true || (this.#host.behaviorPan === 'space' && this.#isSpacePanEligible());\n }\n if (event.button === 1) return this.#host.pannable;\n return false;\n }\n\n #isSpacePanEligible(): boolean {\n return this.#keyStateController.isPressed('Space') && isImmediateRootActiveElement(this.#host);\n }\n\n #shouldIgnoreEvent = (event: Event): boolean => {\n if (event.type === 'dragstart' && !this.#host.pannable) return true;\n return eventTargetsViewportOverlay(event, this.#host) || eventTargetsNativeInteraction(event, this.#host);\n };\n\n #shouldPreventContextMenu = (event: MouseEvent): boolean => {\n if (!event.ctrlKey) return false;\n return this.#host.behaviorPan === true || (this.#host.behaviorPan === 'space' && this.#isSpacePanEligible());\n };\n\n #shouldReservePointerDown = (event: PointerEvent): boolean =>\n this.#host.behaviorPan === 'space' &&\n this.#isSpacePanEligible() &&\n (event.pointerType === 'mouse' || event.pointerType === 'pen') &&\n event.button === 0;\n\n #getTouchAction = (): string | undefined =>\n this.#host.pannable || this.#host.zoomable || this.#pointerPanSessions.size > 0 ? 'none' : undefined;\n\n #rebasePointerPans(): void {\n const transformStart = this.#delegate.getTransform();\n for (const session of this.#pointerPanSessions.values()) {\n this.#rebaseEligiblePointerPan(session, transformStart);\n }\n }\n\n #rebaseEligiblePointerPan(session: PointerPanSession, transformStart: ViewportTransform): void {\n if (session.lifecycle.state === 'suppressed') return;\n session.transformStart = transformStart;\n session.transformStartDisplacementX = session.lastTotalDisplacementX;\n session.transformStartDisplacementY = session.lastTotalDisplacementY;\n }\n\n #handleClickCapture = (event: PointerEvent): void => {\n const suppression = this.#suppressedClicks.get(event.pointerId);\n if (!suppression || suppression.eventType !== event.type) return;\n this.#clearSuppressedClick(event.pointerId);\n this.#syncBehaviorPolicy();\n event.preventDefault();\n event.stopImmediatePropagation();\n };\n\n #queueClickSuppression(pointerId: number, session: PointerPanSession): void {\n const eventType = session.pointerType === 'touch' || session.button === 0 ? 'click' : 'auxclick';\n this.#clearSuppressedClick(pointerId);\n const timer = globalThis.setTimeout(() => {\n this.#clearSuppressedClick(pointerId);\n this.#syncBehaviorPolicy();\n });\n this.#suppressedClicks.set(pointerId, { eventType, timer });\n this.#setClickListeners(true);\n }\n\n #clearSuppressedClick(pointerId: number): void {\n const suppression = this.#suppressedClicks.get(pointerId);\n if (!suppression) return;\n clearTimeout(suppression.timer);\n this.#suppressedClicks.delete(pointerId);\n }\n\n #clearPointerState(): void {\n this.#pointerPanSessions.clear();\n this.#pinchPointerIds = undefined;\n for (const pointerId of this.#suppressedClicks.keys()) this.#clearSuppressedClick(pointerId);\n this.#syncPanningState();\n }\n\n #syncPanningState(): void {\n const panning = [...this.#pointerPanSessions.values()].some(session => session.lifecycle.state === 'active');\n if (panning) this.#host._internals.states.add('panning');\n else this.#host._internals.states.delete('panning');\n }\n\n #syncPanEligibleState(): void {\n const eligible =\n !this.#nativeInteractionHovered &&\n (this.#host.behaviorPan === true || (this.#host.behaviorPan === 'space' && this.#isSpacePanEligible()));\n if (eligible) this.#host._internals.states.add('pan-eligible');\n else this.#host._internals.states.delete('pan-eligible');\n }\n\n #setSpaceKeyListener(enabled: boolean): void {\n const target = enabled ? (this.#host.ownerDocument.defaultView ?? undefined) : undefined;\n if (target === this.#spaceKeyListenerTarget) return;\n this.#spaceKeyListenerTarget?.removeEventListener('keydown', this.#handleSpaceKeyDown as EventListener);\n this.#spaceKeyListenerTarget = target;\n this.#spaceKeyListenerTarget?.addEventListener('keydown', this.#handleSpaceKeyDown as EventListener);\n }\n\n #setClickListeners(enabled: boolean): void {\n if (enabled === this.#clickListenersActive) return;\n this.#clickListenersActive = enabled;\n if (enabled) {\n this.#host.addEventListener('click', this.#handleClickCapture as EventListener, true);\n this.#host.addEventListener('auxclick', this.#handleClickCapture as EventListener, true);\n } else {\n this.#host.removeEventListener('click', this.#handleClickCapture as EventListener, true);\n this.#host.removeEventListener('auxclick', this.#handleClickCapture as EventListener, true);\n }\n }\n}\n\nfunction eventTargetsViewportOverlay(event: Event, boundary: Element): boolean {\n for (const target of event.composedPath()) {\n if (target === boundary) break;\n if (target instanceof Element && target.parentElement === boundary && target.slot === 'overlay') return true;\n }\n return false;\n}\n\nfunction eventTargetsNativeInteraction(event: Event, boundary: Element): boolean {\n for (const target of event.composedPath()) {\n if (target === boundary) break;\n if (!(target instanceof Element)) continue;\n if (isNativeInteractionElement(target)) return true;\n }\n return false;\n}\n\nfunction isNativeInteractionElement(element: Element): boolean {\n return (\n element.matches('button, input, textarea, select, a[href], [contenteditable], [tabindex]') ||\n isFormAssociatedCustomElement(element)\n );\n}\n\nfunction isFormAssociatedCustomElement(element: Element): boolean {\n if (!element.localName.includes('-')) return false;\n return (element.constructor as typeof HTMLElement & { formAssociated?: boolean }).formAssociated === true;\n}\n"],"mappings":"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"}
|
|
@@ -24,7 +24,7 @@ var h = 10, g = 64, _ = class extends u {
|
|
|
24
24
|
static {
|
|
25
25
|
this.metadata = {
|
|
26
26
|
tag: "nve-viewport-gridlines",
|
|
27
|
-
version: "2.
|
|
27
|
+
version: "2.11.1"
|
|
28
28
|
};
|
|
29
29
|
}
|
|
30
30
|
#e;
|
|
@@ -72,8 +72,8 @@ var h = 10, g = 64, _ = class extends u {
|
|
|
72
72
|
}
|
|
73
73
|
connectedCallback() {
|
|
74
74
|
super.connectedCallback();
|
|
75
|
-
let e = this.parentElement
|
|
76
|
-
|
|
75
|
+
let e = this.parentElement;
|
|
76
|
+
e instanceof a && (this.#i = e, this.style.setProperty("--_scale", `${e.scale}`), this.#i.addEventListener("viewportchange", this.#s), typeof ResizeObserver < "u" && (this.#a = new ResizeObserver(this.#c), this.#a.observe(e)), this.#u({
|
|
77
77
|
forceCoverage: !0,
|
|
78
78
|
resetInterval: !0
|
|
79
79
|
}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viewport-gridlines2.js","names":[],"sources":["../../src/viewport/viewport-gridlines.ts"],"sourcesContent":["// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { html, LitElement, nothing, type PropertyValues } from 'lit';\nimport { property } from 'lit/decorators/property.js';\nimport { styleMap } from 'lit/directives/style-map.js';\nimport { finiteOr, hostAttr, positiveFiniteOr, useStyles } from '@nvidia-elements/core/internal';\nimport styles from './viewport-gridlines.css?inline';\nimport {\n createGridlineCoverage,\n gridlineCoverageNeedsUpdate,\n selectGridlineInterval\n} from './viewport-gridlines.utils.js';\nimport { Viewport } from './viewport.js';\nimport type { ViewportRect } from './viewport.types.js';\n\nconst DEFAULT_STEP = 10;\nconst DEFAULT_TARGET_SPACING = 64;\n\ninterface GridlineProjection {\n readonly coverage: ViewportRect;\n readonly interval: number;\n}\n\ninterface ProjectionSyncOptions {\n readonly forceCoverage?: boolean;\n readonly resetInterval?: boolean;\n}\n\n/**\n * @element nve-viewport-gridlines\n * @description Renders origin-stable, zoom-adaptive gridlines behind viewport content.\n * @documentation https://nvidia.github.io/elements/docs/elements/viewport/\n * @since 0.0.0\n * @entrypoint \\@nvidia-elements/core/viewport\n * @cssprop --color - Sets the gridline stroke color.\n * @cssprop --line-width - Sets the approximate stroke width in CSS pixels.\n */\nexport class ViewportGridlines extends LitElement {\n static styles = useStyles([styles]);\n\n static readonly metadata = {\n tag: 'nve-viewport-gridlines',\n version: '0.0.0'\n };\n\n /** @private */\n @hostAttr() slot = 'background';\n\n /** @private */\n @hostAttr({ attribute: 'aria-hidden' }) protected accessibilityHidden = 'true';\n\n #step = DEFAULT_STEP;\n #originX = 0;\n #originY = 0;\n #targetSpacing = DEFAULT_TARGET_SPACING;\n #viewport?: Viewport;\n #resizeObserver?: ResizeObserver;\n #projection?: GridlineProjection;\n\n /** Smallest gridline interval in content-space units. */\n @property({ type: Number, reflect: true })\n get step(): number {\n return this.#step;\n }\n\n set step(value: number) {\n const next = positiveFiniteOr(value, DEFAULT_STEP);\n if (next === this.#step) return;\n const previous = this.#step;\n this.#step = next;\n this.requestUpdate('step', previous);\n }\n\n /** Content-space x coordinate of the grid origin. */\n @property({ type: Number, attribute: 'origin-x', reflect: true })\n get originX(): number {\n return this.#originX;\n }\n\n set originX(value: number) {\n const next = finiteOr(value, 0);\n if (next === this.#originX) return;\n const previous = this.#originX;\n this.#originX = next;\n this.requestUpdate('originX', previous);\n }\n\n /** Content-space y coordinate of the grid origin. */\n @property({ type: Number, attribute: 'origin-y', reflect: true })\n get originY(): number {\n return this.#originY;\n }\n\n set originY(value: number) {\n const next = finiteOr(value, 0);\n if (next === this.#originY) return;\n const previous = this.#originY;\n this.#originY = next;\n this.requestUpdate('originY', previous);\n }\n\n /** Target on-screen separation used to choose the adaptive gridline interval, in CSS pixels. */\n @property({ type: Number, attribute: 'target-spacing', reflect: true })\n get targetSpacing(): number {\n return this.#targetSpacing;\n }\n\n set targetSpacing(value: number) {\n const next = positiveFiniteOr(value, DEFAULT_TARGET_SPACING);\n if (next === this.#targetSpacing) return;\n const previous = this.#targetSpacing;\n this.#targetSpacing = next;\n this.requestUpdate('targetSpacing', previous);\n }\n\n connectedCallback(): void {\n super.connectedCallback();\n const viewport = this.parentElement?.closest(Viewport.metadata.tag);\n if (!(viewport instanceof Viewport) || viewport !== this.parentElement) return;\n this.#viewport = viewport;\n this.style.setProperty('--_scale', `${viewport.scale}`);\n this.#viewport.addEventListener('viewportchange', this.#handleViewportChange);\n if (typeof ResizeObserver !== 'undefined') {\n this.#resizeObserver = new ResizeObserver(this.#handleViewportResize);\n this.#resizeObserver.observe(viewport);\n }\n this.#updateProjection({ forceCoverage: true, resetInterval: true });\n }\n\n disconnectedCallback(): void {\n if (this.#viewport) {\n this.#viewport.removeEventListener('viewportchange', this.#handleViewportChange);\n }\n this.#resizeObserver?.disconnect();\n this.#resizeObserver = undefined;\n this.#viewport = undefined;\n this.#projection = undefined;\n super.disconnectedCallback();\n }\n\n protected willUpdate(changed: PropertyValues<this>): void {\n super.willUpdate(changed);\n if (changed.has('step') || changed.has('targetSpacing')) {\n this.#updateProjection({ forceCoverage: true, resetInterval: true });\n }\n }\n\n render() {\n const projection = this.#projection;\n if (!projection) return nothing;\n const { coverage, interval } = projection;\n return html`\n <svg\n internal-host\n aria-hidden=\"true\"\n focusable=\"false\"\n preserveAspectRatio=\"none\"\n style=${styleMap(coverageStyles(coverage))}\n viewBox=\"${coverage.x} ${coverage.y} ${coverage.width} ${coverage.height}\"\n >\n <defs>\n <pattern\n data-gridline-pattern\n id=\"gridlines\"\n x=${this.#originX}\n y=${this.#originY}\n width=${interval}\n height=${interval}\n patternUnits=\"userSpaceOnUse\"\n >\n <path d=\"M ${interval} 0 H 0 V ${interval}\"></path>\n </pattern>\n </defs>\n <rect\n x=${coverage.x}\n y=${coverage.y}\n width=${coverage.width}\n height=${coverage.height}\n fill=\"url(#gridlines)\"\n ></rect>\n </svg>\n `;\n }\n\n #handleViewportChange = (): void => {\n const viewport = this.#viewport;\n if (!viewport) return;\n this.style.setProperty('--_scale', `${viewport.scale}`);\n this.#requestProjectionUpdate();\n };\n\n #handleViewportResize = (): void => {\n this.#requestProjectionUpdate({ forceCoverage: true });\n };\n\n #requestProjectionUpdate(options: ProjectionSyncOptions = {}): void {\n if (this.#updateProjection(options)) this.requestUpdate();\n }\n\n #updateProjection(options: ProjectionSyncOptions = {}): boolean {\n const viewport = this.#viewport;\n if (!viewport) return false;\n const visible = viewport.getVisibleRect();\n if (visible.width <= 0 || visible.height <= 0) return false;\n const interval = selectGridlineInterval({\n current: options.resetInterval ? undefined : this.#projection?.interval,\n scale: viewport.scale,\n step: this.#step,\n targetSpacing: this.#targetSpacing\n });\n const projection = nextProjection({\n forceCoverage: options.forceCoverage === true,\n interval,\n previous: this.#projection,\n visible\n });\n if (!projection) return false;\n this.#projection = projection;\n return true;\n }\n}\n\nfunction nextProjection({\n forceCoverage,\n interval,\n previous,\n visible\n}: {\n forceCoverage: boolean;\n interval: number;\n previous?: GridlineProjection;\n visible: ViewportRect;\n}): GridlineProjection | undefined {\n const coverageChanged = forceCoverage || gridlineCoverageNeedsUpdate(previous?.coverage, visible);\n const intervalChanged = interval !== previous?.interval;\n if (!coverageChanged && !intervalChanged) return undefined;\n const coverage = coverageChanged || !previous ? createGridlineCoverage(visible) : previous.coverage;\n return { coverage, interval };\n}\n\nfunction coverageStyles(coverage: ViewportRect): Record<string, string> {\n return {\n height: `${coverage.height}px`,\n left: `${coverage.x}px`,\n top: `${coverage.y}px`,\n width: `${coverage.width}px`\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"viewport-gridlines2.js","names":[],"sources":["../../src/viewport/viewport-gridlines.ts"],"sourcesContent":["// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport { html, LitElement, nothing, type PropertyValues } from 'lit';\nimport { property } from 'lit/decorators/property.js';\nimport { styleMap } from 'lit/directives/style-map.js';\nimport { finiteOr, hostAttr, positiveFiniteOr, useStyles } from '@nvidia-elements/core/internal';\nimport styles from './viewport-gridlines.css?inline';\nimport {\n createGridlineCoverage,\n gridlineCoverageNeedsUpdate,\n selectGridlineInterval\n} from './viewport-gridlines.utils.js';\nimport { Viewport } from './viewport.js';\nimport type { ViewportRect } from './viewport.types.js';\n\nconst DEFAULT_STEP = 10;\nconst DEFAULT_TARGET_SPACING = 64;\n\ninterface GridlineProjection {\n readonly coverage: ViewportRect;\n readonly interval: number;\n}\n\ninterface ProjectionSyncOptions {\n readonly forceCoverage?: boolean;\n readonly resetInterval?: boolean;\n}\n\n/**\n * @element nve-viewport-gridlines\n * @description Renders origin-stable, zoom-adaptive gridlines behind viewport content.\n * @documentation https://nvidia.github.io/elements/docs/elements/viewport/\n * @since 0.0.0\n * @entrypoint \\@nvidia-elements/core/viewport\n * @cssprop --color - Sets the gridline stroke color.\n * @cssprop --line-width - Sets the approximate stroke width in CSS pixels.\n */\nexport class ViewportGridlines extends LitElement {\n static styles = useStyles([styles]);\n\n static readonly metadata = {\n tag: 'nve-viewport-gridlines',\n version: '0.0.0'\n };\n\n /** @private */\n @hostAttr() slot = 'background';\n\n /** @private */\n @hostAttr({ attribute: 'aria-hidden' }) protected accessibilityHidden = 'true';\n\n #step = DEFAULT_STEP;\n #originX = 0;\n #originY = 0;\n #targetSpacing = DEFAULT_TARGET_SPACING;\n #viewport?: Viewport;\n #resizeObserver?: ResizeObserver;\n #projection?: GridlineProjection;\n\n /** Smallest gridline interval in content-space units. */\n @property({ type: Number, reflect: true })\n get step(): number {\n return this.#step;\n }\n\n set step(value: number) {\n const next = positiveFiniteOr(value, DEFAULT_STEP);\n if (next === this.#step) return;\n const previous = this.#step;\n this.#step = next;\n this.requestUpdate('step', previous);\n }\n\n /** Content-space x coordinate of the grid origin. */\n @property({ type: Number, attribute: 'origin-x', reflect: true })\n get originX(): number {\n return this.#originX;\n }\n\n set originX(value: number) {\n const next = finiteOr(value, 0);\n if (next === this.#originX) return;\n const previous = this.#originX;\n this.#originX = next;\n this.requestUpdate('originX', previous);\n }\n\n /** Content-space y coordinate of the grid origin. */\n @property({ type: Number, attribute: 'origin-y', reflect: true })\n get originY(): number {\n return this.#originY;\n }\n\n set originY(value: number) {\n const next = finiteOr(value, 0);\n if (next === this.#originY) return;\n const previous = this.#originY;\n this.#originY = next;\n this.requestUpdate('originY', previous);\n }\n\n /** Target on-screen separation used to choose the adaptive gridline interval, in CSS pixels. */\n @property({ type: Number, attribute: 'target-spacing', reflect: true })\n get targetSpacing(): number {\n return this.#targetSpacing;\n }\n\n set targetSpacing(value: number) {\n const next = positiveFiniteOr(value, DEFAULT_TARGET_SPACING);\n if (next === this.#targetSpacing) return;\n const previous = this.#targetSpacing;\n this.#targetSpacing = next;\n this.requestUpdate('targetSpacing', previous);\n }\n\n connectedCallback(): void {\n super.connectedCallback();\n const viewport = this.parentElement;\n if (!(viewport instanceof Viewport)) return;\n this.#viewport = viewport;\n this.style.setProperty('--_scale', `${viewport.scale}`);\n this.#viewport.addEventListener('viewportchange', this.#handleViewportChange);\n if (typeof ResizeObserver !== 'undefined') {\n this.#resizeObserver = new ResizeObserver(this.#handleViewportResize);\n this.#resizeObserver.observe(viewport);\n }\n this.#updateProjection({ forceCoverage: true, resetInterval: true });\n }\n\n disconnectedCallback(): void {\n if (this.#viewport) {\n this.#viewport.removeEventListener('viewportchange', this.#handleViewportChange);\n }\n this.#resizeObserver?.disconnect();\n this.#resizeObserver = undefined;\n this.#viewport = undefined;\n this.#projection = undefined;\n super.disconnectedCallback();\n }\n\n protected willUpdate(changed: PropertyValues<this>): void {\n super.willUpdate(changed);\n if (changed.has('step') || changed.has('targetSpacing')) {\n this.#updateProjection({ forceCoverage: true, resetInterval: true });\n }\n }\n\n render() {\n const projection = this.#projection;\n if (!projection) return nothing;\n const { coverage, interval } = projection;\n return html`\n <svg\n internal-host\n aria-hidden=\"true\"\n focusable=\"false\"\n preserveAspectRatio=\"none\"\n style=${styleMap(coverageStyles(coverage))}\n viewBox=\"${coverage.x} ${coverage.y} ${coverage.width} ${coverage.height}\"\n >\n <defs>\n <pattern\n data-gridline-pattern\n id=\"gridlines\"\n x=${this.#originX}\n y=${this.#originY}\n width=${interval}\n height=${interval}\n patternUnits=\"userSpaceOnUse\"\n >\n <path d=\"M ${interval} 0 H 0 V ${interval}\"></path>\n </pattern>\n </defs>\n <rect\n x=${coverage.x}\n y=${coverage.y}\n width=${coverage.width}\n height=${coverage.height}\n fill=\"url(#gridlines)\"\n ></rect>\n </svg>\n `;\n }\n\n #handleViewportChange = (): void => {\n const viewport = this.#viewport;\n if (!viewport) return;\n this.style.setProperty('--_scale', `${viewport.scale}`);\n this.#requestProjectionUpdate();\n };\n\n #handleViewportResize = (): void => {\n this.#requestProjectionUpdate({ forceCoverage: true });\n };\n\n #requestProjectionUpdate(options: ProjectionSyncOptions = {}): void {\n if (this.#updateProjection(options)) this.requestUpdate();\n }\n\n #updateProjection(options: ProjectionSyncOptions = {}): boolean {\n const viewport = this.#viewport;\n if (!viewport) return false;\n const visible = viewport.getVisibleRect();\n if (visible.width <= 0 || visible.height <= 0) return false;\n const interval = selectGridlineInterval({\n current: options.resetInterval ? undefined : this.#projection?.interval,\n scale: viewport.scale,\n step: this.#step,\n targetSpacing: this.#targetSpacing\n });\n const projection = nextProjection({\n forceCoverage: options.forceCoverage === true,\n interval,\n previous: this.#projection,\n visible\n });\n if (!projection) return false;\n this.#projection = projection;\n return true;\n }\n}\n\nfunction nextProjection({\n forceCoverage,\n interval,\n previous,\n visible\n}: {\n forceCoverage: boolean;\n interval: number;\n previous?: GridlineProjection;\n visible: ViewportRect;\n}): GridlineProjection | undefined {\n const coverageChanged = forceCoverage || gridlineCoverageNeedsUpdate(previous?.coverage, visible);\n const intervalChanged = interval !== previous?.interval;\n if (!coverageChanged && !intervalChanged) return undefined;\n const coverage = coverageChanged || !previous ? createGridlineCoverage(visible) : previous.coverage;\n return { coverage, interval };\n}\n\nfunction coverageStyles(coverage: ViewportRect): Record<string, string> {\n return {\n height: `${coverage.height}px`,\n left: `${coverage.x}px`,\n top: `${coverage.y}px`,\n width: `${coverage.width}px`\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
/**
|
|
3
|
+
* @element nve-viewport-minimap
|
|
4
|
+
* @description Provides a simplified overview and direct navigation for its parent viewport.
|
|
5
|
+
* @documentation https://nvidia.github.io/elements/docs/elements/viewport/
|
|
6
|
+
* @since 0.0.0
|
|
7
|
+
* @entrypoint \@nvidia-elements/core/viewport
|
|
8
|
+
* @cssprop --background - Color of the surface behind preview geometry.
|
|
9
|
+
* @cssprop --color - Color of automatically drawn root rectangles.
|
|
10
|
+
* @cssprop --indicator-color - Color of the current visible-region indicator.
|
|
11
|
+
* @cssprop --border-radius - Corner radius of the minimap.
|
|
12
|
+
* @cssprop --recenter-cursor - Cursor for clicking the background to center the viewport.
|
|
13
|
+
* @cssprop --pan-cursor - Cursor over a draggable visible region.
|
|
14
|
+
* @cssprop --panning-cursor - Cursor during a visible-region drag.
|
|
15
|
+
* @slot preview - Optional simplified content drawn in viewport content coordinates. Replaces automatic root rectangles.
|
|
16
|
+
*/
|
|
17
|
+
export declare class ViewportMinimap extends LitElement {
|
|
18
|
+
#private;
|
|
19
|
+
static styles: import("lit").CSSResult[];
|
|
20
|
+
static readonly metadata: {
|
|
21
|
+
tag: string;
|
|
22
|
+
version: string;
|
|
23
|
+
};
|
|
24
|
+
/** @private */
|
|
25
|
+
_internals: ElementInternals;
|
|
26
|
+
/** @private */
|
|
27
|
+
slot: string;
|
|
28
|
+
/** @private */
|
|
29
|
+
protected accessibilityHidden: string;
|
|
30
|
+
constructor();
|
|
31
|
+
connectedCallback(): void;
|
|
32
|
+
disconnectedCallback(): void;
|
|
33
|
+
/** Remeasures content bounds after preview or viewport roots move without resizing. */
|
|
34
|
+
refresh(): void;
|
|
35
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
36
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
//#region src/viewport/viewport-minimap.css?inline
|
|
2
|
+
var e = ":host{--background: var(--nve-sys-layer-container-background);--color: var(--nve-sys-text-muted-color);--indicator-color: var(--nve-sys-support-accent-color);--border-radius: var(--nve-ref-border-radius-md);position:absolute;inset-block-end:var(--nve-ref-space-xs);inset-inline-end:var(--nve-ref-space-xs);display:block;box-sizing:border-box;inline-size:clamp(8rem,32%,10rem);max-inline-size:calc(100% - 1rem);aspect-ratio:4 / 3;overflow:hidden;border-radius:var(--border-radius);contain:content;cursor:default;user-select:none}.surface{position:relative;inline-size:100%;block-size:100%}svg{position:absolute;inset:0;display:block;inline-size:100%;block-size:100%;overflow:hidden;border-radius:inherit}.preview{position:absolute;inset:0;transform-origin:0 0;pointer-events:none}.foreground-layer{pointer-events:none}.background{fill:var(--background)}.item{fill:var(--color);pointer-events:none}.visible-region{fill:var(--indicator-color);fill-opacity:.15;stroke:var(--indicator-color);stroke-width:1.5;vector-effect:non-scaling-stroke;pointer-events:all}:host(:state(pan-eligible)) .background{cursor:var(--recenter-cursor, pointer)}:host(:state(pan-eligible)) .visible-region{cursor:var(--pan-cursor, grab)}:host(:state(panning)),:host(:state(panning)) .background,:host(:state(panning)) .visible-region{cursor:var(--panning-cursor, grabbing)}";
|
|
3
|
+
//#endregion
|
|
4
|
+
export { e as default };
|
|
5
|
+
|
|
6
|
+
//# sourceMappingURL=viewport-minimap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewport-minimap.js","names":[],"sources":["../../src/viewport/viewport-minimap.css?inline"],"sourcesContent":["/* SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. */\n/* SPDX-License-Identifier: Apache-2.0 */\n:host {\n --background: var(--nve-sys-layer-container-background);\n --color: var(--nve-sys-text-muted-color);\n --indicator-color: var(--nve-sys-support-accent-color);\n --border-radius: var(--nve-ref-border-radius-md);\n\n position: absolute;\n inset-block-end: var(--nve-ref-space-xs);\n inset-inline-end: var(--nve-ref-space-xs);\n display: block;\n box-sizing: border-box;\n inline-size: clamp(8rem, 32%, 10rem);\n max-inline-size: calc(100% - 1rem);\n aspect-ratio: 4 / 3;\n overflow: hidden;\n border-radius: var(--border-radius);\n contain: content;\n cursor: default;\n user-select: none;\n}\n\n.surface {\n position: relative;\n inline-size: 100%;\n block-size: 100%;\n}\n\nsvg {\n position: absolute;\n inset: 0;\n display: block;\n inline-size: 100%;\n block-size: 100%;\n overflow: hidden;\n border-radius: inherit;\n}\n\n.preview {\n position: absolute;\n inset: 0;\n transform-origin: 0 0;\n pointer-events: none;\n}\n\n.foreground-layer {\n pointer-events: none;\n}\n\n.background {\n fill: var(--background);\n}\n\n.item {\n fill: var(--color);\n pointer-events: none;\n}\n\n.visible-region {\n fill: var(--indicator-color);\n fill-opacity: 0.15;\n stroke: var(--indicator-color);\n stroke-width: 1.5;\n vector-effect: non-scaling-stroke;\n pointer-events: all;\n}\n\n:host(:state(pan-eligible)) .background {\n cursor: var(--recenter-cursor, pointer);\n}\n\n:host(:state(pan-eligible)) .visible-region {\n cursor: var(--pan-cursor, grab);\n}\n\n:host(:state(panning)),\n:host(:state(panning)) .background,\n:host(:state(panning)) .visible-region {\n cursor: var(--panning-cursor, grabbing);\n}\n"],"mappings":""}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ViewportPoint, ViewportRect } from './viewport.types.js';
|
|
2
|
+
export interface ViewportMinimapProjection {
|
|
3
|
+
readonly offsetX: number;
|
|
4
|
+
readonly offsetY: number;
|
|
5
|
+
readonly scale: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function hasPositiveFiniteRect(rect: ViewportRect): boolean;
|
|
8
|
+
export declare function createViewportMinimapProjection(content: readonly ViewportRect[], visible: ViewportRect, size: {
|
|
9
|
+
readonly width: number;
|
|
10
|
+
readonly height: number;
|
|
11
|
+
}): ViewportMinimapProjection | undefined;
|
|
12
|
+
export declare function projectViewportMinimapRect(rect: ViewportRect, projection: ViewportMinimapProjection): ViewportRect;
|
|
13
|
+
export declare function unprojectViewportMinimapPoint(point: ViewportPoint, projection: ViewportMinimapProjection): ViewportPoint;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
//#region src/viewport/viewport-minimap.utils.ts
|
|
2
|
+
var e = .1;
|
|
3
|
+
function t(e) {
|
|
4
|
+
return Number.isFinite(e.x) && Number.isFinite(e.y) && Number.isFinite(e.width) && Number.isFinite(e.height) && e.width > 0 && e.height > 0;
|
|
5
|
+
}
|
|
6
|
+
function n(t, n, r) {
|
|
7
|
+
if (r.width <= 0 || r.height <= 0 || n.width <= 0 || n.height <= 0) return;
|
|
8
|
+
let i = a([n, ...t]);
|
|
9
|
+
if (!i) return;
|
|
10
|
+
let o = Math.max(i.width * e, 2 ** -52), s = Math.max(i.height * e, 2 ** -52), c = {
|
|
11
|
+
x: i.x - o,
|
|
12
|
+
y: i.y - s,
|
|
13
|
+
width: i.width + o * 2,
|
|
14
|
+
height: i.height + s * 2
|
|
15
|
+
}, l = Math.min(r.width / c.width, r.height / c.height);
|
|
16
|
+
if (!(!Number.isFinite(l) || l <= 0)) return {
|
|
17
|
+
offsetX: (r.width - c.width * l) / 2 - c.x * l,
|
|
18
|
+
offsetY: (r.height - c.height * l) / 2 - c.y * l,
|
|
19
|
+
scale: l
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function r(e, t) {
|
|
23
|
+
return {
|
|
24
|
+
x: e.x * t.scale + t.offsetX,
|
|
25
|
+
y: e.y * t.scale + t.offsetY,
|
|
26
|
+
width: e.width * t.scale,
|
|
27
|
+
height: e.height * t.scale
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function i(e, t) {
|
|
31
|
+
return {
|
|
32
|
+
x: (e.x - t.offsetX) / t.scale,
|
|
33
|
+
y: (e.y - t.offsetY) / t.scale
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function a(e) {
|
|
37
|
+
let n = Infinity, r = Infinity, i = -Infinity, a = -Infinity;
|
|
38
|
+
for (let o of e) t(o) && (n = Math.min(n, o.x), r = Math.min(r, o.y), i = Math.max(i, o.x + o.width), a = Math.max(a, o.y + o.height));
|
|
39
|
+
if (!(!Number.isFinite(n) || !Number.isFinite(r) || i <= n || a <= r)) return {
|
|
40
|
+
x: n,
|
|
41
|
+
y: r,
|
|
42
|
+
width: i - n,
|
|
43
|
+
height: a - r
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { n as createViewportMinimapProjection, t as hasPositiveFiniteRect, r as projectViewportMinimapRect, i as unprojectViewportMinimapPoint };
|
|
48
|
+
|
|
49
|
+
//# sourceMappingURL=viewport-minimap.utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewport-minimap.utils.js","names":[],"sources":["../../src/viewport/viewport-minimap.utils.ts"],"sourcesContent":["// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.\n// SPDX-License-Identifier: Apache-2.0\n\nimport type { ViewportPoint, ViewportRect } from './viewport.types.js';\n\nconst PROJECTION_PADDING = 0.1;\n\nexport interface ViewportMinimapProjection {\n readonly offsetX: number;\n readonly offsetY: number;\n readonly scale: number;\n}\n\nexport function hasPositiveFiniteRect(rect: ViewportRect): boolean {\n return (\n Number.isFinite(rect.x) &&\n Number.isFinite(rect.y) &&\n Number.isFinite(rect.width) &&\n Number.isFinite(rect.height) &&\n rect.width > 0 &&\n rect.height > 0\n );\n}\n\nexport function createViewportMinimapProjection(\n content: readonly ViewportRect[],\n visible: ViewportRect,\n size: { readonly width: number; readonly height: number }\n): ViewportMinimapProjection | undefined {\n if (size.width <= 0 || size.height <= 0 || visible.width <= 0 || visible.height <= 0) return undefined;\n const extent = unionRects([visible, ...content]);\n if (!extent) return undefined;\n const paddingX = Math.max(extent.width * PROJECTION_PADDING, Number.EPSILON);\n const paddingY = Math.max(extent.height * PROJECTION_PADDING, Number.EPSILON);\n const coverage = {\n x: extent.x - paddingX,\n y: extent.y - paddingY,\n width: extent.width + paddingX * 2,\n height: extent.height + paddingY * 2\n };\n const scale = Math.min(size.width / coverage.width, size.height / coverage.height);\n if (!Number.isFinite(scale) || scale <= 0) return undefined;\n return {\n offsetX: (size.width - coverage.width * scale) / 2 - coverage.x * scale,\n offsetY: (size.height - coverage.height * scale) / 2 - coverage.y * scale,\n scale\n };\n}\n\nexport function projectViewportMinimapRect(rect: ViewportRect, projection: ViewportMinimapProjection): ViewportRect {\n return {\n x: rect.x * projection.scale + projection.offsetX,\n y: rect.y * projection.scale + projection.offsetY,\n width: rect.width * projection.scale,\n height: rect.height * projection.scale\n };\n}\n\nexport function unprojectViewportMinimapPoint(\n point: ViewportPoint,\n projection: ViewportMinimapProjection\n): ViewportPoint {\n return {\n x: (point.x - projection.offsetX) / projection.scale,\n y: (point.y - projection.offsetY) / projection.scale\n };\n}\n\nfunction unionRects(rects: readonly ViewportRect[]): ViewportRect | undefined {\n let left = Number.POSITIVE_INFINITY;\n let top = Number.POSITIVE_INFINITY;\n let right = Number.NEGATIVE_INFINITY;\n let bottom = Number.NEGATIVE_INFINITY;\n for (const rect of rects) {\n if (!hasPositiveFiniteRect(rect)) continue;\n left = Math.min(left, rect.x);\n top = Math.min(top, rect.y);\n right = Math.max(right, rect.x + rect.width);\n bottom = Math.max(bottom, rect.y + rect.height);\n }\n if (!Number.isFinite(left) || !Number.isFinite(top) || right <= left || bottom <= top) return undefined;\n return { x: left, y: top, width: right - left, height: bottom - top };\n}\n"],"mappings":";AAKA,IAAM,IAAqB;AAQ3B,SAAgB,EAAsB,GAA6B;CACjE,OACE,OAAO,SAAS,EAAK,CAAC,KACtB,OAAO,SAAS,EAAK,CAAC,KACtB,OAAO,SAAS,EAAK,KAAK,KAC1B,OAAO,SAAS,EAAK,MAAM,KAC3B,EAAK,QAAQ,KACb,EAAK,SAAS;AAElB;AAEA,SAAgB,EACd,GACA,GACA,GACuC;CACvC,IAAI,EAAK,SAAS,KAAK,EAAK,UAAU,KAAK,EAAQ,SAAS,KAAK,EAAQ,UAAU,GAAG;CACtF,IAAM,IAAS,EAAW,CAAC,GAAS,GAAG,CAAO,CAAC;CAC/C,IAAI,CAAC,GAAQ;CACb,IAAM,IAAW,KAAK,IAAI,EAAO,QAAQ,WAAkC,GACrE,IAAW,KAAK,IAAI,EAAO,SAAS,WAAkC,GACtE,IAAW;EACf,GAAG,EAAO,IAAI;EACd,GAAG,EAAO,IAAI;EACd,OAAO,EAAO,QAAQ,IAAW;EACjC,QAAQ,EAAO,SAAS,IAAW;CACrC,GACM,IAAQ,KAAK,IAAI,EAAK,QAAQ,EAAS,OAAO,EAAK,SAAS,EAAS,MAAM;CAC7E,OAAC,OAAO,SAAS,CAAK,KAAK,KAAS,IACxC,OAAO;EACL,UAAU,EAAK,QAAQ,EAAS,QAAQ,KAAS,IAAI,EAAS,IAAI;EAClE,UAAU,EAAK,SAAS,EAAS,SAAS,KAAS,IAAI,EAAS,IAAI;EACpE;CACF;AACF;AAEA,SAAgB,EAA2B,GAAoB,GAAqD;CAClH,OAAO;EACL,GAAG,EAAK,IAAI,EAAW,QAAQ,EAAW;EAC1C,GAAG,EAAK,IAAI,EAAW,QAAQ,EAAW;EAC1C,OAAO,EAAK,QAAQ,EAAW;EAC/B,QAAQ,EAAK,SAAS,EAAW;CACnC;AACF;AAEA,SAAgB,EACd,GACA,GACe;CACf,OAAO;EACL,IAAI,EAAM,IAAI,EAAW,WAAW,EAAW;EAC/C,IAAI,EAAM,IAAI,EAAW,WAAW,EAAW;CACjD;AACF;AAEA,SAAS,EAAW,GAA0D;CAC5E,IAAI,IAAO,UACP,IAAM,UACN,IAAQ,WACR,IAAS;CACb,KAAK,IAAM,KAAQ,GACZ,EAAsB,CAAI,MAC/B,IAAO,KAAK,IAAI,GAAM,EAAK,CAAC,GAC5B,IAAM,KAAK,IAAI,GAAK,EAAK,CAAC,GAC1B,IAAQ,KAAK,IAAI,GAAO,EAAK,IAAI,EAAK,KAAK,GAC3C,IAAS,KAAK,IAAI,GAAQ,EAAK,IAAI,EAAK,MAAM;CAE5C,OAAC,OAAO,SAAS,CAAI,KAAK,CAAC,OAAO,SAAS,CAAG,KAAK,KAAS,KAAQ,KAAU,IAClF,OAAO;EAAE,GAAG;EAAM,GAAG;EAAK,OAAO,IAAQ;EAAM,QAAQ,IAAS;CAAI;AACtE"}
|