Skip to content

Commit

Permalink
add typr editor demo
Browse files Browse the repository at this point in the history
GraemeFulton committed Aug 16, 2024
1 parent a04148d commit a229018
Showing 30 changed files with 5,646 additions and 644 deletions.
2 changes: 1 addition & 1 deletion app/dashboard/drafts/page.js
Original file line number Diff line number Diff line change
@@ -46,7 +46,7 @@ export default async function AccountPage() {

return (
<Layout sessionUser={userData?.user?.id} background={"#fbfcff"}>
<div className="flex flex-col overflow-y-auto pt-[96px] mx-auto w-full">
<div className="flex flex-col overflow-y-auto mx-auto w-full">
{/* {userData?.user?.id} */}
<div
className="pb-20 mx-auto px-2 sm:px-6 lg:px-8 w-full"
2 changes: 1 addition & 1 deletion app/dashboard/published/page.js
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ export default async function AccountPage() {

return (
<Layout sessionUser={userData?.user?.id} background={"#fbfcff"}>
<div className="flex flex-col overflow-y-auto pt-[96px] mx-auto w-full">
<div className="flex flex-col overflow-y-auto mx-auto w-full">
{/* {userData?.user?.id} */}
<div
className="pb-20 mx-auto px-2 sm:px-6 lg:px-8 w-full"
2 changes: 1 addition & 1 deletion app/dashboard/tagged/page.js
Original file line number Diff line number Diff line change
@@ -35,7 +35,7 @@ export default async function AccountPage() {

return (
<Layout sessionUser={userData?.user?.id} background={"#fbfcff"}>
<div className="flex flex-col overflow-y-auto pt-[96px] mx-auto w-full">
<div className="flex flex-col overflow-y-auto mx-auto w-full">
{/* {userData?.user?.id} */}
<div
className="pb-20 mx-auto px-2 sm:px-6 lg:px-8 w-full"
2 changes: 1 addition & 1 deletion app/notifications/page.js
Original file line number Diff line number Diff line change
@@ -47,7 +47,7 @@ export default async function NotificationsPage() {

return (
<Layout sessionUser={userData?.user?.id} background={"#fbfcff"}>
<div className="flex flex-col overflow-y-auto pt-[96px] mx-auto w-full">
<div className="flex flex-col overflow-y-auto mx-auto w-full">
{/* {userData?.user?.id} */}
<div
className="pb-20 mx-auto px-2 sm:px-6 lg:px-8 w-full"
12 changes: 12 additions & 0 deletions app/typr/metadata.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export default function Metadata({ seoTitle, seoDescription }) {
return (
<>
<title>{seoTitle}</title>
<meta name="description" content={seoDescription} />
<meta property="og:image" content="/static/images/typr-og.jpg" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:image:alt" content="Tiptypr Editor Preview" />
</>
);
}
673 changes: 673 additions & 0 deletions app/typr/page.js

Large diffs are not rendered by default.

26 changes: 26 additions & 0 deletions components/KofiWidget.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React, { useEffect } from 'react';

const KofiWidget = () => {
useEffect(() => {
const script = document.createElement('script');
script.src = 'https://storage.ko-fi.com/cdn/scripts/overlay-widget.js';
script.async = true;
script.onload = () => {
window.kofiWidgetOverlay.draw('prototypr', {
'type': 'floating-chat',
'floating-chat.donateButton.text': 'Tip Me',
'floating-chat.donateButton.background-color': '#00b9fe',
'floating-chat.donateButton.text-color': '#fff'
});
};
document.body.appendChild(script);

return () => {
document.body.removeChild(script);
};
}, []);

return null; // This component doesn't render anything visible
};

export default KofiWidget;
16 changes: 14 additions & 2 deletions components/Navbar/parts/MenuItems.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"use client"
"use client";
import React from "react";
import { keyframes } from "@stitches/react";
import { styled } from "../../../stitches.config";
@@ -311,6 +311,14 @@ const ContentList = styled("ul", {
gridTemplateRows: "repeat(3, 1fr)",
},
},
four: {
"@media only screen and (min-width: 600px)": {
width: 750,
gridAutoFlow: "column",
gridTemplateColumns: "repeat(3, 1fr)",
gridTemplateRows: "repeat(2, 1fr)",
},
},
},
},
});
@@ -520,7 +528,8 @@ export const NavigationMenuDemo = ({ activeNav, collapse }) => {
}}
className="normal-case"
>
<ContentList layout="two">
<ContentList layout="three">
{/* <ContentListItemCallout /> */}
<ContentListItem title={submenu2Title1} href="/toolbox">
{submenu2Desc1}
</ContentListItem>
@@ -610,6 +619,9 @@ export const NavigationMenuDemo = ({ activeNav, collapse }) => {
<ContentListItem title={"Sponsor"} href="/sponsor">
Promote your product.
</ContentListItem>
<ContentListItem title={"Typr Editor"} href="/typr">
Explore our open-source writing tool.
</ContentListItem>
</ContentList>
</NavigationMenuContent>
</NavigationMenuItem>
2 changes: 1 addition & 1 deletion components/Navbar/parts/MenuItemsApp.js
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu";
import { CaretDownIcon } from "@radix-ui/react-icons";
import { indigo, mauve, green, teal } from "@radix-ui/colors";
import Link from "next/link";
import { useRouter } from "next/navigation";
// import { useRouter } from "next/navigation";
import { usePathname } from "next/navigation";

import { FormattedMessage, useIntl } from "react-intl";
73 changes: 73 additions & 0 deletions components/ko-fi-button/KoFiDialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React, { useState } from "react";
import * as Dialog from "@radix-ui/react-dialog";
import { Cross2Icon } from "@radix-ui/react-icons";

const KoFiDialog = ({ color, id, label }) => {
const [isOpen, setIsOpen] = useState(false);

return (
<Dialog.Root open={isOpen} onOpenChange={setIsOpen}>
<Dialog.Trigger asChild>
<div
title={label}
className="kofi-button w-fit text-sm hover:shadow-md transition transition-all duration-400 cursor-pointer rounded-lg px-3 h-[28px] flex flex-col justify-center"
style={{ backgroundColor: color }}
onClick={() => setIsOpen(!isOpen)}
>
<div className="kofitext gap-1 flex my-auto items-center">
<img
src="https://ko-fi.com/img/cup-border.png"
className="kofiimg"
alt="Ko-Fi button"
/>
<div className="flex my-auto text-xs items-center text-white">
{label}
</div>
</div>
</div>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 bg-black bg-opacity-50 z-[9999]" />
<Dialog.Content
forceMount={true}
className="fixed w-[400px] h-[712px] overflow-hidden top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-white p-0 rounded-xl shadow-lg z-[9999]"
>
<div className="relative w-full h-full">
<div className="flex flex-col h-full z-10 justify-center items-center kofi-button">
<div className="gap-1 flex my-auto items-center">
<div className="flex flex-col gap-2">
<img
src="https://ko-fi.com/img/cup-border.png"
className="kofiimg !h-[auto] !w-[27px] mx-auto animate-[kofi-wiggle_3s_infinite]"
alt="Ko-Fi button"
/>
<div className="mx-auto text-sm text-gray-600">Loading...</div>
</div>
</div>
</div>
<div className="z-20 absolute top-0 left-0 w-full h-full">
<iframe
id="kofiframe"
src="https://ko-fi.com/prototypr/?hidefeed=true&widget=true&embed=true&preview=true"
style={{
border: "none",
width: "100%",
background: "transparent",
}}
height="712"
title="prototypr"
/>
</div>
</div>
<Dialog.Close asChild>
<button className="absolute z-30 top-2 bg-gray-200 right-2 p-1 rounded-full hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200">
<Cross2Icon className="w-4 h-4 text-gray-500" />
</button>
</Dialog.Close>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
};

export default KoFiDialog;
103 changes: 103 additions & 0 deletions components/new-index/layoutForDemo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
"use client";
import { NAV_OFFSET } from "@/lib/constants";
// import Meta from "../meta";
import Navbar from "@/components/Navbar/NavbarApp";
// import { PlausibleProvider } from "next-plausible";
import { IntlProvider } from "react-intl";
// import { useRouter } from "next/navigation";
import { useMemo } from "react";
import { Inter } from "next/font/google";
import { getCssText } from "../../stitches.config";
import Head from "next/head";
import "../../styles/index.scss";

import EN from "locales/en-US";
import ES from "locales/es-ES";
const font = Inter({
// weight: '400',
weight: ["300", "400", "500", "600", "700", "800", "900"],
subsets: ["latin"],
display: "swap",
});

export default function Layout({
preview,
children,
sponsor,
activeNav,
background,
padding,
seo,
navType,
navOffset,
navBackground,
sessionUser
}) {
// const { locale } = useRouter();
const [shortLocale] = ["en"];

const messages = useMemo(() => {
switch (shortLocale) {
case "es":
return ES;
case "en":
return EN;
default:
return EN;
}
}, [shortLocale]);

// console.log(getCssText())
return (
<div className={`${font.className}`}>
{/* <Head> */}

<Head>
<style
id="stitches"
dangerouslySetInnerHTML={{ __html:getCssText() }}
/>
</Head>
<link rel="apple-touch-icon" sizes="180x180" href="/favicon/apple-touch-icon.png"/>
<link rel="icon" type="image/png" sizes="32x32" href="/favicon/favicon-32x32.png"/>
<link rel="icon" type="image/png" sizes="16x16" href="/favicon/favicon-16x16.png"/>
<link rel="manifest" href="/favicon/site.webmanifest"/>
<meta name="msapplication-TileColor" content="#da532c"/>
<meta name="theme-color" content="#ffffff"/>
{/* </Head> */}

{/* <PlausibleProvider customDomain="https://analytics.prototypr.io" selfHosted={true} domain="4.prototypr.io"> */}
<IntlProvider
key={"en-US"}
defaultLocale="en-US"
locale={"en-US"}
messages={messages}
>
{/* <Navbar activeNav={activeNav} /> */}
{/* <div className="fixed w-full z-50"> */}
<Navbar
sessionUser={sessionUser}
background={navBackground}
navType={'full'}
sponsor={sponsor}
maxWidth={"calc(100vw-24px)"}
/>
{/* </div> */}

<div
className={`min-h-screen overflow-hidden`}
style={{ background: background ? background : "#fbfcff" }}
>
<main
className={`mx-auto`}
// style={{ maxWidth: padding == false ? "" : "1200px" }}
>
{children}
</main>
</div>
{/* <Footer /> */}
</IntlProvider>
{/* </PlausibleProvider> */}
</div>
);
}
22 changes: 22 additions & 0 deletions components/typr-demo/CustomPostStatusesCheckbox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from "react";
import * as RadixCheckbox from "@radix-ui/react-checkbox";
import { CheckIcon } from "@radix-ui/react-icons";

const CustomPostStatusesCheckbox = ({ customPostStatuses, onCustomPostStatusesChange }) => (
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700 mb-2">
Custom Post Statuses:
</label>
<RadixCheckbox.Root
checked={customPostStatuses}
onCheckedChange={onCustomPostStatusesChange}
className="w-6 h-6 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500"
>
<RadixCheckbox.Indicator>
<CheckIcon className="w-4 h-4 text-indigo-600" />
</RadixCheckbox.Indicator>
</RadixCheckbox.Root>
</div>
);

export default CustomPostStatusesCheckbox;
56 changes: 56 additions & 0 deletions components/typr-demo/DemoCodeDialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React, { useState } from "react";
import * as Dialog from "@radix-ui/react-dialog";

const DemoCodeDialog = ({ isDialogOpen, setIsDialogOpen, demoCode, theme }) => {
const [copyButtonText, setCopyButtonText] = useState("Copy");

const copyToClipboard = () => {
navigator.clipboard.writeText(demoCode).then(() => {
setCopyButtonText("Copied!");
setTimeout(() => {
setCopyButtonText("Copy");
}, 2000);
});
};

return (
<Dialog.Root open={isDialogOpen} onOpenChange={setIsDialogOpen}>
<Dialog.Trigger asChild>
<button className="hidden">Open Dialog</button>
</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 bg-black bg-opacity-50 z-[98]" />
<Dialog.Content className="fixed m-6 top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-white p-6 rounded-md shadow-lg max-w-[680px] w-full z-[9999]">
<Dialog.Title className="text-lg font-semibold mb-4">
Demo Code
</Dialog.Title>
<Dialog.Description className="mb-4">
Here is the demo code with the current props:
</Dialog.Description>
<pre className="bg-gray-100 p-4 h-[500px] max-h-[90%] text-gray-600 rounded-md overflow-auto">
<code>{demoCode}</code>
</pre>
<div className="mt-4 flex justify-end">
<button
onClick={copyToClipboard}
className={`py-2 px-4 ${
theme === "blue" ? "bg-blue-600" : "bg-gray-600"
} text-white rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 ${
theme === "blue" ? "focus:ring-blue-500" : "focus:ring-gray-500"
} mr-2`}
>
{copyButtonText}
</button>
<Dialog.Close asChild>
<button className="py-2 px-4 bg-transparent border border-gray-300 text-gray-700 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
Close
</button>
</Dialog.Close>
</div>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);
};

export default DemoCodeDialog;
23 changes: 23 additions & 0 deletions components/typr-demo/EnablePublishingFLowCheckbox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from "react";
import * as RadixSwitch from "@radix-ui/react-switch";

const EnablePublishingFlowCheckbox = ({ enablePublishingFlow, onEnablePublishingFlowChange }) => (
<div className="flex items-center justify-between w-full">
<label className="text-sm font-medium text-gray-700">Enable Publishing Flow</label>
<RadixSwitch.Root
checked={enablePublishingFlow}
onCheckedChange={onEnablePublishingFlowChange}
className={`w-10 h-5 rounded-full relative shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 ${
enablePublishingFlow ? "bg-blue-500/90" : "bg-gray-300"
}`}
>
<RadixSwitch.Thumb
className={`block w-4 h-4 bg-white rounded-full shadow-md transform transition-transform ${
enablePublishingFlow ? "translate-x-5" : "translate-x-1"
}`}
/>
</RadixSwitch.Root>
</div>
);

export default EnablePublishingFlowCheckbox;
163 changes: 163 additions & 0 deletions components/typr-demo/GeneralSettingsPanel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import React, { useState } from "react";
import * as Select from "@radix-ui/react-select";
import { TrashIcon, PlusIcon } from "@radix-ui/react-icons";

const GeneralPanel = ({ generalMenu, onValueChange, theme }) => {
const [entries, setEntries] = useState(generalMenu);

const handleAddEntry = () => {
const newEntries = [
...entries,
{ type: "", field: "", label: "", initialValue: "" },
];
setEntries(newEntries);
onValueChange(newEntries);
};

const handleChange = (index, field, value) => {
const newEntries = entries.map((entry, i) =>
i === index ? { ...entry, [field]: value } : entry
);
setEntries(newEntries);
onValueChange(newEntries);
};

const handleRemoveEntry = (index) => {
if (window.confirm("Are you sure you want to remove this field?")) {
const newEntries = entries.filter((_, i) => i !== index);
setEntries(newEntries);
onValueChange(newEntries);
}
};

return (
<div className="relative flex flex-col gap-3">
{entries?.length ?
entries.map((entry, index) => (
<div key={index} className="rounded-lg border border-gray-200 rounded-lg p-3">
<div className="flex justify-between items-center mb-2">
<h3 className="block text-sm my-auto font-semibold text-gray-700 ">
Field {index + 1}
{/* {entry.label || "New Entry"} */}
</h3>
<button
onClick={() => handleRemoveEntry(index)}
className="text-red-600 hover:text-red-700 focus:outline-none my-auto"
>
<TrashIcon className="h-4 w-4" />
</button>
</div>
<div className="flex flex-col gap-2">
<div className="flex justify-between items-center">
<label className="block text-sm font-medium text-gray-700">Type</label>
<Select.Root
value={entry.type}
onValueChange={value => {
handleChange(index, "type", value);
}}
>
<Select.Trigger className="w-full max-w-[140px] h-[32px] px-2 text-sm border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 flex items-center">
<Select.Value placeholder="Choose type" />
</Select.Trigger>
<Select.Portal avoidCollisions={false}>
<Select.Content className="bg-white border border-gray-300 rounded-md shadow-sm">
<Select.Viewport>
<Select.Item
value="text"
className="h-[32px] text-xs px-2 cursor-pointer hover:bg-gray-100 focus:bg-gray-200 flex flex-col justify-center"
>
<Select.ItemText>text</Select.ItemText>
</Select.Item>
<Select.Item
value="description"
className="h-[32px] text-xs px-2 cursor-pointer hover:bg-gray-100 focus:bg-gray-200 flex flex-col justify-center"
>
<Select.ItemText>description</Select.ItemText>
</Select.Item>
<Select.Item
value="number"
className="h-[32px] text-xs px-2 cursor-pointer hover:bg-gray-100 focus:bg-gray-200 flex flex-col justify-center"
>
<Select.ItemText>number</Select.ItemText>
</Select.Item>
<Select.Item
value="date"
className="h-[32px] text-xs px-2 cursor-pointer hover:bg-gray-100 focus:bg-gray-200 flex flex-col justify-center"
>
<Select.ItemText>date</Select.ItemText>
</Select.Item>
<Select.Item
value="select"
className="h-[32px] text-xs px-2 cursor-pointer hover:bg-gray-100 focus:bg-gray-200 flex flex-col justify-center"
>
<Select.ItemText>select</Select.ItemText>
</Select.Item>
<Select.Item
value="url"
className="h-[32px] text-xs px-2 cursor-pointer hover:bg-gray-100 focus:bg-gray-200 flex flex-col justify-center"
>
<Select.ItemText>url</Select.ItemText>
</Select.Item>
</Select.Viewport>
</Select.Content>
</Select.Portal>
</Select.Root>
</div>
<div className="flex justify-between items-center">
<label className="block text-sm font-medium text-gray-700">Field</label>
<input
type="text"
placeholder="Field"
value={entry.field}
onChange={(e) => handleChange(index, "field", e.target.value)}
className="w-full max-w-[140px] h-[32px] px-2 border border-gray-300 text-sm bg-white rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 truncate"
/>
</div>
<div className="flex justify-between items-center">
<label className="block text-sm font-medium text-gray-700">Label</label>
<input
type="text"
placeholder="Label"
value={entry.label}
onChange={(e) => handleChange(index, "label", e.target.value)}
className="w-full max-w-[140px] h-[32px] px-2 border border-gray-300 text-sm bg-white rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 truncate"
/>
</div>
{/* <div className="flex justify-between items-center">
<label className="block text-sm font-medium text-gray-700">Initial Value</label>
<input
type="text"
placeholder="Initial Value"
value={entry.initialValue}
onChange={(e) => handleChange(index, "initialValue", e.target.value)}
className="w-full max-w-[140px] h-[32px] px-2 border border-gray-300 text-sm bg-white rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 truncate"
/>
</div> */}
<div className="flex justify-between items-center">
<label className="block text-sm font-medium text-gray-700">Admin Only</label>
<input
type="checkbox"
checked={entry.adminOnly}
onChange={(e) => handleChange(index, "adminOnly", e.target.checked)}
className="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded"
/>
</div>
</div>
</div>
)):null}
<button
onClick={handleAddEntry}
className={`h-[26px] px-3 w-fit text-xs font-medium ${
theme === 'blue' ? 'bg-blue-600 hover:bg-blue-700 focus:ring-blue-500' :
theme === 'gray' ? 'bg-gray-600 hover:bg-gray-700 focus:ring-gray-500' :
'bg-indigo-600 hover:bg-indigo-700 focus:ring-indigo-500'
} text-white rounded-full shadow-sm focus:outline-none focus:ring-2 flex items-center`}
>
<PlusIcon className="h-4 w-4 mr-1" />
Add field
</button>
</div>
);
};

export default GeneralPanel;
100 changes: 100 additions & 0 deletions components/typr-demo/IndexedDBBrowser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import React, { useState, useEffect } from "react";
import { openDB } from "idb";
import { DB_NAME, STORE_NAME } from "@/lib/typr-demo/indexedDB"; // Import the IndexedDB utility
import { TrashIcon } from "@radix-ui/react-icons"; // Import the Radix TrashIcon
import { PlusIcon } from "@radix-ui/react-icons"; // Import the Radix PlusIcon

const IndexedDBBrowser = ({ data, onDelete, router, searchParams }) => {
const deleteRow = async id => {
// const db = await openDB(DB_NAME, 1);
const db = await openDB(DB_NAME);
const tx = db.transaction(STORE_NAME, "readwrite");
const store = tx.objectStore(STORE_NAME);
await store.delete(id);
tx.done;
onDelete(); // Fetch data again after deletion
};

const handleCreateNew = () => {
router.push("/typr");
};

return (
<div id="dbbrowser" className="p-4 pt-2 pb-[150px] overflow-y-auto">
<div className="space-y-4">
{data.length === 0 ? (
<p className="text-gray-500 text-center mt-2">No posts found.</p>
) : (
<>
{data.map(entry => (
<Card key={entry.id} entry={entry} router={router} searchParams={searchParams} onDelete={deleteRow} />
))}
<div
className="bg-gradient-to-b group from-slate-100 transition transition-all duration-600 to-slate-50 hover:shadow border border-dotted border-gray-300/70 rounded-lg p-2 mb-4 relative group cursor-pointer flex items-center justify-center"
onClick={handleCreateNew}
>
<PlusIcon className="text-gray-500" />
<span className="ml-2 text-sm group-hover:text-gray-800 text-gray-500">Start New</span>
</div>
</>
)}
</div>
</div>
);
};

export default IndexedDBBrowser;

const Card = ({ entry, onDelete, router, searchParams }) => {
const { id, versioned_title, title, versioned_content, content } = entry;
const [displayTitle, setDisplayTitle] = useState(versioned_title || title);
const [displayContent, setDisplayContent] = useState(
versioned_content || content
);

useEffect(() => {
setDisplayTitle(versioned_title || title);
setDisplayContent(versioned_content || content);
}, [versioned_title, title, versioned_content, content]);

const handleClick = () => {
const params = new URLSearchParams(searchParams);
params.set("id", id);
router.push(`?${params.toString()}`, undefined, {
shallow: false,
scroll: false,
});
};

const currentId = new URLSearchParams(searchParams).get("id");
const isActive = currentId === id.toString();

return (
<div
className={`bg-gradient-to-b from-slate-100 transition transition-all duration-600 to-slate-50 hover:shadow border border-gray-300/70 rounded-lg p-2 mb-4 relative group hover:from-white hover:to-white cursor-pointer ${isActive ? 'border-gray-500 from-white to-white' : ''}`}
onClick={handleClick}
>
<div className="flex justify-between items-start">
<h2 className="text-base p-1 font-bold text-wrap line-clamp-2 truncate">
{displayTitle}
</h2>
<button
onClick={e => {
e.stopPropagation();
onDelete(id);
}}
className="text-gray-500 h-6 w-6 hover:bg-slate-200 rounded-lg hover:text-red-500 ml-2 hidden group-hover:flex items-center justify-center"
>
<TrashIcon />
</button>
</div>
<div className="p-1">
<div
className="text-gray-700 mb-2 truncate line-clamp-2 truncate text-wrap text-xs"
dangerouslySetInnerHTML={{ __html: `${displayContent}` }}
></div>
<p className="text-gray-500 text-[11px] mt-3">#{id}</p>
</div>
</div>
);
};
240 changes: 240 additions & 0 deletions components/typr-demo/NavSettings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
import React from "react";
import * as Select from "@radix-ui/react-select";
import * as RadixSwitch from "@radix-ui/react-switch";

const NavSettings = ({ nav, onNavChange, avatarOptions }) => (
<div className="mb-4 flex flex-col gap-4">
<div className="flex items-center justify-between w-full">
<label className="text-sm font-medium text-gray-700 my-auto">
Show Nav
</label>
<RadixSwitch.Root
checked={nav.show}
onCheckedChange={checked => onNavChange({ ...nav, show: checked })}
className={`w-10 h-5 rounded-full relative shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 ${
nav.show ? "bg-blue-500/90" : "bg-gray-300"
}`}
>
<RadixSwitch.Thumb
className={`block w-4 h-4 bg-white rounded-full shadow-md transform transition-transform ${
nav.show ? "translate-x-5" : "translate-x-1"
}`}
/>
</RadixSwitch.Root>
</div>

<div className="flex flex-row justify-between">
<label className="block text-sm font-medium text-gray-700 my-auto">
Avatar Placeholder
</label>
<Select.Root
value={nav.userBadge.avatarPlaceholder}
onValueChange={value =>
onNavChange({
...nav,
userBadge: { ...nav.userBadge, avatarPlaceholder: value },
})
}
>
<Select.Trigger className="max-w-[110px] truncate text-nowrap overflow-hidden h-[32px] px-2 pl-1 text-sm border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 flex items-center">
<img
src={nav.userBadge.avatarPlaceholder}
alt="Avatar"
className="w-6 h-6 border border-gray-300 flex-none rounded-full object-contain mr-1.5"
/>
<Select.Value placeholder="Select an avatar" />
</Select.Trigger>
<Select.Portal>
<Select.Content className="bg-white border border-gray-300 rounded-md shadow-sm">
<Select.Viewport>
{avatarOptions.map(({ name, imgSrc }) => (
<Select.Item
key={name}
value={imgSrc}
className="h-[32px] px-2 text-sm hover:bg-gray-100 cursor-pointer flex items-center"
>
<img
src={imgSrc}
alt="Avatar"
className="w-6 h-6 border border-gray-300 rounded-full mr-2 object-contain"
/>
<Select.ItemText className="truncate text-nowrap overflow-hidden">
{name ? name : "Avatartion"}
</Select.ItemText>
</Select.Item>
))}
</Select.Viewport>
</Select.Content>
</Select.Portal>
</Select.Root>
</div>

<div className="flex flex-row justify-between">
<label className="block my-auto text-sm font-medium text-gray-700 mb-2">
Nav Position
</label>
<Select.Root
value={nav.position}
onValueChange={value => onNavChange({ ...nav, position: value })}
>
<Select.Trigger className="w-full max-w-[110px] truncate text-nowrap overflow-hidden h-[32px] px-2 text-sm border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 flex items-center">
<Select.Value />
</Select.Trigger>
<Select.Portal>
<Select.Content className="bg-white border border-gray-300 rounded-md shadow-sm">
<Select.Viewport>
<Select.Item
value="sticky"
className="h-[32px] px-2 text-sm hover:bg-gray-100 cursor-pointer flex items-center"
>
<Select.ItemText>Sticky</Select.ItemText>
</Select.Item>
<Select.Item
value="relative"
className="h-[32px] px-2 text-sm hover:bg-gray-100 cursor-pointer flex items-center"
>
<Select.ItemText>Relative</Select.ItemText>
</Select.Item>
<Select.Item
value="fixed"
className="h-[32px] px-2 text-sm hover:bg-gray-100 cursor-pointer flex items-center"
>
<Select.ItemText>Fixed</Select.ItemText>
</Select.Item>
</Select.Viewport>
</Select.Content>
</Select.Portal>
</Select.Root>
</div>

<div className="flex flex-row justify-between">
<label className="block my-auto text-sm font-medium text-gray-700 mb-2">
Logo URL
</label>
<div className="relative">
<input
type="text"
value={nav.logo.url}
onChange={e =>
onNavChange({ ...nav, logo: { image: e.target.value, ...nav.url } })
}
className="w-full pl-[26px] pr-2 max-w-[110px] truncate text-nowrap overflow-hidden h-[32px] text-sm border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500"
/>
{nav.logo.url?.length > 2 ? (
<img
src={nav.logo.url}
alt="Logo"
className="absolute left-1 top-1/2 transform -translate-y-1/2 w-5 h-5 border border-gray-300 rounded-sm object-contain"
/>
) : (
<svg
className={"absolute left-1 top-1/2 transform -translate-y-1/2 w-5 h-5"}
width="164"
height="164"
viewBox="0 0 164 164"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clip-path="url(#clip0_1_12)">
<rect width="164" height="164" fill="white" />
<rect
x="6"
y="6"
width="152"
height="152"
rx="6"
stroke="black"
stroke-width="12"
/>
<path
d="M111.138 45.058L89.022 45.446V121.688L104.736 124.986C104.736 128.995 103.637 131 101.438 131L82.62 129.836L63.414 131C60.9567 131 59.728 128.995 59.728 124.986L75.442 121.688V45.446L53.52 45.058L51.58 58.638C51.58 60.578 48.9933 61.548 43.82 61.548L41.88 38.656C42.1387 36.9747 44.0787 36.134 47.7 36.134L82.62 36.91L116.958 36.134C120.579 36.134 122.519 36.9747 122.778 38.656L120.838 61.548C115.665 61.548 113.078 60.578 113.078 58.638L111.138 45.058Z"
fill="black"
/>
</g>
<defs>
<clipPath id="clip0_1_12">
<rect width="164" height="164" fill="white" />
</clipPath>
</defs>
</svg>
)}
</div>
</div>
<div className="flex items-center justify-between w-full">
<label className="text-sm my-auto font-medium text-gray-700">
Undo/Redo Buttons
</label>
<RadixSwitch.Root
checked={nav.undoRedoButtons.show}
onCheckedChange={checked =>
onNavChange({
...nav,
undoRedoButtons: { ...nav.undoRedoButtons, show: checked },
})
}
className={`w-10 h-5 rounded-full relative shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 ${
nav.undoRedoButtons.show ? "bg-blue-500/90" : "bg-gray-300"
}`}
>
<RadixSwitch.Thumb
className={`block w-4 h-4 bg-white rounded-full shadow-md transform transition-transform ${
nav.undoRedoButtons.show ? "translate-x-5" : "translate-x-1"
}`}
/>
</RadixSwitch.Root>
</div>

<div className="flex items-center justify-between w-full">
<label className="text-sm my-auto font-medium text-gray-700">
Publish status
</label>
<RadixSwitch.Root
checked={nav.postStatus.show}
onCheckedChange={checked =>
onNavChange({
...nav,
postStatus: {
...nav.postStatus,
show: checked,
},
})
}
className={`w-10 h-5 rounded-full relative shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 ${
nav.postStatus.show ? "bg-blue-500/90" : "bg-gray-300"
}`}
>
<RadixSwitch.Thumb
className={`block w-4 h-4 bg-white rounded-full shadow-md transform transition-transform ${
nav.postStatus.show ? "translate-x-5" : "translate-x-1"
}`}
/>
</RadixSwitch.Root>
</div>

<div className="flex items-center justify-between w-full">
<label className="text-sm my-auto font-medium text-gray-700">
User Badge
</label>
<RadixSwitch.Root
checked={nav.userBadge.show}
onCheckedChange={checked =>
onNavChange({
...nav,
userBadge: { ...nav.userBadge, show: checked },
})
}
className={`w-10 h-5 rounded-full relative shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 ${
nav.userBadge.show ? "bg-blue-500/90" : "bg-gray-300"
}`}
>
<RadixSwitch.Thumb
className={`block w-4 h-4 bg-white rounded-full shadow-md transform transition-transform ${
nav.userBadge.show ? "translate-x-5" : "translate-x-1"
}`}
/>
</RadixSwitch.Root>
</div>
</div>
);

export default NavSettings;
23 changes: 23 additions & 0 deletions components/typr-demo/RequireLoginCheckbox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from "react";
import * as RadixSwitch from "@radix-ui/react-switch";

const RequireLoginCheckbox = ({ requireLogin, onRequireLoginChange }) => (
<div className="flex items-center justify-between w-full">
<label className="text-sm font-medium text-gray-700">Require Login</label>
<RadixSwitch.Root
checked={requireLogin}
onCheckedChange={onRequireLoginChange}
className={`w-10 h-5 rounded-full relative shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 ${
requireLogin ? "bg-blue-500/90" : "bg-gray-300"
}`}
>
<RadixSwitch.Thumb
className={`block w-4 h-4 bg-white rounded-full shadow-md transform transition-transform ${
requireLogin ? "translate-x-5" : "translate-x-1"
}`}
/>
</RadixSwitch.Root>
</div>
);

export default RequireLoginCheckbox;
160 changes: 160 additions & 0 deletions components/typr-demo/SeoPanel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import React, { useState } from "react";
import * as Select from "@radix-ui/react-select";
import { TrashIcon, PlusIcon } from "@radix-ui/react-icons";

const SeoPanel = ({ seoMenu, onValueChange, theme }) => {
const [entries, setEntries] = useState(seoMenu);

const handleAddEntry = () => {
const newEntries = [
...entries,
{ type: "", field: "", label: "", initialValue: "" },
];
setEntries(newEntries);
onValueChange(newEntries);
};

const handleChange = (index, field, value) => {
const newEntries = entries.map((entry, i) =>
i === index ? { ...entry, [field]: value } : entry
);
setEntries(newEntries);
onValueChange(newEntries);
};

const handleRemoveEntry = index => {
if (window.confirm("Are you sure you want to remove this field?")) {
const newEntries = entries.filter((_, i) => i !== index);
setEntries(newEntries);
onValueChange(newEntries);
}
};

return (
<div className="relative flex flex-col gap-3">
{entries?.length
? entries.map((entry, index) => (
<div
key={index}
className="rounded-lg border border-gray-200 rounded-lg p-3"
>
<div className="flex justify-between items-center mb-2">
<h3 className="block text-sm my-auto font-semibold text-gray-700 ">
Field {index + 1}
{/* {entry.label || "New Entry"} */}
</h3>
<button
onClick={() => handleRemoveEntry(index)}
className="text-red-600 hover:text-red-700 focus:outline-none my-auto"
>
<TrashIcon className="h-4 w-4" />
</button>
</div>
<div className="flex flex-col gap-2">
<div className="flex justify-between items-center">
<label className="block text-sm font-medium text-gray-700">Type</label>
<Select.Root
value={entry.type}
onValueChange={value => {
handleChange(index, "type", value);
}}
>
<Select.Trigger className="w-full max-w-[140px] h-[32px] px-2 text-sm border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 flex items-center">
<Select.Value placeholder="Choose type" />
</Select.Trigger>
<Select.Portal avoidCollisions={false}>
<Select.Content className="bg-white border border-gray-300 rounded-md shadow-sm">
<Select.Viewport>
<Select.Item
value="text"
className="h-[32px] text-xs px-2 cursor-pointer hover:bg-gray-100 focus:bg-gray-200 flex flex-col justify-center"
>
<Select.ItemText>text</Select.ItemText>
</Select.Item>
<Select.Item
value="description"
className="h-[32px] text-xs px-2 cursor-pointer hover:bg-gray-100 focus:bg-gray-200 flex flex-col justify-center"
>
<Select.ItemText>description</Select.ItemText>
</Select.Item>
<Select.Item
value="number"
className="h-[32px] text-xs px-2 cursor-pointer hover:bg-gray-100 focus:bg-gray-200 flex flex-col justify-center"
>
<Select.ItemText>number</Select.ItemText>
</Select.Item>
<Select.Item
value="date"
className="h-[32px] text-xs px-2 cursor-pointer hover:bg-gray-100 focus:bg-gray-200 flex flex-col justify-center"
>
<Select.ItemText>date</Select.ItemText>
</Select.Item>
<Select.Item
value="select"
className="h-[32px] text-xs px-2 cursor-pointer hover:bg-gray-100 focus:bg-gray-200 flex flex-col justify-center"
>
<Select.ItemText>select</Select.ItemText>
</Select.Item>
<Select.Item
value="url"
className="h-[32px] text-xs px-2 cursor-pointer hover:bg-gray-100 focus:bg-gray-200 flex flex-col justify-center"
>
<Select.ItemText>url</Select.ItemText>
</Select.Item>
</Select.Viewport>
</Select.Content>
</Select.Portal>
</Select.Root>
</div>
<div className="flex justify-between items-center">
<label className="block text-sm font-medium text-gray-700">Field</label>
<input
type="text"
placeholder="Field"
value={entry.field}
onChange={e => handleChange(index, "field", e.target.value)}
className="w-full max-w-[140px] h-[32px] px-2 border border-gray-300 text-sm bg-white rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 truncate"
/>
</div>
<div className="flex justify-between items-center">
<label className="block text-sm font-medium text-gray-700">Label</label>
<input
type="text"
placeholder="Label"
value={entry.label}
onChange={e => handleChange(index, "label", e.target.value)}
className="w-full max-w-[140px] h-[32px] px-2 border border-gray-300 text-sm bg-white rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 truncate"
/>
</div>
{/* <div className="flex justify-between items-center">
<label className="block text-sm font-medium text-gray-700">Initial Value</label>
<input
type="text"
placeholder="Initial Value"
value={entry.initialValue}
onChange={e => handleChange(index, "initialValue", e.target.value)}
className="w-full max-w-[140px] h-[32px] px-2 border border-gray-300 text-sm bg-white rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 truncate"
/>
</div> */}
</div>
</div>
))
: null}
<button
onClick={handleAddEntry}
className={`h-[26px] px-3 w-fit text-xs font-medium ${
theme === "blue"
? "bg-blue-600 hover:bg-blue-700 focus:ring-blue-500"
: theme === "gray"
? "bg-gray-600 hover:bg-gray-700 focus:ring-gray-500"
: "bg-indigo-600 hover:bg-indigo-700 focus:ring-indigo-500"
} text-white rounded-full shadow-sm focus:outline-none focus:ring-2 flex items-center`}
>
<PlusIcon className="h-4 w-4 mr-1" />
Add field
</button>
</div>
);
};

export default SeoPanel;
67 changes: 67 additions & 0 deletions components/typr-demo/SimulatorDialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React from "react";
import * as Dialog from "@radix-ui/react-dialog";
import * as RadixSwitch from "@radix-ui/react-switch";

const SimulatorDialog = ({ isOpen, onOpenChange, simulatorSettings, onSimulatorChange }) => (
<Dialog.Root open={isOpen} onOpenChange={onOpenChange}>
<Dialog.Portal>
<Dialog.Overlay className="fixed inset-0 bg-black bg-opacity-30 z-[999]" />
<Dialog.Content className="z-[9999] fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 bg-white p-6 rounded-lg shadow-lg">
<Dialog.Title className="text-lg font-medium">Simulator Settings</Dialog.Title>
<div className="mt-4 flex flex-col gap-4">
<div className="flex items-center justify-between w-full">
<label className="text-sm font-medium text-gray-700">Simulate onSave</label>
<RadixSwitch.Root
checked={simulatorSettings.onSave}
onCheckedChange={checked => onSimulatorChange({ ...simulatorSettings, onSave: checked })}
className={`w-10 h-5 rounded-full relative shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 ${
simulatorSettings.onSave ? "bg-blue-500/90" : "bg-gray-300"
}`}
>
<RadixSwitch.Thumb
className={`block w-4 h-4 bg-white rounded-full shadow-md transform transition-transform ${
simulatorSettings.onSave ? "translate-x-5" : "translate-x-1"
}`}
/>
</RadixSwitch.Root>
</div>
<div className="flex items-center justify-between w-full">
<label className="text-sm font-medium text-gray-700">Simulate onCreate</label>
<RadixSwitch.Root
checked={simulatorSettings.onCreate}
onCheckedChange={checked => onSimulatorChange({ ...simulatorSettings, onCreate: checked })}
className={`w-10 h-5 rounded-full relative shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 ${
simulatorSettings.onCreate ? "bg-blue-500/90" : "bg-gray-300"
}`}
>
<RadixSwitch.Thumb
className={`block w-4 h-4 bg-white rounded-full shadow-md transform transition-transform ${
simulatorSettings.onCreate ? "translate-x-5" : "translate-x-1"
}`}
/>
</RadixSwitch.Root>
</div>
<div className="flex items-center justify-between w-full">
<label className="text-sm font-medium text-gray-700">Simulate onPublish</label>
<RadixSwitch.Root
checked={simulatorSettings.onPublish}
onCheckedChange={checked => onSimulatorChange({ ...simulatorSettings, onPublish: checked })}
className={`w-10 h-5 rounded-full relative shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 ${
simulatorSettings.onPublish ? "bg-blue-500/90" : "bg-gray-300"
}`}
>
<RadixSwitch.Thumb
className={`block w-4 h-4 bg-white rounded-full shadow-md transform transition-transform ${
simulatorSettings.onPublish ? "translate-x-5" : "translate-x-1"
}`}
/>
</RadixSwitch.Root>
</div>
</div>
<Dialog.Close className="absolute top-2 right-2">X</Dialog.Close>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
);

export default SimulatorDialog;
26 changes: 26 additions & 0 deletions components/typr-demo/ThemeSelector.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from "react";
import * as ToggleGroup from "@radix-ui/react-toggle-group";

const ThemeSelector = ({ theme, onThemeChange, themeOptions }) => (
<div className="flex flex-row justify-between">
<label className="block text-sm font-medium text-gray-700">
Theme
</label>
<ToggleGroup.Root
type="single"
value={theme}
onValueChange={onThemeChange}
className="flex space-x-2"
>
{themeOptions.map(option => (
<ToggleGroup.Item
key={option.value}
value={option.value}
className={`w-6 h-6 rounded-full ${option.value=='blue' ? 'bg-blue-600': option.value=='gray'?'bg-gray-400':''} ${theme === option.value ? 'border border-2 border-blue-400 shadow-lg' : ''}`}
/>
))}
</ToggleGroup.Root>
</div>
);

export default ThemeSelector;
81 changes: 81 additions & 0 deletions components/typr-demo/UserPopover.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import React, { useState, useEffect } from "react";
import * as Popover from "@radix-ui/react-popover";
import * as RadixSwitch from "@radix-ui/react-switch";

export default function UserPopover({ editorProps, handleUserChange }) {
const [open, setOpen] = useState(true);

const toggleOpen = isOpen => {
if (open !== isOpen) {
setOpen(isOpen);
}
};

return (
<Popover.Root open={open} onOpenChange={toggleOpen}>
<Popover.Trigger asChild>
<button className="absolute bottom-12 right-4 md:right-12 md:bottom-6 md:right-6 z-[999] shadow-lg rounded-full overflow-hidden">
<img
src={editorProps?.components?.nav?.userBadge?.avatarPlaceholder}
alt="User Avatar"
className="w-14 h-14 rounded-full bg-white object-contain border border-gray-300"
/>
</button>
</Popover.Trigger>
<Popover.Content
onFocusOutside={e => e.preventDefault()}
onInteractOutside={e => e.preventDefault()}
className="p-4 mb-2 mr-3 bg-white rounded shadow-md z-[999] min-w-[200px]"
>
<h3 className="mb-4 tracking-tight font-medium">User state</h3>
<div className="flex flex-col space-y-4">
<label className="flex items-center justify-between">
<div className="text-sm text-gray-600">Logged In</div>
<RadixSwitch.Root
checked={editorProps?.user?.isLoggedIn}
onCheckedChange={checked =>
handleUserChange("isLoggedIn", checked)
}
className={`w-10 h-5 rounded-full relative shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 ${
editorProps?.user?.isLoggedIn ? "bg-blue-500/90" : "bg-gray-300"
}`}
>
<RadixSwitch.Thumb
className={`block w-4 h-4 bg-white rounded-full shadow-md transform transition-transform ${
editorProps?.user?.isLoggedIn
? "translate-x-5"
: "translate-x-1"
}`}
/>
</RadixSwitch.Root>
</label>
<label className="flex items-center justify-between">
<div className="text-sm text-gray-600">Admin</div>
<RadixSwitch.Root
checked={editorProps?.user?.isAdmin}
onCheckedChange={checked => handleUserChange("isAdmin", checked)}
className={`w-10 h-5 rounded-full relative shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 ${
editorProps?.user?.isAdmin ? "bg-blue-500/90" : "bg-gray-300"
}`}
>
<RadixSwitch.Thumb
className={`block w-4 h-4 bg-white rounded-full shadow-md transform transition-transform ${
editorProps?.user?.isAdmin ? "translate-x-5" : "translate-x-1"
}`}
/>
</RadixSwitch.Root>
</label>
{/* <label>
Username:
<input
type="text"
value={editorProps.user.username || ""}
onChange={e => handleUserChange("username", e.target.value)}
className="w-full h-[32px] px-2 text-sm border border-gray-300 bg-white rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500"
/>
</label> */}
</div>
</Popover.Content>
</Popover.Root>
);
}
30 changes: 30 additions & 0 deletions lib/typr-demo/customDeepMerge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react'
// Custom deep merge function
export function customDeepMerge(target, source) {
if (typeof source !== "object" || source === null) {
return source;
}

const output = Array.isArray(target) ? [...target] : { ...target };

if (Array.isArray(target) && Array.isArray(source)) {
return [...source];
}

Object.keys(source).forEach(key => {
if (source[key] !== undefined) {
if (
typeof source[key] === "object" &&
!React.isValidElement(source[key]) &&
key in target &&
typeof target[key] === "object"
) {
output[key] = customDeepMerge(target[key], source[key]);
} else {
output[key] = source[key];
}
}
});

return output;
}
109 changes: 109 additions & 0 deletions lib/typr-demo/indexedDB.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
export const DB_NAME = 'DemoTyprDB';
// export const DB_VERSION = 1;
export const STORE_NAME = 'posts';

export const openDB = () => {
return new Promise((resolve, reject) => {
const request = indexedDB.open(DB_NAME);

request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains(STORE_NAME)) {
db.createObjectStore(STORE_NAME, { keyPath: 'id', autoIncrement: true });
}
};

request.onsuccess = (event) => {
const db = event.target.result;
// Check if the object store exists, if not, close and reopen with a new version
if (!db.objectStoreNames.contains(STORE_NAME)) {
const currentVersion = db.version;
db.close();
const reopenRequest = indexedDB.open(DB_NAME, currentVersion + 1);
reopenRequest.onupgradeneeded = (event) => {
const db = event.target.result;
db.createObjectStore(STORE_NAME, { keyPath: 'id', autoIncrement: true });
};
reopenRequest.onsuccess = (event) => {
resolve(event.target.result);
};
reopenRequest.onerror = (event) => {
reject(event.target.error);
};
} else {
resolve(db);
}
};

request.onerror = (event) => {
reject(event.target.error);
};
});
};

export const savePost = async (post, id) => {
const db = await openDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction([STORE_NAME], 'readwrite');
const store = transaction.objectStore(STORE_NAME);

// Add the id to the post object if provided
if (id !== undefined) {
post.id = id;
}

const request = store.put(post); // Use put instead of add

request.onsuccess = async () => {
try {
const savedPost = await loadPostById(request.result);
resolve(savedPost);
} catch (error) {
reject(error);
}
};

request.onerror = (event) => {
reject(event.target.error);
};
});
};

export const loadPosts = async () => {
const db = await openDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction([STORE_NAME], 'readonly');
const store = transaction.objectStore(STORE_NAME);
const request = store.getAll();

request.onsuccess = () => {
resolve(request.result);
};

request.onerror = (event) => {
reject(event.target.error);
};
});
};

