UNPKG

@tldraw/editor

Version:

tldraw infinite canvas SDK (editor).

103 lines (92 loc) 3.93 kB
import { atom } from '@tldraw/state' import { getGlobalWindow } from '../utils/dom' /** * An object that contains information about the current device and environment. * This object is not reactive and will not update automatically when the environment changes, * so only include values that are fixed, such as the user's browser and operating system. * * @public */ const tlenv = { isSafari: false, isIos: false, isChromeForIos: false, isFirefox: false, isAndroid: false, isWebview: false, isDarwin: false, hasCanvasSupport: false, } let isForcedFinePointer = false if (typeof window !== 'undefined') { if ('navigator' in window) { tlenv.isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent) tlenv.isIos = !!navigator.userAgent.match(/iPad/i) || !!navigator.userAgent.match(/iPhone/i) tlenv.isChromeForIos = /crios.*safari/i.test(navigator.userAgent) tlenv.isFirefox = /firefox/i.test(navigator.userAgent) tlenv.isAndroid = /android/i.test(navigator.userAgent) tlenv.isDarwin = getGlobalWindow().navigator.userAgent.toLowerCase().indexOf('mac') > -1 } tlenv.hasCanvasSupport = 'Promise' in window && 'HTMLCanvasElement' in window isForcedFinePointer = tlenv.isFirefox && !tlenv.isAndroid && !tlenv.isIos } /** * An atom that contains information about the current device and environment. * This object is reactive and will update automatically when the environment changes. * Use it for values that may change over time, such as the pointer type. * * @public */ const tlenvReactive = atom('tlenvReactive', { // Whether the user's device has a coarse pointer. This is dynamic on many systems, especially // on touch-screen laptops, which will become "coarse" if the user touches the screen. // See https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@media/pointer#coarse isCoarsePointer: false, // Whether the user's display supports P3 color space. This is dynamic because a window can // move between displays with different color gamut support. supportsP3ColorSpace: false, }) if (typeof window !== 'undefined') { const canRenderP3 = typeof CSS !== 'undefined' && CSS.supports('color', 'color(display-p3 1 1 1)') if (canRenderP3) { const p3mql = window.matchMedia('(color-gamut: p3)') const updateSupportsP3 = () => { const supportsP3 = p3mql.matches if (supportsP3 !== tlenvReactive.__unsafe__getWithoutCapture().supportsP3ColorSpace) { tlenvReactive.update((prev) => ({ ...prev, supportsP3ColorSpace: supportsP3 })) } } updateSupportsP3() p3mql.addEventListener('change', updateSupportsP3) } } if (typeof window !== 'undefined' && !isForcedFinePointer) { const mql = getGlobalWindow().matchMedia && getGlobalWindow().matchMedia('(any-pointer: coarse)') const isCurrentCoarsePointer = () => tlenvReactive.__unsafe__getWithoutCapture().isCoarsePointer if (mql) { // 1. Update the coarse pointer automatically when the media query changes const updateIsCoarsePointer = () => { const isCoarsePointer = mql.matches if (isCoarsePointer !== isCurrentCoarsePointer()) { tlenvReactive.update((prev) => ({ ...prev, isCoarsePointer: isCoarsePointer })) } } updateIsCoarsePointer() mql.addEventListener('change', updateIsCoarsePointer) } // 2. Also update the coarse pointer state when a pointer down event occurs. We need `capture: true` // here because the tldraw component itself stops propagation on pointer events it receives. getGlobalWindow().addEventListener( 'pointerdown', (e: PointerEvent) => { // when the user interacts with a mouse, we assume they have a fine pointer. // otherwise, we assume they have a coarse pointer. const isCoarseEvent = e.pointerType !== 'mouse' if (isCoarseEvent !== isCurrentCoarsePointer()) { tlenvReactive.update((prev) => ({ ...prev, isCoarsePointer: isCoarseEvent })) } }, { capture: true } ) } export { tlenv, tlenvReactive }