Skip to content

Commit

Permalink
Merge pull request #1524 from AmruthPillai/feat/v4/replace-templates-…
Browse files Browse the repository at this point in the history
…library-with-microfrontend-app

feat(templates): replace library with microfrontend app for templates
  • Loading branch information
AmruthPillai authored Nov 7, 2023
2 parents fca6154 + 1aa8aa6 commit 9acf7e8
Show file tree
Hide file tree
Showing 87 changed files with 1,514 additions and 1,837 deletions.
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
{
"typescript.tsdk": "node_modules/typescript/lib",
"tailwindCSS.experimental.classRegex": [
["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
["cn\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
],
"yaml.schemas": {
"https://json.schemastore.org/github-workflow.json": ".github/workflows/*.yml",
"https://raw.githubusercontent.com/compose-spec/compose-spec/master/schema/compose-spec.json": [
Expand Down
31 changes: 31 additions & 0 deletions apps/artboard/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"extends": ["plugin:@nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"extends": ["plugin:tailwindcss/recommended"],
"settings": {
"tailwindcss": {
"callees": ["cn", "clsx", "cva"],
"config": "tailwind.config.js"
}
},
"rules": {
// react-hooks
"react-hooks/exhaustive-deps": "off",

// tailwindcss
"tailwindcss/no-custom-classname": "off"
}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}
37 changes: 37 additions & 0 deletions apps/artboard/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Artboard | Reactive Resume</title>
<base href="/" />

<!-- Meta -->
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

<!-- Favicon -->
<link
rel="icon"
type="image/svg+xml"
href="/icon/dark.svg"
media="(prefers-color-scheme: light)"
/>
<link
rel="icon"
type="image/svg+xml"
href="/icon/light.svg"
media="(prefers-color-scheme: dark)"
/>

<!-- Styles -->
<link rel="stylesheet" href="/src/styles/main.css" />
</head>
<body>
<div id="root"></div>

<script type="module" src="/src/main.tsx"></script>

<!-- Phosphor Icons -->
<script src="https://unpkg.com/@phosphor-icons/web"></script>
</body>
</html>
10 changes: 10 additions & 0 deletions apps/artboard/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const { join } = require("path");

module.exports = {
plugins: {
tailwindcss: {
config: join(__dirname, "tailwind.config.js"),
},
autoprefixer: {},
},
};
64 changes: 64 additions & 0 deletions apps/artboard/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"name": "artboard",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/artboard/src",
"projectType": "application",
"targets": {
"build": {
"executor": "@nx/vite:build",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "production",
"options": {
"outputPath": "dist/apps/artboard"
},
"configurations": {
"development": {
"mode": "development"
},
"production": {
"mode": "production"
}
}
},
"serve": {
"executor": "@nx/vite:dev-server",
"defaultConfiguration": "development",
"options": {
"buildTarget": "artboard:build"
},
"configurations": {
"development": {
"buildTarget": "artboard:build:development",
"hmr": true
},
"production": {
"buildTarget": "artboard:build:production",
"hmr": false
}
}
},
"preview": {
"executor": "@nx/vite:preview-server",
"defaultConfiguration": "development",
"options": {
"buildTarget": "artboard:build"
},
"configurations": {
"development": {
"buildTarget": "artboard:build:development"
},
"production": {
"buildTarget": "artboard:build:production"
}
}
},
"lint": {
"executor": "@nx/eslint:lint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["apps/artboard/**/*.{ts,tsx,js,jsx}"]
}
}
},
"tags": ["frontend"]
}
Binary file added apps/artboard/public/favicon.ico
Binary file not shown.
8 changes: 8 additions & 0 deletions apps/artboard/public/icon/dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions apps/artboard/public/icon/light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file.
49 changes: 49 additions & 0 deletions apps/artboard/src/components/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useTheme } from "@reactive-resume/hooks";
import { cn, pageSizeMap } from "@reactive-resume/utils";

import { useArtboardStore } from "../store/artboard";

type Props = {
mode?: "preview" | "builder";
pageNumber: number;
children: React.ReactNode;
};

export const MM_TO_PX = 3.78;

export const Page = ({ mode = "preview", pageNumber, children }: Props) => {
const { isDarkMode } = useTheme();

const page = useArtboardStore((state) => state.resume.metadata.page);
const fontFamily = useArtboardStore((state) => state.resume.metadata.typography.font.family);

return (
<div
data-page={pageNumber}
className={cn("relative bg-white", mode === "builder" && "shadow-2xl")}
style={{
fontFamily,
padding: page.margin,
width: `${pageSizeMap[page.format].width * MM_TO_PX * window.devicePixelRatio}px`,
minHeight: `${pageSizeMap[page.format].height * MM_TO_PX * window.devicePixelRatio}px`,
}}
>
{mode === "builder" && page.options.pageNumbers && (
<div className={cn("absolute -top-7 left-0 font-bold", isDarkMode && "text-white")}>
Page {pageNumber}
</div>
)}

{children}

{mode === "builder" && page.options.breakLine && (
<div
className="absolute inset-x-0 border-b border-dashed"
style={{
top: `${pageSizeMap[page.format].height * MM_TO_PX * window.devicePixelRatio}px`,
}}
/>
)}
</div>
);
};
22 changes: 22 additions & 0 deletions apps/artboard/src/components/picture.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { isUrl } from "@reactive-resume/utils";

import { useArtboardStore } from "../store/artboard";

export const Picture = () => {
const picture = useArtboardStore((state) => state.resume.basics.picture);

if (!isUrl(picture.url) || picture.effects.hidden) return null;

return (
<img
src={picture.url}
alt="Profile"
className="object-cover"
style={{
maxWidth: `${picture.size}px`,
aspectRatio: `${picture.aspectRatio}`,
borderRadius: `${picture.borderRadius}px`,
}}
/>
);
};
13 changes: 13 additions & 0 deletions apps/artboard/src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { StrictMode } from "react";
import * as ReactDOM from "react-dom/client";
import { RouterProvider } from "react-router-dom";

import { router } from "./router";

const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);

root.render(
<StrictMode>
<RouterProvider router={router} />
</StrictMode>,
);
45 changes: 45 additions & 0 deletions apps/artboard/src/pages/artboard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useEffect, useMemo } from "react";
import { Outlet } from "react-router-dom";
import webfontloader from "webfontloader";

