Skip to content

Commit

Permalink
Remove percentage string for x, y, width, height and add `useLayout()…
Browse files Browse the repository at this point in the history
…` hook
  • Loading branch information
TooTallNate committed May 13, 2024
1 parent 48460ef commit a3443d7
Show file tree
Hide file tree
Showing 20 changed files with 242 additions and 95 deletions.
17 changes: 12 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,22 @@
```tsx
// App.jsx
import React from "react";
import { Group, Rect, Text } from "react-tela";
import { Group, Rect, Text, useDimensions } from "react-tela";

function Contents() {
const dims = useDimensions();
return <>
<Rect width={dims.width} height={dims.height} fill="purple" alpha={0.5} />
<Text fontSize={32} fontFamily="Geist" fill="white">
Hello world!
</Text>
</>;
}

export function App() {
return (
<Group x={5} y={15} width={180} height={30} rotate={0.1}>
<Rect width="100%" height="100%" fill="purple" alpha={0.5} />
<Text fontSize={32} fontFamily="Geist" fill="white">
Hello world!
</Text>
<Contents />
</Group>
);
}
Expand Down
2 changes: 1 addition & 1 deletion biome.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"linter": {
"enabled": true
"enabled": false
},
"javascript": {
"formatter": {
Expand Down
13 changes: 2 additions & 11 deletions src/canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@ export class Canvas extends Entity {

constructor(opts: CanvasProps, root: Root) {
super(opts);
this.subcanvas = new root.Canvas(
this.calculatedWidth,
this.calculatedHeight,
);
this.subcanvas = new root.Canvas(this.width, this.height);
}

getContext(...args: Parameters<ICanvas['getContext']>) {
Expand All @@ -21,12 +18,6 @@ export class Canvas extends Entity {

render(): void {
super.render();
this.root.ctx.drawImage(
this.subcanvas,
0,
0,
this.calculatedWidth,
this.calculatedHeight,
);
this.root.ctx.drawImage(this.subcanvas, 0, 0, this.width, this.height);
}
}
53 changes: 15 additions & 38 deletions src/entity.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
import { TelaEventTarget } from './event-target.js';
import { parsePercent } from './util.js';
import type { Root } from './root.js';
import type { PercentageString, TelaMouseEvent } from './types.js';
import type { TelaMouseEvent } from './types.js';

export type EntityProps = {
/**
* The x (horizontal) coordinate of the entity from the top-left corner of the context.
*/
x?: number | PercentageString;
x?: number;
/**
* The y (vertical) coordinate of the entity from the top-left corner of the context.
*/
y?: number | PercentageString;
y?: number;
/**
* The height of the entity in pixels.
* The width of the entity in pixels.
*/
width?: number | PercentageString;
width?: number;
/**
* The height of the entity in pixels.
*/
height?: number | PercentageString;
height?: number;
/**
* The alpha transparency value of the entity. The value `0` is fully transparent. The value `1` is fully opaque.
*
Expand Down Expand Up @@ -62,10 +61,10 @@ export type EntityProps = {
};

export class Entity extends TelaEventTarget {
x: number | PercentageString;
y: number | PercentageString;
width: number | PercentageString;
height: number | PercentageString;
x: number;
y: number;
width: number;
height: number;
alpha: number;
rotate: number;
scaleX?: number;
Expand Down Expand Up @@ -123,41 +122,19 @@ export class Entity extends TelaEventTarget {
}

get calculatedX() {
let { x } = this;
if (typeof x !== 'number') {
x = this.root.ctx.canvas.width * parsePercent(x);
}
return x + this.calculatedWidth / 2;
return this.x + this.width / 2;
}

get calculatedY() {
let { y } = this;
if (typeof y !== 'number') {
y = this.root.ctx.canvas.height * parsePercent(y);
}
return y + this.calculatedHeight / 2;
}

get calculatedWidth() {
if (typeof this.width === 'number') {
return this.width;
}
return this.root.ctx.canvas.width * parsePercent(this.width);
}

get calculatedHeight() {
if (typeof this.height === 'number') {
return this.height;
}
return this.root.ctx.canvas.height * parsePercent(this.height);
return this.y + this.height / 2;
}

get offsetX() {
return -this.calculatedWidth / 2;
return -this.width / 2;
}

get offsetY() {
return -this.calculatedHeight / 2;
return -this.height / 2;
}

get matrix() {
Expand Down Expand Up @@ -191,7 +168,7 @@ export class Entity extends TelaEventTarget {

get path() {
const p = new this.root.Path2D();
p.rect(0, 0, this.calculatedWidth, this.calculatedHeight);
p.rect(0, 0, this.width, this.height);
return p;
}

Expand Down
9 changes: 3 additions & 6 deletions src/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ import { Entity, EntityProps } from './entity.js';
import { proxyEvents } from './events.js';
import type { ICanvasRenderingContext2D } from './types.js';

export interface GroupProps extends Omit<EntityProps, 'width' | 'height'> {
width: number;
height: number;
}
export interface GroupProps extends EntityProps {}

export class Group extends Entity {
subroot: Root;
Expand All @@ -24,8 +21,8 @@ export class Group extends Entity {
this.subroot.ctx.canvas,
0,
0,
this.calculatedWidth,
this.calculatedHeight,
this.width,
this.height,
);
}
}
Expand Down
19 changes: 19 additions & 0 deletions src/hooks/use-layout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { createContext, useContext } from 'react';

export interface Layout {
x: number;
y: number;
width: number;
height: number;
}

export const LayoutContext = createContext<Layout>({
x: 0,
y: 0,
width: 0,
height: 0,
});

export function useLayout() {
return useContext(LayoutContext);
}
10 changes: 4 additions & 6 deletions src/image.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { Entity, type EntityProps } from './entity.js';
import type { Root } from './root.js';
import type { IImage, PercentageString } from './types.js';
import type { IImage } from './types.js';

export interface ImageProps extends Omit<EntityProps, 'width' | 'height'> {
export interface ImageProps extends EntityProps {
src: string;
sx?: number;
sy?: number;
sw?: number;
sh?: number;
width?: number | PercentageString;
height?: number | PercentageString;
}

export class Image extends Entity {
Expand Down Expand Up @@ -70,8 +68,8 @@ export class Image extends Entity {
this.sh ?? img.naturalHeight,
0,
0,
this.calculatedWidth,
this.calculatedHeight,
this.width,
this.height,
);
}
}
45 changes: 30 additions & 15 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,24 @@ import { Path as _Path, type PathProps } from './path.js';
import { Image as _Image, type ImageProps } from './image.js';
import { Text as _Text, type TextProps as _TextProps } from './text.js';
import { ICanvas } from './types.js';
import { LayoutContext, useLayout } from './hooks/use-layout.js';
import { EntityProps } from './entity.js';

type MaybeArray<T> = T | T[];

const factory = <Ref, Props>(type: string) => {
const c = forwardRef<Ref, Props>((props, ref) =>
createElement(type, { ...props, ref }),
);
function useAdjustedLayout(props: any) {
let { x, y, width, height } = useLayout();
x += props.x ?? 0;
y += props.y ?? 0;
width += props.width ?? 0;
height += props.height ?? 0;
return { x, y, width, height };
}

const factory = <Ref, Props extends EntityProps>(type: string) => {
const c = forwardRef<Ref, Props>((props, ref) => {
return createElement(type, { ...props, ...useAdjustedLayout(props), ref });
});
c.displayName = type;
return c;
};
Expand All @@ -42,7 +53,6 @@ export type { _Canvas as CanvasRef };

export const Arc = factory<_Arc, ArcProps>('Arc');
export const Canvas = factory<_Canvas, CanvasProps>('Canvas');
//export const Group = factory<_Group, GroupProps>('Group');
export const Image = factory<_Image, ImageProps>('Image');
export const Path = factory<_Path, PathProps>('Path');
export const Rect = factory<_Rect, RectProps>('Rect');
Expand All @@ -67,35 +77,40 @@ export const Group = forwardRef<_Group, GroupProps>((props, ref) => {
const root = useParent();
const rootRef = useRef<GroupRoot>();
let canvas: ICanvas;
const layout = useAdjustedLayout(props);
if (rootRef.current) {
canvas = rootRef.current.ctx.canvas;
} else {
canvas = new root.Canvas(props.width || 300, props.height || 150);
canvas = new root.Canvas(layout.width || 300, layout.height || 150);
const ctx = canvas.getContext('2d');
if (!ctx) {
throw new Error('Could not get "2d" canvas context');
}
rootRef.current = new GroupRoot(ctx, root);
}
if (props.width > 0 && props.width !== canvas.width) {
canvas.width = props.width;
if (layout.width > 0 && layout.width !== canvas.width) {
canvas.width = layout.width;
}
if (props.height > 0 && props.height !== canvas.height) {
canvas.height = props.height;
if (layout.height > 0 && layout.height !== canvas.height) {
canvas.height = layout.height;
}
//console.log({ props })
return (
<ParentContext.Provider value={rootRef.current}>
{createElement('Group', {
...props,
root: rootRef.current,
ref,
})}
<LayoutContext.Provider value={{ x: 0, y: 0, width: 0, height: 0 }}>
{createElement('Group', {
...props,
...layout,
root: rootRef.current,
ref,
})}
</LayoutContext.Provider>
</ParentContext.Provider>
);
});
Group.displayName = 'Group';

export { useParent } from './hooks/use-parent.js';
export { useLayout, LayoutContext, type Layout } from './hooks/use-layout.js';
export { useDimensions } from './hooks/use-dimensions.js';
export { useTextMetrics } from './hooks/use-text-metrics.js';
2 changes: 1 addition & 1 deletion src/round-rect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export class RoundRect extends Shape {

get path() {
const p = new this.root.Path2D();
p.roundRect(0, 0, this.calculatedWidth, this.calculatedHeight, this.radii);
p.roundRect(0, 0, this.width, this.height, this.radii);
return p;
}
}
Loading

0 comments on commit a3443d7

Please sign in to comment.