fix: apply fixes & formatting from biomejs

This commit is contained in:
p-sw 2024-06-29 20:53:19 +09:00
parent f8562bf2bc
commit 417deb4aa6
77 changed files with 2327 additions and 2148 deletions

View File

@ -1 +1,19 @@
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

View File

@ -1,256 +1,290 @@
import {
Route,
createBrowserRouter,
createRoutesFromElements,
RouterProvider,
redirect,
} from "react-router-dom";
import MainLayout from "./MainLayout";
import Home from "./Home";
import DocsLayout from "./DocsLayout";
import ErrorBoundary from "./ErrorHandler";
import DynamicLayout from "./DynamicLayout";
import { Code } from "@/components/LoadedCode";
import DocsIntroduction, {
tableOfContents as docsIntroductionToc,
} from "./docs/introduction.mdx";
import DocsInstallation, {
tableOfContents as docsInstallationToc,
} from "./docs/installation.mdx";
import DocsConfiguration, {
tableOfContents as docsConfigurationToc,
} from "./docs/configuration.mdx";
import { HeadingContext } from "./HeadingContext";
import React, {
ForwardedRef,
forwardRef,
useContext,
useEffect,
useRef,
useState,
} from "react";
import { Tooltip, TooltipContent } from "@pswui/Tooltip";
function buildThresholdList() {
const thresholds: number[] = [];
const numSteps = 20;
for (let i = 1.0; i <= numSteps; i++) {
const ratio = i / numSteps;
thresholds.push(ratio);
}
thresholds.push(0);
return thresholds;
}
function HashedHeaders(Level: `h${1 | 2 | 3 | 4 | 5 | 6}`) {
return (prop: any, ref: ForwardedRef<HTMLHeadingElement>) => {
const internalRef = useRef<HTMLHeadingElement | null>(null);
const [_, setActiveHeadings] = useContext(HeadingContext);
const { children, ...restProp } = prop;
useEffect(() => {
const observer = new IntersectionObserver(
([{ target, intersectionRatio }]) => {
if (intersectionRatio > 0.5) {
setActiveHeadings((prev) => [...prev, target.id]);
} else {
setActiveHeadings((prev) => prev.filter((id) => id !== target.id));
}
},
{
root: null,
rootMargin: "0px",
threshold: buildThresholdList(),
},
);
if (internalRef.current) {
observer.observe(internalRef.current);
}
return () => {
observer.disconnect();
};
}, [internalRef.current]);
const [status, setStatus] = useState<"normal" | "error" | "success">(
"normal",
);
const messages = {
normal: "Click to copy link",
success: "Copied link!",
error: "Failed to copy..",
};
useEffect(() => {
if (status !== "normal") {
const timeout = setTimeout(() => {
setStatus("normal");
}, 3000);
return () => {
clearTimeout(timeout);
};
}
}, [status]);
return (
<Tooltip asChild position={"right"}>
<Level
ref={(el) => {
internalRef.current = el;
if (typeof ref === "function") {
ref(el);
} else if (el && ref) {
ref.current = el;
}
}}
className={`${prop.className} cursor-pointer select-none`}
onClick={async (e) => {
try {
await navigator.clipboard.writeText(
window.location.href.split("#")[0] + "#" + e.currentTarget.id,
);
setStatus("success");
} catch (e) {
setStatus("error");
}
}}
{...restProp}
>
{children}
<TooltipContent status={status} offset={"lg"} delay={"early"}>
<p className={"text-base font-normal whitespace-nowrap not-prose"}>
{messages[status]}
</p>
</TooltipContent>
</Level>
</Tooltip>
);
};
}
const overrideComponents = {
pre: (props: any) => {
const {
props: { children, className },
} = React.cloneElement(React.Children.only(props.children));
const language =
(!className || !className.includes("language-")
? "typescript"
: /language-([a-z]+)/.exec(className)![1]) ?? "typescript";
return <Code language={language}>{children as string}</Code>;
},
code: forwardRef<HTMLElement, any>((props: any, ref) => (
<code
ref={ref}
{...props}
className={`${props.className} rounded-md bg-neutral-800 text-orange-500 font-light p-1 before:content-none after:content-none`}
/>
)),
table: forwardRef<HTMLTableElement, any>((props: any, ref) => (
<div className="overflow-auto">
<table ref={ref} {...props} className={`${props.className}`} />
</div>
)),
h1: forwardRef<HTMLHeadingElement, any>(HashedHeaders("h1")),
h2: forwardRef<HTMLHeadingElement, any>(HashedHeaders("h2")),
h3: forwardRef<HTMLHeadingElement, any>(HashedHeaders("h3")),
h4: forwardRef<HTMLHeadingElement, any>(HashedHeaders("h4")),
h5: forwardRef<HTMLHeadingElement, any>(HashedHeaders("h5")),
h6: forwardRef<HTMLHeadingElement, any>(HashedHeaders("h6")),
};
const docsModules = import.meta.glob("./docs/components/*.mdx");
const routes = Object.keys(docsModules).map((path) => {
const sfPath = path.split("/").pop()?.replace(".mdx", "");
return (
<Route
key={path}
path={sfPath}
lazy={async () => {
const { default: C, tableOfContents } = await import(
`./docs/components/${sfPath}.mdx`
);
return {
Component: () => (
<DynamicLayout toc={tableOfContents}>
<C components={overrideComponents} />
</DynamicLayout>
),
};
}}
/>
);
});
const REDIRECTED_404 = /^\?(\/([a-zA-Z0-9\-_]+\/?)+)(&.*)*$/;
const router = createBrowserRouter(
createRoutesFromElements(
<Route path="/" element={<MainLayout />} errorElement={<ErrorBoundary />}>
<Route
index
loader={() =>
REDIRECTED_404.test(window.location.search)
? redirect(REDIRECTED_404.exec(window.location.search)?.[1] ?? "/")
: true
}
element={<Home />}
/>
<Route path="docs" element={<DocsLayout />}>
<Route index loader={() => redirect("/docs/introduction")} />
<Route
path="introduction"
element={
<DynamicLayout toc={docsIntroductionToc}>
<DocsIntroduction components={overrideComponents} />
</DynamicLayout>
}
/>
<Route
path="installation"
element={
<DynamicLayout toc={docsInstallationToc}>
<DocsInstallation components={overrideComponents} />
</DynamicLayout>
}
/>
<Route
path="configuration"
element={
<DynamicLayout toc={docsConfigurationToc}>
<DocsConfiguration components={overrideComponents} />
</DynamicLayout>
}
/>
<Route path="components">
<Route
index
loader={() =>
redirect(
`/docs/components/${Object.keys(docsModules)[0]
.split("/")
.pop()
?.replace(".mdx", "")}`,
)
}
/>
{routes}
</Route>
</Route>
</Route>,
),
);
function App() {
return <RouterProvider router={router} />;
}
export default App;
import { Code } from "@/components/LoadedCode";
import {
Route,
RouterProvider,
createBrowserRouter,
createRoutesFromElements,
redirect,
} from "react-router-dom";
import DocsLayout from "./DocsLayout";
import DynamicLayout from "./DynamicLayout";
import ErrorBoundary from "./ErrorHandler";
import Home from "./Home";
import MainLayout from "./MainLayout";
import DocsConfiguration, {
tableOfContents as docsConfigurationToc,
} from "./docs/configuration.mdx";
import DocsInstallation, {
tableOfContents as docsInstallationToc,
} from "./docs/installation.mdx";
import DocsIntroduction, {
tableOfContents as docsIntroductionToc,
} from "./docs/introduction.mdx";
import { Tooltip, TooltipContent } from "@pswui/Tooltip";
import React, {
type ForwardedRef,
forwardRef,
useContext,
useEffect,
useRef,
useState,
} from "react";
import { HeadingContext } from "./HeadingContext";
function buildThresholdList() {
const thresholds: number[] = [];
const numSteps = 20;
for (let i = 1.0; i <= numSteps; i++) {
const ratio = i / numSteps;
thresholds.push(ratio);
}
thresholds.push(0);
return thresholds;
}
type HashedHeaderProps = React.ComponentPropsWithoutRef<"h1">;
function HashedHeaders(Level: `h${1 | 2 | 3 | 4 | 5 | 6}`) {
return (prop: HashedHeaderProps, ref: ForwardedRef<HTMLHeadingElement>) => {
const internalRef = useRef<HTMLHeadingElement | null>(null);
const [_, setActiveHeadings] = useContext(HeadingContext);
const { children, ...restProp } = prop;
useEffect(() => {
const observer = new IntersectionObserver(
([{ target, intersectionRatio }]) => {
if (intersectionRatio > 0.5) {
setActiveHeadings((prev) => [...prev, target.id]);
} else {
setActiveHeadings((prev) => prev.filter((id) => id !== target.id));
}
},
{
root: null,
rootMargin: "0px",
threshold: buildThresholdList(),
},
);
if (internalRef.current) {
observer.observe(internalRef.current);
}
return () => {
observer.disconnect();
};
}, [setActiveHeadings]);
const [status, setStatus] = useState<"normal" | "error" | "success">(
"normal",
);
const messages = {
normal: "Click to copy link",
success: "Copied link!",
error: "Failed to copy..",
};
useEffect(() => {
if (status !== "normal") {
const timeout = setTimeout(() => {
setStatus("normal");
}, 3000);
return () => {
clearTimeout(timeout);
};
}
}, [status]);
return (
<Tooltip
asChild
position={"right"}
>
<Level
ref={(el) => {
internalRef.current = el;
if (typeof ref === "function") {
ref(el);
} else if (el && ref) {
ref.current = el;
}
}}
className={`${prop.className} cursor-pointer select-none`}
onClick={async (e) => {
try {
await navigator.clipboard.writeText(
`${window.location.href.split("#")[0]}#${e.currentTarget.id}`,
);
setStatus("success");
} catch (e) {
setStatus("error");
}
}}
{...restProp}
>
{children}
<TooltipContent
status={status}
offset={"lg"}
delay={"early"}
>
<p className={"text-base font-normal whitespace-nowrap not-prose"}>
{messages[status]}
</p>
</TooltipContent>
</Level>
</Tooltip>
);
};
}
const overrideComponents = {
pre: (props: React.ComponentPropsWithoutRef<"pre">) => {
if (!React.isValidElement(props.children)) {
return null;
}
const { props: clonedProps } = React.cloneElement(
React.Children.only(props.children),
);
const { children, className } = clonedProps as {
children: React.ReactNode;
className?: string;
};
let language = "typescript";
if (className) {
const detectedLanguage = /language-([a-z]+)/.exec(className);
if (detectedLanguage) language = detectedLanguage[1];
}
return <Code language={language}>{children as string}</Code>;
},
code: forwardRef<HTMLElement, { className?: string }>((props, ref) => (
<code
ref={ref}
{...props}
className={`${props.className} rounded-md bg-neutral-800 text-orange-500 font-light p-1 before:content-none after:content-none`}
/>
)),
table: forwardRef<HTMLTableElement, { className?: string }>((props, ref) => (
<div className="overflow-auto">
<table
ref={ref}
{...props}
className={`${props.className}`}
/>
</div>
)),
h1: forwardRef<HTMLHeadingElement, HashedHeaderProps>(HashedHeaders("h1")),
h2: forwardRef<HTMLHeadingElement, HashedHeaderProps>(HashedHeaders("h2")),
h3: forwardRef<HTMLHeadingElement, HashedHeaderProps>(HashedHeaders("h3")),
h4: forwardRef<HTMLHeadingElement, HashedHeaderProps>(HashedHeaders("h4")),
h5: forwardRef<HTMLHeadingElement, HashedHeaderProps>(HashedHeaders("h5")),
h6: forwardRef<HTMLHeadingElement, HashedHeaderProps>(HashedHeaders("h6")),
};
const docsModules = import.meta.glob("./docs/components/*.mdx");
const routes = Object.keys(docsModules).map((path) => {
const sfPath = path.split("/").pop()?.replace(".mdx", "");
return (
<Route
key={path}
path={sfPath}
lazy={async () => {
const { default: C, tableOfContents } = await import(
`./docs/components/${sfPath}.mdx`
);
return {
Component: () => (
<DynamicLayout toc={tableOfContents}>
<C components={overrideComponents} />
</DynamicLayout>
),
};
}}
/>
);
});
const REDIRECTED_404 = /^\?(\/([a-zA-Z0-9\-_]+\/?)+)(&.*)*$/;
const router = createBrowserRouter(
createRoutesFromElements(
<Route
path="/"
element={<MainLayout />}
errorElement={<ErrorBoundary />}
>
<Route
index
loader={() =>
REDIRECTED_404.test(window.location.search)
? redirect(REDIRECTED_404.exec(window.location.search)?.[1] ?? "/")
: true
}
element={<Home />}
/>
<Route
path="docs"
element={<DocsLayout />}
>
<Route
index
loader={() => redirect("/docs/introduction")}
/>
<Route
path="introduction"
element={
<DynamicLayout toc={docsIntroductionToc}>
<DocsIntroduction components={overrideComponents} />
</DynamicLayout>
}
/>
<Route
path="installation"
element={
<DynamicLayout toc={docsInstallationToc}>
<DocsInstallation components={overrideComponents} />
</DynamicLayout>
}
/>
<Route
path="configuration"
element={
<DynamicLayout toc={docsConfigurationToc}>
<DocsConfiguration components={overrideComponents} />
</DynamicLayout>
}
/>
<Route path="components">
<Route
index
loader={() =>
redirect(
`/docs/components/${Object.keys(docsModules)[0]
.split("/")
.pop()
?.replace(".mdx", "")}`,
)
}
/>
{routes}
</Route>
</Route>
</Route>,
),
);
function App() {
return <RouterProvider router={router} />;
}
export default App;

