From b29613ca3d7d82cf7ea268e563878d5aeb3a1d8a Mon Sep 17 00:00:00 2001 From: hfcRed Date: Fri, 26 Dec 2025 13:28:35 +0100 Subject: [PATCH] Add components page --- .../components-showcase.module.css | 28 + .../components-showcase/routes/components.tsx | 729 ++++++++++++++++++ app/routes.ts | 4 + 3 files changed, 761 insertions(+) create mode 100644 app/features/components-showcase/components-showcase.module.css create mode 100644 app/features/components-showcase/routes/components.tsx diff --git a/app/features/components-showcase/components-showcase.module.css b/app/features/components-showcase/components-showcase.module.css new file mode 100644 index 000000000..e51b5aa80 --- /dev/null +++ b/app/features/components-showcase/components-showcase.module.css @@ -0,0 +1,28 @@ +.sectionTitle { + font-size: var(--fonts-xl); + border-bottom: 2px solid var(--color-bg-high); + margin-bottom: var(--s-4); +} + +.componentRow { + display: grid; + align-items: center; + grid-template-columns: 200px 1fr; + gap: var(--s-4); +} + +.componentRow:last-child { + border-bottom: none; +} + +.componentLabel { + color: var(--color-text-high); + font-size: var(--fonts-sm); +} + +.componentContent { + display: flex; + flex-wrap: wrap; + gap: var(--spacing-sm); + align-items: center; +} diff --git a/app/features/components-showcase/routes/components.tsx b/app/features/components-showcase/routes/components.tsx new file mode 100644 index 000000000..b4be83a2d --- /dev/null +++ b/app/features/components-showcase/routes/components.tsx @@ -0,0 +1,729 @@ +import { useState } from "react"; +import { Alert } from "~/components/Alert"; +import { Divider } from "~/components/Divider"; +import { LinkButton, SendouButton } from "~/components/elements/Button"; +import { SendouDialog } from "~/components/elements/Dialog"; +import { SendouMenu, SendouMenuItem } from "~/components/elements/Menu"; +import { SendouPopover } from "~/components/elements/Popover"; +import { SendouSelect, SendouSelectItem } from "~/components/elements/Select"; +import { SendouSwitch } from "~/components/elements/Switch"; +import { + SendouTab, + SendouTabList, + SendouTabPanel, + SendouTabs, +} from "~/components/elements/Tabs"; +import { toastQueue } from "~/components/elements/Toast"; +import { InfoPopover } from "~/components/InfoPopover"; +import { Input } from "~/components/Input"; +import { CheckmarkIcon } from "~/components/icons/Checkmark"; +import { EditIcon } from "~/components/icons/Edit"; +import { PlusIcon } from "~/components/icons/Plus"; +import { SearchIcon } from "~/components/icons/Search"; +import { TrashIcon } from "~/components/icons/Trash"; +import { Label } from "~/components/Label"; +import { Main } from "~/components/Main"; +import { Section } from "~/components/Section"; +import { SubmitButton } from "~/components/SubmitButton"; +import styles from "../components-showcase.module.css"; + +export default function ComponentsShowcasePage() { + return ( +
+

Components

+ + + + + + + + + + + + +
+ ); +} + +function SectionTitle({ children }: { children: React.ReactNode }) { + return

{children}

; +} + +function ComponentRow({ + label, + children, +}: { + label: string; + children: React.ReactNode; +}) { + return ( +
+
{label}
+
{children}
+
+ ); +} + +function ButtonsSection() { + return ( +
+ Buttons + +
+ + Primary Button + + + + Success Button + + + + Outlined Button + + + + + Outlined Success + + + + + Destructive Button + + + + Minimal Button + + + + Minimal Success + + + + + Minimal Destructive + + + + Sizes + + + Miniscule + + + + Small + + + + Medium + + + + Big + + + With Icons + + + }>Add Item + + + + } /> + + + + }> + Delete + + + + States + + + Disabled Button + + + Link Buttons + + + Go to Home + + + + + GitHub + + + + Submit Button + + + Submit Form + +
+
+ ); +} + +function AlertsSection() { + return ( +
+ Alerts + +
+ + This is an informational alert message. + + + + + This is a warning alert. Please be careful! + + + + + + This is an error alert. Something went wrong. + + + + + + This is a success alert. Operation completed! + + + + + This is a tiny alert message. + +
+
+ ); +} + +function InputsSection() { + return ( +
+ Inputs + +
+ + + + + + + + + + } + placeholder="Search..." + aria-label="Search" + /> + + + + + + + + + + + + + + + Labels + + +
+ + +
+
+ + +
+ + +
+
+ + +
+ + +
+
+
+
+ ); +} + +const SELECT_ITEMS = [ + { id: "1", name: "Option 1" }, + { id: "2", name: "Option 2" }, + { id: "3", name: "Option 3" }, + { id: "4", name: "Option 4" }, + { id: "5", name: "Option 5" }, +]; + +function SelectSection() { + return ( +
+ Select + +
+ + + {(item) => ( + + {item.name} + + )} + + + + + + {(item) => ( + + {item.name} + + )} + + + + + + {(item) => ( + + {item.name} + + )} + + + + + + {(item) => ( + + {item.name} + + )} + + + + + + {(item) => ( + + {item.name} + + )} + + + + + + {(item) => ( + + {item.name} + + )} + + +
+
+ ); +} + +function SwitchSection() { + const [isOn, setIsOn] = useState(false); + const [isSmallOn, setIsSmallOn] = useState(true); + + return ( +
+ Switch + +
+ + + Toggle me + + + + + + Small switch + + + + + + + + + Disabled switch + +
+
+ ); +} + +function TabsSection() { + return ( +
+ Tabs + +
+ + + + First Tab + Second Tab + Third Tab + + + Content for the first tab. + + + Content for the second tab. + + + Content for the third tab. + + + + + + + + }> + Search + + }> + Edit + + }> + Review + + + Search content here. + Edit content here. + Review content here. + + + + + + + + Pending + + + Approved + + + Rejected + + + 5 pending items. + 12 approved items. + No rejected items. + + +
+
+ ); +} + +function DialogSection() { + const [isOpen, setIsOpen] = useState(false); + + return ( +
+ Dialog + +
+ + Open Dialog} + heading="Dialog Title" + > +

This is the content of the dialog.

+

+ You can put any content here, including forms, lists, or other + components. +

+
+
+ + +
+ setIsOpen(true)}> + Open Controlled Dialog + + setIsOpen(false)} + heading="Controlled Dialog" + showCloseButton + > +

