UNPKG

@dcl/react-ecs

Version:
178 lines (177 loc) 5.69 kB
import { engine, UiCanvasInformation as engineUiCanvasInformation } from '@dcl/ecs'; import { parseUiBackground } from './uiBackground'; import { parseUiTransform } from './uiTransform'; let uiScaleFactor = 1; let uiScaleOwner = undefined; const ZERO_INSETS = { top: 0, left: 0, right: 0, bottom: 0 }; let screenInsetArea = { ...ZERO_INSETS }; let screenInsetAreaOwner = undefined; let interactableArea = { ...ZERO_INSETS }; let interactableAreaOwner = undefined; /** * @internal */ export function parseProps(props) { const { uiTransform, uiBackground, ...otherProps } = props; const uiTransformProps = parseUiTransform(uiTransform); const uiBackgroundProps = uiBackground ? { uiBackground: parseUiBackground(uiBackground) } : undefined; return { ...otherProps, uiTransform: uiTransformProps, ...uiBackgroundProps }; } /** * @internal */ export function getScaleAndUnit(scaleUnit) { if (typeof scaleUnit === 'number') { return [scaleUnit, 'vw']; } const value = Number(scaleUnit.slice(0, -2)); if (scaleUnit.endsWith('vh')) return [value, 'vh']; if (scaleUnit.endsWith('vw')) return [value, 'vw']; return [NaN, 'vw']; } /** * @internal */ export function scaleOnDim(scale, dim, pxRatio) { return (dim / 100) * (scale / pxRatio); } /** * @internal */ export function getScaleCtx(_engine = engine) { const UiCanvasInformation = _engine.getComponent(engineUiCanvasInformation.componentId); const canvasInfo = UiCanvasInformation.getOrNull(_engine.RootEntity); if (!canvasInfo) return undefined; const { width, height, devicePixelRatio } = canvasInfo; return { width, height, ratio: devicePixelRatio }; } /** * @internal */ export function getUiScaleFactor() { return uiScaleFactor; } /** * Sets the global UI scale factor. * * The `owner` symbol implements a cooperative reset-protection scheme shared * with {@link resetUiScaleFactor}: * - Writes always succeed — last writer claims ownership (the most recent * `owner` passed to `set` is the one allowed to `reset`). * - Resets from a non-matching owner are ignored, so a stale system can't * stomp the active scale while another system is driving it. * - A reset called without an owner always wins (used by tests / teardown). * * @internal */ export function setUiScaleFactor(nextScale, owner) { if (!Number.isFinite(nextScale) || nextScale < 0) return; if (owner) { uiScaleOwner = owner; } uiScaleFactor = nextScale; } /** * @internal */ export function resetUiScaleFactor(owner) { // No-op for non-owners (see ownership rules on `setUiScaleFactor`). // A reset with no owner always wins — used by tests and teardown. if (owner && uiScaleOwner !== owner) return; uiScaleOwner = undefined; uiScaleFactor = 1; } /** * @internal */ export function getScreenInsetArea() { return { ...screenInsetArea }; } /** * Sets the global screen inset area. * * The `owner` symbol implements a cooperative reset-protection scheme shared * with {@link resetScreenInsetArea}: * - Writes always succeed — last writer claims ownership (the most recent * `owner` passed to `set` is the one allowed to `reset`). * - Resets from a non-matching owner are ignored, so a stale system can't * stomp the active insets while another system is driving them. * - A reset called without an owner always wins (used by tests / teardown). * * @internal */ export function setScreenInsetArea(next, owner) { if (owner) { screenInsetAreaOwner = owner; } screenInsetArea = { top: next.top, left: next.left, right: next.right, bottom: next.bottom }; } /** * @internal */ export function resetScreenInsetArea(owner) { // No-op for non-owners (see ownership rules on `setScreenInsetArea`). // A reset with no owner always wins — used by tests and teardown. if (owner && screenInsetAreaOwner !== owner) return; screenInsetAreaOwner = undefined; screenInsetArea = { ...ZERO_INSETS }; } /** * @internal */ export function getInteractableArea() { return { ...interactableArea }; } /** * Sets the global interactable area. * * The `owner` symbol implements a cooperative reset-protection scheme shared * with {@link resetInteractableArea}: * - Writes always succeed — last writer claims ownership (the most recent * `owner` passed to `set` is the one allowed to `reset`). * - Resets from a non-matching owner are ignored, so a stale system can't * stomp the active area while another system is driving it. * - A reset called without an owner always wins (used by tests / teardown). * * @internal */ export function setInteractableArea(next, owner) { if (owner) { interactableAreaOwner = owner; } interactableArea = { top: next.top, left: next.left, right: next.right, bottom: next.bottom }; } /** * @internal */ export function resetInteractableArea(owner) { // No-op for non-owners (see ownership rules on `setInteractableArea`). // A reset with no owner always wins — used by tests and teardown. if (owner && interactableAreaOwner !== owner) return; interactableAreaOwner = undefined; interactableArea = { ...ZERO_INSETS }; } /** * @internal */ export function calcOnViewport(value, ctx = getScaleCtx()) { const [scale, unit] = getScaleAndUnit(value); if (!ctx) return scale; const { height, width, ratio } = ctx; if (unit === 'vh') return scaleOnDim(scale, height, ratio); // by default, we scale by 'vw' (width) return scaleOnDim(scale, width, ratio); }