View File

@ -1,43 +1,43 @@
import { Link, useLocation } from "react-router-dom";
import { Outlet } from "react-router-dom";
import RouteObject from "./RouteObject";
function SideNav() {
const location = useLocation();
return (
<nav className="sticky top-16 overflow-auto max-h-[calc(100vh-4rem)] md:flex flex-col justify-start items-start gap-8 p-8 hidden">
{Object.entries(RouteObject.sideNav).map(([categoryName, links]) => {
return (
<section
className="flex flex-col gap-2 justify-center items-start"
key={categoryName}
>
<span className="font-bold">{categoryName}</span>
{links.map((link) => (
<Link
to={link.path}
key={link.path}
className="text-sm text-neutral-500 hover:text-neutral-700 data-[active=true]:text-current"
data-active={link.eq(location.pathname)}
>
{link.name}
</Link>
))}
</section>
);
})}
</nav>
);
}
function DocsLayout() {
return (
<div className="flex-grow grid grid-cols-1 md:grid-cols-[12rem_1fr] lg:grid-cols-[12rem_1fr_10rem] w-full max-w-5xl mx-auto">
<SideNav />
<Outlet />
</div>
);
}
export default DocsLayout;
import { Link, useLocation } from "react-router-dom";
import { Outlet } from "react-router-dom";
import RouteObject from "./RouteObject";
function SideNav() {
const location = useLocation();
return (
<nav className="sticky top-16 overflow-auto max-h-[calc(100vh-4rem)] md:flex flex-col justify-start items-start gap-8 p-8 hidden">
{Object.entries(RouteObject.sideNav).map(([categoryName, links]) => {
return (
<section
className="flex flex-col gap-2 justify-center items-start"
key={categoryName}
>
<span className="font-bold">{categoryName}</span>
{links.map((link) => (
<Link
to={link.path}
key={link.path}
className="text-sm text-neutral-500 hover:text-neutral-700 data-[active=true]:text-current"
data-active={link.eq(location.pathname)}
>
{link.name}
</Link>
))}
</section>
);
})}
</nav>
);
}
function DocsLayout() {
return (
<div className="flex-grow grid grid-cols-1 md:grid-cols-[12rem_1fr] lg:grid-cols-[12rem_1fr_10rem] w-full max-w-5xl mx-auto">
<SideNav />
<Outlet />
</div>
);
}
export default DocsLayout;

View File

@ -1,63 +1,63 @@
import { ReactNode, Fragment, useState, useContext } from "react";
import { type Toc } from "@stefanprobst/rehype-extract-toc";
import { useLocation } from "react-router-dom";
import { HeadingContext } from "./HeadingContext";
function RecursivelyToc({ toc }: { toc: Toc }) {
const location = useLocation();
const [activeHeadings] = useContext(HeadingContext);
return (
<ul>
{toc.map((tocEntry) => {
return (
<Fragment key={tocEntry.id}>
<li
key={tocEntry.id}
data-id={tocEntry.id}
className="text-neutral-500 data-[active='true']:text-black dark:data-[active='true']:text-white text-sm font-medium"
style={{ paddingLeft: `${tocEntry.depth - 1}rem` }}
data-active={
activeHeadings.includes(tocEntry.id ?? "")
? true
: location.hash.length > 0
? location.hash === `#${tocEntry.id}`
: false
}
>
<a href={`#${tocEntry.id}`}>{tocEntry.value}</a>
</li>
{Array.isArray(tocEntry.children) && (
<RecursivelyToc toc={tocEntry.children} />
)}
</Fragment>
);
})}
</ul>
);
}
export default function DynamicLayout({
children,
toc,
}: {
children: ReactNode;
toc: Toc;
}) {
const [activeHeadings, setActiveHeadings] = useState<string[]>([]);
return (
<HeadingContext.Provider value={[activeHeadings, setActiveHeadings]}>
<div className="w-full flex flex-col items-center">
<main className="w-full [:not(:where([class~='not-prose'],[class~='not-prose']_*))]:prose-sm prose lg:[:not(:where([class~='not-prose'],_[class~='not-prose']_*))]:prose-lg p-8 dark:prose-invert">
{children}
</main>
</div>
<nav className="hidden lg:flex flex-col gap-2 py-8 px-4 sticky top-16 overflow-auto max-h-[calc(100vh-4rem)]">
<span className="font-bold text-sm">On This Page</span>
<RecursivelyToc toc={toc} />
</nav>
</HeadingContext.Provider>
);
}
import type { Toc } from "@stefanprobst/rehype-extract-toc";
import { Fragment, type ReactNode, useContext, useState } from "react";
import { useLocation } from "react-router-dom";
import { HeadingContext } from "./HeadingContext";
function RecursivelyToc({ toc }: { toc: Toc }) {
const location = useLocation();
const [activeHeadings] = useContext(HeadingContext);
return (
<ul>
{toc.map((tocEntry) => {
return (
<Fragment key={tocEntry.id}>
<li
key={tocEntry.id}
data-id={tocEntry.id}
className="text-neutral-500 data-[active='true']:text-black dark:data-[active='true']:text-white text-sm font-medium"
style={{ paddingLeft: `${tocEntry.depth - 1}rem` }}
data-active={
activeHeadings.includes(tocEntry.id ?? "")
? true
: location.hash.length > 0
? location.hash === `#${tocEntry.id}`
: false
}
>
<a href={`#${tocEntry.id}`}>{tocEntry.value}</a>
</li>
{Array.isArray(tocEntry.children) && (
<RecursivelyToc toc={tocEntry.children} />
)}
</Fragment>
);
})}
</ul>
);
}
export default function DynamicLayout({
children,
toc,
}: {
children: ReactNode;
toc: Toc;
}) {
const [activeHeadings, setActiveHeadings] = useState<string[]>([]);
return (
<HeadingContext.Provider value={[activeHeadings, setActiveHeadings]}>
<div className="w-full flex flex-col items-center">
<main className="w-full [:not(:where([class~='not-prose'],[class~='not-prose']_*))]:prose-sm prose lg:[:not(:where([class~='not-prose'],_[class~='not-prose']_*))]:prose-lg p-8 dark:prose-invert">
{children}
</main>
</div>
<nav className="hidden lg:flex flex-col gap-2 py-8 px-4 sticky top-16 overflow-auto max-h-[calc(100vh-4rem)]">
<span className="font-bold text-sm">On This Page</span>
<RecursivelyToc toc={toc} />
</nav>
</HeadingContext.Provider>
);
}

View File

@ -1,19 +1,17 @@
import { isRouteErrorResponse, useRouteError } from "react-router-dom";
import UnexpectedError from "./errors/Unexpected";
import PageNotFound from "./errors/PageNotFound";
function ErrorBoundary() {
const error = useRouteError();
if (isRouteErrorResponse(error)) {
if (error.status === 404) {
return <PageNotFound />;
} else {
return <UnexpectedError />;
}
} else {
return <UnexpectedError />;
}
}
export default ErrorBoundary;
import { isRouteErrorResponse, useRouteError } from "react-router-dom";
import PageNotFound from "./errors/PageNotFound";
import UnexpectedError from "./errors/Unexpected";
function ErrorBoundary() {
const error = useRouteError();
if (isRouteErrorResponse(error)) {
if (error.status === 404) {
return <PageNotFound />;
}
}
return <UnexpectedError />;
}
export default ErrorBoundary;

View File

@ -1,12 +1,12 @@
import { Dispatch, SetStateAction, createContext } from "react";
export const HeadingContext = createContext<
[string[], Dispatch<SetStateAction<string[]>>]
>([
[],
() => {
if (process.env && process.env.NODE_ENV === "development") {
console.log("HeadingContext outside");
}
},
]);
import { type Dispatch, type SetStateAction, createContext } from "react";
export const HeadingContext = createContext<
[string[], Dispatch<SetStateAction<string[]>>]
>([
[],
() => {
if (process.env && process.env.NODE_ENV === "development") {
console.log("HeadingContext outside");
}
},
]);

View File

@ -1,30 +1,36 @@
import { Link } from "react-router-dom";
import { Button } from "@pswui/Button";
function Home() {
return (
<main className="flex-grow h-full flex flex-col p-4 justify-center items-center">
<section className="h-full flex flex-col justify-center items-center text-center gap-8">
<header className="flex flex-col justify-center items-center gap-2">
<h1 className="text-4xl font-bold">
Build your components in isolation
</h1>
<p className="text-xl max-w-xl">
There are a lot of component libraries out there, but why it install
so many things?
</p>
</header>
<div className="flex flex-row justify-center items-center gap-2">
<Button asChild preset="default">
<Link to="/docs">Get Started</Link>
</Button>
<Button asChild preset="ghost">
<Link to="/docs/components">Components</Link>
</Button>
</div>
</section>
</main>
);
}
export default Home;
import { Button } from "@pswui/Button";
import { Link } from "react-router-dom";
function Home() {
return (
<main className="flex-grow h-full flex flex-col p-4 justify-center items-center">
<section className="h-full flex flex-col justify-center items-center text-center gap-8">
<header className="flex flex-col justify-center items-center gap-2">
<h1 className="text-4xl font-bold">
Build your components in isolation
</h1>
<p className="text-xl max-w-xl">
There are a lot of component libraries out there, but why it install
so many things?
</p>
</header>
<div className="flex flex-row justify-center items-center gap-2">
<Button
asChild
preset="default"
>
<Link to="/docs">Get Started</Link>
</Button>
<Button
asChild
preset="ghost"
>
<Link to="/docs/components">Components</Link>
</Button>
</div>
</section>
</main>
);
}
export default Home;

View File