This dialog is controlled via state.

+ setIsOpen(false)}> + Close + +
+
+
+
+
+ ); +} + +function PopoverSection() { + return ( +
+ Popover + +
+ + Click for Popover} + > +

This is popover content!

+
+
+ + +
+ Some text with help + + This is additional information shown in a popover. + +
+
+ + +
+ Compact help icon + Tiny popover with less padding. +
+
+
+
+ ); +} + +function MenuSection() { + return ( +
+ Menu + +
+ + Open Menu}> + {}}>Menu Item 1 + {}}>Menu Item 2 + {}}>Menu Item 3 + + + + + Menu with Icons}> + } onAction={() => {}}> + Edit + + } onAction={() => {}}> + Add New + + } onAction={() => {}}> + Delete + + + + + + Menu with Active}> + {}}> + Active Item + + {}}>Normal Item + {}}>Another Item + + + + + Scrolling Menu} + > + {Array.from({ length: 10 }, (_, i) => ( + {}}> + Item {i + 1} + + ))} + + +
+
+ ); +} + +function ToastSection() { + return ( +
+ Toast + +
+ + + toastQueue.add({ + message: "Operation completed successfully!", + variant: "success", + }) + } + > + Show Success Toast + + + + + + toastQueue.add({ + message: "Something went wrong. Please try again.", + variant: "error", + }) + } + > + Show Error Toast + + + + + + toastQueue.add({ + message: "Here is some information for you.", + variant: "info", + }) + } + > + Show Info Toast + + +
+
+ ); +} + +function DividerSection() { + return ( +
+ Divider + +
+ +
+

Content above

+ +

Content below

+
+
+ + +
+

Section 1

+ OR +

Section 2

+
+
+ + +
+

Above

+ small text divider +

Below

+
+
+
+
+ ); +} + +function MiscSection() { + return ( +
+ Miscellaneous + +
+ +
+

This is content inside a Section component.

+

It provides consistent styling for content blocks.

+
+
+
+
+ ); +} diff --git a/app/routes.ts b/app/routes.ts index 48bf783a7..f97714608 100644 --- a/app/routes.ts +++ b/app/routes.ts @@ -12,6 +12,10 @@ const devOnlyRoutes = "/admin/generate-images", "features/admin/routes/generate-images.tsx", ), + route( + "/components", + "features/components-showcase/routes/components.tsx", + ), ] satisfies RouteConfig) : [];