Skip to content

Commit

Permalink
feat(react): VivliostyleViewer component
Browse files Browse the repository at this point in the history
  • Loading branch information
uetchy committed Jun 22, 2020
1 parent 010578e commit b857860
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 26 deletions.
35 changes: 30 additions & 5 deletions packages/react/example/src/App.js
@@ -1,11 +1,36 @@
import React from "react";

import { Renderer } from "@vivliostyle/react";
import "@vivliostyle/react/dist/index.css";
import React, { useState } from "react";
import { VivliostyleViewer } from "@vivliostyle/react";
// import "@vivliostyle/react/dist/index.css";

const App = () => {
const [source, setSource] = useState(
"https://vivliostyle.github.io/vivliostyle_doc/samples/gon/index.html",
);

function change(url) {
setSource(url);
}

return (
<Renderer entrypoint="https://vivliostyle.github.io/vivliostyle_doc/samples/gon/index.html" />
<>
<button
onClick={() =>
change(
"https://vivliostyle.github.io/vivliostyle_doc/samples/gon/index.html",
)
}>
Gon
</button>
<button
onClick={() =>
change(
"https://vivliostyle.github.io/vivliostyle_doc/samples/gutenberg/Alice.html",
)
}>
Alice
</button>
<VivliostyleViewer source={source} />
</>
);
};

Expand Down
7 changes: 6 additions & 1 deletion packages/react/package.json
Expand Up @@ -15,9 +15,14 @@
"test:watch": "react-scripts test --env=jsdom"
},
"dependencies": {
"@chakra-ui/core": "^0.8.0",
"@emotion/core": "^10.0.28",
"@emotion/styled": "^10.0.27",
"@vivliostyle/core": "^2.0.0",
"@vivliostyle/viewer": "^2.0.0",
"react-helmet": "^6.1.0"
"emotion-theming": "^10.0.27",
"react-helmet": "^6.1.0",
"react-spinners": "^0.8.3"
},
"peerDependencies": {
"react": "^16.13.1"
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/index.tsx
Expand Up @@ -2,6 +2,7 @@ import * as React from "react";
import styles from "./styles.module.css";

export { Renderer } from "./renderer";
export { VivliostyleViewer } from "./viewer";

interface Props {
text: string;
Expand Down
80 changes: 60 additions & 20 deletions packages/react/src/renderer.tsx
@@ -1,37 +1,77 @@
import React, { useRef, useEffect } from "react";
import styled from "@emotion/styled";
import { CoreViewer, Payload, ReadyState } from "@vivliostyle/core";

import { CoreViewer, Payload } from "@vivliostyle/core";
type MessageType = "debug" | "info" | "warn";

type EventCallbackFn = (payload: Payload) => void;
interface RendererProps {
entrypoint: string;
onLoad?: EventCallbackFn;
onError?: EventCallbackFn;
}

function addListener(
ref: CoreViewer,
event: string,
fn: EventCallbackFn | undefined,
) {
fn && ref.addListener(event, fn);
onLoad?: () => void;
onMessage?: (message: string, type: MessageType) => void;
onError?: (error: string) => void;
onReadyStateChange?: (state: ReadyState) => void;
}

export const Renderer: React.FC<RendererProps> = ({
entrypoint,
onLoad,
onMessage,
onError,
onReadyStateChange,
}) => {
const coreRef = useRef<HTMLDivElement>(null);
const viewer = useRef<CoreViewer>();
const containerRef = useRef<HTMLDivElement>(null);
const instanceRef = useRef<CoreViewer>();

useEffect(() => {
viewer.current = new CoreViewer({
viewportElement: coreRef.current!,
instanceRef.current = new CoreViewer({
viewportElement: containerRef.current!,
});
viewer.current.loadDocument(entrypoint, undefined, {});
addListener(viewer.current, "loaded", onLoad);
addListener(viewer.current, "error", onError);
const instance = instanceRef.current!;

function handleMessage(payload: Payload, type: MessageType) {
onMessage && onMessage(payload.content, type);
}

const handleDebug = (payload: Payload) => handleMessage(payload, "debug");
const handleInfo = (payload: Payload) => handleMessage(payload, "info");
const handleWarn = (payload: Payload) => handleMessage(payload, "warn");

function handleError(payload: Payload) {
onError && onError(payload.content);
}

function handleReadyStateChange() {
const { readyState } = instanceRef.current!;
onReadyStateChange && onReadyStateChange(readyState);
}

function handleLoaded() {
onLoad && onLoad();
}

instance.addListener("debug", handleDebug);
instance.addListener("info", handleInfo);
instance.addListener("warn", handleWarn);
instance.addListener("error", handleError);
instance.addListener("readystatechange", handleReadyStateChange);
instance.addListener("loaded", handleLoaded);

instance.loadDocument(entrypoint, undefined, {});

return () => {
onReadyStateChange && onReadyStateChange(ReadyState.LOADING);
instance.removeListener("debug", handleDebug);
instance.removeListener("info", handleInfo);
instance.removeListener("warn", handleWarn);
instance.removeListener("error", handleError);
instance.removeListener("readystatechange", handleReadyStateChange);
instance.removeListener("loaded", handleLoaded);
containerRef.current!.innerHTML = "";
};
}, [entrypoint]);
return <div ref={coreRef} />;
return <Container ref={containerRef} />;
};

const Container = styled.div`
position: relative;
`;
20 changes: 20 additions & 0 deletions packages/react/src/theme.ts
@@ -0,0 +1,20 @@
export default {
breakpoints: ["30em", "48em", "62em", "80em"],
fonts: {
heading: '"Avenir Next", sans-serif',
body: "system-ui, sans-serif",
mono: "Menlo, monospace",
},
fontSizes: {
xs: "0.75rem",
sm: "0.875rem",
md: "1rem",
lg: "1.125rem",
xl: "1.25rem",
"2xl": "1.5rem",
"3xl": "1.875rem",
"4xl": "2.25rem",
"5xl": "3rem",
"6xl": "4rem",
},
};
2 changes: 2 additions & 0 deletions packages/react/src/typings.d.ts
Expand Up @@ -16,3 +16,5 @@ declare module "*.svg" {
export default svgUrl;
export { svgComponent as ReactComponent };
}

declare module "react-spinners/*";
57 changes: 57 additions & 0 deletions packages/react/src/viewer.tsx
@@ -0,0 +1,57 @@
import React, { useState } from "react";
import { ReadyState } from "@vivliostyle/core";
import { Spinner } from "@chakra-ui/core";
import styled from "@emotion/styled";
import { ThemeProvider } from "@chakra-ui/core";
import { theme } from "@chakra-ui/core";

import { Renderer } from "./renderer";

interface VivliostyleViewerProps {
source: string;
}

export const VivliostyleViewer: React.FC<VivliostyleViewerProps> = ({
source,
}) => {
const [loading, setLoading] = useState(true);

function handleStateChange(state: ReadyState) {
console.log(state);

if (state === ReadyState.COMPLETE) {
setLoading(false);
} else {
setLoading(true);
}
}

return (
<ThemeProvider theme={theme}>
<Container>
<Renderer entrypoint={source} onReadyStateChange={handleStateChange} />
{loading && (
<OverflowView>
<Spinner />
</OverflowView>
)}
</Container>
</ThemeProvider>
);
};

const Container = styled.div`
position: relative;
width: fit-content;
`;

const OverflowView = styled.div`
position: absolute;
top: 0%;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
/* background: #2d2d2d; */
`;

0 comments on commit b857860

Please sign in to comment.