From a3443d7b1031ff58cadee506502b0c2db0aa69d5 Mon Sep 17 00:00:00 2001 From: Nathan Rajlich Date: Mon, 13 May 2024 02:18:49 -0700 Subject: [PATCH] Remove percentage string for x, y, width, height and add `useLayout()` hook --- README.md | 17 ++-- biome.json | 2 +- src/canvas.ts | 13 +-- src/entity.ts | 53 ++++-------- src/group.ts | 9 +- src/hooks/use-layout.ts | 19 +++++ src/image.ts | 10 +-- src/index.tsx | 45 ++++++---- src/round-rect.ts | 2 +- src/test.tsx | 22 ++++- src/text.ts | 2 +- src/types.ts | 2 - src/util.ts | 6 +- ...up-test-tsx-should-render-group-1-snap.png | Bin 0 -> 10200 bytes ...roup-with-parent-layout-context-1-snap.png | Bin 0 -> 8859 bytes ...ath-test-tsx-should-render-path-1-snap.png | Bin 0 -> 12045 bytes ...render-rect-with-layout-context-1-snap.png | Bin 0 -> 266 bytes test/group.test.tsx | 79 ++++++++++++++++++ test/path.test.tsx | 39 +++++++++ test/rect.test.tsx | 17 +++- 20 files changed, 242 insertions(+), 95 deletions(-) create mode 100644 src/hooks/use-layout.ts create mode 100644 test/__image_snapshots__/group-test-tsx-test-group-test-tsx-should-render-group-1-snap.png create mode 100644 test/__image_snapshots__/group-test-tsx-test-group-test-tsx-should-render-group-with-parent-layout-context-1-snap.png create mode 100644 test/__image_snapshots__/path-test-tsx-test-path-test-tsx-should-render-path-1-snap.png create mode 100644 test/__image_snapshots__/rect-test-tsx-test-rect-test-tsx-should-render-rect-with-layout-context-1-snap.png create mode 100644 test/group.test.tsx create mode 100644 test/path.test.tsx diff --git a/README.md b/README.md index bd49b34..90582b2 100644 --- a/README.md +++ b/README.md @@ -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 <> + + + Hello world! + + ; +} export function App() { return ( - - - Hello world! - + ); } diff --git a/biome.json b/biome.json index 59493f8..ac7221f 100644 --- a/biome.json +++ b/biome.json @@ -1,6 +1,6 @@ { "linter": { - "enabled": true + "enabled": false }, "javascript": { "formatter": { diff --git a/src/canvas.ts b/src/canvas.ts index 5c79ed7..160b260 100644 --- a/src/canvas.ts +++ b/src/canvas.ts @@ -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) { @@ -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); } } diff --git a/src/entity.ts b/src/entity.ts index f06a392..95bdc2c 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -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. * @@ -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; @@ -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() { @@ -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; } diff --git a/src/group.ts b/src/group.ts index 303ab35..b9a923f 100644 --- a/src/group.ts +++ b/src/group.ts @@ -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 { - width: number; - height: number; -} +export interface GroupProps extends EntityProps {} export class Group extends Entity { subroot: Root; @@ -24,8 +21,8 @@ export class Group extends Entity { this.subroot.ctx.canvas, 0, 0, - this.calculatedWidth, - this.calculatedHeight, + this.width, + this.height, ); } } diff --git a/src/hooks/use-layout.ts b/src/hooks/use-layout.ts new file mode 100644 index 0000000..eb11794 --- /dev/null +++ b/src/hooks/use-layout.ts @@ -0,0 +1,19 @@ +import { createContext, useContext } from 'react'; + +export interface Layout { + x: number; + y: number; + width: number; + height: number; +} + +export const LayoutContext = createContext({ + x: 0, + y: 0, + width: 0, + height: 0, +}); + +export function useLayout() { + return useContext(LayoutContext); +} diff --git a/src/image.ts b/src/image.ts index f17d48f..a77486e 100644 --- a/src/image.ts +++ b/src/image.ts @@ -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 { +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 { @@ -70,8 +68,8 @@ export class Image extends Entity { this.sh ?? img.naturalHeight, 0, 0, - this.calculatedWidth, - this.calculatedHeight, + this.width, + this.height, ); } } diff --git a/src/index.tsx b/src/index.tsx index aaa08e1..be24b04 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -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[]; -const factory = (type: string) => { - const c = forwardRef((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 = (type: string) => { + const c = forwardRef((props, ref) => { + return createElement(type, { ...props, ...useAdjustedLayout(props), ref }); + }); c.displayName = type; return c; }; @@ -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'); @@ -67,35 +77,40 @@ export const Group = forwardRef<_Group, GroupProps>((props, ref) => { const root = useParent(); const rootRef = useRef(); 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 ( - {createElement('Group', { - ...props, - root: rootRef.current, - ref, - })} + + {createElement('Group', { + ...props, + ...layout, + root: rootRef.current, + ref, + })} + ); }); 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'; diff --git a/src/round-rect.ts b/src/round-rect.ts index b6b05a1..c5e7675 100644 --- a/src/round-rect.ts +++ b/src/round-rect.ts @@ -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; } } diff --git a/src/test.tsx b/src/test.tsx index 43bb6fc..696c4b4 100644 --- a/src/test.tsx +++ b/src/test.tsx @@ -1,3 +1,4 @@ +// @ts-nocheck import React, { useEffect, useRef, @@ -64,6 +65,7 @@ import { useTextMetrics, TextProps, useDimensions, + LayoutContext, } from './index.js'; const canvas = document.getElementById('c') as HTMLCanvasElement; @@ -758,12 +760,13 @@ function CenteredText({ children, ...props }: TextProps) { function Page1() { // biome-ignore lint/suspicious/noExplicitAny: const data = useLoaderData() as any; + const dims = useDimensions(); return ( <> - + - + hello world @@ -794,6 +797,18 @@ function Page1() { ); } +function Page3() { + return ( + + + + ); +} + +function RedRect() { + return ; +} + function ErrorBoundary() { // biome-ignore lint/suspicious/noExplicitAny: const error = useAsyncError() as any; @@ -820,7 +835,7 @@ function Async() { const data = useAsyncValue(); console.log(data); return ( - + Loaded! ); @@ -835,6 +850,7 @@ const routes: RouteObject[] = [ { path: '/test', //element: , + //element: , //element: , element: , //element: , diff --git a/src/text.ts b/src/text.ts index b9dc60e..b23a833 100644 --- a/src/text.ts +++ b/src/text.ts @@ -49,7 +49,7 @@ export class Text extends Entity { } render(): void { - let { + const { value, fontFamily = 'sans-serif', fontWeight = '', diff --git a/src/types.ts b/src/types.ts index d71255f..2622515 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,3 @@ -export type PercentageString = `${string}%`; - export interface ICanvasRenderingContext2D { globalAlpha: number; canvas: ICanvas; diff --git a/src/util.ts b/src/util.ts index 9e42615..c91affc 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,10 +1,6 @@ import { Root } from './root.js'; import type { Entity } from './entity.js'; -import type { PercentageString, Point } from './types.js'; - -export function parsePercent(str: PercentageString) { - return parseFloat(str.slice(0, -1)) / 100; -} +import type { Point } from './types.js'; const MouseEvent = globalThis.MouseEvent || class MouseEvent extends Event {}; diff --git a/test/__image_snapshots__/group-test-tsx-test-group-test-tsx-should-render-group-1-snap.png b/test/__image_snapshots__/group-test-tsx-test-group-test-tsx-should-render-group-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..2aee8c8dda81001986c09860889f4c218833527e GIT binary patch literal 10200 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 TxWkYOCQ$Oy%2G8F#zFrFqU$w= literal 0 HcmV?d00001 diff --git a/test/__image_snapshots__/path-test-tsx-test-path-test-tsx-should-render-path-1-snap.png b/test/__image_snapshots__/path-test-tsx-test-path-test-tsx-should-render-path-1-snap.png new file mode 100644 index 0000000000000000000000000000000000000000..1bc97e787ba0b182b5c47a06fa82aaccc7499511 GIT binary patch literal 12045 zcmeHt_g7O{-|wLaIH>682#ORR60p#ucTmQX&>TYVN-xrjv=AMIair)8W#}qRLhn+8 zGJ>HhHPnbi5Q3CIh_vwT_};(Z{&d$}Pu60wa;h8&sv@i ze?twkm)c=gEG-*^6`dTk##Qog-Eou>HxR!gEONXf|9<{!f&W_I|78m}eIVQsfFR#8L=8&Tco0Q@e98JO{Tbx( z4B~A6m`p82=%!O2pjH~!)tI3%-!oBd|4=6oxwq(%=7GVNyADH;k2A8-66Np*{2oqL zj5;Sn@=K-mcd#@d2zSB)j!U7+L_bQzYtNX$y8^p3P6)bTh{6^kypP5v^>v|9SQoOR z7zD|4q7|%R)g$8fr&#N@NKdfz5Cp~MBNo!Ac-!MTDq>>Nr11_`69nKolB z(s4w*0HK;n&AJn#3&iBONU}nei#}MZ+P=@MBuYV0J1&_@NznWDW`C}+gVhGD-5aUo zh1&aX!;1*VzHdg6J6>?41!<>}ms4->>=p!(uQ>ekiC+vAIHq<{#L49w4*K{PY?n*j zlk^Jd7dB`@?K(LdJ@qc3&KUj1^&;k&7w3z5cyDs}h}iVD#c86uiG`Tbep!rVS&Ctl zm%5^#c7|7#ZP8vUnl52Zj|=%5x}AHB52{O{HX#HzBcsF0Q}T49jho!-Ony)%dBZeV?#d%L25Oi z{~7WWx}5}Z>QT@N&#Ahj#Z}l(zmKkHhIA7fcAhN;9~;h5-@E+2RlwioV#IV2FKboWOW0SshwI8e;91Ff+Yg@zcND zJ&I`m$R`eM(z=E;o%8$*EVHmo36v?)z(k|NyKN{MuT|oE0@{(lLH?a8O=5MU*kIXn z0~7CyDEs8v9o%AYfBE#G@M2lMc7HaRxtpdnyH)pHb7|@Xm&NWIP2vNuZMkuZQegiL zmC)xAsO+=Z{9QGnr1UUdl`@^z&t*SUjLJam=_mCdHOv5Nx=t))EWnHu$mM+%O>YHO z@OR@r4jE70VCKzCByl-gYbONh8oPddJG8i>xjp;1Xc1Y*%h`VB1lWI4_n#T-v(dIq zfo%@3Ft0O%3SGo#Q;`)?69jZFb?Zkgr9Lg$p zFY;y1Jqhab#ZIDAW&QH`=~T&BuJ&T~kAt1*XBeZ{EbY`0USn0ISAMqPKZ2Vgy(s+- zgYCB=wWDn6bJmO5*`FG|UOu~m_3^T_#$Nj_`ymfa&u5bn0s6hs7oJW^8!i4cJ=Wj- zXXYx_#|ArW*zNP(cA_Ukh#v|wgg8E%ImOd zz9I@4v#BSx z;NZF)P!rOl^#@qs&t6bCT7LWI{=5E~j@i9*XSd964~Hih{5U+&@W(M^INB>zH#Jv2 zt(LcuG4dqk$)z5OeKUWcYwk+R0W}xrI%rs*OG7}>rcgm@Wbjoe0|_kTeNf#0{%Gr>U+}gwT4Q^^ffX{^ z*mB>WJiSdwzvsv6wjEH{ zaH!mL!{-zf!F?QO@0+(9a6S9%QIS1Wd0c#98_DURJ<*vWBvSsCK4;_D%zSCC9=-)S;Q5&ebFiG2&Sw zT)3+d*4a;c?xz%-aM%rqG)Nm5Y2B#%Vs{*8!{+Xlx4XUx2*_rl(x-JA;GaQd6M3^# zZ1fPvH_BF{H}tVKZMgAGyrp%-o}Iq_xQ2mcFT)~evm%8mn0e-C`w9SsI|gN}mDH8{TDI0G*B5`48d-T5|K8>} zt_>hYbMFo2+?fzBpuOa@_9XN4pn9Zqi2z~x(DoJk<2c2J=v7gGP6fpq+cC}E^skc* zyH%b)fra?7*8>U_;NW%=9RG&ZDltw-i8Jw|ftctYceZXlFq>A;wT3q`g#_AttM*sH zDSjeY)W5(gOQ=x_{j1>eJP6!9Q^hE?FA33MU&j?M@s`@c5y@h6*Bx zy#)|^Svr6b^S9@F|cQ7?Zj3oOrMB~rk?KWH)}dt=PQLbI7VtVEChI3qgn{V5aM5fF@hxp{ z8aSuU%fXQ5Bn{STGJY_^ySxm<_=)?g>{BGUUB?s4Rp|_(OVbclXt0WFNkWDjm)#xd zpW0q>IlHvE7lmRKB6RH{y!D|sDG^D2TVDY)#Pc<1A z>h3LXPwSuA9rVI{wSc28k_3&{eng}s7v}IeH6Pc5ZdjpYd+&9;f4?32;dN`+a`CzC zam|7trv(#YQ{B3^rQ*SozU{i10jj+3@HCI9 z;LT{mG|NUINE5A+)gTdewhdYdQe6u1Q!#S^!btU0d>sDIKShPw#TRjq&jWbsVyj+2 z?o=pYIC>~jLL)G7uUCE1j{L!}{GR*XL`pAlXRR_WK%omCrP5k#pF41`gOpn93oQYW z&zi!mjrZE$Fq|pJ<3c+vM6clID8w*whY+Drw?+MAu7l{7eE;xl*8Nt}0>S0ZKU`d7 z1P;o20QY{So}XJjf2{1vbfZP$-d^KHO!MyPPd4A&Rx2p~col0dzf}6#nWdE_eJseS zeWkj2RTh%E9f5hJA{a^84LyijIUZM!Ox*uq4WhSwtig{TZz<=tHIR5DQpk2zSUo^( z`&x=mv&wP#3GU-gmX}B_##8U#gu(o=YPBzInq3t52c~tG@5Co-6N5Z1pUzJrz|^KJ zwJbYZWb>9V*Cs&E(Aq(omDYbqVFiM+E9o<*2$}*xc6lEAvUAq=XIROe?!z-SG%MAO z3XSBgqqGju?hBxr8bEH}jo|;Hn@06|I{QHLKR8I!7?tykYK_YI$6@XBsnOLd&mZTK za}{Rm{r@v!3U=#cRnbOcwUfVpQAEvTQjN^%vv&k|gk)Bf+7oZX`zWj=X#=07OP%$( z>@8^GHXB}OpN+1wQ#hFDXb#t~Ahu;ul~E49QnKPF^l;G2})+#~kXfHp2I%fO1d<>A`zVO)5 z$a9l@6VXN0KjapM)ZKl^`r{`iC{{howux@b28FJS*>Q7vQHLmZC4A=Trf$7u)C!ar*?baJ ze=KD|+KtubbMihc-d=AC^A?muI|%&H3R_l()i9S~8h^ZMPatJ#xM zo3mwh%uMo0LvM zaNBPRzQQU^krNIH>e{Y~Sy7tFQI}tzTS8>t6ESwMK+1`3iR0b|^>E#AK)JzF|IFm( zm1GBR#b`QBN%}0GR1m6!Ykg;qARCr4rpTz>c}BkGGDptfOT>ct&-(sIYP8UU@1La< zFU{tq1P|==<36doOHbEKJUN3oVvrKdY#5rR^iRCUH|+aO=STmrc+%RqXXVnRLjfKR5W@2_fN8cUtFl#=dA~JBT zo}(Zn*eR$<)QWMsfJ-yESz3Ncl=Ay?Mx>z&LkCZpab4P5m+XF*IHv9=oxRZs@6K#d zEWMFs!m8Iu3A8f@MJe&o1pRy^s$)lHg8LbPyhfMi%ryk# zi-NAo^~z}n$bP4dDKI-FJY$^8j2C%T6!e^2(n&|+o7%WOfNj?I-(qLE>TfMPTt656 zX}mQNbs+k2_&)9vz%hvuL?nKyfI>^s4KnF|Q{Y>f(ruRVzG$Tw^MwPM?%}1?{tX<; zKej$%<(8*Bmu)fnxU#xXl1o!iH1WL$vN8YdEF^IQ;<#Z=5z7l*k&bGYAK8oXZz~Rf z@<0N-G5!`bwcjwU_7Q$Q9;e#*eg(ur^@)P@6(x220=F@{u?&s0-8GI-d7!9kbj&Xk zI&X5G#Mt=;O1FDv>@~lixwsTffHQ^99h1Kc4xNiR>66CTK-=Oio4B2rliXR2TdHG< z8>u!2GzZGC)la4JRwl4=rKwHh8{PVz6f^HjeEt~|6uW^OigvPB+3>|6d(0fi4NzCb zH-(Kgz6q(tZ(E*2@7_4i%~Kq`Z?n}xzfE`_Q8IwpI0$#z1jqArr1vyune+5=`1_Yl zPwzg!dpHtDX;IQ6$HY+dG^0}-H_{{?K6q{cS0Nlt0$+rdEm|b9hQ}Ar36gsxl^S23KZHCHKqGd@)^TAtJoRmQFk|;xu;`iJn>QYrS&W}yL63md_N*U=T5~|jo zBMyvy;Id74xrM?D!YeuVCK8w342_F1XN_Rk)C@tSuCd$aOz!&dVjs;qhS zgxS^qC0l_$4Y%Z1Mf4!q;9ga+_()H;llUfMdral54oqEmEI<|s24MSg)1ji=n7-EO zWo%}BpT#P5RQhCwBCmH6E8%TQv%0mPQq-P#idGrvVf#1WI{-9-h;5IE7!^!VOLrAW z19g$VocF&pH&*JxV#MLy2AF$y;W%@WOoGLs8>tYd&t+tzNyK@VwyIao=BINaEk;+g z$E{FgPf4ActVZ1?bx)U~X}`wvBnbkwguN$2nGJ`G`o$X}im>>u{hQ(;esVq9@9It@yzE)(aanyUtEM_)(aafnr#WMfZ0be{HW-o$no6dw zo70nkF!e_{xXGhv`U}LWL7;{6bz4%E7&&c(*GSdk*ZNFSiJ;={yo6C)k}$00>wF1W zX~2eZOL!N#O;b8^jX)&}{BnNT`2VDJCHt*dM4CA%EkpK*j~pjorG(F;XG|&eqN-Qs zfqUb-)%4rvA5A+ib#y6i2;R(;jkJ&O)_EiVTBOj#1eplkXH-4s>m}45%>FCZIl!UB?wm87Pp17!Ty9=A;C!xFAL+Fc6! z<&O({uoqkVyF!cvdFthWegWvfKO$@yz>8So32f1uB~)%@6;@7H}KV-An2ijk-zPmL^D zE^trD3~B+A0_dbUbAxYNFQVB+*=@UeTv|t;?@s1L(sHIRiQ1K#QNBmo3~ly`5|(jt zuj1h*udB?z)Rk;4KA&*rWB6ftwLkgA#1hsxvLr>0JB?jv#Sc=_s8@|f?Zc^ti7^z1 zir4Y=z6#7(TZMHJFku{Cs}Y^3Y%fGkpOSrGUx0`l%uF2d;S2v3oTwX_YiWK1qnf^( zm#Mg({%zZB-<>xXCSoW^H z-|Z-0CC#T7L)2<5mPkkk(KgSbrshw&n7@9hSh=>o!Y8Je8)eM9Y=cZUrf(sV{Hj-$ z3NY*tjrzC4zbviyV5aZP!L_+Fv-eOr)<|#QzEED^z7e!SI#t9|?NO<{Li%H|Nx4Vz zr$%f@Q#2sL(-NaJ)&^pr9A*GlHphY3h!8$cEkl)iRIf}R@zy^+?W3AW1ph8V9lUKR z)=#$N`BkT!02>cIelY#sJ*d6B;Y!l};-Zm^XYUW?%jq%Gvo5Vl)~AgLHX>F(?(07i zcyV4s91=z=q*9GgIpZ$Vb&HvO}+mz-QC~ONF;rZgkN-Wf1 z8ga6arF1mz8M{|m<2?W9?Q1q(6FNK{!Rfwl1PlHmriY(8%;D=U@o4W4SPTgKGnKJe z$%o}ym5OYZ+y*WmD~Q&gqrG#)Amu!oF5r~Kp@y0(1N(7)3?-*yO)RLz9ReCc0vQ}BCsc;)8ZN%fsqHAB<155dOeA@`jpMxKL! zHL)c50XaXrfOb~V3_kdWYMD_wu3eIyr;B{#fA_oanBqZqXR|A^RZ-{cvX(U;HnP;-Jl!XspChKl6#E0{k|G zsBRr*%%?r#McShsQx(xc!(u0OaCg8IZm0H&U)!gSiHWf?*Oi2u8PcRsbGUJglC_nP zkhAd#Mb|g)i1XphI?TcSA~s=rL+;ip=Iadr`(ZpQjRg_3okHp$$a z9xM5jGwUK)U6^p3KIbX_$dDLx{=0rcu797(6_7~{qd(urBggPsd1zJ(=@qok?&2!1 z6nq74|6=NhDx?(iSKAs9gICz{SZ|jSd+o!&y3z&Ff69LF zRRrWDiyX=j;Z18C@xKC|SA{Wl0@ik|jIaYBSd5%2odGx=6wksQX5zxIAEpQyvn=IOKp} z`-hd{@&(hA5dY+p>NcrBviqEYTvXJXCwCzuFiPWbabD`dm?}Q9;;oKUTC35?pa#xz z3AuvqAG&AwppjsluyvKgrNJYnW@}(4l0eNfia+FZ8@QCC1a2toob~&jIf`d=CJNp8 zz3y~VacWZtx`lw&ArIgG8S~_`2aefKZF@LB7+WevAzHbvaNNiNBMjy8vY>$U*BNJd zB?u34*B3H`c-voqae?hzNpXP6Ur_bN$le*sZAK`OQz#q=Gx{3dRPHK_=ND_f+6O7j z468vrMqto!R!}Pa8x1|3G<~O(5M(*y2yY0VJKEk3jAc8A-FL7mzAL%U&5`vKF+4YojeH=s`(<1(NI zR0SUADcwAsOKGOi#sv7po3AhEmHApyXz@@z+vN9|M#PGSlw%)!tFhR!T!s&7WoFpw zryYb^SVfN6imA2VAJ*9>r&}}yg}&nqRRPnOwVj-^Ls#~_#)`++6^`=%)b_JAuHUny zk(PdpPo55J{g$SS8@5gERWYlff%&2Y=&^-fz`c@oh@PB%c4d%IkEW`0ON7qxe_YJWuEb@d*4vZL*O|JtC=G!YUfOeP;uizJ zTYV)E>I>r3NU&Bt%1^!nsLosCjGarlI6}{Yb05AhrzfcQCOgCa3fr)tbMd&n*jnx| zxXLJW3W!t|d_@E9Wp=S+cEJMMNy9Dxbzlz8T_BDp|cK2YSimCpMa* z_q@yWvuhKB2Q?;8v|Fmh@XCyZxaLkW1RX13zYEY5Ik~vvS=3C_1{0+B^Q+hEY<6S< zJYhd2HN?+>t=lw_!XIYO%tOpMT9Jn11QoG;fq?|(SeXk0i?MF>QSoLjHQ2GKimO&1 z22ajc?|n;m@mb(G!H!E8cglCD{`R7b4lxXb;aDVIy-5R!5wq?c-c2kBDV_9K^s35C zZxHDlj!Sf3Lv^f9LkE16gd%R;k~%M>Oh$++%J^LtKD zo%x2TOAJ=nb-$^=V9i-a(CsY5)Uuaf#NO.i{pMyBiqfmZIdbKLZID3)-*#W!%q zU_x_2WaD4=RK@+o$F%}EZ_g(2)C!cjuO#`Q7GlH(zF_9TbrdyK=io)~g3U$6GP9@A zVb4|sLHl_E=pVG>cu=KZ=$H_52RQ_^C6A59Y9wx{jw&2P#?@fQ)}^tlGo_S}UAZ+! z-5BXP$I`mv`!ULD=WuP`<26OwJ;-(ZAG_MxQqzfA8C7!3eV%af&bFphZDKTLw9Q-L zsmWwb8F3(+Yc7O~ul*Let>RxOujmG^OC)@`rNQ8MnD+{C@}M&L ziHV%+*I7mDrn@>|((c|kq>tPf1Ry^=Gqu6~Y;JC3WhPQ+nL0gSYLvd%y8M?>|)6-5!1-=y4PH*wPIB_qbU?XUp-y3M{bnX4GqmVF_$}O4uaoP zWKnb59-KTO4q$XeFKIooX@(exJ1U5go&7G1%=g}L&5=OTX7gzUhH6(05Fk}9^US!+ zb5M#c@jN>i|LhO#fZ?Q4-hratjez}|Qt3?*S1_ygx4iGT^CV4{B!($&jOL~9(B^;% zWG@Sdv|>gy7ZP0eHZJcwtN?gL#DBIIaI#(6?VH~D>(H|&CbF*cBK)2rqD%it@*cEp z@E<6{egUTKWi56VFPUTyfO_0k`w^ZnE^`t%LEa`z8+Y$D!X4T8f6#pv%&A>!xdgA1KFhAo|W zOUq);_=-)aweAh+YqsurFB%o^{JC_*fUwh6_T8y0S2gDeYit4ZfFIcc-Fqr?-}a_PI(DJT5E~U>wQz^>&&=5*OEZ#J z_BHeBRB9NS-h?=iB@IZ8TgLoVRE#*8Yt{X4QED~)uLR7mRZwNq$J^ut^>8nR z`x5#&>Yu~s3=}2@>i7pHbL*b+Hos}qnCaV$Ombb4c#3kr5XUEKL>ES3ThXU^*SQM> zby)iFfg-6_JZJ7$ZcIyecbOo!? zTBj?@Y0Mp4?D|wN^O)N&CSZ`wc;L*Avto<5U$s*4d&cmA9Wt)r%VC2+U)a%@zK5`F zYqd)EWnZpZvsT8F*WXUvodk2`8A?mB@smbdxlLZO*XfsB!$Zs>l53N4<=Pbn%XysX zc+735wJY{F9fxdcjyXNZ_BS0kAAQV%L_`JVA}T0^h5gYDxezweE$`R;nZEPUX=X00 zzaqP2Wwc!$+5s&IAQ_z7{~$JPHUv*1+V{3OYX6hoAeV15MSm=cY<)k=d<_d)kQlbw zI54*#FsD1C+Aghbf#~1tkhOvshRn!2VQnmTJ@!0_eHde$J$R}6_uv>u2J4)) zd0+_Kl}7!0GxSRY*+M5~_jAO23^9zbvmR3?MGGrQttEjoEilW2GqS{*njAg!7JJYM^!eSoyB24wYk%WKw)*=SB(C z7zeSvYawf;fcw#Vq>KMEino@eimD@IdGc;~z9HE=$GLS0p!rpcJ4@K*ULA&nd zCsG&Uqr;UqDm1ga8dpz6Xpy3nONIE4gX*b%aMFIUD#WW)>#k%^Qo7MXi$kKPnB4%C zB71CE`pHmb^33AbgSw^c%{K33ml)XbmR!8g{zj6eb$G8&s+c-{h2?P5WtrxkzV~q* zph2^+P==ra>8P93YCQn-{sqD*@=#FB~$bv_Vr+lE~lUTz~*{CJQYKxG=DEd zBwZriyzzM#l;gHPrL-kM{- zyp=0MGLl%^kVA(1EsCTbhT4slInPu5Q7nB_VcdQzxeTpf38$sHT>Sw-PM>2>T}w=% z>ZZ61R1&cf=s;r>wg%B-yVtxn_71#iC0q0Oyh{OsXrASuQ;iP1vnaCprtSKyvm4n_ z5rXt9Ph7)#P>o{>Hc+cm7e(IHvlTguQn3Xfh^Uq{p5AWVz{#}EreOwGVM z@OT1|&OnHn(3!$9Ec=uxwMQ?(%Wwbv{MQ2i-&$bppn*4zo2}kM7I7<<k44ofy`glX(f`uoOFahHwBu z4M$1`kiW&##WAE}&f80lTnz>SEC-ML|8H66cr=V7^ttUBr+!AD5(b7n>2db4t7m^) vTKPDAp4C6O1M^y3og!3dMu08FMdUnVQLkVM@P4uUCP;~=tDnm{r-UW|697tF literal 0 HcmV?d00001 diff --git a/test/group.test.tsx b/test/group.test.tsx new file mode 100644 index 0000000..5373f27 --- /dev/null +++ b/test/group.test.tsx @@ -0,0 +1,79 @@ +import React from 'react'; +import { join } from 'path'; +import { test, expect } from 'vitest'; +import config, { Canvas, GlobalFonts } from '@napi-rs/canvas'; +import { Group, Rect, Text, useDimensions, LayoutContext } from '../src'; +import { render } from '../src/render'; + +GlobalFonts.registerFromPath( + join(__dirname, 'Geist-Regular.otf'), + 'Geist Sans', +); + +test('should render ', async () => { + const canvas = new Canvas(300, 100); + let dims: { width: number; height: number }; + function Inner() { + dims = useDimensions(); + return ( + <> + + + Hello world! + + + ); + } + await render( + + + , + canvas, + config, + ); + expect(dims!).toEqual({ + width: 200, + height: 50, + }); + expect(canvas.toBuffer('image/png')).toMatchImageSnapshot(); +}); + +test('should render with parent layout context', async () => { + const canvas = new Canvas(300, 100); + let dims: { width: number; height: number }; + function Inner() { + dims = useDimensions(); + return ( + <> + + + Hello world! + + + ); + } + await render( + + + + + , + canvas, + config, + ); + expect(dims!).toEqual({ + width: 200, + height: 50, + }); + expect(canvas.toBuffer('image/png')).toMatchImageSnapshot(); +}); diff --git a/test/path.test.tsx b/test/path.test.tsx new file mode 100644 index 0000000..20ea2d1 --- /dev/null +++ b/test/path.test.tsx @@ -0,0 +1,39 @@ +import React from 'react'; +import { test, expect } from 'vitest'; +import config, { Canvas } from '@napi-rs/canvas'; +import { Path } from '../src'; +import { render } from '../src/render'; + +test('should render ', async () => { + const canvas = new Canvas(800, 800); + await render( + { + // setStroke3('black'); + //}} + //onMouseLeave={() => { + // setStroke3(undefined); + //}} + />, + canvas, + config, + ); + expect(canvas.toBuffer('image/png')).toMatchImageSnapshot(); +}); diff --git a/test/rect.test.tsx b/test/rect.test.tsx index 67efe09..dd18100 100644 --- a/test/rect.test.tsx +++ b/test/rect.test.tsx @@ -1,7 +1,7 @@ import React, { useState } from 'react'; import { test, expect } from 'vitest'; import config, { Canvas } from '@napi-rs/canvas'; -import { Rect } from '../src'; +import { LayoutContext, Rect } from '../src'; import { render } from '../src/render'; import { enableEvents, dispatchEvent } from './helpers/event'; @@ -15,6 +15,21 @@ test('should render ', async () => { expect(canvas.toBuffer('image/png')).toMatchImageSnapshot(); }); +test('should render with layout context', async () => { + const canvas = new Canvas(150, 100); + function BlueRect() { + return ; + } + await render( + + + , + canvas, + config, + ); + expect(canvas.toBuffer('image/png')).toMatchImageSnapshot(); +}); + test('should receive "click" event', async () => { const canvas = new Canvas(150, 100); enableEvents(canvas);