mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-12 15:52:56 +10:00
fixes #2176, text align was not being reflected in summary sections
This commit is contained in:
@ -1,7 +1,7 @@
|
|||||||
|
import { sanitize } from "@reactive-resume/utils";
|
||||||
import { useEffect, useMemo } from "react";
|
import { useEffect, useMemo } from "react";
|
||||||
import { Helmet } from "react-helmet-async";
|
import { Helmet } from "react-helmet-async";
|
||||||
import { Outlet } from "react-router";
|
import { Outlet } from "react-router";
|
||||||
import sanitizeHtml from "sanitize-html";
|
|
||||||
import webfontloader from "webfontloader";
|
import webfontloader from "webfontloader";
|
||||||
|
|
||||||
import { useArtboardStore } from "../store/artboard";
|
import { useArtboardStore } from "../store/artboard";
|
||||||
@ -64,7 +64,7 @@ export const ArtboardPage = () => {
|
|||||||
<title>{name} | Reactive Resume</title>
|
<title>{name} | Reactive Resume</title>
|
||||||
{metadata.css.visible && (
|
{metadata.css.visible && (
|
||||||
<style id="custom-css" lang="css">
|
<style id="custom-css" lang="css">
|
||||||
{sanitizeHtml(metadata.css.value)}
|
{sanitize(metadata.css.value)}
|
||||||
</style>
|
</style>
|
||||||
)}
|
)}
|
||||||
</Helmet>
|
</Helmet>
|
||||||
|
|||||||
@ -12,27 +12,21 @@ export const Providers = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleMessage = (event: MessageEvent) => {
|
const handleMessage = (event: MessageEvent) => {
|
||||||
if (event.origin !== window.location.origin) return;
|
if (event.origin !== window.location.origin) return;
|
||||||
|
|
||||||
if (event.data.type === "SET_RESUME") setResume(event.data.payload);
|
if (event.data.type === "SET_RESUME") setResume(event.data.payload);
|
||||||
if (event.data.type === "SET_THEME") {
|
|
||||||
event.data.payload === "dark"
|
|
||||||
? document.documentElement.classList.add("dark")
|
|
||||||
: document.documentElement.classList.remove("dark");
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const resumeData = window.localStorage.getItem("resume");
|
window.addEventListener("message", handleMessage, false);
|
||||||
if (resumeData) {
|
|
||||||
setResume(JSON.parse(resumeData));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("message", handleMessage);
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("message", handleMessage);
|
window.removeEventListener("message", handleMessage, false);
|
||||||
};
|
};
|
||||||
}, [setResume]);
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const resumeData = window.localStorage.getItem("resume");
|
||||||
|
|
||||||
|
if (resumeData) setResume(JSON.parse(resumeData));
|
||||||
|
}, [window.localStorage.getItem("resume")]);
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
if (!resume) return null;
|
if (!resume) return null;
|
||||||
|
|||||||
@ -15,10 +15,9 @@ import type {
|
|||||||
URL,
|
URL,
|
||||||
} from "@reactive-resume/schema";
|
} from "@reactive-resume/schema";
|
||||||
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
||||||
import { cn, isEmptyString, isUrl, linearTransform } from "@reactive-resume/utils";
|
import { cn, isEmptyString, isUrl, linearTransform, sanitize } from "@reactive-resume/utils";
|
||||||
import get from "lodash.get";
|
import get from "lodash.get";
|
||||||
import React, { Fragment } from "react";
|
import React, { Fragment } from "react";
|
||||||
import sanitizeHtml from "sanitize-html";
|
|
||||||
|
|
||||||
import { BrandIcon } from "../components/brand-icon";
|
import { BrandIcon } from "../components/brand-icon";
|
||||||
import { Picture } from "../components/picture";
|
import { Picture } from "../components/picture";
|
||||||
@ -99,7 +98,7 @@ const Summary = () => {
|
|||||||
<div className="absolute left-[-4.5px] top-[8px] hidden size-[8px] rounded-full bg-primary group-[.main]:block" />
|
<div className="absolute left-[-4.5px] top-[8px] hidden size-[8px] rounded-full bg-primary group-[.main]:block" />
|
||||||
|
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(section.content) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(section.content) }}
|
||||||
style={{ columns: section.columns }}
|
style={{ columns: section.columns }}
|
||||||
className="wysiwyg"
|
className="wysiwyg"
|
||||||
/>
|
/>
|
||||||
@ -226,7 +225,7 @@ const Section = <T,>({
|
|||||||
|
|
||||||
{summary !== undefined && !isEmptyString(summary) && (
|
{summary !== undefined && !isEmptyString(summary) && (
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(summary) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(summary) }}
|
||||||
className="wysiwyg"
|
className="wysiwyg"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -15,10 +15,9 @@ import type {
|
|||||||
URL,
|
URL,
|
||||||
} from "@reactive-resume/schema";
|
} from "@reactive-resume/schema";
|
||||||
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
||||||
import { cn, isEmptyString, isUrl } from "@reactive-resume/utils";
|
import { cn, isEmptyString, isUrl, sanitize } from "@reactive-resume/utils";
|
||||||
import get from "lodash.get";
|
import get from "lodash.get";
|
||||||
import { Fragment } from "react";
|
import { Fragment } from "react";
|
||||||
import sanitizeHtml from "sanitize-html";
|
|
||||||
|
|
||||||
import { BrandIcon } from "../components/brand-icon";
|
import { BrandIcon } from "../components/brand-icon";
|
||||||
import { Picture } from "../components/picture";
|
import { Picture } from "../components/picture";
|
||||||
@ -90,7 +89,7 @@ const Summary = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(section.content) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(section.content) }}
|
||||||
style={{ columns: section.columns }}
|
style={{ columns: section.columns }}
|
||||||
className="wysiwyg col-span-4"
|
className="wysiwyg col-span-4"
|
||||||
/>
|
/>
|
||||||
@ -207,7 +206,7 @@ const Section = <T,>({
|
|||||||
|
|
||||||
{summary !== undefined && !isEmptyString(summary) && (
|
{summary !== undefined && !isEmptyString(summary) && (
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(summary) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(summary) }}
|
||||||
className="wysiwyg"
|
className="wysiwyg"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -15,10 +15,9 @@ import type {
|
|||||||
URL,
|
URL,
|
||||||
} from "@reactive-resume/schema";
|
} from "@reactive-resume/schema";
|
||||||
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
||||||
import { cn, isEmptyString, isUrl } from "@reactive-resume/utils";
|
import { cn, isEmptyString, isUrl, sanitize } from "@reactive-resume/utils";
|
||||||
import get from "lodash.get";
|
import get from "lodash.get";
|
||||||
import { Fragment } from "react";
|
import { Fragment } from "react";
|
||||||
import sanitizeHtml from "sanitize-html";
|
|
||||||
|
|
||||||
import { BrandIcon } from "../components/brand-icon";
|
import { BrandIcon } from "../components/brand-icon";
|
||||||
import { Picture } from "../components/picture";
|
import { Picture } from "../components/picture";
|
||||||
@ -90,7 +89,7 @@ const Summary = () => {
|
|||||||
<h4 className="mb-2 border-b pb-0.5 text-sm font-bold">{section.name}</h4>
|
<h4 className="mb-2 border-b pb-0.5 text-sm font-bold">{section.name}</h4>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(section.content) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(section.content) }}
|
||||||
style={{ columns: section.columns }}
|
style={{ columns: section.columns }}
|
||||||
className="wysiwyg"
|
className="wysiwyg"
|
||||||
/>
|
/>
|
||||||
@ -210,7 +209,7 @@ const Section = <T,>({
|
|||||||
|
|
||||||
{summary !== undefined && !isEmptyString(summary) && (
|
{summary !== undefined && !isEmptyString(summary) && (
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(summary) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(summary) }}
|
||||||
className="wysiwyg group-[.sidebar]:prose-invert"
|
className="wysiwyg group-[.sidebar]:prose-invert"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -15,10 +15,9 @@ import type {
|
|||||||
URL,
|
URL,
|
||||||
} from "@reactive-resume/schema";
|
} from "@reactive-resume/schema";
|
||||||
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
||||||
import { cn, isEmptyString, isUrl } from "@reactive-resume/utils";
|
import { cn, isEmptyString, isUrl, sanitize } from "@reactive-resume/utils";
|
||||||
import get from "lodash.get";
|
import get from "lodash.get";
|
||||||
import { Fragment } from "react";
|
import { Fragment } from "react";
|
||||||
import sanitizeHtml from "sanitize-html";
|
|
||||||
|
|
||||||
import { BrandIcon } from "../components/brand-icon";
|
import { BrandIcon } from "../components/brand-icon";
|
||||||
import { Picture } from "../components/picture";
|
import { Picture } from "../components/picture";
|
||||||
@ -110,7 +109,7 @@ const Summary = () => {
|
|||||||
<h4 className="mb-2 text-base font-bold">{section.name}</h4>
|
<h4 className="mb-2 text-base font-bold">{section.name}</h4>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(section.content) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(section.content) }}
|
||||||
style={{ columns: section.columns }}
|
style={{ columns: section.columns }}
|
||||||
className="wysiwyg"
|
className="wysiwyg"
|
||||||
/>
|
/>
|
||||||
@ -232,7 +231,7 @@ const Section = <T,>({
|
|||||||
|
|
||||||
{summary !== undefined && !isEmptyString(summary) && (
|
{summary !== undefined && !isEmptyString(summary) && (
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(summary) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(summary) }}
|
||||||
className="wysiwyg"
|
className="wysiwyg"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -15,10 +15,9 @@ import type {
|
|||||||
URL,
|
URL,
|
||||||
} from "@reactive-resume/schema";
|
} from "@reactive-resume/schema";
|
||||||
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
||||||
import { cn, hexToRgb, isEmptyString, isUrl } from "@reactive-resume/utils";
|
import { cn, hexToRgb, isEmptyString, isUrl, sanitize } from "@reactive-resume/utils";
|
||||||
import get from "lodash.get";
|
import get from "lodash.get";
|
||||||
import { Fragment } from "react";
|
import { Fragment } from "react";
|
||||||
import sanitizeHtml from "sanitize-html";
|
|
||||||
|
|
||||||
import { BrandIcon } from "../components/brand-icon";
|
import { BrandIcon } from "../components/brand-icon";
|
||||||
import { Picture } from "../components/picture";
|
import { Picture } from "../components/picture";
|
||||||
@ -90,7 +89,7 @@ const Summary = () => {
|
|||||||
<div className="p-custom space-y-4" style={{ backgroundColor: hexToRgb(primaryColor, 0.2) }}>
|
<div className="p-custom space-y-4" style={{ backgroundColor: hexToRgb(primaryColor, 0.2) }}>
|
||||||
<section id={section.id}>
|
<section id={section.id}>
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(section.content) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(section.content) }}
|
||||||
style={{ columns: section.columns }}
|
style={{ columns: section.columns }}
|
||||||
className="wysiwyg"
|
className="wysiwyg"
|
||||||
/>
|
/>
|
||||||
@ -212,7 +211,7 @@ const Section = <T,>({
|
|||||||
|
|
||||||
{summary !== undefined && !isEmptyString(summary) && (
|
{summary !== undefined && !isEmptyString(summary) && (
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(summary) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(summary) }}
|
||||||
className="wysiwyg"
|
className="wysiwyg"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -15,10 +15,16 @@ import type {
|
|||||||
URL,
|
URL,
|
||||||
} from "@reactive-resume/schema";
|
} from "@reactive-resume/schema";
|
||||||
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
||||||
import { cn, hexToRgb, isEmptyString, isUrl, linearTransform } from "@reactive-resume/utils";
|
import {
|
||||||
|
cn,
|
||||||
|
hexToRgb,
|
||||||
|
isEmptyString,
|
||||||
|
isUrl,
|
||||||
|
linearTransform,
|
||||||
|
sanitize,
|
||||||
|
} from "@reactive-resume/utils";
|
||||||
import get from "lodash.get";
|
import get from "lodash.get";
|
||||||
import { Fragment } from "react";
|
import { Fragment } from "react";
|
||||||
import sanitizeHtml from "sanitize-html";
|
|
||||||
|
|
||||||
import { BrandIcon } from "../components/brand-icon";
|
import { BrandIcon } from "../components/brand-icon";
|
||||||
import { Picture } from "../components/picture";
|
import { Picture } from "../components/picture";
|
||||||
@ -90,7 +96,7 @@ const Summary = () => {
|
|||||||
<h4 className="mb-2 border-b pb-0.5 text-sm font-bold">{section.name}</h4>
|
<h4 className="mb-2 border-b pb-0.5 text-sm font-bold">{section.name}</h4>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(section.content) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(section.content) }}
|
||||||
style={{ columns: section.columns }}
|
style={{ columns: section.columns }}
|
||||||
className="wysiwyg"
|
className="wysiwyg"
|
||||||
/>
|
/>
|
||||||
@ -215,7 +221,7 @@ const Section = <T,>({
|
|||||||
|
|
||||||
{summary !== undefined && !isEmptyString(summary) && (
|
{summary !== undefined && !isEmptyString(summary) && (
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(summary) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(summary) }}
|
||||||
className="wysiwyg"
|
className="wysiwyg"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -14,10 +14,9 @@ import type {
|
|||||||
URL,
|
URL,
|
||||||
} from "@reactive-resume/schema";
|
} from "@reactive-resume/schema";
|
||||||
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
||||||
import { cn, isEmptyString, isUrl } from "@reactive-resume/utils";
|
import { cn, isEmptyString, isUrl, sanitize } from "@reactive-resume/utils";
|
||||||
import get from "lodash.get";
|
import get from "lodash.get";
|
||||||
import React, { Fragment } from "react";
|
import React, { Fragment } from "react";
|
||||||
import sanitizeHtml from "sanitize-html";
|
|
||||||
|
|
||||||
import { BrandIcon } from "../components/brand-icon";
|
import { BrandIcon } from "../components/brand-icon";
|
||||||
import { Picture } from "../components/picture";
|
import { Picture } from "../components/picture";
|
||||||
@ -109,7 +108,7 @@ const Summary = () => {
|
|||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(section.content) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(section.content) }}
|
||||||
style={{ columns: section.columns }}
|
style={{ columns: section.columns }}
|
||||||
className="wysiwyg"
|
className="wysiwyg"
|
||||||
/>
|
/>
|
||||||
@ -223,7 +222,7 @@ const Section = <T,>({
|
|||||||
|
|
||||||
{summary !== undefined && !isEmptyString(summary) && (
|
{summary !== undefined && !isEmptyString(summary) && (
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(summary) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(summary) }}
|
||||||
className="wysiwyg"
|
className="wysiwyg"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -14,10 +14,9 @@ import type {
|
|||||||
URL,
|
URL,
|
||||||
} from "@reactive-resume/schema";
|
} from "@reactive-resume/schema";
|
||||||
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
||||||
import { cn, hexToRgb, isEmptyString, isUrl } from "@reactive-resume/utils";
|
import { cn, hexToRgb, isEmptyString, isUrl, sanitize } from "@reactive-resume/utils";
|
||||||
import get from "lodash.get";
|
import get from "lodash.get";
|
||||||
import React, { Fragment } from "react";
|
import React, { Fragment } from "react";
|
||||||
import sanitizeHtml from "sanitize-html";
|
|
||||||
|
|
||||||
import { BrandIcon } from "../components/brand-icon";
|
import { BrandIcon } from "../components/brand-icon";
|
||||||
import { Picture } from "../components/picture";
|
import { Picture } from "../components/picture";
|
||||||
@ -43,7 +42,7 @@ const Header = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(section.content) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(section.content) }}
|
||||||
style={{ columns: section.columns }}
|
style={{ columns: section.columns }}
|
||||||
className="wysiwyg"
|
className="wysiwyg"
|
||||||
/>
|
/>
|
||||||
@ -218,7 +217,7 @@ const Section = <T,>({
|
|||||||
|
|
||||||
{summary !== undefined && !isEmptyString(summary) && (
|
{summary !== undefined && !isEmptyString(summary) && (
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(summary) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(summary) }}
|
||||||
className="wysiwyg"
|
className="wysiwyg"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -15,10 +15,9 @@ import type {
|
|||||||
URL,
|
URL,
|
||||||
} from "@reactive-resume/schema";
|
} from "@reactive-resume/schema";
|
||||||
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
||||||
import { cn, isEmptyString, isUrl } from "@reactive-resume/utils";
|
import { cn, isEmptyString, isUrl, sanitize } from "@reactive-resume/utils";
|
||||||
import get from "lodash.get";
|
import get from "lodash.get";
|
||||||
import { Fragment } from "react";
|
import { Fragment } from "react";
|
||||||
import sanitizeHtml from "sanitize-html";
|
|
||||||
|
|
||||||
import { BrandIcon } from "../components/brand-icon";
|
import { BrandIcon } from "../components/brand-icon";
|
||||||
import { Picture } from "../components/picture";
|
import { Picture } from "../components/picture";
|
||||||
@ -106,7 +105,7 @@ const Summary = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(section.content) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(section.content) }}
|
||||||
style={{ columns: section.columns }}
|
style={{ columns: section.columns }}
|
||||||
className="wysiwyg"
|
className="wysiwyg"
|
||||||
/>
|
/>
|
||||||
@ -219,7 +218,7 @@ const Section = <T,>({
|
|||||||
|
|
||||||
{summary !== undefined && !isEmptyString(summary) && (
|
{summary !== undefined && !isEmptyString(summary) && (
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(summary) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(summary) }}
|
||||||
className="wysiwyg"
|
className="wysiwyg"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -257,7 +256,7 @@ const Section = <T,>({
|
|||||||
|
|
||||||
{summary !== undefined && !isEmptyString(summary) && (
|
{summary !== undefined && !isEmptyString(summary) && (
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(summary) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(summary) }}
|
||||||
className="wysiwyg"
|
className="wysiwyg"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -14,10 +14,9 @@ import type {
|
|||||||
URL,
|
URL,
|
||||||
} from "@reactive-resume/schema";
|
} from "@reactive-resume/schema";
|
||||||
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
||||||
import { cn, isEmptyString, isUrl } from "@reactive-resume/utils";
|
import { cn, isEmptyString, isUrl, sanitize } from "@reactive-resume/utils";
|
||||||
import get from "lodash.get";
|
import get from "lodash.get";
|
||||||
import React, { Fragment } from "react";
|
import React, { Fragment } from "react";
|
||||||
import sanitizeHtml from "sanitize-html";
|
|
||||||
|
|
||||||
import { BrandIcon } from "../components/brand-icon";
|
import { BrandIcon } from "../components/brand-icon";
|
||||||
import { Picture } from "../components/picture";
|
import { Picture } from "../components/picture";
|
||||||
@ -110,7 +109,7 @@ const Summary = () => {
|
|||||||
<h4 className="font-bold text-primary">{section.name}</h4>
|
<h4 className="font-bold text-primary">{section.name}</h4>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(section.content) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(section.content) }}
|
||||||
style={{ columns: section.columns }}
|
style={{ columns: section.columns }}
|
||||||
className="wysiwyg"
|
className="wysiwyg"
|
||||||
/>
|
/>
|
||||||
@ -225,7 +224,7 @@ const Section = <T,>({
|
|||||||
|
|
||||||
{summary !== undefined && !isEmptyString(summary) && (
|
{summary !== undefined && !isEmptyString(summary) && (
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(summary) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(summary) }}
|
||||||
className="wysiwyg"
|
className="wysiwyg"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -15,10 +15,9 @@ import type {
|
|||||||
URL,
|
URL,
|
||||||
} from "@reactive-resume/schema";
|
} from "@reactive-resume/schema";
|
||||||
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
||||||
import { cn, isEmptyString, isUrl } from "@reactive-resume/utils";
|
import { cn, isEmptyString, isUrl, sanitize } from "@reactive-resume/utils";
|
||||||
import get from "lodash.get";
|
import get from "lodash.get";
|
||||||
import { Fragment } from "react";
|
import { Fragment } from "react";
|
||||||
import sanitizeHtml from "sanitize-html";
|
|
||||||
|
|
||||||
import { BrandIcon } from "../components/brand-icon";
|
import { BrandIcon } from "../components/brand-icon";
|
||||||
import { Picture } from "../components/picture";
|
import { Picture } from "../components/picture";
|
||||||
@ -111,7 +110,7 @@ const Summary = () => {
|
|||||||
<h4 className="mb-2 border-b border-primary text-base font-bold">{section.name}</h4>
|
<h4 className="mb-2 border-b border-primary text-base font-bold">{section.name}</h4>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(section.content) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(section.content) }}
|
||||||
style={{ columns: section.columns }}
|
style={{ columns: section.columns }}
|
||||||
className="wysiwyg"
|
className="wysiwyg"
|
||||||
/>
|
/>
|
||||||
@ -240,7 +239,7 @@ const Section = <T,>({
|
|||||||
|
|
||||||
{summary !== undefined && !isEmptyString(summary) && (
|
{summary !== undefined && !isEmptyString(summary) && (
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(summary) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(summary) }}
|
||||||
className="wysiwyg"
|
className="wysiwyg"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -15,10 +15,9 @@ import type {
|
|||||||
URL,
|
URL,
|
||||||
} from "@reactive-resume/schema";
|
} from "@reactive-resume/schema";
|
||||||
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
import { Education, Experience, Volunteer } from "@reactive-resume/schema";
|
||||||
import { cn, isEmptyString, isUrl } from "@reactive-resume/utils";
|
import { cn, isEmptyString, isUrl, sanitize } from "@reactive-resume/utils";
|
||||||
import get from "lodash.get";
|
import get from "lodash.get";
|
||||||
import { Fragment } from "react";
|
import { Fragment } from "react";
|
||||||
import sanitizeHtml from "sanitize-html";
|
|
||||||
|
|
||||||
import { BrandIcon } from "../components/brand-icon";
|
import { BrandIcon } from "../components/brand-icon";
|
||||||
import { Picture } from "../components/picture";
|
import { Picture } from "../components/picture";
|
||||||
@ -91,7 +90,7 @@ const Summary = () => {
|
|||||||
<h4 className="mb-2 border-b pb-0.5 text-sm font-bold">{section.name}</h4>
|
<h4 className="mb-2 border-b pb-0.5 text-sm font-bold">{section.name}</h4>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(section.content) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(section.content) }}
|
||||||
style={{ columns: section.columns }}
|
style={{ columns: section.columns }}
|
||||||
className="wysiwyg"
|
className="wysiwyg"
|
||||||
/>
|
/>
|
||||||
@ -206,7 +205,7 @@ const Section = <T,>({
|
|||||||
|
|
||||||
{summary !== undefined && !isEmptyString(summary) && (
|
{summary !== undefined && !isEmptyString(summary) && (
|
||||||
<div
|
<div
|
||||||
dangerouslySetInnerHTML={{ __html: sanitizeHtml(summary) }}
|
dangerouslySetInnerHTML={{ __html: sanitize(summary) }}
|
||||||
className="wysiwyg"
|
className="wysiwyg"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -17,25 +17,41 @@ export const BuilderPage = () => {
|
|||||||
const resume = useResumeStore((state) => state.resume);
|
const resume = useResumeStore((state) => state.resume);
|
||||||
const title = useResumeStore((state) => state.resume.title);
|
const title = useResumeStore((state) => state.resume.title);
|
||||||
|
|
||||||
const updateResumeInFrame = useCallback(() => {
|
const syncResumeToArtboard = useCallback(() => {
|
||||||
const message = { type: "SET_RESUME", payload: resume.data };
|
|
||||||
|
|
||||||
setImmediate(() => {
|
setImmediate(() => {
|
||||||
frameRef?.contentWindow?.postMessage(message, "*");
|
if (!frameRef?.contentWindow) return;
|
||||||
|
const message = { type: "SET_RESUME", payload: resume.data };
|
||||||
|
frameRef.contentWindow.postMessage(message, "*");
|
||||||
});
|
});
|
||||||
}, [frameRef?.contentWindow, resume.data]);
|
}, [frameRef?.contentWindow, resume.data]);
|
||||||
|
|
||||||
// Send resume data to iframe on initial load
|
// Send resume data to iframe on initial load
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!frameRef) return;
|
if (!frameRef) return;
|
||||||
frameRef.addEventListener("load", updateResumeInFrame);
|
|
||||||
|
frameRef.addEventListener("load", syncResumeToArtboard);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
frameRef.removeEventListener("load", updateResumeInFrame);
|
frameRef.removeEventListener("load", syncResumeToArtboard);
|
||||||
|
};
|
||||||
|
}, [frameRef]);
|
||||||
|
|
||||||
|
// Persistently check if iframe has loaded using setInterval
|
||||||
|
useEffect(() => {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
if (frameRef?.contentWindow?.document.readyState === "complete") {
|
||||||
|
syncResumeToArtboard();
|
||||||
|
clearInterval(interval);
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearInterval(interval);
|
||||||
};
|
};
|
||||||
}, [frameRef]);
|
}, [frameRef]);
|
||||||
|
|
||||||
// Send resume data to iframe on change of resume data
|
// Send resume data to iframe on change of resume data
|
||||||
useEffect(updateResumeInFrame, [resume.data]);
|
useEffect(syncResumeToArtboard, [resume.data]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -43,6 +43,8 @@ export const TypographySection = () => {
|
|||||||
|
|
||||||
const loadFontSuggestions = useCallback(() => {
|
const loadFontSuggestions = useCallback(() => {
|
||||||
for (const font of fontSuggestions) {
|
for (const font of fontSuggestions) {
|
||||||
|
if (localFonts.includes(font)) continue;
|
||||||
|
|
||||||
webfontloader.load({
|
webfontloader.load({
|
||||||
events: false,
|
events: false,
|
||||||
classes: false,
|
classes: false,
|
||||||
|
|||||||
@ -9,12 +9,13 @@
|
|||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"papaparse": "^5.4.1",
|
|
||||||
"dayjs": "^1.11.11",
|
|
||||||
"unique-names-generator": "^4.7.1",
|
|
||||||
"clsx": "^2.1.1",
|
|
||||||
"tailwind-merge": "^2.3.0",
|
|
||||||
"@swc/helpers": "~0.5.11",
|
"@swc/helpers": "~0.5.11",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"dayjs": "^1.11.11",
|
||||||
|
"papaparse": "^5.4.1",
|
||||||
|
"sanitize-html": "^2.14.0",
|
||||||
|
"tailwind-merge": "^2.3.0",
|
||||||
|
"unique-names-generator": "^4.7.1",
|
||||||
"zod": "^3.24.1"
|
"zod": "^3.24.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import sanitizeHtml from "sanitize-html";
|
||||||
import type { Config as UniqueNamesConfig } from "unique-names-generator";
|
import type { Config as UniqueNamesConfig } from "unique-names-generator";
|
||||||
import { adjectives, animals, uniqueNamesGenerator } from "unique-names-generator";
|
import { adjectives, animals, uniqueNamesGenerator } from "unique-names-generator";
|
||||||
|
|
||||||
@ -55,3 +56,17 @@ export const parseLayoutLocator = (payload: SortablePayload | null): LayoutLocat
|
|||||||
|
|
||||||
return { page, column, section };
|
return { page, column, section };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const sanitize = (html: string, options?: sanitizeHtml.IOptions) => {
|
||||||
|
return sanitizeHtml(html, {
|
||||||
|
...options,
|
||||||
|
allowedAttributes: {
|
||||||
|
...options?.allowedAttributes,
|
||||||
|
"*": ["class", "style"],
|
||||||
|
},
|
||||||
|
allowedStyles: {
|
||||||
|
...options?.allowedStyles,
|
||||||
|
"*": { "text-align": [/^left$/, /^right$/, /^center$/, /^justify$/] },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user