first commit
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Anton Lavrenov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
<p align="center">
|
||||
<img src="https://konvajs.org/img/icon.png" alt="Konva logo" height="60" />
|
||||
</p>
|
||||
|
||||
# React Konva
|
||||
|
||||
[](https://opencollective.com/konva)
|
||||
[](http://badge.fury.io/js/react-konva)
|
||||
[](https://github.com/konvajs/react-konva/actions/workflows/test.yml)
|
||||
|
||||
React Konva provides declarative React components for the [Konva](https://konvajs.org/) 2D canvas scene graph. Use it to build design editors, whiteboards, diagrams, annotations, and other interactive graphics.
|
||||
|
||||
React Konva is MIT licensed. It supports Konva shapes and events. It does not support React Native.
|
||||
|
||||
- [React tutorial](https://konvajs.org/docs/react/index.html)
|
||||
- [Live demos](https://konvajs.org/docs/sandbox.html)
|
||||
- [Konva API](https://konvajs.org/api/Konva.html)
|
||||
- [Star the project](https://github.com/konvajs/react-konva)
|
||||
|
||||
[](https://konvajs.org/docs/react/Transformer.html)
|
||||
|
||||
## Install
|
||||
|
||||
React Konva 19.3 requires React and React DOM `^19.3.0`.
|
||||
For React 19.2, install the latest React Konva 19.2 release.
|
||||
For React 18, install the latest React Konva 18 release.
|
||||
|
||||
```bash
|
||||
npm install react-konva konva
|
||||
```
|
||||
|
||||
## Quick example
|
||||
|
||||
```javascript
|
||||
import {useState} from 'react';
|
||||
import {Stage, Layer, Rect} from 'react-konva';
|
||||
|
||||
export default function App() {
|
||||
const [color, setColor] = useState('#00a8e8');
|
||||
return (
|
||||
<Stage width={600} height={400}>
|
||||
<Layer>
|
||||
<Rect
|
||||
x={50} y={50} width={120} height={80}
|
||||
fill={color} draggable
|
||||
onClick={() => setColor(color === '#00a8e8' ? '#ff7a00' : '#00a8e8')}
|
||||
/>
|
||||
</Layer>
|
||||
</Stage>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
> **Building a full design editor?** [Polotno](https://polotno.com/?utm_source=konvajs&utm_medium=readme&utm_content=react-konva) is a commercial design editor SDK built on Konva by the Konva maintainers. It ships templates, text editing, and export, so you integrate an editor instead of building one: `npm install polotno`.
|
||||
|
||||
`react-konva` follows the Konva API. See the [Konva overview](https://konvajs.org/docs/overview.html) for the scene graph, properties, and events.
|
||||
|
||||
## Core API
|
||||
|
||||
`react-konva` supports Konva shapes with the same names. Event props use React names such as `onClick`, `onTouchMove`, and `onDragEnd`.
|
||||
|
||||
### Getting reference to Konva objects
|
||||
|
||||
Use `ref` to access the underlying Konva node.
|
||||
|
||||
```javascript
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { Circle } from 'react-konva';
|
||||
|
||||
const MyShape = () => {
|
||||
const circleRef = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
// log Konva.Circle instance
|
||||
console.log(circleRef.current);
|
||||
}, []);
|
||||
|
||||
return <Circle ref={circleRef} radius={50} fill="black" />;
|
||||
};
|
||||
```
|
||||
|
||||
### React features inside Stage
|
||||
|
||||
Components inside `Stage` use React hooks and inherit context from the surrounding React tree.
|
||||
|
||||
`Stage` creates a separate canvas root. Place error and Suspense boundaries inside
|
||||
`Stage`, with Konva elements as fallbacks.
|
||||
|
||||
See the [React compatibility matrix](docs/react-compatibility.md) for hook support and renderer limits.
|
||||
|
||||
### Strict mode
|
||||
|
||||
By default, `react-konva` updates only props that change between renders.
|
||||
Manual changes, such as a dragged position, persist until the corresponding prop changes.
|
||||
|
||||
Strict mode restores all supplied props to their rendered values on each update.
|
||||
For example, a circle with `x={0}` and `y={0}` returns to that position after a drag and a React update.
|
||||
This mode is separate from React's `StrictMode`.
|
||||
|
||||
To enable strict mode globally:
|
||||
|
||||
```javascript
|
||||
import { useStrictMode } from 'react-konva';
|
||||
|
||||
useStrictMode(true);
|
||||
```
|
||||
|
||||
To enable strict mode for one node:
|
||||
|
||||
```javascript
|
||||
<Rect width={50} height={50} fill="black" _useStrictMode />
|
||||
```
|
||||
|
||||
### Minimal bundle
|
||||
|
||||
The default package includes all Konva shapes and filters.
|
||||
For a smaller bundle, use the core entry point and import individual shapes:
|
||||
|
||||
```javascript
|
||||
import { Stage, Layer, Rect } from 'react-konva/lib/ReactKonvaCore';
|
||||
|
||||
import 'konva/lib/shapes/Rect';
|
||||
```
|
||||
|
||||
Demo: [https://codesandbox.io/s/6l97wny44z](https://codesandbox.io/s/6l97wny44z)
|
||||
|
||||
## Usage with Next.js
|
||||
|
||||
Konva 10+ works with Next.js without extra canvas setup. Use a
|
||||
[Client Component](https://nextjs.org/docs/app/api-reference/directives/use-client) (`'use client'`).
|
||||
|
||||
Konva 9 and earlier need extra setup, such as installing `canvas` or
|
||||
[disabling SSR](https://nextjs.org/docs/app/guides/lazy-loading#skipping-ssr) for the canvas component.
|
||||
|
||||
## Event synchronization
|
||||
|
||||
With Konva 10.5+, React state updates from native input commit before the event finishes.
|
||||
To batch MobX reactions too, pass `runInAction`:
|
||||
|
||||
```tsx
|
||||
import { runInAction } from 'mobx';
|
||||
|
||||
<Stage eventBatchFunc={runInAction} width={600} height={400}>
|
||||
{/* Existing layers, shapes, and handlers */}
|
||||
</Stage>
|
||||
```
|
||||
|
||||
The wrapper must call its callback exactly once, synchronously. Older Konva versions ignore this prop.
|
||||
|
||||
With Konva 10.9+, react-konva also batches Transformer updates automatically across each canvas commit.
|
||||
See the [release notes](RELEASE_NOTES.md) for geometry timing and compatibility details.
|
||||
|
||||
## Development and tests
|
||||
|
||||
Use Node.js 24 LTS for development.
|
||||
|
||||
```sh
|
||||
npm install
|
||||
npx playwright install chromium
|
||||
npm test
|
||||
```
|
||||
|
||||
`npm test` covers development and production builds, performance counts, server rendering, package exports, and types.
|
||||
Set `BROWSER=firefox` or `BROWSER=webkit` to use another installed Playwright browser.
|
||||
See the [benchmark guide](benchmarks/README.md) for performance checks and timing comparisons.
|
||||
|
||||
## [CHANGELOG](https://github.com/konvajs/react-konva/releases)
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
# 19.3.2: Clean release artifacts
|
||||
|
||||
Builds now clear previous output before compilation. This prevents stale,
|
||||
unused files from entering the package after a compiler configuration change.
|
||||
|
||||
# 19.3.1: Batched Transformer updates
|
||||
|
||||
With Konva 10.9+, react-konva batches automatic Transformer updates across each
|
||||
canvas commit. Updating many selected nodes triggers one Transformer refresh
|
||||
after the mutations. Layout effects see the final anchor positions.
|
||||
|
||||
This works automatically. Upgrade Konva to enable it:
|
||||
|
||||
```sh
|
||||
npm install react-konva@19.3.1 konva@^10.9.0
|
||||
```
|
||||
|
||||
Older supported Konva versions keep their existing behavior and do not require
|
||||
the new batching API. React and React DOM still require matching 19.3+ versions
|
||||
within React 19.
|
||||
|
||||
Updates the context bridge and renderer types, with the same React 19.3.0 minimum.
|
||||
|
||||
Attribute-change listeners run during mutation. They can read the changed
|
||||
attributes and Transformer bounds synchronously. To read final anchor positions,
|
||||
use a layout effect or call `transformer.update()` explicitly in the listener.
|
||||
|
||||
## Validation
|
||||
|
||||
The suite covers 203 correctness tests per build and 41 performance checks,
|
||||
including resizing 600 selected shapes. Regression tests cover individual
|
||||
selections, selected Groups, layout reads, and a missing batching API.
|
||||
Package checks cover server rendering, both module formats, consumer types,
|
||||
package exports, and React DevTools.
|
||||
|
||||
# 19.3.0: React 19.3 support
|
||||
|
||||
## Upgrade requirements
|
||||
|
||||
Upgrade React and React DOM to matching 19.3 versions before you install this release.
|
||||
The minimum React and React DOM version is now 19.3.0.
|
||||
|
||||
```sh
|
||||
npm install react@19.3.0 react-dom@19.3.0 react-konva@19.3.0
|
||||
```
|
||||
|
||||
For React 19.2 projects, pin `react-konva` to `~19.2.7`.
|
||||
The range `^19.2.7` also allows 19.3.0, which requires the React upgrade.
|
||||
For React 18 projects, use the React Konva 18 release line.
|
||||
|
||||
## Fixes and improvements
|
||||
|
||||
- Fixes the `useActionState` crash inside `Stage` with React 19.3. This resolves [#859](https://github.com/konvajs/react-konva/issues/859) through [#860](https://github.com/konvajs/react-konva/pull/860).
|
||||
- Updates the bundled `react-reconciler` to 0.34.0 and `scheduler` to 0.28.0 for React 19.3.
|
||||
- Canvas roots inherit React StrictMode checks from the DOM tree.
|
||||
- Activity and Suspense preserve canvas state and node identity through hiding and reveal. Effects and refs disconnect and reconnect as each boundary requires.
|
||||
- Stage removal discards pending child updates and runs effect cleanup, including for hidden Stages.
|
||||
- Canvas components read the surrounding DOM form status through `useFormStatus`, including the submitted data and action.
|
||||
- `useId` values remain stable on updates and distinct across DOM and Stage roots.
|
||||
- Both package formats register with React DevTools. Development builds support hook state changes.
|
||||
|
||||
## Renderer boundaries
|
||||
|
||||
Error boundaries inside `Stage` handle canvas errors and display Konva fallbacks.
|
||||
DOM error boundaries around `Stage` do not catch errors from canvas children.
|
||||
Uncaught canvas errors appear in the console with the original error and component stack, including errors during cleanup.
|
||||
|
||||
Canvas Suspense boundaries display Konva fallbacks.
|
||||
Canvas suspension does not activate a surrounding DOM Suspense boundary.
|
||||
A promise read before Stage renders can activate that DOM fallback.
|
||||
|
||||
A DOM `ViewTransition` can animate the Stage container.
|
||||
A `ViewTransition` inside Stage with Konva nodes or a ref throws a clear error.
|
||||
Fragment children remain supported, but Fragment refs stay `null` and provide no DOM methods.
|
||||
|
||||
The [React compatibility matrix](docs/react-compatibility.md) lists the supported hooks, features, fallbacks, and remaining limits.
|
||||
Native input synchronization and the fallback for older Konva versions continue from [19.2.7](https://github.com/konvajs/react-konva/blob/d9c73c21a5c164d5551ca62c15d5aa150a420053/RELEASE_NOTES.md).
|
||||
|
||||
## Validation
|
||||
|
||||
CI passes with Chromium, Firefox, and WebKit in development and production.
|
||||
The minimum React and React DOM 19.3.0 job also passes.
|
||||
The suite covers 196 correctness tests per build and 38 performance checks.
|
||||
Package checks cover both module formats, server rendering, package exports, consumer types, and the DevTools protocol.
|
||||
+119
@@ -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 };
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
# React compatibility
|
||||
|
||||
This matrix covers React and React DOM 19.3, with react-reconciler 0.34.0.
|
||||
It records renderer behavior and regression coverage. It does not guarantee every combination of React features or framework integrations.
|
||||
|
||||
A `Stage` creates a separate React root for its canvas children. Shape refs contain Konva objects.
|
||||
Application context and DOM form status cross this boundary.
|
||||
Error and Suspense boundaries stay within their renderer.
|
||||
Place an error boundary inside Stage to display a Konva fallback for canvas errors.
|
||||
Uncaught canvas errors appear in the console with the original error and component stack.
|
||||
|
||||
## Hooks
|
||||
|
||||
The inventory follows the [React hook reference](https://react.dev/reference/react/hooks) and the installed React exports.
|
||||
Tests run components inside `Stage` unless a row describes the DOM boundary.
|
||||
|
||||
| API | Behavior | Regression coverage |
|
||||
| --- | --- | --- |
|
||||
| `useState`, `useReducer` | State updates reach Konva nodes. | [Hook tests](../test/27-react-hooks.test.tsx), [native events](../test/21-native-events.test.tsx) |
|
||||
| `useRef`, `useImperativeHandle` | Refs expose Konva nodes or custom handles. Replacement and unmount clear refs. | [Ref lifecycle](../test/10-refs-lifecycle.test.tsx) |
|
||||
| `useContext`, `use(context)` | Context updates cross Stage, including provider shorthand and memoized consumers. | [Hook tests](../test/27-react-hooks.test.tsx) |
|
||||
| `useEffect`, `useLayoutEffect` | Effects run and clean up. Activity inside Stage disconnects and reconnects effects. | [Boundary tests](../test/28-react-boundaries.test.tsx), [scheduling tests](../test/16-scheduling-invariants.test.tsx) |
|
||||
| `useInsertionEffect` | Insertion effects precede layout effects and clean up on update and unmount. | [Hook tests](../test/27-react-hooks.test.tsx) |
|
||||
| `useEffectEvent` | Effect subscriptions read current props without reconnecting. | [Hook tests](../test/27-react-hooks.test.tsx) |
|
||||
| `useMemo`, `useCallback` | Dependency changes update values and callbacks. Stable dependencies allow memoized children to skip rendering. | [Hook tests](../test/27-react-hooks.test.tsx) |
|
||||
| `useTransition` | Canvas components expose pending state and commit asynchronous actions. | [Scheduler tests](../test/02-scheduler.test.tsx) |
|
||||
| `useDeferredValue` | Old canvas content stays visible while deferred content suspends. | [Scheduler tests](../test/02-scheduler.test.tsx) |
|
||||
| `useSyncExternalStore` | Subscriptions update canvas children and clean up. | [External store tests](../test/06-sync-external-store.test.tsx) |
|
||||
| `useActionState` | Synchronous and asynchronous actions update canvas state. Rejections reach canvas error boundaries. | [Action tests](../test/25-actions.test.tsx), [error tests](../test/13-error-handling.test.tsx) |
|
||||
| `useOptimistic` | Optimistic state appears during an action and settles to the saved state. | [Action tests](../test/25-actions.test.tsx) |
|
||||
| `use(promise)` | Suspense handles pending promises. Error boundaries handle rejected promises. | [Suspense tests](../test/03-suspense.test.tsx), [error tests](../test/13-error-handling.test.tsx) |
|
||||
| `useId` | IDs stay stable on updates and distinct across DOM and Stage roots. | [Hook tests](../test/27-react-hooks.test.tsx), [hydration test](../test/28-react-boundaries.test.tsx) |
|
||||
| `useDebugValue` | Safe to call. Built packages register with React DevTools and support hook-state editing in development. | [Hook tests](../test/27-react-hooks.test.tsx), [DevTools protocol tests](../test/29-devtools.mjs). No inspector UI test. |
|
||||
| React DOM `useFormStatus` | Inherits the surrounding DOM form status, including submitted data, method, and action. Other Stages remain independent. | [Hook tests](../test/27-react-hooks.test.tsx) |
|
||||
|
||||
Outside a form, the status is `{ pending: false, data: null, method: null, action: null }`.
|
||||
Canvas controls can read `useFormStatus` directly without additional props.
|
||||
React DOM's deprecated `useFormState` alias has no separate regression coverage. Use React's `useActionState` instead.
|
||||
|
||||
## Components and rendering
|
||||
|
||||
| Feature | Behavior and limits | Regression coverage |
|
||||
| --- | --- | --- |
|
||||
| Function and class components, keys, `memo` | React reconciles components and preserves keyed node identity. | [Mounting tests](../test/01-mounting.test.tsx), [hook tests](../test/27-react-hooks.test.tsx), [error tests](../test/13-error-handling.test.tsx) |
|
||||
| `StrictMode` | Canvas roots inherit DOM StrictMode checks, including state initializers and memo calculations. React Konva's strict prop mode is a separate feature. | [StrictMode tests](../test/04-strict-mode.test.tsx), [action tests](../test/25-actions.test.tsx) |
|
||||
| Function component `ref` props, `forwardRef`, callback cleanup | Refs reach shapes. Callback cleanup runs on replacement and unmount. | [Ref lifecycle](../test/10-refs-lifecycle.test.tsx) |
|
||||
| `Fragment` | Children mount, update, reorder, and unmount. Fragment refs remain `null`. DOM Fragment methods are unavailable. | [Host feature tests](../test/26-react-host-features.test.tsx) |
|
||||
| `Suspense`, `lazy` inside Stage | Fallbacks, retries, and hiding committed nodes work. Fallbacks must contain Konva elements. | [Suspense tests](../test/03-suspense.test.tsx) |
|
||||
| Error boundaries | Boundaries inside Stage handle canvas errors and display Konva fallbacks. Uncaught errors are reported in the console. DOM boundaries do not catch canvas errors. | [Error tests](../test/13-error-handling.test.tsx) |
|
||||
| `Activity` inside Stage | Hidden nodes retain state and identity. Nested boundaries preserve visibility and reconnect effects. | [Boundary tests](../test/28-react-boundaries.test.tsx), [pixel tests](../test/18-drawing.test.tsx) |
|
||||
| `Activity` around Stage | Hide and reveal preserve Stage identity and canvas state. Effects and refs disconnect and reconnect. Hidden Stages dispose safely. | [Boundary tests](../test/28-react-boundaries.test.tsx) |
|
||||
| `Suspense` around Stage | Promises read above Stage activate the DOM fallback. Mounted canvas state survives hiding. Layout effects disconnect, while passive effects remain active. | [Boundary tests](../test/28-react-boundaries.test.tsx) |
|
||||
| `ViewTransition` | A DOM wrapper can animate the Stage container. A boundary inside Stage with Konva nodes or a ref throws an actionable error. | [Host feature tests](../test/26-react-host-features.test.tsx), [error tests](../test/13-error-handling.test.tsx) |
|
||||
| Empty `ViewTransition` inside Stage | Commits finish without animation. Layout and passive effects still complete. | [Host feature tests](../test/26-react-host-features.test.tsx) |
|
||||
| `startTransition`, `addTransitionType` | Each renderer schedules its own transitions. DOM pending state does not wait for canvas suspension. Transition types do not animate individual canvas shapes. | [Scheduler tests](../test/02-scheduler.test.tsx), [action tests](../test/25-actions.test.tsx) |
|
||||
| `Profiler` | Canvas commit callbacks work in the profiling build. | [Commit-count benchmarks](../benchmarks/counts.bench.tsx) |
|
||||
| Konva portals | `KonvaRenderer.createPortal` accepts Konva containers. Context, Activity visibility, target replacement, and disposal have coverage. | [Boundary tests](../test/28-react-boundaries.test.tsx) |
|
||||
| DOM portals and overlays | DOM content requires React DOM and a DOM container. The Html fixture covers a separate DOM root. | [Portal tests](../test/05-roots-portals.test.tsx) |
|
||||
| Server rendering and hydration | The server renders an empty Stage container. React DOM hydrates that container, then Konva mounts the canvas children. | [Packaged SSR tests](../test/11-ssr.mjs), [hydration test](../test/28-react-boundaries.test.tsx) |
|
||||
|
||||
The lifecycle tests cover Activity and compare canvas effects with DOM siblings under Suspense.
|
||||
They cover overlapping suspension, replacement of pending children, and deletion before a promise resolves.
|
||||
An initially hidden Stage creates its canvas when revealed. It does not prerender canvas children before that first reveal.
|
||||
Development effects can replay in both roots. Cleanup must support repeated setup.
|
||||
Errors during Stage disposal are reported in the console, including errors from multiple child cleanups.
|
||||
Event-handler errors use browser error reporting. Error boundaries handle render, effect, and action failures.
|
||||
|
||||
## Choosing a Suspense boundary
|
||||
|
||||
For a canvas fallback, put the boundary inside Stage:
|
||||
|
||||
```jsx
|
||||
<Stage width={600} height={400}>
|
||||
<Layer>
|
||||
<Suspense fallback={<Text text="Loading…" />}>
|
||||
<CanvasContent />
|
||||
</Suspense>
|
||||
</Layer>
|
||||
</Stage>
|
||||
```
|
||||
|
||||
Without a canvas boundary, Stage uses an empty canvas fallback until its children are ready.
|
||||
It can replace pending children or unmount without waiting for their promises.
|
||||
Canvas suspension does not activate a DOM Suspense boundary around Stage.
|
||||
|
||||
For a DOM fallback, read the promise above Stage:
|
||||
|
||||
```jsx
|
||||
function Drawing({ drawingPromise }) {
|
||||
const drawing = use(drawingPromise);
|
||||
return (
|
||||
<Stage width={600} height={400}>
|
||||
<Layer><Rect fill={drawing.fill} width={100} height={100} /></Layer>
|
||||
</Stage>
|
||||
);
|
||||
}
|
||||
|
||||
<Suspense fallback={<p>Loading…</p>}>
|
||||
<Drawing drawingPromise={drawingPromise} />
|
||||
</Suspense>
|
||||
```
|
||||
|
||||
Keep each promise stable across retries. Create it outside the component that reads it.
|
||||
With this pattern, a DOM transition can keep the previous canvas visible while data loads.
|
||||
Alternatively, own transition state inside Stage when canvas components read the promises.
|
||||
An update passed through Stage props commits in the DOM before the canvas renders those props.
|
||||
It cannot extend the original DOM transition when a canvas child suspends.
|
||||
|
||||
## APIs outside the canvas renderer
|
||||
|
||||
DOM elements, text children, forms, stylesheets, and document metadata are not Konva nodes.
|
||||
Use `<Text text="..." />` for canvas text. Use a DOM overlay for HTML content.
|
||||
Unknown element types produce a warning and substitute a `Group`. This fallback does not implement HTML behavior.
|
||||
|
||||
React DOM resource hints, form reset, and DOM portals operate on real DOM resources.
|
||||
React DOM's `flushSync` does not independently flush state owned by the separate canvas renderer.
|
||||
The native Konva event integration coordinates both renderers. [Scheduling tests](../test/16-scheduling-invariants.test.tsx) cover their commit ordering.
|
||||
|
||||
Client `cache` calls its function without server memoization. Client `cacheSignal` returns `null`.
|
||||
These are React client defaults, not canvas caching features.
|
||||
Element utilities such as `Children`, `cloneElement`, and `isValidElement` use React's implementation.
|
||||
|
||||
DevTools protocol tests cover registration, commit notifications, and development hook-state editing in both built package formats.
|
||||
Browser extension UI, Server Components, Server Functions, streaming resumption, and React Compiler output require separate integration coverage.
|
||||
This matrix does not claim that coverage. Experimental exports and future React minors are also outside this audit.
|
||||
+69
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -0,0 +1 @@
|
||||
{"type":"module"}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.addPendingUnmount = addPendingUnmount;
|
||||
exports.prepareUnmounts = prepareUnmounts;
|
||||
exports.getEventBatch = getEventBatch;
|
||||
exports.scheduleMicrotask = scheduleMicrotask;
|
||||
exports.batchEvents = batchEvents;
|
||||
const react_dom_1 = require("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();
|
||||
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.
|
||||
function prepareUnmounts() {
|
||||
for (const callback of pendingUnmounts)
|
||||
callback();
|
||||
}
|
||||
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.
|
||||
function scheduleMicrotask(callback) {
|
||||
const run = () => {
|
||||
if (pending.delete(run)) {
|
||||
prepareUnmounts();
|
||||
callback();
|
||||
}
|
||||
};
|
||||
pending.add(run);
|
||||
enqueue(run);
|
||||
}
|
||||
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.
|
||||
(0, react_dom_1.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) {
|
||||
(0, react_dom_1.flushSync)(() => {
|
||||
for (const run of pending)
|
||||
run();
|
||||
});
|
||||
}
|
||||
}
|
||||
finally {
|
||||
depth--;
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
/**
|
||||
* Based on ReactArt.js
|
||||
* Copyright (c) 2017-present Lavrenov Anton.
|
||||
* All rights reserved.
|
||||
*
|
||||
* MIT
|
||||
*/
|
||||
'use strict';
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
require("konva");
|
||||
__exportStar(require("./ReactKonvaCore.js"), exports);
|
||||
+119
@@ -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 };
|
||||
+244
@@ -0,0 +1,244 @@
|
||||
/**
|
||||
* Based on ReactArt.js
|
||||
* Copyright (c) 2017-present Lavrenov Anton.
|
||||
* All rights reserved.
|
||||
*
|
||||
* MIT
|
||||
*/
|
||||
'use strict';
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.useContextBridge = exports.useStrictMode = exports.Stage = exports.KonvaRenderer = exports.version = exports.Transformer = exports.Shape = exports.Arrow = exports.RegularPolygon = exports.Path = exports.Tag = exports.Arc = exports.Ring = exports.Star = exports.TextPath = exports.Text = exports.Image = exports.Sprite = exports.Line = exports.Wedge = exports.Ellipse = exports.Circle = exports.Rect = exports.Label = exports.Group = exports.FastLayer = exports.Layer = void 0;
|
||||
const react_1 = __importDefault(require("react"));
|
||||
const react_dom_1 = require("react-dom");
|
||||
const [reactMajor, reactMinor] = react_1.default.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.');
|
||||
}
|
||||
const Core_js_1 = __importDefault(require("konva/lib/Core.js"));
|
||||
const react_reconciler_1 = __importDefault(require("react-reconciler"));
|
||||
const constants_js_1 = require("react-reconciler/constants.js");
|
||||
const HostConfig = __importStar(require("./ReactKonvaHostConfig.js"));
|
||||
const EventBatch_js_1 = require("./EventBatch.js");
|
||||
const makeUpdates_js_1 = require("./makeUpdates.js");
|
||||
const its_fine_1 = require("its-fine");
|
||||
Object.defineProperty(exports, "useContextBridge", { enumerable: true, get: function () { return its_fine_1.useContextBridge; } });
|
||||
function SuspendCanvas({ promise }) {
|
||||
throw promise;
|
||||
}
|
||||
const StageWrap = (props) => {
|
||||
const container = react_1.default.useRef(null);
|
||||
const stage = react_1.default.useRef(null);
|
||||
const fiberRef = react_1.default.useRef(null);
|
||||
const children = react_1.default.useRef(null);
|
||||
const suspension = react_1.default.useRef(null);
|
||||
// DOM and Konva reconcilers have separate useId counters.
|
||||
const identifierPrefix = react_1.default.useId();
|
||||
const formStatus = (0, react_dom_1.useFormStatus)();
|
||||
const previousProps = react_1.default.useRef({});
|
||||
// Keep the render-time snapshot when React replays layout effects.
|
||||
const oldProps = previousProps.current;
|
||||
const Bridge = (0, its_fine_1.useContextBridge)();
|
||||
const fiber = (0, its_fine_1.useFiber)();
|
||||
const isStrictMode = !!(0, its_fine_1.traverseFiber)(fiber, true, (node) => node.type === react_1.default.StrictMode);
|
||||
const effectsDisconnected = react_1.default.useRef(false);
|
||||
const isUnmounting = react_1.default.useRef(false);
|
||||
const cancelUnmount = react_1.default.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_1.default.useInsertionEffect(() => {
|
||||
isUnmounting.current = false;
|
||||
return () => {
|
||||
isUnmounting.current = true;
|
||||
if (effectsDisconnected.current && stage.current)
|
||||
destroyStage();
|
||||
};
|
||||
}, []);
|
||||
const destroyStage = () => {
|
||||
cancelUnmount.current?.();
|
||||
cancelUnmount.current = null;
|
||||
(0, EventBatch_js_1.prepareUnmounts)();
|
||||
// Discard queued child work before destroying the Stage, including updates
|
||||
// from external stores whose objects are no longer alive.
|
||||
exports.KonvaRenderer.flushSyncFromReconciler(() => {
|
||||
exports.KonvaRenderer.updateContainer(null, fiberRef.current, null);
|
||||
});
|
||||
stage.current?.destroy();
|
||||
stage.current?.off(makeUpdates_js_1.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;
|
||||
}
|
||||
exports.KonvaRenderer.updateContainer(react_1.default.createElement(react_1.default.Activity, {
|
||||
mode: mode === 'hidden' ? 'hidden' : 'visible',
|
||||
children: react_1.default.createElement(react_1.default.Suspense, { fallback: null }, children.current, suspension.current &&
|
||||
react_1.default.createElement(SuspendCanvas, {
|
||||
promise: suspension.current.promise,
|
||||
})),
|
||||
}), fiberRef.current, null);
|
||||
(0, EventBatch_js_1.prepareUnmounts)();
|
||||
// Parent layout effects must see the committed canvas nodes and refs.
|
||||
exports.KonvaRenderer.flushSyncWork();
|
||||
};
|
||||
react_1.default.useLayoutEffect(() => {
|
||||
cancelUnmount.current?.();
|
||||
cancelUnmount.current = null;
|
||||
// Reconnecting layout effects reuses the existing Stage.
|
||||
if (!stage.current) {
|
||||
stage.current = new Core_js_1.default.Stage({
|
||||
width: props.width,
|
||||
height: props.height,
|
||||
container: container.current,
|
||||
});
|
||||
fiberRef.current = exports.KonvaRenderer.createContainer(stage.current, constants_js_1.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;
|
||||
exports.KonvaRenderer.updateContainer(null, fiberRef.current, null);
|
||||
};
|
||||
cancelUnmount.current = (0, EventBatch_js_1.addPendingUnmount)(unmount);
|
||||
// Suspense disconnects layout effects but keeps passive effects.
|
||||
setCanvasVisibility('suspended');
|
||||
}
|
||||
else {
|
||||
destroyStage();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
react_1.default.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_1.default.useLayoutEffect(() => {
|
||||
// Older Konva versions use the normal asynchronous React scheduler.
|
||||
if (typeof stage.current.eventBatchFunc === 'function') {
|
||||
stage.current.eventBatchFunc((0, EventBatch_js_1.getEventBatch)(props.eventBatchFunc));
|
||||
}
|
||||
}, [props.eventBatchFunc]);
|
||||
react_1.default.useImperativeHandle(props.forwardedRef, () => stage.current, []);
|
||||
react_1.default.useLayoutEffect(() => {
|
||||
previousProps.current = props;
|
||||
(0, makeUpdates_js_1.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_1.default.createElement(HostConfig.HostTransitionContext.Provider, { value: formStatus }, react_1.default.createElement(Bridge, {}, props.children));
|
||||
setCanvasVisibility('visible');
|
||||
});
|
||||
return react_1.default.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,
|
||||
});
|
||||
};
|
||||
exports.Layer = 'Layer';
|
||||
exports.FastLayer = 'FastLayer';
|
||||
exports.Group = 'Group';
|
||||
exports.Label = 'Label';
|
||||
exports.Rect = 'Rect';
|
||||
exports.Circle = 'Circle';
|
||||
exports.Ellipse = 'Ellipse';
|
||||
exports.Wedge = 'Wedge';
|
||||
exports.Line = 'Line';
|
||||
exports.Sprite = 'Sprite';
|
||||
exports.Image = 'Image';
|
||||
exports.Text = 'Text';
|
||||
exports.TextPath = 'TextPath';
|
||||
exports.Star = 'Star';
|
||||
exports.Ring = 'Ring';
|
||||
exports.Arc = 'Arc';
|
||||
exports.Tag = 'Tag';
|
||||
exports.Path = 'Path';
|
||||
exports.RegularPolygon = 'RegularPolygon';
|
||||
exports.Arrow = 'Arrow';
|
||||
exports.Shape = 'Shape';
|
||||
exports.Transformer = 'Transformer';
|
||||
exports.version = '19.3.2';
|
||||
const rendererConfig = {
|
||||
...HostConfig,
|
||||
// React's public context type omits fields used by the reconciler.
|
||||
HostTransitionContext: HostConfig.HostTransitionContext,
|
||||
rendererVersion: exports.version,
|
||||
rendererPackageName: 'react-konva',
|
||||
extraDevToolsConfig: null,
|
||||
};
|
||||
exports.KonvaRenderer = (0, react_reconciler_1.default)(rendererConfig);
|
||||
exports.KonvaRenderer.injectIntoDevTools();
|
||||
exports.Stage = react_1.default.forwardRef((props, ref) => {
|
||||
return react_1.default.createElement(its_fine_1.FiberProvider, {}, react_1.default.createElement(StageWrap, { ...props, forwardedRef: ref }));
|
||||
});
|
||||
exports.useStrictMode = makeUpdates_js_1.toggleStrictMode;
|
||||
+306
@@ -0,0 +1,306 @@
|
||||
"use strict";
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.HostTransitionContext = exports.NotPendingTransition = exports.maySuspendCommitInSyncRender = exports.maySuspendCommitOnUpdate = exports.createViewTransitionInstance = exports.removeChildFromContainer = exports.insertInContainerBefore = exports.appendChildToContainer = exports.supportsHydration = exports.supportsPersistence = exports.supportsMutation = exports.warnsIfNotActing = exports.isPrimaryRenderer = exports.noTimeout = exports.scheduleMicrotask = exports.supportsMicrotasks = exports.cancelTimeout = exports.scheduleTimeout = exports.run = exports.idlePriority = exports.now = void 0;
|
||||
exports.appendInitialChild = appendInitialChild;
|
||||
exports.createInstance = createInstance;
|
||||
exports.createTextInstance = createTextInstance;
|
||||
exports.finalizeInitialChildren = finalizeInitialChildren;
|
||||
exports.getPublicInstance = getPublicInstance;
|
||||
exports.createFragmentInstance = createFragmentInstance;
|
||||
exports.updateFragmentInstanceFiber = updateFragmentInstanceFiber;
|
||||
exports.commitNewChildToFragmentInstance = commitNewChildToFragmentInstance;
|
||||
exports.deleteChildFromFragmentInstance = deleteChildFromFragmentInstance;
|
||||
exports.prepareForCommit = prepareForCommit;
|
||||
exports.preparePortalMount = preparePortalMount;
|
||||
exports.resetAfterCommit = resetAfterCommit;
|
||||
exports.resetTextContent = resetTextContent;
|
||||
exports.getRootHostContext = getRootHostContext;
|
||||
exports.getChildHostContext = getChildHostContext;
|
||||
exports.shouldSetTextContent = shouldSetTextContent;
|
||||
exports.appendChild = appendChild;
|
||||
exports.insertBefore = insertBefore;
|
||||
exports.removeChild = removeChild;
|
||||
exports.commitTextUpdate = commitTextUpdate;
|
||||
exports.commitMount = commitMount;
|
||||
exports.commitUpdate = commitUpdate;
|
||||
exports.hideInstance = hideInstance;
|
||||
exports.hideTextInstance = hideTextInstance;
|
||||
exports.unhideInstance = unhideInstance;
|
||||
exports.unhideTextInstance = unhideTextInstance;
|
||||
exports.clearContainer = clearContainer;
|
||||
exports.detachDeletedInstance = detachDeletedInstance;
|
||||
exports.getInstanceFromNode = getInstanceFromNode;
|
||||
exports.bindToConsole = bindToConsole;
|
||||
exports.beforeActiveInstanceBlur = beforeActiveInstanceBlur;
|
||||
exports.afterActiveInstanceBlur = afterActiveInstanceBlur;
|
||||
exports.prepareScopeUpdate = prepareScopeUpdate;
|
||||
exports.getInstanceFromScope = getInstanceFromScope;
|
||||
exports.setCurrentUpdatePriority = setCurrentUpdatePriority;
|
||||
exports.getCurrentUpdatePriority = getCurrentUpdatePriority;
|
||||
exports.resolveUpdatePriority = resolveUpdatePriority;
|
||||
exports.shouldAttemptEagerTransition = shouldAttemptEagerTransition;
|
||||
exports.trackSchedulerEvent = trackSchedulerEvent;
|
||||
exports.resolveEventType = resolveEventType;
|
||||
exports.resolveEventTimeStamp = resolveEventTimeStamp;
|
||||
exports.requestPostPaintCallback = requestPostPaintCallback;
|
||||
exports.maySuspendCommit = maySuspendCommit;
|
||||
exports.startViewTransition = startViewTransition;
|
||||
exports.preloadInstance = preloadInstance;
|
||||
exports.startSuspendingCommit = startSuspendingCommit;
|
||||
exports.suspendInstance = suspendInstance;
|
||||
exports.suspendOnActiveViewTransition = suspendOnActiveViewTransition;
|
||||
exports.waitForCommitToBeReady = waitForCommitToBeReady;
|
||||
exports.getSuspendedCommitReason = getSuspendedCommitReason;
|
||||
exports.resetFormInstance = resetFormInstance;
|
||||
const react_1 = __importDefault(require("react"));
|
||||
const Core_js_1 = __importDefault(require("konva/lib/Core.js"));
|
||||
const makeUpdates_js_1 = require("./makeUpdates.js");
|
||||
var scheduler_1 = require("scheduler");
|
||||
Object.defineProperty(exports, "now", { enumerable: true, get: function () { return scheduler_1.unstable_now; } });
|
||||
Object.defineProperty(exports, "idlePriority", { enumerable: true, get: function () { return scheduler_1.unstable_IdlePriority; } });
|
||||
Object.defineProperty(exports, "run", { enumerable: true, get: function () { return scheduler_1.unstable_runWithPriority; } });
|
||||
const constants_js_1 = require("react-reconciler/constants.js");
|
||||
const NO_CONTEXT = {};
|
||||
// for react-spring capability
|
||||
Core_js_1.default.Node.prototype._applyProps = makeUpdates_js_1.applyNodeProps;
|
||||
// let currentUpdatePriority: number = NoEventPriority;
|
||||
let currentUpdatePriority = constants_js_1.DefaultEventPriority;
|
||||
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);
|
||||
(0, makeUpdates_js_1.updatePicture)(parentInstance);
|
||||
}
|
||||
function createInstance(type, props, internalInstanceHandle) {
|
||||
let NodeClass = Core_js_1.default[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 = Core_js_1.default.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);
|
||||
(0, makeUpdates_js_1.applyNodeProps)(instance, propsWithOnlyEvents);
|
||||
return instance;
|
||||
}
|
||||
function createTextInstance(text, rootContainerInstance, internalInstanceHandle) {
|
||||
console.error(`Text components are not supported for now in ReactKonva. Your text is: "${text}"`);
|
||||
}
|
||||
function finalizeInitialChildren(domElement, type, props) {
|
||||
return false;
|
||||
}
|
||||
function getPublicInstance(instance) {
|
||||
return instance;
|
||||
}
|
||||
// Fragments group Konva children but have no DOM instance or Fragment methods.
|
||||
// This matches React's noop renderer.
|
||||
function createFragmentInstance() {
|
||||
return null;
|
||||
}
|
||||
function updateFragmentInstanceFiber() { }
|
||||
function commitNewChildToFragmentInstance() { }
|
||||
function deleteChildFromFragmentInstance() { }
|
||||
let finishUpdateBatch;
|
||||
function prepareForCommit() {
|
||||
// Older supported Konva versions do not expose commit batching.
|
||||
finishUpdateBatch = Core_js_1.default.beginUpdateBatch?.();
|
||||
return null;
|
||||
}
|
||||
function preparePortalMount() {
|
||||
return null;
|
||||
}
|
||||
function resetAfterCommit() {
|
||||
const finish = finishUpdateBatch;
|
||||
finishUpdateBatch = undefined;
|
||||
// React layout effects must see the final Transformer geometry.
|
||||
finish?.();
|
||||
}
|
||||
function resetTextContent(domElement) {
|
||||
// Noop
|
||||
}
|
||||
function getRootHostContext() {
|
||||
return NO_CONTEXT;
|
||||
}
|
||||
function getChildHostContext() {
|
||||
return NO_CONTEXT;
|
||||
}
|
||||
exports.scheduleTimeout = setTimeout;
|
||||
exports.cancelTimeout = clearTimeout;
|
||||
exports.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).
|
||||
var EventBatch_js_1 = require("./EventBatch.js");
|
||||
Object.defineProperty(exports, "scheduleMicrotask", { enumerable: true, get: function () { return EventBatch_js_1.scheduleMicrotask; } });
|
||||
exports.noTimeout = -1;
|
||||
// export const schedulePassiveEffects = scheduleDeferredCallback;
|
||||
// export const cancelPassiveEffects = cancelDeferredCallback;
|
||||
function shouldSetTextContent(type, props) {
|
||||
return false;
|
||||
}
|
||||
// The Konva renderer is secondary to the React DOM renderer.
|
||||
exports.isPrimaryRenderer = false;
|
||||
exports.warnsIfNotActing = false;
|
||||
exports.supportsMutation = true;
|
||||
exports.supportsPersistence = false;
|
||||
exports.supportsHydration = false;
|
||||
function appendChild(parentInstance, child) {
|
||||
if (child.parent === parentInstance) {
|
||||
child.moveToTop();
|
||||
}
|
||||
else {
|
||||
parentInstance.add(child);
|
||||
}
|
||||
(0, makeUpdates_js_1.updatePicture)(parentInstance);
|
||||
}
|
||||
exports.appendChildToContainer = appendChild;
|
||||
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());
|
||||
(0, makeUpdates_js_1.updatePicture)(parentInstance);
|
||||
}
|
||||
exports.insertInContainerBefore = insertBefore;
|
||||
function removeChild(parentInstance, child) {
|
||||
child.destroy();
|
||||
child.off(makeUpdates_js_1.EVENTS_NAMESPACE);
|
||||
(0, makeUpdates_js_1.updatePicture)(parentInstance);
|
||||
}
|
||||
exports.removeChildFromContainer = removeChild;
|
||||
function commitTextUpdate(textInstance, oldText, newText) {
|
||||
console.error(`Text components are not yet supported in ReactKonva. You text is: "${newText}"`);
|
||||
}
|
||||
function commitMount(instance, type, newProps) {
|
||||
// Noop
|
||||
}
|
||||
function commitUpdate(instance, type, oldProps, newProps) {
|
||||
(0, makeUpdates_js_1.applyNodeProps)(instance, newProps, oldProps);
|
||||
}
|
||||
function hideInstance(instance) {
|
||||
instance.hide();
|
||||
(0, makeUpdates_js_1.updatePicture)(instance);
|
||||
}
|
||||
function hideTextInstance(textInstance) {
|
||||
// Noop
|
||||
}
|
||||
function unhideInstance(instance, props) {
|
||||
if (props.visible == null || props.visible) {
|
||||
instance.show();
|
||||
(0, makeUpdates_js_1.updatePicture)(instance);
|
||||
}
|
||||
}
|
||||
function unhideTextInstance(textInstance, text) {
|
||||
// Noop
|
||||
}
|
||||
function clearContainer(container) {
|
||||
// Noop
|
||||
}
|
||||
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(makeUpdates_js_1.EVENTS_NAMESPACE);
|
||||
}
|
||||
function getInstanceFromNode() {
|
||||
return null;
|
||||
}
|
||||
function bindToConsole(methodName, args) {
|
||||
return console[methodName].bind(console, ...args);
|
||||
}
|
||||
function beforeActiveInstanceBlur() { }
|
||||
function afterActiveInstanceBlur() { }
|
||||
function prepareScopeUpdate() { }
|
||||
function getInstanceFromScope() {
|
||||
return null;
|
||||
}
|
||||
function setCurrentUpdatePriority(newPriority) {
|
||||
currentUpdatePriority = newPriority;
|
||||
}
|
||||
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.
|
||||
function resolveUpdatePriority() {
|
||||
return constants_js_1.DiscreteEventPriority;
|
||||
}
|
||||
function shouldAttemptEagerTransition() {
|
||||
return false;
|
||||
}
|
||||
function trackSchedulerEvent() { }
|
||||
function resolveEventType() {
|
||||
return null;
|
||||
}
|
||||
function resolveEventTimeStamp() {
|
||||
return -1.1;
|
||||
}
|
||||
function requestPostPaintCallback() { }
|
||||
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.');
|
||||
}
|
||||
exports.createViewTransitionInstance = maySuspendCommit;
|
||||
exports.maySuspendCommitOnUpdate = maySuspendCommit;
|
||||
exports.maySuspendCommitInSyncRender = maySuspendCommit;
|
||||
// Empty ViewTransition boundaries can reach commit without any Konva nodes.
|
||||
// Finish those commits without animation so ref errors can reach a boundary.
|
||||
function startViewTransition(suspendedState, rootContainer, transitionTypes, mutationCallback, layoutCallback, afterMutationCallback, spawnedWorkCallback, passiveCallback, errorCallback, blockedCallback, finishedAnimation) {
|
||||
mutationCallback();
|
||||
layoutCallback();
|
||||
finishedAnimation?.();
|
||||
spawnedWorkCallback();
|
||||
return null;
|
||||
}
|
||||
function preloadInstance() {
|
||||
return true;
|
||||
}
|
||||
function startSuspendingCommit() { }
|
||||
function suspendInstance() { }
|
||||
// React also calls this for Suspense retries without a ViewTransition component.
|
||||
function suspendOnActiveViewTransition() { }
|
||||
function waitForCommitToBeReady() {
|
||||
return null;
|
||||
}
|
||||
function getSuspendedCommitReason() {
|
||||
return null;
|
||||
}
|
||||
// Stage provides the surrounding DOM form status. Standalone roots stay idle.
|
||||
exports.NotPendingTransition = Object.freeze({
|
||||
pending: false,
|
||||
data: null,
|
||||
method: null,
|
||||
action: null,
|
||||
});
|
||||
exports.HostTransitionContext = react_1.default.createContext(exports.NotPendingTransition);
|
||||
function resetFormInstance() { }
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.EVENTS_NAMESPACE = void 0;
|
||||
exports.toggleStrictMode = toggleStrictMode;
|
||||
exports.applyNodeProps = applyNodeProps;
|
||||
exports.updatePicture = updatePicture;
|
||||
const Global_js_1 = require("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;
|
||||
exports.EVENTS_NAMESPACE = '.react-konva-event';
|
||||
let useStrictMode = false;
|
||||
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 = {};
|
||||
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 + exports.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 + exports.EVENTS_NAMESPACE);
|
||||
// then attach new one
|
||||
instance.on(eventName + exports.EVENTS_NAMESPACE, newEvents[eventName]);
|
||||
}
|
||||
}
|
||||
function updatePicture(node) {
|
||||
if (!Global_js_1.Konva.autoDrawEnabled) {
|
||||
var drawingNode = node.getLayer() || node.getStage();
|
||||
drawingNode && drawingNode.batchDraw();
|
||||
}
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
{
|
||||
"license": "MIT",
|
||||
"name": "react-konva",
|
||||
"description": "React components for the Konva 2d canvas library. Build interactive graphics, design editors, and whiteboards with JSX.",
|
||||
"keywords": [
|
||||
"react",
|
||||
"canvas",
|
||||
"jsx",
|
||||
"konva",
|
||||
"graphics",
|
||||
"design-editor",
|
||||
"canvas-editor",
|
||||
"whiteboard",
|
||||
"transformer"
|
||||
],
|
||||
"homepage": "https://konvajs.org/docs/react/index.html",
|
||||
"version": "19.3.2",
|
||||
"bugs": "https://github.com/konvajs/react-konva/issues",
|
||||
"main": "lib/ReactKonva.js",
|
||||
"module": "es/ReactKonva.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": {
|
||||
"types": "./es/ReactKonvaCore.d.ts",
|
||||
"default": "./es/ReactKonva.js"
|
||||
},
|
||||
"require": {
|
||||
"types": "./lib/ReactKonvaCore.d.ts",
|
||||
"default": "./lib/ReactKonva.js"
|
||||
}
|
||||
},
|
||||
"./lib/*.js": {
|
||||
"import": "./es/*.js",
|
||||
"require": "./lib/*.js"
|
||||
},
|
||||
"./lib/*.d.ts": "./lib/*.d.ts",
|
||||
"./lib/*": {
|
||||
"import": "./es/*.js",
|
||||
"require": "./lib/*.js"
|
||||
},
|
||||
"./es/*.js": "./es/*.js",
|
||||
"./es/*.d.ts": "./es/*.d.ts",
|
||||
"./es/package.json": "./es/package.json",
|
||||
"./es/*": "./es/*.js",
|
||||
"./*": "./*"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+ssh://git@github.com/konvajs/react-konva.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/react-reconciler": "0.34.0",
|
||||
"its-fine": "^2.1.1",
|
||||
"react-reconciler": "0.34.0",
|
||||
"scheduler": "0.28.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/lavrton"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/konva"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/lavrton"
|
||||
}
|
||||
],
|
||||
"peerDependencies": {
|
||||
"konva": "^8.0.1 || ^7.2.5 || ^9.0.0 || ^10.0.0",
|
||||
"react": "^19.3.0",
|
||||
"react-dom": "^19.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "19.3.0",
|
||||
"@vitest/browser": "^5.0.4",
|
||||
"@vitest/browser-playwright": "^5.0.4",
|
||||
"konva": "^10.9.0",
|
||||
"mobx": "^7.0.6",
|
||||
"mobx-react-lite": "^5.1.0",
|
||||
"playwright": "^1.64.1",
|
||||
"react": "^19.3.1",
|
||||
"react-dom": "^19.3.1",
|
||||
"typescript": "^7.0.2",
|
||||
"use-image": "^1.1.4",
|
||||
"vitest": "^5.0.4"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "node ./build.mjs",
|
||||
"test:typings": "tsc --noEmit && tsc -p test/types/tsconfig.json",
|
||||
"preversion": "npm test",
|
||||
"version": "npm run build",
|
||||
"test": "npm run test:browser && npm run test:performance && npm run test:ssr && npm run test:typings",
|
||||
"test:browser": "vitest run && npm run test:prod",
|
||||
"test:performance": "NODE_ENV=production vitest run --config benchmarks/vitest.counts.config.mts",
|
||||
"test:prod": "NODE_ENV=production vitest run",
|
||||
"test:ssr": "npm run build && node --no-experimental-detect-module test/11-ssr.mjs && node test/24-package-exports.mjs && node test/29-devtools.mjs",
|
||||
"bench:events": "node benchmarks/run.mjs",
|
||||
"test:watch": "vitest",
|
||||
"test:ui": "vitest --ui",
|
||||
"test:debug": "HEADLESS=false vitest"
|
||||
},
|
||||
"typings": "ReactKonvaCore.d.ts",
|
||||
"files": [
|
||||
"README.md",
|
||||
"docs/react-compatibility.md",
|
||||
"RELEASE_NOTES.md",
|
||||
"lib",
|
||||
"es",
|
||||
"ReactKonvaCore.d.ts"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user