Skip to content

Commit

Permalink
Merge pull request #5 from agneym/feature/draggable
Browse files Browse the repository at this point in the history
Draggable Borders for Sections
  • Loading branch information
agneym authored Dec 21, 2019
2 parents b1fbc1c + 864ee96 commit d6a31cc
Show file tree
Hide file tree
Showing 10 changed files with 147 additions and 39 deletions.
1 change: 1 addition & 0 deletions example/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ render(app, document.getElementById('app'));
<Playground
initialSnippet={snippet}
defaultEditorTab="javascript"
defaultResultTab="console"
transformJs
presets={["react"]}
/>
Expand Down
43 changes: 43 additions & 0 deletions playground/src/Draggable/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React, { FC, ReactNode, useRef, useContext } from "react";
import styled, { ThemeContext } from "styled-components";

import useDrag from "./useDrag";

const Container = styled.div`
display: flex;
align-items: stretch;
`;

const Divider = styled.div`
width: ${props => props.theme.divider.width}px;
cursor: col-resize;
background-color: ${props => props.theme.divider.background};
`;

interface IProps {
className?: string;
leftChild: (width: number) => ReactNode;
rightChild: (width: number) => ReactNode;
}

const Draggable: FC<IProps> = ({ className = "", leftChild, rightChild }) => {
const containerRef = useRef<HTMLDivElement>(null);
const dividerRef = useRef<HTMLDivElement>(null);
const themeContext = useContext(ThemeContext);

const { leftWidth, rightWidth } = useDrag({
containerRef,
dividerRef,
dividerWidth: themeContext.divider.width,
});

return (
<Container className={className} ref={containerRef}>
{leftChild(leftWidth)}
<Divider ref={dividerRef} />
{rightChild(rightWidth)}
</Container>
);
};

export default Draggable;
57 changes: 57 additions & 0 deletions playground/src/Draggable/useDrag.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { useState, useEffect, useCallback, RefObject } from "react";

interface IProps {
containerRef: RefObject<HTMLDivElement | null>;
dividerRef: RefObject<HTMLDivElement | null>;
dividerWidth: number;
}

function useDrag({ containerRef, dividerRef, dividerWidth }: IProps) {
const [width, setWidth] = useState(0);
const [containerRect, setContainerRect] = useState<DOMRect | null>(null);

useEffect(() => {
const containerEl = containerRef.current;
if (containerEl) {
const fullWidth = containerEl.clientWidth;
const containerRect = containerEl.getBoundingClientRect();
setContainerRect(containerRect);
setWidth(fullWidth / 2);
}
}, []);
const keepDragging = useCallback(
(event: MouseEvent) => {
const { clientX } = event;
if (containerRect) {
setWidth(clientX - containerRect.left);
}
},
[containerRect]
);
const stopDrag = useCallback(() => {
document.removeEventListener("mousemove", keepDragging);
document.removeEventListener("mouseup", stopDrag);
}, [keepDragging]);
const startDrag = useCallback(() => {
document.addEventListener("mousemove", keepDragging);
document.addEventListener("mouseup", stopDrag);
}, [keepDragging, stopDrag]);
useEffect(() => {
const dividerEl = dividerRef.current;
if (dividerEl) {
dividerEl.addEventListener("mousedown", startDrag);
}
return () => {
if (dividerEl) {
dividerEl.removeEventListener("mousedown", startDrag);
}
};
}, [startDrag]);

return {
leftWidth: width,
rightWidth: containerRect ? containerRect.width - width - dividerWidth : 0,
};
}

export default useDrag;
7 changes: 4 additions & 3 deletions playground/src/Editor/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { FC, useMemo } from "react";
import styled from "styled-components";

import { IEditorTabs, ISnippet } from "../types";
import EditorSetup from "./EditorSetup";
import { ITabConfig } from "../types";
Expand All @@ -13,16 +12,17 @@ import {
} from "../TabStyles";

const TabContainer = styled(StyledTabs)`
border-right: 0.05em solid rgba(0, 0, 0, 0.2);
min-width: ${props => props.theme.container.minWidth};
`;

interface IProps {
width: number;
code: ISnippet;
defaultTab: IEditorTabs;
onChange: (changed: string, type: IEditorTabs) => void;
}

