feat: apply new playground in Popover documentation
This commit is contained in:
parent
cf331b93e2
commit
fb69f288a1
@ -1,6 +1,6 @@
|
||||
import { Button } from "@pswui/Button";
|
||||
import { useToast } from "@pswui/Toast";
|
||||
import { forwardRef, useEffect, useState } from "react";
|
||||
import { forwardRef, useEffect, useMemo, useState } from "react";
|
||||
import SyntaxHighlighter from "react-syntax-highlighter";
|
||||
import { gruvboxDark } from "react-syntax-highlighter/dist/cjs/styles/hljs";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
@ -16,12 +16,16 @@ export const GITHUB_COMP_PREVIEW = (componentName: string) =>
|
||||
export const GITHUB_STORY = (componentName: string, storyName: string) =>
|
||||
`${GITHUB_DOCS}/src/docs/components/${componentName}Blocks/Examples/${storyName}.tsx`;
|
||||
|
||||
export type TEMPLATE = Record<string, Record<string, string | boolean>>;
|
||||
|
||||
export const LoadedCode = ({
|
||||
from,
|
||||
className,
|
||||
template,
|
||||
}: {
|
||||
from: string;
|
||||
className?: string;
|
||||
template?: TEMPLATE;
|
||||
}) => {
|
||||
const [state, setState] = useState<string | undefined | null>();
|
||||
const { toast } = useToast();
|
||||
@ -34,6 +38,33 @@ export const LoadedCode = ({
|
||||
})();
|
||||
}, [from]);
|
||||
|
||||
const postProcessedCode = useMemo(() => {
|
||||
if (!state) return "";
|
||||
if (!template) return state;
|
||||
|
||||
let templatedCode = state;
|
||||
|
||||
for (const [componentName, componentTemplateProps] of Object.entries(
|
||||
template,
|
||||
)) {
|
||||
for (const [propName, propValue] of Object.entries(
|
||||
componentTemplateProps,
|
||||
)) {
|
||||
const regex = new RegExp(
|
||||
`(<${componentName}\s[^]*)\s${propName}=(\{(true|false|"[^"\n]*"|'[^'\n]*'|\`[^\`\n]*\`)\}|"[^"\n]*"|'[^'\n]*')`,
|
||||
);
|
||||
templatedCode = templatedCode.replace(
|
||||
regex,
|
||||
typeof propValue === "string"
|
||||
? `\$1 ${propName}="${propValue}"`
|
||||
: `$1 ${propName}={${propValue}}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return templatedCode;
|
||||
}, [state, template]);
|
||||
|
||||
return (
|
||||
<div className={twMerge("relative", className)}>
|
||||
<Button
|
||||
@ -76,7 +107,7 @@ export const LoadedCode = ({
|
||||
className={`w-full h-64 rounded-lg ${!state ? "animate-pulse" : ""} scrollbar-none`}
|
||||
customStyle={{ padding: "1rem" }}
|
||||
>
|
||||
{state ?? ""}
|
||||
{postProcessedCode}
|
||||
</SyntaxHighlighter>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,26 +1,15 @@
|
||||
import { TabProvider, TabTrigger, TabContent, TabList } from "@pswui/Tabs";
|
||||
import { Story } from "@/components/Story";
|
||||
import { LoadedCode, GITHUB_STORY, GITHUB_COMP, GITHUB_COMP_PREVIEW } from "@/components/LoadedCode";
|
||||
import { PopoverDemo } from "./PopoverBlocks/Preview";
|
||||
import { LoadedCode, GITHUB_STORY, GITHUB_COMP } from "@/components/LoadedCode";
|
||||
import Examples from "./PopoverBlocks/Examples";
|
||||
import PopoverPlayground from "./PopoverBlocks/Playground";
|
||||
|
||||
# Popover
|
||||
Displays rich content in a portal, triggered by a button.
|
||||
|
||||
<TabProvider defaultName="preview">
|
||||
<TabList>
|
||||
<TabTrigger name="preview">Preview</TabTrigger>
|
||||
<TabTrigger name="code">Code</TabTrigger>
|
||||
</TabList>
|
||||
<TabContent name="preview">
|
||||
<Story layout="centered">
|
||||
<PopoverDemo />
|
||||
</Story>
|
||||
</TabContent>
|
||||
<TabContent name="code">
|
||||
<LoadedCode from={GITHUB_COMP_PREVIEW("Popover")} />
|
||||
</TabContent>
|
||||
</TabProvider>
|
||||
## Playground
|
||||
|
||||
<PopoverPlayground />
|
||||
|
||||
## Installation
|
||||
|
||||
@ -60,10 +49,12 @@ import { Popover, PopoverTrigger, PopoverContent } from "@components/popover"
|
||||
|
||||
#### Special
|
||||
|
||||
| Prop | Type | Default | Description |
|
||||
|:----------|:----------|:--------|:------------------------------------------------------------------|
|
||||
| `opened` | `boolean` | `false` | Initial open state |
|
||||
| `asChild` | `boolean` | `false` | Whether the root of popover is rendered as a child of a component |
|
||||
| Prop | Type | Default | Description |
|
||||
|:----------|:------------------------|:------------|:------------------------------------------------------------------|
|
||||
| `opened` | `boolean \| undefined` | `undefined` | Opened state |
|
||||
| `asChild` | `boolean` | `false` | Whether the root of popover is rendered as a child of a component |
|
||||
|
||||
Note that giving `opened` prop not undefined will also sets the internal state `controlled` which disables internal popover open/close handling, like close on outside click.
|
||||
|
||||
### PopoverContent
|
||||
|
||||
|
328
src/docs/components/PopoverBlocks/Playground.tsx
Normal file
328
src/docs/components/PopoverBlocks/Playground.tsx
Normal file
@ -0,0 +1,328 @@
|
||||
import { GITHUB_COMP_PREVIEW, LoadedCode } from "@/components/LoadedCode.tsx";
|
||||
import { Story } from "@/components/Story";
|
||||
import useMutable from "@/utils/useMutable.ts";
|
||||
import { Button } from "@pswui/Button.tsx";
|
||||
import { Checkbox } from "@pswui/Checkbox.tsx";
|
||||
import { Label } from "@pswui/Label.tsx";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@pswui/Popover.tsx";
|
||||
import { TabContent, TabList, TabProvider, TabTrigger } from "@pswui/Tabs";
|
||||
import { type ControlledPopoverDemoProps, PopoverDemo } from "./Preview.tsx";
|
||||
|
||||
export default function PopoverPlayground() {
|
||||
const [props, mutate] = useMutable<ControlledPopoverDemoProps>({
|
||||
PopoverProps: {
|
||||
opened: false,
|
||||
},
|
||||
PopoverContentProps: {
|
||||
direction: "col",
|
||||
position: "end",
|
||||
anchor: "middle",
|
||||
align: "middle",
|
||||
offset: "md",
|
||||
className: "",
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<TabProvider defaultName="preview">
|
||||
<TabList>
|
||||
<TabTrigger name="preview">Preview</TabTrigger>
|
||||
<TabTrigger name="code">Code</TabTrigger>
|
||||
</TabList>
|
||||
<TabContent name="preview">
|
||||
<Story layout="centered">
|
||||
<PopoverDemo {...props} />
|
||||
</Story>
|
||||
</TabContent>
|
||||
<TabContent name="code">
|
||||
<LoadedCode from={GITHUB_COMP_PREVIEW("Popover")} />
|
||||
</TabContent>
|
||||
</TabProvider>
|
||||
<h3>Controls</h3>
|
||||
<div
|
||||
className={
|
||||
"rounded-lg p-4 border border-neutral-300 dark:border-neutral-700 flex flex-col justify-center items-start gap-2"
|
||||
}
|
||||
>
|
||||
<Label direction={"horizontal"}>
|
||||
<span>opened </span>
|
||||
<Checkbox
|
||||
checked={props.PopoverProps.opened}
|
||||
onChange={(e) => {
|
||||
const v = e.currentTarget.checked;
|
||||
mutate((p) => {
|
||||
p.PopoverProps.opened = v;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</Label>
|
||||
<Label direction={"horizontal"}>
|
||||
<span>direction = </span>
|
||||
<Popover>
|
||||
<PopoverTrigger>
|
||||
<Button className={"gap-2"}>
|
||||
{props.PopoverContentProps.direction}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<title>Expand</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M7.41 8.58L12 13.17l4.59-4.59L18 10l-6 6l-6-6z"
|
||||
/>
|
||||
</svg>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
anchor={"start"}
|
||||
align={"start"}
|
||||
>
|
||||
<Button
|
||||
preset={"ghost"}
|
||||
onClick={() =>
|
||||
mutate((p) => {
|
||||
p.PopoverContentProps.direction = "col";
|
||||
})
|
||||
}
|
||||
>
|
||||
Column
|
||||
</Button>
|
||||
<Button
|
||||
preset={"ghost"}
|
||||
onClick={() =>
|
||||
mutate((p) => {
|
||||
p.PopoverContentProps.direction = "row";
|
||||
})
|
||||
}
|
||||
>
|
||||
Row
|
||||
</Button>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</Label>
|
||||
<Label direction={"horizontal"}>
|
||||
<span>position = </span>
|
||||
<Popover>
|
||||
<PopoverTrigger>
|
||||
<Button className={"gap-2"}>
|
||||
{props.PopoverContentProps.position}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<title>Expand</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M7.41 8.58L12 13.17l4.59-4.59L18 10l-6 6l-6-6z"
|
||||
/>
|
||||
</svg>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
anchor={"start"}
|
||||
align={"start"}
|
||||
>
|
||||
<Button
|
||||
preset={"ghost"}
|
||||
onClick={() =>
|
||||
mutate((p) => {
|
||||
p.PopoverContentProps.position = "start";
|
||||
})
|
||||
}
|
||||
>
|
||||
Start
|
||||
</Button>
|
||||
<Button
|
||||
preset={"ghost"}
|
||||
onClick={() =>
|
||||
mutate((p) => {
|
||||
p.PopoverContentProps.position = "end";
|
||||
})
|
||||
}
|
||||
>
|
||||
End
|
||||
</Button>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</Label>
|
||||
<Label direction={"horizontal"}>
|
||||
<span>anchor = </span>
|
||||
<Popover>
|
||||
<PopoverTrigger>
|
||||
<Button className={"gap-2"}>
|
||||
{props.PopoverContentProps.anchor}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<title>Expand</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M7.41 8.58L12 13.17l4.59-4.59L18 10l-6 6l-6-6z"
|
||||
/>
|
||||
</svg>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
anchor={"start"}
|
||||
align={"start"}
|
||||
>
|
||||
<Button
|
||||
preset={"ghost"}
|
||||
onClick={() =>
|
||||
mutate((p) => {
|
||||
p.PopoverContentProps.anchor = "start";
|
||||
})
|
||||
}
|
||||
>
|
||||
Start
|
||||
</Button>
|
||||
<Button
|
||||
preset={"ghost"}
|
||||
onClick={() =>
|
||||
mutate((p) => {
|
||||
p.PopoverContentProps.anchor = "middle";
|
||||
})
|
||||
}
|
||||
>
|
||||
Middle
|
||||
</Button>
|
||||
<Button
|
||||
preset={"ghost"}
|
||||
onClick={() =>
|
||||
mutate((p) => {
|
||||
p.PopoverContentProps.anchor = "end";
|
||||
})
|
||||
}
|
||||
>
|
||||
End
|
||||
</Button>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</Label>
|
||||
<Label direction={"horizontal"}>
|
||||
<span>align = </span>
|
||||
<Popover>
|
||||
<PopoverTrigger>
|
||||
<Button className={"gap-2"}>
|
||||
{props.PopoverContentProps.align}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<title>Expand</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M7.41 8.58L12 13.17l4.59-4.59L18 10l-6 6l-6-6z"
|
||||
/>
|
||||
</svg>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
anchor={"start"}
|
||||
align={"start"}
|
||||
>
|
||||
<Button
|
||||
preset={"ghost"}
|
||||
onClick={() =>
|
||||
mutate((p) => {
|
||||
p.PopoverContentProps.align = "start";
|
||||
})
|
||||
}
|
||||
>
|
||||
Start
|
||||
</Button>
|
||||
<Button
|
||||
preset={"ghost"}
|
||||
onClick={() =>
|
||||
mutate((p) => {
|
||||
p.PopoverContentProps.align = "middle";
|
||||
})
|
||||
}
|
||||
>
|
||||
Middle
|
||||
</Button>
|
||||
<Button
|
||||
preset={"ghost"}
|
||||
onClick={() =>
|
||||
mutate((p) => {
|
||||
p.PopoverContentProps.align = "end";
|
||||
})
|
||||
}
|
||||
>
|
||||
End
|
||||
</Button>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</Label>
|
||||
<Label direction={"horizontal"}>
|
||||
<span>offset = </span>
|
||||
<Popover>
|
||||
<PopoverTrigger>
|
||||
<Button className={"gap-2"}>
|
||||
{props.PopoverContentProps.offset}
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<title>Expand</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M7.41 8.58L12 13.17l4.59-4.59L18 10l-6 6l-6-6z"
|
||||
/>
|
||||
</svg>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
anchor={"start"}
|
||||
align={"start"}
|
||||
>
|
||||
<Button
|
||||
preset={"ghost"}
|
||||
onClick={() =>
|
||||
mutate((p) => {
|
||||
p.PopoverContentProps.offset = "sm";
|
||||
})
|
||||
}
|
||||
>
|
||||
Small
|
||||
</Button>
|
||||
<Button
|
||||
preset={"ghost"}
|
||||
onClick={() =>
|
||||
mutate((p) => {
|
||||
p.PopoverContentProps.offset = "md";
|
||||
})
|
||||
}
|
||||
>
|
||||
Middle
|
||||
</Button>
|
||||
<Button
|
||||
preset={"ghost"}
|
||||
onClick={() =>
|
||||
mutate((p) => {
|
||||
p.PopoverContentProps.offset = "lg";
|
||||
})
|
||||
}
|
||||
>
|
||||
Large
|
||||
</Button>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</Label>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,9 +1,26 @@
|
||||
import { Button } from "@pswui/Button";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@pswui/Popover";
|
||||
|
||||
export function PopoverDemo() {
|
||||
export interface ControlledPopoverDemoProps {
|
||||
PopoverProps: {
|
||||
opened: boolean;
|
||||
};
|
||||
PopoverContentProps: {
|
||||
direction: "row" | "col";
|
||||
position: "start" | "end";
|
||||
anchor: "start" | "middle" | "end";
|
||||
align: "start" | "middle" | "end";
|
||||
offset: "sm" | "md" | "lg";
|
||||
className?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export function PopoverDemo({
|
||||
PopoverProps,
|
||||
PopoverContentProps,
|
||||
}: ControlledPopoverDemoProps) {
|
||||
return (
|
||||
<Popover>
|
||||
<Popover {...PopoverProps}>
|
||||
<PopoverTrigger>
|
||||
<Button size="icon">
|
||||
<svg
|
||||
@ -20,7 +37,7 @@ export function PopoverDemo() {
|
||||
</svg>
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent>
|
||||
<PopoverContent {...PopoverContentProps}>
|
||||
<Button
|
||||
preset="ghost"
|
||||
className="gap-2"
|
||||
|
12
src/utils/useMutable.ts
Normal file
12
src/utils/useMutable.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { useReducer } from "react";
|
||||
|
||||
function reduce<T>(state: T, action: (state: T) => void) {
|
||||
const mutableState: T = { ...state };
|
||||
action(mutableState);
|
||||
return mutableState;
|
||||
}
|
||||
|
||||
export default function useMutable<T>(initialData: T) {
|
||||
const [state, dispatch] = useReducer(reduce<T>, initialData);
|
||||
return [state, dispatch] as const;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user