@ -1,215 +1,243 @@
import { useEffect, useState } from "react";
import { Link, Outlet, useLocation } from "react-router-dom";
import { Button } from "@pswui/Button";
import RouteObject from "./RouteObject";
import { Toaster } from "@pswui/Toast";
import { Popover, PopoverContent, PopoverTrigger } from "@pswui/Popover";
import {
DrawerClose,
DrawerContent,
DrawerOverlay,
DrawerRoot,
DrawerTrigger,
} from "@pswui/Drawer";
type Theme = "light" | "dark" | "system";
function ThemeButton() {
const [theme, setTheme] = useState<Theme>(
(localStorage.getItem("theme") as Theme) || "system"
);
useEffect(() => {
document.documentElement.classList.toggle("dark", theme === "dark");
document.documentElement.classList.toggle("system", theme === "system");
localStorage.setItem("theme", theme);
}, [theme]);
return (
<Popover>
<PopoverTrigger>
<Button preset="ghost" size="icon">
{/* material-symbols:light-mode */}
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
className="dark:hidden"
>
<path
fill="currentColor"
d="M12 17q-2.075 0-3.537-1.463T7 12t1.463-3.537T12 7t3.538 1.463T17 12t-1.463 3.538T12 17m-7-4H1v-2h4zm18 0h-4v-2h4zM11 5V1h2v4zm0 18v-4h2v4zM6.4 7.75L3.875 5.325L5.3 3.85l2.4 2.5zm12.3 12.4l-2.425-2.525L17.6 16.25l2.525 2.425zM16.25 6.4l2.425-2.525L20.15 5.3l-2.5 2.4zM3.85 18.7l2.525-2.425L7.75 17.6l-2.425 2.525z"
/>
</svg>
{/* material-symbols:dark-mode */}
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
className="hidden dark:block"
>
<path
fill="currentColor"
d="M12 21q-3.75 0-6.375-2.625T3 12t2.625-6.375T12 3q.35 0 .688.025t.662.075q-1.025.725-1.638 1.888T11.1 7.5q0 2.25 1.575 3.825T16.5 12.9q1.375 0 2.525-.613T20.9 10.65q.05.325.075.662T21 12q0 3.75-2.625 6.375T12 21"
/>
</svg>
</Button>
</PopoverTrigger>
<PopoverContent anchor="bottomLeft" className="w-32">
<Button
preset="ghost"
onClick={() => setTheme("light")}
className="w-full px-2 py-1.5 text-sm"
>
Light
</Button>
<Button
preset="ghost"
onClick={() => setTheme("dark")}
className="w-full px-2 py-1.5 text-sm"
>
Dark
</Button>
<Button
preset="ghost"
onClick={() => setTheme("system")}
className="w-full px-2 py-1.5 text-sm"
>
System
</Button>
</PopoverContent>
</Popover>
);
}
function TopNav() {
const location = useLocation();
return (
<>
<nav className="sticky top-0 z-20 bg-transparent backdrop-blur-lg border-b border-neutral-200 dark:border-neutral-800 w-full max-w-screen px-8 flex flex-row justify-center items-center h-16">
<div
data-role="wrapper"
className="flex flex-row items-center justify-between w-full max-w-6xl text-lg"
>
<div
data-role="links"
className="hidden md:flex flex-row items-center gap-3"
>
<Link to="/" className="font-bold">
PSW/UI
</Link>
{RouteObject.mainNav.map((link) => {
return (
<Link
key={link.path}
to={link.path}
data-active={link.eq(location.pathname)}
className="font-light text-base data-[active=true]:text-current text-neutral-500 hover:text-neutral-700"
>
{link.name}
</Link>
);
})}
</div>
<div data-role="mobile-links" className="flex md:hidden">
<DrawerRoot>
<DrawerTrigger>
<Button preset="ghost" size="icon">
{/* mdi:menu */}
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"
/>
</svg>
</Button>
</DrawerTrigger>
<DrawerOverlay className="z-[99]">
<DrawerContent className="w-[300px] overflow-auto">
<DrawerClose className="absolute top-4 right-4">
<Button preset="ghost" size="icon">
{/* mdi:close */}
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M19 6.41L17.59 5 12 9.27 6.41 5 5 6.41 9.27 11 5 17.59 6.41 19 12 14.73 17.59 19 19 17.59 13.41 12 19 6.41"
/>
</svg>
</Button>
</DrawerClose>
<div className="flex flex-col justify-start items-start gap-6 text-lg">
<div className="flex flex-col justify-start items-start gap-3">
<DrawerClose>
<Link to="/" className="font-extrabold">
PSW/UI
</Link>
</DrawerClose>
{RouteObject.mainNav.map((link) => {
return (
<DrawerClose key={link.path}>
<Link to={link.path}>{link.name}</Link>
</DrawerClose>
);
})}
</div>
{Object.entries(RouteObject.sideNav).map(
([categoryName, links]) => {
return (
<div
className="flex flex-col justify-start items-start gap-3"
key={categoryName}
>
<h2 className="font-bold">{categoryName}</h2>
{links.map((link) => {
return (
<DrawerClose key={link.path}>
<Link
to={link.path}
className="text-base opacity-75"
>
{link.name}
</Link>
</DrawerClose>
);
})}
</div>
);
}
)}
</div>
</DrawerContent>
</DrawerOverlay>
</DrawerRoot>
</div>
<div data-role="controls" className="flex flex-row items-center">
<ThemeButton />
</div>
</div>
</nav>
</>
);
}
function MainLayout() {
return (
<>
<Toaster className="top-16" />
<TopNav />
<Outlet />
</>
);
}
export default MainLayout;
import { Button } from "@pswui/Button";
import {
DrawerClose,
DrawerContent,
DrawerOverlay,
DrawerRoot,
DrawerTrigger,
} from "@pswui/Drawer";
import { Popover, PopoverContent, PopoverTrigger } from "@pswui/Popover";
import { Toaster } from "@pswui/Toast";
import { useEffect, useState } from "react";
import { Link, Outlet, useLocation } from "react-router-dom";
import RouteObject from "./RouteObject";
type Theme = "light" | "dark" | "system";
function ThemeButton() {
const [theme, setTheme] = useState<Theme>(
(localStorage.getItem("theme") as Theme) || "system",
);
useEffect(() => {
document.documentElement.classList.toggle("dark", theme === "dark");
document.documentElement.classList.toggle("system", theme === "system");
localStorage.setItem("theme", theme);
}, [theme]);
return (
<Popover>
<PopoverTrigger>
<Button
preset="ghost"
size="icon"
>
{/* material-symbols:light-mode */}
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
className="dark:hidden"
>
<title>Sun</title>
<path
fill="currentColor"
d="M12 17q-2.075 0-3.537-1.463T7 12t1.463-3.537T12 7t3.538 1.463T17 12t-1.463 3.538T12 17m-7-4H1v-2h4zm18 0h-4v-2h4zM11 5V1h2v4zm0 18v-4h2v4zM6.4 7.75L3.875 5.325L5.3 3.85l2.4 2.5zm12.3 12.4l-2.425-2.525L17.6 16.25l2.525 2.425zM16.25 6.4l2.425-2.525L20.15 5.3l-2.5 2.4zM3.85 18.7l2.525-2.425L7.75 17.6l-2.425 2.525z"
/>
</svg>
{/* material-symbols:dark-mode */}
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
className="hidden dark:block"
>
<title>Moon</title>
<path
fill="currentColor"
d="M12 21q-3.75 0-6.375-2.625T3 12t2.625-6.375T12 3q.35 0 .688.025t.662.075q-1.025.725-1.638 1.888T11.1 7.5q0 2.25 1.575 3.825T16.5 12.9q1.375 0 2.525-.613T20.9 10.65q.05.325.075.662T21 12q0 3.75-2.625 6.375T12 21"
/>
</svg>
</Button>
</PopoverTrigger>
<PopoverContent
anchor="bottomLeft"
className="w-32"
>
<Button
preset="ghost"
onClick={() => setTheme("light")}
className="w-full px-2 py-1.5 text-sm"
>
Light
</Button>
<Button
preset="ghost"
onClick={() => setTheme("dark")}
className="w-full px-2 py-1.5 text-sm"
>
Dark
</Button>
<Button
preset="ghost"
onClick={() => setTheme("system")}
className="w-full px-2 py-1.5 text-sm"
>
System
</Button>
</PopoverContent>
</Popover>
);
}
function TopNav() {
const location = useLocation();
return (
<>
<nav className="sticky top-0 z-20 bg-transparent backdrop-blur-lg border-b border-neutral-200 dark:border-neutral-800 w-full max-w-screen px-8 flex flex-row justify-center items-center h-16">
<div
data-role="wrapper"
className="flex flex-row items-center justify-between w-full max-w-6xl text-lg"
>
<div
data-role="links"
className="hidden md:flex flex-row items-center gap-3"
>
<Link
to="/"
className="font-bold"
>
PSW/UI
</Link>
{RouteObject.mainNav.map((link) => {
return (
<Link
key={link.path}
to={link.path}
data-active={link.eq(location.pathname)}
className="font-light text-base data-[active=true]:text-current text-neutral-500 hover:text-neutral-700"
>
{link.name}
</Link>
);
})}
</div>
<div
data-role="mobile-links"
className="flex md:hidden"
>
<DrawerRoot>
<DrawerTrigger>
<Button
preset="ghost"
size="icon"
>
{/* mdi:menu */}
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
>
<title>Menu</title>
<path
fill="currentColor"
d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"
/>
</svg>
</Button>
</DrawerTrigger>
<DrawerOverlay className="z-[99]">
<DrawerContent className="w-[300px] overflow-auto">
<DrawerClose className="absolute top-4 right-4">
<Button
preset="ghost"
size="icon"
>
{/* mdi:close */}
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
>
<title>Close</title>
<path
fill="currentColor"
d="M19 6.41L17.59 5 12 9.27 6.41 5 5 6.41 9.27 11 5 17.59 6.41 19 12 14.73 17.59 19 19 17.59 13.41 12 19 6.41"
/>
</svg>
</Button>
</DrawerClose>
<div className="flex flex-col justify-start items-start gap-6 text-lg">
<div className="flex flex-col justify-start items-start gap-3">
<DrawerClose>
<Link
to="/"
className="font-extrabold"
>
PSW/UI
</Link>
</DrawerClose>
{RouteObject.mainNav.map((link) => {
return (
<DrawerClose key={link.path}>
<Link to={link.path}>{link.name}</Link>
</DrawerClose>
);
})}
</div>
{Object.entries(RouteObject.sideNav).map(
([categoryName, links]) => {
return (
<div
className="flex flex-col justify-start items-start gap-3"
key={categoryName}
>
<h2 className="font-bold">{categoryName}</h2>
{links.map((link) => {
return (
<DrawerClose key={link.path}>
<Link
to={link.path}
className="text-base opacity-75"
>
{link.name}
</Link>
</DrawerClose>
);
})}
</div>
);
},
)}
</div>
</DrawerContent>
</DrawerOverlay>
</DrawerRoot>
</div>
<div
data-role="controls"
className="flex flex-row items-center"
>
<ThemeButton />
</div>
</div>
</nav>
</>
);
}
function MainLayout() {
return (
<>
<Toaster className="top-16" />
<TopNav />
<Outlet />
</>
);
}
export default MainLayout;

View File

@ -1,59 +1,59 @@
const docsModules = import.meta.glob("./docs/components/*.mdx");
const mainNav = [
{
path: "/docs",
name: "Docs",
eq: (pathname: string) =>
pathname.startsWith("/docs") && !pathname.startsWith("/docs/components"),
},
{
path: "/docs/components",
name: "Components",
eq: (pathname: string) => pathname.startsWith("/docs/components"),
},
{
path: "https://github.com/pswui/ui",
name: "Github",
eq: () => false,
},
];
const sideNav: Record<
string,
{ path: string; name: string; eq: (path: string) => boolean }[]
> = {
Documents: [
{
path: "/docs/introduction",
name: "Introduction",
eq: (pathname: string) => pathname === "/docs/introduction",
},
{
path: "/docs/installation",
name: "Installation",
eq: (pathname: string) => pathname === "/docs/installation",
},
{
path: "/docs/configuration",
name: "Configuration",
eq: (pathname: string) => pathname === "/docs/configuration",
},
],
Components: [],
};
Object.keys(docsModules).forEach((path) => {
const name = (path.split("/").pop() ?? "").replace(".mdx", "");
sideNav["Components"].push({
path: path.replace("./docs", "/docs").replace(".mdx", ""),
name: name.charAt(0).toUpperCase() + name.slice(1),
eq: (pathname: string) =>
pathname === path.replace("./docs", "/docs").replace(".mdx", ""),
});
});
export default {
mainNav,
sideNav,
};
const docsModules = import.meta.glob("./docs/components/*.mdx");
const mainNav = [
{
path: "/docs",
name: "Docs",
eq: (pathname: string) =>
pathname.startsWith("/docs") && !pathname.startsWith("/docs/components"),
},
{
path: "/docs/components",
name: "Components",
eq: (pathname: string) => pathname.startsWith("/docs/components"),
},
{
path: "https://github.com/pswui/ui",
name: "Github",
eq: () => false,
},
];
const sideNav: Record<
string,
{ path: string; name: string; eq: (path: string) => boolean }[]
> = {
Documents: [
{
path: "/docs/introduction",
name: "Introduction",
eq: (pathname: string) => pathname === "/docs/introduction",
},
{
path: "/docs/installation",
name: "Installation",
eq: (pathname: string) => pathname === "/docs/installation",
},
{
path: "/docs/configuration",
name: "Configuration",
eq: (pathname: string) => pathname === "/docs/configuration",
},
],
Components: [],
};
for (const path of Object.keys(docsModules)) {
const name = (path.split("/").pop() ?? "").replace(".mdx", "");
sideNav.Components.push({
path: path.replace("./docs", "/docs").replace(".mdx", ""),
name: name.charAt(0).toUpperCase() + name.slice(1),
eq: (pathname: string) =>
pathname === path.replace("./docs", "/docs").replace(".mdx", ""),
});
}
export default {
mainNav,
sideNav,
};

View File

