feat: function components

This commit is contained in:
aarthificial
2022-05-11 01:28:55 +02:00
parent 8c2bf27ac7
commit 178db3d95c
15 changed files with 165 additions and 32 deletions

14
package-lock.json generated
View File

@@ -18,7 +18,7 @@
"strongly-typed-events": "^3.0.1",
"three": "^0.138.3",
"ts-loader": "^9.2.6",
"typescript": "^4.5.5",
"typescript": "^4.6.4",
"url-loader": "^4.1.1",
"webpack": "^5.68.0",
"webpack-dev-server": "^4.7.4"
@@ -5533,9 +5533,9 @@
}
},
"node_modules/typescript": {
"version": "4.5.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz",
"integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==",
"version": "4.6.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz",
"integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -10302,9 +10302,9 @@
}
},
"typescript": {
"version": "4.5.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.5.tgz",
"integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA=="
"version": "4.6.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz",
"integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg=="
},
"unique-filename": {
"version": "1.1.1",

View File

@@ -20,7 +20,7 @@
"strongly-typed-events": "^3.0.1",
"three": "^0.138.3",
"ts-loader": "^9.2.6",
"typescript": "^4.5.5",
"typescript": "^4.6.4",
"url-loader": "^4.1.1",
"webpack": "^5.68.0",
"webpack-dev-server": "^4.7.4"

View File

@@ -14,6 +14,7 @@ import {SceneTransition} from './transitions';
import {decorate, threadable} from './decorators';
import {PROJECT, SCENE} from './symbols';
import {SimpleEventDispatcher} from 'strongly-typed-events';
import {setScene} from './utils';
export interface SceneRunner {
(layer: Scene, project: Project): ThreadGenerator;
@@ -130,6 +131,7 @@ export class Scene extends Layer {
}
public async next() {
setScene(this);
let result = this.runner.next();
while (result.value) {
if (isPromise(result.value)) {
@@ -192,7 +194,7 @@ export class Scene extends Layer {
}
public add(...children: (Shape | Group)[]): this {
super.add(...children);
super.add(...children.flat());
this.debugNode.moveToTop();
return this;
}

View File

@@ -5,6 +5,14 @@ import {PROJECT, SCENE} from '../symbols';
import {ThreadGenerator} from '../threading';
decorate(waitUntil, threadable());
export function waitUntil(
time: number,
after?: ThreadGenerator,
): ThreadGenerator;
export function waitUntil(
event: string,
after?: ThreadGenerator,
): ThreadGenerator;
export function* waitUntil(
targetTime: number | string = 0,
after?: ThreadGenerator,
@@ -29,7 +37,7 @@ export function* waitFor(
seconds = 0,
after?: ThreadGenerator,
): ThreadGenerator {
const project = (yield PROJECT) as Project;
const project: Project = yield PROJECT;
const frames = project.secondsToFrames(seconds);
const startFrame = project.frame;
while (project.frame - startFrame < frames) {

62
src/components/Icon.ts Normal file
View File

@@ -0,0 +1,62 @@
import {LayoutShape, LayoutShapeConfig} from './LayoutShape';
import {KonvaNode} from '../decorators';
import {Context} from 'konva/lib/Context';
const FILL = [
new Path2D(
'M16.56,8.94L7.62,0L6.21,1.41l2.38,2.38L3.44,8.94c-0.59,0.59-0.59,1.54,0,2.12l5.5,5.5C9.23,16.85,9.62,17,10,17 s0.77-0.15,1.06-0.44l5.5-5.5C17.15,10.48,17.15,9.53,16.56,8.94z M5.21,10L10,5.21L14.79,10H5.21z M19,11.5c0,0-2,2.17-2,3.5 c0,1.1,0.9,2,2,2s2-0.9,2-2C21,13.67,19,11.5,19,11.5z M2',
),
];
const BRUSH = [
new Path2D(
'M7 14c-1.66 0-3 1.34-3 3 0 1.31-1.16 2-2 2 .92 1.22 2.49 2 4 2 2.21 0 4-1.79 4-4 0-1.66-1.34-3-3-3zm13.71-9.37l-1.34-1.34c-.39-.39-1.02-.39-1.41 0L9 12.25 11.75 15l8.96-8.96c.39-.39.39-1.02 0-1.41z',
),
];
const UNITY = [
new Path2D(
'M 46.523438 17.292969 L 61.820313 26.125 C 62.375 26.429688 62.386719 27.296875 61.820313 27.605469 L 43.636719 38.101563 C 43.089844 38.417969 42.4375 38.394531 41.921875 38.101563 L 23.742188 27.605469 C 23.1875 27.300781 23.171875 26.429688 23.742188 26.121094 L 39.039063 17.292969 L 39.039063 0 L 0 22.539063 L 0 67.617188 L 0 67.410156 L 0 67.617188 L 14.972656 58.972656 L 14.972656 41.308594 C 14.964844 40.675781 15.707031 40.230469 16.257813 40.570313 L 34.4375 51.070313 C 34.988281 51.386719 35.292969 51.960938 35.292969 52.554688 L 35.292969 73.546875 C 35.308594 74.175781 34.566406 74.625 34.019531 74.289063 L 18.71875 65.457031 L 3.742188 74.101563 L 42.78125 96.640625 L 81.820313 74.101563 L 66.84375 65.457031 L 51.550781 74.289063 C 51.007813 74.613281 50.25 74.191406 50.269531 73.546875 L 50.269531 52.550781 C 50.269531 51.917969 50.613281 51.363281 51.125 51.066406 L 69.304688 40.570313 C 69.847656 40.242188 70.609375 40.664063 70.589844 41.3125 L 70.589844 58.972656 L 85.5625 67.617188 L 85.5625 22.539063 L 46.523438 0 L 46.523438 17.292969 ',
),
];
export enum IconType {
Fill,
Brush,
Unity,
}
interface IconConfig extends LayoutShapeConfig {
type?: IconType;
}
@KonvaNode()
export class Icon extends LayoutShape {
private readonly paths: Path2D[];
constructor(config?: IconConfig) {
super(config);
switch (config?.type ?? IconType.Fill) {
case IconType.Brush:
this.paths = BRUSH;
break;
case IconType.Fill:
this.paths = FILL;
break;
case IconType.Unity:
this.paths = UNITY;
break;
}
this._fillFunc = context => {
for (const path of this.paths) {
context.fill(path);
}
};
}
_sceneFunc(context: Context) {
context.fillShape(this);
}
}

View File

@@ -1,5 +1,5 @@
import {Text, TextConfig} from 'konva/lib/shapes/Text';
import {IRect, Vector2d} from 'konva/lib/types';
import {GetSet, IRect, Vector2d} from 'konva/lib/types';
import {ShapeGetClientRectConfig} from 'konva/lib/Shape';
import {
getOriginDelta,
@@ -11,13 +11,16 @@ import {
} from './ILayoutNode';
import {Origin, Size, PossibleSpacing, Spacing} from '../types';
import {Animator, tween, textTween, InterpolationFunction} from '../tweening';
import {threadable} from '../decorators';
import {getset, threadable} from '../decorators';
export interface LayoutTextConfig extends Partial<LayoutAttrs>, TextConfig {
minWidth?: number;
}
export class LayoutText extends Text implements ILayoutNode {
@getset('', undefined, LayoutText.prototype.textTween)
public text: GetSet<LayoutTextConfig['text'], this>;
private overrideWidth: number | null = null;
private isConstructed = false;

View File

@@ -15,6 +15,7 @@ import {easeOutExpo, linear, tween} from '../tweening';
import {GetSet, IRect} from 'konva/lib/types';
import {getset, threadable} from '../decorators';
import {Node} from 'konva/lib/Node';
import {Reference} from '../utils';
export interface SurfaceMask {
width: number;
@@ -30,6 +31,7 @@ export interface CircleMask {
}
export interface SurfaceConfig extends LayoutGroupConfig {
ref?: Reference<Surface>;
radius?: number;
origin?: Origin;
circleMask?: CircleMask;

7
src/global.d.ts vendored
View File

@@ -27,3 +27,10 @@ declare module '*.wav?meta' {
const value: import('./types/Waveform').Waveform;
export = value;
}
declare namespace JSX {
type ElementClass = import('konva/lib/Node').Node;
interface ElementChildrenAttribute {
children: {};
}
}

View File

@@ -1,31 +1,45 @@
import {Node} from 'konva/lib/Node';
import type {Scene} from './Scene';
import {Node, NodeConfig} from 'konva/lib/Node';
import {Container} from 'konva/lib/Container';
import {Surface} from './components';
import {LayoutNode} from './components/ILayoutNode';
function isConstructor(fn: Function): fn is new (...args: any[]) => any {
return !!fn.prototype?.name;
}
export const Fragment = Symbol.for('mc.fragment');
export function jsx<TNode extends Node, TConfig>(
type: new (config?: TConfig) => TNode,
config: TConfig & {children?: Node | Node[]; ref?: {value: TNode} | [any, string]},
export function jsx(
type:
| (new (config?: NodeConfig) => Node)
| ((config: NodeConfig) => Node)
| typeof Fragment,
config: NodeConfig & {
children?: Node | Node[];
ref?: {value: Node} | [any, string];
},
maybeKey: string,
): TNode {
): Node | Node[] {
const {children, ref, ...rest} = config;
const node = new type(<TConfig>rest);
const flatChildren = Array.isArray(children) ? children.flat() : [children];
if (type === Fragment) {
return flatChildren;
}
if (!isConstructor(type)) {
return type(config);
}
const node = new type(rest);
if (children) {
if (node instanceof Surface) {
if (Array.isArray(children)) {
node.setChild(<LayoutNode>children[0]);
} else {
node.setChild(<LayoutNode>children);
}
node.setChild(<LayoutNode>flatChildren[0]);
} else if (node instanceof Container) {
if (Array.isArray(children)) {
node.add(...children);
} else {
node.add(children);
}
node.add(...flatChildren);
}
}
if (ref) {
if (Array.isArray(ref)) {
ref[0][ref[1]] = node;

View File

@@ -132,7 +132,10 @@ export class Animator<Type, This extends Node> {
return this;
}
public waitUntil(event: string): this
public waitUntil(time: number): this
public waitUntil(time: number | string): this {
// @ts-ignore
this.keys.push(() => waitUntil(time));
return this;
}

View File

@@ -1,2 +1,3 @@
export * from './pop';
export * from './useRef';
export * from './useScene';

View File

@@ -1,5 +1,25 @@
import type {Node} from 'konva/lib/Node';
export function useRef<T extends Node>(): {value: T} {
return {value: null};
export type Reference<TValue> = TValue extends (config: {
ref: infer TReference;
}) => any
? TReference
: {value: TValue};
export function useRef<T>(): Reference<T> {
return {} as Reference<T>;
}
export function makeRef<TObject, TKey extends keyof TObject>(
object: TObject,
key: TKey,
): Reference<TObject[TKey]> {
return {
get value(): TObject[TKey] {
return object[key];
},
set value(value: TObject[TKey]) {
object[key] = value;
},
} as Reference<TObject[TKey]>;
}

11
src/utils/useScene.ts Normal file
View File

@@ -0,0 +1,11 @@
import {Scene} from '../Scene';
let currentScene: Scene = null;
export function useScene(): Scene {
return currentScene;
}
export function setScene(scene: Scene) {
currentScene = scene;
}

View File

@@ -6,7 +6,7 @@
"sourceMap": true,
"noImplicitAny": true,
"module": "esnext",
"target": "es2017",
"target": "es2020",
"moduleResolution": "node",
"resolveJsonModule": true,
"declaration": true,

View File

@@ -3,7 +3,7 @@
"sourceMap": true,
"noImplicitAny": true,
"module": "esnext",
"target": "es2017",
"target": "es2020",
"moduleResolution": "node",
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,