export const createPost = async (post) => {
return savePost(post);
};

export const loadPostById = async (id) => {
const db = await openDB();
return new Promise((resolve, reject) => {
const transaction = db.transaction([STORE_NAME], 'readonly');
const store = transaction.objectStore(STORE_NAME);
const request = store.get(id);


request.onsuccess = () => {
resolve(request.result);
};

request.onerror = (event) => {
reject(event.target.error);
};
});
};
2,008 changes: 1,762 additions & 246 deletions package-lock.json

Large diffs are not rendered by default.

11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -19,11 +19,12 @@
"@next/bundle-analyzer": "^12.1.5",
"@phosphor-icons/react": "^2.0.15",
"@radix-ui/colors": "^0.1.8",
"@radix-ui/react-accordion": "^1.2.0",
"@radix-ui/react-checkbox": "^1.0.1",
"@radix-ui/react-collapsible": "^1.0.1",
"@radix-ui/react-dialog": "^1.0.2",
"@radix-ui/react-dropdown-menu": "2.0.1",
"@radix-ui/react-icons": "^1.1.1",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-navigation-menu": "^1.1.4",
"@radix-ui/react-popover": "^1.0.2",
"@radix-ui/react-portal": "^1.0.1",
@@ -35,7 +36,6 @@
"@radix-ui/react-tooltip": "^1.0.2",
"@stitches/react": "^1.2.8",
"@supabase/supabase-js": "^2.0.0",
"@tailwindcss/line-clamp": "^0.4.2",
"@vercel/og": "0.0.21",
"@yaireo/tagify": "^4.17.4",
"ai": "^3.2.38",
@@ -62,6 +62,7 @@
"formik": "^2.2.9",
"framer-motion": "^8.4.0",
"fs": "0.0.1-security",
"idb": "^8.0.0",
"image-validator": "^1.2.1",
"instantsearch.css": "^7.4.5",
"iron-session": "8.0.1",
@@ -134,14 +135,14 @@
"swiper": "^8.0.0",
"swr": "^1.3.0",
"tailwindcss-border-gradient-radius": "^3.0.1",
"tiptypr": "^0.0.78",
"tiptypr": "^0.0.79",
"tsparticles": "^2.7.1",
"uuidv4": "^6.2.13",
"yup": "^0.32.11",
"zod": "^3.23.8"
},
"optionalDependencies": {
"@prototypr/paper-interview": "^0.0.464",
"@prototypr/paper-interview": "^0.0.465",
"@prototypr/prototypr-postie": "^1.0.91"
},
"resolutions": {
@@ -162,7 +163,7 @@
"postcss": "^8.4.33",
"sass": "^1.70.0",
"sass-loader": "^13.2.0",
"tailwindcss": "^3.2.4",
"tailwindcss": "^3.4.1",
"tailwindcss-scoped-groups": "^2.0.0"
}
}
2,255 changes: 1,870 additions & 385 deletions public/sitemap-0.xml

Large diffs are not rendered by default.

Binary file added public/static/images/typr-og.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/static/images/typr.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion tailwind.config.js
Original file line number Diff line number Diff line change
@@ -6,6 +6,8 @@ module.exports = {
"./pages/**/*.{js,ts,jsx,tsx}",
// "./prototypr-packages/**/src/**/*.{js,ts,jsx,tsx}",
"./components/**/*.{js,ts,jsx,tsx}",
"./components/typr-demo/**/*.{js,ts,jsx,tsx}",
'./app/**/*.{js,jsx}',
"./lib/constants.js",
"./node_modules/@prototypr/paper-interview/dist/components/**/*.{js,ts,jsx,tsx}",
"./node_modules/tiptypr/dist/**/*.{js,ts,jsx,tsx}",
@@ -256,6 +258,5 @@ module.exports = {
plugins: [
require("@tailwindcss/forms"),
require("tailwindcss-border-gradient-radius"),
require("@tailwindcss/line-clamp"),
],
};

0 comments on commit a229018

Please sign in to comment.