Skip to content

Commit efb4959

Browse files
add interactive canvas with scale & resize(wip) support with control
1 parent 99bb588 commit efb4959

8 files changed

Lines changed: 250 additions & 27 deletions

File tree

editor/components/app-runner/vanilla-app-runner.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,29 @@ export function VanillaRunner({
1414
}) {
1515
const ref = useRef<HTMLIFrameElement>();
1616

17+
useEffect(() => {
18+
if (ref.current) {
19+
function disablezoom() {
20+
ref.current.contentWindow.addEventListener(
21+
"wheel",
22+
(event) => {
23+
const { ctrlKey } = event;
24+
if (ctrlKey) {
25+
event.preventDefault();
26+
return;
27+
}
28+
},
29+
{ passive: false }
30+
);
31+
}
32+
ref.current.contentWindow.addEventListener(
33+
"DOMContentLoaded",
34+
disablezoom,
35+
false
36+
);
37+
}
38+
}, [ref.current]);
39+
1740
useEffect(() => {
1841
if (ref.current && enableInspector) {
1942
ref.current.onload = () => {
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import React from "react";
2+
import styled from "@emotion/styled";
3+
import RefreshSharpIcon from "@material-ui/icons/RefreshSharp";
4+
5+
export function ZoomControl({
6+
scale,
7+
stepper,
8+
onChange,
9+
}: {
10+
onChange: (scale: number) => void;
11+
resetControl?: boolean;
12+
select?: boolean;
13+
stepper?: boolean;
14+
scale: number;
15+
}) {
16+
const [isEditing, setIsEditing] = React.useState(false);
17+
const displayScale = (scale * 100).toFixed(0);
18+
const mincontrol = 0.01;
19+
return (
20+
<Wrapper>
21+
<Controls>
22+
<ControlsContainer>
23+
<Valuedisplay
24+
onMouseEnter={() => setIsEditing(true)}
25+
onClick={() => setIsEditing(true)}
26+
>
27+
{isEditing ? (
28+
<StyledInput
29+
autoFocus
30+
min={mincontrol * 100}
31+
onChange={(e) => {
32+
const num = Number(e.target.value.replace(/[^0-9]/g, ""));
33+
const scae = num / 100;
34+
if (scae >= mincontrol) {
35+
onChange(scae);
36+
}
37+
}}
38+
onKeyDown={(e) => {
39+
if (e.key === "Enter") {
40+
setIsEditing(false);
41+
}
42+
}}
43+
onMouseLeave={() => setIsEditing(false)}
44+
onBlur={() => setIsEditing(false)}
45+
value={displayScale}
46+
defaultValue={displayScale}
47+
type="number"
48+
/>
49+
) : (
50+
<>
51+
<ReadonlyValue>{displayScale}</ReadonlyValue>
52+
<PercentText>%</PercentText>
53+
</>
54+
)}
55+
</Valuedisplay>
56+
{scale !== 1 && (
57+
<RefreshSharpIcon
58+
onClick={() => onChange(1)}
59+
style={{ color: "white", fontSize: 18 }}
60+
/>
61+
)}
62+
</ControlsContainer>
63+
</Controls>
64+
</Wrapper>
65+
);
66+
}
67+
68+
const Wrapper = styled.div`
69+
display: flex;
70+
justify-content: center;
71+
flex-direction: column;
72+
align-items: center;
73+
flex: none;
74+
gap: 10px;
75+
box-sizing: border-box;
76+
padding: 10px 24px;
77+
`;
78+
79+
const Controls = styled.div`
80+
display: flex;
81+
justify-content: center;
82+
flex-direction: row;
83+
align-items: center;
84+
flex: none;
85+
gap: 10px;
86+
height: 24px;
87+
box-sizing: border-box;
88+
border-radius: 4px;
89+
padding: 4px;
90+
background-color: #252526;
91+
box-shadow: #25252650 0px 0px 0px 16px inset;
92+
`;
93+
94+
const ControlsContainer = styled.div`
95+
display: flex;
96+
justify-content: center;
97+
flex-direction: row;
98+
align-items: center;
99+
flex: none;
100+
gap: 4px;
101+
height: 16px;
102+
box-sizing: border-box;
103+
`;
104+
105+
const Valuedisplay = styled.div`
106+
display: flex;
107+
justify-content: center;
108+
flex-direction: row;
109+
align-items: center;
110+
flex: none;
111+
gap: 1px;
112+
height: 16px;
113+
box-sizing: border-box;
114+
`;
115+
116+
const ReadonlyValue = styled.span`
117+
color: rgba(124, 124, 124, 1);
118+
text-overflow: ellipsis;
119+
font-size: 14px;
120+
font-family: Roboto, sans-serif;
121+
font-weight: 400;
122+
text-align: left;
123+
`;
124+
125+
const PercentText = styled.span`
126+
color: rgba(124, 124, 124, 1);
127+
text-overflow: ellipsis;
128+
font-size: 14px;
129+
font-family: Roboto, sans-serif;
130+
font-weight: 400;
131+
text-align: left;
132+
`;
133+
134+
const StyledInput = styled.input`
135+
border: none;
136+
outline: none;
137+
border-radius: 4px;
138+
color: white;
139+
background-color: transparent;
140+
width: 32px;
141+
142+
/* hide number arrow */
143+
::-webkit-inner-spin-button,
144+
::-webkit-outer-spin-button {
145+
-webkit-appearance: none;
146+
margin: 0;
147+
}
148+
`;

editor/components/canvas/interactive-canvas.tsx

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import React, { useRef, useState } from "react";
2-
import { useSpring, animated } from "react-spring";
2+
import styled from "@emotion/styled";
33
import { usePinch } from "@use-gesture/react";
44
import { Resizable } from "re-resizable";
5+
import { ZoomControl } from "./controller-zoom-control";
56

67
export function InteractiveCanvas({
78
children,
@@ -11,14 +12,43 @@ export function InteractiveCanvas({
1112
const [scale, setScale] = useState(1);
1213

1314
return (
14-
<div id="interactive-canvas" style={{ width: "100%", height: "100%" }}>
15+
<InteractiveCanvasWrapper id="interactive-canvas">
1516
<ScalableFrame onRescale={setScale} scale={scale}>
16-
<ResizableFrame scale={scale}>{children}</ResizableFrame>
17+
<Controls>
18+
<ZoomControl scale={scale} onChange={setScale} />
19+
</Controls>
20+
<ScalingAreaStaticRoot>
21+
<ScalingArea scale={scale}>
22+
<ResizableFrame scale={scale}>{children}</ResizableFrame>
23+
</ScalingArea>
24+
</ScalingAreaStaticRoot>
1725
</ScalableFrame>
18-
</div>
26+
</InteractiveCanvasWrapper>
1927
);
2028
}
2129

30+
const InteractiveCanvasWrapper = styled.div`
31+
display: flex;
32+
flex-direction: column;
33+
flex: 1;
34+
`;
35+
36+
const Controls = styled.div`
37+
z-index: 2;
38+
display: flex;
39+
flex-direction: row;
40+
justify-content: flex-end;
41+
`;
42+
43+
const ScalingAreaStaticRoot = styled.div`
44+
display: flex;
45+
align-items: center;
46+
justify-content: center;
47+
align-content: flex-start;
48+
align-self: stretch;
49+
flex: 1;
50+
`;
51+
2252
function ScalableFrame({
2353
children,
2454
scale,
@@ -28,14 +58,15 @@ function ScalableFrame({
2858
onRescale?: (scale: number) => void;
2959
children?: React.ReactNode;
3060
}) {
31-
// const [{ xyzs }, set] = useSpring(() => ({ xyzs: [0, 0, 0, 100] }));
3261
const ref = useRef();
3362

3463
usePinch(
3564
(state) => {
65+
const prevscale = scale;
3666
const { offset } = state;
37-
const [scale] = offset;
38-
onRescale(scale);
67+
const thisscale = offset[0];
68+
// const newscale = thisscale - prevscale;
69+
onRescale(thisscale);
3970
},
4071
{ target: ref }
4172
);
@@ -45,26 +76,36 @@ function ScalableFrame({
4576
id="scale-event-listener"
4677
ref={ref}
4778
style={{
48-
width: "100%",
49-
height: "100%",
79+
display: "flex",
80+
flexDirection: "column",
81+
flex: 1,
82+
alignItems: "center",
83+
alignContent: "center",
5084
}}
5185
>
52-
<div
53-
style={{
54-
transform: `scale(${scale})`,
55-
// xyzs
56-
// .to((x, y, z, s): string => {
57-
// return `translate3D(${x}px, ${y}px, 0) scale(${s / 100})`;
58-
// })
59-
// .get(),
60-
}}
61-
>
62-
{children}
63-
</div>
86+
{children}
6487
</div>
6588
);
6689
}
6790

91+
const ScalingArea = ({
92+
scale,
93+
children,
94+
}: {
95+
scale: number;
96+
children: React.ReactNode;
97+
}) => {
98+
return (
99+
<div
100+
style={{
101+
transform: `scale(${scale})`,
102+
}}
103+
>
104+
{children}
105+
</div>
106+
);
107+
};
108+
68109
function ResizableFrame({
69110
scale,
70111
children,

editor/components/editor/editor-appbar/editor-appbar-fragment-for-canvas.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ export function AppbarFragmentForCanvas() {
1212
}
1313

1414
const RootWrapperAppbarFragmentForCanvas = styled.div`
15+
z-index: 10;
1516
display: flex;
1617
justify-content: center;
1718
flex-direction: row;
1819
align-items: center;
19-
flex: 1;
2020
gap: 10px;
2121
align-self: stretch;
2222
background-color: rgba(37, 37, 38, 1);

editor/components/editor/editor-appbar/editor-appbar-fragment-for-code-editor.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export function AppbarFragmentForCodeEditor() {
2626
}
2727

2828
const RootWrapperAppbarFragmentForCodeEditor = styled.div`
29+
z-index: 10;
2930
display: flex;
3031
justify-content: center;
3132
flex-direction: row;

editor/components/editor/editor-appbar/editor-appbar-fragment-for-sidebar.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export function AppbarFragmentForSidebar() {
1414
}
1515

1616
const RootWrapperAppbarFragmentForSidebar = styled.div`
17+
z-index: 10;
1718
display: flex;
1819
justify-content: flex-start;
1920
flex-direction: row;

editor/layouts/panel/workspace-content-panel.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@ export function WorkspaceContentPanel({
1616
disableBorder?: boolean;
1717
}) {
1818
return (
19-
<Container backgroundColor={backgroundColor} disableBorder={disableBorder}>
19+
<WorkspaceCPanel
20+
backgroundColor={backgroundColor}
21+
disableBorder={disableBorder}
22+
>
2023
{children}
21-
</Container>
24+
</WorkspaceCPanel>
2225
);
2326
}
2427

25-
const Container = styled.div<{
28+
const WorkspaceCPanel = styled.div<{
2629
backgroundColor: string;
2730
disableBorder: boolean;
2831
}>`

editor/scaffolds/canvas/canvas.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export function Canvas({
1818
preview: Result;
1919
}) {
2020
return (
21-
<>
21+
<CanvasContainer id="canvas">
2222
<EditorAppbarFragments.Canvas />
2323
{/* {preview ? (
2424
<PreviewAndRunPanel
@@ -65,7 +65,7 @@ export function Canvas({
6565
}}
6666
></div> */}
6767
</InteractiveCanvas>
68-
</>
68+
</CanvasContainer>
6969
);
7070
}
7171

@@ -88,3 +88,9 @@ const EditorCanvasSkeleton = () => {
8888
/>
8989
);
9090
};
91+
92+
const CanvasContainer = styled.div`
93+
display: flex;
94+
flex-direction: column;
95+
height: 100%;
96+
`;

0 commit comments

Comments
 (0)