@ -1,123 +1,132 @@
import { forwardRef, useEffect, useState } from "react";
import SyntaxHighlighter from "react-syntax-highlighter";
import { gruvboxDark } from "react-syntax-highlighter/dist/cjs/styles/hljs";
import { Button } from "@pswui/Button";
import { useToast } from "@pswui/Toast";
import { twMerge } from "tailwind-merge";
export const GITHUB_UI = "https://raw.githubusercontent.com/pswui/ui/main";
export const GITHUB_DOCS = "https://raw.githubusercontent.com/pswui/docs/main";
export const GITHUB_COMP = (componentName: string) => `${GITHUB_UI}/packages/react/components/${componentName}.tsx`
export const GITHUB_DIR_COMP = (componentName: string, source: string) => `${GITHUB_UI}/packages/react/components/${componentName}/${source}`
export const GITHUB_COMP_PREVIEW = (componentName: string) => `${GITHUB_DOCS}/src/docs/components/${componentName}Blocks/Preview.tsx`
export const GITHUB_STORY = (componentName: string, storyName: string) => `${GITHUB_DOCS}/src/docs/components/${componentName}Blocks/Examples/${storyName}.tsx`;
export const LoadedCode = ({
from,
className,
}: {
from: string;
className?: string;
}) => {
const [state, setState] = useState<string | undefined | null>();
const { toast } = useToast();
useEffect(() => {
(async () => {
const res = await fetch(from);
const text = await res.text();
setState(text);
})();
}, [from]);
return (
<div className={twMerge("relative", className)}>
<Button
preset="default"
size="icon"
className="absolute top-4 right-4 text-black dark:text-white z-10"
onClick={() => {
if (state && state.length > 0) {
void navigator.clipboard.writeText(state ?? "");
toast({
title: "Copied",
description: "The code has been copied to your clipboard.",
status: "success",
});
} else {
toast({
title: "Error",
description: "It seems like code is not loaded yet.",
status: "error",
});
}
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M4 7v14h14v2H4c-1.1 0-2-.9-2-2V7zm16-4c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H8c-1.1 0-2-.9-2-2V5c0-1.1.9-2 2-2h3.18C11.6 1.84 12.7 1 14 1s2.4.84 2.82 2zm-6 0c-.55 0-1 .45-1 1s.45 1 1 1s1-.45 1-1s-.45-1-1-1m-4 4V5H8v12h12V5h-2v2z"
/>
</svg>
</Button>
<SyntaxHighlighter
language="typescript"
style={gruvboxDark}
className={`w-full h-64 rounded-lg ${!state ? "animate-pulse" : ""} scrollbar-none`}
customStyle={{ padding: "1rem" }}
>
{state ?? ""}
</SyntaxHighlighter>
</div>
);
};
export const Code = forwardRef<
HTMLDivElement,
{ children: string; className?: string; language: string }
>(({ children, className, language }, ref) => {
const { toast } = useToast();
return (
<div className={twMerge("relative", className)} ref={ref}>
<Button
preset="default"
size="icon"
className="absolute top-4 right-4 text-black dark:text-white z-10"
onClick={() => {
void navigator.clipboard.writeText(children ?? "");
toast({
title: "Copied",
description: "The code has been copied to your clipboard.",
status: "success",
});
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M4 7v14h14v2H4c-1.1 0-2-.9-2-2V7zm16-4c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H8c-1.1 0-2-.9-2-2V5c0-1.1.9-2 2-2h3.18C11.6 1.84 12.7 1 14 1s2.4.84 2.82 2zm-6 0c-.55 0-1 .45-1 1s.45 1 1 1s1-.45 1-1s-.45-1-1-1m-4 4V5H8v12h12V5h-2v2z"
/>
</svg>
</Button>
<SyntaxHighlighter
language={language}
style={gruvboxDark}
className={`w-full h-auto max-h-64 rounded-lg scrollbar-none`}
customStyle={{ padding: "1rem" }}
>
{children}
</SyntaxHighlighter>
</div>
);
});
import { Button } from "@pswui/Button";
import { useToast } from "@pswui/Toast";
import { forwardRef, useEffect, useState } from "react";
import SyntaxHighlighter from "react-syntax-highlighter";
import { gruvboxDark } from "react-syntax-highlighter/dist/cjs/styles/hljs";
import { twMerge } from "tailwind-merge";
export const GITHUB_UI = "https://raw.githubusercontent.com/pswui/ui/main";
export const GITHUB_DOCS = "https://raw.githubusercontent.com/pswui/docs/main";
export const GITHUB_COMP = (componentName: string) =>
`${GITHUB_UI}/packages/react/components/${componentName}.tsx`;
export const GITHUB_DIR_COMP = (componentName: string, source: string) =>
`${GITHUB_UI}/packages/react/components/${componentName}/${source}`;
export const GITHUB_COMP_PREVIEW = (componentName: string) =>
`${GITHUB_DOCS}/src/docs/components/${componentName}Blocks/Preview.tsx`;
export const GITHUB_STORY = (componentName: string, storyName: string) =>
`${GITHUB_DOCS}/src/docs/components/${componentName}Blocks/Examples/${storyName}.tsx`;
export const LoadedCode = ({
from,
className,
}: {
from: string;
className?: string;
}) => {
const [state, setState] = useState<string | undefined | null>();
const { toast } = useToast();
useEffect(() => {
(async () => {
const res = await fetch(from);
const text = await res.text();
setState(text);
})();
}, [from]);
return (
<div className={twMerge("relative", className)}>
<Button
preset="default"
size="icon"
className="absolute top-4 right-4 text-black dark:text-white z-10"
onClick={() => {
if (state && state.length > 0) {
void navigator.clipboard.writeText(state ?? "");
toast({
title: "Copied",
description: "The code has been copied to your clipboard.",
status: "success",
});
} else {
toast({
title: "Error",
description: "It seems like code is not loaded yet.",
status: "error",
});
}
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
>
<title>Copy</title>
<path
fill="currentColor"
d="M4 7v14h14v2H4c-1.1 0-2-.9-2-2V7zm16-4c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H8c-1.1 0-2-.9-2-2V5c0-1.1.9-2 2-2h3.18C11.6 1.84 12.7 1 14 1s2.4.84 2.82 2zm-6 0c-.55 0-1 .45-1 1s.45 1 1 1s1-.45 1-1s-.45-1-1-1m-4 4V5H8v12h12V5h-2v2z"
/>
</svg>
</Button>
<SyntaxHighlighter
language="typescript"
style={gruvboxDark}
className={`w-full h-64 rounded-lg ${!state ? "animate-pulse" : ""} scrollbar-none`}
customStyle={{ padding: "1rem" }}
>
{state ?? ""}
</SyntaxHighlighter>
</div>
);
};
export const Code = forwardRef<
HTMLDivElement,
{ children: string; className?: string; language: string }
>(({ children, className, language }, ref) => {
const { toast } = useToast();
return (
<div
className={twMerge("relative", className)}
ref={ref}
>
<Button
preset="default"
size="icon"
className="absolute top-4 right-4 text-black dark:text-white z-10"
onClick={() => {
void navigator.clipboard.writeText(children ?? "");
toast({
title: "Copied",
description: "The code has been copied to your clipboard.",
status: "success",
});
}}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
>
<title>Copy</title>
<path
fill="currentColor"
d="M4 7v14h14v2H4c-1.1 0-2-.9-2-2V7zm16-4c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H8c-1.1 0-2-.9-2-2V5c0-1.1.9-2 2-2h3.18C11.6 1.84 12.7 1 14 1s2.4.84 2.82 2zm-6 0c-.55 0-1 .45-1 1s.45 1 1 1s1-.45 1-1s-.45-1-1-1m-4 4V5H8v12h12V5h-2v2z"
/>
</svg>
</Button>
<SyntaxHighlighter
language={language}
style={gruvboxDark}
className={"w-full h-auto max-h-64 rounded-lg scrollbar-none"}
customStyle={{ padding: "1rem" }}
>
{children}
</SyntaxHighlighter>
</div>
);
});

View File

@ -1,32 +1,32 @@
import React from "react";
import { twMerge } from "tailwind-merge";
const layoutClasses = {
default: "",
centered: "flex items-center justify-center",
};
const Story = React.forwardRef<
HTMLDivElement,
{
layout?: keyof typeof layoutClasses;
children: React.ReactNode;
className?: string;
id?: string;
}
>(({ layout = "default", children, className, id }, ref) => {
return (
<div
className={twMerge(
`bg-white dark:bg-black border border-neutral-300 dark:border-neutral-700 rounded-lg w-full p-4 min-h-48 h-auto my-8 not-prose ${layoutClasses[layout]}`,
className
)}
ref={ref}
id={id}
>
{children}
</div>
);
});
export { Story };
import React from "react";
import { twMerge } from "tailwind-merge";
const layoutClasses = {
default: "",
centered: "flex items-center justify-center",
};
const Story = React.forwardRef<
HTMLDivElement,
{
layout?: keyof typeof layoutClasses;
children: React.ReactNode;
className?: string;
id?: string;
}
>(({ layout = "default", children, className, id }, ref) => {
return (
<div
className={twMerge(
`bg-white dark:bg-black border border-neutral-300 dark:border-neutral-700 rounded-lg w-full p-4 min-h-48 h-auto my-8 not-prose ${layoutClasses[layout]}`,
className,
)}
ref={ref}
id={id}
>
{children}
</div>
);
});
export { Story };

View File

@ -1,5 +1,5 @@
import { Button } from "@pswui/Button";
export const Danger = () => {
return <Button preset="danger">Danger</Button>;
};
import { Button } from "@pswui/Button";
export const Danger = () => {
return <Button preset="danger">Danger</Button>;
};

View File

@ -1,5 +1,5 @@
import { Button } from "@pswui/Button";
export const Default = () => {
return <Button preset="default">Default</Button>;
};
import { Button } from "@pswui/Button";
export const Default = () => {
return <Button preset="default">Default</Button>;
};

View File

@ -1,5 +1,5 @@
import { Button } from "@pswui/Button";
export const Ghost = () => {
return <Button preset="ghost">Ghost</Button>;
};
import { Button } from "@pswui/Button";
export const Ghost = () => {
return <Button preset="ghost">Ghost</Button>;
};

View File

@ -1,5 +1,5 @@
import { Button } from "@pswui/Button";
export const Link = () => {
return <Button preset="link">Link</Button>;
};
import { Button } from "@pswui/Button";
export const Link = () => {
return <Button preset="link">Link</Button>;
};

View File

@ -1,5 +1,5 @@
import { Button } from "@pswui/Button";
export const Success = () => {
return <Button preset="success">Success</Button>;
};
import { Button } from "@pswui/Button";
export const Success = () => {
return <Button preset="success">Success</Button>;
};

View File

@ -1,5 +1,5 @@
import { Button } from "@pswui/Button";
export const Warning = () => {
return <Button preset="warning">Warning</Button>;
};
import { Button } from "@pswui/Button";
export const Warning = () => {
return <Button preset="warning">Warning</Button>;
};

View File

@ -1,16 +1,15 @@
import { Danger } from "./Danger";
import { Warning } from "./Warning";
import { Success } from "./Success";
import { Link } from "./Link";
import { Ghost } from "./Ghost";
import { Default } from "./Default";
export default {
Danger,
Warning,
Success,
Link,
Ghost,
Default,
};
import { Danger } from "./Danger";
import { Default } from "./Default";
import { Ghost } from "./Ghost";
import { Link } from "./Link";
import { Success } from "./Success";
import { Warning } from "./Warning";
export default {
Danger,
Warning,
Success,
Link,
Ghost,
Default,
};

View File

@ -1,5 +1,5 @@
import { Button } from "@pswui/Button";
export function ButtonDemo() {
return <Button>Button</Button>;
}
import { Button } from "@pswui/Button";
export function ButtonDemo() {
return <Button>Button</Button>;
}

View File

@ -1,11 +1,14 @@
import { Label } from "@pswui/Label";
import { Checkbox } from "@pswui/Checkbox";
export function Disabled() {
return (
<Label direction="horizontal">
<Checkbox size="base" disabled />
<span>Agree terms and conditions</span>
</Label>
);
}
import { Checkbox } from "@pswui/Checkbox";
import { Label } from "@pswui/Label";
export function Disabled() {
return (
<Label direction="horizontal">
<Checkbox
size="base"
disabled
/>
<span>Agree terms and conditions</span>
</Label>
);
}

View File

@ -1,11 +1,11 @@
import { Label } from "@pswui/Label";
import { Checkbox } from "@pswui/Checkbox";
export function Text() {
return (
<Label direction="horizontal">
<Checkbox size="base" />
<span>Agree terms and conditions</span>
</Label>
);
}
import { Checkbox } from "@pswui/Checkbox";
import { Label } from "@pswui/Label";
export function Text() {
return (
<Label direction="horizontal">
<Checkbox size="base" />
<span>Agree terms and conditions</span>
</Label>
);
}

View File

@ -1,5 +1,4 @@
import { Text } from "./Text";
import { Disabled } from "./Disabled";
export default { Text, Disabled };
import { Disabled } from "./Disabled";
import { Text } from "./Text";
export default { Text, Disabled };

View File

@ -1,11 +1,11 @@
import { Checkbox } from "@pswui/Checkbox";
import { Label } from "@pswui/Label";
export function CheckboxDemo() {
return (
<Label direction="horizontal">
<Checkbox />
<span>Checkbox</span>
</Label>
);
}
import { Checkbox } from "@pswui/Checkbox";
import { Label } from "@pswui/Label";
export function CheckboxDemo() {
return (
<Label direction="horizontal">
<Checkbox />
<span>Checkbox</span>
</Label>
);
}

View File

@ -1,36 +1,36 @@
import { Button } from "@pswui/Button";
import {
DialogRoot,
DialogTrigger,
DialogOverlay,
DialogContent,
DialogHeader,
DialogTitle,
DialogSubtitle,
DialogFooter,
DialogClose,
} from "@pswui/Dialog";
export function BasicInformationalDialog() {
return (
<DialogRoot>
<DialogTrigger>
<Button preset="default">What is this?</Button>
</DialogTrigger>
<DialogOverlay>
<DialogContent size={"fullMd"}>
<DialogHeader>
<DialogTitle>Dialog Title</DialogTitle>
<DialogSubtitle>Dialog Subtitle</DialogSubtitle>
</DialogHeader>
<p>This is a dialog. You can put the information you want to show.</p>
<DialogFooter>
<DialogClose>
<Button preset="default">Ok!</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</DialogOverlay>
</DialogRoot>
);
}
import { Button } from "@pswui/Button";
import {
DialogClose,
DialogContent,
DialogFooter,
DialogHeader,
DialogOverlay,
DialogRoot,
DialogSubtitle,
DialogTitle,
DialogTrigger,
} from "@pswui/Dialog";
export function BasicInformationalDialog() {
return (
<DialogRoot>
<DialogTrigger>
<Button preset="default">What is this?</Button>
</DialogTrigger>
<DialogOverlay>
<DialogContent size={"fullMd"}>
<DialogHeader>
<DialogTitle>Dialog Title</DialogTitle>
<DialogSubtitle>Dialog Subtitle</DialogSubtitle>
</DialogHeader>
<p>This is a dialog. You can put the information you want to show.</p>
<DialogFooter>
<DialogClose>
<Button preset="default">Ok!</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</DialogOverlay>
</DialogRoot>
);
}

View File

@ -1,79 +1,79 @@
import {
DialogRoot,
DialogTrigger,
DialogOverlay,
DialogContent,
DialogHeader,
DialogTitle,
DialogSubtitle,
DialogFooter,
DialogClose,
} from "@pswui/Dialog";
import { Button } from "@pswui/Button";
import { useToast } from "@pswui/Toast";
export function DeletingItem() {
const { toast } = useToast();
return (
<DialogRoot>
<DialogTrigger>
<Button preset="danger">Delete Item</Button>
</DialogTrigger>
<DialogOverlay>
<DialogContent size={"fullMd"}>
<DialogHeader>
<DialogTitle>Delete Item</DialogTitle>
<DialogSubtitle>
Are you sure you want to delete this item?
</DialogSubtitle>
</DialogHeader>
<div className="flex flex-col gap-3">
<ul className="list-disc list-inside">
<li>This action will delete the item, and related data</li>
<li>This action cannot be undone</li>
</ul>
</div>
<DialogFooter>
<DialogClose>
<Button
preset="danger"
onClick={async () => {
const toasted = toast({
title: "Deleting Item",
description: "Item deletion is requested",
status: "loading",
});
await new Promise((r) => setTimeout(r, 1000));
toasted.update({
title: "Item Deleted",
description: "The item has been deleted",
status: "success",
});
}}
>
Delete
</Button>
</DialogClose>
<DialogClose>
<Button
preset="default"
onClick={() => {
toast({
title: "Action Canceled",
description: "The delete action has been canceled",
status: "error",
});
}}
>
Cancel
</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</DialogOverlay>
</DialogRoot>
);
}
import { Button } from "@pswui/Button";
import {
DialogClose,
DialogContent,
DialogFooter,
DialogHeader,
DialogOverlay,
DialogRoot,
DialogSubtitle,
DialogTitle,
DialogTrigger,
} from "@pswui/Dialog";
import { useToast } from "@pswui/Toast";
export function DeletingItem() {
const { toast } = useToast();
return (
<DialogRoot>
<DialogTrigger>
<Button preset="danger">Delete Item</Button>
</DialogTrigger>
<DialogOverlay>
<DialogContent size={"fullMd"}>
<DialogHeader>
<DialogTitle>Delete Item</DialogTitle>
<DialogSubtitle>
Are you sure you want to delete this item?
</DialogSubtitle>
</DialogHeader>
<div className="flex flex-col gap-3">
<ul className="list-disc list-inside">
<li>This action will delete the item, and related data</li>
<li>This action cannot be undone</li>
</ul>
</div>
<DialogFooter>
<DialogClose>
<Button
preset="danger"
onClick={async () => {
const toasted = toast({
title: "Deleting Item",
description: "Item deletion is requested",
status: "loading",
});
await new Promise((r) => setTimeout(r, 1000));
toasted.update({
title: "Item Deleted",
description: "The item has been deleted",
status: "success",
});
}}
>
Delete
</Button>
</DialogClose>
<DialogClose>
<Button
preset="default"
onClick={() => {
toast({
title: "Action Canceled",
description: "The delete action has been canceled",
status: "error",
});
}}
>
Cancel
</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</DialogOverlay>
</DialogRoot>
);
}

View File

@ -1,8 +1,7 @@
import { BasicInformationalDialog } from "./BasicInformationalDialog";
import { DeletingItem } from "./DeletingItem";
export default {
BasicInformationalDialog,
DeletingItem,
}
import { BasicInformationalDialog } from "./BasicInformationalDialog";
import { DeletingItem } from "./DeletingItem";
export default {
BasicInformationalDialog,
DeletingItem,
};

View File

@ -1,42 +1,42 @@
import {
DialogRoot,
DialogTrigger,
DialogOverlay,
DialogContent,
DialogHeader,
DialogTitle,
DialogSubtitle,
DialogFooter,
DialogClose,
} from "@pswui/Dialog";
import { Button } from "@pswui/Button";
export function DialogDemo() {
return (
<DialogRoot>
<DialogTrigger>
<Button preset="default">Open Dialog</Button>
</DialogTrigger>
<DialogOverlay>
<DialogContent size={"fullMd"}>
<DialogHeader>
<DialogTitle>Dialog Title</DialogTitle>
<DialogSubtitle>Dialog Subtitle</DialogSubtitle>
</DialogHeader>
<p>
Laborum non adipisicing enim enim culpa esse anim esse consequat
Lorem incididunt. Enim mollit laborum sunt cillum voluptate est
officia nostrud non consequat adipisicing cupidatat aliquip magna.
Voluptate nisi cupidatat qui nisi in pariatur. Sint consequat labore
pariatur mollit sint nostrud tempor commodo pariatur ea laboris.
</p>
<DialogFooter>
<DialogClose>
<Button preset="default">Close</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</DialogOverlay>
</DialogRoot>
);
}
import { Button } from "@pswui/Button";
import {
DialogClose,
DialogContent,
DialogFooter,
DialogHeader,
DialogOverlay,
DialogRoot,
DialogSubtitle,
DialogTitle,
DialogTrigger,
} from "@pswui/Dialog";
export function DialogDemo() {
return (
<DialogRoot>
<DialogTrigger>
<Button preset="default">Open Dialog</Button>
</DialogTrigger>
<DialogOverlay>
<DialogContent size={"fullMd"}>
<DialogHeader>
<DialogTitle>Dialog Title</DialogTitle>
<DialogSubtitle>Dialog Subtitle</DialogSubtitle>
</DialogHeader>
<p>
Laborum non adipisicing enim enim culpa esse anim esse consequat
Lorem incididunt. Enim mollit laborum sunt cillum voluptate est
officia nostrud non consequat adipisicing cupidatat aliquip magna.
Voluptate nisi cupidatat qui nisi in pariatur. Sint consequat labore
pariatur mollit sint nostrud tempor commodo pariatur ea laboris.
</p>
<DialogFooter>
<DialogClose>
<Button preset="default">Close</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</DialogOverlay>
</DialogRoot>
);
}

View File

@ -1,40 +1,40 @@
import {
DrawerRoot,
DrawerTrigger,
DrawerOverlay,
DrawerContent,
DrawerHeader,
DrawerBody,
DrawerFooter,
DrawerClose,
} from "@pswui/Drawer";
import { Button } from "@pswui/Button";
export const Bottom = () => {
return (
<DrawerRoot>
<DrawerTrigger>
<Button>Open Drawer</Button>
</DrawerTrigger>
<DrawerOverlay className="z-[99]">
<DrawerContent position="bottom">
<DrawerHeader>
<h1 className="text-2xl font-bold">Drawer</h1>
</DrawerHeader>
<DrawerBody>
<p>
Drawers are a type of overlay that slides in from the edge of the
screen. They are typically used for navigation or additional
content.
</p>
</DrawerBody>
<DrawerFooter>
<DrawerClose>
<Button>Done</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</DrawerOverlay>
</DrawerRoot>
);
};
import { Button } from "@pswui/Button";
import {
DrawerBody,
DrawerClose,
DrawerContent,
DrawerFooter,
DrawerHeader,
DrawerOverlay,
DrawerRoot,
DrawerTrigger,
} from "@pswui/Drawer";
export const Bottom = () => {
return (
<DrawerRoot>
<DrawerTrigger>
<Button>Open Drawer</Button>
</DrawerTrigger>
<DrawerOverlay className="z-[99]">
<DrawerContent position="bottom">
<DrawerHeader>
<h1 className="text-2xl font-bold">Drawer</h1>
</DrawerHeader>
<DrawerBody>
<p>
Drawers are a type of overlay that slides in from the edge of the
screen. They are typically used for navigation or additional
content.
</p>
</DrawerBody>
<DrawerFooter>
<DrawerClose>
<Button>Done</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</DrawerOverlay>
</DrawerRoot>
);
};

View File

@ -1,40 +1,43 @@
import {
DrawerRoot,
DrawerTrigger,
DrawerOverlay,
DrawerContent,
DrawerHeader,
DrawerBody,
DrawerFooter,
DrawerClose,
} from "@pswui/Drawer";
import { Button } from "@pswui/Button";
export const Left = () => {
return (
<DrawerRoot>
<DrawerTrigger>
<Button>Open Drawer</Button>
</DrawerTrigger>
<DrawerOverlay className="z-[99]">
<DrawerContent position="left" className="max-w-[320px]">
<DrawerHeader>
<h1 className="text-2xl font-bold">Drawer</h1>
</DrawerHeader>
<DrawerBody>
<p>
Drawers are a type of overlay that slides in from the edge of the
screen. They are typically used for navigation or additional
content.
</p>
</DrawerBody>
<DrawerFooter>
<DrawerClose>
<Button>Done</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</DrawerOverlay>
</DrawerRoot>
);
};
import { Button } from "@pswui/Button";
import {
DrawerBody,
DrawerClose,
DrawerContent,
DrawerFooter,
DrawerHeader,
DrawerOverlay,
DrawerRoot,
DrawerTrigger,
} from "@pswui/Drawer";
export const Left = () => {
return (
<DrawerRoot>
<DrawerTrigger>
<Button>Open Drawer</Button>
</DrawerTrigger>
<DrawerOverlay className="z-[99]">
<DrawerContent
position="left"
className="max-w-[320px]"
>
<DrawerHeader>
<h1 className="text-2xl font-bold">Drawer</h1>
</DrawerHeader>
<DrawerBody>
<p>
Drawers are a type of overlay that slides in from the edge of the
screen. They are typically used for navigation or additional
content.
</p>
</DrawerBody>
<DrawerFooter>
<DrawerClose>
<Button>Done</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</DrawerOverlay>
</DrawerRoot>
);
};

View File

@ -1,40 +1,43 @@
import {
DrawerRoot,
DrawerTrigger,
DrawerOverlay,
DrawerContent,
DrawerHeader,
DrawerBody,
DrawerFooter,
DrawerClose,
} from "@pswui/Drawer";
import { Button } from "@pswui/Button";
export const Right = () => {
return (
<DrawerRoot>
<DrawerTrigger>
<Button>Open Drawer</Button>
</DrawerTrigger>
<DrawerOverlay className="z-[99]">
<DrawerContent position="right" className="max-w-[320px]">
<DrawerHeader>
<h1 className="text-2xl font-bold">Drawer</h1>
</DrawerHeader>
<DrawerBody>
<p>
Drawers are a type of overlay that slides in from the edge of the
screen. They are typically used for navigation or additional
content.
</p>
</DrawerBody>
<DrawerFooter>
<DrawerClose>
<Button>Done</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</DrawerOverlay>
</DrawerRoot>
);
};
import { Button } from "@pswui/Button";
import {
DrawerBody,
DrawerClose,
DrawerContent,
DrawerFooter,
DrawerHeader,
DrawerOverlay,
DrawerRoot,
DrawerTrigger,
} from "@pswui/Drawer";
export const Right = () => {
return (
<DrawerRoot>
<DrawerTrigger>
<Button>Open Drawer</Button>
</DrawerTrigger>
<DrawerOverlay className="z-[99]">
<DrawerContent
position="right"
className="max-w-[320px]"
>
<DrawerHeader>
<h1 className="text-2xl font-bold">Drawer</h1>
</DrawerHeader>
<DrawerBody>
<p>
Drawers are a type of overlay that slides in from the edge of the
screen. They are typically used for navigation or additional
content.
</p>
</DrawerBody>
<DrawerFooter>
<DrawerClose>
<Button>Done</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</DrawerOverlay>
</DrawerRoot>
);
};

View File

@ -1,40 +1,40 @@
import {
DrawerRoot,
DrawerTrigger,
DrawerOverlay,
DrawerContent,
DrawerHeader,
DrawerBody,
DrawerFooter,
DrawerClose,
} from "@pswui/Drawer";
import { Button } from "@pswui/Button";
export const Top = () => {
return (
<DrawerRoot>
<DrawerTrigger>
<Button>Open Drawer</Button>
</DrawerTrigger>
<DrawerOverlay className="z-[99]">
<DrawerContent position="top">
<DrawerHeader>
<h1 className="text-2xl font-bold">Drawer</h1>
</DrawerHeader>
<DrawerBody>
<p>
Drawers are a type of overlay that slides in from the edge of the
screen. They are typically used for navigation or additional
content.
</p>
</DrawerBody>
<DrawerFooter>
<DrawerClose>
<Button>Done</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</DrawerOverlay>
</DrawerRoot>
);
};
import { Button } from "@pswui/Button";
import {
DrawerBody,
DrawerClose,
DrawerContent,
DrawerFooter,
DrawerHeader,
DrawerOverlay,
DrawerRoot,
DrawerTrigger,
} from "@pswui/Drawer";
export const Top = () => {
return (
<DrawerRoot>
<DrawerTrigger>
<Button>Open Drawer</Button>
</DrawerTrigger>
<DrawerOverlay className="z-[99]">
<DrawerContent position="top">
<DrawerHeader>
<h1 className="text-2xl font-bold">Drawer</h1>
</DrawerHeader>
<DrawerBody>
<p>
Drawers are a type of overlay that slides in from the edge of the
screen. They are typically used for navigation or additional
content.
</p>
</DrawerBody>
<DrawerFooter>
<DrawerClose>
<Button>Done</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</DrawerOverlay>
</DrawerRoot>
);
};

View File

@ -1,7 +1,6 @@
import { Left } from "./Left";
import { Right } from "./Right";
import { Top } from "./Top";
import { Bottom } from "./Bottom";
export default { Left, Right, Top, Bottom };
import { Bottom } from "./Bottom";
import { Left } from "./Left";
import { Right } from "./Right";
import { Top } from "./Top";
export default { Left, Right, Top, Bottom };

View File

@ -1,40 +1,40 @@
import { Button } from "@pswui/Button";
import {
DrawerRoot,
DrawerTrigger,
DrawerOverlay,
DrawerContent,
DrawerClose,
DrawerHeader,
DrawerBody,
DrawerFooter,
} from "@pswui/Drawer";
export const DrawerDemo = () => {
return (
<DrawerRoot>
<DrawerTrigger>
<Button>Open Drawer</Button>
</DrawerTrigger>
<DrawerOverlay className="z-[99]">
<DrawerContent className="max-w-[320px]">
<DrawerHeader>
<h1 className="text-2xl font-bold">Drawer</h1>
</DrawerHeader>
<DrawerBody>
<p>
Drawers are a type of overlay that slides in from the edge of the
screen. They are typically used for navigation or additional
content.
</p>
</DrawerBody>
<DrawerFooter>
<DrawerClose>
<Button>Close</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</DrawerOverlay>
</DrawerRoot>
);
};
import { Button } from "@pswui/Button";
import {
DrawerBody,
DrawerClose,
DrawerContent,
DrawerFooter,
DrawerHeader,
DrawerOverlay,
DrawerRoot,
DrawerTrigger,
} from "@pswui/Drawer";
export const DrawerDemo = () => {
return (
<DrawerRoot>
<DrawerTrigger>
<Button>Open Drawer</Button>
</DrawerTrigger>
<DrawerOverlay className="z-[99]">
<DrawerContent className="max-w-[320px]">
<DrawerHeader>
<h1 className="text-2xl font-bold">Drawer</h1>
</DrawerHeader>
<DrawerBody>
<p>
Drawers are a type of overlay that slides in from the edge of the
screen. They are typically used for navigation or additional
content.
</p>
</DrawerBody>
<DrawerFooter>
<DrawerClose>
<Button>Close</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</DrawerOverlay>
</DrawerRoot>
);
};

View File

@ -1,5 +1,10 @@
import { Input } from "@pswui/Input";
export function Disabled() {
return <Input type="text" disabled />;
}
import { Input } from "@pswui/Input";
export function Disabled() {
return (
<Input
type="text"
disabled
/>
);
}

View File

@ -1,5 +1,10 @@
import { Input } from "@pswui/Input";
export function Invalid() {
return <Input type="text" invalid="Invalid Input" />;
}
import { Input } from "@pswui/Input";
export function Invalid() {
return (
<Input
type="text"
invalid="Invalid Input"
/>
);
}

View File

@ -1,5 +1,4 @@
import { Invalid } from "./Invalid";
import { Disabled } from "./Disabled";
export default { Invalid, Disabled };
import { Disabled } from "./Disabled";
import { Invalid } from "./Invalid";
export default { Invalid, Disabled };

View File

@ -1,5 +1,5 @@
import { Input } from "@pswui/Input";
export function InputDemo() {
return <Input type="text" />;
}
import { Input } from "@pswui/Input";
export function InputDemo() {
return <Input type="text" />;
}

View File

@ -1,23 +1,30 @@
import { Button } from "@pswui/Button";
import { Input, InputFrame } from "@pswui/Input";
export function InputFrameDemo() {
return (
<InputFrame>
<Input type="text" unstyled />
<Button preset="success" size="icon">
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5l-1.5 1.5l-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16A6.5 6.5 0 0 1 3 9.5A6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14S14 12 14 9.5S12 5 9.5 5"
/>
</svg>
</Button>
</InputFrame>
);
}
import { Button } from "@pswui/Button";
import { Input, InputFrame } from "@pswui/Input";
export function InputFrameDemo() {
return (
<InputFrame>
<Input
type="text"
unstyled
/>
<Button
preset="success"
size="icon"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
>
<title>Search</title>
<path
fill="currentColor"
d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5l-1.5 1.5l-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16A6.5 6.5 0 0 1 3 9.5A6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14S14 12 14 9.5S12 5 9.5 5"
/>
</svg>
</Button>
</InputFrame>
);
}

View File

@ -1,11 +1,14 @@
import { Label } from "@pswui/Label";
import { Input } from "@pswui/Input";
export function Disabled() {
return (
<Label direction="vertical">
<span>Email</span>
<Input type="email" disabled />
</Label>
);
}
import { Input } from "@pswui/Input";
import { Label } from "@pswui/Label";
export function Disabled() {
return (
<Label direction="vertical">
<span>Email</span>
<Input
type="email"
disabled
/>
</Label>
);
}

View File

@ -1,11 +1,11 @@
import { Label } from "@pswui/Label";
import { Checkbox } from "@pswui/Checkbox";
export function Horizontal() {
return (
<Label direction="horizontal">
<Checkbox />
<span>Checkbox</span>
</Label>
);
}
import { Checkbox } from "@pswui/Checkbox";
import { Label } from "@pswui/Label";
export function Horizontal() {
return (
<Label direction="horizontal">
<Checkbox />
<span>Checkbox</span>
</Label>
);
}

View File

@ -1,11 +1,14 @@
import { Label } from "@pswui/Label";
import { Input } from "@pswui/Input";
export function Invalid() {
return (
<Label direction="vertical">
<span>Email</span>
<Input type="email" invalid="Invalid Email" />
</Label>
);
}
import { Input } from "@pswui/Input";
import { Label } from "@pswui/Label";
export function Invalid() {
return (
<Label direction="vertical">
<span>Email</span>
<Input
type="email"
invalid="Invalid Email"
/>
</Label>
);
}

View File

@ -1,11 +1,11 @@
import { Label } from "@pswui/Label";
import { Input } from "@pswui/Input";
export function Vertical() {
return (
<Label direction="vertical">
<span>Email</span>
<Input type="email" />
</Label>
);
}
import { Input } from "@pswui/Input";
import { Label } from "@pswui/Label";
export function Vertical() {
return (
<Label direction="vertical">
<span>Email</span>
<Input type="email" />
</Label>
);
}

View File

@ -1,12 +1,11 @@
import { Vertical } from "./Vertical";
import { Horizontal } from "./Horizontal";
import { Invalid } from "./Invalid";
import { Disabled } from "./Disabled";
export default {
Vertical,
Horizontal,
Invalid,
Disabled,
};
import { Disabled } from "./Disabled";
import { Horizontal } from "./Horizontal";
import { Invalid } from "./Invalid";
import { Vertical } from "./Vertical";
export default {
Vertical,
Horizontal,
Invalid,
Disabled,
};

View File

@ -1,11 +1,11 @@
import { Label } from "@pswui/Label";
import { Input } from "@pswui/Input";
export function LabelDemo() {
return (
<Label direction="vertical">
<span>Email</span>
<Input type="email" />
</Label>
);
}
import { Input } from "@pswui/Input";
import { Label } from "@pswui/Label";
export function LabelDemo() {
return (
<Label direction="vertical">
<span>Email</span>
<Input type="email" />
</Label>
);
}

View File

@ -1,43 +1,74 @@
import { Popover, PopoverTrigger, PopoverContent } from "@pswui/Popover.tsx";
import { Button } from "@pswui/Button.tsx";
import { useState } from "react";
const DarkIcon = () => {
// ic:baseline-dark-mode
return <svg xmlns="http://www.w3.org/2000/svg" width="1.2em" height="1.2em" viewBox="0 0 24 24">
<path fill="currentColor"
d="M12 3a9 9 0 1 0 9 9c0-.46-.04-.92-.1-1.36a5.389 5.389 0 0 1-4.4 2.26a5.403 5.403 0 0 1-3.14-9.8c-.44-.06-.9-.1-1.36-.1"/>
</svg>
}
const LightIcon = () => {
// ic:baseline-light-mode
return <svg xmlns="http://www.w3.org/2000/svg" width="1.2em" height="1.2em" viewBox="0 0 24 24">
<path fill="currentColor"
d="M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5s5-2.24 5-5s-2.24-5-5-5M2 13h2c.55 0 1-.45 1-1s-.45-1-1-1H2c-.55 0-1 .45-1 1s.45 1 1 1m18 0h2c.55 0 1-.45 1-1s-.45-1-1-1h-2c-.55 0-1 .45-1 1s.45 1 1 1M11 2v2c0 .55.45 1 1 1s1-.45 1-1V2c0-.55-.45-1-1-1s-1 .45-1 1m0 18v2c0 .55.45 1 1 1s1-.45 1-1v-2c0-.55-.45-1-1-1s-1 .45-1 1M5.99 4.58a.996.996 0 0 0-1.41 0a.996.996 0 0 0 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0s.39-1.03 0-1.41zm12.37 12.37a.996.996 0 0 0-1.41 0a.996.996 0 0 0 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0a.996.996 0 0 0 0-1.41zm1.06-10.96a.996.996 0 0 0 0-1.41a.996.996 0 0 0-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0zM7.05 18.36a.996.996 0 0 0 0-1.41a.996.996 0 0 0-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0z"/>
</svg>
}
export const ThemeSelector = () => {
const [theme, setTheme] = useState<"light" | "dark">("dark");
return <Popover>
<PopoverTrigger>
<Button preset={"default"} size={"icon"}>
{
theme === "light" ? <LightIcon /> : <DarkIcon />
}
</Button>
</PopoverTrigger>
<PopoverContent anchor={"bottomCenter"}>
<Button onClick={() => setTheme("dark")} preset={"ghost"} className={"gap-2"}>
<DarkIcon />
<span className={"whitespace-nowrap"}>Dark Mode</span>
</Button>
<Button onClick={() => setTheme("light")} preset={"ghost"} className={"gap-2"}>
<LightIcon />
<span className={"whitespace-nowrap"}>Light Mode</span>
</Button>
</PopoverContent>
</Popover>
}
import { Button } from "@pswui/Button.tsx";
import { Popover, PopoverContent, PopoverTrigger } from "@pswui/Popover.tsx";
import { useState } from "react";
const DarkIcon = () => {
// ic:baseline-dark-mode
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
>
<title>Dark</title>
<path
fill="currentColor"
d="M12 3a9 9 0 1 0 9 9c0-.46-.04-.92-.1-1.36a5.389 5.389 0 0 1-4.4 2.26a5.403 5.403 0 0 1-3.14-9.8c-.44-.06-.9-.1-1.36-.1"
/>
</svg>
);
};
const LightIcon = () => {
// ic:baseline-light-mode
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
>
<title>Light</title>
<path
fill="currentColor"
d="M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5s5-2.24 5-5s-2.24-5-5-5M2 13h2c.55 0 1-.45 1-1s-.45-1-1-1H2c-.55 0-1 .45-1 1s.45 1 1 1m18 0h2c.55 0 1-.45 1-1s-.45-1-1-1h-2c-.55 0-1 .45-1 1s.45 1 1 1M11 2v2c0 .55.45 1 1 1s1-.45 1-1V2c0-.55-.45-1-1-1s-1 .45-1 1m0 18v2c0 .55.45 1 1 1s1-.45 1-1v-2c0-.55-.45-1-1-1s-1 .45-1 1M5.99 4.58a.996.996 0 0 0-1.41 0a.996.996 0 0 0 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0s.39-1.03 0-1.41zm12.37 12.37a.996.996 0 0 0-1.41 0a.996.996 0 0 0 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0a.996.996 0 0 0 0-1.41zm1.06-10.96a.996.996 0 0 0 0-1.41a.996.996 0 0 0-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0zM7.05 18.36a.996.996 0 0 0 0-1.41a.996.996 0 0 0-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0z"
/>
</svg>
);
};
export const ThemeSelector = () => {
const [theme, setTheme] = useState<"light" | "dark">("dark");
return (
<Popover>
<PopoverTrigger>
<Button
preset={"default"}
size={"icon"}
>
{theme === "light" ? <LightIcon /> : <DarkIcon />}
</Button>
</PopoverTrigger>
<PopoverContent anchor={"bottomCenter"}>
<Button
onClick={() => setTheme("dark")}
preset={"ghost"}
className={"gap-2"}
>
<DarkIcon />
<span className={"whitespace-nowrap"}>Dark Mode</span>
</Button>
<Button
onClick={() => setTheme("light")}
preset={"ghost"}
className={"gap-2"}
>
<LightIcon />
<span className={"whitespace-nowrap"}>Light Mode</span>
</Button>
</PopoverContent>
</Popover>
);
};

View File

@ -1,151 +1,157 @@
import {
Popover,
PopoverTrigger,
PopoverContent,
} from "@pswui/Popover.tsx";
import { Button } from "@pswui/Button.tsx";
import { useToast } from "@pswui/Toast";
import {
createContext,
Dispatch,
SetStateAction,
SVGProps,
useContext,
useState,
useTransition,
} from "react";
import { Label } from "@pswui/Label.tsx";
import { Input } from "@pswui/Input.tsx";
interface UserControlState {
signIn: boolean;
}
const initialState: UserControlState = {
signIn: false,
};
const UserControlContext = createContext<
[UserControlState, Dispatch<SetStateAction<UserControlState>>]
>([initialState, () => {}]);
const logInServerAction = async () => {
return new Promise((r) => setTimeout(r, 2000));
};
const logOutServerAction = async () => {
return new Promise((r) => setTimeout(r, 1000));
};
function MdiLoading(props: SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
{...props}
>
<path
fill="currentColor"
d="M12 4V2A10 10 0 0 0 2 12h2a8 8 0 0 1 8-8"
></path>
</svg>
);
}
const SignInForm = () => {
const [isSigningIn, setIsSigningIn] = useState(false);
const transition = useTransition();
const [_, setState] = useContext(UserControlContext);
const { toast } = useToast();
function startSignIn() {
transition[1](() => {
setIsSigningIn(true);
const toasted = toast({
title: "Logging In...",
description: "Please wait until server responses",
status: "loading",
});
logInServerAction().then(() => {
toasted.update({
title: "Log In Success",
description: "Successfully logged in!",
status: "success",
});
setIsSigningIn(false);
setState((prev) => ({ ...prev, signIn: true }));
});
});
}
return (
<PopoverContent anchor={"bottomLeft"} className={"p-4 space-y-3"}>
<Label>
<span>Username</span>
<Input type={"text"} />
</Label>
<Label>
<span>Password</span>
<Input type={"password"} />
</Label>
<div className={"flex flex-row justify-end"}>
<Button preset={"success"} onClick={startSignIn}>
{isSigningIn ? <MdiLoading className={"animate-spin"} /> : "Sign In"}
</Button>
</div>
</PopoverContent>
);
};
const UserControlContent = () => {
const [isSigningOut, setIsSigningOut] = useState(false);
const transition = useTransition();
const [_, setState] = useContext(UserControlContext);
const { toast } = useToast();
function startSignOut() {
transition[1](() => {
setIsSigningOut(true);
const toasted = toast({
title: "Logging Out",
description: "Please wait until server responses",
status: "loading",
});
logOutServerAction().then(() => {
toasted.update({
title: "Log Out Success",
description: "Successfully logged out!",
status: "success",
});
setIsSigningOut(false);
setState((prev) => ({ ...prev, signIn: false }));
});
});
}
return (
<PopoverContent anchor={"bottomLeft"}>
<Button preset={"ghost"}>Dashboard</Button>
<Button preset={"ghost"} onClick={startSignOut}>
{isSigningOut ? <MdiLoading className={"animate-spin"} /> : "Sign Out"}
</Button>
</PopoverContent>
);
};
export const UserControl = () => {
const [state, setState] = useState<UserControlState>({
signIn: false,
});
return (
<Popover>
<PopoverTrigger>
<Button>{state.signIn ? "Log Out" : "Log In"}</Button>
</PopoverTrigger>
<UserControlContext.Provider value={[state, setState]}>
{state.signIn ? <UserControlContent /> : <SignInForm />}
</UserControlContext.Provider>
</Popover>
);
};
import { Button } from "@pswui/Button.tsx";
import { Input } from "@pswui/Input.tsx";
import { Label } from "@pswui/Label.tsx";
import { Popover, PopoverContent, PopoverTrigger } from "@pswui/Popover.tsx";
import { useToast } from "@pswui/Toast";
import {
type Dispatch,
type SVGProps,
type SetStateAction,
createContext,
useContext,
useState,
useTransition,
} from "react";
interface UserControlState {
signIn: boolean;
}
const initialState: UserControlState = {
signIn: false,
};
const UserControlContext = createContext<
[UserControlState, Dispatch<SetStateAction<UserControlState>>]
>([initialState, () => {}]);
const logInServerAction = async () => {
return new Promise((r) => setTimeout(r, 2000));
};
const logOutServerAction = async () => {
return new Promise((r) => setTimeout(r, 1000));
};
function MdiLoading(props: SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
{...props}
>
<title>Loading</title>
<path
fill="currentColor"
d="M12 4V2A10 10 0 0 0 2 12h2a8 8 0 0 1 8-8"
/>
</svg>
);
}
const SignInForm = () => {
const [isSigningIn, setIsSigningIn] = useState(false);
const transition = useTransition();
const [_, setState] = useContext(UserControlContext);
const { toast } = useToast();
function startSignIn() {
transition[1](() => {
setIsSigningIn(true);
const toasted = toast({
title: "Logging In...",
description: "Please wait until server responses",
status: "loading",
});
logInServerAction().then(() => {
toasted.update({
title: "Log In Success",
description: "Successfully logged in!",
status: "success",
});
setIsSigningIn(false);
setState((prev) => ({ ...prev, signIn: true }));
});
});
}
return (
<PopoverContent
anchor={"bottomLeft"}
className={"p-4 space-y-3"}
>
<Label>
<span>Username</span>
<Input type={"text"} />
</Label>
<Label>
<span>Password</span>
<Input type={"password"} />
</Label>
<div className={"flex flex-row justify-end"}>
<Button
preset={"success"}
onClick={startSignIn}
>
{isSigningIn ? <MdiLoading className={"animate-spin"} /> : "Sign In"}
</Button>
</div>
</PopoverContent>
);
};
const UserControlContent = () => {
const [isSigningOut, setIsSigningOut] = useState(false);
const transition = useTransition();
const [_, setState] = useContext(UserControlContext);
const { toast } = useToast();
function startSignOut() {
transition[1](() => {
setIsSigningOut(true);
const toasted = toast({
title: "Logging Out",
description: "Please wait until server responses",
status: "loading",
});
logOutServerAction().then(() => {
toasted.update({
title: "Log Out Success",
description: "Successfully logged out!",
status: "success",
});
setIsSigningOut(false);
setState((prev) => ({ ...prev, signIn: false }));
});
});
}
return (
<PopoverContent anchor={"bottomLeft"}>
<Button preset={"ghost"}>Dashboard</Button>
<Button
preset={"ghost"}
onClick={startSignOut}
>
{isSigningOut ? <MdiLoading className={"animate-spin"} /> : "Sign Out"}
</Button>
</PopoverContent>
);
};
export const UserControl = () => {
const [state, setState] = useState<UserControlState>({
signIn: false,
});
return (
<Popover>
<PopoverTrigger>
<Button>{state.signIn ? "Log Out" : "Log In"}</Button>
</PopoverTrigger>
<UserControlContext.Provider value={[state, setState]}>
{state.signIn ? <UserControlContent /> : <SignInForm />}
</UserControlContext.Provider>
</Popover>
);
};

View File

@ -1,7 +1,7 @@
import { ThemeSelector } from "./ThemeSelector";
import { UserControl } from "./UserControl";
export default {
ThemeSelector,
UserControl,
}
import { ThemeSelector } from "./ThemeSelector";
import { UserControl } from "./UserControl";
export default {
ThemeSelector,
UserControl,
};

View File

@ -1,54 +1,63 @@
import { Button } from "@pswui/Button";
import { Popover, PopoverContent, PopoverTrigger } from "@pswui/Popover";
export function PopoverDemo() {
return (
<Popover>
<PopoverTrigger>
<Button size="icon">
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M12 4a4 4 0 0 1 4 4a4 4 0 0 1-4 4a4 4 0 0 1-4-4a4 4 0 0 1 4-4m0 10c4.42 0 8 1.79 8 4v2H4v-2c0-2.21 3.58-4 8-4"
/>
</svg>
</Button>
</PopoverTrigger>
<PopoverContent>
<Button preset="ghost" className="gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"
/>
</svg>
<span className="flex-grow text-left">Dashboard</span>
</Button>
<Button preset="ghost" className="gap-2">
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
>
<path
fill="currentColor"
d="m17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.58L17 17l5-5M4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4z"
/>
</svg>
<span className="flex-grow text-left">Log out</span>
</Button>
</PopoverContent>
</Popover>
);
}
import { Button } from "@pswui/Button";
import { Popover, PopoverContent, PopoverTrigger } from "@pswui/Popover";
export function PopoverDemo() {
return (
<Popover>
<PopoverTrigger>
<Button size="icon">
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
>
<title>User</title>
<path
fill="currentColor"
d="M12 4a4 4 0 0 1 4 4a4 4 0 0 1-4 4a4 4 0 0 1-4-4a4 4 0 0 1 4-4m0 10c4.42 0 8 1.79 8 4v2H4v-2c0-2.21 3.58-4 8-4"
/>
</svg>
</Button>
</PopoverTrigger>
<PopoverContent>
<Button
preset="ghost"
className="gap-2"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
>
<title>Dashboard</title>
<path
fill="currentColor"
d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"
/>
</svg>
<span className="flex-grow text-left">Dashboard</span>
</Button>
<Button
preset="ghost"
className="gap-2"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="1.2em"
height="1.2em"
viewBox="0 0 24 24"
>
<title>Log out</title>
<path
fill="currentColor"
d="m17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.58L17 17l5-5M4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4z"
/>
</svg>
<span className="flex-grow text-left">Log out</span>
</Button>
</PopoverContent>
</Popover>
);
}

View File

@ -1,5 +1,5 @@
import { Switch } from "@pswui/Switch";
export function SwitchDemo() {
return <Switch />;
}
import { Switch } from "@pswui/Switch";
export function SwitchDemo() {
return <Switch />;
}

View File

@ -1,18 +1,18 @@
import { TabProvider, TabTrigger, TabContent, TabList } from "@pswui/Tabs";
export function TabsDemo() {
return (
<TabProvider defaultName="tab1">
<TabList>
<TabTrigger name="tab1">Tab 1</TabTrigger>
<TabTrigger name="tab2">Tab 2</TabTrigger>
</TabList>
<TabContent name="tab1">
<div className="w-full text-center">Tab 1 Content</div>
</TabContent>
<TabContent name="tab2">
<div className="w-full text-center">Tab 2 Content</div>
</TabContent>
</TabProvider>
);
}
import { TabContent, TabList, TabProvider, TabTrigger } from "@pswui/Tabs";
export function TabsDemo() {
return (
<TabProvider defaultName="tab1">
<TabList>
<TabTrigger name="tab1">Tab 1</TabTrigger>
<TabTrigger name="tab2">Tab 2</TabTrigger>
</TabList>
<TabContent name="tab1">
<div className="w-full text-center">Tab 1 Content</div>
</TabContent>
<TabContent name="tab2">
<div className="w-full text-center">Tab 2 Content</div>
</TabContent>
</TabProvider>
);
}

View File

@ -1,29 +1,30 @@
import { Button } from "@pswui/Button";
import { Toaster, useToast } from "@pswui/Toast";
function Children() {
const { toast } = useToast();
return (
<Button
onClick={() =>
toast({
title: "Toast Title",
description: "Toast Description",
status: "error",
})
}
>
Toast
</Button>
);
}
export function Error() {
return (
<>
<Toaster />
<Children />
</>
);
}
import { Button } from "@pswui/Button";
import { Toaster, useToast } from "@pswui/Toast";
function Children() {
const { toast } = useToast();
return (
<Button
onClick={() =>
toast({
title: "Toast Title",
description: "Toast Description",
status: "error",
})
}
>
Toast
</Button>
);
}
/* not shadowing global Error (lol) */
export function _Error() {
return (
<>
<Toaster />
<Children />
</>
);
}

View File

@ -1,29 +1,29 @@
import { Button } from "@pswui/Button";
import { Toaster, useToast } from "@pswui/Toast";
function Children() {
const { toast } = useToast();
return (
<Button
onClick={() =>
toast({
title: "Toast Title",
description: "Toast Description",
status: "default",
})
}
>
Toast
</Button>
);
}
export function Normal() {
return (
<>
<Toaster />
<Children />
</>
);
}
import { Button } from "@pswui/Button";
import { Toaster, useToast } from "@pswui/Toast";
function Children() {
const { toast } = useToast();
return (
<Button
onClick={() =>
toast({
title: "Toast Title",
description: "Toast Description",
status: "default",
})
}
>
Toast
</Button>
);
}
export function Normal() {
return (
<>
<Toaster />
<Children />
</>
);
}

View File

@ -1,35 +1,35 @@
import { Button } from "@pswui/Button";
import { Toaster, useToast } from "@pswui/Toast";
function Children() {
const { toast } = useToast();
return (
<Button
onClick={async () => {
const toasted = toast({
title: "Waiting...",
description: "Waiting for result...",
status: "loading",
});
await new Promise((r) => setTimeout(r, 1000));
toasted.update({
title: "Promise Failed",
description: "Something went wrong!",
status: "error",
});
}}
>
Toast
</Button>
);
}
export function PendingFail() {
return (
<>
<Toaster />
<Children />
</>
);
}
import { Button } from "@pswui/Button";
import { Toaster, useToast } from "@pswui/Toast";
function Children() {
const { toast } = useToast();
return (
<Button
onClick={async () => {
const toasted = toast({
title: "Waiting...",
description: "Waiting for result...",
status: "loading",
});
await new Promise((r) => setTimeout(r, 1000));
toasted.update({
title: "Promise Failed",
description: "Something went wrong!",
status: "error",
});
}}
>
Toast
</Button>
);
}
export function PendingFail() {
return (
<>
<Toaster />
<Children />
</>
);
}

View File

@ -1,37 +1,37 @@
import { Button } from "@pswui/Button";
import { Toaster, useToast } from "@pswui/Toast";
function Children() {
const { toast } = useToast();
return (
<Button
onClick={async () => {
const toasted = toast({
title: "Waiting...",
description: "Waiting for result...",
status: "loading",
});
await new Promise((r) => setTimeout(r, 1000));
toasted.update({
title: "Promise Success",
description: "Promise resolved!",
status: "success",
});
}}
>
Toast
</Button>
);
}
export function PendingSuccess() {
return (
<>
<Toaster />
<Children />
</>
);
}
import { Button } from "@pswui/Button";
import { Toaster, useToast } from "@pswui/Toast";
function Children() {
const { toast } = useToast();
return (
<Button
onClick={async () => {
const toasted = toast({
title: "Waiting...",
description: "Waiting for result...",
status: "loading",
});
await new Promise((r) => setTimeout(r, 1000));
toasted.update({
title: "Promise Success",
description: "Promise resolved!",
status: "success",
});
}}
>
Toast
</Button>
);
}
export function PendingSuccess() {
return (
<>
<Toaster />
<Children />
</>
);
}

View File

@ -1,29 +1,29 @@
import { Button } from "@pswui/Button";
import { Toaster, useToast } from "@pswui/Toast";
function Children() {
const { toast } = useToast();
return (
<Button
onClick={() =>
toast({
title: "Toast Title",
description: "Toast Description",
status: "success",
})
}
>
Toast
</Button>
);
}
export function Success() {
return (
<>
<Toaster />
<Children />
</>
);
}
import { Button } from "@pswui/Button";
import { Toaster, useToast } from "@pswui/Toast";
function Children() {
const { toast } = useToast();
return (
<Button
onClick={() =>
toast({
title: "Toast Title",
description: "Toast Description",
status: "success",
})
}
>
Toast
</Button>
);
}
export function Success() {
return (
<>
<Toaster />
<Children />
</>
);
}

View File

@ -1,29 +1,29 @@
import { Button } from "@pswui/Button";
import { Toaster, useToast } from "@pswui/Toast";
function Children() {
const { toast } = useToast();
return (
<Button
onClick={() =>
toast({
title: "Toast Title",
description: "Toast Description",
status: "warning",
})
}
>
Toast
</Button>
);
}
export function Warning() {
return (
<>
<Toaster />
<Children />
</>
);
}
import { Button } from "@pswui/Button";
import { Toaster, useToast } from "@pswui/Toast";
function Children() {
const { toast } = useToast();
return (
<Button
onClick={() =>
toast({
title: "Toast Title",
description: "Toast Description",
status: "warning",
})
}
>
Toast
</Button>
);
}
export function Warning() {
return (
<>
<Toaster />
<Children />
</>
);
}

View File

@ -1,16 +1,15 @@
import { Error } from "./Error";
import { Normal } from "./Normal";
import { PendingFail } from "./PendingFail";
import { PendingSuccess } from "./PendingSuccess";
import { Success } from "./Success";
import { Warning } from "./Warning";
export default {
Error,
Normal,
PendingFail,
PendingSuccess,
Success,
Warning,
};
import { _Error } from "./Error";
import { Normal } from "./Normal";
import { PendingFail } from "./PendingFail";
import { PendingSuccess } from "./PendingSuccess";
import { Success } from "./Success";
import { Warning } from "./Warning";
export default {
Error: _Error /* not shadowing global Error (lol) */,
Normal,
PendingFail,
PendingSuccess,
Success,
Warning,
};

View File

@ -1,25 +1,25 @@
import { Button } from "@pswui/Button";
import { Toaster, useToast } from "@pswui/Toast";
function Children() {
const { toast } = useToast();
return (
<Button
onClick={() =>
toast({ title: "Toast Title", description: "Toast Description" })
}
>
Toast
</Button>
);
}
export function ToastDemo() {
return (
<>
<Toaster />
<Children />
</>
);
}
import { Button } from "@pswui/Button";
import { Toaster, useToast } from "@pswui/Toast";
function Children() {
const { toast } = useToast();
return (
<Button
onClick={() =>
toast({ title: "Toast Title", description: "Toast Description" })
}
>
Toast
</Button>
);
}
export function ToastDemo() {
return (
<>
<Toaster />
<Children />
</>
);
}

View File

@ -1,13 +1,13 @@
import { Button } from "@pswui/Button";
import { Tooltip, TooltipContent } from "@pswui/Tooltip";
export function Bottom() {
return (
<Tooltip position="bottom">
<TooltipContent>
<p>Tooltip!</p>
</TooltipContent>
<Button>Hover me</Button>
</Tooltip>
);
}
import { Button } from "@pswui/Button";
import { Tooltip, TooltipContent } from "@pswui/Tooltip";
export function Bottom() {
return (
<Tooltip position="bottom">
<TooltipContent>
<p>Tooltip!</p>
</TooltipContent>
<Button>Hover me</Button>
</Tooltip>
);
}

View File

@ -1,16 +1,20 @@
import { Button } from "@pswui/Button";
import { Tooltip, TooltipContent } from "@pswui/Tooltip";
import { useState } from "react";
export function Controlled() {
const [opened, setOpened] = useState(false);
return (
<Tooltip position="top" controlled opened={opened}>
<TooltipContent delay={"early"}>
<p>Tooltip!</p>
</TooltipContent>
<Button onClick={() => setOpened((p) => !p)}>Open hover</Button>
</Tooltip>
);
}
import { Button } from "@pswui/Button";
import { Tooltip, TooltipContent } from "@pswui/Tooltip";
import { useState } from "react";
export function Controlled() {
const [opened, setOpened] = useState(false);
return (
<Tooltip
position="top"
controlled
opened={opened}
>
<TooltipContent delay={"early"}>
<p>Tooltip!</p>
</TooltipContent>
<Button onClick={() => setOpened((p) => !p)}>Open hover</Button>
</Tooltip>
);
}

View File

@ -1,13 +1,13 @@
import { Button } from "@pswui/Button";
import { Tooltip, TooltipContent } from "@pswui/Tooltip";
export function EarlyDelay() {
return (
<Tooltip position="bottom">
<TooltipContent delay={"early"}>
<p>Tooltip!</p>
</TooltipContent>
<Button>Hover me</Button>
</Tooltip>
);
}
import { Button } from "@pswui/Button";
import { Tooltip, TooltipContent } from "@pswui/Tooltip";
export function EarlyDelay() {
return (
<Tooltip position="bottom">
<TooltipContent delay={"early"}>
<p>Tooltip!</p>
</TooltipContent>
<Button>Hover me</Button>
</Tooltip>
);
}

View File

@ -1,13 +1,13 @@
import { Button } from "@pswui/Button";
import { Tooltip, TooltipContent } from "@pswui/Tooltip";
export function LateDelay() {
return (
<Tooltip position="bottom">
<TooltipContent delay={"late"}>
<p>Tooltip!</p>
</TooltipContent>
<Button>Hover me</Button>
</Tooltip>
);
}
import { Button } from "@pswui/Button";
import { Tooltip, TooltipContent } from "@pswui/Tooltip";
export function LateDelay() {
return (
<Tooltip position="bottom">
<TooltipContent delay={"late"}>
<p>Tooltip!</p>
</TooltipContent>
<Button>Hover me</Button>
</Tooltip>
);
}

View File

@ -1,13 +1,13 @@
import { Button } from "@pswui/Button";
import { Tooltip, TooltipContent } from "@pswui/Tooltip";
export function Left() {
return (
<Tooltip position="left">
<TooltipContent>
<p>Tooltip!</p>
</TooltipContent>
<Button>Hover me</Button>
</Tooltip>
);
}
import { Button } from "@pswui/Button";
import { Tooltip, TooltipContent } from "@pswui/Tooltip";
export function Left() {
return (
<Tooltip position="left">
<TooltipContent>
<p>Tooltip!</p>
</TooltipContent>
<Button>Hover me</Button>
</Tooltip>
);
}

View File

@ -1,13 +1,13 @@
import { Button } from "@pswui/Button";
import { Tooltip, TooltipContent } from "@pswui/Tooltip";
export function NoDelay() {
return (
<Tooltip position="bottom">
<TooltipContent delay={"none"}>
<p>Tooltip!</p>
</TooltipContent>
<Button>Hover me</Button>
</Tooltip>
);
}
import { Button } from "@pswui/Button";
import { Tooltip, TooltipContent } from "@pswui/Tooltip";
export function NoDelay() {
return (
<Tooltip position="bottom">
<TooltipContent delay={"none"}>
<p>Tooltip!</p>
</TooltipContent>
<Button>Hover me</Button>
</Tooltip>
);
}

View File

@ -1,13 +1,13 @@
import { Button } from "@pswui/Button";
import { Tooltip, TooltipContent } from "@pswui/Tooltip";
export function Right() {
return (
<Tooltip position="right">
<TooltipContent>
<p>Tooltip!</p>
</TooltipContent>
<Button>Hover me</Button>
</Tooltip>
);
}
import { Button } from "@pswui/Button";
import { Tooltip, TooltipContent } from "@pswui/Tooltip";
export function Right() {
return (
<Tooltip position="right">
<TooltipContent>
<p>Tooltip!</p>
</TooltipContent>
<Button>Hover me</Button>
</Tooltip>
);
}

View File

@ -1,13 +1,13 @@
import { Button } from "@pswui/Button";
import { Tooltip, TooltipContent } from "@pswui/Tooltip";
export function Top() {
return (
<Tooltip position="top">
<TooltipContent>
<p>Tooltip!</p>
</TooltipContent>
<Button>Hover me</Button>
</Tooltip>
);
}
import { Button } from "@pswui/Button";
import { Tooltip, TooltipContent } from "@pswui/Tooltip";
export function Top() {
return (
<Tooltip position="top">
<TooltipContent>
<p>Tooltip!</p>
</TooltipContent>
<Button>Hover me</Button>
</Tooltip>
);
}

View File

@ -1,19 +1,19 @@
import { Bottom } from "./Bottom";
import { Left } from "./Left";
import { Right } from "./Right";
import { Top } from "./Top";
import { NoDelay } from "./NoDelay";
import { EarlyDelay } from "./EarlyDelay";
import { LateDelay } from "./LateDelay";
import { Controlled } from "./Controlled";
export default {
Bottom,
Left,
Right,
Top,
NoDelay,
EarlyDelay,
LateDelay,
Controlled,
};
import { Bottom } from "./Bottom";
import { Controlled } from "./Controlled";
import { EarlyDelay } from "./EarlyDelay";
import { LateDelay } from "./LateDelay";
import { Left } from "./Left";
import { NoDelay } from "./NoDelay";
import { Right } from "./Right";
import { Top } from "./Top";
export default {
Bottom,
Left,
Right,
Top,
NoDelay,
EarlyDelay,
LateDelay,
Controlled,
};

View File

@ -1,13 +1,13 @@
import { Button } from "@pswui/Button";
import { Tooltip, TooltipContent } from "@pswui/Tooltip";
export function TooltipDemo() {
return (
<Tooltip>
<TooltipContent>
<p>Tooltip!</p>
</TooltipContent>
<Button>Hover me</Button>
</Tooltip>
);
}
import { Button } from "@pswui/Button";
import { Tooltip, TooltipContent } from "@pswui/Tooltip";
export function TooltipDemo() {
return (
<Tooltip>
<TooltipContent>
<p>Tooltip!</p>
</TooltipContent>
<Button>Hover me</Button>
</Tooltip>
);
}

View File

@ -1,22 +1,25 @@
import { Link } from "react-router-dom";
import { Button } from "@pswui/Button";
function PageNotFound() {
return (
<main className="flex-grow h-full flex flex-col justify-center items-center gap-8">
<section className="flex flex-col justify-center items-center text-center gap-2">
<h1 className="text-4xl font-bold">Page not found</h1>
<p className="text-base">
The page you are looking for does not exist.
</p>
</section>
<section className="flex flex-row justify-center items-center text-center gap-2">
<Button asChild preset="default">
<Link to="/">Go home</Link>
</Button>
</section>
</main>
);
}
export default PageNotFound;
import { Button } from "@pswui/Button";
import { Link } from "react-router-dom";
function PageNotFound() {
return (
<main className="flex-grow h-full flex flex-col justify-center items-center gap-8">
<section className="flex flex-col justify-center items-center text-center gap-2">
<h1 className="text-4xl font-bold">Page not found</h1>
<p className="text-base">
The page you are looking for does not exist.
</p>
</section>
<section className="flex flex-row justify-center items-center text-center gap-2">
<Button
asChild
preset="default"
>
<Link to="/">Go home</Link>
</Button>
</section>
</main>
);
}
export default PageNotFound;

View File

@ -1,22 +1,25 @@
import { Link } from "react-router-dom";
import { Button } from "@pswui/Button";
function UnexpectedError() {
return (
<main className="flex-grow h-full flex flex-col justify-center items-center gap-8">
<section className="flex flex-col justify-center items-center text-center gap-2">
<h1 className="text-4xl font-bold">Something went wrong</h1>
<p className="text-base">
There was an unexpected error while loading the page.
</p>
</section>
<section className="flex flex-row justify-center items-center text-center gap-2">
<Button asChild preset="default">
<Link to="/">Go home</Link>
</Button>
</section>
</main>
);
}
export default UnexpectedError;
import { Button } from "@pswui/Button";
import { Link } from "react-router-dom";
function UnexpectedError() {
return (
<main className="flex-grow h-full flex flex-col justify-center items-center gap-8">
<section className="flex flex-col justify-center items-center text-center gap-2">
<h1 className="text-4xl font-bold">Something went wrong</h1>
<p className="text-base">
There was an unexpected error while loading the page.
</p>
</section>
<section className="flex flex-row justify-center items-center text-center gap-2">
<Button
asChild
preset="default"
>
<Link to="/">Go home</Link>
</Button>
</section>
</main>
);
}
export default UnexpectedError;

View File

@ -1,10 +1,14 @@
import "./tailwind.css";
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
import "./tailwind.css";
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
const ROOT = document.getElementById("root");
if (!ROOT) throw new Error("root is not found");
ReactDOM.createRoot(ROOT).render(
<React.StrictMode>
<App />
</React.StrictMode>,
);

14
src/mdx.d.ts vendored
View File

@ -1,7 +1,7 @@
declare module '*.mdx' {
import type { MDXProps } from 'mdx/types'
import type { Toc } from '@stefanprobst/rehype-extract-toc'
export const tableOfContents: Toc
export default function MDXContent(props: MDXProps): JSX.Element
}
declare module "*.mdx" {
import type { MDXProps } from "mdx/types";
import type { Toc } from "@stefanprobst/rehype-extract-toc";
export const tableOfContents: Toc;
export default function MDXContent(props: MDXProps): JSX.Element;
}

12
src/vite-env.d.ts vendored
View File

@ -1,6 +1,6 @@
/// <reference types="vite/client" />
import { ImportGlobFunction } from "vite/types/importGlob";
interface ImportMeta {
glob: ImportGlobFunction;
}
/// <reference types="vite/client" />
import type { ImportGlobFunction } from "vite/types/importGlob";
interface ImportMeta {
glob: ImportGlobFunction;
}

View File

@ -12,4 +12,4 @@ module.exports = {
extend: {},
},
plugins: [require("@tailwindcss/typography"), require("tailwind-scrollbar")],
};
};

View File

@ -30,4 +30,4 @@
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
}

View File

@ -8,4 +8,4 @@
"strict": true
},
"include": ["vite.config.ts"]
}
}

View File

@ -1,12 +1,12 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tailwindcss from "tailwindcss";
import mdx from "@mdx-js/rollup";
import { resolve } from "node:path";
import remarkGfm from "remark-gfm";
import withSlug from "rehype-slug";
import mdx from "@mdx-js/rollup";
import withToc from "@stefanprobst/rehype-extract-toc";
import withTocExport from "@stefanprobst/rehype-extract-toc/mdx";
import react from "@vitejs/plugin-react";
import withSlug from "rehype-slug";
import remarkGfm from "remark-gfm";
import tailwindcss from "tailwindcss";
import { defineConfig } from "vite";
import dynamicImport from "vite-plugin-dynamic-import";
// https://vitejs.dev/config/
@ -31,5 +31,5 @@ export default defineConfig({
"@pswui-lib": resolve(__dirname, "./src/pswui/lib"),
},
},
cacheDir: './.vite'
});
cacheDir: "./.vite",
});