import { useArtboardStore } from "../store/artboard";

export const ArtboardPage = () => {
const metadata = useArtboardStore((state) => state.resume.metadata);

const fontString = useMemo(() => {
const family = metadata.typography.font.family;
const variants = metadata.typography.font.variants.join(",");
const subset = metadata.typography.font.subset;

return `${family}:${variants}:${subset}`;
}, [metadata.typography.font]);

useEffect(() => {
webfontloader.load({
google: { families: [fontString] },
active: () => {
const height = window.document.body.offsetHeight;
const message = { type: "PAGE_LOADED", payload: { height } };
window.postMessage(message, "*");
},
});
}, [fontString]);

// Font Size & Line Height
useEffect(() => {
document.documentElement.style.setProperty("font-size", `${metadata.typography.font.size}px`);
document.documentElement.style.setProperty("line-height", `${metadata.typography.lineHeight}`);
}, [metadata]);

// Underline Links
useEffect(() => {
if (metadata.typography.underlineLinks) {
document.querySelector("#root")!.classList.add("underline-links");
} else {
document.querySelector("#root")!.classList.remove("underline-links");
}
}, [metadata]);

return <Outlet />;
};
63 changes: 63 additions & 0 deletions apps/artboard/src/pages/builder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { SectionKey } from "@reactive-resume/schema";
import { pageSizeMap } from "@reactive-resume/utils";
import { useEffect, useRef } from "react";
import { ReactZoomPanPinchRef, TransformComponent, TransformWrapper } from "react-zoom-pan-pinch";

import { MM_TO_PX, Page } from "../components/page";
import { useArtboardStore } from "../store/artboard";
import { Rhyhorn } from "../templates/rhyhorn";

export const BuilderLayout = () => {
const transformRef = useRef<ReactZoomPanPinchRef>(null);
const format = useArtboardStore((state) => state.resume.metadata.page.format);
const layout = useArtboardStore((state) => state.resume.metadata.layout);
const template = useArtboardStore((state) => state.resume.metadata.template);

useEffect(() => {
const handleMessage = (event: MessageEvent) => {
if (event.origin !== window.location.origin) return;

if (event.data.type === "ZOOM_IN") transformRef.current?.zoomIn(0.2);
if (event.data.type === "ZOOM_OUT") transformRef.current?.zoomOut(0.2);
if (event.data.type === "CENTER_VIEW") transformRef.current?.centerView();
if (event.data.type === "RESET_VIEW") {
transformRef.current?.resetTransform(0);
setTimeout(() => transformRef.current?.centerView(0.8, 0), 10);
}
};

window.addEventListener("message", handleMessage);

return () => {
window.removeEventListener("message", handleMessage);
};
}, [transformRef]);

return (
<TransformWrapper
centerOnInit
maxScale={2}
minScale={0.4}
initialScale={0.8}
ref={transformRef}
limitToBounds={false}
>
<TransformComponent
wrapperClass="!w-screen !h-screen"
contentClass="grid items-start justify-center space-x-12 pointer-events-none"
contentStyle={{
width: `${layout.length * (pageSizeMap[format].width * MM_TO_PX + 42)}px`,
gridTemplateColumns: `repeat(${layout.length}, 1fr)`,
}}
>
{layout.map((columns, pageIndex) => (
<Page key={pageIndex} mode="builder" pageNumber={pageIndex + 1}>
{template === "rhyhorn" && (
<Rhyhorn isFirstPage={pageIndex === 0} columns={columns as SectionKey[][]} />
)}
</Page>
))}
</TransformComponent>
</TransformWrapper>
);
};
22 changes: 22 additions & 0 deletions apps/artboard/src/pages/preview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { SectionKey } from "@reactive-resume/schema";

import { Page } from "../components/page";
import { useArtboardStore } from "../store/artboard";
import { Rhyhorn } from "../templates/rhyhorn";

export const PreviewLayout = () => {
const layout = useArtboardStore((state) => state.resume.metadata.layout);
const template = useArtboardStore((state) => state.resume.metadata.template);

return (
<>
{layout.map((columns, pageIndex) => (
<Page key={pageIndex} mode="preview" pageNumber={pageIndex + 1}>
{template === "rhyhorn" && (
<Rhyhorn isFirstPage={pageIndex === 0} columns={columns as SectionKey[][]} />
)}
</Page>
))}
</>
);
};
Loading

0 comments on commit 9acf7e8

Please sign in to comment.