first commit

This commit is contained in:
2026-10-17 12:09:36 -04:00
commit 56046a63fc
1149 changed files with 658055 additions and 0 deletions
+69
View File
@@ -0,0 +1,69 @@
import { flushSync } from 'react-dom';
const pending = new Set();
const pendingUnmounts = new Set();
const enqueue = typeof queueMicrotask === 'function'
? queueMicrotask
: (callback) => Promise.resolve().then(callback);
let depth = 0;
const batches = new WeakMap();
export function addPendingUnmount(callback) {
pendingUnmounts.add(callback);
return () => {
pendingUnmounts.delete(callback);
};
}
// Hidden Stages may be deleted without another layout cleanup. Prepare their
// root removals before either a scheduled render or a Stage's inline flush.
export function prepareUnmounts() {
for (const callback of pendingUnmounts)
callback();
}
export function getEventBatch(batch) {
if (!batch)
return batchEvents;
let wrapped = batches.get(batch);
if (!wrapped) {
// Konva deduplicates shared callbacks when several Stages drag together.
wrapped = (run) => batchEvents(() => batch(run));
batches.set(batch, wrapped);
}
return wrapped;
}
// React's host scheduler stays asynchronous while a handler or subscription is
// running. A completed native input batch can finish those same scheduled jobs.
export function scheduleMicrotask(callback) {
const run = () => {
if (pending.delete(run)) {
prepareUnmounts();
callback();
}
};
pending.add(run);
enqueue(run);
}
export function batchEvents(callback) {
if (depth)
return callback();
depth++;
try {
// DOM-owned state must be scheduled inside this scope. Flush it before
// finishing canvas work, so Stage can transfer the final DOM-owned props.
flushSync(callback);
}
finally {
try {
// Canvas effects can schedule DOM state, and DOM effects can schedule
// more canvas work. Finish that work in a DOM scope too. Empty input
// does not add a second flush, and queued microtasks become no-ops.
while (pending.size) {
flushSync(() => {
for (const run of pending)
run();
});
}
}
finally {
depth--;
}
}
}
+10
View File
@@ -0,0 +1,10 @@
/**
* Based on ReactArt.js
* Copyright (c) 2017-present Lavrenov Anton.
* All rights reserved.
*
* MIT
*/
'use strict';
import 'konva';
export * from './ReactKonvaCore.js';
+119
View File
@@ -0,0 +1,119 @@
// special file for minimal import
import * as React from 'react';
import * as ReactReconciler from 'react-reconciler';
import Konva from 'konva';
import { useContextBridge } from 'its-fine';
export interface KonvaNodeEvents {
onMouseOver?(evt: Konva.KonvaEventObject<MouseEvent>): void;
onMouseMove?(evt: Konva.KonvaEventObject<MouseEvent>): void;
onMouseOut?(evt: Konva.KonvaEventObject<MouseEvent>): void;
onMouseEnter?(evt: Konva.KonvaEventObject<MouseEvent>): void;
onMouseLeave?(evt: Konva.KonvaEventObject<MouseEvent>): void;
onMouseDown?(evt: Konva.KonvaEventObject<MouseEvent>): void;
onMouseUp?(evt: Konva.KonvaEventObject<MouseEvent>): void;
onWheel?(evt: Konva.KonvaEventObject<WheelEvent>): void;
onClick?(evt: Konva.KonvaEventObject<MouseEvent>): void;
onDblClick?(evt: Konva.KonvaEventObject<MouseEvent>): void;
onTouchStart?(evt: Konva.KonvaEventObject<TouchEvent>): void;
onTouchMove?(evt: Konva.KonvaEventObject<TouchEvent>): void;
onTouchEnd?(evt: Konva.KonvaEventObject<TouchEvent>): void;
onTouchCancel?(evt: Konva.KonvaEventObject<TouchEvent>): void;
onTap?(evt: Konva.KonvaEventObject<TouchEvent>): void;
onDblTap?(evt: Konva.KonvaEventObject<TouchEvent>): void;
onDragStart?(evt: Konva.KonvaEventObject<DragEvent>): void;
onDragMove?(evt: Konva.KonvaEventObject<DragEvent>): void;
onDragEnd?(evt: Konva.KonvaEventObject<DragEvent>): void;
onTransform?(evt: Konva.KonvaEventObject<Event>): void;
onTransformStart?(evt: Konva.KonvaEventObject<Event>): void;
onTransformEnd?(evt: Konva.KonvaEventObject<Event>): void;
onContextMenu?(evt: Konva.KonvaEventObject<PointerEvent>): void;
onPointerDown?(evt: Konva.KonvaEventObject<PointerEvent>): void;
onPointerMove?(evt: Konva.KonvaEventObject<PointerEvent>): void;
onPointerUp?(evt: Konva.KonvaEventObject<PointerEvent>): void;
onPointerCancel?(evt: Konva.KonvaEventObject<PointerEvent>): void;
onPointerEnter?(evt: Konva.KonvaEventObject<PointerEvent>): void;
onPointerLeave?(evt: Konva.KonvaEventObject<PointerEvent>): void;
onPointerOver?(evt: Konva.KonvaEventObject<PointerEvent>): void;
onPointerOut?(evt: Konva.KonvaEventObject<PointerEvent>): void;
onPointerClick?(evt: Konva.KonvaEventObject<PointerEvent>): void;
onPointerDblClick?(evt: Konva.KonvaEventObject<PointerEvent>): void;
onGotPointerCapture?(evt: Konva.KonvaEventObject<PointerEvent>): void;
onLostPointerCapture?(evt: Konva.KonvaEventObject<PointerEvent>): void;
}
export interface KonvaNodeComponent<
Node extends Konva.Node,
Props = Konva.NodeConfig
// We use React.ClassAttributes to fake the 'ref' attribute. This will ensure
// consumers get the proper 'Node' type in 'ref' instead of the wrapper
// component type.
> extends React.FC<Props & KonvaNodeEvents & React.ClassAttributes<Node>> {}
export interface StageProps
extends Konva.NodeConfig,
KonvaNodeEvents,
Pick<
React.HTMLAttributes<HTMLDivElement>,
'className' | 'role' | 'style' | 'tabIndex' | 'title'
> {
/**
* Wraps native Konva input inside react-konva's React batch (Konva 10.5+).
* Must call its callback exactly once, synchronously. For example: runInAction.
*/
eventBatchFunc?: (callback: () => void) => void;
}
// Stage is the only React component because the others are stubs that only know how
// to be rendered when they are under stage. Since there is no real backing
// class and are in reality are a string literal we don't want users to actually
// try and use them as a type. By defining them as a variable with an interface
// consumers will not be able to use the values as a type or constructor.
// The down side to this approach, is that typescript thinks the type is a
// function, but if the user tries to call it a runtime exception will occur.
export var Stage: KonvaNodeComponent<Konva.Stage, StageProps>;
export var Layer: KonvaNodeComponent<Konva.Layer, Konva.LayerConfig>;
export var FastLayer: KonvaNodeComponent<Konva.FastLayer, Konva.LayerConfig>;
export var Group: KonvaNodeComponent<Konva.Group, Konva.ContainerConfig>;
export var Label: KonvaNodeComponent<Konva.Label, Konva.LabelConfig>;
/** Shapes */
export var Rect: KonvaNodeComponent<Konva.Rect, Konva.RectConfig>;
export var Circle: KonvaNodeComponent<Konva.Circle, Konva.CircleConfig>;
export var Ellipse: KonvaNodeComponent<Konva.Ellipse, Konva.EllipseConfig>;
export var Wedge: KonvaNodeComponent<Konva.Wedge, Konva.WedgeConfig>;
export var Transformer: KonvaNodeComponent<
Konva.Transformer,
Konva.TransformerConfig
>;
export var Line: KonvaNodeComponent<Konva.Line, Konva.LineConfig>;
export var Sprite: KonvaNodeComponent<Konva.Sprite, Konva.SpriteConfig>;
export var Image: KonvaNodeComponent<Konva.Image, Konva.ImageConfig>;
export var Text: KonvaNodeComponent<Konva.Text, Konva.TextConfig>;
export var TextPath: KonvaNodeComponent<Konva.TextPath, Konva.TextPathConfig>;
export var Star: KonvaNodeComponent<Konva.Star, Konva.StarConfig>;
export var Ring: KonvaNodeComponent<Konva.Ring, Konva.RingConfig>;
export var Arc: KonvaNodeComponent<Konva.Arc, Konva.ArcConfig>;
export var Tag: KonvaNodeComponent<Konva.Tag, Konva.TagConfig>;
export var Path: KonvaNodeComponent<Konva.Path, Konva.PathConfig>;
export var RegularPolygon: KonvaNodeComponent<
Konva.RegularPolygon,
Konva.RegularPolygonConfig
>;
export var Arrow: KonvaNodeComponent<Konva.Arrow, Konva.ArrowConfig>;
export var Shape: KonvaNodeComponent<Konva.Shape, Konva.ShapeConfig>;
export var useStrictMode: (useStrictMode: boolean) => void;
export var KonvaRenderer: ReactReconciler.Reconciler<
any,
any,
any,
any,
any,
any
>;
export var version: string;
export { useContextBridge };
+208
View File
@@ -0,0 +1,208 @@
/**
* Based on ReactArt.js
* Copyright (c) 2017-present Lavrenov Anton.
* All rights reserved.
*
* MIT
*/
'use strict';
import React from 'react';
import { useFormStatus } from 'react-dom';
const [reactMajor, reactMinor] = React.version.split('.').map(Number);
if (reactMajor !== 19 || reactMinor < 3) {
throw new Error('react-konva requires React 19.3 or later within React 19. ' +
'Install matching versions of react and react-dom. ' +
'For React 19.2, use react-konva 19.2. For React 18, use react-konva 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 { getEventBatch, addPendingUnmount, prepareUnmounts } from './EventBatch.js';
import { applyNodeProps, toggleStrictMode, EVENTS_NAMESPACE, } from './makeUpdates.js';
import { useContextBridge, useFiber, traverseFiber, FiberProvider } from 'its-fine';
function SuspendCanvas({ promise }) {
throw promise;
}
const StageWrap = (props) => {
const container = React.useRef(null);
const stage = React.useRef(null);
const fiberRef = React.useRef(null);
const children = React.useRef(null);
const suspension = React.useRef(null);
// DOM and Konva reconcilers have separate useId counters.
const identifierPrefix = React.useId();
const formStatus = useFormStatus();
const previousProps = React.useRef({});
// Keep the render-time snapshot when React replays layout effects.
const oldProps = previousProps.current;
const Bridge = useContextBridge();
const fiber = useFiber();
const isStrictMode = !!traverseFiber(fiber, true, (node) => node.type === React.StrictMode);
const effectsDisconnected = React.useRef(false);
const isUnmounting = React.useRef(false);
const cancelUnmount = React.useRef(null);
// Insertion effects stay mounted during hiding and StrictMode replay.
// Activity has already disconnected layout and passive effects when hidden.
// Its remaining insertion cleanups must still run inside a DOM commit.
React.useInsertionEffect(() => {
isUnmounting.current = false;
return () => {
isUnmounting.current = true;
if (effectsDisconnected.current && stage.current)
destroyStage();
};
}, []);
const destroyStage = () => {
cancelUnmount.current?.();
cancelUnmount.current = null;
prepareUnmounts();
// Discard queued child work before destroying the Stage, including updates
// from external stores whose objects are no longer alive.
KonvaRenderer.flushSyncFromReconciler(() => {
KonvaRenderer.updateContainer(null, fiberRef.current, null);
});
stage.current?.destroy();
stage.current?.off(EVENTS_NAMESPACE);
stage.current = null;
suspension.current?.resolve();
suspension.current = null;
};
const setCanvasVisibility = (mode) => {
if (mode === 'suspended') {
if (!suspension.current) {
let resolve;
const promise = new Promise((done) => {
resolve = done;
});
suspension.current = { promise, resolve };
}
}
else {
suspension.current?.resolve();
suspension.current = null;
}
KonvaRenderer.updateContainer(React.createElement(React.Activity, {
mode: mode === 'hidden' ? 'hidden' : 'visible',
children: React.createElement(React.Suspense, { fallback: null }, children.current, suspension.current &&
React.createElement(SuspendCanvas, {
promise: suspension.current.promise,
})),
}), fiberRef.current, null);
prepareUnmounts();
// Parent layout effects must see the committed canvas nodes and refs.
KonvaRenderer.flushSyncWork();
};
React.useLayoutEffect(() => {
cancelUnmount.current?.();
cancelUnmount.current = null;
// Reconnecting layout effects reuses the existing Stage.
if (!stage.current) {
stage.current = new Konva.Stage({
width: props.width,
height: props.height,
container: container.current,
});
fiberRef.current = KonvaRenderer.createContainer(stage.current, ConcurrentRoot, null, isStrictMode, null, identifierPrefix, console.error, console.error, console.error, null, null);
}
return () => {
if (!isUnmounting.current) {
// If this hidden Stage is deleted, discard queued child work before
// another root or the scheduler flushes it.
const unmount = () => {
if (!isUnmounting.current)
return;
cancelUnmount.current?.();
cancelUnmount.current = null;
KonvaRenderer.updateContainer(null, fiberRef.current, null);
};
cancelUnmount.current = addPendingUnmount(unmount);
// Suspense disconnects layout effects but keeps passive effects.
setCanvasVisibility('suspended');
}
else {
destroyStage();
}
};
}, []);
React.useEffect(() => {
effectsDisconnected.current = false;
return () => {
effectsDisconnected.current = true;
// Activity and StrictMode also disconnect the DOM passive effects.
if (!stage.current)
return;
if (isUnmounting.current)
destroyStage();
else
setCanvasVisibility('hidden');
};
}, []);
React.useLayoutEffect(() => {
// Older Konva versions use the normal asynchronous React scheduler.
if (typeof stage.current.eventBatchFunc === 'function') {
stage.current.eventBatchFunc(getEventBatch(props.eventBatchFunc));
}
}, [props.eventBatchFunc]);
React.useImperativeHandle(props.forwardedRef, () => stage.current, []);
React.useLayoutEffect(() => {
previousProps.current = props;
applyNodeProps(stage.current, props, oldProps);
// updateContainer schedules sync-lane work; with async scheduleMicrotask
// the work is queued, so flushSyncWork() drains it inline here so that
// a parent useLayoutEffect can read Konva nodes added by subscribing
// children. (Lighter than flushSyncFromReconciler — no callback wrapper.)
children.current = React.createElement(HostConfig.HostTransitionContext.Provider, { value: formStatus }, React.createElement(Bridge, {}, props.children));
setCanvasVisibility('visible');
});
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.3.2';
const rendererConfig = {
...HostConfig,
// React's public context type omits fields used by the reconciler.
HostTransitionContext: HostConfig.HostTransitionContext,
rendererVersion: version,
rendererPackageName: 'react-konva',
extraDevToolsConfig: null,
};
export const KonvaRenderer = ReactFiberReconciler(rendererConfig);
KonvaRenderer.injectIntoDevTools();
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 };
+247
View File
@@ -0,0 +1,247 @@
import React from 'react';
import Konva from 'konva/lib/Core.js';
import { applyNodeProps, updatePicture, EVENTS_NAMESPACE, } from './makeUpdates.js';
export { unstable_now as now, unstable_IdlePriority as idlePriority, unstable_runWithPriority as run, } from 'scheduler';
import {
// NoEventPriority,
DefaultEventPriority, DiscreteEventPriority, } from 'react-reconciler/constants.js';
const NO_CONTEXT = {};
// for react-spring capability
Konva.Node.prototype._applyProps = applyNodeProps;
// let currentUpdatePriority: number = NoEventPriority;
let currentUpdatePriority = DefaultEventPriority;
export function appendInitialChild(parentInstance, child) {
if (typeof child === 'string') {
// Noop for string children of Text (eg <Text>foo</Text>)
console.error(`Do not use plain text as child of Konva.Node. You are using text: ${child}`);
return;
}
parentInstance.add(child);
updatePicture(parentInstance);
}
export function createInstance(type, props, internalInstanceHandle) {
let NodeClass = Konva[type];
if (!NodeClass) {
console.error(`Konva has no node with the type ${type}. Group will be used instead. If you use minimal version of react-konva, just import required nodes into Konva: "import "konva/lib/shapes/${type}" If you want to render DOM elements as part of canvas tree take a look into this demo: https://konvajs.github.io/docs/react/DOM_Portal.html`);
NodeClass = Konva.Group;
}
// we need to split props into events and non events
// we we can pass non events into constructor directly
// that way the performance should be better
// we we apply change "applyNodeProps"
// then it will trigger change events on first run
// but we don't need them!
const propsWithoutEvents = {};
const propsWithOnlyEvents = {};
for (var key in props) {
// ignore ref
if (key === 'ref') {
continue;
}
var isEvent = key.slice(0, 2) === 'on';
if (isEvent) {
propsWithOnlyEvents[key] = props[key];
}
else {
propsWithoutEvents[key] = props[key];
}
}
const instance = new NodeClass(propsWithoutEvents);
applyNodeProps(instance, propsWithOnlyEvents);
return instance;
}
export function createTextInstance(text, rootContainerInstance, internalInstanceHandle) {
console.error(`Text components are not supported for now in ReactKonva. Your text is: "${text}"`);
}
export function finalizeInitialChildren(domElement, type, props) {
return false;
}
export function getPublicInstance(instance) {
return instance;
}
// Fragments group Konva children but have no DOM instance or Fragment methods.
// This matches React's noop renderer.
export function createFragmentInstance() {
return null;
}
export function updateFragmentInstanceFiber() { }
export function commitNewChildToFragmentInstance() { }
export function deleteChildFromFragmentInstance() { }
let finishUpdateBatch;
export function prepareForCommit() {
// Older supported Konva versions do not expose commit batching.
finishUpdateBatch = Konva.beginUpdateBatch?.();
return null;
}
export function preparePortalMount() {
return null;
}
export function resetAfterCommit() {
const finish = finishUpdateBatch;
finishUpdateBatch = undefined;
// React layout effects must see the final Transformer geometry.
finish?.();
}
export function resetTextContent(domElement) {
// Noop
}
export function getRootHostContext() {
return NO_CONTEXT;
}
export function getChildHostContext() {
return NO_CONTEXT;
}
export const scheduleTimeout = setTimeout;
export const cancelTimeout = clearTimeout;
export const supportsMicrotasks = true;
// Async — fixes the mobx-react-lite snapshot race. Stage's useLayoutEffect
// then explicitly calls `flushSyncWork()` to keep parent useLayoutEffect able
// to read Konva nodes added by subscribing children (see ReactKonvaCore.tsx).
export { scheduleMicrotask } from './EventBatch.js';
export const noTimeout = -1;
// export const schedulePassiveEffects = scheduleDeferredCallback;
// export const cancelPassiveEffects = cancelDeferredCallback;
export function shouldSetTextContent(type, props) {
return false;
}
// The Konva renderer is secondary to the React DOM renderer.
export const isPrimaryRenderer = false;
export const warnsIfNotActing = false;
export const supportsMutation = true;
export const supportsPersistence = false;
export const supportsHydration = false;
export function appendChild(parentInstance, child) {
if (child.parent === parentInstance) {
child.moveToTop();
}
else {
parentInstance.add(child);
}
updatePicture(parentInstance);
}
export const appendChildToContainer = appendChild;
export function insertBefore(parentInstance, child, beforeChild) {
// child._remove() will not stop dragging
// but child.remove() will stop it, but we don't need it
// removing will reset zIndexes
child._remove();
parentInstance.add(child);
child.setZIndex(beforeChild.getZIndex());
updatePicture(parentInstance);
}
export const insertInContainerBefore = insertBefore;
export function removeChild(parentInstance, child) {
child.destroy();
child.off(EVENTS_NAMESPACE);
updatePicture(parentInstance);
}
export const removeChildFromContainer = removeChild;
export function commitTextUpdate(textInstance, oldText, newText) {
console.error(`Text components are not yet supported in ReactKonva. You text is: "${newText}"`);
}
export function commitMount(instance, type, newProps) {
// Noop
}
export function commitUpdate(instance, type, oldProps, newProps) {
applyNodeProps(instance, newProps, oldProps);
}
export function hideInstance(instance) {
instance.hide();
updatePicture(instance);
}
export function hideTextInstance(textInstance) {
// Noop
}
export function unhideInstance(instance, props) {
if (props.visible == null || props.visible) {
instance.show();
updatePicture(instance);
}
}
export function unhideTextInstance(textInstance, text) {
// Noop
}
export function clearContainer(container) {
// Noop
}
export function detachDeletedInstance(instance) {
// React visits every deleted host node here, including descendants of a
// removed container. Konva.destroy() alone does not release their listeners.
instance.off(EVENTS_NAMESPACE);
}
export function getInstanceFromNode() {
return null;
}
export function bindToConsole(methodName, args) {
return console[methodName].bind(console, ...args);
}
export function beforeActiveInstanceBlur() { }
export function afterActiveInstanceBlur() { }
export function prepareScopeUpdate() { }
export function getInstanceFromScope() {
return null;
}
export function setCurrentUpdatePriority(newPriority) {
currentUpdatePriority = newPriority;
}
export function getCurrentUpdatePriority() {
return currentUpdatePriority;
}
// Load-bearing: pins secondary commits to React's sync lane so they land
// before the scheduler yields. Required for parent useLayoutEffect to read
// Konva nodes added by subscribing children. Changing to Default/Continuous/
// Idle breaks ~18 tests in test/01-mounting and test/16-scheduling-invariants.
export function resolveUpdatePriority() {
return DiscreteEventPriority;
}
export function shouldAttemptEagerTransition() {
return false;
}
export function trackSchedulerEvent() { }
export function resolveEventType() {
return null;
}
export function resolveEventTimeStamp() {
return -1.1;
}
export function requestPostPaintCallback() { }
export function maySuspendCommit() {
// React 19.3 calls this during render only for nodes inside ViewTransition.
// Reject before commit, where a view-transition error can escape boundaries.
throw new Error('ViewTransition is not supported inside a Stage. ' +
'Place ViewTransition around Stage to animate its DOM container.');
}
export const createViewTransitionInstance = maySuspendCommit;
export const maySuspendCommitOnUpdate = maySuspendCommit;
export const maySuspendCommitInSyncRender = maySuspendCommit;
// Empty ViewTransition boundaries can reach commit without any Konva nodes.
// Finish those commits without animation so ref errors can reach a boundary.
export function startViewTransition(suspendedState, rootContainer, transitionTypes, mutationCallback, layoutCallback, afterMutationCallback, spawnedWorkCallback, passiveCallback, errorCallback, blockedCallback, finishedAnimation) {
mutationCallback();
layoutCallback();
finishedAnimation?.();
spawnedWorkCallback();
return null;
}
export function preloadInstance() {
return true;
}
export function startSuspendingCommit() { }
export function suspendInstance() { }
// React also calls this for Suspense retries without a ViewTransition component.
export function suspendOnActiveViewTransition() { }
export function waitForCommitToBeReady() {
return null;
}
export function getSuspendedCommitReason() {
return null;
}
// Stage provides the surrounding DOM form status. Standalone roots stay idle.
export const NotPendingTransition = Object.freeze({
pending: false,
data: null,
method: null,
action: null,
});
export const HostTransitionContext = /* @__PURE__ */ React.createContext(NotPendingTransition);
export function resetFormInstance() { }
+122
View File
@@ -0,0 +1,122 @@
import { Konva } from 'konva/lib/Global.js';
const propsToSkip = {
children: true,
ref: true,
key: true,
style: true,
forwardedRef: true,
// Stage's native input boundary is owned by the renderer.
eventBatchFunc: true,
unstable_applyCache: true,
unstable_applyDrawHitFromCache: true,
};
let zIndexWarningShowed = false;
let dragWarningShowed = false;
export const EVENTS_NAMESPACE = '.react-konva-event';
let useStrictMode = false;
export function toggleStrictMode(value) {
useStrictMode = value;
}
const DRAGGABLE_WARNING = `ReactKonva: You have a Konva node with draggable = true and position defined but no onDragMove or onDragEnd events are handled.
Position of a node will be changed during drag&drop, so you should update state of the react app as well.
Consider to add onDragMove or onDragEnd events.
For more info see: https://github.com/konvajs/react-konva/issues/256
`;
const Z_INDEX_WARNING = `ReactKonva: You are using "zIndex" attribute for a Konva node.
react-konva may get confused with ordering. Just define correct order of elements in your render function of a component.
For more info see: https://github.com/konvajs/react-konva/issues/194
`;
const EMPTY_PROPS = {};
export function applyNodeProps(instance, props, oldProps = EMPTY_PROPS) {
// don't use zIndex in react-konva
if (!zIndexWarningShowed && 'zIndex' in props) {
console.warn(Z_INDEX_WARNING);
zIndexWarningShowed = true;
}
// check correct draggable usage
if (!dragWarningShowed && props.draggable) {
var hasPosition = props.x !== undefined || props.y !== undefined;
var hasEvents = props.onDragEnd || props.onDragMove;
if (hasPosition && !hasEvents) {
console.warn(DRAGGABLE_WARNING);
dragWarningShowed = true;
}
}
var updatedProps = {};
var hasUpdates = false;
// check old props
// we need to unset properties that are not in new props
// and remove all events
for (var key in oldProps) {
if (propsToSkip[key]) {
continue;
}
var isEvent = key.slice(0, 2) === 'on';
var propChanged = oldProps[key] !== props[key];
// if that is a changed event, we need to remove it
if (isEvent && propChanged) {
var eventName = key.substr(2).toLowerCase();
if (eventName.substr(0, 7) === 'content') {
eventName =
'content' +
eventName.substr(7, 1).toUpperCase() +
eventName.substr(8);
}
// Remove by namespace because react-konva binds at most one listener
// for each event in that namespace.
instance.off(eventName + EVENTS_NAMESPACE);
}
var toRemove = !props.hasOwnProperty(key);
if (!isEvent && toRemove) {
updatedProps[key] = undefined;
hasUpdates = true;
}
}
var strictUpdate = useStrictMode || props._useStrictMode;
const newEvents = {};
for (var key in props) {
if (propsToSkip[key]) {
continue;
}
var isEvent = key.slice(0, 2) === 'on';
var toAdd = oldProps[key] !== props[key];
if (isEvent && toAdd) {
var eventName = key.substr(2).toLowerCase();
if (eventName.substr(0, 7) === 'content') {
eventName =
'content' +
eventName.substr(7, 1).toUpperCase() +
eventName.substr(8);
}
// check that event is not undefined
if (props[key]) {
newEvents[eventName] = props[key];
}
}
if (!isEvent &&
(props[key] !== oldProps[key] ||
(strictUpdate && props[key] !== instance.getAttr(key)))) {
hasUpdates = true;
updatedProps[key] = props[key];
}
}
if (hasUpdates) {
instance.setAttrs(updatedProps);
updatePicture(instance);
}
// subscribe to events AFTER we set attrs
// we need it to fix https://github.com/konvajs/react-konva/issues/471
// settings attrs may add events. Like "draggable: true" will add "mousedown" listener
for (var eventName in newEvents) {
// first clear any existing listeners, it is required for strict mode
instance.off(eventName + EVENTS_NAMESPACE);
// then attach new one
instance.on(eventName + EVENTS_NAMESPACE, newEvents[eventName]);
}
}
export function updatePicture(node) {
if (!Konva.autoDrawEnabled) {
var drawingNode = node.getLayer() || node.getStage();
drawingNode && drawingNode.batchDraw();
}
}
+1
View File
@@ -0,0 +1 @@
{"type":"module"}