UNPKG

react-konva

Version:

React binding to canvas element via Konva framework

149 lines (148 loc) 5.32 kB
/** * Based on ReactArt.js * Copyright (c) 2017-present Lavrenov Anton. * All rights reserved. * * MIT */ 'use strict'; import React from 'react'; if (React.version.indexOf('19') === -1) { throw new Error('react-konva version 19 is only compatible with React 19. Make sure to have the last version of react-konva and react or downgrade react-konva to version 18.'); } import Konva from 'konva/lib/Core.js'; import ReactFiberReconciler from 'react-reconciler'; import { ConcurrentRoot } from 'react-reconciler/constants.js'; import * as HostConfig from './ReactKonvaHostConfig.js'; import { applyNodeProps, toggleStrictMode } from './makeUpdates.js'; import { useContextBridge, FiberProvider } from 'its-fine'; function usePrevious(value) { const ref = React.useRef({}); React.useLayoutEffect(() => { ref.current = value; }); React.useLayoutEffect(() => { return () => { // when using suspense it is possible that stage is unmounted // but React still keep component ref // in that case we need to manually flush props // we have a special test for that ref.current = {}; }; }, []); return ref.current; } const useIsReactStrictMode = () => { const memoCount = React.useRef(0); // in strict mode, memo will be called twice React.useMemo(() => { memoCount.current++; }, []); return memoCount.current > 1; }; const StageWrap = (props) => { const container = React.useRef(null); const stage = React.useRef(null); const fiberRef = React.useRef(null); const oldProps = usePrevious(props); const Bridge = useContextBridge(); const pendingDestroy = React.useRef(null); const _setRef = (stage) => { const { forwardedRef } = props; if (!forwardedRef) { return; } if (typeof forwardedRef === 'function') { forwardedRef(stage); } else { forwardedRef.current = stage; } }; const isStrictMode = useIsReactStrictMode(); const destroyStage = () => { _setRef(null); KonvaRenderer.updateContainer(null, fiberRef.current, null); stage.current?.destroy(); stage.current = null; }; React.useLayoutEffect(() => { // Cancel any pending destruction (happens during re-ordering in strict mode) if (pendingDestroy.current) { clearTimeout(pendingDestroy.current); pendingDestroy.current = null; } // If stage already exists (re-ordering scenario), reuse it if (stage.current) { _setRef(stage.current); } else { stage.current = new Konva.Stage({ width: props.width, height: props.height, container: container.current, }); _setRef(stage.current); // @ts-ignore fiberRef.current = KonvaRenderer.createContainer(stage.current, ConcurrentRoot, null, false, null, '', console.error, console.error, console.error, null); KonvaRenderer.updateContainer(React.createElement(Bridge, {}, props.children), fiberRef.current, null, () => { }); } return () => { if (isStrictMode) { // Delay destruction to allow cancellation on remount pendingDestroy.current = setTimeout(destroyStage, 0); } else { destroyStage(); } }; }, []); React.useLayoutEffect(() => { _setRef(stage.current); applyNodeProps(stage.current, props, oldProps); KonvaRenderer.updateContainer(React.createElement(Bridge, {}, props.children), fiberRef.current, null); }); return React.createElement('div', { ref: container, id: props.id, accessKey: props.accessKey, className: props.className, role: props.role, style: props.style, tabIndex: props.tabIndex, title: props.title, }); }; export const Layer = 'Layer'; export const FastLayer = 'FastLayer'; export const Group = 'Group'; export const Label = 'Label'; export const Rect = 'Rect'; export const Circle = 'Circle'; export const Ellipse = 'Ellipse'; export const Wedge = 'Wedge'; export const Line = 'Line'; export const Sprite = 'Sprite'; export const Image = 'Image'; export const Text = 'Text'; export const TextPath = 'TextPath'; export const Star = 'Star'; export const Ring = 'Ring'; export const Arc = 'Arc'; export const Tag = 'Tag'; export const Path = 'Path'; export const RegularPolygon = 'RegularPolygon'; export const Arrow = 'Arrow'; export const Shape = 'Shape'; export const Transformer = 'Transformer'; export const version = '19.2.1'; // @ts-ignore export const KonvaRenderer = ReactFiberReconciler(HostConfig); // Update Stage component declaration export const Stage = React.forwardRef((props, ref) => { return React.createElement(FiberProvider, {}, React.createElement(StageWrap, { ...props, forwardedRef: ref })); }); export const useStrictMode = toggleStrictMode; // export useContextBridge from its-fine for reuse in react-konva-utils // so react-konva-utils don't use its own version of its-fine (it is possible on pnpm) export { useContextBridge };