(
(
{ code, showLineNumbers = false, wordWrap = false, className, ...props },
- ref
+ ref,
) => {
return (
@@ -30,23 +30,23 @@ export const Pre = forwardRef(
{code.lines.map((line, lineIndex) => (
{showLineNumbers && (
-
+
{lineIndex + 1}
)}
-
+
{line.tokens.map((token, tokenIndex) => (
@@ -58,8 +58,8 @@ export const Pre = forwardRef(
))}
- )
- }
-)
+ );
+ },
+);
-Pre.displayName = 'Pre'
+Pre.displayName = "Pre";
diff --git a/src/components/CodePlayground/index.stories.tsx b/src/components/CodePlayground/index.stories.tsx
index b249aaa4..969e79ca 100644
--- a/src/components/CodePlayground/index.stories.tsx
+++ b/src/components/CodePlayground/index.stories.tsx
@@ -1,11 +1,11 @@
-import { Icon } from '@/components/Icon'
-import { CodePlayground, CodePlaygroundSnippets } from '.'
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { useState } from 'react'
+import { Icon } from "@/components/Icon";
+import { CodePlayground, CodePlaygroundSnippets } from ".";
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { useState } from "react";
const meta: Meta = {
component: CodePlayground,
- tags: ['autodocs'],
+ tags: ["autodocs"],
decorators: [
(Story) => (
@@ -13,11 +13,11 @@ const meta: Meta = {
),
],
-}
+};
-export default meta
+export default meta;
-type Story = StoryObj
+type Story = StoryObj;
const snippets: CodePlaygroundSnippets = {
typescript: {
@@ -56,14 +56,14 @@ console.log(users)`,
]
}`,
},
-}
+};
export const Default: Story = {
decorators: [
(Story, { args }) => {
const [selectedLanguage, setSelectedLanguage] = useState(
- args.selectedLanguage
- )
+ args.selectedLanguage,
+ );
return (
- )
+ );
},
],
args: {
snippets,
- selectedLanguage: 'typescript',
+ selectedLanguage: "typescript",
children: [
- GET {' '}
+ GET {" "}
/users
,
-
+
Code snippets generated by AI ✨
,
],
},
-}
+};
const kitchenSink = `function getFooBar(bar: FooType) : BarType {
return bar
@@ -111,7 +111,7 @@ const fooBar = [
for (const foo of fooBar) {
console.log(foo)
}
-`
+`;
export const KitchenSink: Story = {
...Default,
@@ -119,7 +119,7 @@ export const KitchenSink: Story = {
...Default.args,
snippets: { typescript: { code: kitchenSink } },
},
-}
+};
export const NonCopyable: Story = {
...Default,
@@ -127,7 +127,7 @@ export const NonCopyable: Story = {
...Default.args,
copyable: false,
},
-}
+};
export const NoHeading: Story = {
...Default,
@@ -135,13 +135,13 @@ export const NoHeading: Story = {
...Default.args,
children: [
-
+
Code snippets generated by AI ✨
,
],
},
-}
+};
export const NoFooter: Story = {
...Default,
args: {
@@ -149,13 +149,13 @@ export const NoFooter: Story = {
children: [
- GET {' '}
+ GET {" "}
/users
,
],
},
-}
+};
export const WithReallyLongCode: Story = {
...Default,
@@ -196,7 +196,7 @@ for (const foo of fooBar) {
},
},
},
-}
+};
export const WithSmallerContainer: Story = {
...Default,
@@ -210,7 +210,7 @@ export const WithSmallerContainer: Story = {
),
],
-}
+};
export const Loading: Story = {
...Default,
@@ -219,7 +219,7 @@ export const Loading: Story = {
children: [
-
+
Generating...
,
@@ -230,7 +230,7 @@ export const Loading: Story = {
},
},
},
-}
+};
export const ErrorState: Story = {
...Default,
@@ -241,7 +241,7 @@ export const ErrorState: Story = {
Could not generate
-
+
This might be due to a temporary issue on our side. Please try again
later.
@@ -253,7 +253,7 @@ export const ErrorState: Story = {
},
},
},
-}
+};
export const WithCustomCodeContainer: Story = {
args: {
@@ -307,13 +307,13 @@ fmt.Println(users)`,
children: [
,
-
+
Code snippets generated by AI ✨
,
],
},
-}
+};
export const NoLineNumbers: Story = {
...Default,
@@ -321,7 +321,7 @@ export const NoLineNumbers: Story = {
...Default.args,
showLineNumbers: false,
},
-}
+};
export const OverflowingCode: Story = {
...Default,
@@ -335,7 +335,7 @@ export const OverflowingCode: Story = {
},
},
},
-}
+};
export const WordWrap: Story = {
...Default,
@@ -348,4 +348,4 @@ export const WordWrap: Story = {
},
},
},
-}
+};
diff --git a/src/components/CodePlayground/index.tsx b/src/components/CodePlayground/index.tsx
index baad25ed..d98137aa 100644
--- a/src/components/CodePlayground/index.tsx
+++ b/src/components/CodePlayground/index.tsx
@@ -8,61 +8,61 @@ import {
HTMLAttributes,
forwardRef,
useRef,
-} from 'react'
+} from "react";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
-} from '@/components/Select'
-import { prettyLanguageName, SupportedLanguage } from '@/types'
-import '@/styles/codeSyntax.css'
-import { motion } from 'motion/react'
-import { cn } from '@/lib/utils'
-import { AnimatePresence } from 'motion/react'
-import { Icon } from '@/components/Icon'
-import { Skeleton } from '@/components/Skeleton'
+} from "@/components/Select";
+import { prettyLanguageName, SupportedLanguage } from "@/types";
+import "@/styles/codeSyntax.css";
+import { motion } from "motion/react";
+import { cn } from "@/lib/utils";
+import { AnimatePresence } from "motion/react";
+import { Icon } from "@/components/Icon";
+import { Skeleton } from "@/components/Skeleton";
import {
highlightCode,
HighlightedCode,
LIGHT_THEME,
DARK_THEME,
-} from '@/lib/codeUtils'
-import React from 'react'
-import { Pre } from '../CodeHighlight/Pre'
-import { useConfig } from '@/hooks/useConfig'
+} from "@/lib/codeUtils";
+import React from "react";
+import { Pre } from "../CodeHighlight/Pre";
+import { useConfig } from "@/hooks/useConfig";
const copyIconVariants = {
hidden: { opacity: 0, scale: 0.5 },
visible: { opacity: 1, scale: 1 },
-}
+};
export interface CodePlaygroundSnippet {
/**
* The code to display in the playground.
*/
- code?: string | undefined
+ code?: string | undefined;
/**
* Whether the code is loading.
*/
- loading?: boolean | undefined
+ loading?: boolean | undefined;
}
export type CodePlaygroundSnippets = Partial<
Record
->
+>;
export interface CodePlaygroundProps {
/**
* The children of the playground.
* Accepts a `CodePlayground.Header` and a `CodePlayground.Footer` or a `CodePlayground.Code` component.
*/
- children: React.ReactNode
+ children: React.ReactNode;
/**
* The error to display in the playground if the code could not be loaded.
*/
- error?: React.ReactNode | undefined
+ error?: React.ReactNode | undefined;
/**
* An object of snippets to display in the playground.
@@ -75,46 +75,46 @@ export interface CodePlaygroundProps {
* }}
* />
*/
- snippets: CodePlaygroundSnippets
+ snippets: CodePlaygroundSnippets;
/**
* The language that should be selected when the playground is mounted.
*/
- selectedLanguage: SupportedLanguage
+ selectedLanguage: SupportedLanguage;
/**
* Whether the code should be copyable.
*
* @default true
*/
- copyable?: boolean
+ copyable?: boolean;
/** Custom class name to apply to the container */
- className?: string
+ className?: string;
/**
* Whether to wrap the code.
*
* @default true
*/
- wordWrap?: boolean
+ wordWrap?: boolean;
/**
* A callback to be called when the language is changed.
*/
- onChangeLanguage?: (language: SupportedLanguage) => void
+ onChangeLanguage?: (language: SupportedLanguage) => void;
/**
* Whether to show the language selector.
*/
- showLanguageSelector?: boolean
+ showLanguageSelector?: boolean;
/**
* Whether to show line numbers.
*
* @default true
*/
- showLineNumbers?: boolean
+ showLineNumbers?: boolean;
}
const CodePlayground = ({
@@ -129,34 +129,34 @@ const CodePlayground = ({
showLanguageSelector = true,
wordWrap = true,
}: CodePlaygroundProps) => {
- const codeRef = useRef(null)
+ const codeRef = useRef(null);
const validChildren = Children.toArray(children).filter((child) => {
- if (!isValidElement(child)) return false
- const type = child.type as { displayName?: string }
+ if (!isValidElement(child)) return false;
+ const type = child.type as { displayName?: string };
const isValidSubType =
- type.displayName === 'CodePlayground.Header' ||
- type.displayName === 'CodePlayground.Footer' ||
- type.displayName === 'CodePlayground.Code'
+ type.displayName === "CodePlayground.Header" ||
+ type.displayName === "CodePlayground.Footer" ||
+ type.displayName === "CodePlayground.Code";
if (!isValidSubType) {
console.warn(
- `Invalid child type: ${type.displayName}. Must be one of: CodePlayground.Header, CodePlayground.Footer`
- )
+ `Invalid child type: ${type.displayName}. Must be one of: CodePlayground.Header, CodePlayground.Footer`,
+ );
}
- return isValidSubType
- })
+ return isValidSubType;
+ });
const header = validChildren.find(
(child) =>
isValidElement(child) &&
(child.type as { displayName?: string }).displayName ===
- 'CodePlayground.Header'
- )
+ "CodePlayground.Header",
+ );
- const [highlighted, setHighlighted] = useState(null)
- const selectedCode = snippets[selectedLanguage]!
- const { theme } = useConfig()
+ const [highlighted, setHighlighted] = useState(null);
+ const selectedCode = snippets[selectedLanguage]!;
+ const { theme } = useConfig();
const loadingSkeleton = useMemo(() => {
// Try to measure the existing height of the code container if code has
@@ -164,17 +164,17 @@ const CodePlayground = ({
// TODO: improve this logic
const measuredHeight =
- codeRef.current?.getBoundingClientRect().height ?? 400
+ codeRef.current?.getBoundingClientRect().height ?? 400;
- const lines = Math.ceil(measuredHeight / 40)
+ const lines = Math.ceil(measuredHeight / 40);
return (
{Array.from({ length: lines }).map((_, i) => (
))}
- )
- }, [codeRef.current])
+ );
+ }, [codeRef.current]);
const codeContents = error ? (
error
@@ -185,9 +185,9 @@ const CodePlayground = ({
code={highlighted}
showLineNumbers={showLineNumbers}
wordWrap={wordWrap}
- className="bg-muted/15 dark:bg-background relative m-0 mr-4 w-full px-4 py-3 text-sm"
+ className="relative m-0 mr-4 w-full bg-muted/15 px-4 py-3 text-sm dark:bg-background"
/>
- ) : null
+ ) : null;
const foundCustomCodeContainer = useMemo(
() =>
@@ -195,10 +195,10 @@ const CodePlayground = ({
(child) =>
isValidElement(child) &&
(child.type as { displayName?: string }).displayName ===
- 'CodePlayground.Code'
+ "CodePlayground.Code",
),
- [validChildren]
- )
+ [validChildren],
+ );
const code = foundCustomCodeContainer ? (
React.cloneElement(
@@ -206,59 +206,59 @@ const CodePlayground = ({
{
__children__: codeContents,
ref: codeRef,
- }
+ },
)
) : (
- )
+ );
const footer = validChildren.find(
(child) =>
isValidElement(child) &&
(child.type as { displayName?: string }).displayName ===
- 'CodePlayground.Footer'
- )
+ "CodePlayground.Footer",
+ );
const updateHighlighted = useCallback(
async (code: string, language: SupportedLanguage) => {
- const shikiTheme = theme === 'dark' ? DARK_THEME : LIGHT_THEME
- const highlighted = await highlightCode(code, language, shikiTheme)
- setHighlighted(highlighted)
+ const shikiTheme = theme === "dark" ? DARK_THEME : LIGHT_THEME;
+ const highlighted = await highlightCode(code, language, shikiTheme);
+ setHighlighted(highlighted);
},
- [theme]
- )
+ [theme],
+ );
- const [copying, setCopying] = useState(false)
+ const [copying, setCopying] = useState(false);
const handleCopy = useCallback(() => {
- setCopying(true)
- navigator.clipboard.writeText(selectedCode.code ?? '')
+ setCopying(true);
+ navigator.clipboard.writeText(selectedCode.code ?? "");
setTimeout(() => {
- setCopying(false)
- }, 1000)
- }, [selectedCode.code])
+ setCopying(false);
+ }, 1000);
+ }, [selectedCode.code]);
useEffect(() => {
if (selectedCode.code) {
- updateHighlighted(selectedCode.code, selectedLanguage)
+ updateHighlighted(selectedCode.code, selectedLanguage);
}
- }, [selectedCode, selectedLanguage, updateHighlighted])
+ }, [selectedCode, selectedLanguage, updateHighlighted]);
const handleChangeLanguage = useCallback(
(language: SupportedLanguage) => {
- onChangeLanguage?.(language)
+ onChangeLanguage?.(language);
},
- [onChangeLanguage]
- )
+ [onChangeLanguage],
+ );
return (
-
+
{header && header}
{showLanguageSelector && (
@@ -266,7 +266,7 @@ const CodePlayground = ({
value={selectedLanguage}
onValueChange={handleChangeLanguage}
>
-
+
@@ -288,7 +288,7 @@ const CodePlayground = ({
@@ -323,25 +323,25 @@ const CodePlayground = ({
{footer && footer}
- )
-}
+ );
+};
-CodePlayground.displayName = 'CodePlayground'
+CodePlayground.displayName = "CodePlayground";
export interface CodePlaygroundCodeProps extends Omit<
HTMLAttributes,
- 'children'
+ "children"
> {
- className?: string
+ className?: string;
/**
* internal api for passing children
*/
- __children__?: React.ReactNode
+ __children__?: React.ReactNode;
}
type CodePlaygroundCodeElementProps = CodePlaygroundCodeProps &
- React.RefAttributes
+ React.RefAttributes;
const CodePlaygroundCode = forwardRef(
({ className, __children__, ...props }, ref) => {
@@ -349,18 +349,18 @@ const CodePlaygroundCode = forwardRef(
{__children__}
- )
- }
-)
+ );
+ },
+);
-CodePlaygroundCode.displayName = 'CodePlayground.Code'
+CodePlaygroundCode.displayName = "CodePlayground.Code";
export interface CodePlaygroundHeaderProps extends HTMLAttributes {
- children: React.ReactNode
+ children: React.ReactNode;
}
const CodePlaygroundHeader = ({
@@ -369,16 +369,16 @@ const CodePlaygroundHeader = ({
...props
}: CodePlaygroundHeaderProps) => {
return (
-
+
{children}
- )
-}
+ );
+};
-CodePlaygroundHeader.displayName = 'CodePlayground.Header'
+CodePlaygroundHeader.displayName = "CodePlayground.Header";
export interface CodePlaygroundFooterProps extends HTMLAttributes
{
- children: React.ReactNode
+ children: React.ReactNode;
}
const CodePlaygroundFooter = ({
@@ -389,17 +389,17 @@ const CodePlaygroundFooter = ({
return (
{children}
- )
-}
+ );
+};
-CodePlaygroundFooter.displayName = 'CodePlayground.Footer'
+CodePlaygroundFooter.displayName = "CodePlayground.Footer";
const CodePlaygroundWithSubcomponents = Object.assign(CodePlayground, {
Header: CodePlaygroundHeader,
@@ -412,6 +412,6 @@ const CodePlaygroundWithSubcomponents = Object.assign(CodePlayground, {
*
*/
Code: CodePlaygroundCode,
-})
+});
-export { CodePlaygroundWithSubcomponents as CodePlayground }
+export { CodePlaygroundWithSubcomponents as CodePlayground };
diff --git a/src/components/CodeSnippet/codeSnippet.css b/src/components/CodeSnippet/codeSnippet.css
index 0dd17e11..2e296863 100644
--- a/src/components/CodeSnippet/codeSnippet.css
+++ b/src/components/CodeSnippet/codeSnippet.css
@@ -1,11 +1,11 @@
@property --rotation {
- syntax: '';
+ syntax: "";
inherits: false;
initial-value: 0deg;
}
@property --x {
- syntax: '';
+ syntax: "";
inherits: false;
initial-value: 0;
}
@@ -58,7 +58,7 @@
}
.snippet::before {
- content: '';
+ content: "";
display: block;
height: 100%;
width: 100%;
@@ -68,10 +68,11 @@
}
.shimmer::before {
- content: '';
+ content: "";
display: block;
padding: 1px;
- background: conic-gradient(
+ background:
+ conic-gradient(
from calc(var(--rotation) - 80deg) at var(--x) 30px,
transparent 0,
var(--border-neutral-softest) 20%,
diff --git a/src/components/CodeSnippet/index.stories.tsx b/src/components/CodeSnippet/index.stories.tsx
index 637f1694..c3b6572d 100644
--- a/src/components/CodeSnippet/index.stories.tsx
+++ b/src/components/CodeSnippet/index.stories.tsx
@@ -1,36 +1,36 @@
-import { expect, within } from 'storybook/test'
-import { userEvent } from 'storybook/test'
-import { CodeSnippet } from '.'
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { fn } from 'storybook/test'
+import { expect, within } from "storybook/test";
+import { userEvent } from "storybook/test";
+import { CodeSnippet } from ".";
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
const meta: Meta = {
component: CodeSnippet,
- tags: ['autodocs'],
+ tags: ["autodocs"],
parameters: {
- layout: 'centered',
+ layout: "centered",
},
-}
+};
-export default meta
+export default meta;
-type Story = StoryObj
+type Story = StoryObj;
export const Default: Story = {
args: {
code: 'console.log("Hello, world!")',
- language: 'javascript',
+ language: "javascript",
copyable: true,
},
-}
+};
export const Python: Story = {
args: {
code: 'print("Hello, world!")',
- language: 'python',
+ language: "python",
copyable: true,
},
-}
+};
export const TypescriptMultiline: Story = {
args: {
@@ -38,69 +38,69 @@ export const TypescriptMultiline: Story = {
name: string
age: number
}`,
- language: 'typescript',
+ language: "typescript",
copyable: true,
},
-}
+};
export const TypescriptFunctionMultiline: Story = {
args: {
code: `function greet(name: string) {
return \`Hello, \${name}!\`
}`,
- language: 'typescript',
+ language: "typescript",
copyable: true,
},
-}
+};
export const Javascript: Story = {
args: {
code: 'console.log("Hello, world!")',
- language: 'javascript',
+ language: "javascript",
copyable: true,
},
-}
+};
export const Bash: Story = {
args: {
code: 'echo "Hello, world!"',
- language: 'bash',
+ language: "bash",
copyable: true,
},
-}
+};
export const BashWithCustomPromptSymbol: Story = {
args: {
code: 'echo "Hello, world!"',
- language: 'bash',
+ language: "bash",
copyable: true,
- promptSymbol: '>',
+ promptSymbol: ">",
},
-}
+};
export const Java: Story = {
args: {
code: 'System.out.println("Hello, world!");',
- language: 'java',
+ language: "java",
copyable: true,
},
-}
+};
export const Dotnet: Story = {
args: {
code: 'Console.WriteLine("Hello, world!");',
- language: 'dotnet',
+ language: "dotnet",
copyable: true,
},
-}
+};
export const Go: Story = {
args: {
code: 'fmt.Println("Hello, world!")',
- language: 'go',
+ language: "go",
copyable: true,
},
-}
+};
export const Json: Story = {
args: {
@@ -108,18 +108,18 @@ export const Json: Story = {
"name": "John",
"age": 30
}`,
- language: 'json',
+ language: "json",
copyable: true,
},
-}
+};
export const UnsupportedLanguage: Story = {
args: {
code: 'console.log("Hello, world!")',
- language: 'unsupported',
+ language: "unsupported",
copyable: true,
},
-}
+};
/**
* Shiki supports many languages that Speakeasy does not.
@@ -134,69 +134,69 @@ COPY package.json .
RUN npm install
`,
- language: 'dockerfile',
+ language: "dockerfile",
},
-}
+};
export const NonCopyable: Story = {
args: {
code: 'console.log("Hello, world!")',
- language: 'javascript',
+ language: "javascript",
copyable: false,
},
-}
+};
export const FontSizeXL: Story = {
args: {
code: 'console.log("Hello, world!")',
- language: 'javascript',
+ language: "javascript",
copyable: true,
- fontSize: 'xl',
+ fontSize: "xl",
},
-}
+};
export const FontSize2XL: Story = {
args: {
code: 'console.log("Hello, world!")',
- language: 'javascript',
+ language: "javascript",
copyable: true,
- fontSize: '2xl',
+ fontSize: "2xl",
},
-}
+};
export const Interactive: Story = {
args: {
code: 'console.log("Hello, world!")',
- language: 'javascript',
+ language: "javascript",
copyable: true,
},
play: async ({ canvasElement }) => {
- const canvas = within(canvasElement)
- const codeSnippet = await canvas.findByRole('button', { name: /copy/i })
- await userEvent.click(codeSnippet)
+ const canvas = within(canvasElement);
+ const codeSnippet = await canvas.findByRole("button", { name: /copy/i });
+ await userEvent.click(codeSnippet);
expect(navigator.clipboard.readText()).resolves.toBe(
- 'console.log("Hello, world!")'
- )
+ 'console.log("Hello, world!")',
+ );
},
-}
+};
export const WithOnSelectOrCopy: Story = {
args: {
code: 'console.log("Hello, world!")',
- language: 'javascript',
+ language: "javascript",
copyable: true,
onSelectOrCopy: fn(),
},
-}
+};
export const Shimmer: Story = {
args: {
code: 'console.log("Hello, world!")',
- language: 'javascript',
+ language: "javascript",
copyable: true,
shimmer: true,
},
-}
+};
export const ShowLineNumbers: Story = {
args: {
@@ -206,17 +206,17 @@ console.log("Hello, world!")
console.log("Hello, world!")
console.log("Hello, world!")
console.log("Hello, world!")`,
- language: 'javascript',
+ language: "javascript",
showLineNumbers: true,
},
-}
+};
export const WithSnippetClassName: Story = {
args: {
code: `const reallyLongLine = 'this is a really long line of code that will overflow the container'`,
- language: 'javascript',
+ language: "javascript",
copyable: true,
showLineNumbers: true,
- snippetClassName: 'whitespace-pre-line max-w-lg',
+ snippetClassName: "whitespace-pre-line max-w-lg",
},
-}
+};
diff --git a/src/components/CodeSnippet/index.tsx b/src/components/CodeSnippet/index.tsx
index f158f3a3..52e8b363 100644
--- a/src/components/CodeSnippet/index.tsx
+++ b/src/components/CodeSnippet/index.tsx
@@ -1,81 +1,81 @@
-import { useCallback, useState, useEffect, useRef } from 'react'
-import { cn } from '@/lib/utils'
-import { ProgrammingLanguage, Size } from '@/types'
-import { AnimatePresence, motion } from 'motion/react'
-import '@/styles/codeSyntax.css'
-import './codeSnippet.css'
-import { Icon } from '../Icon'
+import { useCallback, useState, useEffect, useRef } from "react";
+import { cn } from "@/lib/utils";
+import { ProgrammingLanguage, Size } from "@/types";
+import { AnimatePresence, motion } from "motion/react";
+import "@/styles/codeSyntax.css";
+import "./codeSnippet.css";
+import { Icon } from "../Icon";
import {
highlightCode,
HighlightedCode,
LIGHT_THEME,
DARK_THEME,
-} from '@/lib/codeUtils'
-import { useConfig } from '@/hooks/useConfig'
-import { Pre } from '../CodeHighlight/Pre'
-import { preventDefault } from '@/lib/events'
+} from "@/lib/codeUtils";
+import { useConfig } from "@/hooks/useConfig";
+import { Pre } from "../CodeHighlight/Pre";
+import { preventDefault } from "@/lib/events";
export interface CodeSnippetProps {
/**
* The code to display.
*/
- code: string
+ code: string;
/**
* Whether to show a copy button.
*/
- copyable?: boolean
+ copyable?: boolean;
/**
* One of the known Speakeasy target languages, or a language that Shiki supports.
* The full list of supported languages is available at https://shiki.style/languages
*/
- language: ProgrammingLanguage | string
+ language: ProgrammingLanguage | string;
/**
* The symbol to display before the code.
*/
- promptSymbol?: React.ReactNode
+ promptSymbol?: React.ReactNode;
/**
* Whether to display the code snippet inline.
*/
- inline?: boolean
+ inline?: boolean;
/**
* The font size of the code snippet.
*/
- fontSize?: Size
+ fontSize?: Size;
/**
* Whether to show line numbers.
*/
- showLineNumbers?: boolean
+ showLineNumbers?: boolean;
/**
* The callback to call when the code is selected or copied.
*/
- onSelectOrCopy?: () => void
+ onSelectOrCopy?: () => void;
/**
* Whether to shimmer the code snippet.
*/
- shimmer?: boolean
+ shimmer?: boolean;
/**
* Additional CSS classes to apply to the code snippet container
*/
- className?: string
+ className?: string;
/**
* Additional CSS classes to apply to the code snippet inner container (e.g the Pre component).
*/
- snippetClassName?: string
+ snippetClassName?: string;
}
const fontSizeMap: Record = {
- small: 'text-sm',
- medium: 'text-sm',
- large: 'text-base',
- xl: 'text-lg',
- '2xl': 'text-xl',
-}
+ small: "text-sm",
+ medium: "text-sm",
+ large: "text-base",
+ xl: "text-lg",
+ "2xl": "text-xl",
+};
const copyIconVariants = {
hidden: { opacity: 0, scale: 0.5 },
visible: { opacity: 1, scale: 1 },
-}
+};
export function CodeSnippet({
code,
@@ -83,82 +83,82 @@ export function CodeSnippet({
language,
promptSymbol,
inline = false,
- fontSize = 'medium',
+ fontSize = "medium",
onSelectOrCopy,
shimmer = false,
className,
snippetClassName,
showLineNumbers = false,
}: CodeSnippetProps) {
- const [copying, setCopying] = useState(false)
- const [containerWidth, setContainerWidth] = useState(0)
- const containerRef = useRef(null)
+ const [copying, setCopying] = useState(false);
+ const [containerWidth, setContainerWidth] = useState(0);
+ const containerRef = useRef(null);
useEffect(() => {
const updateWidth = () => {
if (containerRef.current) {
- const width = containerRef.current.getBoundingClientRect().width
+ const width = containerRef.current.getBoundingClientRect().width;
// Only update if we have a non-zero width
if (width > 0) {
- setContainerWidth(width)
+ setContainerWidth(width);
}
}
- }
+ };
// Initial measurement
- updateWidth()
+ updateWidth();
// Create ResizeObserver for more reliable width tracking
- const resizeObserver = new ResizeObserver(updateWidth)
+ const resizeObserver = new ResizeObserver(updateWidth);
if (containerRef.current) {
- resizeObserver.observe(containerRef.current)
+ resizeObserver.observe(containerRef.current);
}
- return () => resizeObserver.disconnect()
- }, [])
+ return () => resizeObserver.disconnect();
+ }, []);
const [highlightedCodeState, setHighlightedCodeState] = useState<
HighlightedCode | undefined
- >(undefined)
- const isMultiline = code.split('\n').length > 1
- const { theme } = useConfig()
+ >(undefined);
+ const isMultiline = code.split("\n").length > 1;
+ const { theme } = useConfig();
// Directly highlight the code when code or language changes
useEffect(() => {
- if (!language) return
+ if (!language) return;
- const shikiTheme = theme === 'dark' ? DARK_THEME : LIGHT_THEME
+ const shikiTheme = theme === "dark" ? DARK_THEME : LIGHT_THEME;
// Use the highlightCode utility directly
highlightCode(code, language, shikiTheme).then((highlighted) => {
- setHighlightedCodeState(highlighted)
- })
- }, [code, language, theme])
+ setHighlightedCodeState(highlighted);
+ });
+ }, [code, language, theme]);
const handleCopy = useCallback(() => {
- setCopying(true)
- navigator.clipboard.writeText(highlightedCodeState?.code ?? code)
+ setCopying(true);
+ navigator.clipboard.writeText(highlightedCodeState?.code ?? code);
setTimeout(() => {
- setCopying(false)
- onSelectOrCopy?.()
- }, 1000)
- }, [highlightedCodeState?.code, code])
+ setCopying(false);
+ onSelectOrCopy?.();
+ }, 1000);
+ }, [highlightedCodeState?.code, code]);
return (
-
- {language === 'bash' && (
-
- {promptSymbol ?? '$'}
+
+ {language === "bash" && (
+
+ {promptSymbol ?? "$"}
)}
{highlightedCodeState && (
@@ -166,10 +166,10 @@ export function CodeSnippet({
code={highlightedCodeState}
onClick={onSelectOrCopy}
className={cn(
- 'highlighted-code inline-flex w-fit self-center font-mono outline-none',
+ "highlighted-code inline-flex w-fit self-center font-mono outline-none",
fontSizeMap[fontSize],
- isMultiline && 'min-w-32',
- snippetClassName
+ isMultiline && "min-w-32",
+ snippetClassName,
)}
onBeforeInput={preventDefault}
showLineNumbers={showLineNumbers}
@@ -179,8 +179,8 @@ export function CodeSnippet({
{copyable && (
- )
+ );
}
diff --git a/src/components/Combobox/index.stories.tsx b/src/components/Combobox/index.stories.tsx
index 1d02590c..e54430b8 100644
--- a/src/components/Combobox/index.stories.tsx
+++ b/src/components/Combobox/index.stories.tsx
@@ -1,56 +1,56 @@
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { useState } from 'react'
-import { Combobox } from './index'
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { useState } from "react";
+import { Combobox } from "./index";
const meta: Meta
= {
- title: 'Components/Combobox',
+ title: "Components/Combobox",
component: Combobox,
- tags: ['autodocs'],
-}
+ tags: ["autodocs"],
+};
-export default meta
-type Story = StoryObj
+export default meta;
+type Story = StoryObj;
// Example data
const frameworks = [
- { value: 'next.js', label: 'Next.js' },
- { value: 'sveltekit', label: 'SvelteKit' },
- { value: 'nuxt.js', label: 'Nuxt.js' },
- { value: 'remix', label: 'Remix', disabled: true },
- { value: 'astro', label: 'Astro' },
-]
+ { value: "next.js", label: "Next.js" },
+ { value: "sveltekit", label: "SvelteKit" },
+ { value: "nuxt.js", label: "Nuxt.js" },
+ { value: "remix", label: "Remix", disabled: true },
+ { value: "astro", label: "Astro" },
+];
const groupedFrameworks = [
{
- label: 'React Based',
+ label: "React Based",
options: [
- { value: 'next.js', label: 'Next.js' },
- { value: 'remix', label: 'Remix', disabled: true },
- { value: 'gatsby', label: 'Gatsby' },
+ { value: "next.js", label: "Next.js" },
+ { value: "remix", label: "Remix", disabled: true },
+ { value: "gatsby", label: "Gatsby" },
],
},
{
- label: 'Vue Based',
+ label: "Vue Based",
options: [
- { value: 'nuxt.js', label: 'Nuxt.js' },
- { value: 'vuejs', label: 'Vue.js' },
+ { value: "nuxt.js", label: "Nuxt.js" },
+ { value: "vuejs", label: "Vue.js" },
],
},
{
- label: 'Others',
+ label: "Others",
options: [
- { value: 'sveltekit', label: 'SvelteKit' },
- { value: 'astro', label: 'Astro' },
+ { value: "sveltekit", label: "SvelteKit" },
+ { value: "astro", label: "Astro" },
],
},
-]
+];
// Helpers for generating test data
const generateList = (count: number) =>
Array.from({ length: count }, (_, i) => ({
value: `item-${i}`,
label: `Item ${i + 1}`,
- }))
+ }));
const generateGroupedList = (groupCount: number, itemsPerGroup: number) =>
Array.from({ length: groupCount }, (_, groupIndex) => ({
@@ -59,37 +59,37 @@ const generateGroupedList = (groupCount: number, itemsPerGroup: number) =>
value: `group-${groupIndex}-item-${itemIndex}`,
label: `Group ${groupIndex + 1} - Item ${itemIndex + 1}`,
})),
- }))
+ }));
// Basic Usage
export const Default: Story = {
args: {
options: frameworks,
- placeholder: 'Select framework...',
+ placeholder: "Select framework...",
},
-}
+};
export const WithValue: Story = {
args: {
...Default.args,
- value: 'next.js',
+ value: "next.js",
},
-}
+};
// Grouped Examples
export const Grouped: Story = {
args: {
groups: groupedFrameworks,
- placeholder: 'Select framework...',
+ placeholder: "Select framework...",
},
-}
+};
export const GroupedWithValue: Story = {
args: {
...Grouped.args,
- value: 'next.js',
+ value: "next.js",
},
-}
+};
// States
export const Loading: Story = {
@@ -97,111 +97,111 @@ export const Loading: Story = {
...Default.args,
loading: true,
},
-}
+};
export const LoadingWithValue: Story = {
args: {
...WithValue.args,
loading: true,
},
-}
+};
export const Error: Story = {
args: {
...Default.args,
error: true,
- errorText: 'Failed to load frameworks',
+ errorText: "Failed to load frameworks",
},
-}
+};
export const Disabled: Story = {
args: {
...WithValue.args,
disabled: true,
},
-}
+};
// Variants
export const IconOnly: Story = {
args: {
...Default.args,
iconOnly: true,
- variant: 'tertiary',
+ variant: "tertiary",
},
-}
+};
export const NonSearchable: Story = {
args: {
...Default.args,
searchable: false,
},
-}
+};
// Dynamic Height Examples
export const SmallList: Story = {
args: {
options: generateList(3),
- placeholder: 'Small list (3 items)',
+ placeholder: "Small list (3 items)",
},
-}
+};
export const MediumList: Story = {
args: {
options: generateList(8),
- placeholder: 'Medium list (8 items)',
+ placeholder: "Medium list (8 items)",
},
-}
+};
export const LargeList: Story = {
args: {
options: generateList(1000),
- placeholder: 'Large list (1000 items)',
+ placeholder: "Large list (1000 items)",
},
-}
+};
export const SmallGroupedList: Story = {
args: {
groups: generateGroupedList(2, 2),
- placeholder: 'Small grouped list (4 items)',
+ placeholder: "Small grouped list (4 items)",
},
-}
+};
export const MediumGroupedList: Story = {
args: {
groups: generateGroupedList(3, 3),
- placeholder: 'Medium grouped list (9 items)',
+ placeholder: "Medium grouped list (9 items)",
},
-}
+};
export const LargeGroupedList: Story = {
args: {
groups: generateGroupedList(20, 50),
- placeholder: 'Large grouped list (1000 items)',
+ placeholder: "Large grouped list (1000 items)",
},
-}
+};
// Interactive Examples
export const SearchableList: Story = {
args: {
options: generateList(1000),
- placeholder: 'Type to filter items...',
- searchPlaceholder: 'Search items...',
+ placeholder: "Type to filter items...",
+ searchPlaceholder: "Search items...",
},
-}
+};
export const SearchableGroupedList: Story = {
args: {
groups: generateGroupedList(20, 50),
- placeholder: 'Type to filter groups...',
- searchPlaceholder: 'Search groups and items...',
+ placeholder: "Type to filter groups...",
+ searchPlaceholder: "Search groups and items...",
},
-}
+};
// Create new item example - demonstrates dynamic options
export const WithCreateOption: Story = {
render: () => {
- const [options, setOptions] = useState(generateList(10))
- const [value, setValue] = useState()
+ const [options, setOptions] = useState(generateList(10));
+ const [value, setValue] = useState();
return (
{
- const newValue = query.toLowerCase().replace(/\s+/g, '-')
- setOptions([...options, { value: newValue, label: query }])
- setValue(newValue)
+ const newValue = query.toLowerCase().replace(/\s+/g, "-");
+ setOptions([...options, { value: newValue, label: query }]);
+ setValue(newValue);
},
renderCreatePrompt: (query) => (
@@ -223,6 +223,6 @@ export const WithCreateOption: Story = {
),
}}
/>
- )
+ );
},
-}
+};
diff --git a/src/components/Combobox/index.tsx b/src/components/Combobox/index.tsx
index 99e0f22a..e6772806 100644
--- a/src/components/Combobox/index.tsx
+++ b/src/components/Combobox/index.tsx
@@ -1,17 +1,17 @@
-import * as React from 'react'
-import { Virtuoso } from 'react-virtuoso'
-import { cn } from '@/lib/utils'
-import { Button } from '@/components/Button'
+import * as React from "react";
+import { Virtuoso } from "react-virtuoso";
+import { cn } from "@/lib/utils";
+import { Button } from "@/components/Button";
import {
Command,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
-} from '@/components/Command'
-import { Popover, PopoverContent, PopoverTrigger } from '@/components/Popover'
-import { ButtonProps } from '@/components/Button'
-import { Icon } from '../Icon'
+} from "@/components/Command";
+import { Popover, PopoverContent, PopoverTrigger } from "@/components/Popover";
+import { ButtonProps } from "@/components/Button";
+import { Icon } from "../Icon";
// I don't like that these aren't based on REM but I'm not sure how to fix it right now
const COMBOBOX_CONFIG = {
@@ -21,112 +21,112 @@ const COMBOBOX_CONFIG = {
padding: 8,
max: 300,
},
-} as const
+} as const;
export interface ComboboxOption {
- value: T
- label: string
- disabled?: boolean
+ value: T;
+ label: string;
+ disabled?: boolean;
}
export interface ComboboxGroup {
- label: string
- options: ComboboxOption[]
+ label: string;
+ options: ComboboxOption[];
}
type ComboboxDataProps =
| { options: ComboboxOption[]; groups?: never }
- | { options?: never; groups: ComboboxGroup[] }
+ | { options?: never; groups: ComboboxGroup[] };
interface ComboboxBaseProps {
- value: T
- onValueChange: (value: T | undefined) => void
- variant?: ButtonProps['variant']
- size?: ButtonProps['size']
- disabled?: boolean
- loading?: boolean
- error?: boolean
- errorText?: string
- searchable?: boolean
- placeholder?: string
- emptyText?: string
- searchPlaceholder?: string
- iconOnly?: boolean
+ value: T;
+ onValueChange: (value: T | undefined) => void;
+ variant?: ButtonProps["variant"];
+ size?: ButtonProps["size"];
+ disabled?: boolean;
+ loading?: boolean;
+ error?: boolean;
+ errorText?: string;
+ searchable?: boolean;
+ placeholder?: string;
+ emptyText?: string;
+ searchPlaceholder?: string;
+ iconOnly?: boolean;
createOptions?: {
- handleCreate: (search: string) => void
- renderCreatePrompt?: (search: string) => React.ReactElement
- }
+ handleCreate: (search: string) => void;
+ renderCreatePrompt?: (search: string) => React.ReactElement;
+ };
}
export type ComboboxProps = ComboboxBaseProps &
- ComboboxDataProps
+ ComboboxDataProps;
export function Combobox({
options,
groups,
value,
onValueChange,
- variant = 'secondary',
- size = 'md',
+ variant = "secondary",
+ size = "md",
disabled,
loading,
error,
- errorText = 'An error occurred',
+ errorText = "An error occurred",
searchable = true,
- placeholder = 'Select option...',
- emptyText = 'No option found.',
- searchPlaceholder = 'Search...',
+ placeholder = "Select option...",
+ emptyText = "No option found.",
+ searchPlaceholder = "Search...",
iconOnly = false,
createOptions,
}: ComboboxProps) {
- const [open, setOpen] = React.useState(false)
- const [search, setSearch] = React.useState('')
+ const [open, setOpen] = React.useState(false);
+ const [search, setSearch] = React.useState("");
const allOptions = React.useMemo(() => {
- if (options) return options
- if (groups) return groups.flatMap((group) => group.options)
- return []
- }, [options, groups])
+ if (options) return options;
+ if (groups) return groups.flatMap((group) => group.options);
+ return [];
+ }, [options, groups]);
// TODO: Make the search more efficient and fuzzier
const filteredItems = React.useMemo(() => {
- const items = groups || (options ? [{ label: '', options }] : [])
- if (!search) return items
+ const items = groups || (options ? [{ label: "", options }] : []);
+ if (!search) return items;
return items
.map((group) => ({
label: group.label,
options: group.options.filter((option) =>
- option.label.toLowerCase().includes(search.toLowerCase())
+ option.label.toLowerCase().includes(search.toLowerCase()),
),
}))
- .filter((group) => group.options.length > 0)
- }, [search, options, groups])
+ .filter((group) => group.options.length > 0);
+ }, [search, options, groups]);
const virtuosoHeight = React.useMemo(() => {
- const { row, header, padding, max } = COMBOBOX_CONFIG.heights
- const shouldShowHeaders = filteredItems.some((group) => group.label)
- const headerHeight = shouldShowHeaders ? filteredItems.length * header : 0
+ const { row, header, padding, max } = COMBOBOX_CONFIG.heights;
+ const shouldShowHeaders = filteredItems.some((group) => group.label);
+ const headerHeight = shouldShowHeaders ? filteredItems.length * header : 0;
const contentHeight = filteredItems.reduce(
(sum, group) => sum + group.options.length * row,
- headerHeight
- )
+ headerHeight,
+ );
- return Math.min(contentHeight + padding, max)
- }, [filteredItems])
+ return Math.min(contentHeight + padding, max);
+ }, [filteredItems]);
const handleSelect = React.useCallback(
(currentValue: string) => {
- const newValue = currentValue === value ? undefined : (currentValue as T)
- onValueChange?.(newValue)
- setOpen(false)
- setSearch('')
+ const newValue = currentValue === value ? undefined : (currentValue as T);
+ onValueChange?.(newValue);
+ setOpen(false);
+ setSearch("");
},
- [value, onValueChange]
- )
+ [value, onValueChange],
+ );
- const selectedOption = allOptions.find((option) => option.value === value)
+ const selectedOption = allOptions.find((option) => option.value === value);
return (
@@ -178,8 +178,10 @@ export function Combobox({
>
@@ -192,8 +194,8 @@ export function Combobox({
/>
)}
{(() => {
- if (!createOptions || search.length === 0) return null
- const { renderCreatePrompt, handleCreate } = createOptions
+ if (!createOptions || search.length === 0) return null;
+ const { renderCreatePrompt, handleCreate } = createOptions;
return (
handleCreate(search)}>
@@ -202,11 +204,11 @@ export function Combobox({
: `Create ${search}`}
- )
+ );
})()}
- )
+ );
}
diff --git a/src/components/Command/index.tsx b/src/components/Command/index.tsx
index f8c6670f..35e8dee6 100644
--- a/src/components/Command/index.tsx
+++ b/src/components/Command/index.tsx
@@ -1,12 +1,12 @@
-'use client'
+"use client";
-import * as React from 'react'
-import { Command as CommandPrimitive } from 'cmdk'
-import { cn } from '@/lib/utils'
-import { Icon } from '@/components/Icon'
-import { DialogContent } from '@radix-ui/react-dialog'
-import { DialogProps } from '@radix-ui/react-dialog'
-import { Dialog } from '../Dialog'
+import * as React from "react";
+import { Command as CommandPrimitive } from "cmdk";
+import { cn } from "@/lib/utils";
+import { Icon } from "@/components/Icon";
+import { DialogContent } from "@radix-ui/react-dialog";
+import { DialogProps } from "@radix-ui/react-dialog";
+import { Dialog } from "../Dialog";
const Command = React.forwardRef<
React.ElementRef,
@@ -15,13 +15,13 @@ const Command = React.forwardRef<
-))
-Command.displayName = CommandPrimitive.displayName
+));
+Command.displayName = CommandPrimitive.displayName;
const CommandInput = React.forwardRef<
React.ElementRef,
@@ -32,14 +32,14 @@ const CommandInput = React.forwardRef<
-))
+));
-CommandInput.displayName = CommandPrimitive.Input.displayName
+CommandInput.displayName = CommandPrimitive.Input.displayName;
const CommandList = React.forwardRef<
React.ElementRef,
@@ -47,12 +47,12 @@ const CommandList = React.forwardRef<
>(({ className, ...props }, ref) => (
-))
+));
-CommandList.displayName = CommandPrimitive.List.displayName
+CommandList.displayName = CommandPrimitive.List.displayName;
const CommandEmpty = React.forwardRef<
React.ElementRef,
@@ -63,9 +63,9 @@ const CommandEmpty = React.forwardRef<
className="py-6 text-center text-sm"
{...props}
/>
-))
+));
-CommandEmpty.displayName = CommandPrimitive.Empty.displayName
+CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
const CommandGroup = React.forwardRef<
React.ElementRef,
@@ -74,26 +74,26 @@ const CommandGroup = React.forwardRef<
-))
+));
-CommandGroup.displayName = CommandPrimitive.Group.displayName
+CommandGroup.displayName = CommandPrimitive.Group.displayName;
const CommandDialog = ({ children, ...props }: DialogProps) => {
return (
-
+
{children}
- )
-}
+ );
+};
const CommandSeparator = React.forwardRef<
React.ElementRef,
@@ -101,11 +101,11 @@ const CommandSeparator = React.forwardRef<
>(({ className, ...props }, ref) => (
-))
-CommandSeparator.displayName = CommandPrimitive.Separator.displayName
+));
+CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
const CommandItem = React.forwardRef<
React.ElementRef,
@@ -114,14 +114,14 @@ const CommandItem = React.forwardRef<
-))
+));
-CommandItem.displayName = CommandPrimitive.Item.displayName
+CommandItem.displayName = CommandPrimitive.Item.displayName;
const CommandShortcut = ({
className,
@@ -130,14 +130,14 @@ const CommandShortcut = ({
return (
- )
-}
-CommandShortcut.displayName = 'CommandShortcut'
+ );
+};
+CommandShortcut.displayName = "CommandShortcut";
export {
Command,
@@ -149,4 +149,4 @@ export {
CommandShortcut,
CommandSeparator,
CommandDialog,
-}
+};
diff --git a/src/components/Container/index.stories.tsx b/src/components/Container/index.stories.tsx
index 00810ee3..65584558 100644
--- a/src/components/Container/index.stories.tsx
+++ b/src/components/Container/index.stories.tsx
@@ -1,15 +1,15 @@
-import { Container } from '.'
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { Card, Grid } from '@/index'
+import { Container } from ".";
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { Card, Grid } from "@/index";
const meta: Meta = {
component: Container,
- tags: ['autodocs'],
-}
+ tags: ["autodocs"],
+};
-export default meta
+export default meta;
-type Story = StoryObj
+type Story = StoryObj;
export const Default: Story = {
args: {
@@ -30,4 +30,4 @@ export const Default: Story = {
,
],
},
-}
+};
diff --git a/src/components/Container/index.tsx b/src/components/Container/index.tsx
index e0c1e7cb..aef4d301 100644
--- a/src/components/Container/index.tsx
+++ b/src/components/Container/index.tsx
@@ -1,13 +1,13 @@
-import { paddingMapper } from '@/lib/responsiveMappers'
-import { cn, getResponsiveClasses } from '@/lib/utils'
-import { Padding, ResponsiveValue } from '@/types'
-import { ReactNode } from 'react'
+import { paddingMapper } from "@/lib/responsiveMappers";
+import { cn, getResponsiveClasses } from "@/lib/utils";
+import { Padding, ResponsiveValue } from "@/types";
+import { ReactNode } from "react";
export interface ContainerProps {
- children: ReactNode
- flex?: boolean
- padding?: ResponsiveValue
- className?: string
+ children: ReactNode;
+ flex?: boolean;
+ padding?: ResponsiveValue;
+ className?: string;
}
export function Container({
@@ -19,13 +19,13 @@ export function Container({
return (
{children}
- )
+ );
}
diff --git a/src/components/ContextDropdown/index.stories.tsx b/src/components/ContextDropdown/index.stories.tsx
index 651db999..ac2643be 100644
--- a/src/components/ContextDropdown/index.stories.tsx
+++ b/src/components/ContextDropdown/index.stories.tsx
@@ -1,34 +1,34 @@
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { ContextDropdown } from '.'
-import { useModal } from '../../hooks/useModal'
-import { Button } from '../Button'
-import { Popover, PopoverContent, PopoverTrigger } from '../Popover'
-import { ModalProvider } from '@/context/ModalContext'
-import { faker } from '@faker-js/faker'
-import { cn } from '@/lib/utils'
-import { Heading } from '../Heading'
-import { Text } from '../Text'
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { ContextDropdown } from ".";
+import { useModal } from "../../hooks/useModal";
+import { Button } from "../Button";
+import { Popover, PopoverContent, PopoverTrigger } from "../Popover";
+import { ModalProvider } from "@/context/ModalContext";
+import { faker } from "@faker-js/faker";
+import { cn } from "@/lib/utils";
+import { Heading } from "../Heading";
+import { Text } from "../Text";
-faker.seed(123)
+faker.seed(123);
const meta: Meta = {
component: ContextDropdown,
- tags: ['autodocs'],
-}
+ tags: ["autodocs"],
+};
-export default meta
+export default meta;
-type Story = StoryObj
+type Story = StoryObj;
interface ScreenProps {
- title: string
- content: string
- index: number
+ title: string;
+ content: string;
+ index: number;
}
function Screen({ title, content, index }: ScreenProps) {
- const { pushScreen } = useModal()
- const nextIndex = index + 1
+ const { pushScreen } = useModal();
+ const nextIndex = index + 1;
return (
@@ -51,7 +51,7 @@ function Screen({ title, content, index }: ScreenProps) {
index={nextIndex}
/>
),
- })
+ });
}}
>
Next
@@ -59,7 +59,7 @@ function Screen({ title, content, index }: ScreenProps) {
- )
+ );
}
export const Default: Story = {
@@ -72,8 +72,8 @@ export const Default: Story = {
openScreen({
- id: '1',
- title: 'Screen 1',
+ id: "1",
+ title: "Screen 1",
component: (
)}
- )
+ );
},
-}
+};
export const CustomTitle: Story = {
render: () => {
@@ -111,8 +111,8 @@ export const CustomTitle: Story = {
openScreen({
- id: '1',
- title: 'Screen 1',
+ id: "1",
+ title: "Screen 1",
component: (
(
{screen.title}
@@ -149,6 +149,6 @@ export const CustomTitle: Story = {
)}
- )
+ );
},
-}
+};
diff --git a/src/components/ContextDropdown/index.tsx b/src/components/ContextDropdown/index.tsx
index 78af8977..4bfa7740 100644
--- a/src/components/ContextDropdown/index.tsx
+++ b/src/components/ContextDropdown/index.tsx
@@ -1,20 +1,20 @@
-'use client'
+"use client";
-import { useEffect } from 'react'
-import { motion, AnimatePresence } from 'motion/react'
-import { Icon } from '../Icon'
-import { assert } from '@/lib/typeUtils'
-import { Heading } from '../Heading'
-import React from 'react'
-import { useModal } from '@/hooks/useModal'
-import { Screen } from '@/context/ModalContext'
+import { useEffect } from "react";
+import { motion, AnimatePresence } from "motion/react";
+import { Icon } from "../Icon";
+import { assert } from "@/lib/typeUtils";
+import { Heading } from "../Heading";
+import React from "react";
+import { useModal } from "@/hooks/useModal";
+import { Screen } from "@/context/ModalContext";
-const MotionHeading = motion.create(Heading)
+const MotionHeading = motion.create(Heading);
-const animationDuration = 0.15
+const animationDuration = 0.15;
interface ContextDropdownProps {
- renderTitle?: (screen: Screen, index: number) => React.ReactNode
+ renderTitle?: (screen: Screen, index: number) => React.ReactNode;
}
export function ContextDropdown({ renderTitle }: ContextDropdownProps) {
@@ -25,52 +25,52 @@ export function ContextDropdown({ renderTitle }: ContextDropdownProps) {
close,
popScreen,
navigationDirection,
- } = useModal()
+ } = useModal();
// Animation variants for title and content
const slideVariants = {
enter: (isForward: boolean) => ({
- x: isForward ? '100%' : '-100%',
+ x: isForward ? "100%" : "-100%",
opacity: 0,
- width: '100%',
+ width: "100%",
}),
center: {
x: 0,
opacity: 1,
- width: '100%',
+ width: "100%",
},
exit: (isForward: boolean) => ({
- x: isForward ? '-100%' : '100%',
+ x: isForward ? "-100%" : "100%",
opacity: 0,
- width: '100%',
+ width: "100%",
}),
- }
+ };
// Handle escape key to close modal
useEffect(() => {
const handleEscape = (e: KeyboardEvent) => {
- if (e.key === 'Escape') {
- close()
+ if (e.key === "Escape") {
+ close();
}
- }
+ };
if (isOpen) {
- document.addEventListener('keydown', handleEscape)
+ document.addEventListener("keydown", handleEscape);
// Prevent body scrolling when modal is open
- document.body.style.overflow = 'hidden'
+ document.body.style.overflow = "hidden";
}
return () => {
- document.removeEventListener('keydown', handleEscape)
- document.body.style.overflow = 'auto'
- }
- }, [isOpen, close])
+ document.removeEventListener("keydown", handleEscape);
+ document.body.style.overflow = "auto";
+ };
+ }, [isOpen, close]);
- if (!isOpen) return null
+ if (!isOpen) return null;
- const currentScreen = screens[currentIndex]
- assert(currentScreen, 'No current screen')
- const isForward = navigationDirection === 'forward'
+ const currentScreen = screens[currentIndex];
+ assert(currentScreen, "No current screen");
+ const isForward = navigationDirection === "forward";
return (
@@ -87,7 +87,7 @@ export function ContextDropdown({ renderTitle }: ContextDropdownProps) {
{currentIndex > 0 && (
@@ -106,7 +106,7 @@ export function ContextDropdown({ renderTitle }: ContextDropdownProps) {
animate="center"
exit="exit"
variant="sm"
- transition={{ type: 'tween', duration: animationDuration }}
+ transition={{ type: "tween", duration: animationDuration }}
>
{renderTitle
? renderTitle(currentScreen, currentIndex)
@@ -119,7 +119,7 @@ export function ContextDropdown({ renderTitle }: ContextDropdownProps) {
{/* Static right side with close button */}
@@ -137,7 +137,7 @@ export function ContextDropdown({ renderTitle }: ContextDropdownProps) {
initial="enter"
animate="center"
exit="exit"
- transition={{ type: 'tween', duration: animationDuration }}
+ transition={{ type: "tween", duration: animationDuration }}
>
{currentScreen.component}
@@ -146,5 +146,5 @@ export function ContextDropdown({ renderTitle }: ContextDropdownProps) {
)}
- )
+ );
}
diff --git a/src/components/Dialog/index.stories.tsx b/src/components/Dialog/index.stories.tsx
index de21f4bb..a96080e4 100644
--- a/src/components/Dialog/index.stories.tsx
+++ b/src/components/Dialog/index.stories.tsx
@@ -1,21 +1,21 @@
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { Dialog } from '.'
-import { Button } from '../Button'
-import { Heading } from '../Heading'
-import { Stack } from '../Stack'
-import { Text } from '../Text'
-import { CodeSnippet } from '../CodeSnippet'
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { Dialog } from ".";
+import { Button } from "../Button";
+import { Heading } from "../Heading";
+import { Stack } from "../Stack";
+import { Text } from "../Text";
+import { CodeSnippet } from "../CodeSnippet";
const meta: Meta
= {
component: Dialog,
- tags: ['autodocs'],
+ tags: ["autodocs"],
parameters: {
- layout: 'centered',
+ layout: "centered",
},
-}
+};
-export default meta
-type Story = StoryObj
+export default meta;
+type Story = StoryObj;
export const Default: Story = {
args: {
@@ -65,4 +65,4 @@ export const Default: Story = {
,
],
},
-}
+};
diff --git a/src/components/Dialog/index.tsx b/src/components/Dialog/index.tsx
index 13013468..c6868422 100644
--- a/src/components/Dialog/index.tsx
+++ b/src/components/Dialog/index.tsx
@@ -1,16 +1,16 @@
-'use client'
+"use client";
-import * as React from 'react'
-import * as DialogPrimitive from '@radix-ui/react-dialog'
-import { X } from 'lucide-react'
+import * as React from "react";
+import * as DialogPrimitive from "@radix-ui/react-dialog";
+import { X } from "lucide-react";
-import { cn } from '@/lib/utils'
+import { cn } from "@/lib/utils";
-const DialogTrigger = DialogPrimitive.Trigger
+const DialogTrigger = DialogPrimitive.Trigger;
-const DialogPortal = DialogPrimitive.Portal
+const DialogPortal = DialogPrimitive.Portal;
-const DialogClose = DialogPrimitive.Close
+const DialogClose = DialogPrimitive.Close;
const DialogOverlay = React.forwardRef<
React.ElementRef,
@@ -19,18 +19,18 @@ const DialogOverlay = React.forwardRef<
-))
-DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
+));
+DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
const DialogContent = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef & {
- closeable?: boolean
+ closeable?: boolean;
}
>(({ className, children, closeable = true, ...props }, ref) => (
@@ -38,22 +38,22 @@ const DialogContent = React.forwardRef<
{children}
{closeable && (
-
+
Close
)}
-))
-DialogContent.displayName = DialogPrimitive.Content.displayName
+));
+DialogContent.displayName = DialogPrimitive.Content.displayName;
const DialogHeader = ({
className,
@@ -61,13 +61,13 @@ const DialogHeader = ({
}: React.HTMLAttributes) => (
-)
-DialogHeader.displayName = 'DialogHeader'
+);
+DialogHeader.displayName = "DialogHeader";
const DialogFooter = ({
className,
@@ -75,13 +75,13 @@ const DialogFooter = ({
}: React.HTMLAttributes) => (
-)
-DialogFooter.displayName = 'DialogFooter'
+);
+DialogFooter.displayName = "DialogFooter";
const DialogTitle = React.forwardRef<
React.ElementRef,
@@ -90,13 +90,13 @@ const DialogTitle = React.forwardRef<
-))
-DialogTitle.displayName = DialogPrimitive.Title.displayName
+));
+DialogTitle.displayName = DialogPrimitive.Title.displayName;
const DialogDescription = React.forwardRef<
React.ElementRef,
@@ -104,11 +104,11 @@ const DialogDescription = React.forwardRef<
>(({ className, ...props }, ref) => (
-))
-DialogDescription.displayName = DialogPrimitive.Description.displayName
+));
+DialogDescription.displayName = DialogPrimitive.Description.displayName;
export const Dialog = Object.assign(DialogPrimitive.Root, {
Portal: DialogPortal,
@@ -120,4 +120,4 @@ export const Dialog = Object.assign(DialogPrimitive.Root, {
Footer: DialogFooter,
Title: DialogTitle,
Description: DialogDescription,
-})
+});
diff --git a/src/components/DragNDrop/DragNDropArea.tsx b/src/components/DragNDrop/DragNDropArea.tsx
index df6f69db..ff35fa14 100644
--- a/src/components/DragNDrop/DragNDropArea.tsx
+++ b/src/components/DragNDrop/DragNDropArea.tsx
@@ -1,17 +1,17 @@
-import { CollisionDetection, DndContext, Modifier } from '@dnd-kit/core'
-import { restrictToWindowEdges } from '@dnd-kit/modifiers'
+import { CollisionDetection, DndContext, Modifier } from "@dnd-kit/core";
+import { restrictToWindowEdges } from "@dnd-kit/modifiers";
export interface DragNDropAreaProps {
- children: React.ReactNode
+ children: React.ReactNode;
- modifiers?: Modifier[]
+ modifiers?: Modifier[];
- className?: string
+ className?: string;
- collisionDetectionAlgo?: CollisionDetection
+ collisionDetectionAlgo?: CollisionDetection;
}
-const defaultModifiers: Modifier[] = [restrictToWindowEdges]
+const defaultModifiers: Modifier[] = [restrictToWindowEdges];
export function DragNDropArea({
children,
@@ -26,5 +26,5 @@ export function DragNDropArea({
>
{children}
- )
+ );
}
diff --git a/src/components/DragNDrop/DragOverlay.tsx b/src/components/DragNDrop/DragOverlay.tsx
index 240cce53..ddbbaaa2 100644
--- a/src/components/DragNDrop/DragOverlay.tsx
+++ b/src/components/DragNDrop/DragOverlay.tsx
@@ -1,4 +1,4 @@
-import { DragOverlay as DndOverlay } from '@dnd-kit/core'
+import { DragOverlay as DndOverlay } from "@dnd-kit/core";
// TODO: Add a custom overlay component
-export const DragOverlay = DndOverlay
+export const DragOverlay = DndOverlay;
diff --git a/src/components/DragNDrop/Draggable.tsx b/src/components/DragNDrop/Draggable.tsx
index 4712f5ea..200db416 100644
--- a/src/components/DragNDrop/Draggable.tsx
+++ b/src/components/DragNDrop/Draggable.tsx
@@ -6,17 +6,17 @@ import {
Over,
Data,
UniqueIdentifier,
-} from '@dnd-kit/core'
-import type { ClientRect } from '@dnd-kit/core/dist/types/rect'
-import { CSS } from '@dnd-kit/utilities'
-import { MutableRefObject } from 'react'
+} from "@dnd-kit/core";
+import type { ClientRect } from "@dnd-kit/core/dist/types/rect";
+import { CSS } from "@dnd-kit/utilities";
+import { MutableRefObject } from "react";
export interface DraggableChildrenProps {
- over: Over | null
- active: Active | null
- activeNodeRect: ClientRect | null
- isDragging: boolean
- node: MutableRefObject | null
+ over: Over | null;
+ active: Active | null;
+ activeNodeRect: ClientRect | null;
+ isDragging: boolean;
+ node: MutableRefObject | null;
}
export interface DraggableProps extends DndMonitorListener {
@@ -27,21 +27,21 @@ export interface DraggableProps extends DndMonitorListener {
*/
children:
| React.ReactNode
- | ((props: DraggableChildrenProps) => React.ReactNode)
+ | ((props: DraggableChildrenProps) => React.ReactNode);
/**
* The unique identifier for the draggable.
*/
- id: UniqueIdentifier
+ id: UniqueIdentifier;
- className?: string
+ className?: string;
- disabled?: boolean
+ disabled?: boolean;
/**
* The data to pass to the draggable of generic type TData.
*/
- data?: TData
+ data?: TData;
}
export function Draggable({
@@ -69,17 +69,17 @@ export function Draggable({
id,
data,
disabled,
- })
+ });
useDndMonitor({
onDragEnd,
onDragStart,
onDragCancel,
onDragOver,
- })
+ });
const style: React.CSSProperties = {
transform: CSS.Translate.toString(transform),
- }
+ };
return (
({
{...listeners}
className={className}
>
- {typeof children === 'function'
+ {typeof children === "function"
? children({ over, active, activeNodeRect, isDragging, node })
: children}
- )
+ );
}
diff --git a/src/components/DragNDrop/Droppable.tsx b/src/components/DragNDrop/Droppable.tsx
index 19bc01ef..89d1955c 100644
--- a/src/components/DragNDrop/Droppable.tsx
+++ b/src/components/DragNDrop/Droppable.tsx
@@ -1,12 +1,12 @@
-import { Over, UniqueIdentifier, useDroppable } from '@dnd-kit/core'
-import type { ClientRect } from '@dnd-kit/core/dist/types/rect'
-import type { MutableRefObject, ReactNode } from 'react'
+import { Over, UniqueIdentifier, useDroppable } from "@dnd-kit/core";
+import type { ClientRect } from "@dnd-kit/core/dist/types/rect";
+import type { MutableRefObject, ReactNode } from "react";
interface DroppableData {
- isOver: boolean
- over: Over | null
- rect: MutableRefObject
- node: MutableRefObject
+ isOver: boolean;
+ over: Over | null;
+ rect: MutableRefObject;
+ node: MutableRefObject;
}
export interface DroppableProps> {
@@ -14,20 +14,20 @@ export interface DroppableProps> {
* A function that returns a React node or a React node.
* If a function is provided, it will be called with the droppable data.
*/
- children: ReactNode | ((props: DroppableData) => ReactNode)
+ children: ReactNode | ((props: DroppableData) => ReactNode);
/**
* The unique identifier for the droppable container.
*/
- id: UniqueIdentifier
+ id: UniqueIdentifier;
/**
* The data to pass to the droppable container.
* Will be returned in any dragOver events for draggables that are over this droppable.
*/
- data?: TData
+ data?: TData;
- className?: string
+ className?: string;
}
export function Droppable>({
@@ -39,13 +39,13 @@ export function Droppable>({
const { setNodeRef, isOver, over, rect, node } = useDroppable({
id,
data,
- })
+ });
return (
- {typeof children === 'function'
+ {typeof children === "function"
? children({ isOver, over, rect, node })
: children}
- )
+ );
}
diff --git a/src/components/DragNDrop/index.stories.tsx b/src/components/DragNDrop/index.stories.tsx
index 34208d4d..144cc131 100644
--- a/src/components/DragNDrop/index.stories.tsx
+++ b/src/components/DragNDrop/index.stories.tsx
@@ -1,38 +1,38 @@
-import { useState } from 'react'
-import { Draggable } from './Draggable'
-import { DragNDropArea } from './DragNDropArea'
-import { Droppable } from './Droppable'
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { DragEndEvent } from '@dnd-kit/core'
-import { cn } from '@/lib/utils'
+import { useState } from "react";
+import { Draggable } from "./Draggable";
+import { DragNDropArea } from "./DragNDropArea";
+import { Droppable } from "./Droppable";
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { DragEndEvent } from "@dnd-kit/core";
+import { cn } from "@/lib/utils";
const meta: Meta = {
component: Droppable,
-}
+};
-export default meta
+export default meta;
-type Story = StoryObj
+type Story = StoryObj;
const DragNDropDemo = () => {
- const [droppedItems, setDroppedItems] = useState>(new Set())
- const [draggingOver, setDraggingOver] = useState(false)
- const [validDrop, setValidDrop] = useState(false)
+ const [droppedItems, setDroppedItems] = useState>(new Set());
+ const [draggingOver, setDraggingOver] = useState(false);
+ const [validDrop, setValidDrop] = useState(false);
const checkDrop = (event: DragEndEvent) => {
- setDraggingOver(false)
- const { over, active } = event
+ setDraggingOver(false);
+ const { over, active } = event;
if (over) {
- const activeType = active.data.current?.type
+ const activeType = active.data.current?.type;
if (over.data.current?.acceptsType.includes(activeType)) {
setDroppedItems((prev) => {
- const newSet = new Set(prev)
- newSet.add(activeType)
- return newSet
- })
+ const newSet = new Set(prev);
+ newSet.add(activeType);
+ return newSet;
+ });
}
}
- }
+ };
return (
@@ -40,51 +40,51 @@ const DragNDropDemo = () => {
onDragEnd={checkDrop}
onDragOver={(e) => {
if (e.over) {
- setDraggingOver(true)
+ setDraggingOver(true);
} else {
- setDraggingOver(false)
+ setDraggingOver(false);
}
if (
e.over?.data.current?.acceptsType.includes(
- e.active.data.current?.type
+ e.active.data.current?.type,
)
) {
- setValidDrop(true)
+ setValidDrop(true);
} else {
- setValidDrop(false)
+ setValidDrop(false);
}
}}
id="banana"
- data={{ type: '🍌' }}
+ data={{ type: "🍌" }}
>
🍌 Banana
-
+
🍎 Apple
-
+
🍊 Orange
-
+
Dropzone (accepts 🍌 and 🍎)
-
{Array.from(droppedItems).join(' ')}
+
{Array.from(droppedItems).join(" ")}
- )
-}
+ );
+};
export const Default: Story = {
render: () => ,
-}
+};
diff --git a/src/components/Dropdown/index.stories.tsx b/src/components/Dropdown/index.stories.tsx
index 44066634..bb8954ac 100644
--- a/src/components/Dropdown/index.stories.tsx
+++ b/src/components/Dropdown/index.stories.tsx
@@ -5,20 +5,20 @@ import {
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
-} from '.'
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { Button } from '../Button'
-import { Stack } from '../Stack'
-import { Icon } from '../Icon'
+} from ".";
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { Button } from "../Button";
+import { Stack } from "../Stack";
+import { Icon } from "../Icon";
const meta: Meta = {
component: DropdownMenu,
- tags: ['autodocs'],
-}
+ tags: ["autodocs"],
+};
-export default meta
+export default meta;
-type Story = StoryObj
+type Story = StoryObj;
export const Default: Story = {
args: {
@@ -30,7 +30,7 @@ export const Default: Story = {
Jane Smith
-
+
jane@example.com
@@ -45,4 +45,4 @@ export const Default: Story = {
,
],
},
-}
+};
diff --git a/src/components/Dropdown/index.tsx b/src/components/Dropdown/index.tsx
index 8baee272..a16cdbf8 100644
--- a/src/components/Dropdown/index.tsx
+++ b/src/components/Dropdown/index.tsx
@@ -1,44 +1,44 @@
-'use client'
+"use client";
-import * as React from 'react'
-import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
-import { Check, ChevronRight, Circle } from 'lucide-react'
+import * as React from "react";
+import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
+import { Check, ChevronRight, Circle } from "lucide-react";
-import { cn } from '@/lib/utils'
+import { cn } from "@/lib/utils";
-const DropdownMenu = DropdownMenuPrimitive.Root
+const DropdownMenu = DropdownMenuPrimitive.Root;
-const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
+const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
-const DropdownMenuGroup = DropdownMenuPrimitive.Group
+const DropdownMenuGroup = DropdownMenuPrimitive.Group;
-const DropdownMenuPortal = DropdownMenuPrimitive.Portal
+const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
-const DropdownMenuSub = DropdownMenuPrimitive.Sub
+const DropdownMenuSub = DropdownMenuPrimitive.Sub;
-const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
+const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
const DropdownMenuSubTrigger = React.forwardRef<
React.ElementRef
,
React.ComponentPropsWithoutRef & {
- inset?: boolean
+ inset?: boolean;
}
>(({ className, inset, children, ...props }, ref) => (
{children}