const Editor: FC<IProps> = ({ code, defaultTab, onChange }) => {
const Editor: FC<IProps> = ({ code, defaultTab, onChange, width }) => {
const tabs: Readonly<ITabConfig<IEditorTabs>[]> = useMemo(
() => [
{ name: "HTML", value: "markup" },
Expand All @@ -34,6 +34,7 @@ const Editor: FC<IProps> = ({ code, defaultTab, onChange }) => {
return (
<TabContainer
defaultIndex={tabs.findIndex(tab => tab.value === defaultTab)}
style={{ width: width }}
>
<StyledTabList>
{tabs.map(tab => (
Expand Down
26 changes: 15 additions & 11 deletions playground/src/Playground.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC, useState } from "react";
import React, { FC, useState, useCallback, useEffect, useRef } from "react";
import styled, { ThemeProvider, DefaultTheme } from "styled-components";
import { useId } from "@reach/auto-id";

Expand All @@ -7,8 +7,9 @@ import Result from "./Result";
import { ISnippet, IEditorTabs, IResultTabs } from "./types";
import ourTheme from "./utils/theme";
import media from "./utils/media";
import Draggable from "./Draggable";

const Container = styled.div`
const StyledDraggable = styled(Draggable)`
border: ${props => props.theme.container.border};
display: flex;
min-height: ${props => props.theme.container.minHeight};
Expand Down Expand Up @@ -38,7 +39,6 @@ const Playground: FC<IProps> = ({
theme = ourTheme,
}) => {
const [snippet, setSnippet] = useState<ISnippet>(initialSnippet);

const id = useId(userId);

const onSnippetChange = (changed: string, type: IEditorTabs) => {
Expand All @@ -50,22 +50,26 @@ const Playground: FC<IProps> = ({

return (
<ThemeProvider theme={theme}>
<Container>
<Editor
code={snippet}
defaultTab={defaultEditorTab}
onChange={onSnippetChange}
/>
{id && (
<StyledDraggable
leftChild={width => (
<Editor
width={width}
code={snippet}
defaultTab={defaultEditorTab}
onChange={onSnippetChange}
/>
)}
rightChild={width => (
<Result
width={width}
id={id}
snippet={snippet}
defaultTab={defaultResultTab}
transformJs={transformJs}
presets={presets}
/>
)}
</Container>
/>
</ThemeProvider>
);
};
Expand Down
11 changes: 11 additions & 0 deletions playground/src/Result/Frame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@ import ErrorDisplay from "./ErrorDisplay";
const Container = styled.div`
position: relative;
height: 100%;
&::after {
content: "";
display: inline-block;
position: absolute;
width: 100%;
height: 100%;
z-index: 1;
top: 0;
left: 0;
}
`;

interface IProps {
Expand Down
7 changes: 6 additions & 1 deletion playground/src/Result/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ interface IProps {
defaultTab: IResultTabs;
transformJs: boolean;
presets: string[];
width: number;
}

const Result: FC<IProps> = ({
Expand All @@ -25,6 +26,7 @@ const Result: FC<IProps> = ({
presets,
defaultTab,
transformJs,
width,
}) => {
const [logs, setLogs] = useState<unknown[]>([]);
const tabs: Readonly<ITabConfig<IResultTabs>[]> = useMemo(
Expand All @@ -50,7 +52,10 @@ const Result: FC<IProps> = ({
waitForMessage();
}, [id]);
return (
<StyledTabs defaultIndex={tabs.findIndex(tab => tab.value === defaultTab)}>
<StyledTabs
defaultIndex={tabs.findIndex(tab => tab.value === defaultTab)}
style={{ width: width }}
>
<StyledTabList>
{tabs.map(tab => (
<StyledTab key={tab.value}>{tab.name}</StyledTab>
Expand Down
1 change: 1 addition & 0 deletions playground/src/TabStyles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export const StyledTabs = styled(Tabs)`
display: flex;
flex-direction: column;
width: 50%;
min-width: ${props => props.theme.container.minWidth};
${media.phone`
width: 100%;
Expand Down
28 changes: 4 additions & 24 deletions playground/src/types/styled-components.d.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,8 @@
import "styled-components";

import theme from "../utils/theme";

declare module "styled-components" {
export interface DefaultTheme {
container: {
border: string;
minHeight: string;
};
error: {
background: string;
color: string;
};
console: {
background: string;
};
tabs: {
tabHeader: {
borderBottom: string;
};
tabPanel: {
phoneHeight: string;
};
selectedTab: {
borderBottom: string;
};
};
}
type Theme = typeof theme;
export interface DefaultTheme extends Theme {}
}
5 changes: 5 additions & 0 deletions playground/src/utils/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const theme: DefaultTheme = {
container: {
border: "0.1em solid rgba(0, 0, 0, 0.3)",
minHeight: "20em",
minWidth: "20em",
},
error: {
background: "#e74c3c",
Expand All @@ -12,6 +13,10 @@ const theme: DefaultTheme = {
console: {
background: "rgba(0, 0, 0, 1)",
},
divider: {
width: 2,
background: " #202020",
},
tabs: {
tabHeader: {
borderBottom: "0.1em solid rgba(0, 0, 0, 0.1)",
Expand Down

0 comments on commit d6a31cc

Please sign in to comment.