diff --git a/.gitignore b/.gitignore index b7b7533b..485396e5 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ types out.png cmj .test-temp +.history # Numerous always-ignore extensions *.diff diff --git a/README.md b/README.md index 1ed618e1..19bb7ea8 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- Konva logo + Konva logo

Konva

diff --git a/package.json b/package.json index 4ca58eae..8e854f41 100644 --- a/package.json +++ b/package.json @@ -44,13 +44,13 @@ "test": "npm run test:browser && npm run test:node && npm run test:import", "test:build": "PARCEL_WORKER_BACKEND=process parcel build ./test/unit-tests.html --dist-dir ./test-build --target none --public-url ./ --no-source-maps", "test:browser": "npm run test:build && mocha-headless-chrome -f ./test-build/unit-tests.html -a disable-web-security -a no-sandbox -a disable-setuid-sandbox", - "test:watch": "rm -rf ./.parcel-cache && PARCEL_WORKERS=0 parcel serve ./test/unit-tests.html ./test/manual-tests.html ./test/sandbox.html ./test/text-paths.html ./test/bunnies.html", + "test:watch": "rimraf ./.parcel-cache && PARCEL_WORKERS=0 parcel serve ./test/unit-tests.html ./test/manual-tests.html ./test/sandbox.html ./test/text-paths.html ./test/bunnies.html", "test:node:canvas": "mocha -r tsx -r ./test/node-canvas-global-setup.mjs --extension ts --recursive './test/unit/' --exit", "test:node:skia": "mocha -r tsx -r ./test/node-skia-global-setup.mjs --extension ts --recursive './test/unit/' --exit", "test:node": "npm run test:node:canvas && npm run test:node:skia", "tsc": "tsc --removeComments", "rollup": "rollup -c", - "clean": "rm -rf ./lib && rm -rf ./types && rm -rf ./cmj && rm -rf ./test-build", + "clean": "rimraf ./lib && rimraf ./types && rimraf ./cmj && rimraf ./test-build", "watch": "rollup -c -w", "size": "size-limit" }, @@ -107,6 +107,7 @@ "parcel": "2.15.4", "prettier": "^3.6.2", "process": "^0.11.10", + "rimraf": "^6.0.1", "rollup": "^4.48.0", "rollup-plugin-typescript2": "^0.36.0", "size-limit": "^11.2.0", diff --git a/src/Container.ts b/src/Container.ts index 83d42e8b..36d257d9 100644 --- a/src/Container.ts +++ b/src/Container.ts @@ -32,7 +32,8 @@ export interface ContainerConfig extends NodeConfig { */ export abstract class Container< ChildType extends Node = Node, -> extends Node { + Config extends ContainerConfig = ContainerConfig +> extends Node { children: Array = []; /** diff --git a/src/Global.ts b/src/Global.ts index 367e76d1..093eb9de 100644 --- a/src/Global.ts +++ b/src/Global.ts @@ -196,7 +196,7 @@ export const Konva = { _injectGlobal(Konva) { if (typeof glob.Konva !== 'undefined') { console.error( - 'Severa Konva instances detected. It is not recommended to use multiple Konva instances in the same environment.' + 'Several Konva instances detected. It is not recommended to use multiple Konva instances in the same environment.' ); } glob.Konva = Konva; diff --git a/src/Node.ts b/src/Node.ts index 25f48cf5..b9103a1e 100644 --- a/src/Node.ts +++ b/src/Node.ts @@ -10,7 +10,7 @@ import type { Layer } from './Layer.ts'; import type { Shape } from './Shape.ts'; import type { Stage } from './Stage.ts'; import type { GetSet, IRect, Vector2d } from './types.ts'; -import { Transform, Util } from './Util.ts'; +import { Transform, Util, type AnyString } from './Util.ts'; import { getBooleanValidator, getNumberValidator, @@ -134,9 +134,8 @@ type globalCompositeOperationType = | 'color' | 'luminosity'; -export interface NodeConfig { - // allow any custom attribute - [index: string]: any; +// allow any custom attribute +export type NodeConfig = {}> = Props & { x?: number; y?: number; width?: number; @@ -223,6 +222,20 @@ export type KonvaEventListener = ( ev: KonvaEventObject ) => void; +export type CanvasConfig = { + x?: number; + y?: number; + width?: number; + height?: number; + pixelRatio?: number; + imageSmoothingEnabled?: boolean; +}; + +export type ImageConfig = CanvasConfig & { + mimeType?: string; + quality?: number; +}; + /** * Node constructor. Nodes are entities that can be transformed, layered, * and have bound events. The stage, layers, groups, and shapes all extend Node. @@ -393,17 +406,13 @@ export abstract class Node { * drawBorder: true * }); */ - cache(config?: { - x?: number; - y?: number; - width?: number; - height?: number; - drawBorder?: boolean; - offset?: number; - pixelRatio?: number; - imageSmoothingEnabled?: boolean; - hitCanvasPixelRatio?: number; - }) { + cache( + config?: CanvasConfig & { + drawBorder?: boolean; + offset?: number; + hitCanvasPixelRatio?: number; + } + ) { const conf = config || {}; let rect = {} as IRect; @@ -1015,13 +1024,13 @@ export abstract class Node { * @example * var x = node.getAttr('x'); */ - getAttr(attr: string) { - const method = 'get' + Util._capitalize(attr); + getAttr>(attr: K): K extends keyof AttrConfig ? AttrConfig[K] : any { + const method = 'get' + Util._capitalize(attr as string); if (Util._isFunction((this as any)[method])) { return (this as any)[method](); } // otherwise get directly - return this.attrs[attr] as T | undefined; + return this.attrs[attr]; } /** * get ancestors @@ -1050,8 +1059,8 @@ export abstract class Node { * @name Konva.Node#getAttrs * @returns {Object} */ - getAttrs() { - return (this.attrs || {}) as Config & Record; + getAttrs(): Config { + return (this.attrs || {}); } /** * set multiple attrs at once using an object literal @@ -1065,7 +1074,7 @@ export abstract class Node { * fill: 'red' * }); */ - setAttrs(config: any) { + setAttrs(config?: Config) { this._batchTransformChanges(() => { let key, method; if (!config) { @@ -1624,7 +1633,7 @@ export abstract class Node { * @returns {Object} */ toObject() { - let attrs = this.getAttrs() as any, + let attrs = this.getAttrs(), key, val, getter, @@ -2111,7 +2120,7 @@ export abstract class Node { * @example * var canvas = node.toCanvas(); */ - toCanvas(config?) { + toCanvas(config?: CanvasConfig) { return this._toKonvaCanvas(config)._canvas; } /** @@ -2137,16 +2146,11 @@ export abstract class Node { * @param {Boolean} [config.imageSmoothingEnabled] set this to false if you want to disable imageSmoothing * @returns {String} */ - toDataURL(config?: { - x?: number; - y?: number; - width?: number; - height?: number; - pixelRatio?: number; - mimeType?: string; - quality?: number; - callback?: (str: string) => void; - }) { + toDataURL( + config?: ImageConfig & { + callback?: (url: string) => void; + } + ) { config = config || {}; const mimeType = config.mimeType || null, quality = config.quality || null; @@ -2187,16 +2191,11 @@ export abstract class Node { * } * }); */ - toImage(config?: { - x?: number; - y?: number; - width?: number; - height?: number; - pixelRatio?: number; - mimeType?: string; - quality?: number; - callback?: (img: HTMLImageElement) => void; - }) { + toImage( + config?: ImageConfig & { + callback?: (img: HTMLImageElement) => void; + } + ) { return new Promise((resolve, reject) => { try { const callback = config?.callback; @@ -2231,16 +2230,11 @@ export abstract class Node { * var blob = await node.toBlob({}); * @returns {Promise} */ - toBlob(config?: { - x?: number; - y?: number; - width?: number; - height?: number; - pixelRatio?: number; - mimeType?: string; - quality?: number; - callback?: (blob: Blob | null) => void; - }) { + toBlob( + config?: ImageConfig & { + callback?: (blob: Blob | null) => void; + } + ) { return new Promise((resolve, reject) => { try { const callback = config?.callback; @@ -2405,8 +2399,8 @@ export abstract class Node { * @example * node.setAttr('x', 5); */ - setAttr(attr: string, val) { - const func = this[SET + Util._capitalize(attr)]; + setAttr>(attr: K, val: K extends keyof AttrConfig ? AttrConfig[K] : any) { + const func = this[SET + Util._capitalize(attr as string)]; if (Util._isFunction(func)) { func.call(this, val); @@ -2422,7 +2416,7 @@ export abstract class Node { drawNode?.batchDraw(); } } - _setAttr(key: string, val) { + _setAttr>(key: K, val: K extends keyof AttrConfig ? AttrConfig[K] : any) { const oldVal = this.attrs[key]; if (oldVal === val && !Util.isObject(val)) { return; @@ -2878,7 +2872,7 @@ export abstract class Node { } } -interface AnimTo extends NodeConfig { +interface AnimTo extends NodeConfig> { onFinish?: Function; onUpdate?: Function; duration?: number; diff --git a/src/Shape.ts b/src/Shape.ts index aeb6fa4f..b300a642 100644 --- a/src/Shape.ts +++ b/src/Shape.ts @@ -26,7 +26,7 @@ export type ShapeConfigHandler = { export type LineJoin = 'round' | 'bevel' | 'miter'; export type LineCap = 'butt' | 'round' | 'square'; -export interface ShapeConfig extends NodeConfig { +export type ShapeConfig = {}> = NodeConfig & { fill?: string | CanvasGradient; fillPatternImage?: HTMLImageElement; fillPatternX?: number; diff --git a/src/Stage.ts b/src/Stage.ts index 36893b87..9ba1d87e 100644 --- a/src/Stage.ts +++ b/src/Stage.ts @@ -154,7 +154,7 @@ export const stages: Stage[] = []; * }); */ -export class Stage extends Container { +export class Stage extends Container { content: HTMLDivElement; pointerPos: Vector2d | null; _pointerPositions: (Vector2d & { id?: number })[] = []; diff --git a/src/Tween.ts b/src/Tween.ts index 7c78011b..ef7c2562 100644 --- a/src/Tween.ts +++ b/src/Tween.ts @@ -151,6 +151,9 @@ class TweenEngine { } export interface TweenConfig extends NodeConfig { + easing?: typeof Easings[keyof typeof Easings]; + yoyo?: boolean; + onReset?: Function; onFinish?: Function; onUpdate?: Function; duration?: number; @@ -419,7 +422,7 @@ export class Tween { // after tweening points of line we need to set original end const attrs = Tween.attrs[node._id][this._id]; if (attrs.points && attrs.points.trueEnd) { - node.setAttr('points', attrs.points.trueEnd); + node.setAttr('points' as any, attrs.points.trueEnd); } if (this.onFinish) { diff --git a/src/Util.ts b/src/Util.ts index 372d3ad2..ea59767a 100644 --- a/src/Util.ts +++ b/src/Util.ts @@ -1121,3 +1121,5 @@ export const Util = { } }, }; + +export type AnyString = T | (string & {}) diff --git a/src/canvas-backend.ts b/src/canvas-backend.ts index f9a0bfec..35ff9aec 100644 --- a/src/canvas-backend.ts +++ b/src/canvas-backend.ts @@ -7,6 +7,7 @@ const canvas = Canvas['default'] || Canvas; // @ts-ignore global.DOMMatrix = canvas.DOMMatrix; +// @ts-ignore (global as any).Path2D ??= class Path2D { constructor(path: any) { (this as any).path = path; diff --git a/src/shapes/Rect.ts b/src/shapes/Rect.ts index 581f98c9..be169509 100644 --- a/src/shapes/Rect.ts +++ b/src/shapes/Rect.ts @@ -8,7 +8,7 @@ import type { GetSet } from '../types.ts'; import type { Context } from '../Context.ts'; import { getNumberOrArrayOfNumbersValidator } from '../Validators.ts'; -export interface RectConfig extends ShapeConfig { +export type RectConfig = {}> = ShapeConfig & { cornerRadius?: number | number[]; } @@ -30,7 +30,7 @@ export interface RectConfig extends ShapeConfig { * strokeWidth: 5 * }); */ -export class Rect extends Shape { +export class Rect = {}> extends Shape> { _sceneFunc(context: Context) { const cornerRadius = this.cornerRadius(), width = this.width(), diff --git a/src/skia-backend.ts b/src/skia-backend.ts index 7619e6b0..7856e7da 100644 --- a/src/skia-backend.ts +++ b/src/skia-backend.ts @@ -2,8 +2,10 @@ import { Konva } from './_CoreInternals.ts'; // @ts-ignore import { Canvas, DOMMatrix, Image, Path2D } from 'skia-canvas'; +// @ts-ignore global.DOMMatrix = DOMMatrix as any; +// @ts-ignore global.Path2D = Path2D as any; Path2D.prototype.toString = () => '[object Path2D]';